From 781f3ae58d9da3ed19e466d5900eced1ab0e2c35 Mon Sep 17 00:00:00 2001 From: tranvictor Date: Thu, 13 Apr 2017 03:38:21 +0700 Subject: [PATCH] add go-ethereum to vendor to prevent incompatible go-ethereum interface --- compile.sh | 2 +- ethereum/ethash/ethash.go | 2 +- ethereum/share.go | 2 +- kovan_compile.sh | 4 +- .../ethereum/go-ethereum/.dockerignore | 3 + .../ethereum/go-ethereum/.gitattributes | 2 + .../go-ethereum/.github/CONTRIBUTING.md | 16 + .../go-ethereum/.github/ISSUE_TEMPLATE.md | 20 + .../ethereum/go-ethereum/.gitignore | 32 + .../github.com/ethereum/go-ethereum/.mailmap | 92 + .../ethereum/go-ethereum/.travis.yml | 157 + .../github.com/ethereum/go-ethereum/AUTHORS | 63 + .../github.com/ethereum/go-ethereum/COPYING | 619 + .../ethereum/go-ethereum/COPYING.LESSER | 165 + .../ethereum/go-ethereum/Dockerfile | 14 + .../github.com/ethereum/go-ethereum/Makefile | 123 + .../github.com/ethereum/go-ethereum/README.md | 278 + .../github.com/ethereum/go-ethereum/VERSION | 1 + .../ethereum/go-ethereum/accounts/abi/abi.go | 429 + .../go-ethereum/accounts/abi/abi_test.go | 1241 + .../go-ethereum/accounts/abi/argument.go | 51 + .../go-ethereum/accounts/abi/bind/auth.go | 62 + .../go-ethereum/accounts/abi/bind/backend.go | 97 + .../accounts/abi/bind/backends/simulated.go | 298 + .../go-ethereum/accounts/abi/bind/base.go | 232 + .../go-ethereum/accounts/abi/bind/bind.go | 329 + .../accounts/abi/bind/bind_test.go | 467 + .../go-ethereum/accounts/abi/bind/template.go | 369 + .../go-ethereum/accounts/abi/bind/util.go | 76 + .../accounts/abi/bind/util_test.go | 93 + .../ethereum/go-ethereum/accounts/abi/doc.go | 26 + .../go-ethereum/accounts/abi/error.go | 79 + .../go-ethereum/accounts/abi/event.go | 46 + .../go-ethereum/accounts/abi/event_test.go | 56 + .../go-ethereum/accounts/abi/method.go | 118 + .../go-ethereum/accounts/abi/numbers.go | 84 + .../go-ethereum/accounts/abi/numbers_test.go | 82 + .../go-ethereum/accounts/abi/packing.go | 65 + .../go-ethereum/accounts/abi/reflect.go | 93 + .../ethereum/go-ethereum/accounts/abi/type.go | 212 + .../go-ethereum/accounts/abi/type_test.go | 78 + .../ethereum/go-ethereum/accounts/accounts.go | 155 + .../ethereum/go-ethereum/accounts/errors.go | 68 + .../ethereum/go-ethereum/accounts/hd.go | 130 + .../ethereum/go-ethereum/accounts/hd_test.go | 79 + .../accounts/keystore/account_cache.go | 283 + .../accounts/keystore/account_cache_test.go | 297 + .../go-ethereum/accounts/keystore/key.go | 229 + .../go-ethereum/accounts/keystore/keystore.go | 494 + .../accounts/keystore/keystore_passphrase.go | 305 + .../keystore/keystore_passphrase_test.go | 60 + .../accounts/keystore/keystore_plain.go | 62 + .../accounts/keystore/keystore_plain_test.go | 253 + .../accounts/keystore/keystore_test.go | 365 + .../accounts/keystore/keystore_wallet.go | 139 + .../go-ethereum/accounts/keystore/presale.go | 137 + .../accounts/keystore/testdata/dupes/1 | 1 + .../accounts/keystore/testdata/dupes/2 | 1 + .../accounts/keystore/testdata/dupes/foo | 1 + .../keystore/testdata/keystore/.hiddenfile | 1 + .../keystore/testdata/keystore/README | 21 + ...--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 | 1 + .../accounts/keystore/testdata/keystore/aaa | 1 + .../accounts/keystore/testdata/keystore/empty | 0 .../fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e | 1 + .../keystore/testdata/keystore/garbage | Bin 0 -> 300 bytes .../keystore/testdata/keystore/no-address | 1 + .../accounts/keystore/testdata/keystore/zero | 1 + .../accounts/keystore/testdata/keystore/zzz | 1 + .../cb61d5a9c4896fb9658090b597ef0e7be6f7b67e | 1 + .../keystore/testdata/v1_test_vector.json | 28 + .../keystore/testdata/v3_test_vector.json | 97 + .../keystore/testdata/very-light-scrypt.json | 1 + .../go-ethereum/accounts/keystore/watch.go | 113 + .../accounts/keystore/watch_fallback.go | 28 + .../ethereum/go-ethereum/accounts/manager.go | 198 + .../ethereum/go-ethereum/accounts/url.go | 79 + .../accounts/usbwallet/ledger_hub.go | 192 + .../accounts/usbwallet/ledger_wallet.go | 892 + .../accounts/usbwallet/usbwallet.go | 25 + .../ethereum/go-ethereum/appveyor.yml | 39 + .../ethereum/go-ethereum/circle.yml | 32 + .../ethereum/go-ethereum/cmd/abigen/main.go | 140 + .../ethereum/go-ethereum/cmd/bootnode/main.go | 105 + .../ethereum/go-ethereum/cmd/disasm/main.go | 60 + .../ethereum/go-ethereum/cmd/ethtest/main.go | 222 + .../ethereum/go-ethereum/cmd/evm/main.go | 223 + .../go-ethereum/cmd/geth/accountcmd.go | 351 + .../go-ethereum/cmd/geth/accountcmd_test.go | 292 + .../ethereum/go-ethereum/cmd/geth/bugcmd.go | 108 + .../ethereum/go-ethereum/cmd/geth/chaincmd.go | 338 + .../go-ethereum/cmd/geth/consolecmd.go | 196 + .../go-ethereum/cmd/geth/consolecmd_test.go | 171 + .../ethereum/go-ethereum/cmd/geth/dao_test.go | 164 + .../go-ethereum/cmd/geth/genesis_test.go | 110 + .../ethereum/go-ethereum/cmd/geth/main.go | 306 + .../ethereum/go-ethereum/cmd/geth/misccmd.go | 128 + .../go-ethereum/cmd/geth/monitorcmd.go | 351 + .../ethereum/go-ethereum/cmd/geth/run_test.go | 298 + .../go-ethereum/cmd/geth/testdata/empty.js | 1 + .../cmd/geth/testdata/guswallet.json | 6 + .../cmd/geth/testdata/passwords.txt | 3 + .../cmd/geth/testdata/wrong-passwords.txt | 3 + .../ethereum/go-ethereum/cmd/geth/usage.go | 225 + .../go-ethereum/cmd/gethrpctest/main.go | 162 + .../cmd/internal/browser/browser.go | 46 + .../ethereum/go-ethereum/cmd/rlpdump/main.go | 143 + .../ethereum/go-ethereum/cmd/swarm/cleandb.go | 39 + .../ethereum/go-ethereum/cmd/swarm/hash.go | 49 + .../ethereum/go-ethereum/cmd/swarm/main.go | 408 + .../go-ethereum/cmd/swarm/manifest.go | 360 + .../ethereum/go-ethereum/cmd/swarm/upload.go | 257 + .../ethereum/go-ethereum/cmd/utils/cmd.go | 242 + .../go-ethereum/cmd/utils/customflags.go | 147 + .../go-ethereum/cmd/utils/customflags_test.go | 41 + .../go-ethereum/cmd/utils/fdlimit_freebsd.go | 54 + .../go-ethereum/cmd/utils/fdlimit_test.go | 35 + .../go-ethereum/cmd/utils/fdlimit_unix.go | 50 + .../go-ethereum/cmd/utils/fdlimit_windows.go | 41 + .../ethereum/go-ethereum/cmd/utils/flags.go | 948 + .../ethereum/go-ethereum/cmd/wnode/main.go | 535 + .../ethereum/go-ethereum/common/.gitignore | 12 + .../ethereum/go-ethereum/common/.travis.yml | 3 + .../ethereum/go-ethereum/common/README.md | 140 + .../ethereum/go-ethereum/common/big.go | 156 + .../ethereum/go-ethereum/common/big_test.go | 89 + .../ethereum/go-ethereum/common/bytes.go | 266 + .../ethereum/go-ethereum/common/bytes_test.go | 196 + .../go-ethereum/common/compiler/solidity.go | 186 + .../common/compiler/solidity_test.go | 106 + .../ethereum/go-ethereum/common/debug.go | 52 + .../ethereum/go-ethereum/common/format.go | 40 + .../go-ethereum/common/hexutil/hexutil.go | 227 + .../common/hexutil/hexutil_test.go | 187 + .../go-ethereum/common/hexutil/json.go | 266 + .../go-ethereum/common/hexutil/json_test.go | 258 + .../ethereum/go-ethereum/common/icap.go | 190 + .../ethereum/go-ethereum/common/icap_test.go | 91 + .../ethereum/go-ethereum/common/list.go | 97 + .../ethereum/go-ethereum/common/main_test.go | 25 + .../ethereum/go-ethereum/common/math/dist.go | 96 + .../go-ethereum/common/math/dist_test.go | 82 + .../ethereum/go-ethereum/common/math/exp.go | 47 + .../go-ethereum/common/math/integer.go | 25 + .../go-ethereum/common/math/integer_test.go | 50 + .../go-ethereum/common/mclock/mclock.go | 30 + .../ethereum/go-ethereum/common/number/int.go | 197 + .../go-ethereum/common/number/uint_test.go | 108 + .../ethereum/go-ethereum/common/path.go | 47 + .../ethereum/go-ethereum/common/size.go | 89 + .../ethereum/go-ethereum/common/size_test.go | 59 + .../ethereum/go-ethereum/common/test_utils.go | 53 + .../ethereum/go-ethereum/common/types.go | 165 + .../go-ethereum/common/types_template.go | 64 + .../ethereum/go-ethereum/common/types_test.go | 97 + .../go-ethereum/compression/rle/read_write.go | 101 + .../compression/rle/read_write_test.go | 134 + .../ethereum/go-ethereum/console/bridge.go | 312 + .../ethereum/go-ethereum/console/console.go | 419 + .../go-ethereum/console/console_test.go | 338 + .../ethereum/go-ethereum/console/prompter.go | 165 + .../go-ethereum/console/testdata/exec.js | 1 + .../go-ethereum/console/testdata/preload.js | 1 + .../docker/develop-alpine/Dockerfile | 14 + .../docker/develop-ubuntu/Dockerfile | 15 + .../docker/master-alpine/Dockerfile | 14 + .../docker/master-ubuntu/Dockerfile | 15 + .../containers/vagrant/Vagrantfile | 29 + .../go-ethereum/contracts/chequebook/api.go | 68 + .../contracts/chequebook/cheque.go | 642 + .../contracts/chequebook/cheque_test.go | 531 + .../chequebook/contract/chequebook.go | 541 + .../chequebook/contract/chequebook.sol | 45 + .../contracts/chequebook/contract/code.go | 5 + .../contracts/chequebook/gencode.go | 71 + .../go-ethereum/contracts/ens/README.md | 20 + .../go-ethereum/contracts/ens/contract/ens.go | 921 + .../contracts/ens/contract/ens.sol | 226 + .../ethereum/go-ethereum/contracts/ens/ens.go | 178 + .../go-ethereum/contracts/ens/ens_test.go | 67 + .../go-ethereum/contracts/release/contract.go | 432 + .../contracts/release/contract.sol | 249 + .../contracts/release/contract_test.go | 374 + .../go-ethereum/contracts/release/release.go | 162 + .../ethereum/go-ethereum/core/.gitignore | 12 + .../ethereum/go-ethereum/core/asm.go | 64 + .../ethereum/go-ethereum/core/bench_test.go | 299 + .../go-ethereum/core/block_validator.go | 384 + .../go-ethereum/core/block_validator_test.go | 94 + .../ethereum/go-ethereum/core/blockchain.go | 1392 + .../go-ethereum/core/blockchain_test.go | 1298 + .../ethereum/go-ethereum/core/blocks.go | 25 + .../ethereum/go-ethereum/core/chain_makers.go | 292 + .../go-ethereum/core/chain_makers_test.go | 101 + .../ethereum/go-ethereum/core/chain_pow.go | 87 + .../go-ethereum/core/chain_pow_test.go | 234 + .../ethereum/go-ethereum/core/dao.go | 74 + .../ethereum/go-ethereum/core/dao_test.go | 133 + .../go-ethereum/core/database_util.go | 701 + .../go-ethereum/core/database_util_test.go | 605 + .../go-ethereum/core/default_genesis.go | 28 + .../ethereum/go-ethereum/core/error.go | 203 + .../ethereum/go-ethereum/core/events.go | 80 + .../ethereum/go-ethereum/core/evm.go | 73 + .../ethereum/go-ethereum/core/fees.go | 23 + .../ethereum/go-ethereum/core/filter_test.go | 17 + .../ethereum/go-ethereum/core/gaspool.go | 46 + .../ethereum/go-ethereum/core/genesis.go | 216 + .../ethereum/go-ethereum/core/headerchain.go | 532 + .../ethereum/go-ethereum/core/helper_test.go | 96 + .../ethereum/go-ethereum/core/state/dump.go | 80 + .../go-ethereum/core/state/iterator.go | 159 + .../go-ethereum/core/state/iterator_test.go | 57 + .../go-ethereum/core/state/journal.go | 136 + .../go-ethereum/core/state/main_test.go | 25 + .../go-ethereum/core/state/managed_state.go | 143 + .../core/state/managed_state_test.go | 124 + .../go-ethereum/core/state/state_object.go | 383 + .../go-ethereum/core/state/state_test.go | 237 + .../go-ethereum/core/state/statedb.go | 664 + .../go-ethereum/core/state/statedb_test.go | 375 + .../ethereum/go-ethereum/core/state/sync.go | 71 + .../go-ethereum/core/state/sync_test.go | 323 + .../go-ethereum/core/state_processor.go | 148 + .../go-ethereum/core/state_transition.go | 296 + .../ethereum/go-ethereum/core/tx_list.go | 342 + .../ethereum/go-ethereum/core/tx_list_test.go | 52 + .../ethereum/go-ethereum/core/tx_pool.go | 766 + .../ethereum/go-ethereum/core/tx_pool_test.go | 884 + .../ethereum/go-ethereum/core/types.go | 62 + .../ethereum/go-ethereum/core/types/block.go | 519 + .../go-ethereum/core/types/block_test.go | 70 + .../ethereum/go-ethereum/core/types/bloom9.go | 130 + .../go-ethereum/core/types/bloom9_test.go | 81 + .../go-ethereum/core/types/derive_sha.go | 41 + .../ethereum/go-ethereum/core/types/log.go | 184 + .../go-ethereum/core/types/log_test.go | 131 + .../go-ethereum/core/types/receipt.go | 191 + .../go-ethereum/core/types/transaction.go | 516 + .../core/types/transaction_signing.go | 312 + .../core/types/transaction_signing_test.go | 138 + .../core/types/transaction_test.go | 234 + .../ethereum/go-ethereum/core/vm/analysis.go | 63 + .../ethereum/go-ethereum/core/vm/asm.go | 61 + .../ethereum/go-ethereum/core/vm/common.go | 63 + .../ethereum/go-ethereum/core/vm/contract.go | 153 + .../ethereum/go-ethereum/core/vm/contracts.go | 136 + .../ethereum/go-ethereum/core/vm/disasm.go | 37 + .../ethereum/go-ethereum/core/vm/doc.go | 35 + .../ethereum/go-ethereum/core/vm/errors.go | 27 + .../ethereum/go-ethereum/core/vm/evm.go | 297 + .../ethereum/go-ethereum/core/vm/gas.go | 58 + .../ethereum/go-ethereum/core/vm/gas_table.go | 416 + .../go-ethereum/core/vm/gas_table_test.go | 24 + .../go-ethereum/core/vm/instructions.go | 736 + .../go-ethereum/core/vm/int_pool_verifier.go | 15 + .../core/vm/int_pool_verifier_empty.go | 7 + .../ethereum/go-ethereum/core/vm/interface.go | 78 + .../go-ethereum/core/vm/interpreter.go | 208 + .../ethereum/go-ethereum/core/vm/intpool.go | 49 + .../go-ethereum/core/vm/jump_table.go | 862 + .../ethereum/go-ethereum/core/vm/logger.go | 211 + .../go-ethereum/core/vm/logger_test.go | 94 + .../ethereum/go-ethereum/core/vm/memory.go | 104 + .../go-ethereum/core/vm/memory_table.go | 68 + .../ethereum/go-ethereum/core/vm/noop.go | 70 + .../ethereum/go-ethereum/core/vm/opcodes.go | 509 + .../go-ethereum/core/vm/runtime/doc.go | 18 + .../go-ethereum/core/vm/runtime/env.go | 44 + .../go-ethereum/core/vm/runtime/fuzz.go | 20 + .../go-ethereum/core/vm/runtime/runtime.go | 174 + .../core/vm/runtime/runtime_example_test.go | 34 + .../core/vm/runtime/runtime_test.go | 151 + .../ethereum/go-ethereum/core/vm/stack.go | 93 + .../go-ethereum/core/vm/stack_table.go | 28 + .../ethereum/go-ethereum/core/vm/vm_jit.go | 389 + .../go-ethereum/core/vm/vm_jit_fake.go | 19 + .../ethereum/go-ethereum/crypto/crypto.go | 169 + .../go-ethereum/crypto/crypto_test.go | 218 + .../go-ethereum/crypto/ecies/.gitignore | 24 + .../ethereum/go-ethereum/crypto/ecies/LICENSE | 28 + .../ethereum/go-ethereum/crypto/ecies/README | 94 + .../ethereum/go-ethereum/crypto/ecies/asn1.go | 584 + .../go-ethereum/crypto/ecies/ecies.go | 364 + .../go-ethereum/crypto/ecies/ecies_test.go | 659 + .../go-ethereum/crypto/ecies/params.go | 210 + .../crypto/randentropy/rand_entropy.go | 42 + .../go-ethereum/crypto/secp256k1/.gitignore | 24 + .../go-ethereum/crypto/secp256k1/curve.go | 310 + .../crypto/secp256k1/curve_test.go | 39 + .../go-ethereum/crypto/secp256k1/ext.h | 87 + .../crypto/secp256k1/libsecp256k1/.gitignore | 49 + .../crypto/secp256k1/libsecp256k1/.travis.yml | 69 + .../crypto/secp256k1/libsecp256k1/COPYING | 19 + .../crypto/secp256k1/libsecp256k1/Makefile.am | 177 + .../crypto/secp256k1/libsecp256k1/README.md | 61 + .../crypto/secp256k1/libsecp256k1/TODO | 3 + .../crypto/secp256k1/libsecp256k1/autogen.sh | 3 + .../build-aux/m4/ax_jni_include_dir.m4 | 140 + .../build-aux/m4/ax_prog_cc_for_build.m4 | 125 + .../libsecp256k1/build-aux/m4/bitcoin_secp.m4 | 69 + .../secp256k1/libsecp256k1/configure.ac | 493 + .../libsecp256k1/contrib/lax_der_parsing.c | 150 + .../libsecp256k1/contrib/lax_der_parsing.h | 91 + .../contrib/lax_der_privatekey_parsing.c | 113 + .../contrib/lax_der_privatekey_parsing.h | 90 + .../libsecp256k1/include/secp256k1.h | 577 + .../libsecp256k1/include/secp256k1_ecdh.h | 31 + .../libsecp256k1/include/secp256k1_recovery.h | 110 + .../secp256k1/libsecp256k1/libsecp256k1.pc.in | 13 + .../secp256k1/libsecp256k1/obj/.gitignore | 0 .../libsecp256k1/sage/group_prover.sage | 322 + .../libsecp256k1/sage/secp256k1.sage | 306 + .../libsecp256k1/sage/weierstrass_prover.sage | 264 + .../libsecp256k1/src/asm/field_10x26_arm.s | 919 + .../secp256k1/libsecp256k1/src/basic-config.h | 32 + .../crypto/secp256k1/libsecp256k1/src/bench.h | 66 + .../secp256k1/libsecp256k1/src/bench_ecdh.c | 54 + .../libsecp256k1/src/bench_internal.c | 382 + .../libsecp256k1/src/bench_recover.c | 60 + .../libsecp256k1/src/bench_schnorr_verify.c | 73 + .../secp256k1/libsecp256k1/src/bench_sign.c | 56 + .../secp256k1/libsecp256k1/src/bench_verify.c | 112 + .../crypto/secp256k1/libsecp256k1/src/ecdsa.h | 21 + .../secp256k1/libsecp256k1/src/ecdsa_impl.h | 315 + .../crypto/secp256k1/libsecp256k1/src/eckey.h | 25 + .../secp256k1/libsecp256k1/src/eckey_impl.h | 99 + .../secp256k1/libsecp256k1/src/ecmult.h | 31 + .../secp256k1/libsecp256k1/src/ecmult_const.h | 15 + .../libsecp256k1/src/ecmult_const_impl.h | 239 + .../secp256k1/libsecp256k1/src/ecmult_gen.h | 43 + .../libsecp256k1/src/ecmult_gen_impl.h | 210 + .../secp256k1/libsecp256k1/src/ecmult_impl.h | 406 + .../crypto/secp256k1/libsecp256k1/src/field.h | 132 + .../secp256k1/libsecp256k1/src/field_10x26.h | 47 + .../libsecp256k1/src/field_10x26_impl.h | 1140 + .../secp256k1/libsecp256k1/src/field_5x52.h | 47 + .../libsecp256k1/src/field_5x52_asm_impl.h | 502 + .../libsecp256k1/src/field_5x52_impl.h | 451 + .../libsecp256k1/src/field_5x52_int128_impl.h | 277 + .../secp256k1/libsecp256k1/src/field_impl.h | 315 + .../secp256k1/libsecp256k1/src/gen_context.c | 74 + .../crypto/secp256k1/libsecp256k1/src/group.h | 144 + .../secp256k1/libsecp256k1/src/group_impl.h | 700 + .../crypto/secp256k1/libsecp256k1/src/hash.h | 41 + .../secp256k1/libsecp256k1/src/hash_impl.h | 281 + .../src/java/org/bitcoin/NativeSecp256k1.java | 446 + .../java/org/bitcoin/NativeSecp256k1Test.java | 226 + .../java/org/bitcoin/NativeSecp256k1Util.java | 45 + .../java/org/bitcoin/Secp256k1Context.java | 51 + .../src/java/org_bitcoin_NativeSecp256k1.c | 377 + .../src/java/org_bitcoin_NativeSecp256k1.h | 119 + .../src/java/org_bitcoin_Secp256k1Context.c | 15 + .../src/java/org_bitcoin_Secp256k1Context.h | 22 + .../src/modules/ecdh/Makefile.am.include | 8 + .../libsecp256k1/src/modules/ecdh/main_impl.h | 54 + .../src/modules/ecdh/tests_impl.h | 105 + .../src/modules/recovery/Makefile.am.include | 8 + .../src/modules/recovery/main_impl.h | 193 + .../src/modules/recovery/tests_impl.h | 393 + .../crypto/secp256k1/libsecp256k1/src/num.h | 74 + .../secp256k1/libsecp256k1/src/num_gmp.h | 20 + .../secp256k1/libsecp256k1/src/num_gmp_impl.h | 288 + .../secp256k1/libsecp256k1/src/num_impl.h | 24 + .../secp256k1/libsecp256k1/src/scalar.h | 106 + .../secp256k1/libsecp256k1/src/scalar_4x64.h | 19 + .../libsecp256k1/src/scalar_4x64_impl.h | 949 + .../secp256k1/libsecp256k1/src/scalar_8x32.h | 19 + .../libsecp256k1/src/scalar_8x32_impl.h | 721 + .../secp256k1/libsecp256k1/src/scalar_impl.h | 370 + .../secp256k1/libsecp256k1/src/scalar_low.h | 15 + .../libsecp256k1/src/scalar_low_impl.h | 114 + .../secp256k1/libsecp256k1/src/secp256k1.c | 561 + .../secp256k1/libsecp256k1/src/testrand.h | 38 + .../libsecp256k1/src/testrand_impl.h | 110 + .../crypto/secp256k1/libsecp256k1/src/tests.c | 4525 ++ .../libsecp256k1/src/tests_exhaustive.c | 470 + .../crypto/secp256k1/libsecp256k1/src/util.h | 113 + .../go-ethereum/crypto/secp256k1/panic_cb.go | 33 + .../go-ethereum/crypto/secp256k1/secp256.go | 144 + .../crypto/secp256k1/secp256_test.go | 240 + .../ethereum/go-ethereum/crypto/sha3/LICENSE | 27 + .../ethereum/go-ethereum/crypto/sha3/PATENTS | 22 + .../ethereum/go-ethereum/crypto/sha3/doc.go | 66 + .../go-ethereum/crypto/sha3/hashes.go | 68 + .../go-ethereum/crypto/sha3/keccakf.go | 412 + .../go-ethereum/crypto/sha3/keccakf_amd64.go | 13 + .../go-ethereum/crypto/sha3/keccakf_amd64.s | 392 + .../go-ethereum/crypto/sha3/register.go | 18 + .../ethereum/go-ethereum/crypto/sha3/sha3.go | 193 + .../go-ethereum/crypto/sha3/sha3_test.go | 306 + .../ethereum/go-ethereum/crypto/sha3/shake.go | 60 + .../sha3/testdata/keccakKats.json.deflate | Bin 0 -> 521342 bytes .../ethereum/go-ethereum/crypto/sha3/xor.go | 16 + .../go-ethereum/crypto/sha3/xor_generic.go | 28 + .../go-ethereum/crypto/sha3/xor_unaligned.go | 58 + .../go-ethereum/crypto/signature_cgo.go | 64 + .../go-ethereum/crypto/signature_nocgo.go | 77 + .../go-ethereum/crypto/signature_test.go | 36 + .../ethereum/go-ethereum/errs/errors.go | 88 + .../ethereum/go-ethereum/errs/errors_test.go | 41 + .../ethereum/go-ethereum/eth/api.go | 574 + .../ethereum/go-ethereum/eth/api_backend.go | 220 + .../ethereum/go-ethereum/eth/backend.go | 528 + .../ethereum/go-ethereum/eth/backend_test.go | 83 + .../ethereum/go-ethereum/eth/bad_block.go | 74 + .../ethereum/go-ethereum/eth/bind.go | 140 + .../ethereum/go-ethereum/eth/db_upgrade.go | 356 + .../go-ethereum/eth/downloader/api.go | 166 + .../go-ethereum/eth/downloader/downloader.go | 1529 + .../eth/downloader/downloader_test.go | 1797 + .../go-ethereum/eth/downloader/events.go | 21 + .../go-ethereum/eth/downloader/metrics.go | 45 + .../go-ethereum/eth/downloader/modes.go | 26 + .../go-ethereum/eth/downloader/peer.go | 564 + .../go-ethereum/eth/downloader/queue.go | 1143 + .../go-ethereum/eth/downloader/types.go | 120 + .../go-ethereum/eth/fetcher/fetcher.go | 751 + .../go-ethereum/eth/fetcher/fetcher_test.go | 780 + .../go-ethereum/eth/fetcher/metrics.go | 43 + .../ethereum/go-ethereum/eth/filters/api.go | 555 + .../go-ethereum/eth/filters/api_test.go | 197 + .../go-ethereum/eth/filters/filter.go | 287 + .../go-ethereum/eth/filters/filter_system.go | 432 + .../eth/filters/filter_system_test.go | 465 + .../go-ethereum/eth/filters/filter_test.go | 289 + .../go-ethereum/eth/gasprice/gasprice.go | 229 + .../go-ethereum/eth/gasprice/lightprice.go | 160 + .../ethereum/go-ethereum/eth/handler.go | 768 + .../ethereum/go-ethereum/eth/handler_test.go | 522 + .../ethereum/go-ethereum/eth/helper_test.go | 191 + .../ethereum/go-ethereum/eth/metrics.go | 139 + .../ethereum/go-ethereum/eth/peer.go | 419 + .../ethereum/go-ethereum/eth/protocol.go | 180 + .../ethereum/go-ethereum/eth/protocol_test.go | 219 + .../ethereum/go-ethereum/eth/sync.go | 194 + .../ethereum/go-ethereum/eth/sync_test.go | 54 + .../go-ethereum/ethclient/ethclient.go | 441 + .../go-ethereum/ethclient/ethclient_test.go | 34 + .../ethereum/go-ethereum/ethdb/.gitignore | 12 + .../ethereum/go-ethereum/ethdb/database.go | 359 + .../go-ethereum/ethdb/database_test.go | 34 + .../ethereum/go-ethereum/ethdb/interface.go | 30 + .../go-ethereum/ethdb/memory_database.go | 125 + .../ethereum/go-ethereum/ethstats/ethstats.go | 608 + .../ethereum/go-ethereum/event/event.go | 211 + .../ethereum/go-ethereum/event/event_test.go | 218 + .../go-ethereum/event/example_feed_test.go | 73 + .../go-ethereum/event/example_scope_test.go | 128 + .../event/example_subscription_test.go | 56 + .../go-ethereum/event/example_test.go | 58 + .../ethereum/go-ethereum/event/feed.go | 243 + .../ethereum/go-ethereum/event/feed_test.go | 294 + .../go-ethereum/event/filter/filter.go | 95 + .../go-ethereum/event/filter/filter_test.go | 60 + .../event/filter/generic_filter.go | 48 + .../go-ethereum/event/subscription.go | 275 + .../go-ethereum/event/subscription_test.go | 121 + .../ethereum/go-ethereum/interfaces.go | 210 + .../go-ethereum/internal/build/archive.go | 185 + .../go-ethereum/internal/build/azure.go | 65 + .../go-ethereum/internal/build/env.go | 122 + .../go-ethereum/internal/build/pgp.go | 59 + .../go-ethereum/internal/build/util.go | 147 + .../go-ethereum/internal/debug/api.go | 207 + .../go-ethereum/internal/debug/flags.go | 123 + .../go-ethereum/internal/debug/loudpanic.go | 27 + .../internal/debug/loudpanic_fallback.go | 24 + .../go-ethereum/internal/debug/trace.go | 64 + .../internal/debug/trace_fallback.go | 31 + .../go-ethereum/internal/ethapi/api.go | 1463 + .../go-ethereum/internal/ethapi/backend.go | 120 + .../go-ethereum/internal/ethapi/solc.go | 82 + .../go-ethereum/internal/ethapi/tracer.go | 318 + .../internal/ethapi/tracer_test.go | 147 + .../go-ethereum/internal/guide/guide.go | 18 + .../go-ethereum/internal/guide/guide_test.go | 101 + .../go-ethereum/internal/jsre/completion.go | 74 + .../internal/jsre/completion_test.go | 88 + .../internal/jsre/deps/bignumber.js | 4 + .../go-ethereum/internal/jsre/deps/bindata.go | 258 + .../go-ethereum/internal/jsre/deps/deps.go | 20 + .../go-ethereum/internal/jsre/deps/web3.js | 16151 ++++++ .../go-ethereum/internal/jsre/jsre.go | 335 + .../go-ethereum/internal/jsre/jsre_test.go | 137 + .../go-ethereum/internal/jsre/pretty.go | 280 + .../go-ethereum/internal/web3ext/web3ext.go | 527 + .../ethereum/go-ethereum/les/api_backend.go | 155 + .../ethereum/go-ethereum/les/backend.go | 212 + .../ethereum/go-ethereum/les/fetcher.go | 719 + .../go-ethereum/les/flowcontrol/control.go | 240 + .../go-ethereum/les/flowcontrol/manager.go | 224 + .../ethereum/go-ethereum/les/handler.go | 907 + .../ethereum/go-ethereum/les/handler_test.go | 338 + .../ethereum/go-ethereum/les/helper_test.go | 363 + .../ethereum/go-ethereum/les/metrics.go | 111 + .../ethereum/go-ethereum/les/odr.go | 249 + .../ethereum/go-ethereum/les/odr_requests.go | 353 + .../ethereum/go-ethereum/les/odr_test.go | 203 + .../ethereum/go-ethereum/les/peer.go | 536 + .../ethereum/go-ethereum/les/protocol.go | 198 + .../ethereum/go-ethereum/les/randselect.go | 173 + .../go-ethereum/les/randselect_test.go | 67 + .../ethereum/go-ethereum/les/request_test.go | 112 + .../ethereum/go-ethereum/les/server.go | 408 + .../ethereum/go-ethereum/les/serverpool.go | 826 + .../ethereum/go-ethereum/les/sync.go | 84 + .../ethereum/go-ethereum/les/txrelay.go | 156 + .../ethereum/go-ethereum/light/lightchain.go | 518 + .../go-ethereum/light/lightchain_test.go | 404 + .../ethereum/go-ethereum/light/odr.go | 159 + .../ethereum/go-ethereum/light/odr_test.go | 300 + .../ethereum/go-ethereum/light/odr_util.go | 187 + .../ethereum/go-ethereum/light/state.go | 322 + .../go-ethereum/light/state_object.go | 289 + .../ethereum/go-ethereum/light/state_test.go | 248 + .../ethereum/go-ethereum/light/trie.go | 122 + .../ethereum/go-ethereum/light/txpool.go | 545 + .../ethereum/go-ethereum/light/txpool_test.go | 145 + .../ethereum/go-ethereum/light/vm_env.go | 194 + .../ethereum/go-ethereum/log/CONTRIBUTORS | 11 + .../ethereum/go-ethereum/log/LICENSE | 13 + .../ethereum/go-ethereum/log/README.md | 77 + .../go-ethereum/log/README_ETHEREUM.md | 4 + .../ethereum/go-ethereum/log/doc.go | 333 + .../ethereum/go-ethereum/log/format.go | 279 + .../ethereum/go-ethereum/log/handler.go | 356 + .../ethereum/go-ethereum/log/handler_go13.go | 26 + .../ethereum/go-ethereum/log/handler_go14.go | 23 + .../ethereum/go-ethereum/log/logger.go | 208 + .../ethereum/go-ethereum/log/root.go | 67 + .../ethereum/go-ethereum/log/syslog.go | 55 + .../ethereum/go-ethereum/log/term/LICENSE | 21 + .../log/term/terminal_appengine.go | 13 + .../go-ethereum/log/term/terminal_darwin.go | 13 + .../go-ethereum/log/term/terminal_freebsd.go | 18 + .../go-ethereum/log/term/terminal_linux.go | 14 + .../go-ethereum/log/term/terminal_netbsd.go | 7 + .../log/term/terminal_notwindows.go | 20 + .../go-ethereum/log/term/terminal_openbsd.go | 7 + .../go-ethereum/log/term/terminal_solaris.go | 9 + .../go-ethereum/log/term/terminal_windows.go | 26 + .../ethereum/go-ethereum/logger/glog/LICENSE | 191 + .../ethereum/go-ethereum/logger/glog/README | 44 + .../ethereum/go-ethereum/logger/glog/glog.go | 1223 + .../go-ethereum/logger/glog/glog_file.go | 128 + .../go-ethereum/logger/glog/glog_test.go | 436 + .../ethereum/go-ethereum/logger/verbosity.go | 27 + .../ethereum/go-ethereum/metrics/disk.go | 25 + .../go-ethereum/metrics/disk_linux.go | 72 + .../ethereum/go-ethereum/metrics/disk_nop.go | 26 + .../ethereum/go-ethereum/metrics/metrics.go | 123 + .../ethereum/go-ethereum/miner/agent.go | 125 + .../ethereum/go-ethereum/miner/miner.go | 198 + .../go-ethereum/miner/remote_agent.go | 195 + .../ethereum/go-ethereum/miner/unconfirmed.go | 118 + .../go-ethereum/miner/unconfirmed_test.go | 85 + .../ethereum/go-ethereum/miner/worker.go | 651 + .../ethereum/go-ethereum/mobile/accounts.go | 200 + .../go-ethereum/mobile/android_test.go | 232 + .../ethereum/go-ethereum/mobile/big.go | 95 + .../ethereum/go-ethereum/mobile/big_go1.7.go | 26 + .../ethereum/go-ethereum/mobile/bind.go | 183 + .../ethereum/go-ethereum/mobile/common.go | 187 + .../ethereum/go-ethereum/mobile/context.go | 81 + .../ethereum/go-ethereum/mobile/discover.go | 104 + .../ethereum/go-ethereum/mobile/doc.go | 61 + .../ethereum/go-ethereum/mobile/ethclient.go | 305 + .../ethereum/go-ethereum/mobile/ethereum.go | 125 + .../ethereum/go-ethereum/mobile/geth.go | 210 + .../go-ethereum/mobile/geth_android.go | 22 + .../ethereum/go-ethereum/mobile/geth_ios.go | 22 + .../ethereum/go-ethereum/mobile/geth_other.go | 22 + .../ethereum/go-ethereum/mobile/init.go | 35 + .../ethereum/go-ethereum/mobile/interface.go | 148 + .../ethereum/go-ethereum/mobile/logger.go | 26 + .../ethereum/go-ethereum/mobile/p2p.go | 74 + .../ethereum/go-ethereum/mobile/params.go | 92 + .../ethereum/go-ethereum/mobile/primitives.go | 54 + .../ethereum/go-ethereum/mobile/types.go | 194 + .../ethereum/go-ethereum/mobile/vm.go | 56 + .../ethereum/go-ethereum/node/api.go | 335 + .../ethereum/go-ethereum/node/config.go | 450 + .../ethereum/go-ethereum/node/config_test.go | 149 + .../ethereum/go-ethereum/node/defaults.go | 60 + .../ethereum/go-ethereum/node/doc.go | 90 + .../ethereum/go-ethereum/node/errors.go | 45 + .../ethereum/go-ethereum/node/node.go | 685 + .../go-ethereum/node/node_example_test.go | 90 + .../ethereum/go-ethereum/node/node_test.go | 575 + .../ethereum/go-ethereum/node/service.go | 87 + .../ethereum/go-ethereum/node/service_test.go | 97 + .../ethereum/go-ethereum/node/utils_test.go | 135 + .../ethereum/go-ethereum/p2p/dial.go | 377 + .../ethereum/go-ethereum/p2p/dial_test.go | 564 + .../go-ethereum/p2p/discover/database.go | 369 + .../go-ethereum/p2p/discover/database_test.go | 380 + .../ethereum/go-ethereum/p2p/discover/node.go | 373 + .../go-ethereum/p2p/discover/node_test.go | 305 + .../ethereum/go-ethereum/p2p/discover/ntp.go | 127 + .../go-ethereum/p2p/discover/table.go | 682 + .../go-ethereum/p2p/discover/table_test.go | 615 + .../ethereum/go-ethereum/p2p/discover/udp.go | 636 + .../go-ethereum/p2p/discover/udp_test.go | 562 + .../go-ethereum/p2p/discv5/database.go | 413 + .../go-ethereum/p2p/discv5/database_test.go | 380 + .../ethereum/go-ethereum/p2p/discv5/net.go | 1274 + .../go-ethereum/p2p/discv5/net_test.go | 374 + .../ethereum/go-ethereum/p2p/discv5/node.go | 419 + .../go-ethereum/p2p/discv5/node_test.go | 305 + .../p2p/discv5/nodeevent_string.go | 27 + .../ethereum/go-ethereum/p2p/discv5/ntp.go | 127 + .../go-ethereum/p2p/discv5/sim_run_test.go | 126 + .../go-ethereum/p2p/discv5/sim_test.go | 464 + .../p2p/discv5/sim_testmain_test.go | 43 + .../ethereum/go-ethereum/p2p/discv5/table.go | 323 + .../go-ethereum/p2p/discv5/table_test.go | 337 + .../ethereum/go-ethereum/p2p/discv5/ticket.go | 955 + .../ethereum/go-ethereum/p2p/discv5/topic.go | 406 + .../go-ethereum/p2p/discv5/topic_test.go | 71 + .../ethereum/go-ethereum/p2p/discv5/udp.go | 459 + .../go-ethereum/p2p/discv5/udp_test.go | 455 + .../ethereum/go-ethereum/p2p/message.go | 273 + .../ethereum/go-ethereum/p2p/message_test.go | 152 + .../ethereum/go-ethereum/p2p/metrics.go | 71 + .../ethereum/go-ethereum/p2p/nat/nat.go | 245 + .../ethereum/go-ethereum/p2p/nat/nat_test.go | 63 + .../ethereum/go-ethereum/p2p/nat/natpmp.go | 131 + .../ethereum/go-ethereum/p2p/nat/natupnp.go | 177 + .../go-ethereum/p2p/nat/natupnp_test.go | 244 + .../ethereum/go-ethereum/p2p/netutil/error.go | 25 + .../go-ethereum/p2p/netutil/error_test.go | 73 + .../ethereum/go-ethereum/p2p/netutil/net.go | 166 + .../go-ethereum/p2p/netutil/net_test.go | 173 + .../p2p/netutil/toobig_notwindows.go | 26 + .../go-ethereum/p2p/netutil/toobig_windows.go | 40 + .../ethereum/go-ethereum/p2p/peer.go | 410 + .../ethereum/go-ethereum/p2p/peer_error.go | 113 + .../ethereum/go-ethereum/p2p/peer_test.go | 309 + .../ethereum/go-ethereum/p2p/protocol.go | 81 + .../ethereum/go-ethereum/p2p/rlpx.go | 694 + .../ethereum/go-ethereum/p2p/rlpx_test.go | 599 + .../ethereum/go-ethereum/p2p/server.go | 811 + .../ethereum/go-ethereum/p2p/server_test.go | 497 + .../ethereum/go-ethereum/params/bootnodes.go | 45 + .../ethereum/go-ethereum/params/config.go | 150 + .../ethereum/go-ethereum/params/dao.go | 418 + .../ethereum/go-ethereum/params/gas_table.go | 77 + .../go-ethereum/params/protocol_params.go | 75 + .../ethereum/go-ethereum/params/util.go | 43 + .../ethereum/go-ethereum/params/version.go | 35 + .../ethereum/go-ethereum/pow/block.go | 37 + .../ethereum/go-ethereum/pow/pow.go | 24 + .../ethereum/go-ethereum/rlp/decode.go | 1037 + .../go-ethereum/rlp/decode_tail_test.go | 49 + .../ethereum/go-ethereum/rlp/decode_test.go | 791 + .../ethereum/go-ethereum/rlp/doc.go | 33 + .../ethereum/go-ethereum/rlp/encode.go | 652 + .../ethereum/go-ethereum/rlp/encode_test.go | 340 + .../go-ethereum/rlp/encoder_example_test.go | 54 + .../ethereum/go-ethereum/rlp/raw.go | 156 + .../ethereum/go-ethereum/rlp/raw_test.go | 195 + .../ethereum/go-ethereum/rlp/typecache.go | 150 + .../ethereum/go-ethereum/rpc/client.go | 779 + .../go-ethereum/rpc/client_context_go1.4.go | 60 + .../go-ethereum/rpc/client_context_go1.5.go | 61 + .../go-ethereum/rpc/client_context_go1.6.go | 55 + .../go-ethereum/rpc/client_context_go1.7.go | 51 + .../go-ethereum/rpc/client_example_test.go | 88 + .../ethereum/go-ethereum/rpc/client_test.go | 540 + .../ethereum/go-ethereum/rpc/doc.go | 98 + .../ethereum/go-ethereum/rpc/errors.go | 66 + .../ethereum/go-ethereum/rpc/http.go | 176 + .../ethereum/go-ethereum/rpc/inproc.go | 34 + .../ethereum/go-ethereum/rpc/ipc.go | 55 + .../ethereum/go-ethereum/rpc/ipc_unix.go | 47 + .../ethereum/go-ethereum/rpc/ipc_windows.go | 48 + .../ethereum/go-ethereum/rpc/json.go | 358 + .../ethereum/go-ethereum/rpc/json_test.go | 178 + .../ethereum/go-ethereum/rpc/server.go | 452 + .../ethereum/go-ethereum/rpc/server_test.go | 163 + .../ethereum/go-ethereum/rpc/subscription.go | 135 + .../go-ethereum/rpc/subscription_test.go | 165 + .../ethereum/go-ethereum/rpc/types.go | 189 + .../ethereum/go-ethereum/rpc/utils.go | 263 + .../ethereum/go-ethereum/rpc/utils_test.go | 43 + .../ethereum/go-ethereum/rpc/websocket.go | 152 + .../ethereum/go-ethereum/swarm/api/api.go | 205 + .../go-ethereum/swarm/api/api_test.go | 117 + .../ethereum/go-ethereum/swarm/api/config.go | 146 + .../go-ethereum/swarm/api/config_test.go | 121 + .../go-ethereum/swarm/api/filesystem.go | 285 + .../go-ethereum/swarm/api/filesystem_test.go | 188 + .../swarm/api/http/roundtripper.go | 67 + .../swarm/api/http/roundtripper_test.go | 66 + .../go-ethereum/swarm/api/http/server.go | 340 + .../go-ethereum/swarm/api/http/server_test.go | 136 + .../go-ethereum/swarm/api/manifest.go | 340 + .../go-ethereum/swarm/api/manifest_test.go | 80 + .../ethereum/go-ethereum/swarm/api/storage.go | 70 + .../go-ethereum/swarm/api/storage_test.go | 49 + .../ethereum/go-ethereum/swarm/api/testapi.go | 46 + .../swarm/api/testdata/test0/img/logo.png | Bin 0 -> 18136 bytes .../swarm/api/testdata/test0/index.css | 9 + .../swarm/api/testdata/test0/index.html | 10 + .../go-ethereum/swarm/network/depo.go | 217 + .../go-ethereum/swarm/network/forwarding.go | 150 + .../go-ethereum/swarm/network/hive.go | 388 + .../swarm/network/kademlia/address.go | 173 + .../swarm/network/kademlia/address_test.go | 96 + .../swarm/network/kademlia/kaddb.go | 351 + .../swarm/network/kademlia/kademlia.go | 429 + .../swarm/network/kademlia/kademlia_test.go | 392 + .../go-ethereum/swarm/network/messages.go | 317 + .../go-ethereum/swarm/network/protocol.go | 551 + .../swarm/network/protocol_test.go | 17 + .../go-ethereum/swarm/network/syncdb.go | 390 + .../go-ethereum/swarm/network/syncdb_test.go | 223 + .../go-ethereum/swarm/network/syncer.go | 778 + .../go-ethereum/swarm/services/swap/swap.go | 283 + .../swarm/services/swap/swap/swap.go | 254 + .../swarm/services/swap/swap/swap_test.go | 194 + .../go-ethereum/swarm/storage/chunker.go | 500 + .../go-ethereum/swarm/storage/chunker_test.go | 315 + .../go-ethereum/swarm/storage/common_test.go | 117 + .../go-ethereum/swarm/storage/database.go | 99 + .../go-ethereum/swarm/storage/dbstore.go | 516 + .../go-ethereum/swarm/storage/dbstore_test.go | 191 + .../ethereum/go-ethereum/swarm/storage/dpa.go | 244 + .../go-ethereum/swarm/storage/dpa_test.go | 143 + .../go-ethereum/swarm/storage/localstore.go | 79 + .../go-ethereum/swarm/storage/memstore.go | 328 + .../swarm/storage/memstore_test.go | 50 + .../go-ethereum/swarm/storage/netstore.go | 139 + .../go-ethereum/swarm/storage/pyramid.go | 201 + .../go-ethereum/swarm/storage/types.go | 233 + .../ethereum/go-ethereum/swarm/swarm.go | 323 + .../ethereum/go-ethereum/trie/encoding.go | 128 + .../go-ethereum/trie/encoding_test.go | 129 + .../ethereum/go-ethereum/trie/errors.go | 51 + .../ethereum/go-ethereum/trie/hasher.go | 173 + .../ethereum/go-ethereum/trie/iterator.go | 365 + .../go-ethereum/trie/iterator_test.go | 175 + .../ethereum/go-ethereum/trie/node.go | 224 + .../ethereum/go-ethereum/trie/node_test.go | 58 + .../ethereum/go-ethereum/trie/proof.go | 151 + .../ethereum/go-ethereum/trie/proof_test.go | 155 + .../ethereum/go-ethereum/trie/secure_trie.go | 214 + .../go-ethereum/trie/secure_trie_test.go | 145 + .../ethereum/go-ethereum/trie/sync.go | 285 + .../ethereum/go-ethereum/trie/sync_test.go | 333 + .../ethereum/go-ethereum/trie/trie.go | 511 + .../ethereum/go-ethereum/trie/trie_test.go | 599 + .../Azure/azure-sdk-for-go/CHANGELOG.md | 280 + .../github.com/Azure/azure-sdk-for-go/LICENSE | 202 + .../Azure/azure-sdk-for-go/README.md | 102 + .../Azure/azure-sdk-for-go/glide.lock | 53 + .../Azure/azure-sdk-for-go/glide.yaml | 12 + .../Azure/azure-sdk-for-go/storage/README.md | 5 + .../Azure/azure-sdk-for-go/storage/blob.go | 1278 + .../Azure/azure-sdk-for-go/storage/client.go | 551 + .../Azure/azure-sdk-for-go/storage/file.go | 352 + .../Azure/azure-sdk-for-go/storage/queue.go | 306 + .../Azure/azure-sdk-for-go/storage/table.go | 129 + .../storage/table_entities.go | 355 + .../Azure/azure-sdk-for-go/storage/util.go | 85 + .../aristanetworks/goarista/.travis.yml | 16 + .../aristanetworks/goarista/AUTHORS | 25 + .../aristanetworks/goarista/COPYING | 177 + .../aristanetworks/goarista/Dockerfile | 14 + .../aristanetworks/goarista/Makefile | 58 + .../aristanetworks/goarista/README.md | 62 + .../goarista/check_line_len.awk | 25 + .../aristanetworks/goarista/iptables.sh | 21 + .../goarista/monotime/issue15006.s | 6 + .../goarista/monotime/nanotime.go | 31 + .../aristanetworks/goarista/rpmbuild.sh | 38 + .../vendor/github.com/btcsuite/btcd/LICENSE | 16 + .../github.com/btcsuite/btcd/btcec/README.md | 74 + .../github.com/btcsuite/btcd/btcec/btcec.go | 956 + .../btcsuite/btcd/btcec/ciphering.go | 216 + .../github.com/btcsuite/btcd/btcec/doc.go | 21 + .../github.com/btcsuite/btcd/btcec/field.go | 1262 + .../btcsuite/btcd/btcec/genprecomps.go | 63 + .../btcsuite/btcd/btcec/gensecp256k1.go | 203 + .../btcsuite/btcd/btcec/precompute.go | 67 + .../github.com/btcsuite/btcd/btcec/privkey.go | 73 + .../github.com/btcsuite/btcd/btcec/pubkey.go | 172 + .../btcsuite/btcd/btcec/secp256k1.go | 10 + .../btcsuite/btcd/btcec/signature.go | 539 + .../vendor/github.com/cespare/cp/LICENSE.txt | 19 + .../vendor/github.com/cespare/cp/README.md | 9 + .../vendor/github.com/cespare/cp/cp.go | 58 + .../github.com/davecgh/go-spew/.gitignore | 22 + .../github.com/davecgh/go-spew/.travis.yml | 14 + .../vendor/github.com/davecgh/go-spew/LICENSE | 15 + .../github.com/davecgh/go-spew/README.md | 205 + .../github.com/davecgh/go-spew/cov_report.sh | 22 + .../github.com/davecgh/go-spew/spew/bypass.go | 152 + .../davecgh/go-spew/spew/bypasssafe.go | 38 + .../github.com/davecgh/go-spew/spew/common.go | 341 + .../github.com/davecgh/go-spew/spew/config.go | 306 + .../github.com/davecgh/go-spew/spew/doc.go | 211 + .../github.com/davecgh/go-spew/spew/dump.go | 509 + .../github.com/davecgh/go-spew/spew/format.go | 419 + .../github.com/davecgh/go-spew/spew/spew.go | 148 + .../davecgh/go-spew/test_coverage.txt | 61 + .../github.com/ethereum/ethash/CMakeLists.txt | 14 + .../github.com/ethereum/ethash/MANIFEST.in | 17 + .../github.com/ethereum/ethash/Makefile | 6 + .../github.com/ethereum/ethash/README.md | 22 + .../github.com/ethereum/ethash/Vagrantfile | 7 + .../github.com/ethereum/ethash/appveyor.yml | 43 + .../github.com/ethereum/ethash/ethash.go | 441 + .../github.com/ethereum/ethash/ethashc.go | 51 + .../github.com/ethereum/ethash/setup.py | 47 + .../ethash/src/libethash/CMakeLists.txt | 44 + .../ethereum/ethash/src/libethash/compiler.h | 33 + .../ethash/src/libethash/data_sizes.h | 812 + .../ethereum/ethash/src/libethash/endian.h | 78 + .../ethereum/ethash/src/libethash/ethash.h | 147 + .../ethereum/ethash/src/libethash/fnv.h | 43 + .../ethereum/ethash/src/libethash/internal.c | 507 + .../ethereum/ethash/src/libethash/internal.h | 179 + .../ethereum/ethash/src/libethash/io.c | 119 + .../ethereum/ethash/src/libethash/io.h | 202 + .../ethereum/ethash/src/libethash/io_posix.c | 111 + .../ethereum/ethash/src/libethash/io_win32.c | 100 + .../ethereum/ethash/src/libethash/mmap.h | 47 + .../ethash/src/libethash/mmap_win32.c | 84 + .../ethereum/ethash/src/libethash/sha3.c | 151 + .../ethereum/ethash/src/libethash/sha3.h | 31 + .../ethash/src/libethash/sha3_cryptopp.cpp | 37 + .../ethash/src/libethash/sha3_cryptopp.h | 18 + .../ethereum/ethash/src/libethash/util.h | 47 + .../ethash/src/libethash/util_win32.c | 38 + .../vendor/github.com/fatih/color/LICENSE.md | 20 + .../vendor/github.com/fatih/color/README.md | 175 + .../vendor/github.com/fatih/color/color.go | 525 + .../vendor/github.com/fatih/color/doc.go | 128 + .../vendor/github.com/gizak/termui/LICENSE | 22 + .../vendor/github.com/gizak/termui/README.md | 151 + .../github.com/gizak/termui/barchart.go | 149 + .../vendor/github.com/gizak/termui/block.go | 240 + .../github.com/gizak/termui/block_common.go | 20 + .../github.com/gizak/termui/block_windows.go | 14 + .../vendor/github.com/gizak/termui/buffer.go | 106 + .../vendor/github.com/gizak/termui/canvas.go | 72 + .../vendor/github.com/gizak/termui/config.py | 54 + .../vendor/github.com/gizak/termui/doc.go | 29 + .../vendor/github.com/gizak/termui/events.go | 324 + .../vendor/github.com/gizak/termui/gauge.go | 109 + .../vendor/github.com/gizak/termui/glide.lock | 30 + .../vendor/github.com/gizak/termui/glide.yaml | 9 + .../vendor/github.com/gizak/termui/grid.go | 279 + .../vendor/github.com/gizak/termui/helper.go | 222 + .../github.com/gizak/termui/linechart.go | 331 + .../gizak/termui/linechart_others.go | 11 + .../gizak/termui/linechart_windows.go | 11 + .../vendor/github.com/gizak/termui/list.go | 89 + .../github.com/gizak/termui/mbarchart.go | 242 + .../vendor/github.com/gizak/termui/mkdocs.yml | 28 + .../vendor/github.com/gizak/termui/par.go | 73 + .../vendor/github.com/gizak/termui/pos.go | 78 + .../vendor/github.com/gizak/termui/render.go | 164 + .../github.com/gizak/termui/sparkline.go | 167 + .../vendor/github.com/gizak/termui/table.go | 185 + .../github.com/gizak/termui/textbuilder.go | 278 + .../vendor/github.com/gizak/termui/theme.go | 140 + .../vendor/github.com/gizak/termui/widget.go | 94 + .../github.com/go-stack/stack/LICENSE.md | 13 + .../github.com/go-stack/stack/README.md | 38 + .../vendor/github.com/go-stack/stack/stack.go | 349 + .../vendor/github.com/golang/snappy/AUTHORS | 15 + .../github.com/golang/snappy/CONTRIBUTORS | 37 + .../vendor/github.com/golang/snappy/LICENSE | 27 + .../vendor/github.com/golang/snappy/README | 107 + .../vendor/github.com/golang/snappy/decode.go | 237 + .../github.com/golang/snappy/decode_amd64.go | 14 + .../github.com/golang/snappy/decode_amd64.s | 490 + .../github.com/golang/snappy/decode_other.go | 101 + .../vendor/github.com/golang/snappy/encode.go | 285 + .../github.com/golang/snappy/encode_amd64.go | 29 + .../github.com/golang/snappy/encode_amd64.s | 730 + .../github.com/golang/snappy/encode_other.go | 238 + .../vendor/github.com/golang/snappy/snappy.go | 87 + .../github.com/hashicorp/golang-lru/2q.go | 212 + .../github.com/hashicorp/golang-lru/LICENSE | 362 + .../github.com/hashicorp/golang-lru/README.md | 25 + .../github.com/hashicorp/golang-lru/arc.go | 257 + .../github.com/hashicorp/golang-lru/lru.go | 114 + .../hashicorp/golang-lru/simplelru/lru.go | 160 + .../vendor/github.com/huin/goupnp/LICENSE | 23 + .../vendor/github.com/huin/goupnp/README.md | 44 + .../dcps/internetgateway1/internetgateway1.go | 3717 ++ .../dcps/internetgateway2/internetgateway2.go | 5378 ++ .../vendor/github.com/huin/goupnp/device.go | 184 + .../vendor/github.com/huin/goupnp/goupnp.go | 131 + .../github.com/huin/goupnp/httpu/httpu.go | 132 + .../github.com/huin/goupnp/httpu/serve.go | 108 + .../github.com/huin/goupnp/scpd/scpd.go | 167 + .../github.com/huin/goupnp/service_client.go | 88 + .../github.com/huin/goupnp/soap/soap.go | 157 + .../github.com/huin/goupnp/soap/types.go | 519 + .../github.com/huin/goupnp/ssdp/registry.go | 312 + .../github.com/huin/goupnp/ssdp/ssdp.go | 83 + .../github.com/jackpal/go-nat-pmp/LICENSE | 13 + .../github.com/jackpal/go-nat-pmp/README.md | 52 + .../github.com/jackpal/go-nat-pmp/natpmp.go | 153 + .../github.com/jackpal/go-nat-pmp/network.go | 89 + .../github.com/jackpal/go-nat-pmp/recorder.go | 19 + .../vendor/github.com/karalabe/hid/LICENSE.md | 8 + .../vendor/github.com/karalabe/hid/README.md | 41 + .../vendor/github.com/karalabe/hid/hid.go | 37 + .../github.com/karalabe/hid/hid_disabled.go | 53 + .../github.com/karalabe/hid/hid_enabled.go | 216 + .../karalabe/hid/hidapi/AUTHORS.txt | 16 + .../karalabe/hid/hidapi/LICENSE-bsd.txt | 26 + .../karalabe/hid/hidapi/LICENSE-gpl3.txt | 674 + .../karalabe/hid/hidapi/LICENSE-orig.txt | 9 + .../karalabe/hid/hidapi/LICENSE.txt | 13 + .../github.com/karalabe/hid/hidapi/README.txt | 339 + .../karalabe/hid/hidapi/hidapi/hidapi.h | 391 + .../karalabe/hid/hidapi/libusb/hid.c | 1512 + .../github.com/karalabe/hid/hidapi/mac/hid.c | 1110 + .../karalabe/hid/hidapi/windows/hid.c | 944 + .../github.com/karalabe/hid/libusb/AUTHORS | 89 + .../github.com/karalabe/hid/libusb/COPYING | 504 + .../karalabe/hid/libusb/libusb/config.h | 3 + .../karalabe/hid/libusb/libusb/core.c | 2523 + .../karalabe/hid/libusb/libusb/descriptor.c | 1191 + .../karalabe/hid/libusb/libusb/hotplug.c | 350 + .../karalabe/hid/libusb/libusb/hotplug.h | 90 + .../karalabe/hid/libusb/libusb/io.c | 2819 + .../karalabe/hid/libusb/libusb/libusb.h | 2008 + .../karalabe/hid/libusb/libusb/libusbi.h | 1149 + .../hid/libusb/libusb/os/darwin_usb.c | 2094 + .../hid/libusb/libusb/os/darwin_usb.h | 164 + .../hid/libusb/libusb/os/haiku_pollfs.cpp | 367 + .../karalabe/hid/libusb/libusb/os/haiku_usb.h | 112 + .../libusb/libusb/os/haiku_usb_backend.cpp | 517 + .../hid/libusb/libusb/os/haiku_usb_raw.cpp | 250 + .../hid/libusb/libusb/os/haiku_usb_raw.h | 180 + .../hid/libusb/libusb/os/linux_netlink.c | 400 + .../hid/libusb/libusb/os/linux_udev.c | 311 + .../hid/libusb/libusb/os/linux_usbfs.c | 2738 + .../hid/libusb/libusb/os/linux_usbfs.h | 193 + .../hid/libusb/libusb/os/netbsd_usb.c | 677 + .../hid/libusb/libusb/os/openbsd_usb.c | 771 + .../hid/libusb/libusb/os/poll_posix.c | 53 + .../hid/libusb/libusb/os/poll_posix.h | 11 + .../hid/libusb/libusb/os/poll_windows.c | 728 + .../hid/libusb/libusb/os/poll_windows.h | 131 + .../karalabe/hid/libusb/libusb/os/sunos_usb.c | 1292 + .../karalabe/hid/libusb/libusb/os/sunos_usb.h | 74 + .../hid/libusb/libusb/os/threads_posix.c | 79 + .../hid/libusb/libusb/os/threads_posix.h | 55 + .../hid/libusb/libusb/os/threads_windows.c | 259 + .../hid/libusb/libusb/os/threads_windows.h | 76 + .../karalabe/hid/libusb/libusb/os/wince_usb.c | 899 + .../karalabe/hid/libusb/libusb/os/wince_usb.h | 126 + .../hid/libusb/libusb/os/windows_common.h | 124 + .../hid/libusb/libusb/os/windows_nt_common.c | 591 + .../hid/libusb/libusb/os/windows_nt_common.h | 63 + .../hid/libusb/libusb/os/windows_usbdk.c | 905 + .../hid/libusb/libusb/os/windows_usbdk.h | 146 + .../hid/libusb/libusb/os/windows_winusb.c | 4290 ++ .../hid/libusb/libusb/os/windows_winusb.h | 937 + .../karalabe/hid/libusb/libusb/strerror.c | 202 + .../karalabe/hid/libusb/libusb/sync.c | 327 + .../karalabe/hid/libusb/libusb/version.h | 18 + .../karalabe/hid/libusb/libusb/version_nano.h | 1 + .../vendor/github.com/karalabe/hid/wchar.go | 227 + .../github.com/maruel/panicparse/.travis.yml | 15 + .../github.com/maruel/panicparse/LICENSE | 201 + .../github.com/maruel/panicparse/README.md | 123 + .../maruel/panicparse/stack/source.go | 291 + .../maruel/panicparse/stack/stack.go | 832 + .../github.com/maruel/panicparse/stack/ui.go | 139 + .../github.com/maruel/panicparse/vendor.yml | 17 + .../github.com/mattn/go-colorable/LICENSE | 21 + .../github.com/mattn/go-colorable/README.md | 43 + .../mattn/go-colorable/colorable_others.go | 27 + .../mattn/go-colorable/colorable_windows.go | 820 + .../mattn/go-colorable/noncolorable.go | 61 + .../vendor/github.com/mattn/go-isatty/LICENSE | 9 + .../github.com/mattn/go-isatty/README.md | 47 + .../vendor/github.com/mattn/go-isatty/doc.go | 2 + .../mattn/go-isatty/isatty_appengine.go | 9 + .../github.com/mattn/go-isatty/isatty_bsd.go | 18 + .../mattn/go-isatty/isatty_linux.go | 18 + .../mattn/go-isatty/isatty_not_windows.go | 9 + .../mattn/go-isatty/isatty_solaris.go | 16 + .../mattn/go-isatty/isatty_windows.go | 94 + .../github.com/mattn/go-runewidth/LICENSE | 21 + .../github.com/mattn/go-runewidth/README.mkd | 27 + .../mattn/go-runewidth/runewidth.go | 1223 + .../mattn/go-runewidth/runewidth_js.go | 8 + .../mattn/go-runewidth/runewidth_posix.go | 77 + .../mattn/go-runewidth/runewidth_windows.go | 25 + .../mitchellh/go-wordwrap/LICENSE.md | 21 + .../mitchellh/go-wordwrap/README.md | 39 + .../mitchellh/go-wordwrap/wordwrap.go | 73 + .../vendor/github.com/nsf/termbox-go/AUTHORS | 4 + .../vendor/github.com/nsf/termbox-go/LICENSE | 19 + .../github.com/nsf/termbox-go/README.md | 31 + .../vendor/github.com/nsf/termbox-go/api.go | 457 + .../github.com/nsf/termbox-go/api_common.go | 187 + .../github.com/nsf/termbox-go/api_windows.go | 239 + .../nsf/termbox-go/collect_terminfo.py | 110 + .../github.com/nsf/termbox-go/syscalls.go | 39 + .../nsf/termbox-go/syscalls_darwin.go | 41 + .../nsf/termbox-go/syscalls_darwin_amd64.go | 40 + .../nsf/termbox-go/syscalls_dragonfly.go | 39 + .../nsf/termbox-go/syscalls_freebsd.go | 39 + .../nsf/termbox-go/syscalls_linux.go | 33 + .../nsf/termbox-go/syscalls_netbsd.go | 39 + .../nsf/termbox-go/syscalls_openbsd.go | 39 + .../nsf/termbox-go/syscalls_windows.go | 61 + .../github.com/nsf/termbox-go/termbox.go | 511 + .../nsf/termbox-go/termbox_common.go | 59 + .../nsf/termbox-go/termbox_windows.go | 856 + .../github.com/nsf/termbox-go/terminfo.go | 221 + .../nsf/termbox-go/terminfo_builtin.go | 64 + .../github.com/pborman/uuid/CONTRIBUTING.md | 10 + .../github.com/pborman/uuid/CONTRIBUTORS | 1 + .../vendor/github.com/pborman/uuid/LICENSE | 27 + .../vendor/github.com/pborman/uuid/README.md | 13 + .../vendor/github.com/pborman/uuid/dce.go | 84 + .../vendor/github.com/pborman/uuid/doc.go | 8 + .../vendor/github.com/pborman/uuid/hash.go | 53 + .../vendor/github.com/pborman/uuid/marshal.go | 83 + .../vendor/github.com/pborman/uuid/node.go | 117 + .../vendor/github.com/pborman/uuid/sql.go | 66 + .../vendor/github.com/pborman/uuid/time.go | 132 + .../vendor/github.com/pborman/uuid/util.go | 43 + .../vendor/github.com/pborman/uuid/uuid.go | 201 + .../github.com/pborman/uuid/version1.go | 41 + .../github.com/pborman/uuid/version4.go | 25 + .../vendor/github.com/peterh/liner/COPYING | 21 + .../vendor/github.com/peterh/liner/README.md | 100 + .../github.com/peterh/liner/bsdinput.go | 41 + .../vendor/github.com/peterh/liner/common.go | 250 + .../github.com/peterh/liner/fallbackinput.go | 57 + .../vendor/github.com/peterh/liner/input.go | 364 + .../github.com/peterh/liner/input_darwin.go | 43 + .../github.com/peterh/liner/input_linux.go | 28 + .../github.com/peterh/liner/input_windows.go | 339 + .../vendor/github.com/peterh/liner/line.go | 1070 + .../vendor/github.com/peterh/liner/output.go | 79 + .../github.com/peterh/liner/output_windows.go | 76 + .../github.com/peterh/liner/unixmode.go | 37 + .../vendor/github.com/peterh/liner/width.go | 99 + .../github.com/rcrowley/go-metrics/LICENSE | 29 + .../github.com/rcrowley/go-metrics/README.md | 153 + .../github.com/rcrowley/go-metrics/counter.go | 112 + .../github.com/rcrowley/go-metrics/debug.go | 76 + .../github.com/rcrowley/go-metrics/ewma.go | 118 + .../github.com/rcrowley/go-metrics/exp/exp.go | 156 + .../github.com/rcrowley/go-metrics/gauge.go | 120 + .../rcrowley/go-metrics/gauge_float64.go | 127 + .../rcrowley/go-metrics/graphite.go | 113 + .../rcrowley/go-metrics/healthcheck.go | 61 + .../rcrowley/go-metrics/histogram.go | 202 + .../github.com/rcrowley/go-metrics/json.go | 87 + .../github.com/rcrowley/go-metrics/log.go | 80 + .../github.com/rcrowley/go-metrics/memory.md | 285 + .../github.com/rcrowley/go-metrics/meter.go | 233 + .../github.com/rcrowley/go-metrics/metrics.go | 13 + .../rcrowley/go-metrics/opentsdb.go | 119 + .../rcrowley/go-metrics/registry.go | 270 + .../github.com/rcrowley/go-metrics/runtime.go | 212 + .../rcrowley/go-metrics/runtime_cgo.go | 10 + .../go-metrics/runtime_gccpufraction.go | 9 + .../rcrowley/go-metrics/runtime_no_cgo.go | 7 + .../go-metrics/runtime_no_gccpufraction.go | 9 + .../github.com/rcrowley/go-metrics/sample.go | 616 + .../github.com/rcrowley/go-metrics/syslog.go | 78 + .../github.com/rcrowley/go-metrics/timer.go | 311 + .../rcrowley/go-metrics/validate.sh | 10 + .../github.com/rcrowley/go-metrics/writer.go | 100 + .../github.com/rjeczalik/notify/AUTHORS | 10 + .../github.com/rjeczalik/notify/LICENSE | 21 + .../github.com/rjeczalik/notify/README.md | 21 + .../github.com/rjeczalik/notify/appveyor.yml | 23 + .../github.com/rjeczalik/notify/debug.go | 11 + .../rjeczalik/notify/debug_debug.go | 43 + .../vendor/github.com/rjeczalik/notify/doc.go | 40 + .../github.com/rjeczalik/notify/event.go | 143 + .../github.com/rjeczalik/notify/event_fen.go | 57 + .../rjeczalik/notify/event_fsevents.go | 71 + .../rjeczalik/notify/event_inotify.go | 75 + .../rjeczalik/notify/event_kqueue.go | 59 + .../rjeczalik/notify/event_readdcw.go | 108 + .../github.com/rjeczalik/notify/event_stub.go | 31 + .../rjeczalik/notify/event_trigger.go | 22 + .../github.com/rjeczalik/notify/node.go | 272 + .../github.com/rjeczalik/notify/notify.go | 74 + .../github.com/rjeczalik/notify/tree.go | 22 + .../rjeczalik/notify/tree_nonrecursive.go | 292 + .../rjeczalik/notify/tree_recursive.go | 354 + .../github.com/rjeczalik/notify/util.go | 150 + .../github.com/rjeczalik/notify/watcher.go | 85 + .../rjeczalik/notify/watcher_fen.go | 168 + .../rjeczalik/notify/watcher_fen_cgo.go | 141 + .../rjeczalik/notify/watcher_fsevents.go | 319 + .../rjeczalik/notify/watcher_fsevents_cgo.go | 190 + .../rjeczalik/notify/watcher_inotify.go | 396 + .../rjeczalik/notify/watcher_kqueue.go | 193 + .../rjeczalik/notify/watcher_readdcw.go | 574 + .../rjeczalik/notify/watcher_stub.go | 23 + .../rjeczalik/notify/watcher_trigger.go | 432 + .../github.com/rjeczalik/notify/watchpoint.go | 103 + .../rjeczalik/notify/watchpoint_other.go | 23 + .../rjeczalik/notify/watchpoint_readdcw.go | 38 + .../robertkrimen/otto/DESIGN.markdown | 1 + .../github.com/robertkrimen/otto/LICENSE | 7 + .../github.com/robertkrimen/otto/Makefile | 63 + .../robertkrimen/otto/README.markdown | 871 + .../robertkrimen/otto/ast/README.markdown | 1068 + .../robertkrimen/otto/ast/comments.go | 278 + .../github.com/robertkrimen/otto/ast/node.go | 515 + .../github.com/robertkrimen/otto/builtin.go | 354 + .../robertkrimen/otto/builtin_array.go | 681 + .../robertkrimen/otto/builtin_boolean.go | 28 + .../robertkrimen/otto/builtin_date.go | 615 + .../robertkrimen/otto/builtin_error.go | 126 + .../robertkrimen/otto/builtin_function.go | 129 + .../robertkrimen/otto/builtin_json.go | 299 + .../robertkrimen/otto/builtin_math.go | 151 + .../robertkrimen/otto/builtin_number.go | 93 + .../robertkrimen/otto/builtin_object.go | 289 + .../robertkrimen/otto/builtin_regexp.go | 65 + .../robertkrimen/otto/builtin_string.go | 500 + .../github.com/robertkrimen/otto/clone.go | 173 + .../github.com/robertkrimen/otto/cmpl.go | 24 + .../robertkrimen/otto/cmpl_evaluate.go | 96 + .../otto/cmpl_evaluate_expression.go | 460 + .../otto/cmpl_evaluate_statement.go | 424 + .../robertkrimen/otto/cmpl_parse.go | 656 + .../github.com/robertkrimen/otto/console.go | 51 + .../github.com/robertkrimen/otto/dbg.go | 9 + .../github.com/robertkrimen/otto/dbg/dbg.go | 387 + .../github.com/robertkrimen/otto/error.go | 252 + .../github.com/robertkrimen/otto/evaluate.go | 318 + .../robertkrimen/otto/file/README.markdown | 110 + .../github.com/robertkrimen/otto/file/file.go | 164 + .../github.com/robertkrimen/otto/global.go | 221 + .../github.com/robertkrimen/otto/inline.go | 6649 +++ .../github.com/robertkrimen/otto/inline.pl | 1086 + .../github.com/robertkrimen/otto/object.go | 156 + .../robertkrimen/otto/object_class.go | 493 + .../github.com/robertkrimen/otto/otto.go | 770 + .../github.com/robertkrimen/otto/otto_.go | 178 + .../robertkrimen/otto/parser/Makefile | 4 + .../robertkrimen/otto/parser/README.markdown | 190 + .../robertkrimen/otto/parser/dbg.go | 9 + .../robertkrimen/otto/parser/error.go | 175 + .../robertkrimen/otto/parser/expression.go | 1005 + .../robertkrimen/otto/parser/lexer.go | 866 + .../robertkrimen/otto/parser/parser.go | 344 + .../robertkrimen/otto/parser/regexp.go | 358 + .../robertkrimen/otto/parser/scope.go | 44 + .../robertkrimen/otto/parser/statement.go | 938 + .../github.com/robertkrimen/otto/property.go | 220 + .../otto/registry/README.markdown | 51 + .../robertkrimen/otto/registry/registry.go | 47 + .../github.com/robertkrimen/otto/result.go | 30 + .../github.com/robertkrimen/otto/runtime.go | 711 + .../github.com/robertkrimen/otto/scope.go | 35 + .../github.com/robertkrimen/otto/script.go | 119 + .../github.com/robertkrimen/otto/stash.go | 296 + .../robertkrimen/otto/token/Makefile | 2 + .../robertkrimen/otto/token/README.markdown | 171 + .../robertkrimen/otto/token/token.go | 116 + .../robertkrimen/otto/token/token_const.go | 349 + .../robertkrimen/otto/token/tokenfmt | 222 + .../robertkrimen/otto/type_arguments.go | 106 + .../robertkrimen/otto/type_array.go | 109 + .../robertkrimen/otto/type_boolean.go | 13 + .../github.com/robertkrimen/otto/type_date.go | 299 + .../robertkrimen/otto/type_error.go | 24 + .../robertkrimen/otto/type_function.go | 292 + .../robertkrimen/otto/type_go_array.go | 134 + .../robertkrimen/otto/type_go_map.go | 87 + .../robertkrimen/otto/type_go_slice.go | 126 + .../robertkrimen/otto/type_go_struct.go | 146 + .../robertkrimen/otto/type_number.go | 5 + .../robertkrimen/otto/type_reference.go | 103 + .../robertkrimen/otto/type_regexp.go | 146 + .../robertkrimen/otto/type_string.go | 112 + .../github.com/robertkrimen/otto/value.go | 1033 + .../robertkrimen/otto/value_boolean.go | 43 + .../robertkrimen/otto/value_number.go | 324 + .../robertkrimen/otto/value_primitive.go | 23 + .../robertkrimen/otto/value_string.go | 103 + .../vendor/github.com/rs/cors/LICENSE | 19 + .../vendor/github.com/rs/cors/README.md | 99 + .../vendor/github.com/rs/cors/cors.go | 412 + .../vendor/github.com/rs/cors/utils.go | 70 + .../vendor/github.com/rs/xhandler/LICENSE | 19 + .../vendor/github.com/rs/xhandler/README.md | 134 + .../vendor/github.com/rs/xhandler/chain.go | 121 + .../github.com/rs/xhandler/middleware.go | 59 + .../vendor/github.com/rs/xhandler/xhandler.go | 42 + .../github.com/syndtr/goleveldb/.travis.yml | 12 + .../github.com/syndtr/goleveldb/LICENSE | 24 + .../github.com/syndtr/goleveldb/README.md | 105 + .../syndtr/goleveldb/leveldb/batch.go | 349 + .../syndtr/goleveldb/leveldb/cache/cache.go | 705 + .../syndtr/goleveldb/leveldb/cache/lru.go | 195 + .../syndtr/goleveldb/leveldb/comparer.go | 67 + .../leveldb/comparer/bytes_comparer.go | 51 + .../goleveldb/leveldb/comparer/comparer.go | 57 + .../github.com/syndtr/goleveldb/leveldb/db.go | 1091 + .../syndtr/goleveldb/leveldb/db_compaction.go | 826 + .../syndtr/goleveldb/leveldb/db_iter.go | 360 + .../syndtr/goleveldb/leveldb/db_snapshot.go | 183 + .../syndtr/goleveldb/leveldb/db_state.go | 234 + .../goleveldb/leveldb/db_transaction.go | 325 + .../syndtr/goleveldb/leveldb/db_util.go | 102 + .../syndtr/goleveldb/leveldb/db_write.go | 443 + .../syndtr/goleveldb/leveldb/doc.go | 90 + .../syndtr/goleveldb/leveldb/errors.go | 20 + .../syndtr/goleveldb/leveldb/errors/errors.go | 78 + .../syndtr/goleveldb/leveldb/filter.go | 31 + .../syndtr/goleveldb/leveldb/filter/bloom.go | 116 + .../syndtr/goleveldb/leveldb/filter/filter.go | 60 + .../goleveldb/leveldb/iterator/array_iter.go | 184 + .../leveldb/iterator/indexed_iter.go | 242 + .../syndtr/goleveldb/leveldb/iterator/iter.go | 132 + .../goleveldb/leveldb/iterator/merged_iter.go | 304 + .../goleveldb/leveldb/journal/journal.go | 524 + .../syndtr/goleveldb/leveldb/key.go | 143 + .../syndtr/goleveldb/leveldb/memdb/memdb.go | 475 + .../syndtr/goleveldb/leveldb/opt/options.go | 684 + .../syndtr/goleveldb/leveldb/options.go | 107 + .../syndtr/goleveldb/leveldb/session.go | 210 + .../goleveldb/leveldb/session_compaction.go | 302 + .../goleveldb/leveldb/session_record.go | 323 + .../syndtr/goleveldb/leveldb/session_util.go | 271 + .../goleveldb/leveldb/storage/file_storage.go | 583 + .../leveldb/storage/file_storage_nacl.go | 34 + .../leveldb/storage/file_storage_plan9.go | 65 + .../leveldb/storage/file_storage_solaris.go | 81 + .../leveldb/storage/file_storage_unix.go | 86 + .../leveldb/storage/file_storage_windows.go | 78 + .../goleveldb/leveldb/storage/mem_storage.go | 218 + .../goleveldb/leveldb/storage/storage.go | 179 + .../syndtr/goleveldb/leveldb/table.go | 529 + .../syndtr/goleveldb/leveldb/table/reader.go | 1134 + .../syndtr/goleveldb/leveldb/table/table.go | 177 + .../syndtr/goleveldb/leveldb/table/writer.go | 375 + .../syndtr/goleveldb/leveldb/util.go | 98 + .../syndtr/goleveldb/leveldb/util/buffer.go | 293 + .../goleveldb/leveldb/util/buffer_pool.go | 239 + .../syndtr/goleveldb/leveldb/util/crc32.go | 30 + .../syndtr/goleveldb/leveldb/util/hash.go | 48 + .../syndtr/goleveldb/leveldb/util/range.go | 32 + .../syndtr/goleveldb/leveldb/util/util.go | 73 + .../syndtr/goleveldb/leveldb/version.go | 528 + .../vendor/golang.org/x/crypto/.gitattributes | 10 + .../vendor/golang.org/x/crypto/.gitignore | 2 + .../vendor/golang.org/x/crypto/AUTHORS | 3 + .../golang.org/x/crypto/CONTRIBUTING.md | 31 + .../vendor/golang.org/x/crypto/CONTRIBUTORS | 3 + .../vendor/golang.org/x/crypto/LICENSE | 27 + .../vendor/golang.org/x/crypto/PATENTS | 22 + .../vendor/golang.org/x/crypto/README | 3 + .../vendor/golang.org/x/crypto/cast5/cast5.go | 526 + .../vendor/golang.org/x/crypto/codereview.cfg | 1 + .../x/crypto/openpgp/armor/armor.go | 219 + .../x/crypto/openpgp/armor/encode.go | 160 + .../x/crypto/openpgp/canonical_text.go | 59 + .../x/crypto/openpgp/elgamal/elgamal.go | 122 + .../x/crypto/openpgp/errors/errors.go | 72 + .../golang.org/x/crypto/openpgp/keys.go | 637 + .../x/crypto/openpgp/packet/compressed.go | 123 + .../x/crypto/openpgp/packet/config.go | 91 + .../x/crypto/openpgp/packet/encrypted_key.go | 199 + .../x/crypto/openpgp/packet/literal.go | 89 + .../x/crypto/openpgp/packet/ocfb.go | 143 + .../openpgp/packet/one_pass_signature.go | 73 + .../x/crypto/openpgp/packet/opaque.go | 162 + .../x/crypto/openpgp/packet/packet.go | 537 + .../x/crypto/openpgp/packet/private_key.go | 380 + .../x/crypto/openpgp/packet/public_key.go | 748 + .../x/crypto/openpgp/packet/public_key_v3.go | 279 + .../x/crypto/openpgp/packet/reader.go | 76 + .../x/crypto/openpgp/packet/signature.go | 731 + .../x/crypto/openpgp/packet/signature_v3.go | 146 + .../openpgp/packet/symmetric_key_encrypted.go | 155 + .../openpgp/packet/symmetrically_encrypted.go | 290 + .../x/crypto/openpgp/packet/userattribute.go | 91 + .../x/crypto/openpgp/packet/userid.go | 160 + .../golang.org/x/crypto/openpgp/read.go | 442 + .../golang.org/x/crypto/openpgp/s2k/s2k.go | 273 + .../golang.org/x/crypto/openpgp/write.go | 378 + .../golang.org/x/crypto/pbkdf2/pbkdf2.go | 77 + .../x/crypto/ripemd160/ripemd160.go | 120 + .../x/crypto/ripemd160/ripemd160block.go | 161 + .../golang.org/x/crypto/scrypt/scrypt.go | 243 + .../vendor/golang.org/x/net/.gitattributes | 10 + .../vendor/golang.org/x/net/.gitignore | 2 + .../vendor/golang.org/x/net/AUTHORS | 3 + .../vendor/golang.org/x/net/CONTRIBUTING.md | 31 + .../vendor/golang.org/x/net/CONTRIBUTORS | 3 + .../vendor/golang.org/x/net/LICENSE | 27 + .../vendor/golang.org/x/net/PATENTS | 22 + .../vendor/golang.org/x/net/README | 3 + .../vendor/golang.org/x/net/codereview.cfg | 1 + .../vendor/golang.org/x/net/html/atom/atom.go | 78 + .../vendor/golang.org/x/net/html/atom/gen.go | 648 + .../golang.org/x/net/html/atom/table.go | 713 + .../golang.org/x/net/html/charset/charset.go | 257 + .../vendor/golang.org/x/net/html/const.go | 102 + .../vendor/golang.org/x/net/html/doc.go | 106 + .../vendor/golang.org/x/net/html/doctype.go | 156 + .../vendor/golang.org/x/net/html/entity.go | 2253 + .../vendor/golang.org/x/net/html/escape.go | 258 + .../vendor/golang.org/x/net/html/foreign.go | 226 + .../vendor/golang.org/x/net/html/node.go | 193 + .../vendor/golang.org/x/net/html/parse.go | 2094 + .../vendor/golang.org/x/net/html/render.go | 271 + .../vendor/golang.org/x/net/html/token.go | 1219 + .../golang.org/x/net/websocket/client.go | 106 + .../vendor/golang.org/x/net/websocket/dial.go | 24 + .../vendor/golang.org/x/net/websocket/hybi.go | 583 + .../golang.org/x/net/websocket/server.go | 113 + .../golang.org/x/net/websocket/websocket.go | 448 + .../vendor/golang.org/x/sys/.gitattributes | 10 + .../vendor/golang.org/x/sys/.gitignore | 2 + .../vendor/golang.org/x/sys/AUTHORS | 3 + .../vendor/golang.org/x/sys/CONTRIBUTING.md | 31 + .../vendor/golang.org/x/sys/CONTRIBUTORS | 3 + .../vendor/golang.org/x/sys/LICENSE | 27 + .../vendor/golang.org/x/sys/PATENTS | 22 + .../vendor/golang.org/x/sys/README | 3 + .../vendor/golang.org/x/sys/codereview.cfg | 1 + .../golang.org/x/sys/unix/asm_darwin_386.s | 29 + .../golang.org/x/sys/unix/asm_darwin_amd64.s | 29 + .../golang.org/x/sys/unix/asm_darwin_arm.s | 30 + .../golang.org/x/sys/unix/asm_darwin_arm64.s | 30 + .../x/sys/unix/asm_dragonfly_amd64.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_386.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_amd64.s | 29 + .../golang.org/x/sys/unix/asm_freebsd_arm.s | 29 + .../golang.org/x/sys/unix/asm_linux_386.s | 35 + .../golang.org/x/sys/unix/asm_linux_amd64.s | 29 + .../golang.org/x/sys/unix/asm_linux_arm.s | 29 + .../golang.org/x/sys/unix/asm_linux_arm64.s | 24 + .../golang.org/x/sys/unix/asm_linux_mips64x.s | 28 + .../golang.org/x/sys/unix/asm_linux_mipsx.s | 31 + .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 28 + .../golang.org/x/sys/unix/asm_linux_s390x.s | 28 + .../golang.org/x/sys/unix/asm_netbsd_386.s | 29 + .../golang.org/x/sys/unix/asm_netbsd_amd64.s | 29 + .../golang.org/x/sys/unix/asm_netbsd_arm.s | 29 + .../golang.org/x/sys/unix/asm_openbsd_386.s | 29 + .../golang.org/x/sys/unix/asm_openbsd_amd64.s | 29 + .../golang.org/x/sys/unix/asm_solaris_amd64.s | 17 + .../golang.org/x/sys/unix/bluetooth_linux.go | 35 + .../vendor/golang.org/x/sys/unix/constants.go | 13 + .../vendor/golang.org/x/sys/unix/env_unix.go | 27 + .../vendor/golang.org/x/sys/unix/env_unset.go | 14 + .../vendor/golang.org/x/sys/unix/flock.go | 24 + .../x/sys/unix/flock_linux_32bit.go | 13 + .../vendor/golang.org/x/sys/unix/gccgo.go | 46 + .../vendor/golang.org/x/sys/unix/gccgo_c.c | 41 + .../x/sys/unix/gccgo_linux_amd64.go | 20 + .../x/sys/unix/gccgo_linux_sparc64.go | 20 + .../vendor/golang.org/x/sys/unix/mkall.sh | 294 + .../vendor/golang.org/x/sys/unix/mkerrors.sh | 489 + .../vendor/golang.org/x/sys/unix/mkpost.go | 62 + .../vendor/golang.org/x/sys/unix/mksyscall.pl | 318 + .../x/sys/unix/mksyscall_solaris.pl | 289 + .../golang.org/x/sys/unix/mksysctl_openbsd.pl | 264 + .../golang.org/x/sys/unix/mksysnum_darwin.pl | 39 + .../x/sys/unix/mksysnum_dragonfly.pl | 50 + .../golang.org/x/sys/unix/mksysnum_freebsd.pl | 63 + .../golang.org/x/sys/unix/mksysnum_linux.pl | 68 + .../golang.org/x/sys/unix/mksysnum_netbsd.pl | 58 + .../golang.org/x/sys/unix/mksysnum_openbsd.pl | 50 + .../vendor/golang.org/x/sys/unix/race.go | 30 + .../vendor/golang.org/x/sys/unix/race0.go | 25 + .../golang.org/x/sys/unix/sockcmsg_linux.go | 36 + .../golang.org/x/sys/unix/sockcmsg_unix.go | 103 + .../vendor/golang.org/x/sys/unix/str.go | 26 + .../vendor/golang.org/x/sys/unix/syscall.go | 69 + .../golang.org/x/sys/unix/syscall_bsd.go | 614 + .../golang.org/x/sys/unix/syscall_darwin.go | 509 + .../x/sys/unix/syscall_darwin_386.go | 77 + .../x/sys/unix/syscall_darwin_amd64.go | 79 + .../x/sys/unix/syscall_darwin_arm.go | 71 + .../x/sys/unix/syscall_darwin_arm64.go | 77 + .../x/sys/unix/syscall_dragonfly.go | 411 + .../x/sys/unix/syscall_dragonfly_amd64.go | 61 + .../golang.org/x/sys/unix/syscall_freebsd.go | 682 + .../x/sys/unix/syscall_freebsd_386.go | 61 + .../x/sys/unix/syscall_freebsd_amd64.go | 61 + .../x/sys/unix/syscall_freebsd_arm.go | 61 + .../golang.org/x/sys/unix/syscall_linux.go | 1251 + .../x/sys/unix/syscall_linux_386.go | 399 + .../x/sys/unix/syscall_linux_amd64.go | 152 + .../x/sys/unix/syscall_linux_amd64_gc.go | 13 + .../x/sys/unix/syscall_linux_arm.go | 263 + .../x/sys/unix/syscall_linux_arm64.go | 190 + .../x/sys/unix/syscall_linux_mips64x.go | 208 + .../x/sys/unix/syscall_linux_mipsx.go | 239 + .../x/sys/unix/syscall_linux_ppc64x.go | 135 + .../x/sys/unix/syscall_linux_s390x.go | 328 + .../x/sys/unix/syscall_linux_sparc64.go | 169 + .../golang.org/x/sys/unix/syscall_netbsd.go | 492 + .../x/sys/unix/syscall_netbsd_386.go | 42 + .../x/sys/unix/syscall_netbsd_amd64.go | 42 + .../x/sys/unix/syscall_netbsd_arm.go | 42 + .../golang.org/x/sys/unix/syscall_no_getwd.go | 11 + .../golang.org/x/sys/unix/syscall_openbsd.go | 303 + .../x/sys/unix/syscall_openbsd_386.go | 42 + .../x/sys/unix/syscall_openbsd_amd64.go | 42 + .../golang.org/x/sys/unix/syscall_solaris.go | 725 + .../x/sys/unix/syscall_solaris_amd64.go | 35 + .../golang.org/x/sys/unix/syscall_unix.go | 292 + .../golang.org/x/sys/unix/syscall_unix_gc.go | 15 + .../golang.org/x/sys/unix/types_darwin.go | 250 + .../golang.org/x/sys/unix/types_dragonfly.go | 242 + .../golang.org/x/sys/unix/types_freebsd.go | 353 + .../golang.org/x/sys/unix/types_linux.go | 465 + .../golang.org/x/sys/unix/types_netbsd.go | 232 + .../golang.org/x/sys/unix/types_openbsd.go | 244 + .../golang.org/x/sys/unix/types_solaris.go | 262 + .../x/sys/unix/zerrors_darwin_386.go | 1576 + .../x/sys/unix/zerrors_darwin_amd64.go | 1576 + .../x/sys/unix/zerrors_darwin_arm.go | 1293 + .../x/sys/unix/zerrors_darwin_arm64.go | 1576 + .../x/sys/unix/zerrors_dragonfly_amd64.go | 1530 + .../x/sys/unix/zerrors_freebsd_386.go | 1743 + .../x/sys/unix/zerrors_freebsd_amd64.go | 1748 + .../x/sys/unix/zerrors_freebsd_arm.go | 1729 + .../x/sys/unix/zerrors_linux_386.go | 1851 + .../x/sys/unix/zerrors_linux_amd64.go | 1852 + .../x/sys/unix/zerrors_linux_arm.go | 1775 + .../x/sys/unix/zerrors_linux_arm64.go | 1929 + .../x/sys/unix/zerrors_linux_mips.go | 1827 + .../x/sys/unix/zerrors_linux_mips64.go | 1930 + .../x/sys/unix/zerrors_linux_mips64le.go | 1930 + .../x/sys/unix/zerrors_linux_mipsle.go | 2033 + .../x/sys/unix/zerrors_linux_ppc64.go | 2002 + .../x/sys/unix/zerrors_linux_ppc64le.go | 2001 + .../x/sys/unix/zerrors_linux_s390x.go | 2059 + .../x/sys/unix/zerrors_linux_sparc64.go | 2109 + .../x/sys/unix/zerrors_netbsd_386.go | 1712 + .../x/sys/unix/zerrors_netbsd_amd64.go | 1702 + .../x/sys/unix/zerrors_netbsd_arm.go | 1688 + .../x/sys/unix/zerrors_openbsd_386.go | 1584 + .../x/sys/unix/zerrors_openbsd_amd64.go | 1583 + .../x/sys/unix/zerrors_solaris_amd64.go | 1436 + .../x/sys/unix/zsyscall_darwin_386.go | 1394 + .../x/sys/unix/zsyscall_darwin_amd64.go | 1409 + .../x/sys/unix/zsyscall_darwin_arm.go | 1394 + .../x/sys/unix/zsyscall_darwin_arm64.go | 1394 + .../x/sys/unix/zsyscall_dragonfly_amd64.go | 1382 + .../x/sys/unix/zsyscall_freebsd_386.go | 1617 + .../x/sys/unix/zsyscall_freebsd_amd64.go | 1617 + .../x/sys/unix/zsyscall_freebsd_arm.go | 1617 + .../x/sys/unix/zsyscall_linux_386.go | 1632 + .../x/sys/unix/zsyscall_linux_amd64.go | 1825 + .../x/sys/unix/zsyscall_linux_arm.go | 1734 + .../x/sys/unix/zsyscall_linux_arm64.go | 1708 + .../x/sys/unix/zsyscall_linux_mips.go | 1790 + .../x/sys/unix/zsyscall_linux_mips64.go | 1774 + .../x/sys/unix/zsyscall_linux_mips64le.go | 1774 + .../x/sys/unix/zsyscall_linux_mipsle.go | 1790 + .../x/sys/unix/zsyscall_linux_ppc64.go | 1836 + .../x/sys/unix/zsyscall_linux_ppc64le.go | 1836 + .../x/sys/unix/zsyscall_linux_s390x.go | 1616 + .../x/sys/unix/zsyscall_linux_sparc64.go | 1805 + .../x/sys/unix/zsyscall_netbsd_386.go | 1299 + .../x/sys/unix/zsyscall_netbsd_amd64.go | 1299 + .../x/sys/unix/zsyscall_netbsd_arm.go | 1299 + .../x/sys/unix/zsyscall_openbsd_386.go | 1357 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 1357 + .../x/sys/unix/zsyscall_solaris_amd64.go | 1562 + .../golang.org/x/sys/unix/zsysctl_openbsd.go | 270 + .../x/sys/unix/zsysnum_darwin_386.go | 398 + .../x/sys/unix/zsysnum_darwin_amd64.go | 398 + .../x/sys/unix/zsysnum_darwin_arm.go | 358 + .../x/sys/unix/zsysnum_darwin_arm64.go | 398 + .../x/sys/unix/zsysnum_dragonfly_amd64.go | 304 + .../x/sys/unix/zsysnum_freebsd_386.go | 351 + .../x/sys/unix/zsysnum_freebsd_amd64.go | 351 + .../x/sys/unix/zsysnum_freebsd_arm.go | 351 + .../x/sys/unix/zsysnum_linux_386.go | 355 + .../x/sys/unix/zsysnum_linux_amd64.go | 321 + .../x/sys/unix/zsysnum_linux_arm.go | 356 + .../x/sys/unix/zsysnum_linux_arm64.go | 272 + .../x/sys/unix/zsysnum_linux_mips.go | 359 + .../x/sys/unix/zsysnum_linux_mips64.go | 327 + .../x/sys/unix/zsysnum_linux_mips64le.go | 327 + .../x/sys/unix/zsysnum_linux_mipsle.go | 359 + .../x/sys/unix/zsysnum_linux_ppc64.go | 360 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 353 + .../x/sys/unix/zsysnum_linux_s390x.go | 328 + .../x/sys/unix/zsysnum_linux_sparc64.go | 348 + .../x/sys/unix/zsysnum_netbsd_386.go | 273 + .../x/sys/unix/zsysnum_netbsd_amd64.go | 273 + .../x/sys/unix/zsysnum_netbsd_arm.go | 273 + .../x/sys/unix/zsysnum_openbsd_386.go | 207 + .../x/sys/unix/zsysnum_openbsd_amd64.go | 207 + .../x/sys/unix/zsysnum_solaris_amd64.go | 13 + .../x/sys/unix/ztypes_darwin_386.go | 447 + .../x/sys/unix/ztypes_darwin_amd64.go | 462 + .../x/sys/unix/ztypes_darwin_arm.go | 449 + .../x/sys/unix/ztypes_darwin_arm64.go | 457 + .../x/sys/unix/ztypes_dragonfly_amd64.go | 443 + .../x/sys/unix/ztypes_freebsd_386.go | 502 + .../x/sys/unix/ztypes_freebsd_amd64.go | 505 + .../x/sys/unix/ztypes_freebsd_arm.go | 497 + .../golang.org/x/sys/unix/ztypes_linux_386.go | 644 + .../x/sys/unix/ztypes_linux_amd64.go | 662 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 633 + .../x/sys/unix/ztypes_linux_arm64.go | 641 + .../x/sys/unix/ztypes_linux_mips.go | 651 + .../x/sys/unix/ztypes_linux_mips64.go | 644 + .../x/sys/unix/ztypes_linux_mips64le.go | 644 + .../x/sys/unix/ztypes_linux_mipsle.go | 651 + .../x/sys/unix/ztypes_linux_ppc64.go | 651 + .../x/sys/unix/ztypes_linux_ppc64le.go | 651 + .../x/sys/unix/ztypes_linux_s390x.go | 666 + .../x/sys/unix/ztypes_linux_sparc64.go | 657 + .../x/sys/unix/ztypes_netbsd_386.go | 396 + .../x/sys/unix/ztypes_netbsd_amd64.go | 403 + .../x/sys/unix/ztypes_netbsd_arm.go | 401 + .../x/sys/unix/ztypes_openbsd_386.go | 441 + .../x/sys/unix/ztypes_openbsd_amd64.go | 448 + .../x/sys/unix/ztypes_solaris_amd64.go | 423 + .../vendor/golang.org/x/text/.gitattributes | 10 + .../vendor/golang.org/x/text/.gitignore | 3 + .../vendor/golang.org/x/text/AUTHORS | 3 + .../vendor/golang.org/x/text/CONTRIBUTING.md | 31 + .../vendor/golang.org/x/text/CONTRIBUTORS | 3 + .../vendor/golang.org/x/text/LICENSE | 27 + .../vendor/golang.org/x/text/PATENTS | 22 + .../vendor/golang.org/x/text/README | 23 + .../vendor/golang.org/x/text/codereview.cfg | 1 + .../x/text/encoding/charmap/charmap.go | 209 + .../x/text/encoding/charmap/maketables.go | 556 + .../x/text/encoding/charmap/tables.go | 7410 +++ .../golang.org/x/text/encoding/encoding.go | 335 + .../x/text/encoding/htmlindex/gen.go | 170 + .../x/text/encoding/htmlindex/htmlindex.go | 86 + .../x/text/encoding/htmlindex/map.go | 105 + .../x/text/encoding/htmlindex/tables.go | 352 + .../text/encoding/internal/identifier/gen.go | 137 + .../internal/identifier/identifier.go | 81 + .../text/encoding/internal/identifier/mib.go | 1621 + .../x/text/encoding/internal/internal.go | 75 + .../x/text/encoding/japanese/all.go | 12 + .../x/text/encoding/japanese/eucjp.go | 211 + .../x/text/encoding/japanese/iso2022jp.go | 296 + .../x/text/encoding/japanese/maketables.go | 161 + .../x/text/encoding/japanese/shiftjis.go | 189 + .../x/text/encoding/japanese/tables.go | 26971 ++++++++++ .../x/text/encoding/korean/euckr.go | 178 + .../x/text/encoding/korean/maketables.go | 143 + .../x/text/encoding/korean/tables.go | 34152 ++++++++++++ .../x/text/encoding/simplifiedchinese/all.go | 12 + .../x/text/encoding/simplifiedchinese/gbk.go | 281 + .../encoding/simplifiedchinese/hzgb2312.go | 240 + .../encoding/simplifiedchinese/maketables.go | 161 + .../text/encoding/simplifiedchinese/tables.go | 43999 ++++++++++++++++ .../text/encoding/traditionalchinese/big5.go | 198 + .../encoding/traditionalchinese/maketables.go | 140 + .../encoding/traditionalchinese/tables.go | 37142 +++++++++++++ .../x/text/encoding/unicode/override.go | 82 + .../x/text/encoding/unicode/unicode.go | 434 + .../golang.org/x/text/internal/tag/tag.go | 100 + .../internal/utf8internal/utf8internal.go | 87 + .../golang.org/x/text/language/Makefile | 16 + .../golang.org/x/text/language/common.go | 16 + .../golang.org/x/text/language/coverage.go | 197 + .../golang.org/x/text/language/gen_common.go | 20 + .../golang.org/x/text/language/gen_index.go | 162 + .../golang.org/x/text/language/go1_1.go | 38 + .../golang.org/x/text/language/go1_2.go | 11 + .../golang.org/x/text/language/index.go | 767 + .../golang.org/x/text/language/language.go | 975 + .../golang.org/x/text/language/lookup.go | 396 + .../golang.org/x/text/language/maketables.go | 1648 + .../golang.org/x/text/language/match.go | 841 + .../golang.org/x/text/language/parse.go | 859 + .../golang.org/x/text/language/tables.go | 3547 ++ .../vendor/golang.org/x/text/language/tags.go | 143 + .../vendor/golang.org/x/text/runes/cond.go | 187 + .../vendor/golang.org/x/text/runes/runes.go | 355 + .../golang.org/x/text/transform/transform.go | 705 + .../vendor/golang.org/x/tools/.gitattributes | 10 + .../vendor/golang.org/x/tools/.gitignore | 2 + .../vendor/golang.org/x/tools/AUTHORS | 3 + .../vendor/golang.org/x/tools/CONTRIBUTING.md | 31 + .../vendor/golang.org/x/tools/CONTRIBUTORS | 3 + .../vendor/golang.org/x/tools/LICENSE | 27 + .../vendor/golang.org/x/tools/PATENTS | 22 + .../vendor/golang.org/x/tools/README | 10 + .../vendor/golang.org/x/tools/codereview.cfg | 1 + .../x/tools/go/ast/astutil/enclosing.go | 627 + .../x/tools/go/ast/astutil/imports.go | 450 + .../golang.org/x/tools/go/ast/astutil/util.go | 14 + .../golang.org/x/tools/imports/fastwalk.go | 172 + .../x/tools/imports/fastwalk_dirent_fileno.go | 13 + .../x/tools/imports/fastwalk_dirent_ino.go | 13 + .../x/tools/imports/fastwalk_portable.go | 29 + .../x/tools/imports/fastwalk_unix.go | 122 + .../vendor/golang.org/x/tools/imports/fix.go | 978 + .../golang.org/x/tools/imports/imports.go | 289 + .../golang.org/x/tools/imports/mkindex.go | 173 + .../golang.org/x/tools/imports/mkstdlib.go | 103 + .../golang.org/x/tools/imports/sortimports.go | 212 + .../golang.org/x/tools/imports/zstdlib.go | 9289 ++++ .../vendor/gopkg.in/check.v1/LICENSE | 25 + .../vendor/gopkg.in/check.v1/README.md | 20 + .../go-ethereum/vendor/gopkg.in/check.v1/TODO | 2 + .../vendor/gopkg.in/check.v1/benchmark.go | 187 + .../vendor/gopkg.in/check.v1/check.go | 873 + .../vendor/gopkg.in/check.v1/checkers.go | 458 + .../vendor/gopkg.in/check.v1/helpers.go | 231 + .../vendor/gopkg.in/check.v1/printer.go | 168 + .../vendor/gopkg.in/check.v1/reporter.go | 88 + .../vendor/gopkg.in/check.v1/run.go | 175 + .../vendor/gopkg.in/fatih/set.v0/LICENSE.md | 20 + .../vendor/gopkg.in/fatih/set.v0/README.md | 245 + .../vendor/gopkg.in/fatih/set.v0/set.go | 121 + .../vendor/gopkg.in/fatih/set.v0/set_nots.go | 195 + .../vendor/gopkg.in/fatih/set.v0/set_ts.go | 200 + .../gopkg.in/karalabe/cookiejar.v2/LICENSE | 25 + .../gopkg.in/karalabe/cookiejar.v2/README.md | 109 + .../cookiejar.v2/collections/prque/prque.go | 66 + .../cookiejar.v2/collections/prque/sstack.go | 91 + .../gopkg.in/natefinch/npipe.v2/LICENSE.txt | 8 + .../gopkg.in/natefinch/npipe.v2/README.md | 308 + .../vendor/gopkg.in/natefinch/npipe.v2/doc.go | 50 + .../natefinch/npipe.v2/npipe_windows.go | 531 + .../natefinch/npipe.v2/znpipe_windows_386.go | 124 + .../npipe.v2/znpipe_windows_amd64.go | 124 + .../vendor/gopkg.in/sourcemap.v1/LICENSE | 25 + .../vendor/gopkg.in/sourcemap.v1/Makefile | 4 + .../vendor/gopkg.in/sourcemap.v1/README.md | 35 + .../sourcemap.v1/base64vlq/base64_vlq.go | 92 + .../vendor/gopkg.in/sourcemap.v1/consumer.go | 134 + .../vendor/gopkg.in/sourcemap.v1/sourcemap.go | 157 + .../gopkg.in/urfave/cli.v1/CHANGELOG.md | 392 + .../vendor/gopkg.in/urfave/cli.v1/LICENSE | 21 + .../vendor/gopkg.in/urfave/cli.v1/README.md | 1364 + .../vendor/gopkg.in/urfave/cli.v1/app.go | 492 + .../gopkg.in/urfave/cli.v1/appveyor.yml | 24 + .../vendor/gopkg.in/urfave/cli.v1/category.go | 44 + .../vendor/gopkg.in/urfave/cli.v1/cli.go | 21 + .../vendor/gopkg.in/urfave/cli.v1/command.go | 286 + .../vendor/gopkg.in/urfave/cli.v1/context.go | 276 + .../vendor/gopkg.in/urfave/cli.v1/errors.go | 110 + .../gopkg.in/urfave/cli.v1/flag-types.json | 93 + .../vendor/gopkg.in/urfave/cli.v1/flag.go | 799 + .../gopkg.in/urfave/cli.v1/flag_generated.go | 627 + .../vendor/gopkg.in/urfave/cli.v1/funcs.go | 28 + .../urfave/cli.v1/generate-flag-types | 255 + .../vendor/gopkg.in/urfave/cli.v1/help.go | 294 + .../vendor/gopkg.in/urfave/cli.v1/runtests | 122 + .../ethereum/go-ethereum/vendor/vendor.json | 562 + .../whisper/mailserver/mailserver.go | 190 + .../whisper/mailserver/server_test.go | 185 + .../go-ethereum/whisper/whisperv2/api.go | 411 + .../go-ethereum/whisper/whisperv2/doc.go | 32 + .../go-ethereum/whisper/whisperv2/envelope.go | 147 + .../whisper/whisperv2/envelope_test.go | 158 + .../go-ethereum/whisper/whisperv2/filter.go | 129 + .../whisper/whisperv2/filter_test.go | 215 + .../go-ethereum/whisper/whisperv2/main.go | 106 + .../go-ethereum/whisper/whisperv2/message.go | 158 + .../whisper/whisperv2/message_test.go | 158 + .../go-ethereum/whisper/whisperv2/peer.go | 175 + .../whisper/whisperv2/peer_test.go | 261 + .../go-ethereum/whisper/whisperv2/topic.go | 140 + .../whisper/whisperv2/topic_test.go | 215 + .../go-ethereum/whisper/whisperv2/whisper.go | 378 + .../whisper/whisperv2/whisper_test.go | 216 + .../go-ethereum/whisper/whisperv5/api.go | 470 + .../go-ethereum/whisper/whisperv5/api_test.go | 573 + .../whisper/whisperv5/benchmarks_test.go | 212 + .../go-ethereum/whisper/whisperv5/doc.go | 87 + .../go-ethereum/whisper/whisperv5/envelope.go | 243 + .../go-ethereum/whisper/whisperv5/filter.go | 220 + .../whisper/whisperv5/filter_test.go | 670 + .../go-ethereum/whisper/whisperv5/message.go | 384 + .../whisper/whisperv5/message_test.go | 346 + .../go-ethereum/whisper/whisperv5/peer.go | 182 + .../whisper/whisperv5/peer_test.go | 302 + .../go-ethereum/whisper/whisperv5/topic.go | 69 + .../whisper/whisperv5/topic_test.go | 136 + .../go-ethereum/whisper/whisperv5/whisper.go | 653 + .../whisper/whisperv5/whisper_test.go | 355 + 1700 files changed, 648499 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/ethereum/go-ethereum/.dockerignore create mode 100644 vendor/github.com/ethereum/go-ethereum/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/.github/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/.github/ISSUE_TEMPLATE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/.mailmap create mode 100644 vendor/github.com/ethereum/go-ethereum/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/COPYING create mode 100644 vendor/github.com/ethereum/go-ethereum/COPYING.LESSER create mode 100644 vendor/github.com/ethereum/go-ethereum/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/VERSION create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/abi_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/event.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/event_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/method.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/packing.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/abi/type_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/accounts.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/hd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/hd_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/1 create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/2 create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/foo create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/.hiddenfile create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/README create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/aaa create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/empty create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/garbage create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/no-address create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/zero create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/zzz create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/v1_test_vector.json create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/v3_test_vector.json create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/very-light-scrypt.json create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/manager.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/url.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go create mode 100644 vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go create mode 100644 vendor/github.com/ethereum/go-ethereum/appveyor.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/circle.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/disasm/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/ethtest/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/bugcmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/dao_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/genesis_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/run_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/empty.js create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/guswallet.json create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/passwords.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/wrong-passwords.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/internal/browser/browser.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/rlpdump/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/swarm/hash.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_freebsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go create mode 100644 vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/common/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/common/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/common/big.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/big_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/bytes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/bytes_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/compiler/solidity.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/compiler/solidity_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/debug.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/format.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/hexutil/json_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/icap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/icap_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/list.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/main_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/math/dist.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/math/dist_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/math/exp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/math/integer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/math/integer_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/number/int.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/number/uint_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/path.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/size.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/size_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/test_utils.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/types_template.go create mode 100644 vendor/github.com/ethereum/go-ethereum/common/types_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/compression/rle/read_write.go create mode 100644 vendor/github.com/ethereum/go-ethereum/compression/rle/read_write_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/console/bridge.go create mode 100644 vendor/github.com/ethereum/go-ethereum/console/console.go create mode 100644 vendor/github.com/ethereum/go-ethereum/console/console_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/console/prompter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/console/testdata/exec.js create mode 100644 vendor/github.com/ethereum/go-ethereum/console/testdata/preload.js create mode 100644 vendor/github.com/ethereum/go-ethereum/containers/docker/develop-alpine/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/containers/docker/develop-ubuntu/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/containers/docker/master-alpine/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/containers/docker/master-ubuntu/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/containers/vagrant/Vagrantfile create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/ens/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/ens/ens_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/release/contract_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/contracts/release/release.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/core/asm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/bench_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/block_validator.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/block_validator_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/blockchain.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/blockchain_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/blocks.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/chain_makers.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/chain_makers_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/chain_pow.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/chain_pow_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/dao.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/dao_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/database_util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/database_util_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/default_genesis.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/events.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/evm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/fees.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/filter_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/gaspool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/genesis.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/headerchain.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/helper_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/dump.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/iterator.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/iterator_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/journal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/main_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/managed_state_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/state_object.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/state_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/statedb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/statedb_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/sync.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state/sync_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state_processor.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/state_transition.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/tx_list.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/tx_list_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/tx_pool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/tx_pool_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/block.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/block_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/bloom9_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/derive_sha.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/log.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/log_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/receipt.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/transaction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/types/transaction_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/analysis.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/asm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/contract.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/disasm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/evm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/gas.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/gas_table_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier_empty.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/interface.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/intpool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/logger.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/logger_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/memory.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/memory_table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/noop.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/fuzz.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_example_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/stack.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit.go create mode 100644 vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/crypto.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/crypto_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/README create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/asn1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/ecies/params.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/randentropy/rand_entropy.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/ext.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/COPYING create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/Makefile.am create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/TODO create mode 100755 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/autogen.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/configure.ac create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/obj/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/group_prover.sage create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/basic-config.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_internal.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_recover.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_schnorr_verify.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_sign.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_verify.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/gen_context.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include create mode 100755 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h create mode 100755 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/secp256k1.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand_impl.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/util.h create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/panic_cb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/hashes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/register.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/shake.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/testdata/keccakKats.json.deflate create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_generic.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/crypto/signature_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/errs/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/errs/errors_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/api_backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/backend_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/bad_block.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/bind.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/events.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/modes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/downloader/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/fetcher/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/api_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/filters/filter_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/gasprice/gasprice.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/handler.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/handler_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/helper_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/protocol.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/protocol_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/sync.go create mode 100644 vendor/github.com/ethereum/go-ethereum/eth/sync_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethclient/ethclient_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethdb/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/ethdb/database.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethdb/database_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethdb/interface.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethdb/memory_database.go create mode 100644 vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/event.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/event_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/example_feed_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/example_scope_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/example_subscription_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/example_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/feed.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/feed_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/filter/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/filter/filter_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/filter/generic_filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/subscription.go create mode 100644 vendor/github.com/ethereum/go-ethereum/event/subscription_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/interfaces.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/build/archive.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/build/azure.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/build/env.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/build/pgp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/build/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic_fallback.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/trace.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/debug/trace_fallback.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/ethapi/solc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/guide/guide.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/guide/guide_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/completion.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/completion_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bignumber.js create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/deps.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/web3.js create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go create mode 100644 vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/api_backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/backend.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/fetcher.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/handler.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/handler_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/helper_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/odr.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/odr_requests.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/odr_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/protocol.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/randselect.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/randselect_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/request_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/server.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/serverpool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/sync.go create mode 100644 vendor/github.com/ethereum/go-ethereum/les/txrelay.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/lightchain.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/lightchain_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/odr.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/odr_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/odr_util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/state.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/state_object.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/state_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/trie.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/txpool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/txpool_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/light/vm_env.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/log/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/log/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/log/README_ETHEREUM.md create mode 100644 vendor/github.com/ethereum/go-ethereum/log/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/format.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/handler.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/handler_go13.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/handler_go14.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/logger.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/root.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/syslog.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_appengine.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_darwin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_freebsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_netbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_notwindows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_openbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_solaris.go create mode 100644 vendor/github.com/ethereum/go-ethereum/log/term/terminal_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/glog/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/glog/README create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/glog/glog.go create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/glog/glog_file.go create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/glog/glog_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/logger/verbosity.go create mode 100644 vendor/github.com/ethereum/go-ethereum/metrics/disk.go create mode 100644 vendor/github.com/ethereum/go-ethereum/metrics/disk_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/metrics/disk_nop.go create mode 100644 vendor/github.com/ethereum/go-ethereum/metrics/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/agent.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/miner.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/remote_agent.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/unconfirmed.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/unconfirmed_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/miner/worker.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/accounts.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/android_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/big.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/bind.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/context.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/discover.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/geth.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/init.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/interface.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/logger.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/p2p.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/params.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/primitives.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/mobile/vm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/config.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/config_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/defaults.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/node_example_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/node_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/service.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/service_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/node/utils_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/dial.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/dial_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/database.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/database_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/node_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/ntp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/table_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/database_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/net_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/node_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_run_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_testmain_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/table_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/message.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/message_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/nat/nat_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/nat/natpmp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/error_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/net_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/peer_error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/peer_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/protocol.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/rlpx_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/server.go create mode 100644 vendor/github.com/ethereum/go-ethereum/p2p/server_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/bootnodes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/config.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/dao.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/gas_table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/protocol_params.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/params/version.go create mode 100644 vendor/github.com/ethereum/go-ethereum/pow/block.go create mode 100644 vendor/github.com/ethereum/go-ethereum/pow/pow.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/decode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/decode_tail_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/decode_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/encode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/encode_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/encoder_example_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/raw.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/raw_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rlp/typecache.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.4.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.5.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.6.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.7.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_example_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/client_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/http.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/inproc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/ipc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/ipc_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/ipc_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/json.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/json_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/server.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/server_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/subscription.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/subscription_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/utils.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/utils_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/rpc/websocket.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/api_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/config.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/config_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/http/server_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/manifest_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/storage_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/testdata/test0/img/logo.png create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/testdata/test0/index.css create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/api/testdata/test0/index.html create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/protocol_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/common_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/swarm/swarm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/encoding.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/encoding_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/hasher.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/iterator.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/iterator_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/node_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/proof.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/proof_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/secure_trie_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/sync.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/sync_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/trie.go create mode 100644 vendor/github.com/ethereum/go-ethereum/trie/trie_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/CHANGELOG.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.lock create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.yaml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/COPYING create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Dockerfile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/README.md create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/check_line_len.awk create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/iptables.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/rpmbuild.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/btcec.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/ciphering.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/field.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/precompute.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/privkey.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/pubkey.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/signature.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/LICENSE.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/cp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/cov_report.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypass.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/config.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/dump.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/format.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/spew.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/test_coverage.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/CMakeLists.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/MANIFEST.in create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Vagrantfile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/appveyor.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethash.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethashc.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/setup.py create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/CMakeLists.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/compiler.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/data_sizes.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/endian.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/ethash.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/fnv.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_posix.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_win32.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap_win32.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.cpp create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util_win32.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/LICENSE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/color.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/barchart.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/buffer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/canvas.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/config.py create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/events.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/gauge.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.lock create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.yaml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/grid.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/helper.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_others.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/list.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mbarchart.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mkdocs.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/par.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/pos.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/render.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/sparkline.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/textbuilder.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/theme.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/widget.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/LICENSE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/stack.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_other.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_other.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/snappy.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/2q.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/arc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/lru.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway1/internetgateway1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway2/internetgateway2.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/device.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/goupnp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/httpu.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/serve.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/scpd/scpd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/service_client.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/soap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/types.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/registry.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/ssdp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/natpmp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/network.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/recorder.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/LICENSE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_disabled.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_enabled.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/AUTHORS.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-bsd.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-gpl3.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-orig.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/README.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/hidapi/hidapi.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/libusb/hid.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/mac/hid.c create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/windows/hid.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/COPYING create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/config.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/core.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/descriptor.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/io.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusbi.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_pollfs.cpp create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_backend.cpp create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.cpp create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_netlink.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_udev.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/netbsd_usb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/openbsd_usb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_common.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/strerror.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/sync.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/wchar.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/source.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/stack.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/ui.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/vendor.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_others.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/noncolorable.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_appengine.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_bsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_not_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_solaris.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/README.mkd create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_js.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_posix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_windows.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/collect_terminfo.py create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo_builtin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/dce.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/hash.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/marshal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/sql.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/time.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/uuid.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version4.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/COPYING create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/bsdinput.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/fallbackinput.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_darwin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/line.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/unixmode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/width.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/counter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/debug.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/ewma.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/exp/exp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge_float64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/graphite.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/healthcheck.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/histogram.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/json.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/log.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/memory.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/meter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/metrics.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/opentsdb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/registry.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/sample.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/syslog.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/timer.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/validate.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/writer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/appveyor.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug_debug.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fen.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fsevents.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_inotify.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_kqueue.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_readdcw.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_stub.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_trigger.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/notify.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_nonrecursive.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_recursive.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen_cgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_inotify.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_kqueue.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_readdcw.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_stub.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_trigger.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_other.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_readdcw.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/DESIGN.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/comments.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_array.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_boolean.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_date.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_function.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_json.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_math.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_number.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_object.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_regexp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_string.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/clone.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_parse.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/console.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg/dbg.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/evaluate.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/file.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/global.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.pl create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object_class.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto_.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/dbg.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/expression.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/lexer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/parser.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/regexp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/scope.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/statement.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/property.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/registry.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/result.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/runtime.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/scope.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/script.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/stash.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/README.markdown create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token_const.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/tokenfmt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_arguments.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_array.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_boolean.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_date.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_error.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_function.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_array.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_map.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_slice.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_struct.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_number.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_reference.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_regexp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_string.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_boolean.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_number.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_primitive.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_string.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/cors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/utils.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/chain.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/middleware.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/xhandler.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/.travis.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/batch.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_snapshot.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_state.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_write.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/bloom.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/array_iter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/indexed_iter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/iter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/merged_iter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/journal/journal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/key.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/memdb/memdb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/options.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_record.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_nacl.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_plan9.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_solaris.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/mem_storage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/storage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/reader.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/writer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer_pool.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/crc32.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/hash.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/range.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/cast5/cast5.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/codereview.cfg create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/armor.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/encode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/canonical_text.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/errors/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/keys.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/compressed.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/config.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/literal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/opaque.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/packet.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/private_key.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/reader.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userid.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/read.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/write.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160block.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt/scrypt.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/codereview.cfg create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/atom.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/gen.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/table.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/charset/charset.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/const.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doctype.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/entity.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/escape.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/foreign.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/node.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/parse.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/render.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/token.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/websocket/client.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/websocket/dial.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/websocket/hybi.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/websocket/server.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/websocket/websocket.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/codereview.cfg create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_darwin_386.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_darwin_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_darwin_arm.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_darwin_arm64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_freebsd_386.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_freebsd_arm.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_386.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_arm.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_arm64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_mips64x.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_mipsx.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_linux_s390x.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_netbsd_386.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_netbsd_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_netbsd_arm.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_openbsd_386.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_openbsd_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/asm_solaris_amd64.s create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/bluetooth_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/constants.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/env_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/env_unset.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/flock.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/flock_linux_32bit.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/gccgo.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/gccgo_c.c create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/gccgo_linux_sparc64.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mkall.sh create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mkerrors.sh create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mkpost.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksyscall.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksyscall_solaris.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_darwin.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_linux.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/race.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/race0.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/sockcmsg_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/sockcmsg_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/str.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_bsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_darwin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_darwin_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_darwin_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_dragonfly.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_freebsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_netbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_netbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_no_getwd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_openbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_openbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_solaris.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/syscall_unix_gc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_darwin.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_dragonfly.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_freebsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_linux.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_netbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_openbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/types_solaris.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_darwin_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_darwin_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysctl_openbsd.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_darwin_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/zsysnum_solaris_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/codereview.cfg create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/charmap/charmap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/charmap/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/charmap/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/encoding.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/htmlindex/gen.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/htmlindex/htmlindex.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/htmlindex/map.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/htmlindex/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/internal/identifier/gen.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/internal/identifier/identifier.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/internal/identifier/mib.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/internal/internal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/all.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/eucjp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/iso2022jp.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/shiftjis.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/japanese/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/korean/euckr.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/korean/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/korean/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/simplifiedchinese/all.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/simplifiedchinese/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/simplifiedchinese/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/traditionalchinese/big5.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/traditionalchinese/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/traditionalchinese/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/unicode/override.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/encoding/unicode/unicode.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/internal/tag/tag.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/internal/utf8internal/utf8internal.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/coverage.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/gen_common.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/gen_index.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/go1_1.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/go1_2.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/index.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/language.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/lookup.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/maketables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/match.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/parse.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/tables.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/language/tags.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/runes/cond.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/runes/runes.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/text/transform/transform.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/.gitattributes create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/.gitignore create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/AUTHORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/CONTRIBUTING.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/CONTRIBUTORS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/PATENTS create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/README create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/codereview.cfg create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/go/ast/astutil/imports.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/go/ast/astutil/util.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fastwalk.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fastwalk_dirent_fileno.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fastwalk_dirent_ino.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fastwalk_portable.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fastwalk_unix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/fix.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/imports.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/mkindex.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/mkstdlib.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/sortimports.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/tools/imports/zstdlib.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/TODO create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/benchmark.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/check.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/checkers.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/helpers.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/printer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/reporter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/check.v1/run.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/fatih/set.v0/LICENSE.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/fatih/set.v0/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/fatih/set.v0/set.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/fatih/set.v0/set_nots.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/fatih/set.v0/set_ts.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/karalabe/cookiejar.v2/LICENSE create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/karalabe/cookiejar.v2/README.md create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/prque.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/karalabe/cookiejar.v2/collections/prque/sstack.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/doc.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/Makefile create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/base64vlq/base64_vlq.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/consumer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/sourcemap.v1/sourcemap.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/CHANGELOG.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/LICENSE create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/README.md create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/app.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/appveyor.yml create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/category.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/cli.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/command.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/context.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/errors.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/flag-types.json create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/flag.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/flag_generated.go create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/funcs.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/generate-flag-types create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/help.go create mode 100755 vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1/runtests create mode 100644 vendor/github.com/ethereum/go-ethereum/vendor/vendor.json create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/mailserver/mailserver.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/mailserver/server_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/envelope_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/filter_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/main.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/message_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/peer_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/topic_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv2/whisper_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/api.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/api_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/benchmarks_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/doc.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/envelope.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/filter_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/message_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/peer_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/topic_test.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper.go create mode 100644 vendor/github.com/ethereum/go-ethereum/whisper/whisperv5/whisper_test.go diff --git a/compile.sh b/compile.sh index 45cb967..48426e4 100755 --- a/compile.sh +++ b/compile.sh @@ -7,9 +7,9 @@ mustrun() { } echo "Install dependencies..." -mustrun build/env.sh go get -v github.com/ethereum/go-ethereum mustrun build/env.sh go get -v golang.org/x/crypto/ssh/terminal mustrun build/env.sh go get -v gopkg.in/urfave/cli.v1 +mustrun build/env.sh go get -v golang.org/x/net/context echo "Compiling SmartPool client..." mustrun build/env.sh go build -ldflags -s -o smartpool cmd/ropsten/ropsten.go echo "Done. You can run SmartPool by ./smartpool --help" diff --git a/ethereum/ethash/ethash.go b/ethereum/ethash/ethash.go index e92559d..cd220b0 100644 --- a/ethereum/ethash/ethash.go +++ b/ethereum/ethash/ethash.go @@ -461,7 +461,7 @@ func (pow *Full) Search(block pow.Block, stop <-chan struct{}, index int) (nonce ret := C.ethash_full_compute(dag.ptr, hash, C.uint64_t(nonce)) result := h256ToHash(ret.result).Big() - // TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining + // TODO: disagrees with the spec https://gopkg.in/ethereum/wiki/wiki/Ethash#mining if ret.success && result.Cmp(target) <= 0 { mixDigest = C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32)) atomic.AddInt32(&pow.hashRate, -previousHashrate) diff --git a/ethereum/share.go b/ethereum/share.go index 89a9c51..b47db05 100644 --- a/ethereum/share.go +++ b/ethereum/share.go @@ -98,7 +98,7 @@ func processDuringRead( r := bufio.NewReader(f) buf := [128]byte{} // ignore first 8 bytes magic number at the beginning - // of dataset. See more at https://github.com/ethereum/wiki/wiki/Ethash-DAG-Disk-Storage-Format + // of dataset. See more at https://gopkg.in/ethereum/wiki/wiki/Ethash-DAG-Disk-Storage-Format _, err = io.ReadFull(r, buf[:8]) if err != nil { log.Fatal(err) diff --git a/kovan_compile.sh b/kovan_compile.sh index cbe2587..152bc51 100755 --- a/kovan_compile.sh +++ b/kovan_compile.sh @@ -7,9 +7,9 @@ mustrun() { } echo "Install dependencies..." -mustrun build/env.sh go get -v github.com/ethereum/go-ethereum mustrun build/env.sh go get -v golang.org/x/crypto/ssh/terminal mustrun build/env.sh go get -v gopkg.in/urfave/cli.v1 +mustrun build/env.sh go get -v golang.org/x/net/context echo "Compiling SmartPool client..." -mustrun build/env.sh go build -o kovan cmd/kovan/main.go +mustrun build/env.sh go build -ldflags -s -o kovan cmd/kovan/main.go echo "Done. You can run SmartPool by ./kovan --help" diff --git a/vendor/github.com/ethereum/go-ethereum/.dockerignore b/vendor/github.com/ethereum/go-ethereum/.dockerignore new file mode 100644 index 0000000..d1d79d5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.dockerignore @@ -0,0 +1,3 @@ +.git +build/_workspace +build/_bin diff --git a/vendor/github.com/ethereum/go-ethereum/.gitattributes b/vendor/github.com/ethereum/go-ethereum/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/vendor/github.com/ethereum/go-ethereum/.github/CONTRIBUTING.md b/vendor/github.com/ethereum/go-ethereum/.github/CONTRIBUTING.md new file mode 100644 index 0000000..a332b81 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.github/CONTRIBUTING.md @@ -0,0 +1,16 @@ +## Can I have feature X + +Before you do a feature request please check and make sure that it isn't possible +through some other means. The JavaScript enabled console is a powerful feature +in the right hands. Please check our [Bitchin' tricks](https://github.com/ethereum/go-ethereum/wiki/bitchin-tricks) wiki page for more info +and help. + +## Contributing + +If you'd like to contribute to go-ethereum please fork, fix, commit and +send a pull request. Commits which do not comply with the coding standards +are ignored (use gofmt!). + +See [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) +for more details on configuring your environment, testing, and +dependency management. diff --git a/vendor/github.com/ethereum/go-ethereum/.github/ISSUE_TEMPLATE.md b/vendor/github.com/ethereum/go-ethereum/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..6c1cb9f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ +#### System information + +Geth version: `geth version` +OS & Version: Windows/Linux/OSX +Commit hash : (if `develop`) + +#### Expected behaviour + + +#### Actual behaviour + + +#### Steps to reproduce the behaviour + + +#### Backtrace + +```` +[backtrace] +```` diff --git a/vendor/github.com/ethereum/go-ethereum/.gitignore b/vendor/github.com/ethereum/go-ethereum/.gitignore new file mode 100644 index 0000000..e53e461 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.gitignore @@ -0,0 +1,32 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*/**/*.test +*un~ +.DS_Store +*/**/.DS_Store +.ethtest +*/**/*tx_database* +*/**/*dapps* +build/_vendor/pkg + +#* +.#* +*# +*~ +.project +.settings + +# used by the Makefile +/build/_workspace/ +/build/bin/ +/geth*.zip + +# travis +profile.tmp +profile.cov diff --git a/vendor/github.com/ethereum/go-ethereum/.mailmap b/vendor/github.com/ethereum/go-ethereum/.mailmap new file mode 100644 index 0000000..a36ddc1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.mailmap @@ -0,0 +1,92 @@ +Jeffrey Wilcke +Jeffrey Wilcke +Jeffrey Wilcke +Jeffrey Wilcke + +Viktor Trón + +Joseph Goulden + +Nick Savers + +Maran Hidskes + +Taylor Gerring +Taylor Gerring + +Bas van Kervel +Bas van Kervel +Bas van Kervel +Bas van Kervel + +Sven Ehlert + +Vitalik Buterin + +Marian Oancea + +Christoph Jentzsch + +Heiko Hees + +Alex Leverington +Alex Leverington + +Zsolt Felföldi + +Gavin Wood + +Martin Becze +Martin Becze + +Dimitry Khokhlov + +Roman Mandeleil + +Alec Perseghin + +Alon Muroch + +Arkadiy Paronyan + +Jae Kwon + +Aaron Kumavis + +Nick Dodson + +Jason Carver +Jason Carver + +Joseph Chow +Joseph Chow ethers + +Enrique Fynn + +Vincent G + +RJ Catalano + +Nchinda Nchinda + +Aron Fischer + +Vlad Gluhovsky + +Ville Sundell + +Elliot Shepherd + +Yohann Léon + +Gregg Dourgarian + +Casey Detrio + +Jens Agerberg + +Nick Johnson + +Henning Diedrich +Henning Diedrich Drake Burroughs + diff --git a/vendor/github.com/ethereum/go-ethereum/.travis.yml b/vendor/github.com/ethereum/go-ethereum/.travis.yml new file mode 100644 index 0000000..8ea8704 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/.travis.yml @@ -0,0 +1,157 @@ +language: go +go_import_path: github.com/ethereum/go-ethereum +sudo: false +matrix: + include: + - os: linux + dist: trusty + go: 1.5.4 + env: + - GO15VENDOREXPERIMENT=1 + - os: linux + dist: trusty + go: 1.6.2 + - os: linux + dist: trusty + go: 1.7.5 + + # These are the latest Go versions, only run go vet and misspell on these + - os: linux + dist: trusty + go: 1.8 + script: + - go run build/ci.go install + - go run build/ci.go test -coverage -vet -misspell + + - os: osx + go: 1.8 + script: + - go run build/ci.go install + - go run build/ci.go test -coverage -vet -misspell + + # This builder does the Ubuntu PPA and Linux Azure uploads + - os: linux + dist: trusty + sudo: required + go: 1.8 + env: + - ubuntu-ppa + - azure-linux + addons: + apt: + packages: + - devscripts + - debhelper + - dput + - gcc-multilib + script: + # Build for the primary platforms that Trusty can manage + - go run build/ci.go debsrc -signer "Go Ethereum Linux Builder " -upload ppa:ethereum/ethereum + - go run build/ci.go install + - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + - go run build/ci.go install -arch 386 + - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + + # Switch over GCC to cross compilation (breaks 386, hence why do it here only) + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + - sudo ln -s /usr/include/asm-generic /usr/include/asm + + - GOARM=5 CC=arm-linux-gnueabi-gcc go run build/ci.go install -arch arm + - GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + - GOARM=6 CC=arm-linux-gnueabi-gcc go run build/ci.go install -arch arm + - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + - GOARM=7 CC=arm-linux-gnueabihf-gcc go run build/ci.go install -arch arm + - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + # ARM64 linux builds are broken in Go 1.8 (https://github.com/golang/go/issues/19137), reenable in Go 1.8.1 + # - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 + # - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + + # This builder is a temporary fallback for building ARM64 while Go 1.8 is fixed + - os: linux + dist: trusty + sudo: required + go: 1.7.5 + env: + - azure-linux-arm64-fallback + addons: + apt: + packages: + - gcc-multilib + script: + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-aarch64-linux-gnu libc6-dev-arm64-cross + - sudo ln -s /usr/include/asm-generic /usr/include/asm + + - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 + - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + + # This builder does the Android Maven and Azure uploads + - os: linux + dist: precise # Needed for the android tools + addons: + apt: + packages: + - oracle-java8-installer + - oracle-java8-set-default + language: android + android: + components: + - platform-tools + - tools + - android-15 + - android-19 + - android-24 + env: + - azure-android + - maven-android + before_install: + - curl https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz | tar -xz + - export PATH=`pwd`/go/bin:$PATH + - export GOROOT=`pwd`/go + - export GOPATH=$HOME/go + script: + # Build the Android archive and upload it to Maven Central and Azure + - curl https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip -o android-ndk-r13b.zip + - unzip -q android-ndk-r13b.zip && rm android-ndk-r13b.zip + - mv android-ndk-r13b $HOME + - export ANDROID_NDK=$HOME/android-ndk-r13b + + - mkdir -p $GOPATH/src/github.com/ethereum + - ln -s `pwd` $GOPATH/src/github.com/ethereum + - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds + + # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads + - os: osx + go: 1.8 + env: + - azure-osx + - azure-ios + - cocoapods-ios + script: + - go run build/ci.go install + - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds + + # Build the iOS framework and upload it to CocoaPods and Azure + - gem uninstall cocoapods -a + - gem install cocoapods --pre + + - mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak + - sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb + - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi + + - xctool -version + - xcrun simctl list + + - go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds + +install: + - go get golang.org/x/tools/cmd/cover +script: + - go run build/ci.go install + - go run build/ci.go test -coverage + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/e09ccdce1048c5e03445 + on_success: change + on_failure: always diff --git a/vendor/github.com/ethereum/go-ethereum/AUTHORS b/vendor/github.com/ethereum/go-ethereum/AUTHORS new file mode 100644 index 0000000..50f3c71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/AUTHORS @@ -0,0 +1,63 @@ +# This is the official list of go-ethereum authors for copyright purposes. + +Ales Katona +Alex Leverington +Alexandre Van de Sande +Aron Fischer +Bas van Kervel +Benjamin Brent +Casey Detrio +Christoph Jentzsch +Daniel A. Nagy +Elliot Shepherd +Enrique Fynn +Ethan Buchman +Fabian Vogelsteller +Fabio Berger +Felix Lange +Gregg Dourgarian +Gustav Simonsson +Hao Bryan Cheng +Henning Diedrich +Isidoro Ghezzi +Jae Kwon +Jason Carver +Jeff R. Allen +Jeffrey Wilcke +Jens Agerberg +Jonathan Brown +Joseph Chow +Justin Clark-Casey +Kenji Siu +Kobi Gurkan +Lefteris Karapetsas +Leif Jurvetson +Maran Hidskes +Marek Kotewicz +Martin Holst Swende +Matthew Di Ferrante +Matthew Wampler-Doty +Nchinda Nchinda +Nick Dodson +Nick Johnson +Paulo L F Casaretto +Peter Pratscher +Péter Szilágyi +RJ Catalano +Ramesh Nair +Ricardo Catalinas Jiménez +Rémy Roy +Stein Dekker +Steven Roose +Taylor Gerring +Thomas Bocek +Tosh Camille +Viktor Trón +Ville Sundell +Vincent G +Vitalik Buterin +Vlad Gluhovsky +Yohann Léon +Yoichi Hirai +Zsolt Felföldi +ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com> diff --git a/vendor/github.com/ethereum/go-ethereum/COPYING b/vendor/github.com/ethereum/go-ethereum/COPYING new file mode 100644 index 0000000..8d66e87 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/COPYING @@ -0,0 +1,619 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2014 The go-ethereum Authors. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/COPYING.LESSER b/vendor/github.com/ethereum/go-ethereum/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/github.com/ethereum/go-ethereum/Dockerfile b/vendor/github.com/ethereum/go-ethereum/Dockerfile new file mode 100644 index 0000000..ae6870e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:3.5 + +ADD . /go-ethereum +RUN \ + apk add --update git go make gcc musl-dev linux-headers && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apk del git go make gcc musl-dev linux-headers && \ + rm -rf /go-ethereum && rm -rf /var/cache/apk/* + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/Makefile b/vendor/github.com/ethereum/go-ethereum/Makefile new file mode 100644 index 0000000..a1bc1ce --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/Makefile @@ -0,0 +1,123 @@ +# This Makefile is meant to be used by people that do not usually work +# with Go source code. If you know what GOPATH is then you probably +# don't need to bother with make. + +.PHONY: geth android ios geth-cross evm all test clean +.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le +.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64 +.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64 +.PHONY: geth-windows geth-windows-386 geth-windows-amd64 + +GOBIN = build/bin +GO ?= latest + +geth: + build/env.sh go run build/ci.go install ./cmd/geth + @echo "Done building." + @echo "Run \"$(GOBIN)/geth\" to launch geth." + +evm: + build/env.sh go run build/ci.go install ./cmd/evm + @echo "Done building." + @echo "Run \"$(GOBIN)/evm\" to start the evm." + +all: + build/env.sh go run build/ci.go install + +android: + build/env.sh go run build/ci.go aar --local + @echo "Done building." + @echo "Import \"$(GOBIN)/geth.aar\" to use the library." + +ios: + build/env.sh go run build/ci.go xcode --local + @echo "Done building." + @echo "Import \"$(GOBIN)/Geth.framework\" to use the library." + +test: all + build/env.sh go run build/ci.go test + +clean: + rm -fr build/_workspace/pkg/ $(GOBIN)/* + +# Cross Compilation Targets (xgo) + +geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios + @echo "Full cross compilation done:" + @ls -ld $(GOBIN)/geth-* + +geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 geth-linux-mips64le + @echo "Linux cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* + +geth-linux-386: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth + @echo "Linux 386 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep 386 + +geth-linux-amd64: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth + @echo "Linux amd64 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep amd64 + +geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64 + @echo "Linux ARM cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep arm + +geth-linux-arm-5: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth + @echo "Linux ARMv5 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep arm-5 + +geth-linux-arm-6: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth + @echo "Linux ARMv6 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep arm-6 + +geth-linux-arm-7: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth + @echo "Linux ARMv7 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep arm-7 + +geth-linux-arm64: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth + @echo "Linux ARM64 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep arm64 + +geth-linux-mips64: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth + @echo "Linux MIPS64 cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep mips64 + +geth-linux-mips64le: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth + @echo "Linux MIPS64le cross compilation done:" + @ls -ld $(GOBIN)/geth-linux-* | grep mips64le + +geth-darwin: geth-darwin-386 geth-darwin-amd64 + @echo "Darwin cross compilation done:" + @ls -ld $(GOBIN)/geth-darwin-* + +geth-darwin-386: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth + @echo "Darwin 386 cross compilation done:" + @ls -ld $(GOBIN)/geth-darwin-* | grep 386 + +geth-darwin-amd64: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth + @echo "Darwin amd64 cross compilation done:" + @ls -ld $(GOBIN)/geth-darwin-* | grep amd64 + +geth-windows: geth-windows-386 geth-windows-amd64 + @echo "Windows cross compilation done:" + @ls -ld $(GOBIN)/geth-windows-* + +geth-windows-386: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth + @echo "Windows 386 cross compilation done:" + @ls -ld $(GOBIN)/geth-windows-* | grep 386 + +geth-windows-amd64: + build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth + @echo "Windows amd64 cross compilation done:" + @ls -ld $(GOBIN)/geth-windows-* | grep amd64 diff --git a/vendor/github.com/ethereum/go-ethereum/README.md b/vendor/github.com/ethereum/go-ethereum/README.md new file mode 100644 index 0000000..66f5947 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/README.md @@ -0,0 +1,278 @@ +## Ethereum Go + +Official golang implementation of the Ethereum protocol. + +[![API Reference]( +https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667 +)](https://godoc.org/github.com/ethereum/go-ethereum) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Automated builds are available for stable releases and the unstable master branch. +Binary archives are published at https://geth.ethereum.org/downloads/. + +## Building the source + +For prerequisites and detailed build instructions please read the +[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum) +on the wiki. + +Building geth requires both a Go and a C compiler. +You can install them using your favourite package manager. +Once the dependencies are installed, run + + make geth + +or, to build the full suite of utilities: + + make all + +## Executables + +The go-ethereum project comes with several wrappers/executables found in the `cmd` directory. + +| Command | Description | +|:----------:|-------------| +| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options | +| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. | +| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | +| `disasm` | Bytecode disassembler to convert EVM (Ethereum Virtual Machine) bytecode into more user friendly assembly-like opcodes (e.g. `echo "6001" | disasm`). For details on the individual opcodes, please see pages 22-30 of the [Ethereum Yellow Paper](http://gavwood.com/paper.pdf). | +| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). | +| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | +| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | +| `swarm` | swarm daemon and tools. This is the entrypoint for the swarm network. `swarm --help` for command line options and subcommands. See https://swarm-guide.readthedocs.io for swarm documentation. | + +## Running geth + +Going through all the possible command line flags is out of scope here (please consult our +[CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)), but we've +enumerated a few common parameter combos to get you up to speed quickly on how you can run your +own Geth instance. + +### Full node on the main Ethereum network + +By far the most common scenario is people wanting to simply interact with the Ethereum network: +create accounts; transfer funds; deploy and interact with contracts. For this particular use-case +the user doesn't care about years-old historical data, so we can fast-sync quickly to the current +state of the network. To do so: + +``` +$ geth --fast --cache=512 console +``` + +This command will: + + * Start geth in fast sync mode (`--fast`), causing it to download more data in exchange for avoiding + processing the entire history of the Ethereum network, which is very CPU intensive. + * Bump the memory allowance of the database to 512MB (`--cache=512`), which can help significantly in + sync times especially for HDD users. This flag is optional and you can set it as high or as low as + you'd like, though we'd recommend the 512MB - 2GB range. + * Start up Geth's built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console), + (via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API) + as well as Geth's own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs). + This too is optional and if you leave it out you can always attach to an already running Geth instance + with `geth --attach`. + +### Full node on the Ethereum test network + +Transitioning towards developers, if you'd like to play around with creating Ethereum contracts, you +almost certainly would like to do that without any real money involved until you get the hang of the +entire system. In other words, instead of attaching to the main network, you want to join the **test** +network with your node, which is fully equivalent to the main network, but with play-Ether only. + +``` +$ geth --testnet --fast --cache=512 console +``` + +The `--fast`, `--cache` flags and `console` subcommand have the exact same meaning as above and they +are equially useful on the testnet too. Please see above for their explanations if you've skipped to +here. + +Specifying the `--testnet` flag however will reconfigure your Geth instance a bit: + + * Instead of using the default data directory (`~/.ethereum` on Linux for example), Geth will nest + itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on Linux). + * Instead of connecting the main Ethereum network, the client will connect to the test network, + which uses different P2P bootnodes, different network IDs and genesis states. + +*Note: Although there are some internal protective measures to prevent transactions from crossing +over between the main network and test network (different starting nonces), you should make sure to +always use separate accounts for play-money and real-money. Unless you manually move accounts, Geth +will by default correctly separate the two networks and will not make any accounts available between +them.* + +#### Docker quick start + +One of the quickest ways to get Ethereum up and running on your machine is by using Docker: + +``` +docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \ + -p 8545:8545 -p 30303:30303 \ + ethereum/client-go --fast --cache=512 +``` + +This will start geth in fast sync mode with a DB memory allowance of 512MB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image. + +### Programatically interfacing Geth nodes + +As a developer, sooner rather than later you'll want to start interacting with Geth and the Ethereum +network via your own programs and not manually through the console. To aid this, Geth has built in +support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and +[Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be +exposed via HTTP, WebSockets and IPC (unix sockets on unix based platroms, and named pipes on Windows). + +The IPC interface is enabled by default and exposes all the APIs supported by Geth, whereas the HTTP +and WS interfaces need to manually be enabled and only expose a subset of APIs due to security reasons. +These can be turned on/off and configured as you'd expect. + +HTTP based JSON-RPC API options: + + * `--rpc` Enable the HTTP-RPC server + * `--rpcaddr` HTTP-RPC server listening interface (default: "localhost") + * `--rpcport` HTTP-RPC server listening port (default: 8545) + * `--rpcapi` API's offered over the HTTP-RPC interface (default: "eth,net,web3") + * `--rpccorsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced) + * `--ws` Enable the WS-RPC server + * `--wsaddr` WS-RPC server listening interface (default: "localhost") + * `--wsport` WS-RPC server listening port (default: 8546) + * `--wsapi` API's offered over the WS-RPC interface (default: "eth,net,web3") + * `--wsorigins` Origins from which to accept websockets requests + * `--ipcdisable` Disable the IPC-RPC server + * `--ipcapi` API's offered over the IPC-RPC interface (default: "admin,debug,eth,miner,net,personal,shh,txpool,web3") + * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) + +You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect +via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](http://www.jsonrpc.org/specification) +on all transports. You can reuse the same connection for multiple requests! + +**Note: Please understand the security implications of opening up an HTTP/WS based transport before +doing so! Hackers on the internet are actively trying to subvert Ethereum nodes with exposed APIs! +Further, all browser tabs can access locally running webservers, so malicious webpages could try to +subvert locally available APIs!** + +### Operating a private network + +Maintaining your own private network is more involved as a lot of configurations taken for granted in +the official networks need to be manually set up. + +#### Defining the private genesis state + +First, you'll need to create the genesis state of your networks, which all nodes need to be aware of +and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`): + +```json +{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00" +} +``` + +The above fields should be fine for most purposes, although we'd recommend changing the `nonce` to +some random value so you prevent unknown remote nodes from being able to connect to you. If you'd +like to pre-fund some accounts for easier testing, you can populate the `alloc` field with account +configs: + +```json +"alloc": { + "0x0000000000000000000000000000000000000001": {"balance": "111111111"}, + "0x0000000000000000000000000000000000000002": {"balance": "222222222"} +} +``` + +With the genesis state defined in the above JSON file, you'll need to initialize **every** Geth node +with it prior to starting it up to ensure all blockchain parameters are correctly set: + +``` +$ geth init path/to/genesis.json +``` + +#### Creating the rendezvous point + +With all nodes that you want to run initialized to the desired genesis state, you'll need to start a +bootstrap node that others can use to find each other in your network and/or over the internet. The +clean way is to configure and run a dedicated bootnode: + +``` +$ bootnode --genkey=boot.key +$ bootnode --nodekey=boot.key +``` + +With the bootnode online, it will display an [`enode` URL](https://github.com/ethereum/wiki/wiki/enode-url-format) +that other nodes can use to connect to it and exchange peer information. Make sure to replace the +displayed IP address information (most probably `[::]`) with your externally accessible IP to get the +actual `enode` URL. + +*Note: You could also use a full fledged Geth node as a bootnode, but it's the less recommended way.* + +#### Starting up your member nodes + +With the bootnode operational and externally reachable (you can try `telnet ` to ensure +it's indeed reachable), start every subsequent Geth node pointed to the bootnode for peer discovery +via the `--bootnodes` flag. It will probably also be desirable to keep the data directory of your +private network separated, so do also specify a custom `--datadir` flag. + +``` +$ geth --datadir=path/to/custom/data/folder --bootnodes= +``` + +*Note: Since your network will be completely cut off from the main and test networks, you'll also +need to configure a miner to process transactions and create new blocks for you.* + +#### Running a private miner + +Mining on the public Ethereum network is a complex task as it's only feasible using GPUs, requiring +an OpenCL or CUDA enabled `ethminer` instance. For information on such a setup, please consult the +[EtherMining subreddit](https://www.reddit.com/r/EtherMining/) and the [Genoil miner](https://github.com/Genoil/cpp-ethereum) +repository. + +In a private network setting however, a single CPU miner instance is more than enough for practical +purposes as it can produce a stable stream of blocks at the correct intervals without needing heavy +resources (consider running on a single thread, no need for multiple ones either). To start a Geth +instance for mining, run it with all your usual flags, extended by: + +``` +$ geth --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000 +``` + +Which will start mining bocks and transactions on a single CPU thread, crediting all proceedings to +the account specified by `--etherbase`. You can further tune the mining by changing the default gas +limit blocks converge to (`--targetgaslimit`) and the price transactions are accepted at (`--gasprice`). + +## Contribution + +Thank you for considering to help out with the source code! We welcome contributions from +anyone on the internet, and are grateful for even the smallest of fixes! + +If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request +for the maintainers to review and merge into the main code base. If you wish to submit more +complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum) +to ensure those changes are in line with the general philosophy of the project and/or get some +early feedback which can make both your efforts much lighter as well as our review and merge +procedures quick and simple. + +Please make sure your contributions adhere to our coding guidelines: + + * Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). + * Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. + * Pull requests need to be based on and opened against the `master` branch. + * Commit messages should be prefixed with the package(s) they modify. + * E.g. "eth, rpc: make trace configs optional" + +Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide) +for more details on configuring your environment, managing project dependencies and testing procedures. + +## License + +The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the +[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), also +included in our repository in the `COPYING.LESSER` file. + +The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the +[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also included +in our repository in the `COPYING` file. diff --git a/vendor/github.com/ethereum/go-ethereum/VERSION b/vendor/github.com/ethereum/go-ethereum/VERSION new file mode 100644 index 0000000..dc1e644 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/VERSION @@ -0,0 +1 @@ +1.6.0 diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go new file mode 100644 index 0000000..627a2a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go @@ -0,0 +1,429 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "encoding/json" + "fmt" + "io" + "math/big" + "reflect" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// The ABI holds information about a contract's context and available +// invokable methods. It will allow you to type check function calls and +// packs data accordingly. +type ABI struct { + Constructor Method + Methods map[string]Method + Events map[string]Event +} + +// JSON returns a parsed ABI interface and error if it failed. +func JSON(reader io.Reader) (ABI, error) { + dec := json.NewDecoder(reader) + + var abi ABI + if err := dec.Decode(&abi); err != nil { + return ABI{}, err + } + + return abi, nil +} + +// Pack the given method name to conform the ABI. Method call's data +// will consist of method_id, args0, arg1, ... argN. Method id consists +// of 4 bytes and arguments are all 32 bytes. +// Method ids are created from the first 4 bytes of the hash of the +// methods string signature. (signature = baz(uint32,string32)) +func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { + // Fetch the ABI of the requested method + var method Method + + if name == "" { + method = abi.Constructor + } else { + m, exist := abi.Methods[name] + if !exist { + return nil, fmt.Errorf("method '%s' not found", name) + } + method = m + } + arguments, err := method.pack(method, args...) + if err != nil { + return nil, err + } + // Pack up the method ID too if not a constructor and return + if name == "" { + return arguments, nil + } + return append(method.Id(), arguments...), nil +} + +// toGoSliceType parses the input and casts it to the proper slice defined by the ABI +// argument in T. +func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { + index := i * 32 + // The slice must, at very least be large enough for the index+32 which is exactly the size required + // for the [offset in output, size of offset]. + if index+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32) + } + elem := t.Type.Elem + + // first we need to create a slice of the type + var refSlice reflect.Value + switch elem.T { + case IntTy, UintTy, BoolTy: + // create a new reference slice matching the element type + switch t.Type.Kind { + case reflect.Bool: + refSlice = reflect.ValueOf([]bool(nil)) + case reflect.Uint8: + refSlice = reflect.ValueOf([]uint8(nil)) + case reflect.Uint16: + refSlice = reflect.ValueOf([]uint16(nil)) + case reflect.Uint32: + refSlice = reflect.ValueOf([]uint32(nil)) + case reflect.Uint64: + refSlice = reflect.ValueOf([]uint64(nil)) + case reflect.Int8: + refSlice = reflect.ValueOf([]int8(nil)) + case reflect.Int16: + refSlice = reflect.ValueOf([]int16(nil)) + case reflect.Int32: + refSlice = reflect.ValueOf([]int32(nil)) + case reflect.Int64: + refSlice = reflect.ValueOf([]int64(nil)) + default: + refSlice = reflect.ValueOf([]*big.Int(nil)) + } + case AddressTy: // address must be of slice Address + refSlice = reflect.ValueOf([]common.Address(nil)) + case HashTy: // hash must be of slice hash + refSlice = reflect.ValueOf([]common.Hash(nil)) + case FixedBytesTy: + refSlice = reflect.ValueOf([][]byte(nil)) + default: // no other types are supported + return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) + } + + var slice []byte + var size int + var offset int + if t.Type.IsSlice { + + // get the offset which determines the start of this array ... + offset = int(common.BytesToBig(output[index : index+32]).Uint64()) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + } + + slice = output[offset:] + // ... starting with the size of the array in elements ... + size = int(common.BytesToBig(slice[:32]).Uint64()) + slice = slice[32:] + // ... and make sure that we've at the very least the amount of bytes + // available in the buffer. + if size*32 > len(slice) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) + } + + // reslice to match the required size + slice = slice[:(size * 32)] + } else if t.Type.IsArray { + //get the number of elements in the array + size = t.Type.SliceSize + + //check to make sure array size matches up + if index+32*size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size) + } + //slice is there for a fixed amount of times + slice = output[index : index+size*32] + } + + for i := 0; i < size; i++ { + var ( + inter interface{} // interface type + returnOutput = slice[i*32 : i*32+32] // the return output + ) + + // set inter to the correct type (cast) + switch elem.T { + case IntTy, UintTy: + bigNum := common.BytesToBig(returnOutput) + switch t.Type.Kind { + case reflect.Uint8: + inter = uint8(bigNum.Uint64()) + case reflect.Uint16: + inter = uint16(bigNum.Uint64()) + case reflect.Uint32: + inter = uint32(bigNum.Uint64()) + case reflect.Uint64: + inter = bigNum.Uint64() + case reflect.Int8: + inter = int8(bigNum.Int64()) + case reflect.Int16: + inter = int16(bigNum.Int64()) + case reflect.Int32: + inter = int32(bigNum.Int64()) + case reflect.Int64: + inter = bigNum.Int64() + default: + inter = common.BytesToBig(returnOutput) + } + case BoolTy: + inter = common.BytesToBig(returnOutput).Uint64() > 0 + case AddressTy: + inter = common.BytesToAddress(returnOutput) + case HashTy: + inter = common.BytesToHash(returnOutput) + case FixedBytesTy: + inter = returnOutput + } + // append the item to our reflect slice + refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) + } + + // return the interface + return refSlice.Interface(), nil +} + +// toGoType parses the input and casts it to the proper type defined by the ABI +// argument in T. +func toGoType(i int, t Argument, output []byte) (interface{}, error) { + // we need to treat slices differently + if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy { + return toGoSlice(i, t, output) + } + + index := i * 32 + if index+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32) + } + + // Parse the given index output and check whether we need to read + // a different offset and length based on the type (i.e. string, bytes) + var returnOutput []byte + switch t.Type.T { + case StringTy, BytesTy: // variable arrays are written at the end of the return bytes + // parse offset from which we should start reading + offset := int(common.BytesToBig(output[index : index+32]).Uint64()) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32) + } + // parse the size up until we should be reading + size := int(common.BytesToBig(output[offset : offset+32]).Uint64()) + if offset+32+size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size) + } + + // get the bytes for this return value + returnOutput = output[offset+32 : offset+32+size] + default: + returnOutput = output[index : index+32] + } + + // convert the bytes to whatever is specified by the ABI. + switch t.Type.T { + case IntTy, UintTy: + bigNum := common.BytesToBig(returnOutput) + + // If the type is a integer convert to the integer type + // specified by the ABI. + switch t.Type.Kind { + case reflect.Uint8: + return uint8(bigNum.Uint64()), nil + case reflect.Uint16: + return uint16(bigNum.Uint64()), nil + case reflect.Uint32: + return uint32(bigNum.Uint64()), nil + case reflect.Uint64: + return uint64(bigNum.Uint64()), nil + case reflect.Int8: + return int8(bigNum.Int64()), nil + case reflect.Int16: + return int16(bigNum.Int64()), nil + case reflect.Int32: + return int32(bigNum.Int64()), nil + case reflect.Int64: + return int64(bigNum.Int64()), nil + case reflect.Ptr: + return bigNum, nil + } + case BoolTy: + return common.BytesToBig(returnOutput).Uint64() > 0, nil + case AddressTy: + return common.BytesToAddress(returnOutput), nil + case HashTy: + return common.BytesToHash(returnOutput), nil + case BytesTy, FixedBytesTy, FunctionTy: + return returnOutput, nil + case StringTy: + return string(returnOutput), nil + } + return nil, fmt.Errorf("abi: unknown type %v", t.Type.T) +} + +// these variable are used to determine certain types during type assertion for +// assignment. +var ( + r_interSlice = reflect.TypeOf([]interface{}{}) + r_hash = reflect.TypeOf(common.Hash{}) + r_bytes = reflect.TypeOf([]byte{}) + r_byte = reflect.TypeOf(byte(0)) +) + +// Unpack output in v according to the abi specification +func (abi ABI) Unpack(v interface{}, name string, output []byte) error { + var method = abi.Methods[name] + + if len(output) == 0 { + return fmt.Errorf("abi: unmarshalling empty output") + } + + // make sure the passed value is a pointer + valueOf := reflect.ValueOf(v) + if reflect.Ptr != valueOf.Kind() { + return fmt.Errorf("abi: Unpack(non-pointer %T)", v) + } + + var ( + value = valueOf.Elem() + typ = value.Type() + ) + + if len(method.Outputs) > 1 { + switch value.Kind() { + // struct will match named return values to the struct's field + // names + case reflect.Struct: + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + reflectValue := reflect.ValueOf(marshalledValue) + + for j := 0; j < typ.NumField(); j++ { + field := typ.Field(j) + // TODO read tags: `abi:"fieldName"` + if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] { + if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil { + return err + } + } + } + } + case reflect.Slice: + if !value.Type().AssignableTo(r_interSlice) { + return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v) + } + + // if the slice already contains values, set those instead of the interface slice itself. + if value.Len() > 0 { + if len(method.Outputs) > value.Len() { + return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len()) + } + + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + reflectValue := reflect.ValueOf(marshalledValue) + if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil { + return err + } + } + return nil + } + + // create a new slice and start appending the unmarshalled + // values to the new interface slice. + z := reflect.MakeSlice(typ, 0, len(method.Outputs)) + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + z = reflect.Append(z, reflect.ValueOf(marshalledValue)) + } + value.Set(z) + default: + return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ) + } + + } else { + marshalledValue, err := toGoType(0, method.Outputs[0], output) + if err != nil { + return err + } + if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil { + return err + } + } + + return nil +} + +func (abi *ABI) UnmarshalJSON(data []byte) error { + var fields []struct { + Type string + Name string + Constant bool + Indexed bool + Anonymous bool + Inputs []Argument + Outputs []Argument + } + + if err := json.Unmarshal(data, &fields); err != nil { + return err + } + + abi.Methods = make(map[string]Method) + abi.Events = make(map[string]Event) + for _, field := range fields { + switch field.Type { + case "constructor": + abi.Constructor = Method{ + Inputs: field.Inputs, + } + // empty defaults to function according to the abi spec + case "function", "": + abi.Methods[field.Name] = Method{ + Name: field.Name, + Const: field.Constant, + Inputs: field.Inputs, + Outputs: field.Outputs, + } + case "event": + abi.Events[field.Name] = Event{ + Name: field.Name, + Anonymous: field.Anonymous, + Inputs: field.Inputs, + } + } + } + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi_test.go new file mode 100644 index 0000000..a45bd6c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi_test.go @@ -0,0 +1,1241 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "bytes" + "fmt" + "log" + "math/big" + "reflect" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// formatSilceOutput add padding to the value and adds a size +func formatSliceOutput(v ...[]byte) []byte { + off := common.LeftPadBytes(big.NewInt(int64(len(v))).Bytes(), 32) + output := append(off, make([]byte, 0, len(v)*32)...) + + for _, value := range v { + output = append(output, common.LeftPadBytes(value, 32)...) + } + return output +} + +// quick helper padding +func pad(input []byte, size int, left bool) []byte { + if left { + return common.LeftPadBytes(input, size) + } + return common.RightPadBytes(input, size) +} + +func TestTypeCheck(t *testing.T) { + for i, test := range []struct { + typ string + input interface{} + err string + }{ + {"uint", big.NewInt(1), ""}, + {"int", big.NewInt(1), ""}, + {"uint30", big.NewInt(1), ""}, + {"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"}, + {"uint16", uint16(1), ""}, + {"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"}, + {"uint16[]", []uint16{1, 2, 3}, ""}, + {"uint16[]", [3]uint16{1, 2, 3}, ""}, + {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"}, + {"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"}, + {"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, + {"uint16[3]", []uint16{1, 2, 3}, ""}, + {"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, + {"address[]", []common.Address{{1}}, ""}, + {"address[1]", []common.Address{{1}}, ""}, + {"address[1]", [1]common.Address{{1}}, ""}, + {"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"}, + {"bytes32", [32]byte{}, ""}, + {"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"}, + {"bytes32", common.Hash{1}, ""}, + {"bytes31", [31]byte{}, ""}, + {"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"}, + {"bytes", []byte{0, 1}, ""}, + {"bytes", [2]byte{0, 1}, ""}, + {"bytes", common.Hash{1}, ""}, + {"string", "hello world", ""}, + {"bytes32[]", [][32]byte{{}}, ""}, + {"function", [24]byte{}, ""}, + } { + typ, err := NewType(test.typ) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + + err = typeCheck(typ, reflect.ValueOf(test.input)) + if err != nil && len(test.err) == 0 { + t.Errorf("%d failed. Expected no err but got: %v", i, err) + continue + } + if err == nil && len(test.err) != 0 { + t.Errorf("%d failed. Expected err: %v but got none", i, test.err) + continue + } + + if err != nil && len(test.err) != 0 && err.Error() != test.err { + t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) + } + } +} + +func TestSimpleMethodUnpack(t *testing.T) { + for i, test := range []struct { + def string // definition of the **output** ABI params + marshalledOutput []byte // evm return data + expectedOut interface{} // the expected output + outVar string // the output variable (e.g. uint32, *big.Int, etc) + err string // empty or error if expected + }{ + { + `[ { "type": "uint32" } ]`, + pad([]byte{1}, 32, true), + uint32(1), + "uint32", + "", + }, + { + `[ { "type": "uint32" } ]`, + pad([]byte{1}, 32, true), + nil, + "uint16", + "abi: cannot unmarshal uint32 in to uint16", + }, + { + `[ { "type": "uint17" } ]`, + pad([]byte{1}, 32, true), + nil, + "uint16", + "abi: cannot unmarshal *big.Int in to uint16", + }, + { + `[ { "type": "uint17" } ]`, + pad([]byte{1}, 32, true), + big.NewInt(1), + "*big.Int", + "", + }, + + { + `[ { "type": "int32" } ]`, + pad([]byte{1}, 32, true), + int32(1), + "int32", + "", + }, + { + `[ { "type": "int32" } ]`, + pad([]byte{1}, 32, true), + nil, + "int16", + "abi: cannot unmarshal int32 in to int16", + }, + { + `[ { "type": "int17" } ]`, + pad([]byte{1}, 32, true), + nil, + "int16", + "abi: cannot unmarshal *big.Int in to int16", + }, + { + `[ { "type": "int17" } ]`, + pad([]byte{1}, 32, true), + big.NewInt(1), + "*big.Int", + "", + }, + + { + `[ { "type": "address" } ]`, + pad(pad([]byte{1}, 20, false), 32, true), + common.Address{1}, + "address", + "", + }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "bytes", + "", + }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "hash", + "", + }, + { + `[ { "type": "bytes32" } ]`, + pad([]byte{1}, 32, false), + pad([]byte{1}, 32, false), + "interface", + "", + }, + { + `[ { "type": "function" } ]`, + pad([]byte{1}, 32, false), + [24]byte{1}, + "function", + "", + }, + } { + abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) + abi, err := JSON(strings.NewReader(abiDefinition)) + if err != nil { + t.Errorf("%d failed. %v", i, err) + continue + } + + var outvar interface{} + switch test.outVar { + case "uint8": + var v uint8 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint16": + var v uint16 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint32": + var v uint32 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "uint64": + var v uint64 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int8": + var v int8 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int16": + var v int16 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int32": + var v int32 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "int64": + var v int64 + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "*big.Int": + var v *big.Int + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "address": + var v common.Address + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "bytes": + var v []byte + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "hash": + var v common.Hash + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "function": + var v [24]byte + err = abi.Unpack(&v, "method", test.marshalledOutput) + outvar = v + case "interface": + err = abi.Unpack(&outvar, "method", test.marshalledOutput) + default: + t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar) + continue + } + + if err != nil && len(test.err) == 0 { + t.Errorf("%d failed. Expected no err but got: %v", i, err) + continue + } + if err == nil && len(test.err) != 0 { + t.Errorf("%d failed. Expected err: %v but got none", i, test.err) + continue + } + if err != nil && len(test.err) != 0 && err.Error() != test.err { + t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) + continue + } + + if err == nil { + // bit of an ugly hack for hash type but I don't feel like finding a proper solution + if test.outVar == "hash" { + tmp := outvar.(common.Hash) // without assignment it's unaddressable + outvar = tmp[:] + } + + if !reflect.DeepEqual(test.expectedOut, outvar) { + t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar) + } + } + } +} + +func TestUnpackSetInterfaceSlice(t *testing.T) { + var ( + var1 = new(uint8) + var2 = new(uint8) + ) + out := []interface{}{var1, var2} + abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint8"}, {"type":"uint8"}]}]`)) + if err != nil { + t.Fatal(err) + } + marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...) + err = abi.Unpack(&out, "ints", marshalledReturn) + if err != nil { + t.Fatal(err) + } + if *var1 != 1 { + t.Error("expected var1 to be 1, got", *var1) + } + if *var2 != 2 { + t.Error("expected var2 to be 2, got", *var2) + } + + out = []interface{}{var1} + err = abi.Unpack(&out, "ints", marshalledReturn) + + expErr := "abi: cannot marshal in to slices of unequal size (require: 2, got: 1)" + if err == nil || err.Error() != expErr { + t.Error("expected err:", expErr, "Got:", err) + } +} + +func TestUnpackSetInterfaceArrayOutput(t *testing.T) { + var ( + var1 = new([1]uint32) + var2 = new([1]uint32) + ) + out := []interface{}{var1, var2} + abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`)) + if err != nil { + t.Fatal(err) + } + marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...) + err = abi.Unpack(&out, "ints", marshalledReturn) + if err != nil { + t.Fatal(err) + } + + if *var1 != [1]uint32{1} { + t.Error("expected var1 to be [1], got", *var1) + } + if *var2 != [1]uint32{2} { + t.Error("expected var2 to be [2], got", *var2) + } +} + +func TestPack(t *testing.T) { + for i, test := range []struct { + typ string + + input interface{} + output []byte + }{ + {"uint16", uint16(2), pad([]byte{2}, 32, true)}, + {"uint16[]", []uint16{1, 2}, formatSliceOutput([]byte{1}, []byte{2})}, + {"bytes20", [20]byte{1}, pad([]byte{1}, 32, false)}, + {"uint256[]", []*big.Int{big.NewInt(1), big.NewInt(2)}, formatSliceOutput([]byte{1}, []byte{2})}, + {"address[]", []common.Address{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))}, + {"bytes32[]", []common.Hash{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))}, + {"function", [24]byte{1}, pad([]byte{1}, 32, false)}, + } { + typ, err := NewType(test.typ) + if err != nil { + t.Fatal("unexpected parse error:", err) + } + + output, err := typ.pack(reflect.ValueOf(test.input)) + if err != nil { + t.Fatal("unexpected pack error:", err) + } + + if !bytes.Equal(output, test.output) { + t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output) + } + } +} + +func TestMethodPack(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Fatal(err) + } + + sig := abi.Methods["slice"].Id() + sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + + packed, err := abi.Pack("slice", []uint32{1, 2}) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + var addrA, addrB = common.Address{1}, common.Address{2} + sig = abi.Methods["sliceAddress"].Id() + sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) + + packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB}) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + var addrC, addrD = common.Address{3}, common.Address{4} + sig = abi.Methods["sliceMultiAddress"].Id() + sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrB[:], 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + sig = append(sig, common.LeftPadBytes(addrC[:], 32)...) + sig = append(sig, common.LeftPadBytes(addrD[:], 32)...) + + packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD}) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } + + sig = abi.Methods["slice256"].Id() + sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) + sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) + + packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)}) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } +} + +const jsondata = ` +[ + { "type" : "function", "name" : "balance", "constant" : true }, + { "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] } +]` + +const jsondata2 = ` +[ + { "type" : "function", "name" : "balance", "constant" : true }, + { "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }, + { "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] }, + { "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] }, + { "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] }, + { "type" : "function", "name" : "address", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] }, + { "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, + { "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, + { "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, + { "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, + { "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, + { "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }, + { "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] }, + { "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } +]` + +func TestReader(t *testing.T) { + Uint256, _ := NewType("uint256") + exp := ABI{ + Methods: map[string]Method{ + "balance": { + "balance", true, nil, nil, + }, + "send": { + "send", false, []Argument{ + {"amount", Uint256, false}, + }, nil, + }, + }, + } + + abi, err := JSON(strings.NewReader(jsondata)) + if err != nil { + t.Error(err) + } + + // deep equal fails for some reason + t.Skip() + if !reflect.DeepEqual(abi, exp) { + t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp) + } +} + +func TestTestNumbers(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + if _, err := abi.Pack("balance"); err != nil { + t.Error(err) + } + + if _, err := abi.Pack("balance", 1); err == nil { + t.Error("expected error for balance(1)") + } + + if _, err := abi.Pack("doesntexist", nil); err == nil { + t.Errorf("doesntexist shouldn't exist") + } + + if _, err := abi.Pack("doesntexist", 1); err == nil { + t.Errorf("doesntexist(1) shouldn't exist") + } + + if _, err := abi.Pack("send", big.NewInt(1000)); err != nil { + t.Error(err) + } + + i := new(int) + *i = 1000 + if _, err := abi.Pack("send", i); err == nil { + t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int") + } + + if _, err := abi.Pack("test", uint32(1000)); err != nil { + t.Error(err) + } +} + +func TestTestString(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + if _, err := abi.Pack("string", "hello world"); err != nil { + t.Error(err) + } +} + +func TestTestBool(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + if _, err := abi.Pack("bool", true); err != nil { + t.Error(err) + } +} + +func TestTestSlice(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + slice := make([]uint64, 2) + if _, err := abi.Pack("uint64[2]", slice); err != nil { + t.Error(err) + } + + if _, err := abi.Pack("uint64[]", slice); err != nil { + t.Error(err) + } +} + +func TestMethodSignature(t *testing.T) { + String, _ := NewType("string") + m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil} + exp := "foo(string,string)" + if m.Sig() != exp { + t.Error("signature mismatch", exp, "!=", m.Sig()) + } + + idexp := crypto.Keccak256([]byte(exp))[:4] + if !bytes.Equal(m.Id(), idexp) { + t.Errorf("expected ids to match %x != %x", m.Id(), idexp) + } + + uintt, _ := NewType("uint") + m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil} + exp = "foo(uint256)" + if m.Sig() != exp { + t.Error("signature mismatch", exp, "!=", m.Sig()) + } +} + +func TestMultiPack(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4] + sig = append(sig, make([]byte, 64)...) + sig[35] = 10 + sig[67] = 11 + + packed, err := abi.Pack("bar", uint32(10), uint16(11)) + if err != nil { + t.Error(err) + t.FailNow() + } + + if !bytes.Equal(packed, sig) { + t.Errorf("expected %x got %x", sig, packed) + } +} + +func ExampleJSON() { + const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + log.Fatalln(err) + } + out, err := abi.Pack("isBar", common.HexToAddress("01")) + if err != nil { + log.Fatalln(err) + } + + fmt.Printf("%x\n", out) + // Output: + // 1f2c40920000000000000000000000000000000000000000000000000000000000000001 +} + +func TestInputVariableInputLength(t *testing.T) { + const definition = `[ + { "type" : "function", "name" : "strOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] }, + { "type" : "function", "name" : "bytesOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] }, + { "type" : "function", "name" : "strTwo", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] } + ]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + // test one string + strin := "hello world" + strpack, err := abi.Pack("strOne", strin) + if err != nil { + t.Error(err) + } + + offset := make([]byte, 32) + offset[31] = 32 + length := make([]byte, 32) + length[31] = byte(len(strin)) + value := common.RightPadBytes([]byte(strin), 32) + exp := append(offset, append(length, value...)...) + + // ignore first 4 bytes of the output. This is the function identifier + strpack = strpack[4:] + if !bytes.Equal(strpack, exp) { + t.Errorf("expected %x, got %x\n", exp, strpack) + } + + // test one bytes + btspack, err := abi.Pack("bytesOne", []byte(strin)) + if err != nil { + t.Error(err) + } + // ignore first 4 bytes of the output. This is the function identifier + btspack = btspack[4:] + if !bytes.Equal(btspack, exp) { + t.Errorf("expected %x, got %x\n", exp, btspack) + } + + // test two strings + str1 := "hello" + str2 := "world" + str2pack, err := abi.Pack("strTwo", str1, str2) + if err != nil { + t.Error(err) + } + + offset1 := make([]byte, 32) + offset1[31] = 64 + length1 := make([]byte, 32) + length1[31] = byte(len(str1)) + value1 := common.RightPadBytes([]byte(str1), 32) + + offset2 := make([]byte, 32) + offset2[31] = 128 + length2 := make([]byte, 32) + length2[31] = byte(len(str2)) + value2 := common.RightPadBytes([]byte(str2), 32) + + exp2 := append(offset1, offset2...) + exp2 = append(exp2, append(length1, value1...)...) + exp2 = append(exp2, append(length2, value2...)...) + + // ignore first 4 bytes of the output. This is the function identifier + str2pack = str2pack[4:] + if !bytes.Equal(str2pack, exp2) { + t.Errorf("expected %x, got %x\n", exp, str2pack) + } + + // test two strings, first > 32, second < 32 + str1 = strings.Repeat("a", 33) + str2pack, err = abi.Pack("strTwo", str1, str2) + if err != nil { + t.Error(err) + } + + offset1 = make([]byte, 32) + offset1[31] = 64 + length1 = make([]byte, 32) + length1[31] = byte(len(str1)) + value1 = common.RightPadBytes([]byte(str1), 64) + offset2[31] = 160 + + exp2 = append(offset1, offset2...) + exp2 = append(exp2, append(length1, value1...)...) + exp2 = append(exp2, append(length2, value2...)...) + + // ignore first 4 bytes of the output. This is the function identifier + str2pack = str2pack[4:] + if !bytes.Equal(str2pack, exp2) { + t.Errorf("expected %x, got %x\n", exp, str2pack) + } + + // test two strings, first > 32, second >32 + str1 = strings.Repeat("a", 33) + str2 = strings.Repeat("a", 33) + str2pack, err = abi.Pack("strTwo", str1, str2) + if err != nil { + t.Error(err) + } + + offset1 = make([]byte, 32) + offset1[31] = 64 + length1 = make([]byte, 32) + length1[31] = byte(len(str1)) + value1 = common.RightPadBytes([]byte(str1), 64) + + offset2 = make([]byte, 32) + offset2[31] = 160 + length2 = make([]byte, 32) + length2[31] = byte(len(str2)) + value2 = common.RightPadBytes([]byte(str2), 64) + + exp2 = append(offset1, offset2...) + exp2 = append(exp2, append(length1, value1...)...) + exp2 = append(exp2, append(length2, value2...)...) + + // ignore first 4 bytes of the output. This is the function identifier + str2pack = str2pack[4:] + if !bytes.Equal(str2pack, exp2) { + t.Errorf("expected %x, got %x\n", exp, str2pack) + } +} + +func TestDefaultFunctionParsing(t *testing.T) { + const definition = `[{ "name" : "balance" }]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + if _, ok := abi.Methods["balance"]; !ok { + t.Error("expected 'balance' to be present") + } +} + +func TestBareEvents(t *testing.T) { + const definition = `[ + { "type" : "event", "name" : "balance" }, + { "type" : "event", "name" : "anon", "anonymous" : true}, + { "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] } + ]` + + arg0, _ := NewType("uint256") + arg1, _ := NewType("address") + + expectedEvents := map[string]struct { + Anonymous bool + Args []Argument + }{ + "balance": {false, nil}, + "anon": {true, nil}, + "args": {false, []Argument{ + {Name: "arg0", Type: arg0, Indexed: false}, + {Name: "arg1", Type: arg1, Indexed: true}, + }}, + } + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + if len(abi.Events) != len(expectedEvents) { + t.Fatalf("invalid number of events after parsing, want %d, got %d", len(expectedEvents), len(abi.Events)) + } + + for name, exp := range expectedEvents { + got, ok := abi.Events[name] + if !ok { + t.Errorf("could not found event %s", name) + continue + } + if got.Anonymous != exp.Anonymous { + t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous) + } + if len(got.Inputs) != len(exp.Args) { + t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs)) + continue + } + for i, arg := range exp.Args { + if arg.Name != got.Inputs[i].Name { + t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name) + } + if arg.Indexed != got.Inputs[i].Indexed { + t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed) + } + if arg.Type.T != got.Inputs[i].Type.T { + t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T) + } + } + } +} + +func TestMultiReturnWithStruct(t *testing.T) { + const definition = `[ + { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + // using buff to make the code readable + buff := new(bytes.Buffer) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) + stringOut := "hello" + buff.Write(common.RightPadBytes([]byte(stringOut), 32)) + + var inter struct { + Int *big.Int + String string + } + err = abi.Unpack(&inter, "multi", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if inter.Int == nil || inter.Int.Cmp(big.NewInt(1)) != 0 { + t.Error("expected Int to be 1 got", inter.Int) + } + + if inter.String != stringOut { + t.Error("expected String to be", stringOut, "got", inter.String) + } + + var reversed struct { + String string + Int *big.Int + } + + err = abi.Unpack(&reversed, "multi", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if reversed.Int == nil || reversed.Int.Cmp(big.NewInt(1)) != 0 { + t.Error("expected Int to be 1 got", reversed.Int) + } + + if reversed.String != stringOut { + t.Error("expected String to be", stringOut, "got", reversed.String) + } +} + +func TestMultiReturnWithSlice(t *testing.T) { + const definition = `[ + { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + // using buff to make the code readable + buff := new(bytes.Buffer) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) + stringOut := "hello" + buff.Write(common.RightPadBytes([]byte(stringOut), 32)) + + var inter []interface{} + err = abi.Unpack(&inter, "multi", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if len(inter) != 2 { + t.Fatal("expected 2 results got", len(inter)) + } + + if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 { + t.Error("expected index 0 to be 1 got", num) + } + + if str, ok := inter[1].(string); !ok || str != stringOut { + t.Error("expected index 1 to be", stringOut, "got", str) + } +} + +func TestMarshalArrays(t *testing.T) { + const definition = `[ + { "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] }, + { "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] } + ]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + + output := common.LeftPadBytes([]byte{1}, 32) + + var bytes10 [10]byte + err = abi.Unpack(&bytes10, "bytes32", output) + if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" { + t.Error("expected error or bytes32 not be assignable to bytes10:", err) + } + + var bytes32 [32]byte + err = abi.Unpack(&bytes32, "bytes32", output) + if err != nil { + t.Error("didn't expect error:", err) + } + if !bytes.Equal(bytes32[:], output) { + t.Error("expected bytes32[31] to be 1 got", bytes32[31]) + } + + type ( + B10 [10]byte + B32 [32]byte + ) + + var b10 B10 + err = abi.Unpack(&b10, "bytes32", output) + if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" { + t.Error("expected error or bytes32 not be assignable to bytes10:", err) + } + + var b32 B32 + err = abi.Unpack(&b32, "bytes32", output) + if err != nil { + t.Error("didn't expect error:", err) + } + if !bytes.Equal(b32[:], output) { + t.Error("expected bytes32[31] to be 1 got", bytes32[31]) + } + + output[10] = 1 + var shortAssignLong [32]byte + err = abi.Unpack(&shortAssignLong, "bytes10", output) + if err != nil { + t.Error("didn't expect error:", err) + } + if !bytes.Equal(output, shortAssignLong[:]) { + t.Errorf("expected %x to be %x", shortAssignLong, output) + } +} + +func TestUnmarshal(t *testing.T) { + const definition = `[ + { "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] }, + { "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] }, + { "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] }, + { "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] }, + { "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] }, + { "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] }, + { "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] }, + { "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] }, + { "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + t.Fatal(err) + } + buff := new(bytes.Buffer) + + // marshal int + var Int *big.Int + err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + if err != nil { + t.Error(err) + } + + if Int == nil || Int.Cmp(big.NewInt(1)) != 0 { + t.Error("expected Int to be 1 got", Int) + } + + // marshal bool + var Bool bool + err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + if err != nil { + t.Error(err) + } + + if !Bool { + t.Error("expected Bool to be true") + } + + // marshal dynamic bytes max length 32 + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + bytesOut := common.RightPadBytes([]byte("hello"), 32) + buff.Write(bytesOut) + + var Bytes []byte + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(Bytes, bytesOut) { + t.Errorf("expected %x got %x", bytesOut, Bytes) + } + + // marshall dynamic bytes max length 64 + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) + bytesOut = common.RightPadBytes([]byte("hello"), 64) + buff.Write(bytesOut) + + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(Bytes, bytesOut) { + t.Errorf("expected %x got %x", bytesOut, Bytes) + } + + // marshall dynamic bytes max length 63 + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f")) + bytesOut = common.RightPadBytes([]byte("hello"), 63) + buff.Write(bytesOut) + + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(Bytes, bytesOut) { + t.Errorf("expected %x got %x", bytesOut, Bytes) + } + + // marshal dynamic bytes output empty + err = abi.Unpack(&Bytes, "bytes", nil) + if err == nil { + t.Error("expected error") + } + + // marshal dynamic bytes length 5 + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) + buff.Write(common.RightPadBytes([]byte("hello"), 32)) + + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(Bytes, []byte("hello")) { + t.Errorf("expected %x got %x", bytesOut, Bytes) + } + + // marshal dynamic bytes length 5 + buff.Reset() + buff.Write(common.RightPadBytes([]byte("hello"), 32)) + + var hash common.Hash + err = abi.Unpack(&hash, "fixed", buff.Bytes()) + if err != nil { + t.Error(err) + } + + helloHash := common.BytesToHash(common.RightPadBytes([]byte("hello"), 32)) + if hash != helloHash { + t.Errorf("Expected %x to equal %x", hash, helloHash) + } + + // marshal error + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + if err == nil { + t.Error("expected error") + } + + err = abi.Unpack(&Bytes, "multi", make([]byte, 64)) + if err == nil { + t.Error("expected error") + } + + // marshal mixed bytes + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) + fixed := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001") + buff.Write(fixed) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) + bytesOut = common.RightPadBytes([]byte("hello"), 32) + buff.Write(bytesOut) + + var out []interface{} + err = abi.Unpack(&out, "mixedBytes", buff.Bytes()) + if err != nil { + t.Fatal("didn't expect error:", err) + } + + if !bytes.Equal(bytesOut, out[0].([]byte)) { + t.Errorf("expected %x, got %x", bytesOut, out[0]) + } + + if !bytes.Equal(fixed, out[1].([]byte)) { + t.Errorf("expected %x, got %x", fixed, out[1]) + } + + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003")) + // marshal int array + var intArray [3]*big.Int + err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes()) + if err != nil { + t.Error(err) + } + var testAgainstIntArray [3]*big.Int + testAgainstIntArray[0] = big.NewInt(1) + testAgainstIntArray[1] = big.NewInt(2) + testAgainstIntArray[2] = big.NewInt(3) + + for i, Int := range intArray { + if Int.Cmp(testAgainstIntArray[i]) != 0 { + t.Errorf("expected %v, got %v", testAgainstIntArray[i], Int) + } + } + // marshal address slice + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size + buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) + + var outAddr []common.Address + err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + if err != nil { + t.Fatal("didn't expect error:", err) + } + + if len(outAddr) != 1 { + t.Fatal("expected 1 item, got", len(outAddr)) + } + + if outAddr[0] != (common.Address{1}) { + t.Errorf("expected %x, got %x", common.Address{1}, outAddr[0]) + } + + // marshal multiple address slice + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size + buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size + buff.Write(common.Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000")) + buff.Write(common.Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000")) + + var outAddrStruct struct { + A []common.Address + B []common.Address + } + err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes()) + if err != nil { + t.Fatal("didn't expect error:", err) + } + + if len(outAddrStruct.A) != 1 { + t.Fatal("expected 1 item, got", len(outAddrStruct.A)) + } + + if outAddrStruct.A[0] != (common.Address{1}) { + t.Errorf("expected %x, got %x", common.Address{1}, outAddrStruct.A[0]) + } + + if len(outAddrStruct.B) != 2 { + t.Fatal("expected 1 item, got", len(outAddrStruct.B)) + } + + if outAddrStruct.B[0] != (common.Address{2}) { + t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0]) + } + if outAddrStruct.B[1] != (common.Address{3}) { + t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1]) + } + + // marshal invalid address slice + buff.Reset() + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100")) + + err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + if err == nil { + t.Fatal("expected error:", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go new file mode 100644 index 0000000..4691318 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go @@ -0,0 +1,51 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "encoding/json" + "fmt" +) + +// Argument holds the name of the argument and the corresponding type. +// Types are used when packing and testing arguments. +type Argument struct { + Name string + Type Type + Indexed bool // indexed is only used by events +} + +func (a *Argument) UnmarshalJSON(data []byte) error { + var extarg struct { + Name string + Type string + Indexed bool + } + err := json.Unmarshal(data, &extarg) + if err != nil { + return fmt.Errorf("argument json err: %v", err) + } + + a.Type, err = NewType(extarg.Type) + if err != nil { + return err + } + a.Name = extarg.Name + a.Indexed = extarg.Indexed + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go new file mode 100644 index 0000000..e6bb0c3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/auth.go @@ -0,0 +1,62 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "crypto/ecdsa" + "errors" + "io" + "io/ioutil" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// NewTransactor is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { + json, err := ioutil.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactor(key.PrivateKey), nil +} + +// NewKeyedTransactor is a utility method to easily create a transaction signer +// from a single private key. +func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + return &TransactOpts{ + From: keyAddr, + Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, errors.New("not authorized to sign this account") + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go new file mode 100644 index 0000000..4509e22 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backend.go @@ -0,0 +1,97 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "golang.org/x/net/context" +) + +var ( + // ErrNoCode is returned by call and transact operations for which the requested + // recipient contract to operate on does not exist in the state db or does not + // have any code associated with it (i.e. suicided). + ErrNoCode = errors.New("no contract code at given address") + + // This error is raised when attempting to perform a pending state action + // on a backend that doesn't implement PendingContractCaller. + ErrNoPendingState = errors.New("backend does not support pending state") + + // This error is returned by WaitDeployed if contract creation leaves an + // empty contract behind. + ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") +) + +// ContractCaller defines the methods needed to allow operating with contract on a read +// only basis. +type ContractCaller interface { + // CodeAt returns the code of the given account. This is needed to differentiate + // between contract internal errors and the local chain being out of sync. + CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) + // ContractCall executes an Ethereum contract call with the specified data as the + // input. + CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) +} + +// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. +type DeployBackend interface { + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) +} + +// PendingContractCaller defines methods to perform contract calls on the pending state. +// Call will try to discover this interface when access to the pending state is requested. +// If the backend does not support the pending state, Call returns ErrNoPendingState. +type PendingContractCaller interface { + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) + // PendingCallContract executes an Ethereum contract call against the pending state. + PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) +} + +// ContractTransactor defines the methods needed to allow operating with contract +// on a write only basis. Beside the transacting method, the remainder are helpers +// used when the user does not provide some needed values, but rather leaves it up +// to the transactor to decide. +type ContractTransactor interface { + // PendingCodeAt returns the code of the given account in the pending state. + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + // PendingNonceAt retrieves the current pending nonce associated with an account. + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + // SuggestGasPrice retrieves the currently suggested gas price to allow a timely + // execution of a transaction. + SuggestGasPrice(ctx context.Context) (*big.Int, error) + // EstimateGas tries to estimate the gas needed to execute a specific + // transaction based on the current pending state of the backend blockchain. + // There is no guarantee that this is the true gas limit requirement as other + // transactions may be added or removed by miners, but it should provide a basis + // for setting a reasonable default. + EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) + // SendTransaction injects the transaction into the pending pool for execution. + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +// ContractBackend defines the methods needed to work with contracts on a read-write basis. +type ContractBackend interface { + ContractCaller + ContractTransactor +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go new file mode 100644 index 0000000..1c34ba0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go @@ -0,0 +1,298 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package backends + +import ( + "errors" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "golang.org/x/net/context" +) + +// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) +var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)} + +// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. +var _ bind.ContractBackend = (*SimulatedBackend)(nil) + +var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") + +// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in +// the background. Its main purpose is to allow easily testing contract bindings. +type SimulatedBackend struct { + database ethdb.Database // In memory database to store our testing data + blockchain *core.BlockChain // Ethereum blockchain to handle the consensus + + mu sync.Mutex + pendingBlock *types.Block // Currently pending block that will be imported on request + pendingState *state.StateDB // Currently pending state that will be the active on on request + + config *params.ChainConfig +} + +// NewSimulatedBackend creates a new binding backend using a simulated blockchain +// for testing purposes. +func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { + database, _ := ethdb.NewMemDatabase() + core.WriteGenesisBlockForTesting(database, accounts...) + blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux), vm.Config{}) + backend := &SimulatedBackend{database: database, blockchain: blockchain} + backend.rollback() + return backend +} + +// Commit imports all the pending transactions as a single block and starts a +// fresh new state. +func (b *SimulatedBackend) Commit() { + b.mu.Lock() + defer b.mu.Unlock() + + if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { + panic(err) // This cannot happen unless the simulator is wrong, fail in that case + } + b.rollback() +} + +// Rollback aborts all pending transactions, reverting to the last committed state. +func (b *SimulatedBackend) Rollback() { + b.mu.Lock() + defer b.mu.Unlock() + + b.rollback() +} + +func (b *SimulatedBackend) rollback() { + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + b.pendingBlock = blocks[0] + b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) +} + +// CodeAt returns the code associated with a certain account in the blockchain. +func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + return statedb.GetCode(contract), nil +} + +// BalanceAt returns the wei balance of a certain account in the blockchain. +func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + return statedb.GetBalance(contract), nil +} + +// NonceAt returns the nonce of a certain account in the blockchain. +func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return 0, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + return statedb.GetNonce(contract), nil +} + +// StorageAt returns the value of key in the storage of an account in the blockchain. +func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + statedb, _ := b.blockchain.State() + val := statedb.GetState(contract, key) + return val[:], nil +} + +// TransactionReceipt returns the receipt of a transaction. +func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + return core.GetReceipt(b.database, txHash), nil +} + +// PendingCodeAt returns the code associated with an account in the pending state. +func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.pendingState.GetCode(contract), nil +} + +// CallContract executes a contract call. +func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, errBlockNumberUnsupported + } + state, err := b.blockchain.State() + if err != nil { + return nil, err + } + rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + return rval, err +} + +// PendingCallContract executes a contract call on the pending state. +func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) + + rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + return rval, err +} + +// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving +// the nonce currently pending for the account. +func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.pendingState.GetOrNewStateObject(account).Nonce(), nil +} + +// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated +// chain doens't have miners, we just return a gas price of 1 for any call. +func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return big.NewInt(1), nil +} + +// EstimateGas executes the requested code against the currently pending block/state and +// returns the used amount of gas. +func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() + + // Binary search the gas requirement, as it may be higher than the amount used + var lo, hi uint64 + if call.Gas != nil { + hi = call.Gas.Uint64() + } else { + hi = b.pendingBlock.GasLimit().Uint64() + } + for lo+1 < hi { + // Take a guess at the gas, and check transaction validity + mid := (hi + lo) / 2 + call.Gas = new(big.Int).SetUint64(mid) + + snapshot := b.pendingState.Snapshot() + _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + b.pendingState.RevertToSnapshot(snapshot) + + // If the transaction became invalid or used all the gas (failed), raise the gas limit + if err != nil || gas.Cmp(call.Gas) == 0 { + lo = mid + continue + } + // Otherwise assume the transaction succeeded, lower the gas limit + hi = mid + } + return new(big.Int).SetUint64(hi), nil +} + +// callContract implemens common code between normal and pending contract calls. +// state is modified during execution, make sure to copy it if necessary. +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) + } + if call.Value == nil { + call.Value = new(big.Int) + } + // Set infinite balance to the fake caller account. + from := statedb.GetOrNewStateObject(call.From) + from.SetBalance(common.MaxBig) + // Execute the call. + msg := callmsg{call} + + evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{}) + gaspool := new(core.GasPool).AddGas(common.MaxBig) + ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, err +} + +// SendTransaction updates the pending block to include the given transaction. +// It panics if the transaction is invalid. +func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + b.mu.Lock() + defer b.mu.Unlock() + + sender, err := types.Sender(types.HomesteadSigner{}, tx) + if err != nil { + panic(fmt.Errorf("invalid transaction: %v", err)) + } + nonce := b.pendingState.GetNonce(sender) + if tx.Nonce() != nonce { + panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) + } + + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { + for _, tx := range b.pendingBlock.Transactions() { + block.AddTx(tx) + } + block.AddTx(tx) + }) + b.pendingBlock = blocks[0] + b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) + return nil +} + +// callmsg implements core.Message to allow passing it as a transaction simulator. +type callmsg struct { + ethereum.CallMsg +} + +func (m callmsg) From() common.Address { return m.CallMsg.From } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go new file mode 100644 index 0000000..1f11827 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go @@ -0,0 +1,232 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/net/context" +) + +// SignerFn is a signer function callback when a contract requires a method to +// sign the transaction before submission. +type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error) + +// CallOpts is the collection of options to fine tune a contract call request. +type CallOpts struct { + Pending bool // Whether to operate on the pending state or the last known one + + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// TransactOpts is the collection of authorization data required to create a +// valid Ethereum transaction. +type TransactOpts struct { + From common.Address // Ethereum account to send the transaction from + Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) + Signer SignerFn // Method to use for signing the transaction (mandatory) + + Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) + GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) + GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%) + + Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) +} + +// BoundContract is the base wrapper object that reflects a contract on the +// Ethereum network. It contains a collection of methods that are used by the +// higher level contract bindings to operate. +type BoundContract struct { + address common.Address // Deployment address of the contract on the Ethereum blockchain + abi abi.ABI // Reflect based ABI to access the correct Ethereum methods + caller ContractCaller // Read interface to interact with the blockchain + transactor ContractTransactor // Write interface to interact with the blockchain +} + +// NewBoundContract creates a low level contract interface through which calls +// and transactions may be made through. +func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract { + return &BoundContract{ + address: address, + abi: abi, + caller: caller, + transactor: transactor, + } +} + +// DeployContract deploys a contract onto the Ethereum blockchain and binds the +// deployment address with a Go wrapper. +func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { + // Otherwise try to deploy the contract + c := NewBoundContract(common.Address{}, abi, backend, backend) + + input, err := c.abi.Pack("", params...) + if err != nil { + return common.Address{}, nil, nil, err + } + tx, err := c.transact(opts, nil, append(bytecode, input...)) + if err != nil { + return common.Address{}, nil, nil, err + } + c.address = crypto.CreateAddress(opts.From, tx.Nonce()) + return c.address, tx, c, nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { + // Don't crash on a lazy user + if opts == nil { + opts = new(CallOpts) + } + // Pack the input, call and unpack the results + input, err := c.abi.Pack(method, params...) + if err != nil { + return err + } + var ( + msg = ethereum.CallMsg{To: &c.address, Data: input} + ctx = ensureContext(opts.Context) + code []byte + output []byte + ) + if opts.Pending { + pb, ok := c.caller.(PendingContractCaller) + if !ok { + return ErrNoPendingState + } + output, err = pb.PendingCallContract(ctx, msg) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } else { + output, err = c.caller.CallContract(ctx, msg, nil) + if err == nil && len(output) == 0 { + // Make sure we have a contract to operate on, and bail out otherwise. + if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil { + return err + } else if len(code) == 0 { + return ErrNoCode + } + } + } + if err != nil { + return err + } + return c.abi.Unpack(result, method, output) +} + +// Transact invokes the (paid) contract method with params as input values. +func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + // Otherwise pack up the parameters and invoke the contract + input, err := c.abi.Pack(method, params...) + if err != nil { + return nil, err + } + return c.transact(opts, &c.address, input) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { + return c.transact(opts, &c.address, nil) +} + +// transact executes an actual transaction invocation, first deriving any missing +// authorization fields, and then scheduling the transaction for execution. +func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { + var err error + + // Ensure a valid value field and resolve the account nonce + value := opts.Value + if value == nil { + value = new(big.Int) + } + var nonce uint64 + if opts.Nonce == nil { + nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) + if err != nil { + return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) + } + } else { + nonce = opts.Nonce.Uint64() + } + // Figure out the gas allowance and gas price values + gasPrice := opts.GasPrice + if gasPrice == nil { + gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context)) + if err != nil { + return nil, fmt.Errorf("failed to suggest gas price: %v", err) + } + } + gasLimit := opts.GasLimit + if gasLimit == nil { + // Gas estimation cannot succeed without code for method invocations + if contract != nil { + if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil { + return nil, err + } else if len(code) == 0 { + return nil, ErrNoCode + } + } + // If the contract surely has code (or code is not needed), estimate the transaction + msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} + gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas needed: %v", err) + } + } + // Create the transaction, sign it and schedule it for execution + var rawTx *types.Transaction + if contract == nil { + rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input) + } else { + rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input) + } + if opts.Signer == nil { + return nil, errors.New("no signer to authorize the transaction with") + } + signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx) + if err != nil { + return nil, err + } + if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { + return nil, err + } + return signedTx, nil +} + +func ensureContext(ctx context.Context) context.Context { + if ctx == nil { + return context.TODO() + } + return ctx +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go new file mode 100644 index 0000000..73e95e0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go @@ -0,0 +1,329 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package bind generates Ethereum contract Go bindings. +// +// Detailed usage document and tutorial available on the go-ethereum Wiki page: +// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts +package bind + +import ( + "bytes" + "fmt" + "regexp" + "strings" + "text/template" + "unicode" + + "github.com/ethereum/go-ethereum/accounts/abi" + "golang.org/x/tools/imports" +) + +// Lang is a target programming language selector to generate bindings for. +type Lang int + +const ( + LangGo Lang = iota + LangJava + LangObjC +) + +// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant +// to be used as is in client code, but rather as an intermediate struct which +// enforces compile time type safety and naming convention opposed to having to +// manually maintain hard coded strings that break on runtime. +func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) { + // Process each individual contract requested binding + contracts := make(map[string]*tmplContract) + + for i := 0; i < len(types); i++ { + // Parse the actual ABI to generate the binding for + evmABI, err := abi.JSON(strings.NewReader(abis[i])) + if err != nil { + return "", err + } + // Strip any whitespace from the JSON ABI + strippedABI := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 + } + return r + }, abis[i]) + + // Extract the call and transact methods, and sort them alphabetically + var ( + calls = make(map[string]*tmplMethod) + transacts = make(map[string]*tmplMethod) + ) + for _, original := range evmABI.Methods { + // Normalize the method for capital cases and non-anonymous inputs/outputs + normalized := original + normalized.Name = methodNormalizer[lang](original.Name) + + normalized.Inputs = make([]abi.Argument, len(original.Inputs)) + copy(normalized.Inputs, original.Inputs) + for j, input := range normalized.Inputs { + if input.Name == "" { + normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) + } + } + normalized.Outputs = make([]abi.Argument, len(original.Outputs)) + copy(normalized.Outputs, original.Outputs) + for j, output := range normalized.Outputs { + if output.Name != "" { + normalized.Outputs[j].Name = capitalise(output.Name) + } + } + // Append the methods to the call or transact lists + if original.Const { + calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} + } else { + transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} + } + } + contracts[types[i]] = &tmplContract{ + Type: capitalise(types[i]), + InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), + InputBin: strings.TrimSpace(bytecodes[i]), + Constructor: evmABI.Constructor, + Calls: calls, + Transacts: transacts, + } + } + // Generate the contract template data content and render it + data := &tmplData{ + Package: pkg, + Contracts: contracts, + } + buffer := new(bytes.Buffer) + + funcs := map[string]interface{}{ + "bindtype": bindType[lang], + "namedtype": namedType[lang], + "capitalise": capitalise, + "decapitalise": decapitalise, + } + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) + if err := tmpl.Execute(buffer, data); err != nil { + return "", err + } + // For Go bindings pass the code through goimports to clean it up and double check + if lang == LangGo { + code, err := imports.Process("", buffer.Bytes(), nil) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil + } + // For all others just return as is for now + return string(buffer.Bytes()), nil +} + +// bindType is a set of type binders that convert Solidity types to some supported +// programming language. +var bindType = map[Lang]func(kind abi.Type) string{ + LangGo: bindTypeGo, + LangJava: bindTypeJava, +} + +// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping +// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. *big.Int). +func bindTypeGo(kind abi.Type) string { + stringKind := kind.String() + + switch { + case strings.HasPrefix(stringKind, "address"): + parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + return fmt.Sprintf("%scommon.Address", parts[1]) + + case strings.HasPrefix(stringKind, "bytes"): + parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + return fmt.Sprintf("%s[%s]byte", parts[2], parts[1]) + + case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): + parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 4 { + return stringKind + } + switch parts[2] { + case "8", "16", "32", "64": + return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2]) + } + return fmt.Sprintf("%s*big.Int", parts[3]) + + case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"): + parts := regexp.MustCompile(`([a-z]+)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + return fmt.Sprintf("%s%s", parts[2], parts[1]) + + default: + return stringKind + } +} + +// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping +// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly +// mapped will use an upscaled type (e.g. BigDecimal). +func bindTypeJava(kind abi.Type) string { + stringKind := kind.String() + + switch { + case strings.HasPrefix(stringKind, "address"): + parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("Address") + } + return fmt.Sprintf("Addresses") + + case strings.HasPrefix(stringKind, "bytes"): + parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + if parts[2] != "" { + return "byte[][]" + } + return "byte[]" + + case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): + parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 4 { + return stringKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[1] == "" { + if parts[3] == "" { + return fmt.Sprintf("int%s", parts[2]) + } + return fmt.Sprintf("int%s[]", parts[2]) + } + } + if parts[3] == "" { + return fmt.Sprintf("BigInt") + } + return fmt.Sprintf("BigInts") + + case strings.HasPrefix(stringKind, "bool"): + parts := regexp.MustCompile(`bool(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("bool") + } + return fmt.Sprintf("bool[]") + + case strings.HasPrefix(stringKind, "string"): + parts := regexp.MustCompile(`string(\[[0-9]*\])?`).FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + if parts[1] == "" { + return fmt.Sprintf("String") + } + return fmt.Sprintf("String[]") + + default: + return stringKind + } +} + +// namedType is a set of functions that transform language specific types to +// named versions that my be used inside method names. +var namedType = map[Lang]func(string, abi.Type) string{ + LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, + LangJava: namedTypeJava, +} + +// namedTypeJava converts some primitive data types to named variants that can +// be used as parts of method names. +func namedTypeJava(javaKind string, solKind abi.Type) string { + switch javaKind { + case "byte[]": + return "Binary" + case "byte[][]": + return "Binaries" + case "string": + return "String" + case "string[]": + return "Strings" + case "bool": + return "Bool" + case "bool[]": + return "Bools" + case "BigInt": + parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) + if len(parts) != 4 { + return javaKind + } + switch parts[2] { + case "8", "16", "32", "64": + if parts[3] == "" { + return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) + } + return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) + + default: + return javaKind + } + default: + return javaKind + } +} + +// methodNormalizer is a name transformer that modifies Solidity method names to +// conform to target language naming concentions. +var methodNormalizer = map[Lang]func(string) string{ + LangGo: capitalise, + LangJava: decapitalise, +} + +// capitalise makes the first character of a string upper case. +func capitalise(input string) string { + return strings.ToUpper(input[:1]) + input[1:] +} + +// decapitalise makes the first character of a string lower case. +func decapitalise(input string) string { + return strings.ToLower(input[:1]) + input[1:] +} + +// structured checks whether a method has enough information to return a proper +// Go struct ot if flat returns are needed. +func structured(method abi.Method) bool { + if len(method.Outputs) < 2 { + return false + } + for _, out := range method.Outputs { + if out.Name == "" { + return false + } + } + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind_test.go new file mode 100644 index 0000000..eb46bc0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind_test.go @@ -0,0 +1,467 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "golang.org/x/tools/imports" +) + +var bindTests = []struct { + name string + contract string + bytecode string + abi string + tester string +}{ + // Test that the binding is available in combined and separate forms too + { + `Empty`, + `contract NilContract {}`, + `606060405260068060106000396000f3606060405200`, + `[]`, + ` + if b, err := NewEmpty(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("combined binding (%v) nil or error (%v) not nil", b, nil) + } + if b, err := NewEmptyCaller(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("caller binding (%v) nil or error (%v) not nil", b, nil) + } + if b, err := NewEmptyTransactor(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("transactor binding (%v) nil or error (%v) not nil", b, nil) + } + `, + }, + // Test that all the official sample contracts bind correctly + { + `Token`, + `https://ethereum.org/token`, + `60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`, + `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`, + ` + if b, err := NewToken(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) + } + `, + }, + { + `Crowdsale`, + `https://ethereum.org/crowdsale`, + `606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`, + `[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`, + ` + if b, err := NewCrowdsale(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) + } + `, + }, + { + `DAO`, + `https://ethereum.org/dao`, + `606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`, + `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`, + ` + if b, err := NewDAO(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) + } + `, + }, + // Test that named and anonymous inputs are handled correctly + { + `InputChecker`, ``, ``, + ` + [ + {"type":"function","name":"noInput","constant":true,"inputs":[],"outputs":[]}, + {"type":"function","name":"namedInput","constant":true,"inputs":[{"name":"str","type":"string"}],"outputs":[]}, + {"type":"function","name":"anonInput","constant":true,"inputs":[{"name":"","type":"string"}],"outputs":[]}, + {"type":"function","name":"namedInputs","constant":true,"inputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}],"outputs":[]}, + {"type":"function","name":"anonInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"","type":"string"}],"outputs":[]}, + {"type":"function","name":"mixedInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"str","type":"string"}],"outputs":[]} + ] + `, + `if b, err := NewInputChecker(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) + } else if false { // Don't run, just compile and test types + var err error + + err = b.NoInput(nil) + err = b.NamedInput(nil, "") + err = b.AnonInput(nil, "") + err = b.NamedInputs(nil, "", "") + err = b.AnonInputs(nil, "", "") + err = b.MixedInputs(nil, "", "") + + fmt.Println(err) + }`, + }, + // Test that named and anonymous outputs are handled correctly + { + `OutputChecker`, ``, ``, + ` + [ + {"type":"function","name":"noOutput","constant":true,"inputs":[],"outputs":[]}, + {"type":"function","name":"namedOutput","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"}]}, + {"type":"function","name":"anonOutput","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"}]}, + {"type":"function","name":"namedOutputs","constant":true,"inputs":[],"outputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}]}, + {"type":"function","name":"anonOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"","type":"string"}]}, + {"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]} + ] + `, + `if b, err := NewOutputChecker(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) + } else if false { // Don't run, just compile and test types + var str1, str2 string + var err error + + err = b.NoOutput(nil) + str1, err = b.NamedOutput(nil) + str1, err = b.AnonOutput(nil) + res, _ := b.NamedOutputs(nil) + str1, str2, err = b.AnonOutputs(nil) + str1, str2, err = b.MixedOutputs(nil) + + fmt.Println(str1, str2, res.Str1, res.Str2, err) + }`, + }, + // Test that contract interactions (deploy, transact and call) generate working code + { + `Interactor`, + ` + contract Interactor { + string public deployString; + string public transactString; + + function Interactor(string str) { + deployString = str; + } + + function transact(string str) { + transactString = str; + } + } + `, + `6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056`, + `[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy an interaction tester contract and call a transaction on it + _, _, interactor, err := DeployInteractor(auth, sim, "Deploy string") + if err != nil { + t.Fatalf("Failed to deploy interactor contract: %v", err) + } + if _, err := interactor.Transact(auth, "Transact string"); err != nil { + t.Fatalf("Failed to transact with interactor contract: %v", err) + } + // Commit all pending transactions in the simulator and check the contract state + sim.Commit() + + if str, err := interactor.DeployString(nil); err != nil { + t.Fatalf("Failed to retrieve deploy string: %v", err) + } else if str != "Deploy string" { + t.Fatalf("Deploy string mismatch: have '%s', want 'Deploy string'", str) + } + if str, err := interactor.TransactString(nil); err != nil { + t.Fatalf("Failed to retrieve transact string: %v", err) + } else if str != "Transact string" { + t.Fatalf("Transact string mismatch: have '%s', want 'Transact string'", str) + } + `, + }, + // Tests that plain values can be properly returned and deserialized + { + `Getter`, + ` + contract Getter { + function getter() constant returns (string, int, bytes32) { + return ("Hi", 1, sha3("")); + } + } + `, + `606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, + `[{"constant":true,"inputs":[],"name":"getter","outputs":[{"name":"","type":"string"},{"name":"","type":"int256"},{"name":"","type":"bytes32"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a tuple tester contract and execute a structured call on it + _, _, getter, err := DeployGetter(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy getter contract: %v", err) + } + sim.Commit() + + if str, num, _, err := getter.Getter(nil); err != nil { + t.Fatalf("Failed to call anonymous field retriever: %v", err) + } else if str != "Hi" || num.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", str, num, "Hi", 1) + } + `, + }, + // Tests that tuples can be properly returned and deserialized + { + `Tupler`, + ` + contract Tupler { + function tuple() constant returns (string a, int b, bytes32 c) { + return ("Hi", 1, sha3("")); + } + } + `, + `606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`, + `[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a tuple tester contract and execute a structured call on it + _, _, tupler, err := DeployTupler(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy tupler contract: %v", err) + } + sim.Commit() + + if res, err := tupler.Tuple(nil); err != nil { + t.Fatalf("Failed to call structure retriever: %v", err) + } else if res.A != "Hi" || res.B.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", res.A, res.B, "Hi", 1) + } + `, + }, + // Tests that arrays/slices can be properly returned and deserialized. + // Only addresses are tested, remainder just compiled to keep the test small. + { + `Slicer`, + ` + contract Slicer { + function echoAddresses(address[] input) constant returns (address[] output) { + return input; + } + function echoInts(int[] input) constant returns (int[] output) { + return input; + } + function echoFancyInts(uint24[23] input) constant returns (uint24[23] output) { + return input; + } + function echoBools(bool[] input) constant returns (bool[] output) { + return input; + } + } + `, + `606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`, + `[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a slice tester contract and execute a n array call on it + _, _, slicer, err := DeploySlicer(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy slicer contract: %v", err) + } + sim.Commit() + + if out, err := slicer.EchoAddresses(nil, []common.Address{auth.From, common.Address{}}); err != nil { + t.Fatalf("Failed to call slice echoer: %v", err) + } else if !reflect.DeepEqual(out, []common.Address{auth.From, common.Address{}}) { + t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{auth.From, common.Address{}}) + } + `, + }, + // Tests that anonymous default methods can be correctly invoked + { + `Defaulter`, + ` + contract Defaulter { + address public caller; + + function() { + caller = msg.sender; + } + } + `, + `6060604052606a8060106000396000f360606040523615601d5760e060020a6000350463fc9c8d3981146040575b605e6000805473ffffffffffffffffffffffffffffffffffffffff191633179055565b606060005473ffffffffffffffffffffffffffffffffffffffff1681565b005b6060908152602090f3`, + `[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a default method invoker contract and execute its default method + _, _, defaulter, err := DeployDefaulter(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy defaulter contract: %v", err) + } + if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil { + t.Fatalf("Failed to invoke default method: %v", err) + } + sim.Commit() + + if caller, err := defaulter.Caller(nil); err != nil { + t.Fatalf("Failed to call address retriever: %v", err) + } else if (caller != auth.From) { + t.Fatalf("Address mismatch: have %v, want %v", caller, auth.From) + } + `, + }, + // Tests that non-existent contracts are reported as such (though only simulator test) + { + `NonExistent`, + ` + contract NonExistent { + function String() constant returns(string) { + return "I don't exist"; + } + } + `, + `6060604052609f8060106000396000f3606060405260e060020a6000350463f97a60058114601a575b005b600060605260c0604052600d60809081527f4920646f6e27742065786973740000000000000000000000000000000000000060a052602060c0908152600d60e081905281906101009060a09080838184600060046012f15050815172ffffffffffffffffffffffffffffffffffffff1916909152505060405161012081900392509050f3`, + `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + ` + // Create a simulator and wrap a non-deployed contract + sim := backends.NewSimulatedBackend() + + nonexistent, err := NewNonExistent(common.Address{}, sim) + if err != nil { + t.Fatalf("Failed to access non-existent contract: %v", err) + } + // Ensure that contract calls fail with the appropriate error + if res, err := nonexistent.String(nil); err == nil { + t.Fatalf("Call succeeded on non-existent contract: %v", res) + } else if (err != bind.ErrNoCode) { + t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode) + } + `, + }, + // Tests that gas estimation works for contracts with weird gas mechanics too. + { + `FunkyGasPattern`, + ` + contract FunkyGasPattern { + string public field; + + function SetField(string value) { + // This check will screw gas estimation! Good, good! + if (msg.gas < 100000) { + throw; + } + field = value; + } + } + `, + `606060405261021c806100126000396000f3606060405260e060020a600035046323fcf32a81146100265780634f28bf0e1461007b575b005b6040805160206004803580820135601f8101849004840285018401909552848452610024949193602493909291840191908190840183828082843750949650505050505050620186a05a101561014e57610002565b6100db60008054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281529291908301828280156102145780601f106101e957610100808354040283529160200191610214565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b505050565b8060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101b557805160ff19168380011785555b506101499291505b808211156101e557600081556001016101a1565b82800160010185558215610199579182015b828111156101995782518260005055916020019190600101906101c7565b5090565b820191906000526020600020905b8154815290600101906020018083116101f757829003601f168201915b50505050508156`, + `[{"constant":false,"inputs":[{"name":"value","type":"string"}],"name":"SetField","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"field","outputs":[{"name":"","type":"string"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + + // Deploy a funky gas pattern contract + _, _, limiter, err := DeployFunkyGasPattern(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy funky contract: %v", err) + } + sim.Commit() + + // Set the field with automatic estimation and check that it succeeds + auth.GasLimit = nil + if _, err := limiter.SetField(auth, "automatic"); err != nil { + t.Fatalf("Failed to call automatically gased transaction: %v", err) + } + sim.Commit() + + if field, _ := limiter.Field(nil); field != "automatic" { + t.Fatalf("Field mismatch: have %v, want %v", field, "automatic") + } + `, + }, +} + +// Tests that packages generated by the binder can be successfully compiled and +// the requested tester run against it. +func TestBindings(t *testing.T) { + // Skip the test if no Go command can be found + gocmd := runtime.GOROOT() + "/bin/go" + if !common.FileExist(gocmd) { + t.Skip("go sdk not found for testing") + } + // Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845) + linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend())\n}") + linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil) + if err != nil { + t.Fatalf("failed check for goimports symlink bug: %v", err) + } + if !strings.Contains(string(linkTestDeps), "go-ethereum") { + t.Skip("symlinked environment doesn't support bind (https://github.com/golang/go/issues/14845)") + } + // Create a temporary workspace for the test suite + ws, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create temporary workspace: %v", err) + } + defer os.RemoveAll(ws) + + pkg := filepath.Join(ws, "bindtest") + if err = os.MkdirAll(pkg, 0700); err != nil { + t.Fatalf("failed to create package: %v", err) + } + // Generate the test suite for all the contracts + for i, tt := range bindTests { + // Generate the binding and create a Go source file in the workspace + bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest", LangGo) + if err != nil { + t.Fatalf("test %d: failed to generate binding: %v", i, err) + } + if err = ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil { + t.Fatalf("test %d: failed to write binding: %v", i, err) + } + // Generate the test file with the injected test code + code := fmt.Sprintf("package bindtest\nimport \"testing\"\nfunc Test%s(t *testing.T){\n%s\n}", tt.name, tt.tester) + blob, err := imports.Process("", []byte(code), nil) + if err != nil { + t.Fatalf("test %d: failed to generate tests: %v", i, err) + } + if err := ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), blob, 0600); err != nil { + t.Fatalf("test %d: failed to write tests: %v", i, err) + } + } + // Test the entire package and report any failures + cmd := exec.Command(gocmd, "test", "-v") + cmd.Dir = pkg + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("failed to run binding test: %v\n%s", err, out) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go new file mode 100644 index 0000000..64dd598 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go @@ -0,0 +1,369 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import "github.com/ethereum/go-ethereum/accounts/abi" + +// tmplData is the data structure required to fill the binding template. +type tmplData struct { + Package string // Name of the package to place the generated file in + Contracts map[string]*tmplContract // List of contracts to generate into this file +} + +// tmplContract contains the data needed to generate an individual contract binding. +type tmplContract struct { + Type string // Type name of the main contract binding + InputABI string // JSON ABI used as the input to generate the binding from + InputBin string // Optional EVM bytecode used to denetare deploy code from + Constructor abi.Method // Contract constructor for deploy parametrization + Calls map[string]*tmplMethod // Contract calls that only read state data + Transacts map[string]*tmplMethod // Contract calls that write state data +} + +// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed +// and cached data fields. +type tmplMethod struct { + Original abi.Method // Original method as parsed by the abi package + Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns) + Structured bool // Whether the returns should be accumulated into a contract +} + +// tmplSource is language to template mapping containing all the supported +// programming languages the package can generate to. +var tmplSource = map[Lang]string{ + LangGo: tmplSourceGo, + LangJava: tmplSourceJava, +} + +// tmplSourceGo is the Go source template use to generate the contract binding +// based on. +const tmplSourceGo = ` +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package {{.Package}} + +{{range $contract := .Contracts}} + // {{.Type}}ABI is the input ABI used to generate the binding from. + const {{.Type}}ABI = "{{.InputABI}}" + + {{if .InputBin}} + // {{.Type}}Bin is the compiled bytecode used for deploying new contracts. + const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + ` + + // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it. + func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) { + parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}}) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil + } + {{end}} + + // {{.Type}} is an auto generated Go binding around an Ethereum contract. + type {{.Type}} struct { + {{.Type}}Caller // Read-only binding to the contract + {{.Type}}Transactor // Write-only binding to the contract + } + + // {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract. + type {{.Type}}Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls + } + + // {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract. + type {{.Type}}Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls + } + + // {{.Type}}Session is an auto generated Go binding around an Ethereum contract, + // with pre-set call and transact options. + type {{.Type}}Session struct { + Contract *{{.Type}} // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session + } + + // {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract, + // with pre-set call options. + type {{.Type}}CallerSession struct { + Contract *{{.Type}}Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + } + + // {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract, + // with pre-set transact options. + type {{.Type}}TransactorSession struct { + Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session + } + + // {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract. + type {{.Type}}Raw struct { + Contract *{{.Type}} // Generic contract binding to access the raw methods on + } + + // {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. + type {{.Type}}CallerRaw struct { + Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on + } + + // {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. + type {{.Type}}TransactorRaw struct { + Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on + } + + // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract. + func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) { + contract, err := bind{{.Type}}(address, backend, backend) + if err != nil { + return nil, err + } + return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil + } + + // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract. + func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) { + contract, err := bind{{.Type}}(address, caller, nil) + if err != nil { + return nil, err + } + return &{{.Type}}Caller{contract: contract}, nil + } + + // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract. + func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) { + contract, err := bind{{.Type}}(address, nil, transactor) + if err != nil { + return nil, err + } + return &{{.Type}}Transactor{contract: contract}, nil + } + + // bind{{.Type}} binds a generic wrapper to an already deployed contract. + func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil + } + + // Call invokes the (constant) contract method with params as input values and + // sets the output to result. The result type might be a single field for simple + // returns, a slice of interfaces for anonymous returns and a struct for named + // returns. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...) + } + + // Transfer initiates a plain transaction to move funds to the contract, calling + // its default method if one is available. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts) + } + + // Transact invokes the (paid) contract method with params as input values. + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...) + } + + // Call invokes the (constant) contract method with params as input values and + // sets the output to result. The result type might be a single field for simple + // returns, a slice of interfaces for anonymous returns and a struct for named + // returns. + func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...) + } + + // Transfer initiates a plain transaction to move funds to the contract, calling + // its default method if one is available. + func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.contract.Transfer(opts) + } + + // Transact invokes the (paid) contract method with params as input values. + func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...) + } + + {{range .Calls}} + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}},{{end}}{{end}} error) { + {{if .Structured}}ret := new(struct{ + {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}} + {{end}} + }){{else}}var ( + {{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type}}) + {{end}} + ){{end}} + out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}&[]interface{}{ + {{range $i, $_ := .Normalized.Outputs}}ret{{$i}}, + {{end}} + }{{end}}{{end}} + err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err + } + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type}},{{end}} {{end}} error) { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + } + {{end}} + + {{range .Transacts}} + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) { + return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) + } + + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type}} {{end}}) (*types.Transaction, error) { + return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) + } + {{end}} +{{end}} +` + +// tmplSourceJava is the Java source template use to generate the contract binding +// based on. +const tmplSourceJava = ` +// This file is an automatically generated Java binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package {{.Package}}; + +import org.ethereum.geth.*; +import org.ethereum.geth.internal.*; + +{{range $contract := .Contracts}} + public class {{.Type}} { + // ABI is the input ABI used to generate the binding from. + public final static String ABI = "{{.InputABI}}"; + + {{if .InputBin}} + // BYTECODE is the compiled bytecode used for deploying new contracts. + public final static byte[] BYTECODE = "{{.InputBin}}".getBytes(); + + // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it. + public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}}); + {{range $index, $element := .Constructor.Inputs}} + args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args)); + } + + // Internal constructor used by contract deployment. + private {{.Type}}(BoundContract deployment) { + this.Address = deployment.getAddress(); + this.Deployer = deployment.getDeployer(); + this.Contract = deployment; + } + {{end}} + + // Ethereum address where this contract is located at. + public final Address Address; + + // Ethereum transaction in which this contract was deployed (if known!). + public final Transaction Deployer; + + // Contract instance bound to a blockchain address. + private final BoundContract Contract; + + // Creates a new instance of {{.Type}}, bound to a specific deployed contract. + public {{.Type}}(Address address, EthereumClient client) throws Exception { + this(Geth.bindContract(address, ABI, client)); + } + + {{range .Calls}} + {{if gt (len .Normalized.Outputs) 1}} + // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}. + public class {{capitalise .Normalized.Name}}Results { + {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}}; + {{end}} + } + {{end}} + + // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}}); + {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}}); + {{end}} + + if (opts == null) { + opts = Geth.newCallOpts(); + } + this.Contract.call(opts, results, "{{.Original.Name}}", args); + {{if gt (len .Normalized.Outputs) 1}} + {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results(); + {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}(); + {{end}} + return result; + {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}} + {{end}} + } + {{end}} + + {{range .Transacts}} + // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. + // + // Solidity: {{.Original.String}} + public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception { + Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}}); + {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}}); + {{end}} + + return this.Contract.transact(opts, "{{.Original.Name}}" , args); + } + {{end}} + } +{{end}} +` diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go new file mode 100644 index 0000000..bbb6d6a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util.go @@ -0,0 +1,76 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +// WaitMined waits for tx to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + queryTicker := time.NewTicker(1 * time.Second) + defer queryTicker.Stop() + loghash := tx.Hash().Hex()[:8] + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if receipt != nil { + return receipt, nil + } + if err != nil { + glog.V(logger.Detail).Infof("tx %x error: %v", loghash, err) + } else { + glog.V(logger.Detail).Infof("tx %x not yet mined...", loghash) + } + // Wait for the next round. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} + +// WaitDeployed waits for a contract deployment transaction and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { + if tx.To() != nil { + return common.Address{}, fmt.Errorf("tx is not contract creation") + } + receipt, err := WaitMined(ctx, b, tx) + if err != nil { + return common.Address{}, err + } + if receipt.ContractAddress == (common.Address{}) { + return common.Address{}, fmt.Errorf("zero address") + } + // Check that code has indeed been deployed at the address. + // This matters on pre-Homestead chains: OOG in the constructor + // could leave an empty account behind. + code, err := b.CodeAt(ctx, receipt.ContractAddress, nil) + if err == nil && len(code) == 0 { + err = ErrNoCodeAfterDeploy + } + return receipt.ContractAddress, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util_test.go new file mode 100644 index 0000000..f31dbfc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/util_test.go @@ -0,0 +1,93 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind_test + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/net/context" +) + +var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +var waitDeployedTests = map[string]struct { + code string + gas *big.Int + wantAddress common.Address + wantErr error +}{ + "successful deploy": { + code: `6060604052600a8060106000396000f360606040526008565b00`, + gas: big.NewInt(3000000), + wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"), + }, + "empty code": { + code: ``, + gas: big.NewInt(300000), + wantErr: bind.ErrNoCodeAfterDeploy, + wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"), + }, +} + +func TestWaitDeployed(t *testing.T) { + for name, test := range waitDeployedTests { + backend := backends.NewSimulatedBackend(core.GenesisAccount{ + Address: crypto.PubkeyToAddress(testKey.PublicKey), + Balance: big.NewInt(10000000000), + }) + + // Create the transaction. + tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) + + // Wait for it to get mined in the background. + var ( + err error + address common.Address + mined = make(chan struct{}) + ctx = context.Background() + ) + go func() { + address, err = bind.WaitDeployed(ctx, backend, tx) + close(mined) + }() + + // Send and mine the transaction. + backend.SendTransaction(ctx, tx) + backend.Commit() + + select { + case <-mined: + if err != test.wantErr { + t.Errorf("test %q: error mismatch: got %q, want %q", name, err, test.wantErr) + } + if address != test.wantAddress { + t.Errorf("test %q: unexpected contract address %s", name, address.Hex()) + } + case <-time.After(2 * time.Second): + t.Errorf("test %q: timeout", name) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/doc.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/doc.go new file mode 100644 index 0000000..8242068 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/doc.go @@ -0,0 +1,26 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package abi implements the Ethereum ABI (Application Binary +// Interface). +// +// The Ethereum ABI is strongly typed, known at compile time +// and static. This ABI will handle basic type casting; unsigned +// to signed and visa versa. It does not handle slice casting such +// as unsigned slice to signed slice. Bit size type casting is also +// handled. ints with a bit size of 32 will be properly cast to int256, +// etc. +package abi diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/error.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/error.go new file mode 100644 index 0000000..67739c2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/error.go @@ -0,0 +1,79 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "reflect" +) + +// formatSliceString formats the reflection kind with the given slice size +// and returns a formatted string representation. +func formatSliceString(kind reflect.Kind, sliceSize int) string { + if sliceSize == -1 { + return fmt.Sprintf("[]%v", kind) + } + return fmt.Sprintf("[%d]%v", sliceSize, kind) +} + +// sliceTypeCheck checks that the given slice can by assigned to the reflection +// type in t. +func sliceTypeCheck(t Type, val reflect.Value) error { + if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { + return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type()) + } + if t.IsArray && val.Len() != t.SliceSize { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), formatSliceString(val.Type().Elem().Kind(), val.Len())) + } + + if t.Elem.IsSlice { + if val.Len() > 0 { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + } else if t.Elem.IsArray { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + + if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type()) + } + return nil +} + +// typeCheck checks that the given reflection value can be assigned to the reflection +// type in t. +func typeCheck(t Type, value reflect.Value) error { + if t.IsSlice || t.IsArray { + return sliceTypeCheck(t, value) + } + + // Check base type validity. Element types will be checked later on. + if t.Kind != value.Kind() { + return typeErr(t.Kind, value.Kind()) + } + return nil +} + +// varErr returns a formatted error. +func varErr(expected, got reflect.Kind) error { + return typeErr(expected, got) +} + +// typeErr returns a formatted type casting error. +func typeErr(expected, got interface{}) error { + return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/event.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/event.go new file mode 100644 index 0000000..51ab842 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/event.go @@ -0,0 +1,46 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// Event is an event potentially triggered by the EVM's LOG mechanism. The Event +// holds type information (inputs) about the yielded output. Anonymous events +// don't get the signature canonical representation as the first LOG topic. +type Event struct { + Name string + Anonymous bool + Inputs []Argument +} + +// Id returns the canonical representation of the event's signature used by the +// abi definition to identify event names and types. +func (e Event) Id() common.Hash { + types := make([]string, len(e.Inputs)) + i := 0 + for _, input := range e.Inputs { + types[i] = input.Type.String() + i++ + } + return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ","))))) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/event_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/event_test.go new file mode 100644 index 0000000..b5054a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/event_test.go @@ -0,0 +1,56 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestEventId(t *testing.T) { + var table = []struct { + definition string + expectations map[string]common.Hash + }{ + { + definition: `[ + { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint" }] }, + { "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] } + ]`, + expectations: map[string]common.Hash{ + "balance": crypto.Keccak256Hash([]byte("balance(uint256)")), + "check": crypto.Keccak256Hash([]byte("check(address,uint256)")), + }, + }, + } + + for _, test := range table { + abi, err := JSON(strings.NewReader(test.definition)) + if err != nil { + t.Fatal(err) + } + + for name, event := range abi.Events { + if event.Id() != test.expectations[name] { + t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id()) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/method.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/method.go new file mode 100644 index 0000000..d56f3bc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/method.go @@ -0,0 +1,118 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "reflect" + "strings" + + "github.com/ethereum/go-ethereum/crypto" +) + +// Callable method given a `Name` and whether the method is a constant. +// If the method is `Const` no transaction needs to be created for this +// particular Method call. It can easily be simulated using a local VM. +// For example a `Balance()` method only needs to retrieve something +// from the storage and therefor requires no Tx to be send to the +// network. A method such as `Transact` does require a Tx and thus will +// be flagged `true`. +// Input specifies the required input parameters for this gives method. +type Method struct { + Name string + Const bool + Inputs []Argument + Outputs []Argument +} + +func (m Method) pack(method Method, args ...interface{}) ([]byte, error) { + // Make sure arguments match up and pack them + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) + } + // variable input is the output appended at the end of packed + // output. This is used for strings and bytes types input. + var variableInput []byte + + var ret []byte + for i, a := range args { + input := method.Inputs[i] + // pack the input + packed, err := input.Type.pack(reflect.ValueOf(a)) + if err != nil { + return nil, fmt.Errorf("`%s` %v", method.Name, err) + } + + // check for a slice type (string, bytes, slice) + if input.Type.requiresLengthPrefix() { + // calculate the offset + offset := len(method.Inputs)*32 + len(variableInput) + // set the offset + ret = append(ret, packNum(reflect.ValueOf(offset))...) + // Append the packed output to the variable input. The variable input + // will be appended at the end of the input. + variableInput = append(variableInput, packed...) + } else { + // append the packed value to the input + ret = append(ret, packed...) + } + } + // append the variable input at the end of the packed input + ret = append(ret, variableInput...) + + return ret, nil +} + +// Sig returns the methods string signature according to the ABI spec. +// +// Example +// +// function foo(uint32 a, int b) = "foo(uint32,int256)" +// +// Please note that "int" is substitute for its canonical representation "int256" +func (m Method) Sig() string { + types := make([]string, len(m.Inputs)) + i := 0 + for _, input := range m.Inputs { + types[i] = input.Type.String() + i++ + } + return fmt.Sprintf("%v(%v)", m.Name, strings.Join(types, ",")) +} + +func (m Method) String() string { + inputs := make([]string, len(m.Inputs)) + for i, input := range m.Inputs { + inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) + } + outputs := make([]string, len(m.Outputs)) + for i, output := range m.Outputs { + if len(output.Name) > 0 { + outputs[i] = fmt.Sprintf("%v ", output.Name) + } + outputs[i] += output.Type.String() + } + constant := "" + if m.Const { + constant = "constant " + } + return fmt.Sprintf("function %v(%v) %sreturns(%v)", m.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) +} + +func (m Method) Id() []byte { + return crypto.Keccak256([]byte(m.Sig()))[:4] +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers.go new file mode 100644 index 0000000..3d58422 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers.go @@ -0,0 +1,84 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "math/big" + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + big_t = reflect.TypeOf(big.Int{}) + ubig_t = reflect.TypeOf(big.Int{}) + byte_t = reflect.TypeOf(byte(0)) + byte_ts = reflect.TypeOf([]byte(nil)) + uint_t = reflect.TypeOf(uint(0)) + uint8_t = reflect.TypeOf(uint8(0)) + uint16_t = reflect.TypeOf(uint16(0)) + uint32_t = reflect.TypeOf(uint32(0)) + uint64_t = reflect.TypeOf(uint64(0)) + int_t = reflect.TypeOf(int(0)) + int8_t = reflect.TypeOf(int8(0)) + int16_t = reflect.TypeOf(int16(0)) + int32_t = reflect.TypeOf(int32(0)) + int64_t = reflect.TypeOf(int64(0)) + hash_t = reflect.TypeOf(common.Hash{}) + address_t = reflect.TypeOf(common.Address{}) + + uint_ts = reflect.TypeOf([]uint(nil)) + uint8_ts = reflect.TypeOf([]uint8(nil)) + uint16_ts = reflect.TypeOf([]uint16(nil)) + uint32_ts = reflect.TypeOf([]uint32(nil)) + uint64_ts = reflect.TypeOf([]uint64(nil)) + ubig_ts = reflect.TypeOf([]*big.Int(nil)) + + int_ts = reflect.TypeOf([]int(nil)) + int8_ts = reflect.TypeOf([]int8(nil)) + int16_ts = reflect.TypeOf([]int16(nil)) + int32_ts = reflect.TypeOf([]int32(nil)) + int64_ts = reflect.TypeOf([]int64(nil)) + big_ts = reflect.TypeOf([]*big.Int(nil)) +) + +// U256 converts a big Int into a 256bit EVM number. +func U256(n *big.Int) []byte { + return common.LeftPadBytes(common.U256(n).Bytes(), 32) +} + +// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation +func packNum(value reflect.Value) []byte { + switch kind := value.Kind(); kind { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return U256(new(big.Int).SetUint64(value.Uint())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return U256(big.NewInt(value.Int())) + case reflect.Ptr: + return U256(value.Interface().(*big.Int)) + } + return nil +} + +// checks whether the given reflect value is signed. This also works for slices with a number type +func isSigned(v reflect.Value) bool { + switch v.Type() { + case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t: + return true + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers_test.go new file mode 100644 index 0000000..44afe86 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/numbers_test.go @@ -0,0 +1,82 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "bytes" + "math" + "math/big" + "reflect" + "testing" +) + +func TestNumberTypes(t *testing.T) { + ubytes := make([]byte, 32) + ubytes[31] = 1 + + unsigned := U256(big.NewInt(1)) + if !bytes.Equal(unsigned, ubytes) { + t.Errorf("expected %x got %x", ubytes, unsigned) + } +} + +func TestPackNumber(t *testing.T) { + tests := []struct { + value reflect.Value + packed []byte + }{ + // Protocol limits + {reflect.ValueOf(0), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {reflect.ValueOf(1), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {reflect.ValueOf(-1), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}, + + // Type corner cases + {reflect.ValueOf(uint8(math.MaxUint8)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255}}, + {reflect.ValueOf(uint16(math.MaxUint16)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255}}, + {reflect.ValueOf(uint32(math.MaxUint32)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255}}, + {reflect.ValueOf(uint64(math.MaxUint64)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255}}, + + {reflect.ValueOf(int8(math.MaxInt8)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127}}, + {reflect.ValueOf(int16(math.MaxInt16)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255}}, + {reflect.ValueOf(int32(math.MaxInt32)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255}}, + {reflect.ValueOf(int64(math.MaxInt64)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255}}, + + {reflect.ValueOf(int8(math.MinInt8)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128}}, + {reflect.ValueOf(int16(math.MinInt16)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0}}, + {reflect.ValueOf(int32(math.MinInt32)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0}}, + {reflect.ValueOf(int64(math.MinInt64)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0}}, + } + for i, tt := range tests { + packed := packNum(tt.value) + if !bytes.Equal(packed, tt.packed) { + t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed) + } + } + if packed := packNum(reflect.ValueOf("string")); packed != nil { + t.Errorf("expected 'string' to pack to nil. got %x instead", packed) + } +} + +func TestSigned(t *testing.T) { + if isSigned(reflect.ValueOf(uint(10))) { + t.Error("signed") + } + + if !isSigned(reflect.ValueOf(int(10))) { + t.Error("not signed") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/packing.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/packing.go new file mode 100644 index 0000000..5054dcf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/packing.go @@ -0,0 +1,65 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +// packBytesSlice packs the given bytes as [L, V] as the canonical representation +// bytes slice +func packBytesSlice(bytes []byte, l int) []byte { + len := packNum(reflect.ValueOf(l)) + return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...) +} + +// packElement packs the given reflect value according to the abi specification in +// t. +func packElement(t Type, reflectValue reflect.Value) []byte { + switch t.T { + case IntTy, UintTy: + return packNum(reflectValue) + case StringTy: + return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()) + case AddressTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + + return common.LeftPadBytes(reflectValue.Bytes(), 32) + case BoolTy: + if reflectValue.Bool() { + return common.LeftPadBytes(common.Big1.Bytes(), 32) + } else { + return common.LeftPadBytes(common.Big0.Bytes(), 32) + } + case BytesTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()) + case FixedBytesTy, FunctionTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + + return common.RightPadBytes(reflectValue.Bytes(), 32) + } + panic("abi: fatal error") +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go new file mode 100644 index 0000000..7970ba8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/reflect.go @@ -0,0 +1,93 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "reflect" +) + +// indirect recursively dereferences the value until it either gets the value +// or finds a big.Int +func indirect(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t { + return indirect(v.Elem()) + } + return v +} + +// reflectIntKind returns the reflect using the given size and +// unsignedness. +func reflectIntKind(unsigned bool, size int) reflect.Kind { + switch size { + case 8: + if unsigned { + return reflect.Uint8 + } + return reflect.Int8 + case 16: + if unsigned { + return reflect.Uint16 + } + return reflect.Int16 + case 32: + if unsigned { + return reflect.Uint32 + } + return reflect.Int32 + case 64: + if unsigned { + return reflect.Uint64 + } + return reflect.Int64 + } + return reflect.Ptr +} + +// mustArrayToBytesSlice creates a new byte slice with the exact same size as value +// and copies the bytes in value to the new slice. +func mustArrayToByteSlice(value reflect.Value) reflect.Value { + slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len()) + reflect.Copy(slice, value) + return slice +} + +// set attempts to assign src to dst by either setting, copying or otherwise. +// +// set is a bit more lenient when it comes to assignment and doesn't force an as +// strict ruleset as bare `reflect` does. +func set(dst, src reflect.Value, output Argument) error { + dstType := dst.Type() + srcType := src.Type() + + switch { + case dstType.AssignableTo(src.Type()): + dst.Set(src) + case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: + if dst.Len() < output.Type.SliceSize { + return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) + } + reflect.Copy(dst, src) + case dstType.Kind() == reflect.Interface: + dst.Set(src) + case dstType.Kind() == reflect.Ptr: + return set(dst.Elem(), src, output) + default: + return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go new file mode 100644 index 0000000..f2832ae --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go @@ -0,0 +1,212 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "fmt" + "reflect" + "regexp" + "strconv" +) + +const ( + IntTy byte = iota + UintTy + BoolTy + StringTy + SliceTy + AddressTy + FixedBytesTy + BytesTy + HashTy + FixedpointTy + FunctionTy +) + +// Type is the reflection of the supported argument type +type Type struct { + IsSlice, IsArray bool + SliceSize int + + Elem *Type + + Kind reflect.Kind + Type reflect.Type + Size int + T byte // Our own type checking + + stringKind string // holds the unparsed string for deriving signatures +} + +var ( + // fullTypeRegex parses the abi types + // + // Types can be in the format of: + // + // Input = Type [ "[" [ Number ] "]" ] Name . + // Type = [ "u" ] "int" [ Number ] [ x ] [ Number ]. + // + // Examples: + // + // string int uint fixed + // string32 int8 uint8 uint[] + // address int256 uint256 fixed128x128[2] + fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`) + // typeRegex parses the abi sub types + typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") +) + +// NewType creates a new reflection type of abi type given in t. +func NewType(t string) (typ Type, err error) { + res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0] + // check if type is slice and parse type. + switch { + case res[3] != "": + // err is ignored. Already checked for number through the regexp + typ.SliceSize, _ = strconv.Atoi(res[3]) + typ.IsArray = true + case res[2] != "": + typ.IsSlice, typ.SliceSize = true, -1 + case res[0] == "": + return Type{}, fmt.Errorf("abi: type parse error: %s", t) + } + if typ.IsArray || typ.IsSlice { + sliceType, err := NewType(res[1]) + if err != nil { + return Type{}, err + } + typ.Elem = &sliceType + typ.stringKind = sliceType.stringKind + t[len(res[1]):] + // Although we know that this is an array, we cannot return + // as we don't know the type of the element, however, if it + // is still an array, then don't determine the type. + if typ.Elem.IsArray || typ.Elem.IsSlice { + return typ, nil + } + } + + // parse the type and size of the abi-type. + parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0] + // varSize is the size of the variable + var varSize int + if len(parsedType[3]) > 0 { + var err error + varSize, err = strconv.Atoi(parsedType[2]) + if err != nil { + return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + } + } + // varType is the parsed abi type + varType := parsedType[1] + // substitute canonical integer + if varSize == 0 && (varType == "int" || varType == "uint") { + varSize = 256 + t += "256" + } + + // only set stringKind if not array or slice, as for those, + // the correct string type has been set + if !(typ.IsArray || typ.IsSlice) { + typ.stringKind = t + } + + switch varType { + case "int": + typ.Kind = reflectIntKind(false, varSize) + typ.Type = big_t + typ.Size = varSize + typ.T = IntTy + case "uint": + typ.Kind = reflectIntKind(true, varSize) + typ.Type = ubig_t + typ.Size = varSize + typ.T = UintTy + case "bool": + typ.Kind = reflect.Bool + typ.T = BoolTy + case "address": + typ.Kind = reflect.Array + typ.Type = address_t + typ.Size = 20 + typ.T = AddressTy + case "string": + typ.Kind = reflect.String + typ.Size = -1 + typ.T = StringTy + case "bytes": + sliceType, _ := NewType("uint8") + typ.Elem = &sliceType + if varSize == 0 { + typ.IsSlice = true + typ.T = BytesTy + typ.SliceSize = -1 + } else { + typ.IsArray = true + typ.T = FixedBytesTy + typ.SliceSize = varSize + } + case "function": + sliceType, _ := NewType("uint8") + typ.Elem = &sliceType + typ.IsArray = true + typ.T = FunctionTy + typ.SliceSize = 24 + default: + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + + return +} + +// String implements Stringer +func (t Type) String() (out string) { + return t.stringKind +} + +func (t Type) pack(v reflect.Value) ([]byte, error) { + // dereference pointer first if it's a pointer + v = indirect(v) + + if err := typeCheck(t, v); err != nil { + return nil, err + } + + if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy && t.T != FunctionTy { + var packed []byte + + for i := 0; i < v.Len(); i++ { + val, err := t.Elem.pack(v.Index(i)) + if err != nil { + return nil, err + } + packed = append(packed, val...) + } + if t.IsSlice { + return packBytesSlice(packed, v.Len()), nil + } else if t.IsArray { + return packed, nil + } + } + + return packElement(t, v), nil +} + +// requireLengthPrefix returns whether the type requires any sort of length +// prefixing. +func (t Type) requiresLengthPrefix() bool { + return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type_test.go new file mode 100644 index 0000000..1558064 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type_test.go @@ -0,0 +1,78 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import ( + "reflect" + "testing" +) + +// typeWithoutStringer is a alias for the Type type which simply doesn't implement +// the stringer interface to allow printing type details in the tests below. +type typeWithoutStringer Type + +// Tests that all allowed types get recognized by the type parser. +func TestTypeRegexp(t *testing.T) { + tests := []struct { + blob string + kind Type + }{ + {"int", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}}, + {"int8", Type{Kind: reflect.Int8, Type: big_t, Size: 8, T: IntTy, stringKind: "int8"}}, + {"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}}, + {"int[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, + {"int[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, + {"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, + {"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, + {"uint", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}}, + {"uint8", Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}}, + {"uint256", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}}, + {"uint[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, + {"uint[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, + {"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, + {"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, + {"bytes", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}}, + {"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}}, + {"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}}, + {"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}}, + {"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}}, + {"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}}, + {"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}}, + {"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}}, + {"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}}, + {"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}}, + {"address[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, + {"address[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, + + // TODO when fixed types are implemented properly + // {"fixed", Type{}}, + // {"fixed128x128", Type{}}, + // {"fixed[]", Type{}}, + // {"fixed[2]", Type{}}, + // {"fixed128x128[]", Type{}}, + // {"fixed128x128[2]", Type{}}, + } + for i, tt := range tests { + typ, err := NewType(tt.blob) + if err != nil { + t.Errorf("type %d: failed to parse type string: %v", i, err) + } + if !reflect.DeepEqual(typ, tt.kind) { + t.Errorf("type %d: parsed type mismatch:\n have %+v\n want %+v", i, typeWithoutStringer(typ), typeWithoutStringer(tt.kind)) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go b/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go new file mode 100644 index 0000000..640de52 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/accounts.go @@ -0,0 +1,155 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package accounts implements high level Ethereum account management. +package accounts + +import ( + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Account represents an Ethereum account located at a specific location defined +// by the optional URL field. +type Account struct { + Address common.Address `json:"address"` // Ethereum account address derived from the key + URL URL `json:"url"` // Optional resource locator within a backend +} + +// Wallet represents a software or hardware wallet that might contain one or more +// accounts (derived from the same seed). +type Wallet interface { + // URL retrieves the canonical path under which this wallet is reachable. It is + // user by upper layers to define a sorting order over all wallets from multiple + // backends. + URL() URL + + // Status returns a textual status to aid the user in the current state of the + // wallet. + Status() string + + // Open initializes access to a wallet instance. It is not meant to unlock or + // decrypt account keys, rather simply to establish a connection to hardware + // wallets and/or to access derivation seeds. + // + // The passphrase parameter may or may not be used by the implementation of a + // particular wallet instance. The reason there is no passwordless open method + // is to strive towards a uniform wallet handling, oblivious to the different + // backend providers. + // + // Please note, if you open a wallet, you must close it to release any allocated + // resources (especially important when working with hardware wallets). + Open(passphrase string) error + + // Close releases any resources held by an open wallet instance. + Close() error + + // Accounts retrieves the list of signing accounts the wallet is currently aware + // of. For hierarchical deterministic wallets, the list will not be exhaustive, + // rather only contain the accounts explicitly pinned during account derivation. + Accounts() []Account + + // Contains returns whether an account is part of this particular wallet or not. + Contains(account Account) bool + + // Derive attempts to explicitly derive a hierarchical deterministic account at + // the specified derivation path. If requested, the derived account will be added + // to the wallet's tracked account list. + Derive(path DerivationPath, pin bool) (Account, error) + + // SelfDerive sets a base account derivation path from which the wallet attempts + // to discover non zero accounts and automatically add them to list of tracked + // accounts. + // + // Note, self derivaton will increment the last component of the specified path + // opposed to decending into a child path to allow discovering accounts starting + // from non zero components. + // + // You can disable automatic account discovery by calling SelfDerive with a nil + // chain state reader. + SelfDerive(base DerivationPath, chain ethereum.ChainStateReader) + + // SignHash requests the wallet to sign the given hash. + // + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + // + // If the wallet requires additional authentication to sign the request (e.g. + // a password to decrypt the account, or a PIN code o verify the transaction), + // an AuthNeededError instance will be returned, containing infos for the user + // about which fields or actions are needed. The user may retry by providing + // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock + // the account in a keystore). + SignHash(account Account, hash []byte) ([]byte, error) + + // SignTx requests the wallet to sign the given transaction. + // + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + // + // If the wallet requires additional authentication to sign the request (e.g. + // a password to decrypt the account, or a PIN code o verify the transaction), + // an AuthNeededError instance will be returned, containing infos for the user + // about which fields or actions are needed. The user may retry by providing + // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock + // the account in a keystore). + SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) + + // SignHashWithPassphrase requests the wallet to sign the given hash with the + // given passphrase as extra authentication information. + // + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error) + + // SignTxWithPassphrase requests the wallet to sign the given transaction, with the + // given passphrase as extra authentication information. + // + // It looks up the account specified either solely via its address contained within, + // or optionally with the aid of any location metadata from the embedded URL field. + SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) +} + +// Backend is a "wallet provider" that may contain a batch of accounts they can +// sign transactions with and upon request, do so. +type Backend interface { + // Wallets retrieves the list of wallets the backend is currently aware of. + // + // The returned wallets are not opened by default. For software HD wallets this + // means that no base seeds are decrypted, and for hardware wallets that no actual + // connection is established. + // + // The resulting wallet list will be sorted alphabetically based on its internal + // URL assigned by the backend. Since wallets (especially hardware) may come and + // go, the same wallet might appear at a different positions in the list during + // subsequent retrievals. + Wallets() []Wallet + + // Subscribe creates an async subscription to receive notifications when the + // backend detects the arrival or departure of a wallet. + Subscribe(sink chan<- WalletEvent) event.Subscription +} + +// WalletEvent is an event fired by an account backend when a wallet arrival or +// departure is detected. +type WalletEvent struct { + Wallet Wallet // Wallet instance arrived or departed + Arrive bool // Whether the wallet was added or removed +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/errors.go b/vendor/github.com/ethereum/go-ethereum/accounts/errors.go new file mode 100644 index 0000000..9ecc1ea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/errors.go @@ -0,0 +1,68 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package accounts + +import ( + "errors" + "fmt" +) + +// ErrUnknownAccount is returned for any requested operation for which no backend +// provides the specified account. +var ErrUnknownAccount = errors.New("unknown account") + +// ErrUnknownWallet is returned for any requested operation for which no backend +// provides the specified wallet. +var ErrUnknownWallet = errors.New("unknown wallet") + +// ErrNotSupported is returned when an operation is requested from an account +// backend that it does not support. +var ErrNotSupported = errors.New("not supported") + +// ErrInvalidPassphrase is returned when a decryption operation receives a bad +// passphrase. +var ErrInvalidPassphrase = errors.New("invalid passphrase") + +// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the +// secodn time. +var ErrWalletAlreadyOpen = errors.New("wallet already open") + +// ErrWalletClosed is returned if a wallet is attempted to be opened the +// secodn time. +var ErrWalletClosed = errors.New("wallet closed") + +// AuthNeededError is returned by backends for signing requests where the user +// is required to provide further authentication before signing can succeed. +// +// This usually means either that a password needs to be supplied, or perhaps a +// one time PIN code displayed by some hardware device. +type AuthNeededError struct { + Needed string // Extra authentication the user needs to provide +} + +// NewAuthNeededError creates a new authentication error with the extra details +// about the needed fields set. +func NewAuthNeededError(needed string) error { + return &AuthNeededError{ + Needed: needed, + } +} + +// Error implements the standard error interfacel. +func (err *AuthNeededError) Error() string { + return fmt.Sprintf("authentication needed: %s", err.Needed) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/hd.go b/vendor/github.com/ethereum/go-ethereum/accounts/hd.go new file mode 100644 index 0000000..e8bc191 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/hd.go @@ -0,0 +1,130 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package accounts + +import ( + "errors" + "fmt" + "math" + "math/big" + "strings" +) + +// DefaultRootDerivationPath is the root path to which custom derivation endpoints +// are appended. As such, the first account will be at m/44'/60'/0'/0, the second +// at m/44'/60'/0'/1, etc. +var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0} + +// DefaultBaseDerivationPath is the base path from which custom derivation endpoints +// are incremented. As such, the first account will be at m/44'/60'/0'/0, the second +// at m/44'/60'/0'/1, etc. +var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0} + +// DerivationPath represents the computer friendly version of a hierarchical +// deterministic wallet account derivaion path. +// +// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +// defines derivation paths to be of the form: +// +// m / purpose' / coin_type' / account' / change / address_index +// +// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and +// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns +// the `coin_type` 60' (or 0x8000003C) to Ethereum. +// +// The root path for Ethereum is m/44'/60'/0'/0 according to the specification +// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone +// yet whether accounts should increment the last component or the children of +// that. We will go with the simpler approach of incrementing the last component. +type DerivationPath []uint32 + +// ParseDerivationPath converts a user specified derivation path string to the +// internal binary representation. +// +// Full derivation paths need to start with the `m/` prefix, relative derivation +// paths (which will get appended to the default root path) must not have prefixes +// in front of the first element. Whitespace is ignored. +func ParseDerivationPath(path string) (DerivationPath, error) { + var result DerivationPath + + // Handle absolute or relative paths + components := strings.Split(path, "/") + switch { + case len(components) == 0: + return nil, errors.New("empty derivation path") + + case strings.TrimSpace(components[0]) == "": + return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones") + + case strings.TrimSpace(components[0]) == "m": + components = components[1:] + + default: + result = append(result, DefaultRootDerivationPath...) + } + // All remaining components are relative, append one by one + if len(components) == 0 { + return nil, errors.New("empty derivation path") // Empty relative paths + } + for _, component := range components { + // Ignore any user added whitespace + component = strings.TrimSpace(component) + var value uint32 + + // Handle hardened paths + if strings.HasSuffix(component, "'") { + value = 0x80000000 + component = strings.TrimSpace(strings.TrimSuffix(component, "'")) + } + // Handle the non hardened component + bigval, ok := new(big.Int).SetString(component, 0) + if !ok { + return nil, fmt.Errorf("invalid component: %s", component) + } + max := math.MaxUint32 - value + if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 { + if value == 0 { + return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max) + } + return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max) + } + value += uint32(bigval.Uint64()) + + // Append and repeat + result = append(result, value) + } + return result, nil +} + +// String implements the stringer interface, converting a binary derivation path +// to its canonical representation. +func (path DerivationPath) String() string { + result := "m" + for _, component := range path { + var hardened bool + if component >= 0x80000000 { + component -= 0x80000000 + hardened = true + } + result = fmt.Sprintf("%s/%d", result, component) + if hardened { + result += "'" + } + } + return result +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/hd_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/hd_test.go new file mode 100644 index 0000000..83ec34a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/hd_test.go @@ -0,0 +1,79 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package accounts + +import ( + "reflect" + "testing" +) + +// Tests that HD derivation paths can be correctly parsed into our internal binary +// representation. +func TestHDPathParsing(t *testing.T) { + tests := []struct { + input string + output DerivationPath + }{ + // Plain absolute derivation paths + {"m/44'/60'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"m/44'/60'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, + {"m/44'/60'/0'/0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/44'/60'/0'/128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, + {"m/2147483692/2147483708/2147483648/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"m/2147483692/2147483708/2147483648/2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + + // Plain relative derivation paths + {"0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, + {"0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, + {"2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + + // Hexadecimal absolute derivation paths + {"m/0x2C'/0x3c'/0x00'/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"m/0x2C'/0x3c'/0x00'/0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, + {"m/0x2C'/0x3c'/0x00'/0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"m/0x2C'/0x3c'/0x00'/0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, + {"m/0x8000002C/0x8000003c/0x80000000/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"m/0x8000002C/0x8000003c/0x80000000/0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + + // Hexadecimal relative derivation paths + {"0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + {"0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, + {"0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + {"0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}}, + {"0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}}, + + // Weird inputs just to ensure they work + {" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, + + // Invaid derivation paths + {"", nil}, // Empty relative derivation path + {"m", nil}, // Empty absolute derivation path + {"m/", nil}, // Missing last derivation component + {"/44'/60'/0'/0", nil}, // Absolute path without m prefix, might be user error + {"m/2147483648'", nil}, // Overflows 32 bit integer + {"m/-1'", nil}, // Cannot contain negative number + } + for i, tt := range tests { + if path, err := ParseDerivationPath(tt.input); !reflect.DeepEqual(path, tt.output) { + t.Errorf("test %d: parse mismatch: have %v (%v), want %v", i, path, err, tt.output) + } else if path == nil && err == nil { + t.Errorf("test %d: nil path and error: %v", i, err) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go new file mode 100644 index 0000000..3fae3ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache.go @@ -0,0 +1,283 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// Minimum amount of time between cache reloads. This limit applies if the platform does +// not support change notifications. It also applies if the keystore directory does not +// exist yet, the code will attempt to create a watcher at most this often. +const minReloadInterval = 2 * time.Second + +type accountsByURL []accounts.Account + +func (s accountsByURL) Len() int { return len(s) } +func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 } +func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// AmbiguousAddrError is returned when attempting to unlock +// an address for which more than one file exists. +type AmbiguousAddrError struct { + Addr common.Address + Matches []accounts.Account +} + +func (err *AmbiguousAddrError) Error() string { + files := "" + for i, a := range err.Matches { + files += a.URL.Path + if i < len(err.Matches)-1 { + files += ", " + } + } + return fmt.Sprintf("multiple keys match address (%s)", files) +} + +// accountCache is a live index of all accounts in the keystore. +type accountCache struct { + keydir string + watcher *watcher + mu sync.Mutex + all accountsByURL + byAddr map[common.Address][]accounts.Account + throttle *time.Timer + notify chan struct{} +} + +func newAccountCache(keydir string) (*accountCache, chan struct{}) { + ac := &accountCache{ + keydir: keydir, + byAddr: make(map[common.Address][]accounts.Account), + notify: make(chan struct{}, 1), + } + ac.watcher = newWatcher(ac) + return ac, ac.notify +} + +func (ac *accountCache) accounts() []accounts.Account { + ac.maybeReload() + ac.mu.Lock() + defer ac.mu.Unlock() + cpy := make([]accounts.Account, len(ac.all)) + copy(cpy, ac.all) + return cpy +} + +func (ac *accountCache) hasAddress(addr common.Address) bool { + ac.maybeReload() + ac.mu.Lock() + defer ac.mu.Unlock() + return len(ac.byAddr[addr]) > 0 +} + +func (ac *accountCache) add(newAccount accounts.Account) { + ac.mu.Lock() + defer ac.mu.Unlock() + + i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 }) + if i < len(ac.all) && ac.all[i] == newAccount { + return + } + // newAccount is not in the cache. + ac.all = append(ac.all, accounts.Account{}) + copy(ac.all[i+1:], ac.all[i:]) + ac.all[i] = newAccount + ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount) +} + +// note: removed needs to be unique here (i.e. both File and Address must be set). +func (ac *accountCache) delete(removed accounts.Account) { + ac.mu.Lock() + defer ac.mu.Unlock() + + ac.all = removeAccount(ac.all, removed) + if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 { + delete(ac.byAddr, removed.Address) + } else { + ac.byAddr[removed.Address] = ba + } +} + +func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account { + for i := range slice { + if slice[i] == elem { + return append(slice[:i], slice[i+1:]...) + } + } + return slice +} + +// find returns the cached account for address if there is a unique match. +// The exact matching rules are explained by the documentation of accounts.Account. +// Callers must hold ac.mu. +func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) { + // Limit search to address candidates if possible. + matches := ac.all + if (a.Address != common.Address{}) { + matches = ac.byAddr[a.Address] + } + if a.URL.Path != "" { + // If only the basename is specified, complete the path. + if !strings.ContainsRune(a.URL.Path, filepath.Separator) { + a.URL.Path = filepath.Join(ac.keydir, a.URL.Path) + } + for i := range matches { + if matches[i].URL == a.URL { + return matches[i], nil + } + } + if (a.Address == common.Address{}) { + return accounts.Account{}, ErrNoMatch + } + } + switch len(matches) { + case 1: + return matches[0], nil + case 0: + return accounts.Account{}, ErrNoMatch + default: + err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))} + copy(err.Matches, matches) + return accounts.Account{}, err + } +} + +func (ac *accountCache) maybeReload() { + ac.mu.Lock() + defer ac.mu.Unlock() + + if ac.watcher.running { + return // A watcher is running and will keep the cache up-to-date. + } + if ac.throttle == nil { + ac.throttle = time.NewTimer(0) + } else { + select { + case <-ac.throttle.C: + default: + return // The cache was reloaded recently. + } + } + ac.watcher.start() + ac.reload() + ac.throttle.Reset(minReloadInterval) +} + +func (ac *accountCache) close() { + ac.mu.Lock() + ac.watcher.close() + if ac.throttle != nil { + ac.throttle.Stop() + } + if ac.notify != nil { + close(ac.notify) + ac.notify = nil + } + ac.mu.Unlock() +} + +// reload caches addresses of existing accounts. +// Callers must hold ac.mu. +func (ac *accountCache) reload() { + accounts, err := ac.scan() + if err != nil && glog.V(logger.Debug) { + glog.Errorf("can't load keys: %v", err) + } + ac.all = accounts + sort.Sort(ac.all) + for k := range ac.byAddr { + delete(ac.byAddr, k) + } + for _, a := range accounts { + ac.byAddr[a.Address] = append(ac.byAddr[a.Address], a) + } + select { + case ac.notify <- struct{}{}: + default: + } + glog.V(logger.Debug).Infof("reloaded keys, cache has %d accounts", len(ac.all)) +} + +func (ac *accountCache) scan() ([]accounts.Account, error) { + files, err := ioutil.ReadDir(ac.keydir) + if err != nil { + return nil, err + } + + var ( + buf = new(bufio.Reader) + addrs []accounts.Account + keyJSON struct { + Address string `json:"address"` + } + ) + for _, fi := range files { + path := filepath.Join(ac.keydir, fi.Name()) + if skipKeyFile(fi) { + glog.V(logger.Detail).Infof("ignoring file %s", path) + continue + } + fd, err := os.Open(path) + if err != nil { + glog.V(logger.Detail).Infoln(err) + continue + } + buf.Reset(fd) + // Parse the address. + keyJSON.Address = "" + err = json.NewDecoder(buf).Decode(&keyJSON) + addr := common.HexToAddress(keyJSON.Address) + switch { + case err != nil: + glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err) + case (addr == common.Address{}): + glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path) + default: + addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}}) + } + fd.Close() + } + return addrs, err +} + +func skipKeyFile(fi os.FileInfo) bool { + // Skip editor backups and UNIX-style hidden files. + if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { + return true + } + // Skip misc special files, directories (yes, symlinks too). + if fi.IsDir() || fi.Mode()&os.ModeType != 0 { + return true + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache_test.go new file mode 100644 index 0000000..5541963 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/account_cache_test.go @@ -0,0 +1,297 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "fmt" + "math/rand" + "os" + "path/filepath" + "reflect" + "sort" + "testing" + "time" + + "github.com/cespare/cp" + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" +) + +var ( + cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore")) + cachetestAccounts = []accounts.Account{ + { + Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")}, + }, + { + Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")}, + }, + { + Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")}, + }, + } +) + +func TestWatchNewFile(t *testing.T) { + t.Parallel() + + dir, ks := tmpKeyStore(t, false) + defer os.RemoveAll(dir) + + // Ensure the watcher is started before adding any files. + ks.Accounts() + time.Sleep(200 * time.Millisecond) + + // Move in the files. + wantAccounts := make([]accounts.Account, len(cachetestAccounts)) + for i := range cachetestAccounts { + wantAccounts[i] = accounts.Account{ + Address: cachetestAccounts[i].Address, + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))}, + } + if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil { + t.Fatal(err) + } + } + + // ks should see the accounts. + var list []accounts.Account + for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { + list = ks.Accounts() + if reflect.DeepEqual(list, wantAccounts) { + // ks should have also received change notifications + select { + case <-ks.changes: + default: + t.Fatalf("wasn't notified of new accounts") + } + return + } + time.Sleep(d) + } + t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts)) +} + +func TestWatchNoDir(t *testing.T) { + t.Parallel() + + // Create ks but not the directory that it watches. + rand.Seed(time.Now().UnixNano()) + dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())) + ks := NewKeyStore(dir, LightScryptN, LightScryptP) + + list := ks.Accounts() + if len(list) > 0 { + t.Error("initial account list not empty:", list) + } + time.Sleep(100 * time.Millisecond) + + // Create the directory and copy a key file into it. + os.MkdirAll(dir, 0700) + defer os.RemoveAll(dir) + file := filepath.Join(dir, "aaa") + if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { + t.Fatal(err) + } + + // ks should see the account. + wantAccounts := []accounts.Account{cachetestAccounts[0]} + wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} + for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { + list = ks.Accounts() + if reflect.DeepEqual(list, wantAccounts) { + // ks should have also received change notifications + select { + case <-ks.changes: + default: + t.Fatalf("wasn't notified of new accounts") + } + return + } + time.Sleep(d) + } + t.Errorf("\ngot %v\nwant %v", list, wantAccounts) +} + +func TestCacheInitialReload(t *testing.T) { + cache, _ := newAccountCache(cachetestDir) + accounts := cache.accounts() + if !reflect.DeepEqual(accounts, cachetestAccounts) { + t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts)) + } +} + +func TestCacheAddDeleteOrder(t *testing.T) { + cache, _ := newAccountCache("testdata/no-such-dir") + cache.watcher.running = true // prevent unexpected reloads + + accs := []accounts.Account{ + { + Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"}, + }, + { + Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"}, + }, + { + Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"}, + }, + { + Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"}, + }, + { + Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"}, + }, + { + Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"}, + }, + { + Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"}, + }, + } + for _, a := range accs { + cache.add(a) + } + // Add some of them twice to check that they don't get reinserted. + cache.add(accs[0]) + cache.add(accs[2]) + + // Check that the account list is sorted by filename. + wantAccounts := make([]accounts.Account, len(accs)) + copy(wantAccounts, accs) + sort.Sort(accountsByURL(wantAccounts)) + list := cache.accounts() + if !reflect.DeepEqual(list, wantAccounts) { + t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts)) + } + for _, a := range accs { + if !cache.hasAddress(a.Address) { + t.Errorf("expected hasAccount(%x) to return true", a.Address) + } + } + if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) { + t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) + } + + // Delete a few keys from the cache. + for i := 0; i < len(accs); i += 2 { + cache.delete(wantAccounts[i]) + } + cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}}) + + // Check content again after deletion. + wantAccountsAfterDelete := []accounts.Account{ + wantAccounts[1], + wantAccounts[3], + wantAccounts[5], + } + list = cache.accounts() + if !reflect.DeepEqual(list, wantAccountsAfterDelete) { + t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete)) + } + for _, a := range wantAccountsAfterDelete { + if !cache.hasAddress(a.Address) { + t.Errorf("expected hasAccount(%x) to return true", a.Address) + } + } + if cache.hasAddress(wantAccounts[0].Address) { + t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address) + } +} + +func TestCacheFind(t *testing.T) { + dir := filepath.Join("testdata", "dir") + cache, _ := newAccountCache(dir) + cache.watcher.running = true // prevent unexpected reloads + + accs := []accounts.Account{ + { + Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")}, + }, + { + Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")}, + }, + { + Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")}, + }, + { + Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")}, + }, + } + for _, a := range accs { + cache.add(a) + } + + nomatchAccount := accounts.Account{ + Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), + URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")}, + } + tests := []struct { + Query accounts.Account + WantResult accounts.Account + WantError error + }{ + // by address + {Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]}, + // by file + {Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]}, + // by basename + {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]}, + // by file and address + {Query: accs[0], WantResult: accs[0]}, + // ambiguous address, tie resolved by file + {Query: accs[2], WantResult: accs[2]}, + // ambiguous address error + { + Query: accounts.Account{Address: accs[2].Address}, + WantError: &AmbiguousAddrError{ + Addr: accs[2].Address, + Matches: []accounts.Account{accs[2], accs[3]}, + }, + }, + // no match error + {Query: nomatchAccount, WantError: ErrNoMatch}, + {Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch}, + {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch}, + {Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch}, + } + for i, test := range tests { + a, err := cache.find(test.Query) + if !reflect.DeepEqual(err, test.WantError) { + t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError) + continue + } + if a != test.WantResult { + t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult) + continue + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go new file mode 100644 index 0000000..292b47b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/key.go @@ -0,0 +1,229 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pborman/uuid" +) + +const ( + version = 3 +) + +type Key struct { + Id uuid.UUID // Version 4 "random" for unique id not derived from key data + // to simplify lookups we also store the address + Address common.Address + // we only store privkey as pubkey/address can be derived from it + // privkey in this struct is always in plaintext + PrivateKey *ecdsa.PrivateKey +} + +type keyStore interface { + // Loads and decrypts the key from disk. + GetKey(addr common.Address, filename string, auth string) (*Key, error) + // Writes and encrypts the key. + StoreKey(filename string, k *Key, auth string) error + // Joins filename with the key directory unless it is already absolute. + JoinPath(filename string) string +} + +type plainKeyJSON struct { + Address string `json:"address"` + PrivateKey string `json:"privatekey"` + Id string `json:"id"` + Version int `json:"version"` +} + +type encryptedKeyJSONV3 struct { + Address string `json:"address"` + Crypto cryptoJSON `json:"crypto"` + Id string `json:"id"` + Version int `json:"version"` +} + +type encryptedKeyJSONV1 struct { + Address string `json:"address"` + Crypto cryptoJSON `json:"crypto"` + Id string `json:"id"` + Version string `json:"version"` +} + +type cryptoJSON struct { + Cipher string `json:"cipher"` + CipherText string `json:"ciphertext"` + CipherParams cipherparamsJSON `json:"cipherparams"` + KDF string `json:"kdf"` + KDFParams map[string]interface{} `json:"kdfparams"` + MAC string `json:"mac"` +} + +type cipherparamsJSON struct { + IV string `json:"iv"` +} + +type scryptParamsJSON struct { + N int `json:"n"` + R int `json:"r"` + P int `json:"p"` + DkLen int `json:"dklen"` + Salt string `json:"salt"` +} + +func (k *Key) MarshalJSON() (j []byte, err error) { + jStruct := plainKeyJSON{ + hex.EncodeToString(k.Address[:]), + hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), + k.Id.String(), + version, + } + j, err = json.Marshal(jStruct) + return j, err +} + +func (k *Key) UnmarshalJSON(j []byte) (err error) { + keyJSON := new(plainKeyJSON) + err = json.Unmarshal(j, &keyJSON) + if err != nil { + return err + } + + u := new(uuid.UUID) + *u = uuid.Parse(keyJSON.Id) + k.Id = *u + addr, err := hex.DecodeString(keyJSON.Address) + if err != nil { + return err + } + + privkey, err := hex.DecodeString(keyJSON.PrivateKey) + if err != nil { + return err + } + + k.Address = common.BytesToAddress(addr) + k.PrivateKey = crypto.ToECDSA(privkey) + + return nil +} + +func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { + id := uuid.NewRandom() + key := &Key{ + Id: id, + Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), + PrivateKey: privateKeyECDSA, + } + return key +} + +// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit +// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we +// retry until the first byte is 0. +func NewKeyForDirectICAP(rand io.Reader) *Key { + randBytes := make([]byte, 64) + _, err := rand.Read(randBytes) + if err != nil { + panic("key generation: could not read from random source: " + err.Error()) + } + reader := bytes.NewReader(randBytes) + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) + if err != nil { + panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) + } + key := newKeyFromECDSA(privateKeyECDSA) + if !strings.HasPrefix(key.Address.Hex(), "0x00") { + return NewKeyForDirectICAP(rand) + } + return key +} + +func newKey(rand io.Reader) (*Key, error) { + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) + if err != nil { + return nil, err + } + return newKeyFromECDSA(privateKeyECDSA), nil +} + +func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) { + key, err := newKey(rand) + if err != nil { + return nil, accounts.Account{}, err + } + a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}} + if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { + zeroKey(key.PrivateKey) + return nil, a, err + } + return key, a, err +} + +func writeKeyFile(file string, content []byte) error { + // Create the keystore directory with appropriate permissions + // in case it is not present yet. + const dirPerm = 0700 + if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { + return err + } + // Atomic write: create a temporary hidden file first + // then move it into place. TempFile assigns mode 0600. + f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") + if err != nil { + return err + } + if _, err := f.Write(content); err != nil { + f.Close() + os.Remove(f.Name()) + return err + } + f.Close() + return os.Rename(f.Name(), file) +} + +// keyFileName implements the naming convention for keyfiles: +// UTC---
+func keyFileName(keyAddr common.Address) string { + ts := time.Now().UTC() + return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) +} + +func toISO8601(t time.Time) string { + var tz string + name, offset := t.Zone() + if name == "UTC" { + tz = "Z" + } else { + tz = fmt.Sprintf("%03d00", offset/3600) + } + return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go new file mode 100644 index 0000000..a01ff17 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore.go @@ -0,0 +1,494 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package keystore implements encrypted storage of secp256k1 private keys. +// +// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification. +// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information. +package keystore + +import ( + "crypto/ecdsa" + crand "crypto/rand" + "errors" + "fmt" + "math/big" + "os" + "path/filepath" + "reflect" + "runtime" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/event" +) + +var ( + ErrLocked = accounts.NewAuthNeededError("password or unlock") + ErrNoMatch = errors.New("no key for given address or file") + ErrDecrypt = errors.New("could not decrypt key with given passphrase") +) + +// KeyStoreType is the reflect type of a keystore backend. +var KeyStoreType = reflect.TypeOf(&KeyStore{}) + +// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs. +var KeyStoreScheme = "keystore" + +// Maximum time between wallet refreshes (if filesystem notifications don't work). +const walletRefreshCycle = 3 * time.Second + +// KeyStore manages a key storage directory on disk. +type KeyStore struct { + storage keyStore // Storage backend, might be cleartext or encrypted + cache *accountCache // In-memory account cache over the filesystem storage + changes chan struct{} // Channel receiving change notifications from the cache + unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys) + + wallets []accounts.Wallet // Wallet wrappers around the individual key files + updateFeed event.Feed // Event feed to notify wallet additions/removals + updateScope event.SubscriptionScope // Subscription scope tracking current live listeners + updating bool // Whether the event notification loop is running + + mu sync.RWMutex +} + +type unlocked struct { + *Key + abort chan struct{} +} + +// NewKeyStore creates a keystore for the given directory. +func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { + keydir, _ = filepath.Abs(keydir) + ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}} + ks.init(keydir) + return ks +} + +// NewPlaintextKeyStore creates a keystore for the given directory. +// Deprecated: Use NewKeyStore. +func NewPlaintextKeyStore(keydir string) *KeyStore { + keydir, _ = filepath.Abs(keydir) + ks := &KeyStore{storage: &keyStorePlain{keydir}} + ks.init(keydir) + return ks +} + +func (ks *KeyStore) init(keydir string) { + // Lock the mutex since the account cache might call back with events + ks.mu.Lock() + defer ks.mu.Unlock() + + // Initialize the set of unlocked keys and the account cache + ks.unlocked = make(map[common.Address]*unlocked) + ks.cache, ks.changes = newAccountCache(keydir) + + // TODO: In order for this finalizer to work, there must be no references + // to ks. addressCache doesn't keep a reference but unlocked keys do, + // so the finalizer will not trigger until all timed unlocks have expired. + runtime.SetFinalizer(ks, func(m *KeyStore) { + m.cache.close() + }) + // Create the initial list of wallets from the cache + accs := ks.cache.accounts() + ks.wallets = make([]accounts.Wallet, len(accs)) + for i := 0; i < len(accs); i++ { + ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks} + } +} + +// Wallets implements accounts.Backend, returning all single-key wallets from the +// keystore directory. +func (ks *KeyStore) Wallets() []accounts.Wallet { + // Make sure the list of wallets is in sync with the account cache + ks.refreshWallets() + + ks.mu.RLock() + defer ks.mu.RUnlock() + + cpy := make([]accounts.Wallet, len(ks.wallets)) + copy(cpy, ks.wallets) + return cpy +} + +// refreshWallets retrieves the current account list and based on that does any +// necessary wallet refreshes. +func (ks *KeyStore) refreshWallets() { + // Retrieve the current list of accounts + ks.mu.Lock() + accs := ks.cache.accounts() + + // Transform the current list of wallets into the new one + wallets := make([]accounts.Wallet, 0, len(accs)) + events := []accounts.WalletEvent{} + + for _, account := range accs { + // Drop wallets while they were in front of the next account + for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 { + events = append(events, accounts.WalletEvent{Wallet: ks.wallets[0], Arrive: false}) + ks.wallets = ks.wallets[1:] + } + // If there are no more wallets or the account is before the next, wrap new wallet + if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 { + wallet := &keystoreWallet{account: account, keystore: ks} + + events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true}) + wallets = append(wallets, wallet) + continue + } + // If the account is the same as the first wallet, keep it + if ks.wallets[0].Accounts()[0] == account { + wallets = append(wallets, ks.wallets[0]) + ks.wallets = ks.wallets[1:] + continue + } + } + // Drop any leftover wallets and set the new batch + for _, wallet := range ks.wallets { + events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false}) + } + ks.wallets = wallets + ks.mu.Unlock() + + // Fire all wallet events and return + for _, event := range events { + ks.updateFeed.Send(event) + } +} + +// Subscribe implements accounts.Backend, creating an async subscription to +// receive notifications on the addition or removal of keystore wallets. +func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { + // We need the mutex to reliably start/stop the update loop + ks.mu.Lock() + defer ks.mu.Unlock() + + // Subscribe the caller and track the subscriber count + sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink)) + + // Subscribers require an active notification loop, start it + if !ks.updating { + ks.updating = true + go ks.updater() + } + return sub +} + +// updater is responsible for maintaining an up-to-date list of wallets stored in +// the keystore, and for firing wallet addition/removal events. It listens for +// account change events from the underlying account cache, and also periodically +// forces a manual refresh (only triggers for systems where the filesystem notifier +// is not running). +func (ks *KeyStore) updater() { + for { + // Wait for an account update or a refresh timeout + select { + case <-ks.changes: + case <-time.After(walletRefreshCycle): + } + // Run the wallet refresher + ks.refreshWallets() + + // If all our subscribers left, stop the updater + ks.mu.Lock() + if ks.updateScope.Count() == 0 { + ks.updating = false + ks.mu.Unlock() + return + } + ks.mu.Unlock() + } +} + +// HasAddress reports whether a key with the given address is present. +func (ks *KeyStore) HasAddress(addr common.Address) bool { + return ks.cache.hasAddress(addr) +} + +// Accounts returns all key files present in the directory. +func (ks *KeyStore) Accounts() []accounts.Account { + return ks.cache.accounts() +} + +// Delete deletes the key matched by account if the passphrase is correct. +// If the account contains no filename, the address must match a unique key. +func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error { + // Decrypting the key isn't really necessary, but we do + // it anyway to check the password and zero out the key + // immediately afterwards. + a, key, err := ks.getDecryptedKey(a, passphrase) + if key != nil { + zeroKey(key.PrivateKey) + } + if err != nil { + return err + } + // The order is crucial here. The key is dropped from the + // cache after the file is gone so that a reload happening in + // between won't insert it into the cache again. + err = os.Remove(a.URL.Path) + if err == nil { + ks.cache.delete(a) + ks.refreshWallets() + } + return err +} + +// SignHash calculates a ECDSA signature for the given hash. The produced +// signature is in the [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + // Sign the hash using plain ECDSA operations + return crypto.Sign(hash, unlockedKey.PrivateKey) +} + +// SignTx signs the given transaction with the requested account. +func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + // Look up the key to sign with and abort if it cannot be found + ks.mu.RLock() + defer ks.mu.RUnlock() + + unlockedKey, found := ks.unlocked[a.Address] + if !found { + return nil, ErrLocked + } + // Depending on the presence of the chain ID, sign with EIP155 or homestead + if chainID != nil { + return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey) + } + return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey) +} + +// SignHashWithPassphrase signs hash if the private key matching the given address +// can be decrypted with the given passphrase. The produced signature is in the +// [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string, hash []byte) (signature []byte, err error) { + _, key, err := ks.getDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + defer zeroKey(key.PrivateKey) + return crypto.Sign(hash, key.PrivateKey) +} + +// SignTxWithPassphrase signs the transaction if the private key matching the +// given address can be decrypted with the given passphrase. +func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + _, key, err := ks.getDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + defer zeroKey(key.PrivateKey) + + // Depending on the presence of the chain ID, sign with EIP155 or homestead + if chainID != nil { + return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey) + } + return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey) +} + +// Unlock unlocks the given account indefinitely. +func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error { + return ks.TimedUnlock(a, passphrase, 0) +} + +// Lock removes the private key with the given address from memory. +func (ks *KeyStore) Lock(addr common.Address) error { + ks.mu.Lock() + if unl, found := ks.unlocked[addr]; found { + ks.mu.Unlock() + ks.expire(addr, unl, time.Duration(0)*time.Nanosecond) + } else { + ks.mu.Unlock() + } + return nil +} + +// TimedUnlock unlocks the given account with the passphrase. The account +// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account +// until the program exits. The account must match a unique key file. +// +// If the account address is already unlocked for a duration, TimedUnlock extends or +// shortens the active unlock timeout. If the address was previously unlocked +// indefinitely the timeout is not altered. +func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error { + a, key, err := ks.getDecryptedKey(a, passphrase) + if err != nil { + return err + } + + ks.mu.Lock() + defer ks.mu.Unlock() + u, found := ks.unlocked[a.Address] + if found { + if u.abort == nil { + // The address was unlocked indefinitely, so unlocking + // it with a timeout would be confusing. + zeroKey(key.PrivateKey) + return nil + } + // Terminate the expire goroutine and replace it below. + close(u.abort) + } + if timeout > 0 { + u = &unlocked{Key: key, abort: make(chan struct{})} + go ks.expire(a.Address, u, timeout) + } else { + u = &unlocked{Key: key} + } + ks.unlocked[a.Address] = u + return nil +} + +// Find resolves the given account into a unique entry in the keystore. +func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) { + ks.cache.maybeReload() + ks.cache.mu.Lock() + a, err := ks.cache.find(a) + ks.cache.mu.Unlock() + return a, err +} + +func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) { + a, err := ks.Find(a) + if err != nil { + return a, nil, err + } + key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth) + return a, key, err +} + +func (ks *KeyStore) expire(addr common.Address, u *unlocked, timeout time.Duration) { + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-u.abort: + // just quit + case <-t.C: + ks.mu.Lock() + // only drop if it's still the same key instance that dropLater + // was launched with. we can check that using pointer equality + // because the map stores a new pointer every time the key is + // unlocked. + if ks.unlocked[addr] == u { + zeroKey(u.PrivateKey) + delete(ks.unlocked, addr) + } + ks.mu.Unlock() + } +} + +// NewAccount generates a new key and stores it into the key directory, +// encrypting it with the passphrase. +func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) { + _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase) + if err != nil { + return accounts.Account{}, err + } + // Add the account to the cache immediately rather + // than waiting for file system notifications to pick it up. + ks.cache.add(account) + ks.refreshWallets() + return account, nil +} + +// Export exports as a JSON key, encrypted with newPassphrase. +func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) { + _, key, err := ks.getDecryptedKey(a, passphrase) + if err != nil { + return nil, err + } + var N, P int + if store, ok := ks.storage.(*keyStorePassphrase); ok { + N, P = store.scryptN, store.scryptP + } else { + N, P = StandardScryptN, StandardScryptP + } + return EncryptKey(key, newPassphrase, N, P) +} + +// Import stores the given encrypted JSON key into the key directory. +func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (accounts.Account, error) { + key, err := DecryptKey(keyJSON, passphrase) + if key != nil && key.PrivateKey != nil { + defer zeroKey(key.PrivateKey) + } + if err != nil { + return accounts.Account{}, err + } + return ks.importKey(key, newPassphrase) +} + +// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase. +func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) { + key := newKeyFromECDSA(priv) + if ks.cache.hasAddress(key.Address) { + return accounts.Account{}, fmt.Errorf("account already exists") + } + + return ks.importKey(key, passphrase) +} + +func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) { + a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}} + if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil { + return accounts.Account{}, err + } + ks.cache.add(a) + ks.refreshWallets() + return a, nil +} + +// Update changes the passphrase of an existing account. +func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error { + a, key, err := ks.getDecryptedKey(a, passphrase) + if err != nil { + return err + } + return ks.storage.StoreKey(a.URL.Path, key, newPassphrase) +} + +// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores +// a key file in the key directory. The key file is encrypted with the same passphrase. +func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) { + a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase) + if err != nil { + return a, err + } + ks.cache.add(a) + ks.refreshWallets() + return a, nil +} + +// zeroKey zeroes a private key in memory. +func zeroKey(k *ecdsa.PrivateKey) { + b := k.D.Bits() + for i := range b { + b[i] = 0 + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go new file mode 100644 index 0000000..8ef510f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase.go @@ -0,0 +1,305 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* + +This key store behaves as KeyStorePlain with the difference that +the private key is encrypted and on disk uses another JSON encoding. + +The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition + +*/ + +package keystore + +import ( + "bytes" + "crypto/aes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/randentropy" + "github.com/pborman/uuid" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/crypto/scrypt" +) + +const ( + keyHeaderKDF = "scrypt" + + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptN = 1 << 18 + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptP = 1 + + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptN = 1 << 12 + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptP = 6 + + scryptR = 8 + scryptDKLen = 32 +) + +type keyStorePassphrase struct { + keysDirPath string + scryptN int + scryptP int +} + +func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { + // Load the key from the keystore and decrypt its contents + keyjson, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + key, err := DecryptKey(keyjson, auth) + if err != nil { + return nil, err + } + // Make sure we're really operating on the requested key (no swap attacks) + if key.Address != addr { + return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) + } + return key, nil +} + +func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error { + keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) + if err != nil { + return err + } + return writeKeyFile(filename, keyjson) +} + +func (ks keyStorePassphrase) JoinPath(filename string) string { + if filepath.IsAbs(filename) { + return filename + } else { + return filepath.Join(ks.keysDirPath, filename) + } +} + +// EncryptKey encrypts a key using the specified scrypt parameters into a json +// blob that can be decrypted later on. +func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { + authArray := []byte(auth) + salt := randentropy.GetEntropyCSPRNG(32) + derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen) + if err != nil { + return nil, err + } + encryptKey := derivedKey[:16] + keyBytes0 := crypto.FromECDSA(key.PrivateKey) + keyBytes := common.LeftPadBytes(keyBytes0, 32) + + iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 + cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) + if err != nil { + return nil, err + } + mac := crypto.Keccak256(derivedKey[16:32], cipherText) + + scryptParamsJSON := make(map[string]interface{}, 5) + scryptParamsJSON["n"] = scryptN + scryptParamsJSON["r"] = scryptR + scryptParamsJSON["p"] = scryptP + scryptParamsJSON["dklen"] = scryptDKLen + scryptParamsJSON["salt"] = hex.EncodeToString(salt) + + cipherParamsJSON := cipherparamsJSON{ + IV: hex.EncodeToString(iv), + } + + cryptoStruct := cryptoJSON{ + Cipher: "aes-128-ctr", + CipherText: hex.EncodeToString(cipherText), + CipherParams: cipherParamsJSON, + KDF: "scrypt", + KDFParams: scryptParamsJSON, + MAC: hex.EncodeToString(mac), + } + encryptedKeyJSONV3 := encryptedKeyJSONV3{ + hex.EncodeToString(key.Address[:]), + cryptoStruct, + key.Id.String(), + version, + } + return json.Marshal(encryptedKeyJSONV3) +} + +// DecryptKey decrypts a key from a json blob, returning the private key itself. +func DecryptKey(keyjson []byte, auth string) (*Key, error) { + // Parse the json into a simple map to fetch the key version + m := make(map[string]interface{}) + if err := json.Unmarshal(keyjson, &m); err != nil { + return nil, err + } + // Depending on the version try to parse one way or another + var ( + keyBytes, keyId []byte + err error + ) + if version, ok := m["version"].(string); ok && version == "1" { + k := new(encryptedKeyJSONV1) + if err := json.Unmarshal(keyjson, k); err != nil { + return nil, err + } + keyBytes, keyId, err = decryptKeyV1(k, auth) + } else { + k := new(encryptedKeyJSONV3) + if err := json.Unmarshal(keyjson, k); err != nil { + return nil, err + } + keyBytes, keyId, err = decryptKeyV3(k, auth) + } + // Handle any decryption errors and return the key + if err != nil { + return nil, err + } + key := crypto.ToECDSA(keyBytes) + return &Key{ + Id: uuid.UUID(keyId), + Address: crypto.PubkeyToAddress(key.PublicKey), + PrivateKey: key, + }, nil +} + +func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) { + if keyProtected.Version != version { + return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version) + } + + if keyProtected.Crypto.Cipher != "aes-128-ctr" { + return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher) + } + + keyId = uuid.Parse(keyProtected.Id) + mac, err := hex.DecodeString(keyProtected.Crypto.MAC) + if err != nil { + return nil, nil, err + } + + iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) + if err != nil { + return nil, nil, err + } + + cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) + if err != nil { + return nil, nil, err + } + + derivedKey, err := getKDFKey(keyProtected.Crypto, auth) + if err != nil { + return nil, nil, err + } + + calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) + if !bytes.Equal(calculatedMAC, mac) { + return nil, nil, ErrDecrypt + } + + plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) + if err != nil { + return nil, nil, err + } + return plainText, keyId, err +} + +func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) { + keyId = uuid.Parse(keyProtected.Id) + mac, err := hex.DecodeString(keyProtected.Crypto.MAC) + if err != nil { + return nil, nil, err + } + + iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV) + if err != nil { + return nil, nil, err + } + + cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText) + if err != nil { + return nil, nil, err + } + + derivedKey, err := getKDFKey(keyProtected.Crypto, auth) + if err != nil { + return nil, nil, err + } + + calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) + if !bytes.Equal(calculatedMAC, mac) { + return nil, nil, ErrDecrypt + } + + plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv) + if err != nil { + return nil, nil, err + } + return plainText, keyId, err +} + +func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) { + authArray := []byte(auth) + salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) + if err != nil { + return nil, err + } + dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) + + if cryptoJSON.KDF == "scrypt" { + n := ensureInt(cryptoJSON.KDFParams["n"]) + r := ensureInt(cryptoJSON.KDFParams["r"]) + p := ensureInt(cryptoJSON.KDFParams["p"]) + return scrypt.Key(authArray, salt, n, r, p, dkLen) + + } else if cryptoJSON.KDF == "pbkdf2" { + c := ensureInt(cryptoJSON.KDFParams["c"]) + prf := cryptoJSON.KDFParams["prf"].(string) + if prf != "hmac-sha256" { + return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf) + } + key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) + return key, nil + } + + return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF) +} + +// TODO: can we do without this when unmarshalling dynamic JSON? +// why do integers in KDF params end up as float64 and not int after +// unmarshal? +func ensureInt(x interface{}) int { + res, ok := x.(int) + if !ok { + res = int(x.(float64)) + } + return res +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase_test.go new file mode 100644 index 0000000..086addb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_passphrase_test.go @@ -0,0 +1,60 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "io/ioutil" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + veryLightScryptN = 2 + veryLightScryptP = 1 +) + +// Tests that a json key file can be decrypted and encrypted in multiple rounds. +func TestKeyEncryptDecrypt(t *testing.T) { + keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json") + if err != nil { + t.Fatal(err) + } + password := "" + address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8") + + // Do a few rounds of decryption and encryption + for i := 0; i < 3; i++ { + // Try a bad password first + if _, err := DecryptKey(keyjson, password+"bad"); err == nil { + t.Errorf("test %d: json key decrypted with bad password", i) + } + // Decrypt with the correct password + key, err := DecryptKey(keyjson, password) + if err != nil { + t.Errorf("test %d: json key failed to decrypt: %v", i, err) + } + if key.Address != address { + t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address) + } + // Recrypt with a new password and start over + password += "new data appended" + if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil { + t.Errorf("test %d: failed to recrypt key %v", i, err) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go new file mode 100644 index 0000000..b490ca7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain.go @@ -0,0 +1,62 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" +) + +type keyStorePlain struct { + keysDirPath string +} + +func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + key := new(Key) + if err := json.NewDecoder(fd).Decode(key); err != nil { + return nil, err + } + if key.Address != addr { + return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr) + } + return key, nil +} + +func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error { + content, err := json.Marshal(key) + if err != nil { + return err + } + return writeKeyFile(filename, content) +} + +func (ks keyStorePlain) JoinPath(filename string) string { + if filepath.IsAbs(filename) { + return filename + } else { + return filepath.Join(ks.keysDirPath, filename) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain_test.go new file mode 100644 index 0000000..8c0eb52 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_plain_test.go @@ -0,0 +1,253 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "reflect" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) { + d, err := ioutil.TempDir("", "geth-keystore-test") + if err != nil { + t.Fatal(err) + } + if encrypted { + ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP} + } else { + ks = &keyStorePlain{d} + } + return d, ks +} + +func TestKeyStorePlain(t *testing.T) { + dir, ks := tmpKeyStoreIface(t, false) + defer os.RemoveAll(dir) + + pass := "" // not used but required by API + k1, account, err := storeNewKey(ks, rand.Reader, pass) + if err != nil { + t.Fatal(err) + } + k2, err := ks.GetKey(k1.Address, account.URL.Path, pass) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(k1.Address, k2.Address) { + t.Fatal(err) + } + if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { + t.Fatal(err) + } +} + +func TestKeyStorePassphrase(t *testing.T) { + dir, ks := tmpKeyStoreIface(t, true) + defer os.RemoveAll(dir) + + pass := "foo" + k1, account, err := storeNewKey(ks, rand.Reader, pass) + if err != nil { + t.Fatal(err) + } + k2, err := ks.GetKey(k1.Address, account.URL.Path, pass) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(k1.Address, k2.Address) { + t.Fatal(err) + } + if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { + t.Fatal(err) + } +} + +func TestKeyStorePassphraseDecryptionFail(t *testing.T) { + dir, ks := tmpKeyStoreIface(t, true) + defer os.RemoveAll(dir) + + pass := "foo" + k1, account, err := storeNewKey(ks, rand.Reader, pass) + if err != nil { + t.Fatal(err) + } + if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt { + t.Fatalf("wrong error for invalid passphrase\ngot %q\nwant %q", err, ErrDecrypt) + } +} + +func TestImportPreSaleKey(t *testing.T) { + dir, ks := tmpKeyStoreIface(t, true) + defer os.RemoveAll(dir) + + // file content of a presale key file generated with: + // python pyethsaletool.py genwallet + // with password "foo" + fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}" + pass := "foo" + account, _, err := importPreSaleKey(ks, []byte(fileContent), pass) + if err != nil { + t.Fatal(err) + } + if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") { + t.Errorf("imported account has wrong address %x", account.Address) + } + if !strings.HasPrefix(account.URL.Path, dir) { + t.Errorf("imported account file not in keystore directory: %q", account.URL) + } +} + +// Test and utils for the key store tests in the Ethereum JSON tests; +// testdataKeyStoreTests/basic_tests.json +type KeyStoreTestV3 struct { + Json encryptedKeyJSONV3 + Password string + Priv string +} + +type KeyStoreTestV1 struct { + Json encryptedKeyJSONV1 + Password string + Priv string +} + +func TestV3_PBKDF2_1(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t) + testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t) +} + +func TestV3_PBKDF2_2(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + testDecryptV3(tests["test1"], t) +} + +func TestV3_PBKDF2_3(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + testDecryptV3(tests["python_generated_test_with_odd_iv"], t) +} + +func TestV3_PBKDF2_4(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + testDecryptV3(tests["evilnonce"], t) +} + +func TestV3_Scrypt_1(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t) + testDecryptV3(tests["wikipage_test_vector_scrypt"], t) +} + +func TestV3_Scrypt_2(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t) + testDecryptV3(tests["test2"], t) +} + +func TestV1_1(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV1("testdata/v1_test_vector.json", t) + testDecryptV1(tests["test1"], t) +} + +func TestV1_2(t *testing.T) { + t.Parallel() + ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP} + addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e") + file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e" + k, err := ks.GetKey(addr, file, "g") + if err != nil { + t.Fatal(err) + } + privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)) + expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d" + if privHex != expectedHex { + t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex)) + } +} + +func testDecryptV3(test KeyStoreTestV3, t *testing.T) { + privBytes, _, err := decryptKeyV3(&test.Json, test.Password) + if err != nil { + t.Fatal(err) + } + privHex := hex.EncodeToString(privBytes) + if test.Priv != privHex { + t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) + } +} + +func testDecryptV1(test KeyStoreTestV1, t *testing.T) { + privBytes, _, err := decryptKeyV1(&test.Json, test.Password) + if err != nil { + t.Fatal(err) + } + privHex := hex.EncodeToString(privBytes) + if test.Priv != privHex { + t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) + } +} + +func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 { + tests := make(map[string]KeyStoreTestV3) + err := common.LoadJSON(file, &tests) + if err != nil { + t.Fatal(err) + } + return tests +} + +func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 { + tests := make(map[string]KeyStoreTestV1) + err := common.LoadJSON(file, &tests) + if err != nil { + t.Fatal(err) + } + return tests +} + +func TestKeyForDirectICAP(t *testing.T) { + t.Parallel() + key := NewKeyForDirectICAP(rand.Reader) + if !strings.HasPrefix(key.Address.Hex(), "0x00") { + t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex()) + } +} + +func TestV3_31_Byte_Key(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t) + testDecryptV3(tests["31_byte_key"], t) +} + +func TestV3_30_Byte_Key(t *testing.T) { + t.Parallel() + tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t) + testDecryptV3(tests["30_byte_key"], t) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_test.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_test.go new file mode 100644 index 0000000..60f2606 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_test.go @@ -0,0 +1,365 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "io/ioutil" + "math/rand" + "os" + "runtime" + "sort" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/event" +) + +var testSigData = make([]byte, 32) + +func TestKeyStore(t *testing.T) { + dir, ks := tmpKeyStore(t, true) + defer os.RemoveAll(dir) + + a, err := ks.NewAccount("foo") + if err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(a.URL.Path, dir) { + t.Errorf("account file %s doesn't have dir prefix", a.URL) + } + stat, err := os.Stat(a.URL.Path) + if err != nil { + t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) + } + if runtime.GOOS != "windows" && stat.Mode() != 0600 { + t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) + } + if !ks.HasAddress(a.Address) { + t.Errorf("HasAccount(%x) should've returned true", a.Address) + } + if err := ks.Update(a, "foo", "bar"); err != nil { + t.Errorf("Update error: %v", err) + } + if err := ks.Delete(a, "bar"); err != nil { + t.Errorf("Delete error: %v", err) + } + if common.FileExist(a.URL.Path) { + t.Errorf("account file %s should be gone after Delete", a.URL) + } + if ks.HasAddress(a.Address) { + t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) + } +} + +func TestSign(t *testing.T) { + dir, ks := tmpKeyStore(t, true) + defer os.RemoveAll(dir) + + pass := "" // not used but required by API + a1, err := ks.NewAccount(pass) + if err != nil { + t.Fatal(err) + } + if err := ks.Unlock(a1, ""); err != nil { + t.Fatal(err) + } + if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { + t.Fatal(err) + } +} + +func TestSignWithPassphrase(t *testing.T) { + dir, ks := tmpKeyStore(t, true) + defer os.RemoveAll(dir) + + pass := "passwd" + acc, err := ks.NewAccount(pass) + if err != nil { + t.Fatal(err) + } + + if _, unlocked := ks.unlocked[acc.Address]; unlocked { + t.Fatal("expected account to be locked") + } + + _, err = ks.SignHashWithPassphrase(acc, pass, testSigData) + if err != nil { + t.Fatal(err) + } + + if _, unlocked := ks.unlocked[acc.Address]; unlocked { + t.Fatal("expected account to be locked") + } + + if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil { + t.Fatal("expected SignHashWithPassphrase to fail with invalid password") + } +} + +func TestTimedUnlock(t *testing.T) { + dir, ks := tmpKeyStore(t, true) + defer os.RemoveAll(dir) + + pass := "foo" + a1, err := ks.NewAccount(pass) + if err != nil { + t.Fatal(err) + } + + // Signing without passphrase fails because account is locked + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != ErrLocked { + t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) + } + + // Signing with passphrase works + if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { + t.Fatal(err) + } + + // Signing without passphrase works because account is temp unlocked + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != nil { + t.Fatal("Signing shouldn't return an error after unlocking, got ", err) + } + + // Signing fails again after automatic locking + time.Sleep(250 * time.Millisecond) + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != ErrLocked { + t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) + } +} + +func TestOverrideUnlock(t *testing.T) { + dir, ks := tmpKeyStore(t, false) + defer os.RemoveAll(dir) + + pass := "foo" + a1, err := ks.NewAccount(pass) + if err != nil { + t.Fatal(err) + } + + // Unlock indefinitely. + if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil { + t.Fatal(err) + } + + // Signing without passphrase works because account is temp unlocked + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != nil { + t.Fatal("Signing shouldn't return an error after unlocking, got ", err) + } + + // reset unlock to a shorter period, invalidates the previous unlock + if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { + t.Fatal(err) + } + + // Signing without passphrase still works because account is temp unlocked + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != nil { + t.Fatal("Signing shouldn't return an error after unlocking, got ", err) + } + + // Signing fails again after automatic locking + time.Sleep(250 * time.Millisecond) + _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) + if err != ErrLocked { + t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) + } +} + +// This test should fail under -race if signing races the expiration goroutine. +func TestSignRace(t *testing.T) { + dir, ks := tmpKeyStore(t, false) + defer os.RemoveAll(dir) + + // Create a test account. + a1, err := ks.NewAccount("") + if err != nil { + t.Fatal("could not create the test account", err) + } + + if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { + t.Fatal("could not unlock the test account", err) + } + end := time.Now().Add(500 * time.Millisecond) + for time.Now().Before(end) { + if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { + return + } else if err != nil { + t.Errorf("Sign error: %v", err) + return + } + time.Sleep(1 * time.Millisecond) + } + t.Errorf("Account did not lock within the timeout") +} + +// Tests that the wallet notifier loop starts and stops correctly based on the +// addition and removal of wallet event subscriptions. +func TestWalletNotifierLifecycle(t *testing.T) { + // Create a temporary kesytore to test with + dir, ks := tmpKeyStore(t, false) + defer os.RemoveAll(dir) + + // Ensure that the notification updater is not running yet + time.Sleep(250 * time.Millisecond) + ks.mu.RLock() + updating := ks.updating + ks.mu.RUnlock() + + if updating { + t.Errorf("wallet notifier running without subscribers") + } + // Subscribe to the wallet feed and ensure the updater boots up + updates := make(chan accounts.WalletEvent) + + subs := make([]event.Subscription, 2) + for i := 0; i < len(subs); i++ { + // Create a new subscription + subs[i] = ks.Subscribe(updates) + + // Ensure the notifier comes online + time.Sleep(250 * time.Millisecond) + ks.mu.RLock() + updating = ks.updating + ks.mu.RUnlock() + + if !updating { + t.Errorf("sub %d: wallet notifier not running after subscription", i) + } + } + // Unsubscribe and ensure the updater terminates eventually + for i := 0; i < len(subs); i++ { + // Close an existing subscription + subs[i].Unsubscribe() + + // Ensure the notifier shuts down at and only at the last close + for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ { + ks.mu.RLock() + updating = ks.updating + ks.mu.RUnlock() + + if i < len(subs)-1 && !updating { + t.Fatalf("sub %d: event notifier stopped prematurely", i) + } + if i == len(subs)-1 && !updating { + return + } + time.Sleep(250 * time.Millisecond) + } + } + t.Errorf("wallet notifier didn't terminate after unsubscribe") +} + +// Tests that wallet notifications and correctly fired when accounts are added +// or deleted from the keystore. +func TestWalletNotifications(t *testing.T) { + // Create a temporary kesytore to test with + dir, ks := tmpKeyStore(t, false) + defer os.RemoveAll(dir) + + // Subscribe to the wallet feed + updates := make(chan accounts.WalletEvent, 1) + sub := ks.Subscribe(updates) + defer sub.Unsubscribe() + + // Randomly add and remove account and make sure events and wallets are in sync + live := make(map[common.Address]accounts.Account) + for i := 0; i < 1024; i++ { + // Execute a creation or deletion and ensure event arrival + if create := len(live) == 0 || rand.Int()%4 > 0; create { + // Add a new account and ensure wallet notifications arrives + account, err := ks.NewAccount("") + if err != nil { + t.Fatalf("failed to create test account: %v", err) + } + select { + case event := <-updates: + if !event.Arrive { + t.Errorf("departure event on account creation") + } + if event.Wallet.Accounts()[0] != account { + t.Errorf("account mismatch on created wallet: have %v, want %v", event.Wallet.Accounts()[0], account) + } + default: + t.Errorf("wallet arrival event not fired on account creation") + } + live[account.Address] = account + } else { + // Select a random account to delete (crude, but works) + var account accounts.Account + for _, a := range live { + account = a + break + } + // Remove an account and ensure wallet notifiaction arrives + if err := ks.Delete(account, ""); err != nil { + t.Fatalf("failed to delete test account: %v", err) + } + select { + case event := <-updates: + if event.Arrive { + t.Errorf("arrival event on account deletion") + } + if event.Wallet.Accounts()[0] != account { + t.Errorf("account mismatch on deleted wallet: have %v, want %v", event.Wallet.Accounts()[0], account) + } + default: + t.Errorf("wallet departure event not fired on account creation") + } + delete(live, account.Address) + } + // Retrieve the list of wallets and ensure it matches with our required live set + liveList := make([]accounts.Account, 0, len(live)) + for _, account := range live { + liveList = append(liveList, account) + } + sort.Sort(accountsByURL(liveList)) + + wallets := ks.Wallets() + if len(liveList) != len(wallets) { + t.Errorf("wallet list doesn't match required accounts: have %v, want %v", wallets, liveList) + } else { + for j, wallet := range wallets { + if accs := wallet.Accounts(); len(accs) != 1 { + t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) + } else if accs[0] != liveList[j] { + t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) + } + } + } + } +} + +func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { + d, err := ioutil.TempDir("", "eth-keystore-test") + if err != nil { + t.Fatal(err) + } + new := NewPlaintextKeyStore + if encrypted { + new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } + } + return d, new(d) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go new file mode 100644 index 0000000..7165d28 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/keystore_wallet.go @@ -0,0 +1,139 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/core/types" +) + +// keystoreWallet implements the accounts.Wallet interface for the original +// keystore. +type keystoreWallet struct { + account accounts.Account // Single account contained in this wallet + keystore *KeyStore // Keystore where the account originates from +} + +// URL implements accounts.Wallet, returning the URL of the account within. +func (w *keystoreWallet) URL() accounts.URL { + return w.account.URL +} + +// Status implements accounts.Wallet, always returning "open", since there is no +// concept of open/close for plain keystore accounts. +func (w *keystoreWallet) Status() string { + w.keystore.mu.RLock() + defer w.keystore.mu.RUnlock() + + if _, ok := w.keystore.unlocked[w.account.Address]; ok { + return "Unlocked" + } + return "Locked" +} + +// Open implements accounts.Wallet, but is a noop for plain wallets since there +// is no connection or decryption step necessary to access the list of accounts. +func (w *keystoreWallet) Open(passphrase string) error { return nil } + +// Close implements accounts.Wallet, but is a noop for plain wallets since is no +// meaningful open operation. +func (w *keystoreWallet) Close() error { return nil } + +// Accounts implements accounts.Wallet, returning an account list consisting of +// a single account that the plain kestore wallet contains. +func (w *keystoreWallet) Accounts() []accounts.Account { + return []accounts.Account{w.account} +} + +// Contains implements accounts.Wallet, returning whether a particular account is +// or is not wrapped by this wallet instance. +func (w *keystoreWallet) Contains(account accounts.Account) bool { + return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL) +} + +// Derive implements accounts.Wallet, but is a noop for plain wallets since there +// is no notion of hierarchical account derivation for plain keystore accounts. +func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { + return accounts.Account{}, accounts.ErrNotSupported +} + +// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since +// there is no notion of hierarchical account derivation for plain keystore accounts. +func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {} + +// SignHash implements accounts.Wallet, attempting to sign the given hash with +// the given account. If the wallet does not wrap this particular account, an +// error is returned to avoid account leakage (even though in theory we may be +// able to sign via our shared keystore backend). +func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { + // Make sure the requested account is contained within + if account.Address != w.account.Address { + return nil, accounts.ErrUnknownAccount + } + if account.URL != (accounts.URL{}) && account.URL != w.account.URL { + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignHash(account, hash) +} + +// SignTx implements accounts.Wallet, attempting to sign the given transaction +// with the given account. If the wallet does not wrap this particular account, +// an error is returned to avoid account leakage (even though in theory we may +// be able to sign via our shared keystore backend). +func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + // Make sure the requested account is contained within + if account.Address != w.account.Address { + return nil, accounts.ErrUnknownAccount + } + if account.URL != (accounts.URL{}) && account.URL != w.account.URL { + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignTx(account, tx, chainID) +} + +// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the +// given hash with the given account using passphrase as extra authentication. +func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { + // Make sure the requested account is contained within + if account.Address != w.account.Address { + return nil, accounts.ErrUnknownAccount + } + if account.URL != (accounts.URL{}) && account.URL != w.account.URL { + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignHashWithPassphrase(account, passphrase, hash) +} + +// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given +// transaction with the given account using passphrase as extra authentication. +func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + // Make sure the requested account is contained within + if account.Address != w.account.Address { + return nil, accounts.ErrUnknownAccount + } + if account.URL != (accounts.URL{}) && account.URL != w.account.URL { + return nil, accounts.ErrUnknownAccount + } + // Account seems valid, request the keystore to sign + return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go new file mode 100644 index 0000000..5b883c4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/presale.go @@ -0,0 +1,137 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package keystore + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pborman/uuid" + "golang.org/x/crypto/pbkdf2" +) + +// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON +func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) { + key, err := decryptPreSaleKey(keyJSON, password) + if err != nil { + return accounts.Account{}, nil, err + } + key.Id = uuid.NewRandom() + a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}} + err = keyStore.StoreKey(a.URL.Path, key, password) + return a, key, err +} + +func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) { + preSaleKeyStruct := struct { + EncSeed string + EthAddr string + Email string + BtcAddr string + }{} + err = json.Unmarshal(fileContent, &preSaleKeyStruct) + if err != nil { + return nil, err + } + encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed) + if err != nil { + return nil, errors.New("invalid hex in encSeed") + } + iv := encSeedBytes[:16] + cipherText := encSeedBytes[16:] + /* + See https://github.com/ethereum/pyethsaletool + + pyethsaletool generates the encryption key from password by + 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:(). + 16 byte key length within PBKDF2 and resulting key is used as AES key + */ + passBytes := []byte(password) + derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New) + plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv) + if err != nil { + return nil, err + } + ethPriv := crypto.Keccak256(plainText) + ecKey := crypto.ToECDSA(ethPriv) + key = &Key{ + Id: nil, + Address: crypto.PubkeyToAddress(ecKey.PublicKey), + PrivateKey: ecKey, + } + derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x" + expectedAddr := preSaleKeyStruct.EthAddr + if derivedAddr != expectedAddr { + err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr) + } + return key, err +} + +func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { + // AES-128 is selected due to size of encryptKey. + aesBlock, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + stream := cipher.NewCTR(aesBlock, iv) + outText := make([]byte, len(inText)) + stream.XORKeyStream(outText, inText) + return outText, err +} + +func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { + aesBlock, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + decrypter := cipher.NewCBCDecrypter(aesBlock, iv) + paddedPlaintext := make([]byte, len(cipherText)) + decrypter.CryptBlocks(paddedPlaintext, cipherText) + plaintext := pkcs7Unpad(paddedPlaintext) + if plaintext == nil { + return nil, ErrDecrypt + } + return plaintext, err +} + +// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes +func pkcs7Unpad(in []byte) []byte { + if len(in) == 0 { + return nil + } + + padding := in[len(in)-1] + if int(padding) > len(in) || padding > aes.BlockSize { + return nil + } else if padding == 0 { + return nil + } + + for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { + if in[i] != padding { + return nil + } + } + return in[:len(in)-int(padding)] +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/1 b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/1 new file mode 100644 index 0000000..a3868ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/1 @@ -0,0 +1 @@ +{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/2 b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/2 new file mode 100644 index 0000000..a3868ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/2 @@ -0,0 +1 @@ +{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/foo b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/foo new file mode 100644 index 0000000..c57060a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/dupes/foo @@ -0,0 +1 @@ +{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/.hiddenfile b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/.hiddenfile new file mode 100644 index 0000000..d91facc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/.hiddenfile @@ -0,0 +1 @@ +{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/README b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/README new file mode 100644 index 0000000..a5a86f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/README @@ -0,0 +1,21 @@ +This directory contains accounts for testing. +The passphrase that unlocks them is "foobar". + +The "good" key files which are supposed to be loadable are: + +- File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 + Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8 +- File: aaa + Address: 0xf466859ead1932d743d622cb74fc058882e8648a +- File: zzz + Address: 0x289d485d9771714cce91d3393d764e1311907acc + +The other files (including this README) are broken in various ways +and should not be picked up by package accounts: + +- File: no-address (missing address field, otherwise same as "aaa") +- File: garbage (file with random data) +- File: empty (file with no content) +- File: swapfile~ (should be skipped) +- File: .hiddenfile (should be skipped) +- File: foo/... (should be skipped because it is a directory) diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 new file mode 100644 index 0000000..c57060a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 @@ -0,0 +1 @@ +{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/aaa b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/aaa new file mode 100644 index 0000000..a3868ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/aaa @@ -0,0 +1 @@ +{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/empty b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/empty new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e new file mode 100644 index 0000000..309841e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e @@ -0,0 +1 @@ +{"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/garbage b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/testdata/keystore/garbage new file mode 100644 index 0000000000000000000000000000000000000000..ff45091e714078dd7d3b4ea95964452e33a895f7 GIT binary patch literal 300 zcmV+{0n`3r1xkOa0KiH=0-y31ays31&4D+~b{#6-MH z)8?iosg+26q81!5ujp29iM}4_d}^;*-$8$htAbEpk(KDl*$;NvD$v8GZL@TRuT#)+ zq*|PXNljY5_xwCfoMayTjJ(vY;=t!uVJT5-Fn0O7W{#e;Ho?+NsQQi=!GV>j#9U#& zAbp7L1M-8N-V+7}EDxG9CNuhKbj?($B?=E1a1Xi%v;bYvR+C$EjApbg!W^>zB$Cd( z+NKd!El}@p)NJLnQ}B=D#e5uCh87_~lKd2z=idP7$. + +// +build darwin,!ios freebsd linux,!arm64 netbsd solaris + +package keystore + +import ( + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/rjeczalik/notify" +) + +type watcher struct { + ac *accountCache + starting bool + running bool + ev chan notify.EventInfo + quit chan struct{} +} + +func newWatcher(ac *accountCache) *watcher { + return &watcher{ + ac: ac, + ev: make(chan notify.EventInfo, 10), + quit: make(chan struct{}), + } +} + +// starts the watcher loop in the background. +// Start a watcher in the background if that's not already in progress. +// The caller must hold w.ac.mu. +func (w *watcher) start() { + if w.starting || w.running { + return + } + w.starting = true + go w.loop() +} + +func (w *watcher) close() { + close(w.quit) +} + +func (w *watcher) loop() { + defer func() { + w.ac.mu.Lock() + w.running = false + w.starting = false + w.ac.mu.Unlock() + }() + + err := notify.Watch(w.ac.keydir, w.ev, notify.All) + if err != nil { + glog.V(logger.Detail).Infof("can't watch %s: %v", w.ac.keydir, err) + return + } + defer notify.Stop(w.ev) + glog.V(logger.Detail).Infof("now watching %s", w.ac.keydir) + defer glog.V(logger.Detail).Infof("no longer watching %s", w.ac.keydir) + + w.ac.mu.Lock() + w.running = true + w.ac.mu.Unlock() + + // Wait for file system events and reload. + // When an event occurs, the reload call is delayed a bit so that + // multiple events arriving quickly only cause a single reload. + var ( + debounce = time.NewTimer(0) + debounceDuration = 500 * time.Millisecond + inCycle, hadEvent bool + ) + defer debounce.Stop() + for { + select { + case <-w.quit: + return + case <-w.ev: + if !inCycle { + debounce.Reset(debounceDuration) + inCycle = true + } else { + hadEvent = true + } + case <-debounce.C: + w.ac.mu.Lock() + w.ac.reload() + w.ac.mu.Unlock() + if hadEvent { + debounce.Reset(debounceDuration) + inCycle, hadEvent = true, false + } else { + inCycle, hadEvent = false, false + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go new file mode 100644 index 0000000..7c5e9cb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/keystore/watch_fallback.go @@ -0,0 +1,28 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris + +// This is the fallback implementation of directory watching. +// It is used on unsupported platforms. + +package keystore + +type watcher struct{ running bool } + +func newWatcher(*accountCache) *watcher { return new(watcher) } +func (*watcher) start() {} +func (*watcher) close() {} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/manager.go b/vendor/github.com/ethereum/go-ethereum/accounts/manager.go new file mode 100644 index 0000000..12a5bfc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/manager.go @@ -0,0 +1,198 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package accounts + +import ( + "reflect" + "sort" + "sync" + + "github.com/ethereum/go-ethereum/event" +) + +// Manager is an overarching account manager that can communicate with various +// backends for signing transactions. +type Manager struct { + backends map[reflect.Type][]Backend // Index of backends currently registered + updaters []event.Subscription // Wallet update subscriptions for all backends + updates chan WalletEvent // Subscription sink for backend wallet changes + wallets []Wallet // Cache of all wallets from all registered backends + + feed event.Feed // Wallet feed notifying of arrivals/departures + + quit chan chan error + lock sync.RWMutex +} + +// NewManager creates a generic account manager to sign transaction via various +// supported backends. +func NewManager(backends ...Backend) *Manager { + // Subscribe to wallet notifications from all backends + updates := make(chan WalletEvent, 4*len(backends)) + + subs := make([]event.Subscription, len(backends)) + for i, backend := range backends { + subs[i] = backend.Subscribe(updates) + } + // Retrieve the initial list of wallets from the backends and sort by URL + var wallets []Wallet + for _, backend := range backends { + wallets = merge(wallets, backend.Wallets()...) + } + // Assemble the account manager and return + am := &Manager{ + backends: make(map[reflect.Type][]Backend), + updaters: subs, + updates: updates, + wallets: wallets, + quit: make(chan chan error), + } + for _, backend := range backends { + kind := reflect.TypeOf(backend) + am.backends[kind] = append(am.backends[kind], backend) + } + go am.update() + + return am +} + +// Close terminates the account manager's internal notification processes. +func (am *Manager) Close() error { + errc := make(chan error) + am.quit <- errc + return <-errc +} + +// update is the wallet event loop listening for notifications from the backends +// and updating the cache of wallets. +func (am *Manager) update() { + // Close all subscriptions when the manager terminates + defer func() { + am.lock.Lock() + for _, sub := range am.updaters { + sub.Unsubscribe() + } + am.updaters = nil + am.lock.Unlock() + }() + + // Loop until termination + for { + select { + case event := <-am.updates: + // Wallet event arrived, update local cache + am.lock.Lock() + if event.Arrive { + am.wallets = merge(am.wallets, event.Wallet) + } else { + am.wallets = drop(am.wallets, event.Wallet) + } + am.lock.Unlock() + + // Notify any listeners of the event + am.feed.Send(event) + + case errc := <-am.quit: + // Manager terminating, return + errc <- nil + return + } + } +} + +// Backends retrieves the backend(s) with the given type from the account manager. +func (am *Manager) Backends(kind reflect.Type) []Backend { + return am.backends[kind] +} + +// Wallets returns all signer accounts registered under this account manager. +func (am *Manager) Wallets() []Wallet { + am.lock.RLock() + defer am.lock.RUnlock() + + cpy := make([]Wallet, len(am.wallets)) + copy(cpy, am.wallets) + return cpy +} + +// Wallet retrieves the wallet associated with a particular URL. +func (am *Manager) Wallet(url string) (Wallet, error) { + am.lock.RLock() + defer am.lock.RUnlock() + + parsed, err := parseURL(url) + if err != nil { + return nil, err + } + for _, wallet := range am.Wallets() { + if wallet.URL() == parsed { + return wallet, nil + } + } + return nil, ErrUnknownWallet +} + +// Find attempts to locate the wallet corresponding to a specific account. Since +// accounts can be dynamically added to and removed from wallets, this method has +// a linear runtime in the number of wallets. +func (am *Manager) Find(account Account) (Wallet, error) { + am.lock.RLock() + defer am.lock.RUnlock() + + for _, wallet := range am.wallets { + if wallet.Contains(account) { + return wallet, nil + } + } + return nil, ErrUnknownAccount +} + +// Subscribe creates an async subscription to receive notifications when the +// manager detects the arrival or departure of a wallet from any of its backends. +func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription { + return am.feed.Subscribe(sink) +} + +// merge is a sorted analogue of append for wallets, where the ordering of the +// origin list is preserved by inserting new wallets at the correct position. +// +// The original slice is assumed to be already sorted by URL. +func merge(slice []Wallet, wallets ...Wallet) []Wallet { + for _, wallet := range wallets { + n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) + if n == len(slice) { + slice = append(slice, wallet) + continue + } + slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...) + } + return slice +} + +// drop is the couterpart of merge, which looks up wallets from within the sorted +// cache and removes the ones specified. +func drop(slice []Wallet, wallets ...Wallet) []Wallet { + for _, wallet := range wallets { + n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) + if n == len(slice) { + // Wallet not found, may happen during startup + continue + } + slice = append(slice[:n], slice[n+1:]...) + } + return slice +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/url.go b/vendor/github.com/ethereum/go-ethereum/accounts/url.go new file mode 100644 index 0000000..a2d00c1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/url.go @@ -0,0 +1,79 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package accounts + +import ( + "encoding/json" + "errors" + "fmt" + "strings" +) + +// URL represents the canonical identification URL of a wallet or account. +// +// It is a simplified version of url.URL, with the important limitations (which +// are considered features here) that it contains value-copyable components only, +// as well as that it doesn't do any URL encoding/decoding of special characters. +// +// The former is important to allow an account to be copied without leaving live +// references to the original version, whereas the latter is important to ensure +// one single canonical form opposed to many allowed ones by the RFC 3986 spec. +// +// As such, these URLs should not be used outside of the scope of an Ethereum +// wallet or account. +type URL struct { + Scheme string // Protocol scheme to identify a capable account backend + Path string // Path for the backend to identify a unique entity +} + +// parseURL converts a user supplied URL into the accounts specific structure. +func parseURL(url string) (URL, error) { + parts := strings.Split(url, "://") + if len(parts) != 2 || parts[0] == "" { + return URL{}, errors.New("protocol scheme missing") + } + return URL{ + Scheme: parts[0], + Path: parts[1], + }, nil +} + +// String implements the stringer interface. +func (u URL) String() string { + if u.Scheme != "" { + return fmt.Sprintf("%s://%s", u.Scheme, u.Path) + } + return u.Path +} + +// MarshalJSON implements the json.Marshaller interface. +func (u URL) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (u URL) Cmp(url URL) int { + if u.Scheme == url.Scheme { + return strings.Compare(u.Path, url.Path) + } + return strings.Compare(u.Scheme, url.Scheme) +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go new file mode 100644 index 0000000..70396d3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_hub.go @@ -0,0 +1,192 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains the implementation for interacting with the Ledger hardware +// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo: +// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc + +package usbwallet + +import ( + "errors" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/event" + "github.com/karalabe/hid" +) + +// LedgerScheme is the protocol scheme prefixing account and wallet URLs. +var LedgerScheme = "ledger" + +// ledgerDeviceIDs are the known device IDs that Ledger wallets use. +var ledgerDeviceIDs = []deviceID{ + {Vendor: 0x2c97, Product: 0x0000}, // Ledger Blue + {Vendor: 0x2c97, Product: 0x0001}, // Ledger Nano S +} + +// Maximum time between wallet refreshes (if USB hotplug notifications don't work). +const ledgerRefreshCycle = time.Second + +// Minimum time between wallet refreshes to avoid USB trashing. +const ledgerRefreshThrottling = 500 * time.Millisecond + +// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets. +type LedgerHub struct { + refreshed time.Time // Time instance when the list of wallets was last refreshed + wallets []accounts.Wallet // List of Ledger devices currently tracking + updateFeed event.Feed // Event feed to notify wallet additions/removals + updateScope event.SubscriptionScope // Subscription scope tracking current live listeners + updating bool // Whether the event notification loop is running + + quit chan chan error + lock sync.RWMutex +} + +// NewLedgerHub creates a new hardware wallet manager for Ledger devices. +func NewLedgerHub() (*LedgerHub, error) { + if !hid.Supported() { + return nil, errors.New("unsupported platform") + } + hub := &LedgerHub{ + quit: make(chan chan error), + } + hub.refreshWallets() + return hub, nil +} + +// Wallets implements accounts.Backend, returning all the currently tracked USB +// devices that appear to be Ledger hardware wallets. +func (hub *LedgerHub) Wallets() []accounts.Wallet { + // Make sure the list of wallets is up to date + hub.refreshWallets() + + hub.lock.RLock() + defer hub.lock.RUnlock() + + cpy := make([]accounts.Wallet, len(hub.wallets)) + copy(cpy, hub.wallets) + return cpy +} + +// refreshWallets scans the USB devices attached to the machine and updates the +// list of wallets based on the found devices. +func (hub *LedgerHub) refreshWallets() { + // Don't scan the USB like crazy it the user fetches wallets in a loop + hub.lock.RLock() + elapsed := time.Since(hub.refreshed) + hub.lock.RUnlock() + + if elapsed < ledgerRefreshThrottling { + return + } + // Retrieve the current list of Ledger devices + var ledgers []hid.DeviceInfo + for _, info := range hid.Enumerate(0, 0) { // Can't enumerate directly, one valid ID is the 0 wildcard + for _, id := range ledgerDeviceIDs { + if info.VendorID == id.Vendor && info.ProductID == id.Product { + ledgers = append(ledgers, info) + break + } + } + } + // Transform the current list of wallets into the new one + hub.lock.Lock() + + wallets := make([]accounts.Wallet, 0, len(ledgers)) + events := []accounts.WalletEvent{} + + for _, ledger := range ledgers { + url := accounts.URL{Scheme: LedgerScheme, Path: ledger.Path} + + // Drop wallets in front of the next device or those that failed for some reason + for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) { + events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Arrive: false}) + hub.wallets = hub.wallets[1:] + } + // If there are no more wallets or the device is before the next, wrap new wallet + if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 { + wallet := &ledgerWallet{url: &url, info: ledger} + + events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true}) + wallets = append(wallets, wallet) + continue + } + // If the device is the same as the first wallet, keep it + if hub.wallets[0].URL().Cmp(url) == 0 { + wallets = append(wallets, hub.wallets[0]) + hub.wallets = hub.wallets[1:] + continue + } + } + // Drop any leftover wallets and set the new batch + for _, wallet := range hub.wallets { + events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: false}) + } + hub.refreshed = time.Now() + hub.wallets = wallets + hub.lock.Unlock() + + // Fire all wallet events and return + for _, event := range events { + hub.updateFeed.Send(event) + } +} + +// Subscribe implements accounts.Backend, creating an async subscription to +// receive notifications on the addition or removal of Ledger wallets. +func (hub *LedgerHub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { + // We need the mutex to reliably start/stop the update loop + hub.lock.Lock() + defer hub.lock.Unlock() + + // Subscribe the caller and track the subscriber count + sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink)) + + // Subscribers require an active notification loop, start it + if !hub.updating { + hub.updating = true + go hub.updater() + } + return sub +} + +// updater is responsible for maintaining an up-to-date list of wallets stored in +// the keystore, and for firing wallet addition/removal events. It listens for +// account change events from the underlying account cache, and also periodically +// forces a manual refresh (only triggers for systems where the filesystem notifier +// is not running). +func (hub *LedgerHub) updater() { + for { + // Wait for a USB hotplug event (not supported yet) or a refresh timeout + select { + //case <-hub.changes: // reenable on hutplug implementation + case <-time.After(ledgerRefreshCycle): + } + // Run the wallet refresher + hub.refreshWallets() + + // If all our subscribers left, stop the updater + hub.lock.Lock() + if hub.updateScope.Count() == 0 { + hub.updating = false + hub.lock.Unlock() + return + } + hub.lock.Unlock() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go new file mode 100644 index 0000000..235086d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger_wallet.go @@ -0,0 +1,892 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains the implementation for interacting with the Ledger hardware +// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo: +// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc + +package usbwallet + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + "sync" + "time" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/karalabe/hid" + "golang.org/x/net/context" +) + +// Maximum time between wallet health checks to detect USB unplugs. +const ledgerHeartbeatCycle = time.Second + +// Minimum time to wait between self derivation attempts, even it the user is +// requesting accounts like crazy. +const ledgerSelfDeriveThrottling = time.Second + +// ledgerOpcode is an enumeration encoding the supported Ledger opcodes. +type ledgerOpcode byte + +// ledgerParam1 is an enumeration encoding the supported Ledger parameters for +// specific opcodes. The same parameter values may be reused between opcodes. +type ledgerParam1 byte + +// ledgerParam2 is an enumeration encoding the supported Ledger parameters for +// specific opcodes. The same parameter values may be reused between opcodes. +type ledgerParam2 byte + +const ( + ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path + ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters + ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration + + ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet + ledgerP1ConfirmFetchAddress ledgerParam1 = 0x01 // Require a user confirmation before returning the address + ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing + ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing + ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address + ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address +) + +// errReplyInvalidHeader is the error message returned by a Ledger data exchange +// if the device replies with a mismatching header. This usually means the device +// is in browser mode. +var errReplyInvalidHeader = errors.New("invalid reply header") + +// errInvalidVersionReply is the error message returned by a Ledger version retrieval +// when a response does arrive, but it does not contain the expected data. +var errInvalidVersionReply = errors.New("invalid version reply") + +// ledgerWallet represents a live USB Ledger hardware wallet. +type ledgerWallet struct { + url *accounts.URL // Textual URL uniquely identifying this wallet + + info hid.DeviceInfo // Known USB device infos about the wallet + device *hid.Device // USB device advertising itself as a Ledger wallet + failure error // Any failure that would make the device unusable + + version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline) + browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch) + accounts []accounts.Account // List of derive accounts pinned on the Ledger + paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations + + deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery + deriveNextAddr common.Address // Next derived account address for auto-discovery + deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with + deriveReq chan chan struct{} // Channel to request a self-derivation on + deriveQuit chan chan error // Channel to terminate the self-deriver with + + healthQuit chan chan error + + // Locking a hardware wallet is a bit special. Since hardware devices are lower + // performing, any communication with them might take a non negligible amount of + // time. Worse still, waiting for user confirmation can take arbitrarily long, + // but exclusive communication must be upheld during. Locking the entire wallet + // in the mean time however would stall any parts of the system that don't want + // to communicate, just read some state (e.g. list the accounts). + // + // As such, a hardware wallet needs two locks to function correctly. A state + // lock can be used to protect the wallet's software-side internal state, which + // must not be held exlusively during hardware communication. A communication + // lock can be used to achieve exclusive access to the device itself, this one + // however should allow "skipping" waiting for operations that might want to + // use the device, but can live without too (e.g. account self-derivation). + // + // Since we have two locks, it's important to know how to properly use them: + // - Communication requires the `device` to not change, so obtaining the + // commsLock should be done after having a stateLock. + // - Communication must not disable read access to the wallet state, so it + // must only ever hold a *read* lock to stateLock. + commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked + stateLock sync.RWMutex // Protects read and write access to the wallet struct fields +} + +// URL implements accounts.Wallet, returning the URL of the Ledger device. +func (w *ledgerWallet) URL() accounts.URL { + return *w.url // Immutable, no need for a lock +} + +// Status implements accounts.Wallet, always whether the Ledger is opened, closed +// or whether the Ethereum app was not started on it. +func (w *ledgerWallet) Status() string { + w.stateLock.RLock() // No device communication, state lock is enough + defer w.stateLock.RUnlock() + + if w.failure != nil { + return fmt.Sprintf("Failed: %v", w.failure) + } + if w.device == nil { + return "Closed" + } + if w.browser { + return "Ethereum app in browser mode" + } + if w.offline() { + return "Ethereum app offline" + } + return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]) +} + +// offline returns whether the wallet and the Ethereum app is offline or not. +// +// The method assumes that the state lock is held! +func (w *ledgerWallet) offline() bool { + return w.version == [3]byte{0, 0, 0} +} + +// failed returns if the USB device wrapped by the wallet failed for some reason. +// This is used by the device scanner to report failed wallets as departed. +// +// The method assumes that the state lock is *not* held! +func (w *ledgerWallet) failed() bool { + w.stateLock.RLock() // No device communication, state lock is enough + defer w.stateLock.RUnlock() + + return w.failure != nil +} + +// Open implements accounts.Wallet, attempting to open a USB connection to the +// Ledger hardware wallet. The Ledger does not require a user passphrase, so that +// parameter is silently discarded. +func (w *ledgerWallet) Open(passphrase string) error { + w.stateLock.Lock() // State lock is enough since there's no connection yet at this point + defer w.stateLock.Unlock() + + // If the wallet was already opened, don't try to open again + if w.device != nil { + return accounts.ErrWalletAlreadyOpen + } + // Otherwise iterate over all USB devices and find this again (no way to directly do this) + device, err := w.info.Open() + if err != nil { + return err + } + // Wallet seems to be successfully opened, guess if the Ethereum app is running + w.device = device + w.commsLock = make(chan struct{}, 1) + w.commsLock <- struct{}{} // Enable lock + + w.paths = make(map[common.Address]accounts.DerivationPath) + + w.deriveReq = make(chan chan struct{}) + w.deriveQuit = make(chan chan error) + w.healthQuit = make(chan chan error) + + defer func() { + go w.heartbeat() + go w.selfDerive() + }() + + if _, err = w.ledgerDerive(accounts.DefaultBaseDerivationPath); err != nil { + // Ethereum app is not running or in browser mode, nothing more to do, return + if err == errReplyInvalidHeader { + w.browser = true + } + return nil + } + // Try to resolve the Ethereum app's version, will fail prior to v1.0.2 + if w.version, err = w.ledgerVersion(); err != nil { + w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1 + } + return nil +} + +// heartbeat is a health check loop for the Ledger wallets to periodically verify +// whether they are still present or if they malfunctioned. It is needed because: +// - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs +// - communication timeout on the Ledger requires a device power cycle to fix +func (w *ledgerWallet) heartbeat() { + glog.V(logger.Debug).Infof("%s health-check started", w.url.String()) + defer glog.V(logger.Debug).Infof("%s health-check stopped", w.url.String()) + + // Execute heartbeat checks until termination or error + var ( + errc chan error + err error + ) + for errc == nil && err == nil { + // Wait until termination is requested or the heartbeat cycle arrives + select { + case errc = <-w.healthQuit: + // Termination requested + continue + case <-time.After(ledgerHeartbeatCycle): + // Heartbeat time + } + // Execute a tiny data exchange to see responsiveness + w.stateLock.RLock() + if w.device == nil { + // Terminated while waiting for the lock + w.stateLock.RUnlock() + continue + } + <-w.commsLock // Don't lock state while resolving version + _, err = w.ledgerVersion() + w.commsLock <- struct{}{} + w.stateLock.RUnlock() + + if err != nil && err != errInvalidVersionReply { + w.stateLock.Lock() // Lock state to tear the wallet down + w.failure = err + w.close() + w.stateLock.Unlock() + } + // Ignore non hardware related errors + err = nil + } + // In case of error, wait for termination + if err != nil { + glog.V(logger.Debug).Infof("%s health-check failed: %v", w.url.String(), err) + errc = <-w.healthQuit + } + errc <- err +} + +// Close implements accounts.Wallet, closing the USB connection to the Ledger. +func (w *ledgerWallet) Close() error { + // Ensure the wallet was opened + w.stateLock.RLock() + hQuit, dQuit := w.healthQuit, w.deriveQuit + w.stateLock.RUnlock() + + // Terminate the health checks + var herr error + if hQuit != nil { + errc := make(chan error) + hQuit <- errc + herr = <-errc // Save for later, we *must* close the USB + } + // Terminate the self-derivations + var derr error + if dQuit != nil { + errc := make(chan error) + dQuit <- errc + derr = <-errc // Save for later, we *must* close the USB + } + // Terminate the device connection + w.stateLock.Lock() + defer w.stateLock.Unlock() + + w.healthQuit = nil + w.deriveQuit = nil + w.deriveReq = nil + + if err := w.close(); err != nil { + return err + } + if herr != nil { + return herr + } + return derr +} + +// close is the internal wallet closer that terminates the USB connection and +// resets all the fields to their defaults. +// +// Note, close assumes the state lock is held! +func (w *ledgerWallet) close() error { + // Allow duplicate closes, especially for health-check failures + if w.device == nil { + return nil + } + // Close the device, clear everything, then return + w.device.Close() + w.device = nil + + w.browser, w.version = false, [3]byte{} + w.accounts, w.paths = nil, nil + + return nil +} + +// Accounts implements accounts.Wallet, returning the list of accounts pinned to +// the Ledger hardware wallet. If self-derivation was enabled, the account list +// is periodically expanded based on current chain state. +func (w *ledgerWallet) Accounts() []accounts.Account { + // Attempt self-derivation if it's running + reqc := make(chan struct{}, 1) + select { + case w.deriveReq <- reqc: + // Self-derivation request accepted, wait for it + <-reqc + default: + // Self-derivation offline, throttled or busy, skip + } + // Return whatever account list we ended up with + w.stateLock.RLock() + defer w.stateLock.RUnlock() + + cpy := make([]accounts.Account, len(w.accounts)) + copy(cpy, w.accounts) + return cpy +} + +// selfDerive is an account derivation loop that upon request attempts to find +// new non-zero accounts. +func (w *ledgerWallet) selfDerive() { + glog.V(logger.Debug).Infof("%s self-derivation started", w.url.String()) + defer glog.V(logger.Debug).Infof("%s self-derivation stopped", w.url.String()) + + // Execute self-derivations until termination or error + var ( + reqc chan struct{} + errc chan error + err error + ) + for errc == nil && err == nil { + // Wait until either derivation or termination is requested + select { + case errc = <-w.deriveQuit: + // Termination requested + continue + case reqc = <-w.deriveReq: + // Account discovery requested + } + // Derivation needs a chain and device access, skip if either unavailable + w.stateLock.RLock() + if w.device == nil || w.deriveChain == nil || w.offline() { + w.stateLock.RUnlock() + reqc <- struct{}{} + continue + } + select { + case <-w.commsLock: + default: + w.stateLock.RUnlock() + reqc <- struct{}{} + continue + } + // Device lock obtained, derive the next batch of accounts + var ( + accs []accounts.Account + paths []accounts.DerivationPath + + nextAddr = w.deriveNextAddr + nextPath = w.deriveNextPath + + context = context.Background() + ) + for empty := false; !empty; { + // Retrieve the next derived Ethereum account + if nextAddr == (common.Address{}) { + if nextAddr, err = w.ledgerDerive(nextPath); err != nil { + glog.V(logger.Warn).Infof("%s self-derivation failed: %v", w.url.String(), err) + break + } + } + // Check the account's status against the current chain state + var ( + balance *big.Int + nonce uint64 + ) + balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil) + if err != nil { + glog.V(logger.Warn).Infof("%s self-derivation balance retrieval failed: %v", w.url.String(), err) + break + } + nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil) + if err != nil { + glog.V(logger.Warn).Infof("%s self-derivation nonce retrieval failed: %v", w.url.String(), err) + break + } + // If the next account is empty, stop self-derivation, but add it nonetheless + if balance.BitLen() == 0 && nonce == 0 { + empty = true + } + // We've just self-derived a new account, start tracking it locally + path := make(accounts.DerivationPath, len(nextPath)) + copy(path[:], nextPath[:]) + paths = append(paths, path) + + account := accounts.Account{ + Address: nextAddr, + URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, + } + accs = append(accs, account) + + // Display a log message to the user for new (or previously empty accounts) + if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) { + glog.V(logger.Info).Infof("%s discovered %s (balance %22v, nonce %4d) at %s", w.url.String(), nextAddr.Hex(), balance, nonce, path) + } + // Fetch the next potential account + if !empty { + nextAddr = common.Address{} + nextPath[len(nextPath)-1]++ + } + } + // Self derivation complete, release device lock + w.commsLock <- struct{}{} + w.stateLock.RUnlock() + + // Insert any accounts successfully derived + w.stateLock.Lock() + for i := 0; i < len(accs); i++ { + if _, ok := w.paths[accs[i].Address]; !ok { + w.accounts = append(w.accounts, accs[i]) + w.paths[accs[i].Address] = paths[i] + } + } + // Shift the self-derivation forward + // TODO(karalabe): don't overwrite changes from wallet.SelfDerive + w.deriveNextAddr = nextAddr + w.deriveNextPath = nextPath + w.stateLock.Unlock() + + // Notify the user of termination and loop after a bit of time (to avoid trashing) + reqc <- struct{}{} + if err == nil { + select { + case errc = <-w.deriveQuit: + // Termination requested, abort + case <-time.After(ledgerSelfDeriveThrottling): + // Waited enough, willing to self-derive again + } + } + } + // In case of error, wait for termination + if err != nil { + glog.V(logger.Debug).Infof("%s self-derivation failed: %s", w.url.String(), err) + errc = <-w.deriveQuit + } + errc <- err +} + +// Contains implements accounts.Wallet, returning whether a particular account is +// or is not pinned into this Ledger instance. Although we could attempt to resolve +// unpinned accounts, that would be an non-negligible hardware operation. +func (w *ledgerWallet) Contains(account accounts.Account) bool { + w.stateLock.RLock() + defer w.stateLock.RUnlock() + + _, exists := w.paths[account.Address] + return exists +} + +// Derive implements accounts.Wallet, deriving a new account at the specific +// derivation path. If pin is set to true, the account will be added to the list +// of tracked accounts. +func (w *ledgerWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { + // Try to derive the actual account and update its URL if successful + w.stateLock.RLock() // Avoid device disappearing during derivation + + if w.device == nil || w.offline() { + w.stateLock.RUnlock() + return accounts.Account{}, accounts.ErrWalletClosed + } + <-w.commsLock // Avoid concurrent hardware access + address, err := w.ledgerDerive(path) + w.commsLock <- struct{}{} + + w.stateLock.RUnlock() + + // If an error occurred or no pinning was requested, return + if err != nil { + return accounts.Account{}, err + } + account := accounts.Account{ + Address: address, + URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, + } + if !pin { + return account, nil + } + // Pinning needs to modify the state + w.stateLock.Lock() + defer w.stateLock.Unlock() + + if _, ok := w.paths[address]; !ok { + w.accounts = append(w.accounts, account) + w.paths[address] = path + } + return account, nil +} + +// SelfDerive implements accounts.Wallet, trying to discover accounts that the +// user used previously (based on the chain state), but ones that he/she did not +// explicitly pin to the wallet manually. To avoid chain head monitoring, self +// derivation only runs during account listing (and even then throttled). +func (w *ledgerWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) { + w.stateLock.Lock() + defer w.stateLock.Unlock() + + w.deriveNextPath = make(accounts.DerivationPath, len(base)) + copy(w.deriveNextPath[:], base[:]) + + w.deriveNextAddr = common.Address{} + w.deriveChain = chain +} + +// SignHash implements accounts.Wallet, however signing arbitrary data is not +// supported for Ledger wallets, so this method will always return an error. +func (w *ledgerWallet) SignHash(acc accounts.Account, hash []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +} + +// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger +// wallet to request a confirmation from the user. It returns either the signed +// transaction or a failure if the user denied the transaction. +// +// Note, if the version of the Ethereum application running on the Ledger wallet is +// too old to sign EIP-155 transactions, but such is requested nonetheless, an error +// will be returned opposed to silently signing in Homestead mode. +func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + w.stateLock.RLock() // Comms have own mutex, this is for the state fields + defer w.stateLock.RUnlock() + + // If the wallet is closed, or the Ethereum app doesn't run, abort + if w.device == nil || w.offline() { + return nil, accounts.ErrWalletClosed + } + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + // Ensure the wallet is capable of signing the given transaction + if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 { + return nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) + } + // All infos gathered and metadata checks out, request signing + <-w.commsLock + defer func() { w.commsLock <- struct{}{} }() + + return w.ledgerSign(path, account.Address, tx, chainID) +} + +// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary +// data is not supported for Ledger wallets, so this method will always return +// an error. +func (w *ledgerWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { + return nil, accounts.ErrNotSupported +} + +// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given +// transaction with the given account using passphrase as extra authentication. +// Since the Ledger does not support extra passphrases, it is silently ignored. +func (w *ledgerWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + return w.SignTx(account, tx, chainID) +} + +// ledgerVersion retrieves the current version of the Ethereum wallet app running +// on the Ledger wallet. +// +// The version retrieval protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+----+--- +// E0 | 06 | 00 | 00 | 00 | 04 +// +// With no input data, and the output data being: +// +// Description | Length +// ---------------------------------------------------+-------- +// Flags 01: arbitrary data signature enabled by user | 1 byte +// Application major version | 1 byte +// Application minor version | 1 byte +// Application patch version | 1 byte +func (w *ledgerWallet) ledgerVersion() ([3]byte, error) { + // Send the request and wait for the response + reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil) + if err != nil { + return [3]byte{}, err + } + if len(reply) != 4 { + return [3]byte{}, errInvalidVersionReply + } + // Cache the version for future reference + var version [3]byte + copy(version[:], reply[1:]) + return version, nil +} + +// ledgerDerive retrieves the currently active Ethereum address from a Ledger +// wallet at the specified derivation path. +// +// The address derivation protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 02 | 00 return address +// 01 display address and confirm before returning +// | 00: do not return the chain code +// | 01: return the chain code +// | var | 00 +// +// Where the input data is: +// +// Description | Length +// -------------------------------------------------+-------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes +// +// And the output data is: +// +// Description | Length +// ------------------------+------------------- +// Public Key length | 1 byte +// Uncompressed Public Key | arbitrary +// Ethereum address length | 1 byte +// Ethereum address | 40 bytes hex ascii +// Chain code if requested | 32 bytes +func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, error) { + // Flatten the derivation path into the Ledger request + path := make([]byte, 1+4*len(derivationPath)) + path[0] = byte(len(derivationPath)) + for i, component := range derivationPath { + binary.BigEndian.PutUint32(path[1+4*i:], component) + } + // Send the request and wait for the response + reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path) + if err != nil { + return common.Address{}, err + } + // Discard the public key, we don't need that for now + if len(reply) < 1 || len(reply) < 1+int(reply[0]) { + return common.Address{}, errors.New("reply lacks public key entry") + } + reply = reply[1+int(reply[0]):] + + // Extract the Ethereum hex address string + if len(reply) < 1 || len(reply) < 1+int(reply[0]) { + return common.Address{}, errors.New("reply lacks address entry") + } + hexstr := reply[1 : 1+int(reply[0])] + + // Decode the hex sting into an Ethereum address and return + var address common.Address + hex.Decode(address[:], hexstr) + return address, nil +} + +// ledgerSign sends the transaction to the Ledger wallet, and waits for the user +// to confirm or deny the transaction. +// +// The transaction signing protocol is defined as follows: +// +// CLA | INS | P1 | P2 | Lc | Le +// ----+-----+----+----+-----+--- +// E0 | 04 | 00: first transaction data block +// 80: subsequent transaction data block +// | 00 | variable | variable +// +// Where the input for the first transaction block (first 255 bytes) is: +// +// Description | Length +// -------------------------------------------------+---------- +// Number of BIP 32 derivations to perform (max 10) | 1 byte +// First derivation index (big endian) | 4 bytes +// ... | 4 bytes +// Last derivation index (big endian) | 4 bytes +// RLP transaction chunk | arbitrary +// +// And the input for subsequent transaction blocks (first 255 bytes) are: +// +// Description | Length +// ----------------------+---------- +// RLP transaction chunk | arbitrary +// +// And the output data is: +// +// Description | Length +// ------------+--------- +// signature V | 1 byte +// signature R | 32 bytes +// signature S | 32 bytes +func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { + // Flatten the derivation path into the Ledger request + path := make([]byte, 1+4*len(derivationPath)) + path[0] = byte(len(derivationPath)) + for i, component := range derivationPath { + binary.BigEndian.PutUint32(path[1+4*i:], component) + } + // Create the transaction RLP based on whether legacy or EIP155 signing was requeste + var ( + txrlp []byte + err error + ) + if chainID == nil { + if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil { + return nil, err + } + } else { + if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { + return nil, err + } + } + payload := append(path, txrlp...) + + // Send the request and wait for the response + var ( + op = ledgerP1InitTransactionData + reply []byte + ) + for len(payload) > 0 { + // Calculate the size of the next data chunk + chunk := 255 + if chunk > len(payload) { + chunk = len(payload) + } + // Send the chunk over, ensuring it's processed correctly + reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk]) + if err != nil { + return nil, err + } + // Shift the payload and ensure subsequent chunks are marked as such + payload = payload[chunk:] + op = ledgerP1ContTransactionData + } + // Extract the Ethereum signature and do a sanity validation + if len(reply) != 65 { + return nil, errors.New("reply lacks signature") + } + signature := append(reply[1:], reply[0]) + + // Create the correct signer and signature transform based on the chain ID + var signer types.Signer + if chainID == nil { + signer = new(types.HomesteadSigner) + } else { + signer = types.NewEIP155Signer(chainID) + signature[64] = signature[64] - byte(chainID.Uint64()*2+35) + } + // Inject the final signature into the transaction and sanity check the sender + signed, err := tx.WithSignature(signer, signature) + if err != nil { + return nil, err + } + sender, err := types.Sender(signer, signed) + if err != nil { + return nil, err + } + if sender != address { + return nil, fmt.Errorf("signer mismatch: expected %s, got %s", address.Hex(), sender.Hex()) + } + return signed, nil +} + +// ledgerExchange performs a data exchange with the Ledger wallet, sending it a +// message and retrieving the response. +// +// The common transport header is defined as follows: +// +// Description | Length +// --------------------------------------+---------- +// Communication channel ID (big endian) | 2 bytes +// Command tag | 1 byte +// Packet sequence index (big endian) | 2 bytes +// Payload | arbitrary +// +// The Communication channel ID allows commands multiplexing over the same +// physical link. It is not used for the time being, and should be set to 0101 +// to avoid compatibility issues with implementations ignoring a leading 00 byte. +// +// The Command tag describes the message content. Use TAG_APDU (0x05) for standard +// APDU payloads, or TAG_PING (0x02) for a simple link test. +// +// The Packet sequence index describes the current sequence for fragmented payloads. +// The first fragment index is 0x00. +// +// APDU Command payloads are encoded as follows: +// +// Description | Length +// ----------------------------------- +// APDU length (big endian) | 2 bytes +// APDU CLA | 1 byte +// APDU INS | 1 byte +// APDU P1 | 1 byte +// APDU P2 | 1 byte +// APDU length | 1 byte +// Optional APDU data | arbitrary +func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) { + // Construct the message payload, possibly split into multiple chunks + apdu := make([]byte, 2, 7+len(data)) + + binary.BigEndian.PutUint16(apdu, uint16(5+len(data))) + apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...) + apdu = append(apdu, data...) + + // Stream all the chunks to the device + header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended + chunk := make([]byte, 64) + space := len(chunk) - len(header) + + for i := 0; len(apdu) > 0; i++ { + // Construct the new message to stream + chunk = append(chunk[:0], header...) + binary.BigEndian.PutUint16(chunk[3:], uint16(i)) + + if len(apdu) > space { + chunk = append(chunk, apdu[:space]...) + apdu = apdu[space:] + } else { + chunk = append(chunk, apdu...) + apdu = nil + } + // Send over to the device + if glog.V(logger.Detail) { + glog.Infof("-> %s: %x", w.device.Path, chunk) + } + if _, err := w.device.Write(chunk); err != nil { + return nil, err + } + } + // Stream the reply back from the wallet in 64 byte chunks + var reply []byte + chunk = chunk[:64] // Yeah, we surely have enough space + for { + // Read the next chunk from the Ledger wallet + if _, err := io.ReadFull(w.device, chunk); err != nil { + return nil, err + } + if glog.V(logger.Detail) { + glog.Infof("<- %s: %x", w.device.Path, chunk) + } + // Make sure the transport header matches + if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 { + return nil, errReplyInvalidHeader + } + // If it's the first chunk, retrieve the total message length + var payload []byte + + if chunk[3] == 0x00 && chunk[4] == 0x00 { + reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7]))) + payload = chunk[7:] + } else { + payload = chunk[5:] + } + // Append to the reply and stop when filled up + if left := cap(reply) - len(reply); left > len(payload) { + reply = append(reply, payload...) + } else { + reply = append(reply, payload[:left]...) + break + } + } + return reply[:len(reply)-2], nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go new file mode 100644 index 0000000..938ab1e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/usbwallet.go @@ -0,0 +1,25 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package usbwallet implements support for USB hardware wallets. +package usbwallet + +// deviceID is a combined vendor/product identifier to uniquely identify a USB +// hardware device. +type deviceID struct { + Vendor uint16 // The Vendor identifer + Product uint16 // The Product identifier +} diff --git a/vendor/github.com/ethereum/go-ethereum/appveyor.yml b/vendor/github.com/ethereum/go-ethereum/appveyor.yml new file mode 100644 index 0000000..c156967 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/appveyor.yml @@ -0,0 +1,39 @@ +os: Visual Studio 2015 + +# Clone directly into GOPATH. +clone_folder: C:\gopath\src\github.com\ethereum\go-ethereum +clone_depth: 5 +version: "{branch}.{build}" +environment: + global: + GOPATH: C:\gopath + CC: gcc.exe + matrix: + - GETH_ARCH: amd64 + MSYS2_ARCH: x86_64 + MSYS2_BITS: 64 + MSYSTEM: MINGW64 + PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH% + - GETH_ARCH: 386 + MSYS2_ARCH: i686 + MSYS2_BITS: 32 + MSYSTEM: MINGW32 + PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH% + +install: + - rmdir C:\go /s /q + - appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.windows-%GETH_ARCH%.zip + - 7z x go1.8.windows-%GETH_ARCH%.zip -y -oC:\ > NUL + - go version + - gcc --version + +build_script: + - go run build\ci.go install + +after_build: + - go run build\ci.go archive -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds + - go run build\ci.go nsis -signer WINDOWS_SIGNING_KEY -upload gethstore/builds + +test_script: + - set CGO_ENABLED=1 + - go run build\ci.go test -vet -coverage diff --git a/vendor/github.com/ethereum/go-ethereum/circle.yml b/vendor/github.com/ethereum/go-ethereum/circle.yml new file mode 100644 index 0000000..39ff5d8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/circle.yml @@ -0,0 +1,32 @@ +machine: + services: + - docker + +dependencies: + cache_directories: + - "~/.ethash" # Cache the ethash DAG generated by hive for consecutive builds + - "~/.docker" # Cache all docker images manually to avoid lengthy rebuilds + override: + # Restore all previously cached docker images + - mkdir -p ~/.docker + - for img in `ls ~/.docker`; do docker load -i ~/.docker/$img; done + + # Pull in and hive, restore cached ethash DAGs and do a dry run + - go get -u github.com/karalabe/hive + - (cd ~/.go_workspace/src/github.com/karalabe/hive && mkdir -p workspace/ethash/ ~/.ethash) + - (cd ~/.go_workspace/src/github.com/karalabe/hive && cp -r ~/.ethash/. workspace/ethash/) + - (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=NONE --test=. --sim=. --loglevel=6) + + # Cache all the docker images and the ethash DAGs + - for img in `docker images | grep -v "^" | tail -n +2 | awk '{print $1}'`; do docker save $img > ~/.docker/`echo $img | tr '/' ':'`.tar; done + - cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/ethash/. ~/.ethash + +test: + override: + # Build Geth and move into a known folder + - make geth + - cp ./build/bin/geth $HOME/geth + + # Run hive and move all generated logs into the public artifacts folder + - (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=go-ethereum:local --override=$HOME/geth --test=. --sim=.) + - cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/logs/* $CIRCLE_ARTIFACTS diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go new file mode 100644 index 0000000..3a1ae6f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/abigen/main.go @@ -0,0 +1,140 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/compiler" +) + +var ( + abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") + binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") + typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") + + solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") + solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") + excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") + + pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") + outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") + langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)") +) + +func main() { + // Parse and ensure all needed inputs are specified + flag.Parse() + + if *abiFlag == "" && *solFlag == "" { + fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n") + os.Exit(-1) + } else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" { + fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n") + os.Exit(-1) + } + if *pkgFlag == "" { + fmt.Printf("No destination package specified (--pkg)\n") + os.Exit(-1) + } + var lang bind.Lang + switch *langFlag { + case "go": + lang = bind.LangGo + case "java": + lang = bind.LangJava + case "objc": + lang = bind.LangObjC + default: + fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) + os.Exit(-1) + } + // If the entire solidity code was specified, build and bind based on that + var ( + abis []string + bins []string + types []string + ) + if *solFlag != "" { + // Generate the list of types to exclude from binding + exclude := make(map[string]bool) + for _, kind := range strings.Split(*excFlag, ",") { + exclude[strings.ToLower(kind)] = true + } + contracts, err := compiler.CompileSolidity(*solcFlag, *solFlag) + if err != nil { + fmt.Printf("Failed to build Solidity contract: %v\n", err) + os.Exit(-1) + } + // Gather all non-excluded contract for binding + for name, contract := range contracts { + if exclude[strings.ToLower(name)] { + continue + } + abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + abis = append(abis, string(abi)) + bins = append(bins, contract.Code) + + nameParts := strings.Split(name, ":") + types = append(types, nameParts[len(nameParts)-1]) + } + } else { + // Otherwise load up the ABI, optional bytecode and type name from the parameters + abi, err := ioutil.ReadFile(*abiFlag) + if err != nil { + fmt.Printf("Failed to read input ABI: %v\n", err) + os.Exit(-1) + } + abis = append(abis, string(abi)) + + bin := []byte{} + if *binFlag != "" { + if bin, err = ioutil.ReadFile(*binFlag); err != nil { + fmt.Printf("Failed to read input bytecode: %v\n", err) + os.Exit(-1) + } + } + bins = append(bins, string(bin)) + + kind := *typFlag + if kind == "" { + kind = *pkgFlag + } + types = append(types, kind) + } + // Generate the contract binding + code, err := bind.Bind(types, abis, bins, *pkgFlag, lang) + if err != nil { + fmt.Printf("Failed to generate ABI binding: %v\n", err) + os.Exit(-1) + } + // Either flush it out to a file or display on the standard output + if *outFlag == "" { + fmt.Printf("%s\n", code) + return + } + if err := ioutil.WriteFile(*outFlag, []byte(code), 0600); err != nil { + fmt.Printf("Failed to write ABI binding: %v\n", err) + os.Exit(-1) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go new file mode 100644 index 0000000..9b5ba19 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/bootnode/main.go @@ -0,0 +1,105 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// bootnode runs a bootstrap node for the Ethereum Discovery Protocol. +package main + +import ( + "crypto/ecdsa" + "flag" + "fmt" + "os" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +func main() { + var ( + listenAddr = flag.String("addr", ":30301", "listen address") + genKey = flag.String("genkey", "", "generate a node key") + writeAddr = flag.Bool("writeaddress", false, "write out the node's pubkey hash and quit") + nodeKeyFile = flag.String("nodekey", "", "private key filename") + nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") + natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:)") + netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") + runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") + + nodeKey *ecdsa.PrivateKey + err error + ) + flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)") + flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern") + glog.SetToStderr(true) + flag.Parse() + + natm, err := nat.Parse(*natdesc) + if err != nil { + utils.Fatalf("-nat: %v", err) + } + switch { + case *genKey != "": + nodeKey, err = crypto.GenerateKey() + if err != nil { + utils.Fatalf("could not generate key: %v", err) + } + if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil { + utils.Fatalf("%v", err) + } + case *nodeKeyFile == "" && *nodeKeyHex == "": + utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key") + case *nodeKeyFile != "" && *nodeKeyHex != "": + utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive") + case *nodeKeyFile != "": + if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { + utils.Fatalf("-nodekey: %v", err) + } + case *nodeKeyHex != "": + if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { + utils.Fatalf("-nodekeyhex: %v", err) + } + } + + if *writeAddr { + fmt.Printf("%v\n", discover.PubkeyID(&nodeKey.PublicKey)) + os.Exit(0) + } + + var restrictList *netutil.Netlist + if *netrestrict != "" { + restrictList, err = netutil.ParseNetlist(*netrestrict) + if err != nil { + utils.Fatalf("-netrestrict: %v", err) + } + } + + if *runv5 { + if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { + utils.Fatalf("%v", err) + } + } else { + if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { + utils.Fatalf("%v", err) + } + } + + select {} +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/disasm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/disasm/main.go new file mode 100644 index 0000000..e6a9a66 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/disasm/main.go @@ -0,0 +1,60 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// disasm is a pretty-printer for EVM bytecode. +package main + +import ( + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/ethereum/go-ethereum/core/vm" +) + +func main() { + code, err := ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + code, err = hex.DecodeString(strings.TrimSpace(string(code[:]))) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + fmt.Printf("%x\n", code) + + for pc := uint64(0); pc < uint64(len(code)); pc++ { + op := vm.OpCode(code[pc]) + + switch op { + case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, vm.PUSH30, vm.PUSH31, vm.PUSH32: + a := uint64(op) - uint64(vm.PUSH1) + 1 + u := pc + 1 + a + if uint64(len(code)) <= pc || uint64(len(code)) < u { + fmt.Printf("Error: incomplete push instruction at %v\n", pc) + return + } + fmt.Printf("%-5d %v => %x\n", pc, op, code[pc+1:u]) + pc += a + default: + fmt.Printf("%-5d %v\n", pc, op) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/ethtest/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/ethtest/main.go new file mode 100644 index 0000000..14b8395 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/ethtest/main.go @@ -0,0 +1,222 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// ethtest executes Ethereum JSON tests. +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/tests" + "gopkg.in/urfave/cli.v1" +) + +var ( + continueOnError = false + testExtension = ".json" + defaultTest = "all" + defaultDir = "." + allTests = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"} + testDirMapping = map[string]string{"BlockTests": "BlockchainTests"} + skipTests = []string{} + + TestFlag = cli.StringFlag{ + Name: "test", + Usage: "Test type (string): VMTests, TransactionTests, StateTests, BlockTests", + Value: defaultTest, + } + FileFlag = cli.StringFlag{ + Name: "file", + Usage: "Test file or directory. Directories are searched for .json files 1 level deep", + Value: defaultDir, + EnvVar: "ETHEREUM_TEST_PATH", + } + ContinueOnErrorFlag = cli.BoolFlag{ + Name: "continue", + Usage: "Continue running tests on error (true) or [default] exit immediately (false)", + } + ReadStdInFlag = cli.BoolFlag{ + Name: "stdin", + Usage: "Accept input from stdin instead of reading from file", + } + SkipTestsFlag = cli.StringFlag{ + Name: "skip", + Usage: "Tests names to skip", + } + TraceFlag = cli.BoolFlag{ + Name: "trace", + Usage: "Enable VM tracing", + } +) + +func runTestWithReader(test string, r io.Reader) error { + glog.Infoln("runTest", test) + var err error + switch strings.ToLower(test) { + case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": + err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests) + case "st", "state", "statetest", "statetests": + rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock} + err = tests.RunStateTestWithReader(rs, r, skipTests) + case "tx", "transactiontest", "transactiontests": + rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock} + err = tests.RunTransactionTestsWithReader(rs, r, skipTests) + case "vm", "vmtest", "vmtests": + err = tests.RunVmTestWithReader(r, skipTests) + case "rlp", "rlptest", "rlptests": + err = tests.RunRLPTestWithReader(r, skipTests) + default: + err = fmt.Errorf("Invalid test type specified: %v", test) + } + return err +} + +func getFiles(path string) ([]string, error) { + glog.Infoln("getFiles", path) + var files []string + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return nil, err + } + + switch mode := fi.Mode(); { + case mode.IsDir(): + fi, _ := ioutil.ReadDir(path) + files = make([]string, len(fi)) + for i, v := range fi { + // only go 1 depth and leave directory entires blank + if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension { + files[i] = filepath.Join(path, v.Name()) + glog.Infoln("Found file", files[i]) + } + } + case mode.IsRegular(): + files = make([]string, 1) + files[0] = path + } + + return files, nil +} + +func runSuite(test, file string) { + var tests []string + + if test == defaultTest { + tests = allTests + } else { + tests = []string{test} + } + + for _, curTest := range tests { + glog.Infoln("runSuite", curTest, file) + var err error + var files []string + if test == defaultTest { + // check if we have an explicit directory mapping for the test + if _, ok := testDirMapping[curTest]; ok { + files, err = getFiles(filepath.Join(file, testDirMapping[curTest])) + } else { + // otherwise assume test name + files, err = getFiles(filepath.Join(file, curTest)) + } + } else { + files, err = getFiles(file) + } + if err != nil { + glog.Fatalln(err) + } + + if len(files) == 0 { + glog.Warningln("No files matched path") + } + for _, curFile := range files { + // Skip blank entries + if len(curFile) == 0 { + continue + } + + r, err := os.Open(curFile) + if err != nil { + glog.Fatalln(err) + } + defer r.Close() + + err = runTestWithReader(curTest, r) + if err != nil { + if continueOnError { + glog.Errorln(err) + } else { + glog.Fatalln(err) + } + } + } + } +} + +func setupApp(c *cli.Context) error { + flagTest := c.GlobalString(TestFlag.Name) + flagFile := c.GlobalString(FileFlag.Name) + continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name) + useStdIn := c.GlobalBool(ReadStdInFlag.Name) + skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ") + + if !useStdIn { + runSuite(flagTest, flagFile) + } else { + if err := runTestWithReader(flagTest, os.Stdin); err != nil { + glog.Fatalln(err) + } + } + return nil +} + +func main() { + glog.SetToStderr(true) + + app := cli.NewApp() + app.Name = "ethtest" + app.Usage = "go-ethereum test interface" + app.Action = setupApp + app.Version = "0.2.0" + app.Author = "go-ethereum team" + + app.Flags = []cli.Flag{ + TestFlag, + FileFlag, + ContinueOnErrorFlag, + ReadStdInFlag, + SkipTestsFlag, + TraceFlag, + } + + if err := app.Run(os.Args); err != nil { + glog.Fatalln(err) + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go new file mode 100644 index 0000000..0693d7c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/evm/main.go @@ -0,0 +1,223 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// evm executes EVM code snippets. +package main + +import ( + "fmt" + "io/ioutil" + "os" + goruntime "runtime" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger/glog" + "gopkg.in/urfave/cli.v1" +) + +var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) + +var ( + app = utils.NewApp(gitCommit, "the evm command line interface") + + DebugFlag = cli.BoolFlag{ + Name: "debug", + Usage: "output full trace logs", + } + CodeFlag = cli.StringFlag{ + Name: "code", + Usage: "EVM code", + } + CodeFileFlag = cli.StringFlag{ + Name: "codefile", + Usage: "file containing EVM code", + } + GasFlag = cli.StringFlag{ + Name: "gas", + Usage: "gas limit for the evm", + Value: "10000000000", + } + PriceFlag = cli.StringFlag{ + Name: "price", + Usage: "price set for the evm", + Value: "0", + } + ValueFlag = cli.StringFlag{ + Name: "value", + Usage: "value set for the evm", + Value: "0", + } + DumpFlag = cli.BoolFlag{ + Name: "dump", + Usage: "dumps the state after the run", + } + InputFlag = cli.StringFlag{ + Name: "input", + Usage: "input for the EVM", + } + SysStatFlag = cli.BoolFlag{ + Name: "sysstat", + Usage: "display system stats", + } + VerbosityFlag = cli.IntFlag{ + Name: "verbosity", + Usage: "sets the verbosity level", + } + CreateFlag = cli.BoolFlag{ + Name: "create", + Usage: "indicates the action should be create rather than call", + } + DisableGasMeteringFlag = cli.BoolFlag{ + Name: "nogasmetering", + Usage: "disable gas metering", + } +) + +func init() { + app.Flags = []cli.Flag{ + CreateFlag, + DebugFlag, + VerbosityFlag, + SysStatFlag, + CodeFlag, + CodeFileFlag, + GasFlag, + PriceFlag, + ValueFlag, + DumpFlag, + InputFlag, + DisableGasMeteringFlag, + } + app.Action = run +} + +func run(ctx *cli.Context) error { + glog.SetToStderr(true) + glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) + + var ( + db, _ = ethdb.NewMemDatabase() + statedb, _ = state.New(common.Hash{}, db) + address = common.StringToAddress("sender") + sender = vm.AccountRef(address) + ) + statedb.CreateAccount(common.StringToAddress("sender")) + + logger := vm.NewStructLogger(nil) + + tstart := time.Now() + + var ( + code []byte + ret []byte + err error + ) + + if ctx.GlobalString(CodeFlag.Name) != "" { + code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + } else { + var hexcode []byte + if ctx.GlobalString(CodeFileFlag.Name) != "" { + var err error + hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name)) + if err != nil { + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) + } + } else { + var err error + hexcode, err = ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) + } + } + code = common.Hex2Bytes(string(hexcode[:])) + } + + if ctx.GlobalBool(CreateFlag.Name) { + input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...) + ret, _, err = runtime.Create(input, &runtime.Config{ + Origin: sender.Address(), + State: statedb, + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(), + GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), + Value: common.Big(ctx.GlobalString(ValueFlag.Name)), + EVMConfig: vm.Config{ + Tracer: logger, + Debug: ctx.GlobalBool(DebugFlag.Name), + DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name), + }, + }) + } else { + receiverAddress := common.StringToAddress("receiver") + statedb.CreateAccount(receiverAddress) + statedb.SetCode(receiverAddress, code) + + ret, err = runtime.Call(receiverAddress, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{ + Origin: sender.Address(), + State: statedb, + GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(), + GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)), + Value: common.Big(ctx.GlobalString(ValueFlag.Name)), + EVMConfig: vm.Config{ + Tracer: logger, + Debug: ctx.GlobalBool(DebugFlag.Name), + DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name), + }, + }) + } + vmdone := time.Since(tstart) + + if ctx.GlobalBool(DumpFlag.Name) { + statedb.Commit(true) + fmt.Println(string(statedb.Dump())) + } + vm.StdErrFormat(logger.StructLogs()) + + if ctx.GlobalBool(SysStatFlag.Name) { + var mem goruntime.MemStats + goruntime.ReadMemStats(&mem) + fmt.Printf("vm took %v\n", vmdone) + fmt.Printf(`alloc: %d +tot alloc: %d +no. malloc: %d +heap alloc: %d +heap objs: %d +num gc: %d +`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC) + } + + fmt.Printf("OUT: 0x%x", ret) + if err != nil { + fmt.Printf(" error: %v", err) + } + fmt.Println() + return nil +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go new file mode 100644 index 0000000..cd398ea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd.go @@ -0,0 +1,351 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "fmt" + "io/ioutil" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "gopkg.in/urfave/cli.v1" +) + +var ( + walletCommand = cli.Command{ + Name: "wallet", + Usage: "Manage Ethereum presale wallets", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", + Description: ` + geth wallet import /path/to/my/presale.wallet + +will prompt for your password and imports your ether presale account. +It can be used non-interactively with the --password option taking a +passwordfile as argument containing the wallet password in plaintext. + +`, + Subcommands: []cli.Command{ + { + Action: importWallet, + Name: "import", + Usage: "Import Ethereum presale wallet", + ArgsUsage: "", + Description: ` +TODO: Please write this +`, + }, + }, + } + accountCommand = cli.Command{ + Action: accountList, + Name: "account", + Usage: "Manage accounts", + ArgsUsage: "", + Category: "ACCOUNT COMMANDS", + Description: ` +Manage accounts lets you create new accounts, list all existing accounts, +import a private key into a new account. + +' help' shows a list of subcommands or help for one subcommand. + +It supports interactive mode, when you are prompted for password as well as +non-interactive mode where passwords are supplied via a given password file. +Non-interactive mode is only meant for scripted use on test networks or known +safe environments. + +Make sure you remember the password you gave when creating a new account (with +either new or import). Without it you are not able to unlock your account. + +Note that exporting your key in unencrypted format is NOT supported. + +Keys are stored under /keystore. +It is safe to transfer the entire directory or the individual keys therein +between ethereum nodes by simply copying. +Make sure you backup your keys regularly. + +In order to use your account to send transactions, you need to unlock them using +the '--unlock' option. The argument is a space separated list of addresses or +indexes. If used non-interactively with a passwordfile, the file should contain +the respective passwords one per line. If you unlock n accounts and the password +file contains less than n entries, then the last password is meant to apply to +all remaining accounts. + +And finally. DO NOT FORGET YOUR PASSWORD. +`, + Subcommands: []cli.Command{ + { + Action: accountList, + Name: "list", + Usage: "Print account addresses", + ArgsUsage: " ", + Description: ` +TODO: Please write this +`, + }, + { + Action: accountCreate, + Name: "new", + Usage: "Create a new account", + ArgsUsage: " ", + Description: ` + geth account new + +Creates a new account. Prints the address. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + +For non-interactive use the passphrase can be specified with the --password flag: + + geth --password account new + +Note, this is meant to be used for testing only, it is a bad idea to save your +password to file or expose in any other way. +`, + }, + { + Action: accountUpdate, + Name: "update", + Usage: "Update an existing account", + ArgsUsage: "
", + Description: ` + geth account update
+ +Update an existing account. + +The account is saved in the newest version in encrypted format, you are prompted +for a passphrase to unlock the account and another to save the updated file. + +This same command can therefore be used to migrate an account of a deprecated +format to the newest format or change the password for an account. + +For non-interactive use the passphrase can be specified with the --password flag: + + geth --password account update
+ +Since only one password can be given, only format update can be performed, +changing your password is only possible interactively. +`, + }, + { + Action: accountImport, + Name: "import", + Usage: "Import a private key into a new account", + ArgsUsage: "", + Description: ` + geth account import + +Imports an unencrypted private key from and creates a new account. +Prints the address. + +The keyfile is assumed to contain an unencrypted private key in hexadecimal format. + +The account is saved in encrypted format, you are prompted for a passphrase. + +You must remember this passphrase to unlock your account in the future. + +For non-interactive use the passphrase can be specified with the -password flag: + + geth --password account import + +Note: +As you can directly copy your encrypted accounts to another ethereum instance, +this import mechanism is not needed when you transfer an account between +nodes. +`, + }, + }, + } +) + +func accountList(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + + var index int + for _, wallet := range stack.AccountManager().Wallets() { + for _, account := range wallet.Accounts() { + fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL) + index++ + } + } + return nil +} + +// tries unlocking the specified account a few times. +func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { + account, err := utils.MakeAddress(ks, address) + if err != nil { + utils.Fatalf("Could not list accounts: %v", err) + } + for trials := 0; trials < 3; trials++ { + prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) + password := getPassPhrase(prompt, false, i, passwords) + err = ks.Unlock(account, password) + if err == nil { + glog.V(logger.Info).Infof("Unlocked account %x", account.Address) + return account, password + } + if err, ok := err.(*keystore.AmbiguousAddrError); ok { + glog.V(logger.Info).Infof("Unlocked account %x", account.Address) + return ambiguousAddrRecovery(ks, err, password), password + } + if err != keystore.ErrDecrypt { + // No need to prompt again if the error is not decryption-related. + break + } + } + // All trials expended to unlock account, bail out + utils.Fatalf("Failed to unlock account %s (%v)", address, err) + return accounts.Account{}, "" +} + +// getPassPhrase retrieves the passwor associated with an account, either fetched +// from a list of preloaded passphrases, or requested interactively from the user. +func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { + // If a list of passwords was supplied, retrieve from them + if len(passwords) > 0 { + if i < len(passwords) { + return passwords[i] + } + return passwords[len(passwords)-1] + } + // Otherwise prompt the user for the password + if prompt != "" { + fmt.Println(prompt) + } + password, err := console.Stdin.PromptPassword("Passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + if confirmation { + confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase confirmation: %v", err) + } + if password != confirm { + utils.Fatalf("Passphrases do not match") + } + } + return password +} + +func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { + fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) + for _, a := range err.Matches { + fmt.Println(" ", a.URL) + } + fmt.Println("Testing your passphrase against all of them...") + var match *accounts.Account + for _, a := range err.Matches { + if err := ks.Unlock(a, auth); err == nil { + match = &a + break + } + } + if match == nil { + utils.Fatalf("None of the listed files could be unlocked.") + } + fmt.Printf("Your passphrase unlocked %s\n", match.URL) + fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") + for _, a := range err.Matches { + if a != *match { + fmt.Println(" ", a.URL) + } + } + return *match +} + +// accountCreate creates a new account into the keystore defined by the CLI flags. +func accountCreate(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + account, err := ks.NewAccount(password) + if err != nil { + utils.Fatalf("Failed to create account: %v", err) + } + fmt.Printf("Address: {%x}\n", account.Address) + return nil +} + +// accountUpdate transitions an account from a previous format to the current +// one, also providing the possibility to change the pass-phrase. +func accountUpdate(ctx *cli.Context) error { + if len(ctx.Args()) == 0 { + utils.Fatalf("No accounts specified to update") + } + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil) + newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) + if err := ks.Update(account, oldPassword, newPassword); err != nil { + utils.Fatalf("Could not update the account: %v", err) + } + return nil +} + +func importWallet(ctx *cli.Context) error { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + keyJson, err := ioutil.ReadFile(keyfile) + if err != nil { + utils.Fatalf("Could not read wallet file: %v", err) + } + + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) + + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + acct, err := ks.ImportPreSaleKey(keyJson, passphrase) + if err != nil { + utils.Fatalf("%v", err) + } + fmt.Printf("Address: {%x}\n", acct.Address) + return nil +} + +func accountImport(ctx *cli.Context) error { + keyfile := ctx.Args().First() + if len(keyfile) == 0 { + utils.Fatalf("keyfile must be given as argument") + } + key, err := crypto.LoadECDSA(keyfile) + if err != nil { + utils.Fatalf("Failed to load the private key: %v", err) + } + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + acct, err := ks.ImportECDSA(key, passphrase) + if err != nil { + utils.Fatalf("Could not create the account: %v", err) + } + fmt.Printf("Address: {%x}\n", acct.Address) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd_test.go new file mode 100644 index 0000000..679a7ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/accountcmd_test.go @@ -0,0 +1,292 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "io/ioutil" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/cespare/cp" +) + +// These tests are 'smoke tests' for the account related +// subcommands and flags. +// +// For most tests, the test files from package accounts +// are copied into a temporary keystore directory. + +func tmpDatadirWithKeystore(t *testing.T) string { + datadir := tmpdir(t) + keystore := filepath.Join(datadir, "keystore") + source := filepath.Join("..", "..", "accounts", "keystore", "testdata", "keystore") + if err := cp.CopyAll(keystore, source); err != nil { + t.Fatal(err) + } + return datadir +} + +func TestAccountListEmpty(t *testing.T) { + geth := runGeth(t, "account") + geth.expectExit() +} + +func TestAccountList(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, "--datadir", datadir, "account") + defer geth.expectExit() + if runtime.GOOS == "windows" { + geth.expect(` +Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 +Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa +Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz +`) + } else { + geth.expect(` +Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 +Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa +Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz +`) + } +} + +func TestAccountNew(t *testing.T) { + geth := runGeth(t, "--lightkdf", "account", "new") + defer geth.expectExit() + geth.expect(` +Your new account is locked with a password. Please give a password. Do not forget this password. +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foobar"}} +Repeat passphrase: {{.InputLine "foobar"}} +`) + geth.expectRegexp(`Address: \{[0-9a-f]{40}\}\n`) +} + +func TestAccountNewBadRepeat(t *testing.T) { + geth := runGeth(t, "--lightkdf", "account", "new") + defer geth.expectExit() + geth.expect(` +Your new account is locked with a password. Please give a password. Do not forget this password. +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "something"}} +Repeat passphrase: {{.InputLine "something else"}} +Fatal: Passphrases do not match +`) +} + +func TestAccountUpdate(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--lightkdf", + "account", "update", "f466859ead1932d743d622cb74fc058882e8648a") + defer geth.expectExit() + geth.expect(` +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foobar"}} +Please give a new password. Do not forget this password. +Passphrase: {{.InputLine "foobar2"}} +Repeat passphrase: {{.InputLine "foobar2"}} +`) +} + +func TestWalletImport(t *testing.T) { + geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json") + defer geth.expectExit() + geth.expect(` +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foo"}} +Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f} +`) + + files, err := ioutil.ReadDir(filepath.Join(geth.Datadir, "keystore")) + if len(files) != 1 { + t.Errorf("expected one key file in keystore directory, found %d files (error: %v)", len(files), err) + } +} + +func TestWalletImportBadPassword(t *testing.T) { + geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json") + defer geth.expectExit() + geth.expect(` +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "wrong"}} +Fatal: could not decrypt key with given passphrase +`) +} + +func TestUnlockFlag(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--nat", "none", "--nodiscover", "--dev", + "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", + "js", "testdata/empty.js") + geth.expect(` +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foobar"}} +`) + geth.expectExit() + + wantMessages := []string{ + "Unlocked account f466859ead1932d743d622cb74fc058882e8648a", + } + for _, m := range wantMessages { + if !strings.Contains(geth.stderrText(), m) { + t.Errorf("stderr text does not contain %q", m) + } + } +} + +func TestUnlockFlagWrongPassword(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--nat", "none", "--nodiscover", "--dev", + "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") + defer geth.expectExit() + geth.expect(` +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "wrong1"}} +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3 +Passphrase: {{.InputLine "wrong2"}} +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3 +Passphrase: {{.InputLine "wrong3"}} +Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given passphrase) +`) +} + +// https://github.com/ethereum/go-ethereum/issues/1785 +func TestUnlockFlagMultiIndex(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--nat", "none", "--nodiscover", "--dev", + "--unlock", "0,2", + "js", "testdata/empty.js") + geth.expect(` +Unlocking account 0 | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foobar"}} +Unlocking account 2 | Attempt 1/3 +Passphrase: {{.InputLine "foobar"}} +`) + geth.expectExit() + + wantMessages := []string{ + "Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8", + "Unlocked account 289d485d9771714cce91d3393d764e1311907acc", + } + for _, m := range wantMessages { + if !strings.Contains(geth.stderrText(), m) { + t.Errorf("stderr text does not contain %q", m) + } + } +} + +func TestUnlockFlagPasswordFile(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--nat", "none", "--nodiscover", "--dev", + "--password", "testdata/passwords.txt", "--unlock", "0,2", + "js", "testdata/empty.js") + geth.expectExit() + + wantMessages := []string{ + "Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8", + "Unlocked account 289d485d9771714cce91d3393d764e1311907acc", + } + for _, m := range wantMessages { + if !strings.Contains(geth.stderrText(), m) { + t.Errorf("stderr text does not contain %q", m) + } + } +} + +func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) { + datadir := tmpDatadirWithKeystore(t) + geth := runGeth(t, + "--datadir", datadir, "--nat", "none", "--nodiscover", "--dev", + "--password", "testdata/wrong-passwords.txt", "--unlock", "0,2") + defer geth.expectExit() + geth.expect(` +Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase) +`) +} + +func TestUnlockFlagAmbiguous(t *testing.T) { + store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") + geth := runGeth(t, + "--keystore", store, "--nat", "none", "--nodiscover", "--dev", + "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", + "js", "testdata/empty.js") + defer geth.expectExit() + + // Helper for the expect template, returns absolute keystore path. + geth.setTemplateFunc("keypath", func(file string) string { + abs, _ := filepath.Abs(filepath.Join(store, file)) + return abs + }) + geth.expect(` +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "foobar"}} +Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: + keystore://{{keypath "1"}} + keystore://{{keypath "2"}} +Testing your passphrase against all of them... +Your passphrase unlocked keystore://{{keypath "1"}} +In order to avoid this warning, you need to remove the following duplicate key files: + keystore://{{keypath "2"}} +`) + geth.expectExit() + + wantMessages := []string{ + "Unlocked account f466859ead1932d743d622cb74fc058882e8648a", + } + for _, m := range wantMessages { + if !strings.Contains(geth.stderrText(), m) { + t.Errorf("stderr text does not contain %q", m) + } + } +} + +func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) { + store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") + geth := runGeth(t, + "--keystore", store, "--nat", "none", "--nodiscover", "--dev", + "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") + defer geth.expectExit() + + // Helper for the expect template, returns absolute keystore path. + geth.setTemplateFunc("keypath", func(file string) string { + abs, _ := filepath.Abs(filepath.Join(store, file)) + return abs + }) + geth.expect(` +Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +!! Unsupported terminal, password will be echoed. +Passphrase: {{.InputLine "wrong"}} +Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: + keystore://{{keypath "1"}} + keystore://{{keypath "2"}} +Testing your passphrase against all of them... +Fatal: None of the listed files could be unlocked. +`) + geth.expectExit() +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/bugcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/bugcmd.go new file mode 100644 index 0000000..f218805 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/bugcmd.go @@ -0,0 +1,108 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/url" + "os/exec" + "runtime" + "strings" + + "github.com/ethereum/go-ethereum/cmd/internal/browser" + "github.com/ethereum/go-ethereum/params" + + cli "gopkg.in/urfave/cli.v1" +) + +var bugCommand = cli.Command{ + Action: reportBug, + Name: "bug", + Usage: "opens a window to report a bug on the geth repo", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", +} + +const issueUrl = "https://github.com/ethereum/go-ethereum/issues/new" + +// reportBug reports a bug by opening a new URL to the go-ethereum GH issue +// tracker and setting default values as the issue body. +func reportBug(ctx *cli.Context) error { + // execute template and write contents to buff + var buff bytes.Buffer + + fmt.Fprintln(&buff, header) + fmt.Fprintln(&buff, "Version:", params.Version) + fmt.Fprintln(&buff, "Go Version:", runtime.Version()) + fmt.Fprintln(&buff, "OS:", runtime.GOOS) + printOSDetails(&buff) + + // open a new GH issue + if !browser.Open(issueUrl + "?body=" + url.QueryEscape(buff.String())) { + fmt.Printf("Please file a new issue at %s using this template:\n%s", issueUrl, buff.String()) + } + return nil +} + +// copied from the Go source. Copyright 2017 The Go Authors +func printOSDetails(w io.Writer) { + switch runtime.GOOS { + case "darwin": + printCmdOut(w, "uname -v: ", "uname", "-v") + printCmdOut(w, "", "sw_vers") + case "linux": + printCmdOut(w, "uname -sr: ", "uname", "-sr") + printCmdOut(w, "", "lsb_release", "-a") + case "openbsd", "netbsd", "freebsd", "dragonfly": + printCmdOut(w, "uname -v: ", "uname", "-v") + case "solaris": + out, err := ioutil.ReadFile("/etc/release") + if err == nil { + fmt.Fprintf(w, "/etc/release: %s\n", out) + } else { + fmt.Printf("failed to read /etc/release: %v\n", err) + } + } +} + +// printCmdOut prints the output of running the given command. +// It ignores failures; 'go bug' is best effort. +// +// copied from the Go source. Copyright 2017 The Go Authors +func printCmdOut(w io.Writer, prefix, path string, args ...string) { + cmd := exec.Command(path, args...) + out, err := cmd.Output() + if err != nil { + fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err) + return + } + fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out)) +} + +const header = `Please answer these questions before submitting your issue. Thanks! + +#### What did you do? + +#### What did you expect to see? + +#### What did you see instead? + +#### System details +` diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go new file mode 100644 index 0000000..f38ee04 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/chaincmd.go @@ -0,0 +1,338 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/trie" + "github.com/syndtr/goleveldb/leveldb/util" + "gopkg.in/urfave/cli.v1" +) + +var ( + initCommand = cli.Command{ + Action: initGenesis, + Name: "init", + Usage: "Bootstrap and initialize a new genesis block", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +The init command initializes a new genesis block and definition for the network. +This is a destructive action and changes the network in which you will be +participating. +`, + } + importCommand = cli.Command{ + Action: importChain, + Name: "import", + Usage: "Import a blockchain file", + ArgsUsage: "", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, + } + exportCommand = cli.Command{ + Action: exportChain, + Name: "export", + Usage: "Export blockchain into file", + ArgsUsage: " [ ]", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +Requires a first argument of the file to write to. +Optional second and third arguments control the first and +last block to write. In this mode, the file will be appended +if already existing. +`, + } + upgradedbCommand = cli.Command{ + Action: upgradeDB, + Name: "upgradedb", + Usage: "Upgrade chainblock database", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, + } + removedbCommand = cli.Command{ + Action: removeDB, + Name: "removedb", + Usage: "Remove blockchain and state databases", + ArgsUsage: " ", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +TODO: Please write this +`, + } + dumpCommand = cli.Command{ + Action: dump, + Name: "dump", + Usage: "Dump a specific block from storage", + ArgsUsage: "[ | ]...", + Category: "BLOCKCHAIN COMMANDS", + Description: ` +The arguments are interpreted as block numbers or hashes. +Use "ethereum dump 0" to dump the genesis block. +`, + } +) + +// initGenesis will initialise the given JSON format genesis file and writes it as +// the zero'd block (i.e. genesis) or will fail hard if it can't succeed. +func initGenesis(ctx *cli.Context) error { + genesisPath := ctx.Args().First() + if len(genesisPath) == 0 { + utils.Fatalf("must supply path to genesis JSON file") + } + + stack := makeFullNode(ctx) + chaindb := utils.MakeChainDatabase(ctx, stack) + + genesisFile, err := os.Open(genesisPath) + if err != nil { + utils.Fatalf("failed to read genesis file: %v", err) + } + defer genesisFile.Close() + + block, err := core.WriteGenesisBlock(chaindb, genesisFile) + if err != nil { + utils.Fatalf("failed to write genesis block: %v", err) + } + glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash()) + return nil +} + +func importChain(ctx *cli.Context) error { + if len(ctx.Args()) != 1 { + utils.Fatalf("This command requires an argument.") + } + stack := makeFullNode(ctx) + chain, chainDb := utils.MakeChain(ctx, stack) + defer chainDb.Close() + + // Start periodically gathering memory profiles + var peakMemAlloc, peakMemSys uint64 + go func() { + stats := new(runtime.MemStats) + for { + runtime.ReadMemStats(stats) + if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { + atomic.StoreUint64(&peakMemAlloc, stats.Alloc) + } + if atomic.LoadUint64(&peakMemSys) < stats.Sys { + atomic.StoreUint64(&peakMemSys, stats.Sys) + } + time.Sleep(5 * time.Second) + } + }() + // Import the chain + start := time.Now() + if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { + utils.Fatalf("Import error: %v", err) + } + fmt.Printf("Import done in %v.\n\n", time.Since(start)) + + // Output pre-compaction stats mostly to see the import trashing + db := chainDb.(*ethdb.LDBDatabase) + + stats, err := db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) + fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) + + // Print the memory statistics used by the importing + mem := new(runtime.MemStats) + runtime.ReadMemStats(mem) + + fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) + fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) + fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) + fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) + + // Compact the entire database to more accurately measure disk io and print the stats + start = time.Now() + fmt.Println("Compacting entire database...") + if err = db.LDB().CompactRange(util.Range{}); err != nil { + utils.Fatalf("Compaction failed: %v", err) + } + fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + + stats, err = db.LDB().GetProperty("leveldb.stats") + if err != nil { + utils.Fatalf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + + return nil +} + +func exportChain(ctx *cli.Context) error { + if len(ctx.Args()) < 1 { + utils.Fatalf("This command requires an argument.") + } + stack := makeFullNode(ctx) + chain, _ := utils.MakeChain(ctx, stack) + start := time.Now() + + var err error + fp := ctx.Args().First() + if len(ctx.Args()) < 3 { + err = utils.ExportChain(chain, fp) + } else { + // This can be improved to allow for numbers larger than 9223372036854775807 + first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) + last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) + if ferr != nil || lerr != nil { + utils.Fatalf("Export error in parsing parameters: block number not an integer\n") + } + if first < 0 || last < 0 { + utils.Fatalf("Export error: block number must be greater than 0\n") + } + err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) + } + + if err != nil { + utils.Fatalf("Export error: %v\n", err) + } + fmt.Printf("Export done in %v", time.Since(start)) + return nil +} + +func removeDB(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + dbdir := stack.ResolvePath(utils.ChainDbName(ctx)) + if !common.FileExist(dbdir) { + fmt.Println(dbdir, "does not exist") + return nil + } + + fmt.Println(dbdir) + confirm, err := console.Stdin.PromptConfirm("Remove this database?") + switch { + case err != nil: + utils.Fatalf("%v", err) + case !confirm: + fmt.Println("Operation aborted") + default: + fmt.Println("Removing...") + start := time.Now() + os.RemoveAll(dbdir) + fmt.Printf("Removed in %v\n", time.Since(start)) + } + return nil +} + +func upgradeDB(ctx *cli.Context) error { + glog.Infoln("Upgrading blockchain database") + + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + chain, chainDb := utils.MakeChain(ctx, stack) + bcVersion := core.GetBlockChainVersion(chainDb) + if bcVersion == 0 { + bcVersion = core.BlockChainVersion + } + + // Export the current chain. + filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405")) + exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename) + if err := utils.ExportChain(chain, exportFile); err != nil { + utils.Fatalf("Unable to export chain for reimport %s", err) + } + chainDb.Close() + if dir := dbDirectory(chainDb); dir != "" { + os.RemoveAll(dir) + } + + // Import the chain file. + chain, chainDb = utils.MakeChain(ctx, stack) + core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) + err := utils.ImportChain(chain, exportFile) + chainDb.Close() + if err != nil { + utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile) + } else { + os.Remove(exportFile) + glog.Infoln("Import finished") + } + return nil +} + +func dbDirectory(db ethdb.Database) string { + ldb, ok := db.(*ethdb.LDBDatabase) + if !ok { + return "" + } + return ldb.Path() +} + +func dump(ctx *cli.Context) error { + stack := makeFullNode(ctx) + chain, chainDb := utils.MakeChain(ctx, stack) + for _, arg := range ctx.Args() { + var block *types.Block + if hashish(arg) { + block = chain.GetBlockByHash(common.HexToHash(arg)) + } else { + num, _ := strconv.Atoi(arg) + block = chain.GetBlockByNumber(uint64(num)) + } + if block == nil { + fmt.Println("{}") + utils.Fatalf("block not found") + } else { + state, err := state.New(block.Root(), chainDb) + if err != nil { + utils.Fatalf("could not create new state: %v", err) + } + fmt.Printf("%s\n", state.Dump()) + } + } + chainDb.Close() + return nil +} + +// hashish returns true for strings that look like hashes. +func hashish(x string) bool { + _, err := strconv.Atoi(x) + return err != nil +} + +func closeAll(dbs ...ethdb.Database) { + for _, db := range dbs { + db.Close() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go new file mode 100644 index 0000000..b1c435e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd.go @@ -0,0 +1,196 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "os" + "os/signal" + "strings" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "gopkg.in/urfave/cli.v1" +) + +var ( + consoleCommand = cli.Command{ + Action: localConsole, + Name: "console", + Usage: "Start an interactive JavaScript environment", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", + Description: ` +The Geth console is an interactive shell for the JavaScript runtime environment +which exposes a node admin interface as well as the Ðapp JavaScript API. +See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console +`, + } + attachCommand = cli.Command{ + Action: remoteConsole, + Name: "attach", + Usage: "Start an interactive JavaScript environment (connect to node)", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", + Description: ` +The Geth console is an interactive shell for the JavaScript runtime environment +which exposes a node admin interface as well as the Ðapp JavaScript API. +See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. +This command allows to open a console on a running geth node. +`, + } + javascriptCommand = cli.Command{ + Action: ephemeralConsole, + Name: "js", + Usage: "Execute the specified JavaScript files", + ArgsUsage: "", // TODO: Write this! + Category: "CONSOLE COMMANDS", + Description: ` +The JavaScript VM exposes a node admin interface as well as the Ðapp +JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console +`, + } +) + +// localConsole starts a new geth node, attaching a JavaScript console to it at the +// same time. +func localConsole(ctx *cli.Context) error { + // Create and start the node based on the CLI flags + node := makeFullNode(ctx) + startNode(ctx, node) + defer node.Stop() + + // Attach to the newly started node and start the JavaScript console + client, err := node.Attach() + if err != nil { + utils.Fatalf("Failed to attach to the inproc geth: %v", err) + } + config := console.Config{ + DataDir: node.DataDir(), + DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + Client: client, + Preload: utils.MakeConsolePreloads(ctx), + } + console, err := console.New(config) + if err != nil { + utils.Fatalf("Failed to start the JavaScript console: %v", err) + } + defer console.Stop(false) + + // If only a short execution was requested, evaluate and return + if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { + console.Evaluate(script) + return nil + } + // Otherwise print the welcome screen and enter interactive mode + console.Welcome() + console.Interactive() + + return nil +} + +// remoteConsole will connect to a remote geth instance, attaching a JavaScript +// console to it. +func remoteConsole(ctx *cli.Context) error { + // Attach to a remotely running geth instance and start the JavaScript console + client, err := dialRPC(ctx.Args().First()) + if err != nil { + utils.Fatalf("Unable to attach to remote geth: %v", err) + } + config := console.Config{ + DataDir: utils.MakeDataDir(ctx), + DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + Client: client, + Preload: utils.MakeConsolePreloads(ctx), + } + console, err := console.New(config) + if err != nil { + utils.Fatalf("Failed to start the JavaScript console: %v", err) + } + defer console.Stop(false) + + // If only a short execution was requested, evaluate and return + if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { + console.Evaluate(script) + return nil + } + // Otherwise print the welcome screen and enter interactive mode + console.Welcome() + console.Interactive() + + return nil +} + +// dialRPC returns a RPC client which connects to the given endpoint. +// The check for empty endpoint implements the defaulting logic +// for "geth attach" and "geth monitor" with no argument. +func dialRPC(endpoint string) (*rpc.Client, error) { + if endpoint == "" { + endpoint = node.DefaultIPCEndpoint(clientIdentifier) + } else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { + // Backwards compatibility with geth < 1.5 which required + // these prefixes. + endpoint = endpoint[4:] + } + return rpc.Dial(endpoint) +} + +// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript +// console to it, and each of the files specified as arguments and tears the +// everything down. +func ephemeralConsole(ctx *cli.Context) error { + // Create and start the node based on the CLI flags + node := makeFullNode(ctx) + startNode(ctx, node) + defer node.Stop() + + // Attach to the newly started node and start the JavaScript console + client, err := node.Attach() + if err != nil { + utils.Fatalf("Failed to attach to the inproc geth: %v", err) + } + config := console.Config{ + DataDir: node.DataDir(), + DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), + Client: client, + Preload: utils.MakeConsolePreloads(ctx), + } + console, err := console.New(config) + if err != nil { + utils.Fatalf("Failed to start the JavaScript console: %v", err) + } + defer console.Stop(false) + + // Evaluate each of the specified JavaScript files + for _, file := range ctx.Args() { + if err = console.Execute(file); err != nil { + utils.Fatalf("Failed to execute %s: %v", file, err) + } + } + // Wait for pending callbacks, but stop for Ctrl-C. + abort := make(chan os.Signal, 1) + signal.Notify(abort, os.Interrupt) + + go func() { + <-abort + os.Exit(0) + }() + console.Stop(true) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd_test.go new file mode 100644 index 0000000..820e9d0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/consolecmd_test.go @@ -0,0 +1,171 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "crypto/rand" + "math/big" + "os" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +// Tests that a node embedded within a console can be started up properly and +// then terminated by closing the input stream. +func TestConsoleWelcome(t *testing.T) { + coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + + // Start a geth console, make sure it's cleaned up and terminate the console + geth := runGeth(t, + "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", + "--etherbase", coinbase, "--shh", + "console") + + // Gather all the infos the welcome message needs to contain + geth.setTemplateFunc("goos", func() string { return runtime.GOOS }) + geth.setTemplateFunc("gover", runtime.Version) + geth.setTemplateFunc("gethver", func() string { return params.Version }) + geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) + geth.setTemplateFunc("apis", func() []string { + apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) + sort.Strings(apis) + return apis + }) + + // Verify the actual welcome message to the required template + geth.expect(` +Welcome to the Geth JavaScript console! + +instance: Geth/v{{gethver}}/{{goos}}/{{gover}} +coinbase: {{.Etherbase}} +at block: 0 ({{niltime}}) + datadir: {{.Datadir}} + modules:{{range apis}} {{.}}:1.0{{end}} + +> {{.InputLine "exit"}} +`) + geth.expectExit() +} + +// Tests that a console can be attached to a running node via various means. +func TestIPCAttachWelcome(t *testing.T) { + // Configure the instance for IPC attachement + coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + var ipc string + if runtime.GOOS == "windows" { + ipc = `\\.\pipe\geth` + strconv.Itoa(trulyRandInt(100000, 999999)) + } else { + ws := tmpdir(t) + defer os.RemoveAll(ws) + ipc = filepath.Join(ws, "geth.ipc") + } + // Note: we need --shh because testAttachWelcome checks for default + // list of ipc modules and shh is included there. + geth := runGeth(t, + "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", + "--etherbase", coinbase, "--shh", "--ipcpath", ipc) + + time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open + testAttachWelcome(t, geth, "ipc:"+ipc) + + geth.interrupt() + geth.expectExit() +} + +func TestHTTPAttachWelcome(t *testing.T) { + coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P + geth := runGeth(t, + "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", + "--etherbase", coinbase, "--rpc", "--rpcport", port) + + time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open + testAttachWelcome(t, geth, "http://localhost:"+port) + + geth.interrupt() + geth.expectExit() +} + +func TestWSAttachWelcome(t *testing.T) { + coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" + port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P + + geth := runGeth(t, + "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", + "--etherbase", coinbase, "--ws", "--wsport", port) + + time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open + testAttachWelcome(t, geth, "ws://localhost:"+port) + + geth.interrupt() + geth.expectExit() +} + +func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) { + // Attach to a running geth note and terminate immediately + attach := runGeth(t, "attach", endpoint) + defer attach.expectExit() + attach.stdin.Close() + + // Gather all the infos the welcome message needs to contain + attach.setTemplateFunc("goos", func() string { return runtime.GOOS }) + attach.setTemplateFunc("gover", runtime.Version) + attach.setTemplateFunc("gethver", func() string { return params.Version }) + attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase }) + attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) + attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") }) + attach.setTemplateFunc("datadir", func() string { return geth.Datadir }) + attach.setTemplateFunc("apis", func() []string { + var apis []string + if strings.HasPrefix(endpoint, "ipc") { + apis = append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) + } else { + apis = append(strings.Split(rpc.DefaultHTTPApis, ","), rpc.MetadataApi) + } + sort.Strings(apis) + return apis + }) + + // Verify the actual welcome message to the required template + attach.expect(` +Welcome to the Geth JavaScript console! + +instance: Geth/v{{gethver}}/{{goos}}/{{gover}} +coinbase: {{etherbase}} +at block: 0 ({{niltime}}){{if ipc}} + datadir: {{datadir}}{{end}} + modules:{{range apis}} {{.}}:1.0{{end}} + +> {{.InputLine "exit" }} +`) + attach.expectExit() +} + +// trulyRandInt generates a crypto random integer used by the console tests to +// not clash network ports with other tests running cocurrently. +func trulyRandInt(lo, hi int) int { + num, _ := rand.Int(rand.Reader, big.NewInt(int64(hi-lo))) + return int(num.Int64()) + lo +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/dao_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/dao_test.go new file mode 100644 index 0000000..f9ce802 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/dao_test.go @@ -0,0 +1,164 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "io/ioutil" + "math/big" + "os" + "path/filepath" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +// Genesis block for nodes which don't care about the DAO fork (i.e. not configured) +var daoOldGenesis = `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00", + "config" : {} +}` + +// Genesis block for nodes which actively oppose the DAO fork +var daoNoForkGenesis = `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00", + "config" : { + "daoForkBlock" : 314, + "daoForkSupport" : false + } +}` + +// Genesis block for nodes which actively support the DAO fork +var daoProForkGenesis = `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00", + "config" : { + "daoForkBlock" : 314, + "daoForkSupport" : true + } +}` + +var daoGenesisHash = common.HexToHash("5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0") +var daoGenesisForkBlock = big.NewInt(314) + +// TestDAOForkBlockNewChain tests that the DAO hard-fork number and the nodes support/opposition is correctly +// set in the database after various initialization procedures and invocations. +func TestDAOForkBlockNewChain(t *testing.T) { + for i, arg := range []struct { + testnet bool + genesis string + expectBlock *big.Int + expectVote bool + }{ + // Test DAO Default Mainnet + {false, "", params.MainNetDAOForkBlock, true}, + // test DAO Default Testnet + {true, "", params.TestNetDAOForkBlock, true}, + // test DAO Init Old Privnet + {false, daoOldGenesis, nil, false}, + // test DAO Default No Fork Privnet + {false, daoNoForkGenesis, daoGenesisForkBlock, false}, + // test DAO Default Pro Fork Privnet + {false, daoProForkGenesis, daoGenesisForkBlock, true}, + } { + testDAOForkBlockNewChain(t, i, arg.testnet, arg.genesis, arg.expectBlock, arg.expectVote) + } +} + +func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis string, expectBlock *big.Int, expectVote bool) { + // Create a temporary data directory to use and inspect later + datadir := tmpdir(t) + defer os.RemoveAll(datadir) + + // Start a Geth instance with the requested flags set and immediately terminate + if genesis != "" { + json := filepath.Join(datadir, "genesis.json") + if err := ioutil.WriteFile(json, []byte(genesis), 0600); err != nil { + t.Fatalf("test %d: failed to write genesis file: %v", test, err) + } + runGeth(t, "--datadir", datadir, "init", json).cmd.Wait() + } else { + // Force chain initialization + args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} + if testnet { + args = append(args, "--testnet") + } + geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...) + geth.cmd.Wait() + } + // Retrieve the DAO config flag from the database + path := filepath.Join(datadir, "geth", "chaindata") + if testnet && genesis == "" { + path = filepath.Join(datadir, "testnet", "geth", "chaindata") + } + db, err := ethdb.NewLDBDatabase(path, 0, 0) + if err != nil { + t.Fatalf("test %d: failed to open test database: %v", test, err) + } + defer db.Close() + + genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + if testnet { + genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") + } + if genesis != "" { + genesisHash = daoGenesisHash + } + config, err := core.GetChainConfig(db, genesisHash) + if err != nil { + t.Errorf("test %d: failed to retrieve chain config: %v", test, err) + return // we want to return here, the other checks can't make it past this point (nil panic). + } + // Validate the DAO hard-fork block number against the expected value + if config.DAOForkBlock == nil { + if expectBlock != nil { + t.Errorf("test %d: dao hard-fork block mismatch: have nil, want %v", test, expectBlock) + } + } else if expectBlock == nil { + t.Errorf("test %d: dao hard-fork block mismatch: have %v, want nil", test, config.DAOForkBlock) + } else if config.DAOForkBlock.Cmp(expectBlock) != 0 { + t.Errorf("test %d: dao hard-fork block mismatch: have %v, want %v", test, config.DAOForkBlock, expectBlock) + } + if config.DAOForkSupport != expectVote { + t.Errorf("test %d: dao hard-fork support mismatch: have %v, want %v", test, config.DAOForkSupport, expectVote) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/genesis_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/genesis_test.go new file mode 100644 index 0000000..6c3ca72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/genesis_test.go @@ -0,0 +1,110 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +var customGenesisTests = []struct { + genesis string + query string + result string +}{ + // Plain genesis file without anything extra + { + genesis: `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00" + }`, + query: "eth.getBlock(0).nonce", + result: "0x0000000000000042", + }, + // Genesis file with an empty chain configuration (ensure missing fields work) + { + genesis: `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00", + "config" : {} + }`, + query: "eth.getBlock(0).nonce", + result: "0x0000000000000042", + }, + // Genesis file with specific chain configurations + { + genesis: `{ + "alloc" : {}, + "coinbase" : "0x0000000000000000000000000000000000000000", + "difficulty" : "0x20000", + "extraData" : "", + "gasLimit" : "0x2fefd8", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00", + "config" : { + "homesteadBlock" : 314, + "daoForkBlock" : 141, + "daoForkSupport" : true + }, + }`, + query: "eth.getBlock(0).nonce", + result: "0x0000000000000042", + }, +} + +// Tests that initializing Geth with a custom genesis block and chain definitions +// work properly. +func TestCustomGenesis(t *testing.T) { + for i, tt := range customGenesisTests { + // Create a temporary data directory to use and inspect later + datadir := tmpdir(t) + defer os.RemoveAll(datadir) + + // Initialize the data directory with the custom genesis block + json := filepath.Join(datadir, "genesis.json") + if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil { + t.Fatalf("test %d: failed to write genesis file: %v", i, err) + } + runGeth(t, "--datadir", datadir, "init", json).cmd.Wait() + + // Query the custom genesis block + geth := runGeth(t, + "--datadir", datadir, "--maxpeers", "0", "--port", "0", + "--nodiscover", "--nat", "none", "--ipcdisable", + "--exec", tt.query, "console") + geth.expectRegexp(tt.result) + geth.expectExit() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go new file mode 100644 index 0000000..7d98f6b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/main.go @@ -0,0 +1,306 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// geth is the official command-line client for Ethereum. +package main + +import ( + "encoding/hex" + "fmt" + "os" + "runtime" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/contracts/release" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "gopkg.in/urfave/cli.v1" +) + +const ( + clientIdentifier = "geth" // Client identifier to advertise over the network +) + +var ( + // Git SHA1 commit hash of the release (set via linker flags) + gitCommit = "" + // Ethereum address of the Geth release oracle. + relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf") + // The app that holds all commands and flags. + app = utils.NewApp(gitCommit, "the go-ethereum command line interface") +) + +func init() { + // Initialize the CLI app and start Geth + app.Action = geth + app.HideVersion = true // we have a command to print the version + app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" + app.Commands = []cli.Command{ + // See chaincmd.go: + initCommand, + importCommand, + exportCommand, + upgradedbCommand, + removedbCommand, + dumpCommand, + // See monitorcmd.go: + monitorCommand, + // See accountcmd.go: + accountCommand, + walletCommand, + // See consolecmd.go: + consoleCommand, + attachCommand, + javascriptCommand, + // See misccmd.go: + makedagCommand, + versionCommand, + bugCommand, + licenseCommand, + } + + app.Flags = []cli.Flag{ + utils.IdentityFlag, + utils.UnlockedAccountFlag, + utils.PasswordFileFlag, + utils.BootnodesFlag, + utils.DataDirFlag, + utils.KeyStoreDirFlag, + utils.FastSyncFlag, + utils.LightModeFlag, + utils.LightServFlag, + utils.LightPeersFlag, + utils.LightKDFFlag, + utils.CacheFlag, + utils.TrieCacheGenFlag, + utils.JSpathFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.MaxPendingPeersFlag, + utils.EtherbaseFlag, + utils.GasPriceFlag, + utils.MinerThreadsFlag, + utils.MiningEnabledFlag, + utils.AutoDAGFlag, + utils.TargetGasLimitFlag, + utils.NATFlag, + utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, + utils.NetrestrictFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.RPCEnabledFlag, + utils.RPCListenAddrFlag, + utils.RPCPortFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedOriginsFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + utils.ExecFlag, + utils.PreloadJSFlag, + utils.WhisperEnabledFlag, + utils.DevModeFlag, + utils.TestNetFlag, + utils.VMForceJitFlag, + utils.VMJitCacheFlag, + utils.VMEnableJitFlag, + utils.VMEnableDebugFlag, + utils.NetworkIdFlag, + utils.RPCCORSDomainFlag, + utils.EthStatsURLFlag, + utils.MetricsEnabledFlag, + utils.FakePoWFlag, + utils.SolcPathFlag, + utils.GpoMinGasPriceFlag, + utils.GpoMaxGasPriceFlag, + utils.GpoFullBlockRatioFlag, + utils.GpobaseStepDownFlag, + utils.GpobaseStepUpFlag, + utils.GpobaseCorrectionFactorFlag, + utils.ExtraDataFlag, + } + app.Flags = append(app.Flags, debug.Flags...) + + app.Before = func(ctx *cli.Context) error { + runtime.GOMAXPROCS(runtime.NumCPU()) + if err := debug.Setup(ctx); err != nil { + return err + } + // Start system runtime metrics collection + go metrics.CollectProcessMetrics(3 * time.Second) + + // This should be the only place where reporting is enabled + // because it is not intended to run while testing. + // In addition to this check, bad block reports are sent only + // for chains with the main network genesis block and network id 1. + eth.EnableBadBlockReporting = true + + utils.SetupNetwork(ctx) + return nil + } + + app.After = func(ctx *cli.Context) error { + debug.Exit() + console.Stdin.Close() // Resets terminal mode. + return nil + } +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +// geth is the main entry point into the system if no special subcommand is ran. +// It creates a default node based on the command line arguments and runs it in +// blocking mode, waiting for it to be shut down. +func geth(ctx *cli.Context) error { + node := makeFullNode(ctx) + startNode(ctx, node) + node.Wait() + return nil +} + +func makeFullNode(ctx *cli.Context) *node.Node { + // Create the default extradata and construct the base node + var clientInfo = struct { + Version uint + Name string + GoVersion string + Os string + }{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} + extra, err := rlp.EncodeToBytes(clientInfo) + if err != nil { + glog.V(logger.Warn).Infoln("error setting canonical miner information:", err) + } + if uint64(len(extra)) > params.MaximumExtraDataSize { + glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize) + glog.V(logger.Debug).Infof("extra: %x\n", extra) + extra = nil + } + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + utils.RegisterEthService(ctx, stack, extra) + + // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. + shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) + shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) + if shhEnabled || shhAutoEnabled { + utils.RegisterShhService(stack) + } + // Add the Ethereum Stats daemon if requested + if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" { + utils.RegisterEthStatsService(stack, url) + } + // Add the release oracle service so it boots along with node. + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + config := release.Config{ + Oracle: relOracle, + Major: uint32(params.VersionMajor), + Minor: uint32(params.VersionMinor), + Patch: uint32(params.VersionPatch), + } + commit, _ := hex.DecodeString(gitCommit) + copy(config.Commit[:], commit) + return release.NewReleaseService(ctx, config) + }); err != nil { + utils.Fatalf("Failed to register the Geth release oracle service: %v", err) + } + return stack +} + +// startNode boots up the system node and all registered protocols, after which +// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the +// miner. +func startNode(ctx *cli.Context, stack *node.Node) { + // Start up the node itself + utils.StartNode(stack) + + // Unlock any account specifically requested + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + passwords := utils.MakePasswordList(ctx) + unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") + for i, account := range unlocks { + if trimmed := strings.TrimSpace(account); trimmed != "" { + unlockAccount(ctx, ks, trimmed, i, passwords) + } + } + // Register wallet event handlers to open and auto-derive wallets + events := make(chan accounts.WalletEvent, 16) + stack.AccountManager().Subscribe(events) + + go func() { + // Create an chain state reader for self-derivation + rpcClient, err := stack.Attach() + if err != nil { + utils.Fatalf("Failed to attach to self: %v", err) + } + stateReader := ethclient.NewClient(rpcClient) + + // Open and self derive any wallets already attached + for _, wallet := range stack.AccountManager().Wallets() { + if err := wallet.Open(""); err != nil { + glog.V(logger.Warn).Infof("Failed to open wallet %s: %v", wallet.URL(), err) + } else { + wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) + } + } + // Listen for wallet event till termination + for event := range events { + if event.Arrive { + if err := event.Wallet.Open(""); err != nil { + glog.V(logger.Info).Infof("New wallet appeared: %s, failed to open: %s", event.Wallet.URL(), err) + } else { + glog.V(logger.Info).Infof("New wallet appeared: %s, %s", event.Wallet.URL(), event.Wallet.Status()) + event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) + } + } else { + glog.V(logger.Info).Infof("Old wallet dropped: %s", event.Wallet.URL()) + event.Wallet.Close() + } + } + }() + // Start auxiliary services if enabled + if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { + var ethereum *eth.Ethereum + if err := stack.Service(ðereum); err != nil { + utils.Fatalf("ethereum service not running: %v", err) + } + if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil { + utils.Fatalf("Failed to start mining: %v", err) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go new file mode 100644 index 0000000..077f1ad --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/misccmd.go @@ -0,0 +1,128 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/params" + "gopkg.in/urfave/cli.v1" +) + +var ( + makedagCommand = cli.Command{ + Action: makedag, + Name: "makedag", + Usage: "Generate ethash DAG (for testing)", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + Description: ` +The makedag command generates an ethash DAG in /tmp/dag. + +This command exists to support the system testing project. +Regular users do not need to execute it. +`, + } + versionCommand = cli.Command{ + Action: version, + Name: "version", + Usage: "Print version numbers", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + Description: ` +The output of this command is supposed to be machine-readable. +`, + } + licenseCommand = cli.Command{ + Action: license, + Name: "license", + Usage: "Display license information", + ArgsUsage: " ", + Category: "MISCELLANEOUS COMMANDS", + } +) + +func makedag(ctx *cli.Context) error { + args := ctx.Args() + wrongArgs := func() { + utils.Fatalf(`Usage: geth makedag `) + } + switch { + case len(args) == 2: + blockNum, err := strconv.ParseUint(args[0], 0, 64) + dir := args[1] + if err != nil { + wrongArgs() + } else { + dir = filepath.Clean(dir) + // seems to require a trailing slash + if !strings.HasSuffix(dir, "/") { + dir = dir + "/" + } + _, err = ioutil.ReadDir(dir) + if err != nil { + utils.Fatalf("Can't find dir") + } + fmt.Println("making DAG, this could take awhile...") + ethash.MakeDAG(blockNum, dir) + } + default: + wrongArgs() + } + return nil +} + +func version(ctx *cli.Context) error { + fmt.Println(strings.Title(clientIdentifier)) + fmt.Println("Version:", params.Version) + if gitCommit != "" { + fmt.Println("Git Commit:", gitCommit) + } + fmt.Println("Protocol Versions:", eth.ProtocolVersions) + fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) + fmt.Println("Go Version:", runtime.Version()) + fmt.Println("OS:", runtime.GOOS) + fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) + fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) + return nil +} + +func license(_ *cli.Context) error { + fmt.Println(`Geth is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Geth is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with geth. If not, see . +`) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go new file mode 100644 index 0000000..c63542f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/monitorcmd.go @@ -0,0 +1,351 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "fmt" + "math" + "reflect" + "runtime" + "sort" + "strings" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "github.com/gizak/termui" + "gopkg.in/urfave/cli.v1" +) + +var ( + monitorCommandAttachFlag = cli.StringFlag{ + Name: "attach", + Value: node.DefaultIPCEndpoint(clientIdentifier), + Usage: "API endpoint to attach to", + } + monitorCommandRowsFlag = cli.IntFlag{ + Name: "rows", + Value: 5, + Usage: "Maximum rows in the chart grid", + } + monitorCommandRefreshFlag = cli.IntFlag{ + Name: "refresh", + Value: 3, + Usage: "Refresh interval in seconds", + } + monitorCommand = cli.Command{ + Action: monitor, + Name: "monitor", + Usage: "Monitor and visualize node metrics", + ArgsUsage: " ", + Category: "MONITOR COMMANDS", + Description: ` +The Geth monitor is a tool to collect and visualize various internal metrics +gathered by the node, supporting different chart types as well as the capacity +to display multiple metrics simultaneously. +`, + Flags: []cli.Flag{ + monitorCommandAttachFlag, + monitorCommandRowsFlag, + monitorCommandRefreshFlag, + }, + } +) + +// monitor starts a terminal UI based monitoring tool for the requested metrics. +func monitor(ctx *cli.Context) error { + var ( + client *rpc.Client + err error + ) + // Attach to an Ethereum node over IPC or RPC + endpoint := ctx.String(monitorCommandAttachFlag.Name) + if client, err = dialRPC(endpoint); err != nil { + utils.Fatalf("Unable to attach to geth node: %v", err) + } + defer client.Close() + + // Retrieve all the available metrics and resolve the user pattens + metrics, err := retrieveMetrics(client) + if err != nil { + utils.Fatalf("Failed to retrieve system metrics: %v", err) + } + monitored := resolveMetrics(metrics, ctx.Args()) + if len(monitored) == 0 { + list := expandMetrics(metrics, "") + sort.Strings(list) + + if len(list) > 0 { + utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) + } else { + utils.Fatalf("No metrics collected by geth (--%s).\n", utils.MetricsEnabledFlag.Name) + } + } + sort.Strings(monitored) + if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { + utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) + } + // Create and configure the chart UI defaults + if err := termui.Init(); err != nil { + utils.Fatalf("Unable to initialize terminal UI: %v", err) + } + defer termui.Close() + + rows := len(monitored) + if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { + rows = max + } + cols := (len(monitored) + rows - 1) / rows + for i := 0; i < rows; i++ { + termui.Body.AddRows(termui.NewRow()) + } + // Create each individual data chart + footer := termui.NewPar("") + footer.Block.Border = true + footer.Height = 3 + + charts := make([]*termui.LineChart, len(monitored)) + units := make([]int, len(monitored)) + data := make([][]float64, len(monitored)) + for i := 0; i < len(monitored); i++ { + charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) + row := termui.Body.Rows[i%rows] + row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) + } + termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) + + refreshCharts(client, monitored, data, units, charts, ctx, footer) + termui.Body.Align() + termui.Render(termui.Body) + + // Watch for various system events, and periodically refresh the charts + termui.Handle("/sys/kbd/C-c", func(termui.Event) { + termui.StopLoop() + }) + termui.Handle("/sys/wnd/resize", func(termui.Event) { + termui.Body.Width = termui.TermWidth() + for _, chart := range charts { + chart.Height = (termui.TermHeight() - footer.Height) / rows + } + termui.Body.Align() + termui.Render(termui.Body) + }) + go func() { + tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) + for range tick.C { + if refreshCharts(client, monitored, data, units, charts, ctx, footer) { + termui.Body.Align() + } + termui.Render(termui.Body) + } + }() + termui.Loop() + return nil +} + +// retrieveMetrics contacts the attached geth node and retrieves the entire set +// of collected system metrics. +func retrieveMetrics(client *rpc.Client) (map[string]interface{}, error) { + var metrics map[string]interface{} + err := client.Call(&metrics, "debug_metrics", true) + return metrics, err +} + +// resolveMetrics takes a list of input metric patterns, and resolves each to one +// or more canonical metric names. +func resolveMetrics(metrics map[string]interface{}, patterns []string) []string { + res := []string{} + for _, pattern := range patterns { + res = append(res, resolveMetric(metrics, pattern, "")...) + } + return res +} + +// resolveMetrics takes a single of input metric pattern, and resolves it to one +// or more canonical metric names. +func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string { + results := []string{} + + // If a nested metric was requested, recurse optionally branching (via comma) + parts := strings.SplitN(pattern, "/", 2) + if len(parts) > 1 { + for _, variation := range strings.Split(parts[0], ",") { + if submetrics, ok := metrics[variation].(map[string]interface{}); !ok { + utils.Fatalf("Failed to retrieve system metrics: %s", path+variation) + return nil + } else { + results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...) + } + } + return results + } + // Depending what the last link is, return or expand + for _, variation := range strings.Split(pattern, ",") { + switch metric := metrics[variation].(type) { + case float64: + // Final metric value found, return as singleton + results = append(results, path+variation) + + case map[string]interface{}: + results = append(results, expandMetrics(metric, path+variation+"/")...) + + default: + utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric)) + return nil + } + } + return results +} + +// expandMetrics expands the entire tree of metrics into a flat list of paths. +func expandMetrics(metrics map[string]interface{}, path string) []string { + // Iterate over all fields and expand individually + list := []string{} + for name, metric := range metrics { + switch metric := metric.(type) { + case float64: + // Final metric value found, append to list + list = append(list, path+name) + + case map[string]interface{}: + // Tree of metrics found, expand recursively + list = append(list, expandMetrics(metric, path+name+"/")...) + + default: + utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric)) + return nil + } + } + return list +} + +// fetchMetric iterates over the metrics map and retrieves a specific one. +func fetchMetric(metrics map[string]interface{}, metric string) float64 { + parts := strings.Split(metric, "/") + for _, part := range parts[:len(parts)-1] { + var found bool + metrics, found = metrics[part].(map[string]interface{}) + if !found { + return 0 + } + } + if v, ok := metrics[parts[len(parts)-1]].(float64); ok { + return v + } + return 0 +} + +// refreshCharts retrieves a next batch of metrics, and inserts all the new +// values into the active datasets and charts +func refreshCharts(client *rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) { + values, err := retrieveMetrics(client) + for i, metric := range metrics { + if len(data) < 512 { + data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...) + } else { + data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...) + } + if updateChart(metric, data[i], &units[i], charts[i], err) { + realign = true + } + } + updateFooter(ctx, err, footer) + return +} + +// updateChart inserts a dataset into a line chart, scaling appropriately as to +// not display weird labels, also updating the chart label accordingly. +func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) { + dataUnits := []string{"", "K", "M", "G", "T", "E"} + timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"} + colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed} + + // Extract only part of the data that's actually visible + if chart.Width*2 < len(data) { + data = data[:chart.Width*2] + } + // Find the maximum value and scale under 1K + high := 0.0 + if len(data) > 0 { + high = data[0] + for _, value := range data[1:] { + high = math.Max(high, value) + } + } + unit, scale := 0, 1.0 + for high >= 1000 && unit+1 < len(dataUnits) { + high, unit, scale = high/1000, unit+1, scale*1000 + } + // If the unit changes, re-create the chart (hack to set max height...) + if unit != *base { + realign, *base, *chart = true, unit, *createChart(chart.Height) + } + // Update the chart's data points with the scaled values + if cap(chart.Data) < len(data) { + chart.Data = make([]float64, len(data)) + } + chart.Data = chart.Data[:len(data)] + for i, value := range data { + chart.Data[i] = value / scale + } + // Update the chart's label with the scale units + units := dataUnits + if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") { + units = timeUnits + } + chart.BorderLabel = metric + if len(units[unit]) > 0 { + chart.BorderLabel += " [" + units[unit] + "]" + } + chart.LineColor = colors[unit] | termui.AttrBold + if err != nil { + chart.LineColor = termui.ColorRed | termui.AttrBold + } + return +} + +// createChart creates an empty line chart with the default configs. +func createChart(height int) *termui.LineChart { + chart := termui.NewLineChart() + if runtime.GOOS == "windows" { + chart.Mode = "dot" + } + chart.DataLabels = []string{""} + chart.Height = height + chart.AxesColor = termui.ColorWhite + chart.PaddingBottom = -2 + + chart.BorderLabelFg = chart.BorderFg | termui.AttrBold + chart.BorderFg = chart.BorderBg + + return chart +} + +// updateFooter updates the footer contents based on any encountered errors. +func updateFooter(ctx *cli.Context, err error, footer *termui.Par) { + // Generate the basic footer + refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second + footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh) + footer.TextFgColor = termui.ThemeAttr("par.fg") | termui.AttrBold + + // Append any encountered errors + if err != nil { + footer.Text = fmt.Sprintf("Error: %v.", err) + footer.TextFgColor = termui.ColorRed | termui.AttrBold + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/run_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/run_test.go new file mode 100644 index 0000000..e26b450 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/run_test.go @@ -0,0 +1,298 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "regexp" + "sync" + "testing" + "text/template" + "time" +) + +func tmpdir(t *testing.T) string { + dir, err := ioutil.TempDir("", "geth-test") + if err != nil { + t.Fatal(err) + } + return dir +} + +type testgeth struct { + // For total convenience, all testing methods are available. + *testing.T + // template variables for expect + Datadir string + Executable string + Etherbase string + Func template.FuncMap + + removeDatadir bool + cmd *exec.Cmd + stdout *bufio.Reader + stdin io.WriteCloser + stderr *testlogger +} + +func init() { + // Run the app if we're the child process for runGeth. + if os.Getenv("GETH_TEST_CHILD") != "" { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + } +} + +// spawns geth with the given command line args. If the args don't set --datadir, the +// child g gets a temporary data directory. +func runGeth(t *testing.T, args ...string) *testgeth { + tt := &testgeth{T: t, Executable: os.Args[0]} + for i, arg := range args { + switch { + case arg == "-datadir" || arg == "--datadir": + if i < len(args)-1 { + tt.Datadir = args[i+1] + } + case arg == "-etherbase" || arg == "--etherbase": + if i < len(args)-1 { + tt.Etherbase = args[i+1] + } + } + } + if tt.Datadir == "" { + tt.Datadir = tmpdir(t) + tt.removeDatadir = true + args = append([]string{"-datadir", tt.Datadir}, args...) + // Remove the temporary datadir if something fails below. + defer func() { + if t.Failed() { + os.RemoveAll(tt.Datadir) + } + }() + } + + // Boot "geth". This actually runs the test binary but the init function + // will prevent any tests from running. + tt.stderr = &testlogger{t: t} + tt.cmd = exec.Command(os.Args[0], args...) + tt.cmd.Env = append(os.Environ(), "GETH_TEST_CHILD=1") + tt.cmd.Stderr = tt.stderr + stdout, err := tt.cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + tt.stdout = bufio.NewReader(stdout) + if tt.stdin, err = tt.cmd.StdinPipe(); err != nil { + t.Fatal(err) + } + if err := tt.cmd.Start(); err != nil { + t.Fatal(err) + } + return tt +} + +// InputLine writes the given text to the childs stdin. +// This method can also be called from an expect template, e.g.: +// +// geth.expect(`Passphrase: {{.InputLine "password"}}`) +func (tt *testgeth) InputLine(s string) string { + io.WriteString(tt.stdin, s+"\n") + return "" +} + +func (tt *testgeth) setTemplateFunc(name string, fn interface{}) { + if tt.Func == nil { + tt.Func = make(map[string]interface{}) + } + tt.Func[name] = fn +} + +// expect runs its argument as a template, then expects the +// child process to output the result of the template within 5s. +// +// If the template starts with a newline, the newline is removed +// before matching. +func (tt *testgeth) expect(tplsource string) { + // Generate the expected output by running the template. + tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource)) + wantbuf := new(bytes.Buffer) + if err := tpl.Execute(wantbuf, tt); err != nil { + panic(err) + } + // Trim exactly one newline at the beginning. This makes tests look + // much nicer because all expect strings are at column 0. + want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n")) + if err := tt.matchExactOutput(want); err != nil { + tt.Fatal(err) + } + tt.Logf("Matched stdout text:\n%s", want) +} + +func (tt *testgeth) matchExactOutput(want []byte) error { + buf := make([]byte, len(want)) + n := 0 + tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) }) + buf = buf[:n] + if n < len(want) || !bytes.Equal(buf, want) { + // Grab any additional buffered output in case of mismatch + // because it might help with debugging. + buf = append(buf, make([]byte, tt.stdout.Buffered())...) + tt.stdout.Read(buf[n:]) + // Find the mismatch position. + for i := 0; i < n; i++ { + if want[i] != buf[i] { + return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s", + buf[:i], buf[i:n], want) + } + } + if n < len(want) { + return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s", + buf, want[:n], want[n:]) + } + } + return nil +} + +// expectRegexp expects the child process to output text matching the +// given regular expression within 5s. +// +// Note that an arbitrary amount of output may be consumed by the +// regular expression. This usually means that expect cannot be used +// after expectRegexp. +func (tt *testgeth) expectRegexp(resource string) (*regexp.Regexp, []string) { + var ( + re = regexp.MustCompile(resource) + rtee = &runeTee{in: tt.stdout} + matches []int + ) + tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) }) + output := rtee.buf.Bytes() + if matches == nil { + tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s", + output, resource) + return re, nil + } + tt.Logf("Matched stdout text:\n%s", output) + var submatch []string + for i := 0; i < len(matches); i += 2 { + submatch = append(submatch, string(output[i:i+1])) + } + return re, submatch +} + +// expectExit expects the child process to exit within 5s without +// printing any additional text on stdout. +func (tt *testgeth) expectExit() { + var output []byte + tt.withKillTimeout(func() { + output, _ = ioutil.ReadAll(tt.stdout) + }) + tt.cmd.Wait() + if tt.removeDatadir { + os.RemoveAll(tt.Datadir) + } + if len(output) > 0 { + tt.Errorf("Unmatched stdout text:\n%s", output) + } +} + +func (tt *testgeth) interrupt() { + tt.cmd.Process.Signal(os.Interrupt) +} + +// stderrText returns any stderr output written so far. +// The returned text holds all log lines after expectExit has +// returned. +func (tt *testgeth) stderrText() string { + tt.stderr.mu.Lock() + defer tt.stderr.mu.Unlock() + return tt.stderr.buf.String() +} + +func (tt *testgeth) withKillTimeout(fn func()) { + timeout := time.AfterFunc(5*time.Second, func() { + tt.Log("killing the child process (timeout)") + tt.cmd.Process.Kill() + if tt.removeDatadir { + os.RemoveAll(tt.Datadir) + } + }) + defer timeout.Stop() + fn() +} + +// testlogger logs all written lines via t.Log and also +// collects them for later inspection. +type testlogger struct { + t *testing.T + mu sync.Mutex + buf bytes.Buffer +} + +func (tl *testlogger) Write(b []byte) (n int, err error) { + lines := bytes.Split(b, []byte("\n")) + for _, line := range lines { + if len(line) > 0 { + tl.t.Logf("(stderr) %s", line) + } + } + tl.mu.Lock() + tl.buf.Write(b) + tl.mu.Unlock() + return len(b), err +} + +// runeTee collects text read through it into buf. +type runeTee struct { + in interface { + io.Reader + io.ByteReader + io.RuneReader + } + buf bytes.Buffer +} + +func (rtee *runeTee) Read(b []byte) (n int, err error) { + n, err = rtee.in.Read(b) + rtee.buf.Write(b[:n]) + return n, err +} + +func (rtee *runeTee) ReadRune() (r rune, size int, err error) { + r, size, err = rtee.in.ReadRune() + if err == nil { + rtee.buf.WriteRune(r) + } + return r, size, err +} + +func (rtee *runeTee) ReadByte() (b byte, err error) { + b, err = rtee.in.ReadByte() + if err == nil { + rtee.buf.WriteByte(b) + } + return b, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/empty.js b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/empty.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/empty.js @@ -0,0 +1 @@ + diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/guswallet.json b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/guswallet.json new file mode 100644 index 0000000..e8ea4f3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/guswallet.json @@ -0,0 +1,6 @@ +{ + "encseed": "26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba", + "ethaddr": "d4584b5f6229b7be90727b0fc8c6b91bb427821f", + "email": "gustav.simonsson@gmail.com", + "btcaddr": "1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx" +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/passwords.txt b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/passwords.txt new file mode 100644 index 0000000..96f98c7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/passwords.txt @@ -0,0 +1,3 @@ +foobar +foobar +foobar diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/wrong-passwords.txt b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/wrong-passwords.txt new file mode 100644 index 0000000..7d1e338 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/testdata/wrong-passwords.txt @@ -0,0 +1,3 @@ +wrong +wrong +wrong diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go new file mode 100644 index 0000000..9349857 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/geth/usage.go @@ -0,0 +1,225 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Contains the geth command usage template and generator. + +package main + +import ( + "io" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/internal/debug" + "gopkg.in/urfave/cli.v1" +) + +// AppHelpTemplate is the test template for the default, global app help topic. +var AppHelpTemplate = `NAME: + {{.App.Name}} - {{.App.Usage}} + + Copyright 2013-2016 The go-ethereum Authors + +USAGE: + {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} + {{if .App.Version}} +VERSION: + {{.App.Version}} + {{end}}{{if len .App.Authors}} +AUTHOR(S): + {{range .App.Authors}}{{ . }}{{end}} + {{end}}{{if .App.Commands}} +COMMANDS: + {{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} + {{end}}{{end}}{{if .FlagGroups}} +{{range .FlagGroups}}{{.Name}} OPTIONS: + {{range .Flags}}{{.}} + {{end}} +{{end}}{{end}}{{if .App.Copyright }} +COPYRIGHT: + {{.App.Copyright}} + {{end}} +` + +// flagGroup is a collection of flags belonging to a single topic. +type flagGroup struct { + Name string + Flags []cli.Flag +} + +// AppHelpFlagGroups is the application flags, grouped by functionality. +var AppHelpFlagGroups = []flagGroup{ + { + Name: "ETHEREUM", + Flags: []cli.Flag{ + utils.DataDirFlag, + utils.KeyStoreDirFlag, + utils.NetworkIdFlag, + utils.TestNetFlag, + utils.DevModeFlag, + utils.IdentityFlag, + utils.FastSyncFlag, + utils.LightModeFlag, + utils.LightServFlag, + utils.LightPeersFlag, + utils.LightKDFFlag, + }, + }, + { + Name: "PERFORMANCE TUNING", + Flags: []cli.Flag{ + utils.CacheFlag, + utils.TrieCacheGenFlag, + }, + }, + { + Name: "ACCOUNT", + Flags: []cli.Flag{ + utils.UnlockedAccountFlag, + utils.PasswordFileFlag, + }, + }, + { + Name: "API AND CONSOLE", + Flags: []cli.Flag{ + utils.RPCEnabledFlag, + utils.RPCListenAddrFlag, + utils.RPCPortFlag, + utils.RPCApiFlag, + utils.WSEnabledFlag, + utils.WSListenAddrFlag, + utils.WSPortFlag, + utils.WSApiFlag, + utils.WSAllowedOriginsFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + utils.RPCCORSDomainFlag, + utils.JSpathFlag, + utils.ExecFlag, + utils.PreloadJSFlag, + }, + }, + { + Name: "NETWORKING", + Flags: []cli.Flag{ + utils.BootnodesFlag, + utils.ListenPortFlag, + utils.MaxPeersFlag, + utils.MaxPendingPeersFlag, + utils.NATFlag, + utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + }, + }, + { + Name: "MINER", + Flags: []cli.Flag{ + utils.MiningEnabledFlag, + utils.MinerThreadsFlag, + utils.AutoDAGFlag, + utils.EtherbaseFlag, + utils.TargetGasLimitFlag, + utils.GasPriceFlag, + utils.ExtraDataFlag, + }, + }, + { + Name: "GAS PRICE ORACLE", + Flags: []cli.Flag{ + utils.GpoMinGasPriceFlag, + utils.GpoMaxGasPriceFlag, + utils.GpoFullBlockRatioFlag, + utils.GpobaseStepDownFlag, + utils.GpobaseStepUpFlag, + utils.GpobaseCorrectionFactorFlag, + }, + }, + { + Name: "VIRTUAL MACHINE", + Flags: []cli.Flag{ + utils.VMEnableJitFlag, + utils.VMForceJitFlag, + utils.VMJitCacheFlag, + utils.VMEnableDebugFlag, + }, + }, + { + Name: "LOGGING AND DEBUGGING", + Flags: append([]cli.Flag{ + utils.EthStatsURLFlag, + utils.MetricsEnabledFlag, + utils.FakePoWFlag, + }, debug.Flags...), + }, + { + Name: "EXPERIMENTAL", + Flags: []cli.Flag{ + utils.WhisperEnabledFlag, + }, + }, + { + Name: "MISCELLANEOUS", + Flags: []cli.Flag{ + utils.SolcPathFlag, + }, + }, +} + +func init() { + // Override the default app help template + cli.AppHelpTemplate = AppHelpTemplate + + // Define a one shot struct to pass to the usage template + type helpData struct { + App interface{} + FlagGroups []flagGroup + } + // Override the default app help printer, but only for the global app help + originalHelpPrinter := cli.HelpPrinter + cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) { + if tmpl == AppHelpTemplate { + // Iterate over all the flags and add any uncategorized ones + categorized := make(map[string]struct{}) + for _, group := range AppHelpFlagGroups { + for _, flag := range group.Flags { + categorized[flag.String()] = struct{}{} + } + } + uncategorized := []cli.Flag{} + for _, flag := range data.(*cli.App).Flags { + if _, ok := categorized[flag.String()]; !ok { + uncategorized = append(uncategorized, flag) + } + } + if len(uncategorized) > 0 { + // Append all ungategorized options to the misc group + miscs := len(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags) + AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = append(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags, uncategorized...) + + // Make sure they are removed afterwards + defer func() { + AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags[:miscs] + }() + } + // Render out custom usage screen + originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups}) + } else { + originalHelpPrinter(w, tmpl, data) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go new file mode 100644 index 0000000..9e80ad0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/gethrpctest/main.go @@ -0,0 +1,162 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// gethrpctest is a command to run the external RPC tests. +package main + +import ( + "flag" + "log" + "os" + "os/signal" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/tests" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" +) + +const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" + +var ( + testFile = flag.String("json", "", "Path to the .json test file to load") + testName = flag.String("test", "", "Name of the test from the .json file to run") + testKey = flag.String("key", defaultTestKey, "Private key of a test account to inject") +) + +func main() { + flag.Parse() + + // Enable logging errors, we really do want to see those + glog.SetV(2) + glog.SetToStderr(true) + + // Load the test suite to run the RPC against + tests, err := tests.LoadBlockTests(*testFile) + if err != nil { + log.Fatalf("Failed to load test suite: %v", err) + } + test, found := tests[*testName] + if !found { + log.Fatalf("Requested test (%s) not found within suite", *testName) + } + + stack, err := MakeSystemNode(*testKey, test) + if err != nil { + log.Fatalf("Failed to assemble test stack: %v", err) + } + if err := stack.Start(); err != nil { + log.Fatalf("Failed to start test node: %v", err) + } + defer stack.Stop() + + log.Println("Test node started...") + + // Make sure the tests contained within the suite pass + if err := RunTest(stack, test); err != nil { + log.Fatalf("Failed to run the pre-configured test: %v", err) + } + log.Println("Initial test suite passed...") + + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt) + <-quit +} + +// MakeSystemNode configures a protocol stack for the RPC tests based on a given +// keystore path and initial pre-state. +func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) { + // Create a networkless protocol stack + stack, err := node.New(&node.Config{ + UseLightweightKDF: true, + IPCPath: node.DefaultIPCEndpoint(""), + HTTPHost: node.DefaultHTTPHost, + HTTPPort: node.DefaultHTTPPort, + HTTPModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"}, + WSHost: node.DefaultWSHost, + WSPort: node.DefaultWSPort, + WSModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"}, + NoDiscovery: true, + }) + if err != nil { + return nil, err + } + // Create the keystore and inject an unlocked account if requested + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + if len(privkey) > 0 { + key, err := crypto.HexToECDSA(privkey) + if err != nil { + return nil, err + } + a, err := ks.ImportECDSA(key, "") + if err != nil { + return nil, err + } + if err := ks.Unlock(a, ""); err != nil { + return nil, err + } + } + // Initialize and register the Ethereum protocol + db, _ := ethdb.NewMemDatabase() + if _, err := test.InsertPreState(db); err != nil { + return nil, err + } + ethConf := ð.Config{ + TestGenesisState: db, + TestGenesisBlock: test.Genesis, + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, + } + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { + return nil, err + } + // Initialize and register the Whisper protocol + if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { + return nil, err + } + return stack, nil +} + +// RunTest executes the specified test against an already pre-configured protocol +// stack to ensure basic checks pass before running RPC tests. +func RunTest(stack *node.Node, test *tests.BlockTest) error { + var ethereum *eth.Ethereum + stack.Service(ðereum) + blockchain := ethereum.BlockChain() + + // Process the blocks and verify the imported headers + blocks, err := test.TryBlocksInsert(blockchain) + if err != nil { + return err + } + if err := test.ValidateImportedHeaders(blockchain, blocks); err != nil { + return err + } + // Retrieve the assembled state and validate it + stateDb, err := blockchain.State() + if err != nil { + return err + } + if err := test.ValidatePostState(stateDb); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/internal/browser/browser.go b/vendor/github.com/ethereum/go-ethereum/cmd/internal/browser/browser.go new file mode 100644 index 0000000..897086f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/internal/browser/browser.go @@ -0,0 +1,46 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package browser provides utilities for interacting with users' browsers. +package browser + +import ( + "os" + "os/exec" + "runtime" +) + +// Commands returns a list of possible commands to use to open a url. +func Commands() [][]string { + var cmds [][]string + if exe := os.Getenv("BROWSER"); exe != "" { + cmds = append(cmds, []string{exe}) + } + switch runtime.GOOS { + case "darwin": + cmds = append(cmds, []string{"/usr/bin/open"}) + case "windows": + cmds = append(cmds, []string{"cmd", "/c", "start"}) + default: + cmds = append(cmds, []string{"xdg-open"}) + } + cmds = append(cmds, + []string{"chrome"}, + []string{"google-chrome"}, + []string{"chromium"}, + []string{"firefox"}, + ) + return cmds +} + +// Open tries to open url in a browser and reports whether it succeeded. +func Open(url string) bool { + for _, args := range Commands() { + cmd := exec.Command(args[0], append(args[1:], url)...) + if cmd.Start() == nil { + return true + } + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/rlpdump/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/rlpdump/main.go new file mode 100644 index 0000000..10eea1f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/rlpdump/main.go @@ -0,0 +1,143 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// rlpdump is a pretty-printer for RLP data. +package main + +import ( + "bytes" + "encoding/hex" + "flag" + "fmt" + "io" + "os" + "strings" + + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + hexMode = flag.String("hex", "", "dump given hex data") + noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably") +) + +func init() { + flag.Usage = func() { + fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex ] [filename]") + flag.PrintDefaults() + fmt.Fprintln(os.Stderr, ` +Dumps RLP data from the given file in readable form. +If the filename is omitted, data is read from stdin.`) + } +} + +func main() { + flag.Parse() + + var r io.Reader + switch { + case *hexMode != "": + data, err := hex.DecodeString(*hexMode) + if err != nil { + die(err) + } + r = bytes.NewReader(data) + + case flag.NArg() == 0: + r = os.Stdin + + case flag.NArg() == 1: + fd, err := os.Open(flag.Arg(0)) + if err != nil { + die(err) + } + defer fd.Close() + r = fd + + default: + fmt.Fprintln(os.Stderr, "Error: too many arguments") + flag.Usage() + os.Exit(2) + } + + s := rlp.NewStream(r, 0) + for { + if err := dump(s, 0); err != nil { + if err != io.EOF { + die(err) + } + break + } + fmt.Println() + } +} + +func dump(s *rlp.Stream, depth int) error { + kind, size, err := s.Kind() + if err != nil { + return err + } + switch kind { + case rlp.Byte, rlp.String: + str, err := s.Bytes() + if err != nil { + return err + } + if len(str) == 0 || !*noASCII && isASCII(str) { + fmt.Printf("%s%q", ws(depth), str) + } else { + fmt.Printf("%s%x", ws(depth), str) + } + case rlp.List: + s.List() + defer s.ListEnd() + if size == 0 { + fmt.Print(ws(depth) + "[]") + } else { + fmt.Println(ws(depth) + "[") + for i := 0; ; i++ { + if i > 0 { + fmt.Print(",\n") + } + if err := dump(s, depth+1); err == rlp.EOL { + break + } else if err != nil { + return err + } + } + fmt.Print(ws(depth) + "]") + } + } + return nil +} + +func isASCII(b []byte) bool { + for _, c := range b { + if c < 32 || c > 126 { + return false + } + } + return true +} + +func ws(n int) string { + return strings.Repeat(" ", n) +} + +func die(args ...interface{}) { + fmt.Fprintln(os.Stderr, args...) + os.Exit(1) +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go new file mode 100644 index 0000000..81636ad --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/cleandb.go @@ -0,0 +1,39 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "log" + + "github.com/ethereum/go-ethereum/swarm/storage" + "gopkg.in/urfave/cli.v1" +) + +func cleandb(ctx *cli.Context) { + args := ctx.Args() + if len(args) != 1 { + log.Fatal("need path to chunks database as the first and only argument") + } + + chunkDbPath := args[0] + hash := storage.MakeHashFunc("SHA3") + dbStore, err := storage.NewDbStore(chunkDbPath, hash, 10000000, 0) + if err != nil { + log.Fatalf("cannot initialise dbstore: %v", err) + } + dbStore.Cleanup() +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/hash.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/hash.go new file mode 100644 index 0000000..bcba77a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/hash.go @@ -0,0 +1,49 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzhash computes a swarm tree hash. +package main + +import ( + "fmt" + "log" + "os" + + "github.com/ethereum/go-ethereum/swarm/storage" + "gopkg.in/urfave/cli.v1" +) + +func hash(ctx *cli.Context) { + args := ctx.Args() + if len(args) < 1 { + log.Fatal("Usage: swarm hash ") + } + f, err := os.Open(args[0]) + if err != nil { + fmt.Println("Error opening file " + args[1]) + os.Exit(1) + } + defer f.Close() + + stat, _ := f.Stat() + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + key, err := chunker.Split(f, stat.Size(), nil, nil, nil) + if err != nil { + log.Fatalf("%v\n", err) + } else { + fmt.Printf("%v\n", key) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go new file mode 100644 index 0000000..5661b3f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/main.go @@ -0,0 +1,408 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "os" + "os/signal" + "runtime" + "strconv" + "strings" + "syscall" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm" + bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "gopkg.in/urfave/cli.v1" +) + +const ( + clientIdentifier = "swarm" + versionString = "0.2" +) + +var ( + gitCommit string // Git SHA1 commit hash of the release (set via linker flags) + app = utils.NewApp(gitCommit, "Ethereum Swarm") + testbetBootNodes = []string{ + "enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429", + "enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430", + "enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431", + "enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432", + "enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433", + } +) + +var ( + ChequebookAddrFlag = cli.StringFlag{ + Name: "chequebook", + Usage: "chequebook contract address", + } + SwarmAccountFlag = cli.StringFlag{ + Name: "bzzaccount", + Usage: "Swarm account key file", + } + SwarmPortFlag = cli.StringFlag{ + Name: "bzzport", + Usage: "Swarm local http api port", + } + SwarmNetworkIdFlag = cli.IntFlag{ + Name: "bzznetworkid", + Usage: "Network identifier (integer, default 3=swarm testnet)", + } + SwarmConfigPathFlag = cli.StringFlag{ + Name: "bzzconfig", + Usage: "Swarm config file path (datadir/bzz)", + } + SwarmSwapEnabledFlag = cli.BoolFlag{ + Name: "swap", + Usage: "Swarm SWAP enabled (default false)", + } + SwarmSyncEnabledFlag = cli.BoolTFlag{ + Name: "sync", + Usage: "Swarm Syncing enabled (default true)", + } + EthAPIFlag = cli.StringFlag{ + Name: "ethapi", + Usage: "URL of the Ethereum API provider", + Value: node.DefaultIPCEndpoint("geth"), + } + SwarmApiFlag = cli.StringFlag{ + Name: "bzzapi", + Usage: "Swarm HTTP endpoint", + Value: "http://127.0.0.1:8500", + } + SwarmRecursiveUploadFlag = cli.BoolFlag{ + Name: "recursive", + Usage: "Upload directories recursively", + } + SwarmWantManifestFlag = cli.BoolTFlag{ + Name: "manifest", + Usage: "Automatic manifest upload", + } + SwarmUploadDefaultPath = cli.StringFlag{ + Name: "defaultpath", + Usage: "path to file served for empty url path (none)", + } + CorsStringFlag = cli.StringFlag{ + Name: "corsdomain", + Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')", + } +) + +func init() { + // Override flag defaults so bzzd can run alongside geth. + utils.ListenPortFlag.Value = 30399 + utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} + utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3" + + // Set up the cli app. + app.Action = bzzd + app.HideVersion = true // we have a command to print the version + app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" + app.Commands = []cli.Command{ + { + Action: version, + Name: "version", + Usage: "Print version numbers", + ArgsUsage: " ", + Description: ` +The output of this command is supposed to be machine-readable. +`, + }, + { + Action: upload, + Name: "up", + Usage: "upload a file or directory to swarm using the HTTP API", + ArgsUsage: " ", + Description: ` +"upload a file or directory to swarm using the HTTP API and prints the root hash", +`, + }, + { + Action: hash, + Name: "hash", + Usage: "print the swarm hash of a file or directory", + ArgsUsage: " ", + Description: ` +Prints the swarm hash of file or directory. +`, + }, + { + Name: "manifest", + Usage: "update a MANIFEST", + ArgsUsage: "manifest COMMAND", + Description: ` +Updates a MANIFEST by adding/removing/updating the hash of a path. +`, + Subcommands: []cli.Command{ + { + Action: add, + Name: "add", + Usage: "add a new path to the manifest", + ArgsUsage: " []", + Description: ` +Adds a new path to the manifest +`, + }, + { + Action: update, + Name: "update", + Usage: "update the hash for an already existing path in the manifest", + ArgsUsage: " []", + Description: ` +Update the hash for an already existing path in the manifest +`, + }, + { + Action: remove, + Name: "remove", + Usage: "removes a path from the manifest", + ArgsUsage: " ", + Description: ` +Removes a path from the manifest +`, + }, + }, + }, + { + Action: cleandb, + Name: "cleandb", + Usage: "Cleans database of corrupted entries", + ArgsUsage: " ", + Description: ` +Cleans database of corrupted entries. +`, + }, + } + + app.Flags = []cli.Flag{ + utils.IdentityFlag, + utils.DataDirFlag, + utils.BootnodesFlag, + utils.KeyStoreDirFlag, + utils.ListenPortFlag, + utils.NoDiscoverFlag, + utils.DiscoveryV5Flag, + utils.NetrestrictFlag, + utils.NodeKeyFileFlag, + utils.NodeKeyHexFlag, + utils.MaxPeersFlag, + utils.NATFlag, + utils.IPCDisabledFlag, + utils.IPCApiFlag, + utils.IPCPathFlag, + // bzzd-specific flags + CorsStringFlag, + EthAPIFlag, + SwarmConfigPathFlag, + SwarmSwapEnabledFlag, + SwarmSyncEnabledFlag, + SwarmPortFlag, + SwarmAccountFlag, + SwarmNetworkIdFlag, + ChequebookAddrFlag, + // upload flags + SwarmApiFlag, + SwarmRecursiveUploadFlag, + SwarmWantManifestFlag, + SwarmUploadDefaultPath, + } + app.Flags = append(app.Flags, debug.Flags...) + app.Before = func(ctx *cli.Context) error { + runtime.GOMAXPROCS(runtime.NumCPU()) + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil + } +} + +func main() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func version(ctx *cli.Context) error { + fmt.Println(strings.Title(clientIdentifier)) + fmt.Println("Version:", versionString) + if gitCommit != "" { + fmt.Println("Git Commit:", gitCommit) + } + fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) + fmt.Println("Go Version:", runtime.Version()) + fmt.Println("OS:", runtime.GOOS) + fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) + fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) + return nil +} + +func bzzd(ctx *cli.Context) error { + stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + registerBzzService(ctx, stack) + utils.StartNode(stack) + go func() { + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, syscall.SIGTERM) + defer signal.Stop(sigc) + <-sigc + glog.V(logger.Info).Infoln("Got sigterm, shutting down...") + stack.Stop() + }() + networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name) + // Add bootnodes as initial peers. + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") + injectBootnodes(stack.Server(), bootnodes) + } else { + if networkId == 3 { + injectBootnodes(stack.Server(), testbetBootNodes) + } + } + + stack.Wait() + return nil +} + +func registerBzzService(ctx *cli.Context, stack *node.Node) { + + prvkey := getAccount(ctx, stack) + + chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) + bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name) + if bzzdir == "" { + bzzdir = stack.InstanceDir() + } + + bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name)) + if err != nil { + utils.Fatalf("unable to configure swarm: %v", err) + } + bzzport := ctx.GlobalString(SwarmPortFlag.Name) + if len(bzzport) > 0 { + bzzconfig.Port = bzzport + } + swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name) + syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name) + + ethapi := ctx.GlobalString(EthAPIFlag.Name) + cors := ctx.GlobalString(CorsStringFlag.Name) + + boot := func(ctx *node.ServiceContext) (node.Service, error) { + var client *ethclient.Client + if len(ethapi) > 0 { + client, err = ethclient.Dial(ethapi) + if err != nil { + utils.Fatalf("Can't connect: %v", err) + } + } + return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors) + } + if err := stack.Register(boot); err != nil { + utils.Fatalf("Failed to register the Swarm service: %v", err) + } +} + +func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { + keyid := ctx.GlobalString(SwarmAccountFlag.Name) + + if keyid == "" { + utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) + } + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(keyid); err == nil { + glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey)) + return key + } + // Otherwise try getting it from the keystore. + am := stack.AccountManager() + ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + return decryptStoreAccount(ks, keyid) +} + +func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey { + var a accounts.Account + var err error + if common.IsHexAddress(account) { + a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)}) + } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 { + if accounts := ks.Accounts(); len(accounts) > ix { + a = accounts[ix] + } else { + err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts)) + } + } else { + utils.Fatalf("Can't find swarm account key %s", account) + } + if err != nil { + utils.Fatalf("Can't find swarm account key: %v", err) + } + keyjson, err := ioutil.ReadFile(a.URL.Path) + if err != nil { + utils.Fatalf("Can't load swarm account key: %v", err) + } + for i := 1; i <= 3; i++ { + passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) + key, err := keystore.DecryptKey(keyjson, passphrase) + if err == nil { + return key.PrivateKey + } + } + utils.Fatalf("Can't decrypt swarm account key") + return nil +} + +func promptPassphrase(prompt string) string { + if prompt != "" { + fmt.Println(prompt) + } + password, err := console.Stdin.PromptPassword("Passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + return password +} + +func injectBootnodes(srv *p2p.Server, nodes []string) { + for _, url := range nodes { + n, err := discover.ParseNode(url) + if err != nil { + glog.Errorf("invalid bootnode %q", err) + continue + } + srv.AddPeer(n) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go new file mode 100644 index 0000000..0de0d69 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/manifest.go @@ -0,0 +1,360 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command MANIFEST update +package main + +import ( + "gopkg.in/urfave/cli.v1" + "log" + "mime" + "path/filepath" + "strings" + "fmt" + "encoding/json" +) + +func add(ctx *cli.Context) { + + args := ctx.Args() + if len(args) < 3 { + log.Fatal("need atleast three arguments []") + } + + var ( + mhash = args[0] + path = args[1] + hash = args[2] + + ctype string + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + + + if len(args) > 3 { + ctype = args[3] + } else { + ctype = mime.TypeByExtension(filepath.Ext(path)) + } + + newManifest := addEntryToManifest (ctx, mhash, path, hash, ctype) + fmt.Println(newManifest) + + if !wantManifest { + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) + return + } +} + +func update(ctx *cli.Context) { + + args := ctx.Args() + if len(args) < 3 { + log.Fatal("need atleast three arguments ") + } + + var ( + mhash = args[0] + path = args[1] + hash = args[2] + + ctype string + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + if len(args) > 3 { + ctype = args[3] + } else { + ctype = mime.TypeByExtension(filepath.Ext(path)) + } + + newManifest := updateEntryInManifest (ctx, mhash, path, hash, ctype) + fmt.Println(newManifest) + + if !wantManifest { + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) + return + } +} + +func remove(ctx *cli.Context) { + args := ctx.Args() + if len(args) < 2 { + log.Fatal("need atleast two arguments ") + } + + var ( + mhash = args[0] + path = args[1] + + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + mroot manifest + ) + + newManifest := removeEntryFromManifest (ctx, mhash, path) + fmt.Println(newManifest) + + if !wantManifest { + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) + return + } +} + +func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) string { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", err) + } + + //TODO: check if the "hash" to add is valid and present in swarm + _, err = client.downloadManifest(hash) + if err != nil { + log.Fatalln("hash to add is not present:", err) + } + + + // See if we path is in this Manifest or do we have to dig deeper + for _, entry := range mroot.Entries { + if path == entry.Path { + log.Fatal(path, "Already present, not adding anything") + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path != "" { + // Load the child Manifest add the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := addEntryToManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + } + mroot = newMRoot + } else { + // Add the entry in the leaf Manifest + newEntry := manifestEntry{ + Path: path, + Hash: hash, + ContentType: ctype, + } + mroot.Entries = append(mroot.Entries, newEntry) + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash + + + +} + +func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string) string { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + newEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", err) + } + + //TODO: check if the "hash" with which to update is valid and present in swarm + + + // See if we path is in this Manifest or do we have to dig deeper + for _, entry := range mroot.Entries { + if path == entry.Path { + newEntry = entry + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path == "" && newEntry.Path == "" { + log.Fatal(path, " Path not present in the Manifest, not setting anything") + } + + if longestPathEntry.Path != "" { + // Load the child Manifest add the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := updateEntryInManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + + } + mroot = newMRoot + } + + if newEntry.Path != "" { + // Replace the hash for leaf Manifest + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if newEntry.Path == entry.Path { + myEntry := manifestEntry{ + Path: entry.Path, + Hash: hash, + ContentType: ctype, + } + newMRoot.Entries = append(newMRoot.Entries, myEntry) + } else { + newMRoot.Entries = append(newMRoot.Entries, entry) + } + } + mroot = newMRoot + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash +} + +func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string { + + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = &client{api: bzzapi} + entryToRemove = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + longestPathEntry = manifestEntry{ + Path: "", + Hash: "", + ContentType: "", + } + ) + + mroot, err := client.downloadManifest(mhash) + if err != nil { + log.Fatalln("manifest download failed:", err) + } + + + + // See if we path is in this Manifest or do we have to dig deeper + for _, entry := range mroot.Entries { + if path == entry.Path { + entryToRemove = entry + }else { + if entry.ContentType == "application/bzz-manifest+json" { + prfxlen := strings.HasPrefix(path, entry.Path) + if prfxlen && len(path) > len(longestPathEntry.Path) { + longestPathEntry = entry + } + } + } + } + + if longestPathEntry.Path == "" && entryToRemove.Path == "" { + log.Fatal(path, "Path not present in the Manifest, not removing anything") + } + + if longestPathEntry.Path != "" { + // Load the child Manifest remove the entry there + newPath := path[len(longestPathEntry.Path):] + newHash := removeEntryFromManifest (ctx, longestPathEntry.Hash, newPath) + + // Replace the hash for parent Manifests + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if longestPathEntry.Path == entry.Path { + entry.Hash = newHash + } + newMRoot.Entries = append(newMRoot.Entries, entry) + } + mroot = newMRoot + } + + if entryToRemove.Path != "" { + // remove the entry in this Manifest + newMRoot := manifest{} + for _, entry := range mroot.Entries { + if entryToRemove.Path != entry.Path { + newMRoot.Entries = append(newMRoot.Entries, entry) + } + } + mroot = newMRoot + } + + + newManifestHash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + return newManifestHash + + +} + diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go new file mode 100644 index 0000000..871713b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/swarm/upload.go @@ -0,0 +1,257 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Command bzzup uploads files to the swarm HTTP API. +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "net/http" + "os" + "os/user" + "path" + "path/filepath" + "strings" + + "gopkg.in/urfave/cli.v1" +) + +func upload(ctx *cli.Context) { + args := ctx.Args() + var ( + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name) + ) + if len(args) != 1 { + log.Fatal("need filename as the first and only argument") + } + + var ( + file = args[0] + client = &client{api: bzzapi} + ) + fi, err := os.Stat(expandPath(file)) + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + if !recursive { + log.Fatal("argument is a directory and recursive upload is disabled") + } + if !wantManifest { + log.Fatal("manifest is required for directory uploads") + } + mhash, err := client.uploadDirectory(file, defaultPath) + if err != nil { + log.Fatal(err) + } + fmt.Println(mhash) + return + } + entry, err := client.uploadFile(file, fi) + if err != nil { + log.Fatalln("upload failed:", err) + } + mroot := manifest{[]manifestEntry{entry}} + if !wantManifest { + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) + return + } + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + fmt.Println(hash) +} + +// Expands a file path +// 1. replace tilde with users home dir +// 2. expands embedded environment variables +// 3. cleans the path, e.g. /a/b/../c -> /a/c +// Note, it has limitations, e.g. ~someuser/tmp will not be expanded +func expandPath(p string) string { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + if home := homeDir(); home != "" { + p = home + p[1:] + } + } + return path.Clean(os.ExpandEnv(p)) +} + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} + +// client wraps interaction with the swarm HTTP gateway. +type client struct { + api string +} + +// manifest is the JSON representation of a swarm manifest. +type manifestEntry struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` +} + +// manifest is the JSON representation of a swarm manifest. +type manifest struct { + Entries []manifestEntry `json:"entries,omitempty"` +} + +func (c *client) uploadDirectory(dir string, defaultPath string) (string, error) { + mhash, err := c.postRaw("application/json", 2, ioutil.NopCloser(bytes.NewReader([]byte("{}")))) + if err != nil { + return "", fmt.Errorf("failed to upload empty manifest") + } + if len(defaultPath) > 0 { + fi, err := os.Stat(defaultPath) + if err != nil { + return "", err + } + mhash, err = c.uploadToManifest(mhash, "", defaultPath, fi) + if err != nil { + return "", err + } + } + prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" + err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || fi.IsDir() { + return err + } + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("path %s outside directory %s", path, dir) + } + uripath := strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) + mhash, err = c.uploadToManifest(mhash, uripath, path, fi) + return err + }) + return mhash, err +} + +func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) { + hash, err := c.uploadFileContent(file, fi) + m := manifestEntry{ + Hash: hash, + ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), + } + return m, err +} + +func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { + fd, err := os.Open(file) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes)", file, fi.Size()) + return c.postRaw("application/octet-stream", fi.Size(), fd) +} + +func (c *client) uploadManifest(m manifest) (string, error) { + jsm, err := json.Marshal(m) + if err != nil { + panic(err) + } + log.Println("uploading manifest") + return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) +} + +func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os.FileInfo) (string, error) { + fd, err := os.Open(fpath) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes) and adding path %v", fpath, fi.Size(), path) + req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd) + if err != nil { + return "", err + } + req.Header.Set("content-type", mime.TypeByExtension(filepath.Ext(fi.Name()))) + req.ContentLength = fi.Size() + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} + +func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { + req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) + if err != nil { + return "", err + } + req.Header.Set("content-type", mimetype) + req.ContentLength = size + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} + +func (c *client) downloadManifest(mhash string) (manifest, error) { + + mroot := manifest{} + req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil) + if err != nil { + return mroot, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return mroot, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return mroot, fmt.Errorf("bad status: %s", resp.Status) + + } + content, err := ioutil.ReadAll(resp.Body) + + err = json.Unmarshal(content, &mroot) + if err != nil { + return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err) + } + return mroot, err +} \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go new file mode 100644 index 0000000..8666f37 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/cmd.go @@ -0,0 +1,242 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. +package utils + +import ( + "compress/gzip" + "fmt" + "io" + "os" + "os/signal" + "regexp" + "runtime" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + importBatchSize = 2500 +) + +func openLogFile(Datadir string, filename string) *os.File { + path := common.AbsolutePath(Datadir, filename) + file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + panic(fmt.Sprintf("error opening log file '%s': %v", filename, err)) + } + return file +} + +// Fatalf formats a message to standard error and exits the program. +// The message is also printed to standard output if standard error +// is redirected to a different file. +func Fatalf(format string, args ...interface{}) { + w := io.MultiWriter(os.Stdout, os.Stderr) + if runtime.GOOS == "windows" { + // The SameFile check below doesn't work on Windows. + // stdout is unlikely to get redirected though, so just print there. + w = os.Stdout + } else { + outf, _ := os.Stdout.Stat() + errf, _ := os.Stderr.Stat() + if outf != nil && errf != nil && os.SameFile(outf, errf) { + w = os.Stderr + } + } + fmt.Fprintf(w, "Fatal: "+format+"\n", args...) + os.Exit(1) +} + +func StartNode(stack *node.Node) { + if err := stack.Start(); err != nil { + Fatalf("Error starting protocol stack: %v", err) + } + go func() { + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt) + defer signal.Stop(sigc) + <-sigc + glog.V(logger.Info).Infoln("Got interrupt, shutting down...") + go stack.Stop() + for i := 10; i > 0; i-- { + <-sigc + if i > 1 { + glog.V(logger.Info).Infof("Already shutting down, interrupt %d more times for panic.", i-1) + } + } + debug.Exit() // ensure trace and CPU profile data is flushed. + debug.LoudPanic("boom") + }() +} + +func FormatTransactionData(data string) []byte { + d := common.StringToByteFunc(data, func(s string) (ret []byte) { + slice := regexp.MustCompile(`\n|\s`).Split(s, 1000000000) + for _, dataItem := range slice { + d := common.FormatData(dataItem) + ret = append(ret, d...) + } + return + }) + + return d +} + +func ImportChain(chain *core.BlockChain, fn string) error { + // Watch for Ctrl-C while the import is running. + // If a signal is received, the import will stop at the next batch. + interrupt := make(chan os.Signal, 1) + stop := make(chan struct{}) + signal.Notify(interrupt, os.Interrupt) + defer signal.Stop(interrupt) + defer close(interrupt) + go func() { + if _, ok := <-interrupt; ok { + glog.Info("caught interrupt during import, will stop at next batch") + } + close(stop) + }() + checkInterrupt := func() bool { + select { + case <-stop: + return true + default: + return false + } + } + + glog.Infoln("Importing blockchain ", fn) + fh, err := os.Open(fn) + if err != nil { + return err + } + defer fh.Close() + + var reader io.Reader = fh + if strings.HasSuffix(fn, ".gz") { + if reader, err = gzip.NewReader(reader); err != nil { + return err + } + } + + stream := rlp.NewStream(reader, 0) + + // Run actual the import. + blocks := make(types.Blocks, importBatchSize) + n := 0 + for batch := 0; ; batch++ { + // Load a batch of RLP blocks. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + i := 0 + for ; i < importBatchSize; i++ { + var b types.Block + if err := stream.Decode(&b); err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("at block %d: %v", n, err) + } + // don't import first block + if b.NumberU64() == 0 { + i-- + continue + } + blocks[i] = &b + n++ + } + if i == 0 { + break + } + // Import the batch. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + if hasAllBlocks(chain, blocks[:i]) { + glog.Infof("skipping batch %d, all blocks present [%x / %x]", + batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4]) + continue + } + + if _, err := chain.InsertChain(blocks[:i]); err != nil { + return fmt.Errorf("invalid block %d: %v", n, err) + } + } + return nil +} + +func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { + for _, b := range bs { + if !chain.HasBlock(b.Hash()) { + return false + } + } + return true +} + +func ExportChain(blockchain *core.BlockChain, fn string) error { + glog.Infoln("Exporting blockchain to ", fn) + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + + if err := blockchain.Export(writer); err != nil { + return err + } + glog.Infoln("Exported blockchain to ", fn) + + return nil +} + +func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error { + glog.Infoln("Exporting blockchain to ", fn) + // TODO verify mode perms + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + + if err := blockchain.ExportN(writer, first, last); err != nil { + return err + } + glog.Infoln("Exported blockchain to ", fn) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go new file mode 100644 index 0000000..52cd7b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go @@ -0,0 +1,147 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package utils + +import ( + "flag" + "fmt" + "os" + "os/user" + "path" + "strings" +) + +// Custom type which is registered in the flags library which cli uses for +// argument parsing. This allows us to expand Value to an absolute path when +// the argument is parsed +type DirectoryString struct { + Value string +} + +func (self *DirectoryString) String() string { + return self.Value +} + +func (self *DirectoryString) Set(value string) error { + self.Value = expandPath(value) + return nil +} + +// Custom cli.Flag type which expand the received string to an absolute path. +// e.g. ~/.ethereum -> /home/username/.ethereum +type DirectoryFlag struct { + Name string + Value DirectoryString + Usage string + EnvVar string +} + +func (self DirectoryFlag) String() string { + fmtString := "%s %v\t%v" + if len(self.Value.Value) > 0 { + fmtString = "%s \"%v\"\t%v" + } + return withEnvHint(self.EnvVar, fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage)) +} + +func eachName(longName string, fn func(string)) { + parts := strings.Split(longName, ",") + for _, name := range parts { + name = strings.Trim(name, " ") + fn(name) + } +} + +// called by cli library, grabs variable from environment (if in env) +// and adds variable to flag set for parsing. +func (self DirectoryFlag) Apply(set *flag.FlagSet) { + if self.EnvVar != "" { + for _, envVar := range strings.Split(self.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + self.Value.Value = envVal + break + } + } + } + + eachName(self.Name, func(name string) { + set.Var(&self.Value, self.Name, self.Usage) + }) +} + +func prefixFor(name string) (prefix string) { + if len(name) == 1 { + prefix = "-" + } else { + prefix = "--" + } + + return +} + +func prefixedNames(fullName string) (prefixed string) { + parts := strings.Split(fullName, ",") + for i, name := range parts { + name = strings.Trim(name, " ") + prefixed += prefixFor(name) + name + if i < len(parts)-1 { + prefixed += ", " + } + } + return +} + +func withEnvHint(envVar, str string) string { + envText := "" + if envVar != "" { + envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $")) + } + return str + envText +} + +func (self DirectoryFlag) GetName() string { + return self.Name +} + +func (self *DirectoryFlag) Set(value string) { + self.Value.Value = value +} + +// Expands a file path +// 1. replace tilde with users home dir +// 2. expands embedded environment variables +// 3. cleans the path, e.g. /a/b/../c -> /a/c +// Note, it has limitations, e.g. ~someuser/tmp will not be expanded +func expandPath(p string) string { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + if home := homeDir(); home != "" { + p = home + p[1:] + } + } + return path.Clean(os.ExpandEnv(p)) +} + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags_test.go new file mode 100644 index 0000000..de39ca3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags_test.go @@ -0,0 +1,41 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package utils + +import ( + "os" + "os/user" + "testing" +) + +func TestPathExpansion(t *testing.T) { + user, _ := user.Current() + tests := map[string]string{ + "/home/someuser/tmp": "/home/someuser/tmp", + "~/tmp": user.HomeDir + "/tmp", + "~thisOtherUser/b/": "~thisOtherUser/b", + "$DDDXXX/a/b": "/tmp/a/b", + "/a/b/": "/a/b", + } + os.Setenv("DDDXXX", "/tmp") + for test, expected := range tests { + got := expandPath(test) + if got != expected { + t.Errorf("test %s, got %s, expected %s\n", test, got, expected) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_freebsd.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_freebsd.go new file mode 100644 index 0000000..4cb5013 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_freebsd.go @@ -0,0 +1,54 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// +build freebsd + +package utils + +import "syscall" + +// This file is largely identical to fdlimit_unix.go, +// but Rlimit fields have type int64 on FreeBSD so it needs +// an extra conversion. + +// raiseFdLimit tries to maximize the file descriptor allowance of this process +// to the maximum hard-limit allowed by the OS. +func raiseFdLimit(max uint64) error { + // Get the current limit + var limit syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return err + } + // Try to update the limit to the max allowance + limit.Cur = limit.Max + if limit.Cur > int64(max) { + limit.Cur = int64(max) + } + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return err + } + return nil +} + +// getFdLimit retrieves the number of file descriptors allowed to be opened by this +// process. +func getFdLimit() (int, error) { + var limit syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return 0, err + } + return int(limit.Cur), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_test.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_test.go new file mode 100644 index 0000000..0a950a6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_test.go @@ -0,0 +1,35 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package utils + +import "testing" + +// TestFileDescriptorLimits simply tests whether the file descriptor allowance +// per this process can be retrieved. +func TestFileDescriptorLimits(t *testing.T) { + target := 4096 + + if limit, err := getFdLimit(); err != nil || limit <= 0 { + t.Fatalf("failed to retrieve file descriptor limit (%d): %v", limit, err) + } + if err := raiseFdLimit(uint64(target)); err != nil { + t.Fatalf("failed to raise file allowance") + } + if limit, err := getFdLimit(); err != nil || limit < target { + t.Fatalf("failed to retrieve raised descriptor limit (have %v, want %v): %v", limit, target, err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_unix.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_unix.go new file mode 100644 index 0000000..08e153b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_unix.go @@ -0,0 +1,50 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// +build linux darwin netbsd openbsd solaris + +package utils + +import "syscall" + +// raiseFdLimit tries to maximize the file descriptor allowance of this process +// to the maximum hard-limit allowed by the OS. +func raiseFdLimit(max uint64) error { + // Get the current limit + var limit syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return err + } + // Try to update the limit to the max allowance + limit.Cur = limit.Max + if limit.Cur > max { + limit.Cur = max + } + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return err + } + return nil +} + +// getFdLimit retrieves the number of file descriptors allowed to be opened by this +// process. +func getFdLimit() (int, error) { + var limit syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + return 0, err + } + return int(limit.Cur), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_windows.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_windows.go new file mode 100644 index 0000000..53aad3d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/fdlimit_windows.go @@ -0,0 +1,41 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package utils + +import "errors" + +// raiseFdLimit tries to maximize the file descriptor allowance of this process +// to the maximum hard-limit allowed by the OS. +func raiseFdLimit(max uint64) error { + // This method is NOP by design: + // * Linux/Darwin counterparts need to manually increase per process limits + // * On Windows Go uses the CreateFile API, which is limited to 16K files, non + // changeable from within a running process + // This way we can always "request" raising the limits, which will either have + // or not have effect based on the platform we're running on. + if max > 16384 { + return errors.New("file descriptor limit (16384) reached") + } + return nil +} + +// getFdLimit retrieves the number of file descriptors allowed to be opened by this +// process. +func getFdLimit() (int, error) { + // Please see raiseFdLimit for the reason why we use hard coded 16K as the limit + return 16384, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go new file mode 100644 index 0000000..92eb05e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -0,0 +1,948 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. +package utils + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rpc" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" + "gopkg.in/urfave/cli.v1" +) + +func init() { + cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] + +VERSION: + {{.Version}} + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` + + cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...] +{{if .Description}}{{.Description}} +{{end}}{{if .Subcommands}} +SUBCOMMANDS: + {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{end}}{{if .Flags}} +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` +} + +// NewApp creates an app with sane defaults. +func NewApp(gitCommit, usage string) *cli.App { + app := cli.NewApp() + app.Name = filepath.Base(os.Args[0]) + app.Author = "" + //app.Authors = nil + app.Email = "" + app.Version = params.Version + if gitCommit != "" { + app.Version += "-" + gitCommit[:8] + } + app.Usage = usage + return app +} + +// These are all the command line flags we support. +// If you add to this list, please remember to include the +// flag in the appropriate command definition. +// +// The flags are defined here so their names and help texts +// are the same for all commands. + +var ( + // General settings + DataDirFlag = DirectoryFlag{ + Name: "datadir", + Usage: "Data directory for the databases and keystore", + Value: DirectoryString{node.DefaultDataDir()}, + } + KeyStoreDirFlag = DirectoryFlag{ + Name: "keystore", + Usage: "Directory for the keystore (default = inside the datadir)", + } + NetworkIdFlag = cli.IntFlag{ + Name: "networkid", + Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)", + Value: eth.NetworkId, + } + TestNetFlag = cli.BoolFlag{ + Name: "testnet", + Usage: "Ropsten network: pre-configured test network", + } + DevModeFlag = cli.BoolFlag{ + Name: "dev", + Usage: "Developer mode: pre-configured private network with several debugging flags", + } + IdentityFlag = cli.StringFlag{ + Name: "identity", + Usage: "Custom node name", + } + DocRootFlag = DirectoryFlag{ + Name: "docroot", + Usage: "Document Root for HTTPClient file scheme", + Value: DirectoryString{homeDir()}, + } + FastSyncFlag = cli.BoolFlag{ + Name: "fast", + Usage: "Enable fast syncing through state downloads", + } + LightModeFlag = cli.BoolFlag{ + Name: "light", + Usage: "Enable light client mode", + } + LightServFlag = cli.IntFlag{ + Name: "lightserv", + Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", + Value: 0, + } + LightPeersFlag = cli.IntFlag{ + Name: "lightpeers", + Usage: "Maximum number of LES client peers", + Value: 20, + } + LightKDFFlag = cli.BoolFlag{ + Name: "lightkdf", + Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", + } + // Performance tuning settings + CacheFlag = cli.IntFlag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", + Value: 128, + } + TrieCacheGenFlag = cli.IntFlag{ + Name: "trie-cache-gens", + Usage: "Number of trie node generations to keep in memory", + Value: int(state.MaxTrieCacheGen), + } + // Miner settings + MiningEnabledFlag = cli.BoolFlag{ + Name: "mine", + Usage: "Enable mining", + } + MinerThreadsFlag = cli.IntFlag{ + Name: "minerthreads", + Usage: "Number of CPU threads to use for mining", + Value: runtime.NumCPU(), + } + TargetGasLimitFlag = cli.StringFlag{ + Name: "targetgaslimit", + Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", + Value: params.GenesisGasLimit.String(), + } + AutoDAGFlag = cli.BoolFlag{ + Name: "autodag", + Usage: "Enable automatic DAG pregeneration", + } + EtherbaseFlag = cli.StringFlag{ + Name: "etherbase", + Usage: "Public address for block mining rewards (default = first account created)", + Value: "0", + } + GasPriceFlag = cli.StringFlag{ + Name: "gasprice", + Usage: "Minimal gas price to accept for mining a transactions", + Value: new(big.Int).Mul(big.NewInt(20), common.Shannon).String(), + } + ExtraDataFlag = cli.StringFlag{ + Name: "extradata", + Usage: "Block extra data set by the miner (default = client version)", + } + // Account settings + UnlockedAccountFlag = cli.StringFlag{ + Name: "unlock", + Usage: "Comma separated list of accounts to unlock", + Value: "", + } + PasswordFileFlag = cli.StringFlag{ + Name: "password", + Usage: "Password file to use for non-inteactive password input", + Value: "", + } + + VMForceJitFlag = cli.BoolFlag{ + Name: "forcejit", + Usage: "Force the JIT VM to take precedence", + } + VMJitCacheFlag = cli.IntFlag{ + Name: "jitcache", + Usage: "Amount of cached JIT VM programs", + Value: 64, + } + VMEnableJitFlag = cli.BoolFlag{ + Name: "jitvm", + Usage: "Enable the JIT VM", + } + VMEnableDebugFlag = cli.BoolFlag{ + Name: "vmdebug", + Usage: "Record information useful for VM and contract debugging", + } + // Logging and debug settings + EthStatsURLFlag = cli.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + } + MetricsEnabledFlag = cli.BoolFlag{ + Name: metrics.MetricsEnabledFlag, + Usage: "Enable metrics collection and reporting", + } + FakePoWFlag = cli.BoolFlag{ + Name: "fakepow", + Usage: "Disables proof-of-work verification", + } + + // RPC settings + RPCEnabledFlag = cli.BoolFlag{ + Name: "rpc", + Usage: "Enable the HTTP-RPC server", + } + RPCListenAddrFlag = cli.StringFlag{ + Name: "rpcaddr", + Usage: "HTTP-RPC server listening interface", + Value: node.DefaultHTTPHost, + } + RPCPortFlag = cli.IntFlag{ + Name: "rpcport", + Usage: "HTTP-RPC server listening port", + Value: node.DefaultHTTPPort, + } + RPCCORSDomainFlag = cli.StringFlag{ + Name: "rpccorsdomain", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", + Value: "", + } + RPCApiFlag = cli.StringFlag{ + Name: "rpcapi", + Usage: "API's offered over the HTTP-RPC interface", + Value: rpc.DefaultHTTPApis, + } + IPCDisabledFlag = cli.BoolFlag{ + Name: "ipcdisable", + Usage: "Disable the IPC-RPC server", + } + IPCApiFlag = cli.StringFlag{ + Name: "ipcapi", + Usage: "APIs offered over the IPC-RPC interface", + Value: rpc.DefaultIPCApis, + } + IPCPathFlag = DirectoryFlag{ + Name: "ipcpath", + Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", + Value: DirectoryString{"geth.ipc"}, + } + WSEnabledFlag = cli.BoolFlag{ + Name: "ws", + Usage: "Enable the WS-RPC server", + } + WSListenAddrFlag = cli.StringFlag{ + Name: "wsaddr", + Usage: "WS-RPC server listening interface", + Value: node.DefaultWSHost, + } + WSPortFlag = cli.IntFlag{ + Name: "wsport", + Usage: "WS-RPC server listening port", + Value: node.DefaultWSPort, + } + WSApiFlag = cli.StringFlag{ + Name: "wsapi", + Usage: "API's offered over the WS-RPC interface", + Value: rpc.DefaultHTTPApis, + } + WSAllowedOriginsFlag = cli.StringFlag{ + Name: "wsorigins", + Usage: "Origins from which to accept websockets requests", + Value: "", + } + ExecFlag = cli.StringFlag{ + Name: "exec", + Usage: "Execute JavaScript statement (only in combination with console/attach)", + } + PreloadJSFlag = cli.StringFlag{ + Name: "preload", + Usage: "Comma separated list of JavaScript files to preload into the console", + } + + // Network Settings + MaxPeersFlag = cli.IntFlag{ + Name: "maxpeers", + Usage: "Maximum number of network peers (network disabled if set to 0)", + Value: 25, + } + MaxPendingPeersFlag = cli.IntFlag{ + Name: "maxpendpeers", + Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", + Value: 0, + } + ListenPortFlag = cli.IntFlag{ + Name: "port", + Usage: "Network listening port", + Value: 30303, + } + BootnodesFlag = cli.StringFlag{ + Name: "bootnodes", + Usage: "Comma separated enode URLs for P2P discovery bootstrap", + Value: "", + } + NodeKeyFileFlag = cli.StringFlag{ + Name: "nodekey", + Usage: "P2P node key file", + } + NodeKeyHexFlag = cli.StringFlag{ + Name: "nodekeyhex", + Usage: "P2P node key as hex (for testing)", + } + NATFlag = cli.StringFlag{ + Name: "nat", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)", + Value: "any", + } + NoDiscoverFlag = cli.BoolFlag{ + Name: "nodiscover", + Usage: "Disables the peer discovery mechanism (manual peer addition)", + } + DiscoveryV5Flag = cli.BoolFlag{ + Name: "v5disc", + Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", + } + NetrestrictFlag = cli.StringFlag{ + Name: "netrestrict", + Usage: "Restricts network communication to the given IP networks (CIDR masks)", + } + + WhisperEnabledFlag = cli.BoolFlag{ + Name: "shh", + Usage: "Enable Whisper", + } + + // ATM the url is left to the user and deployment to + JSpathFlag = cli.StringFlag{ + Name: "jspath", + Usage: "JavaScript root path for `loadScript`", + Value: ".", + } + SolcPathFlag = cli.StringFlag{ + Name: "solc", + Usage: "Solidity compiler command to be used", + Value: "solc", + } + + // Gas price oracle settings + GpoMinGasPriceFlag = cli.StringFlag{ + Name: "gpomin", + Usage: "Minimum suggested gas price", + Value: new(big.Int).Mul(big.NewInt(20), common.Shannon).String(), + } + GpoMaxGasPriceFlag = cli.StringFlag{ + Name: "gpomax", + Usage: "Maximum suggested gas price", + Value: new(big.Int).Mul(big.NewInt(500), common.Shannon).String(), + } + GpoFullBlockRatioFlag = cli.IntFlag{ + Name: "gpofull", + Usage: "Full block threshold for gas price calculation (%)", + Value: 80, + } + GpobaseStepDownFlag = cli.IntFlag{ + Name: "gpobasedown", + Usage: "Suggested gas price base step down ratio (1/1000)", + Value: 10, + } + GpobaseStepUpFlag = cli.IntFlag{ + Name: "gpobaseup", + Usage: "Suggested gas price base step up ratio (1/1000)", + Value: 100, + } + GpobaseCorrectionFactorFlag = cli.IntFlag{ + Name: "gpobasecf", + Usage: "Suggested gas price base correction factor (%)", + Value: 110, + } +) + +// MakeDataDir retrieves the currently requested data directory, terminating +// if none (or the empty string) is specified. If the node is starting a testnet, +// the a subdirectory of the specified datadir will be used. +func MakeDataDir(ctx *cli.Context) string { + if path := ctx.GlobalString(DataDirFlag.Name); path != "" { + // TODO: choose a different location outside of the regular datadir. + if ctx.GlobalBool(TestNetFlag.Name) { + return filepath.Join(path, "testnet") + } + return path + } + Fatalf("Cannot determine default data directory, please set manually (--datadir)") + return "" +} + +// MakeIPCPath creates an IPC path configuration from the set command line flags, +// returning an empty string if IPC was explicitly disabled, or the set path. +func MakeIPCPath(ctx *cli.Context) string { + if ctx.GlobalBool(IPCDisabledFlag.Name) { + return "" + } + return ctx.GlobalString(IPCPathFlag.Name) +} + +// MakeNodeKey creates a node key from set command line flags, either loading it +// from a file or as a specified hex value. If neither flags were provided, this +// method returns nil and an emphemeral key is to be generated. +func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey { + var ( + hex = ctx.GlobalString(NodeKeyHexFlag.Name) + file = ctx.GlobalString(NodeKeyFileFlag.Name) + + key *ecdsa.PrivateKey + err error + ) + switch { + case file != "" && hex != "": + Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name) + + case file != "": + if key, err = crypto.LoadECDSA(file); err != nil { + Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err) + } + + case hex != "": + if key, err = crypto.HexToECDSA(hex); err != nil { + Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err) + } + } + return key +} + +// makeNodeUserIdent creates the user identifier from CLI flags. +func makeNodeUserIdent(ctx *cli.Context) string { + var comps []string + if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { + comps = append(comps, identity) + } + if ctx.GlobalBool(VMEnableJitFlag.Name) { + comps = append(comps, "JIT") + } + return strings.Join(comps, "/") +} + +// MakeBootstrapNodes creates a list of bootstrap nodes from the command line +// flags, reverting to pre-configured ones if none have been specified. +func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { + urls := params.MainnetBootnodes + if ctx.GlobalIsSet(BootnodesFlag.Name) { + urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") + } else if ctx.GlobalBool(TestNetFlag.Name) { + urls = params.TestnetBootnodes + } + + bootnodes := make([]*discover.Node, 0, len(urls)) + for _, url := range urls { + node, err := discover.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) + continue + } + bootnodes = append(bootnodes, node) + } + return bootnodes +} + +// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line +// flags, reverting to pre-configured ones if none have been specified. +func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node { + urls := params.DiscoveryV5Bootnodes + if ctx.GlobalIsSet(BootnodesFlag.Name) { + urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") + } + + bootnodes := make([]*discv5.Node, 0, len(urls)) + for _, url := range urls { + node, err := discv5.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) + continue + } + bootnodes = append(bootnodes, node) + } + return bootnodes +} + +// MakeListenAddress creates a TCP listening address string from set command +// line flags. +func MakeListenAddress(ctx *cli.Context) string { + return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) +} + +// MakeDiscoveryV5Address creates a UDP listening address string from set command +// line flags for the V5 discovery protocol. +func MakeDiscoveryV5Address(ctx *cli.Context) string { + return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) +} + +// MakeNAT creates a port mapper from set command line flags. +func MakeNAT(ctx *cli.Context) nat.Interface { + natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) + if err != nil { + Fatalf("Option %s: %v", NATFlag.Name, err) + } + return natif +} + +// MakeRPCModules splits input separated by a comma and trims excessive white +// space from the substrings. +func MakeRPCModules(input string) []string { + result := strings.Split(input, ",") + for i, r := range result { + result[i] = strings.TrimSpace(r) + } + return result +} + +// MakeHTTPRpcHost creates the HTTP RPC listener interface string from the set +// command line flags, returning empty if the HTTP endpoint is disabled. +func MakeHTTPRpcHost(ctx *cli.Context) string { + if !ctx.GlobalBool(RPCEnabledFlag.Name) { + return "" + } + return ctx.GlobalString(RPCListenAddrFlag.Name) +} + +// MakeWSRpcHost creates the WebSocket RPC listener interface string from the set +// command line flags, returning empty if the HTTP endpoint is disabled. +func MakeWSRpcHost(ctx *cli.Context) string { + if !ctx.GlobalBool(WSEnabledFlag.Name) { + return "" + } + return ctx.GlobalString(WSListenAddrFlag.Name) +} + +// MakeDatabaseHandles raises out the number of allowed file handles per process +// for Geth and returns half of the allowance to assign to the database. +func MakeDatabaseHandles() int { + if err := raiseFdLimit(2048); err != nil { + Fatalf("Failed to raise file descriptor allowance: %v", err) + } + limit, err := getFdLimit() + if err != nil { + Fatalf("Failed to retrieve file descriptor allowance: %v", err) + } + if limit > 2048 { // cap database file descriptors even if more is available + limit = 2048 + } + return limit / 2 // Leave half for networking and other stuff +} + +// MakeAddress converts an account specified directly as a hex encoded string or +// a key index in the key store to an internal account representation. +func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { + // If the specified account is a valid address, return it + if common.IsHexAddress(account) { + return accounts.Account{Address: common.HexToAddress(account)}, nil + } + // Otherwise try to interpret the account as a keystore index + index, err := strconv.Atoi(account) + if err != nil || index < 0 { + return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) + } + accs := ks.Accounts() + if len(accs) <= index { + return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) + } + return accs[index], nil +} + +// MakeEtherbase retrieves the etherbase either from the directly specified +// command line flags or from the keystore if CLI indexed. +func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address { + accounts := ks.Accounts() + if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 { + glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default") + return common.Address{} + } + etherbase := ctx.GlobalString(EtherbaseFlag.Name) + if etherbase == "" { + return common.Address{} + } + // If the specified etherbase is a valid address, return it + account, err := MakeAddress(ks, etherbase) + if err != nil { + Fatalf("Option %q: %v", EtherbaseFlag.Name, err) + } + return account.Address +} + +// MakeMinerExtra resolves extradata for the miner from the set command line flags +// or returns a default one composed on the client, runtime and OS metadata. +func MakeMinerExtra(extra []byte, ctx *cli.Context) []byte { + if ctx.GlobalIsSet(ExtraDataFlag.Name) { + return []byte(ctx.GlobalString(ExtraDataFlag.Name)) + } + return extra +} + +// MakePasswordList reads password lines from the file specified by --password. +func MakePasswordList(ctx *cli.Context) []string { + path := ctx.GlobalString(PasswordFileFlag.Name) + if path == "" { + return nil + } + text, err := ioutil.ReadFile(path) + if err != nil { + Fatalf("Failed to read password file: %v", err) + } + lines := strings.Split(string(text), "\n") + // Sanitise DOS line endings. + for i := range lines { + lines[i] = strings.TrimRight(lines[i], "\r") + } + return lines +} + +// MakeNode configures a node with no services from command line flags. +func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { + vsn := params.Version + if gitCommit != "" { + vsn += "-" + gitCommit[:8] + } + + // if we're running a light client or server, force enable the v5 peer discovery unless it is explicitly disabled with --nodiscover + // note that explicitly specifying --v5disc overrides --nodiscover, in which case the later only disables v4 discovery + forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) + + config := &node.Config{ + DataDir: MakeDataDir(ctx), + KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name), + UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name), + PrivateKey: MakeNodeKey(ctx), + Name: name, + Version: vsn, + UserIdent: makeNodeUserIdent(ctx), + NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // always disable v4 discovery in light client mode + DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || forceV5Discovery, + DiscoveryV5Addr: MakeDiscoveryV5Address(ctx), + BootstrapNodes: MakeBootstrapNodes(ctx), + BootstrapNodesV5: MakeBootstrapNodesV5(ctx), + ListenAddr: MakeListenAddress(ctx), + NAT: MakeNAT(ctx), + MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), + MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), + IPCPath: MakeIPCPath(ctx), + HTTPHost: MakeHTTPRpcHost(ctx), + HTTPPort: ctx.GlobalInt(RPCPortFlag.Name), + HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name), + HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)), + WSHost: MakeWSRpcHost(ctx), + WSPort: ctx.GlobalInt(WSPortFlag.Name), + WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name), + WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)), + } + if ctx.GlobalBool(DevModeFlag.Name) { + if !ctx.GlobalIsSet(DataDirFlag.Name) { + config.DataDir = filepath.Join(os.TempDir(), "/ethereum_dev_mode") + } + // --dev mode does not need p2p networking. + config.MaxPeers = 0 + config.ListenAddr = ":0" + } + if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { + list, err := netutil.ParseNetlist(netrestrict) + if err != nil { + Fatalf("Option %q: %v", NetrestrictFlag.Name, err) + } + config.NetRestrict = list + } + + stack, err := node.New(config) + if err != nil { + Fatalf("Failed to create the protocol stack: %v", err) + } + return stack +} + +// RegisterEthService configures eth.Ethereum from command line flags and adds it to the +// given node. +func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { + // Avoid conflicting network flags + networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag} + for _, flag := range netFlags { + if ctx.GlobalBool(flag.Name) { + networks++ + } + } + if networks > 1 { + Fatalf("The %v flags are mutually exclusive", netFlags) + } + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + + ethConf := ð.Config{ + Etherbase: MakeEtherbase(ks, ctx), + ChainConfig: MakeChainConfig(ctx, stack), + FastSync: ctx.GlobalBool(FastSyncFlag.Name), + LightMode: ctx.GlobalBool(LightModeFlag.Name), + LightServ: ctx.GlobalInt(LightServFlag.Name), + LightPeers: ctx.GlobalInt(LightPeersFlag.Name), + MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), + DatabaseCache: ctx.GlobalInt(CacheFlag.Name), + DatabaseHandles: MakeDatabaseHandles(), + NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), + MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), + ExtraData: MakeMinerExtra(extra, ctx), + DocRoot: ctx.GlobalString(DocRootFlag.Name), + GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), + GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)), + GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)), + GpoFullBlockRatio: ctx.GlobalInt(GpoFullBlockRatioFlag.Name), + GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name), + GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name), + GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name), + SolcPath: ctx.GlobalString(SolcPathFlag.Name), + AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), + EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name), + } + + // Override any default configs in dev mode or the test net + switch { + case ctx.GlobalBool(TestNetFlag.Name): + if !ctx.GlobalIsSet(NetworkIdFlag.Name) { + ethConf.NetworkId = 3 + } + ethConf.Genesis = core.DefaultTestnetGenesisBlock() + + case ctx.GlobalBool(DevModeFlag.Name): + ethConf.Genesis = core.DevGenesisBlock() + if !ctx.GlobalIsSet(GasPriceFlag.Name) { + ethConf.GasPrice = new(big.Int) + } + ethConf.PowTest = true + } + // Override any global options pertaining to the Ethereum protocol + if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { + state.MaxTrieCacheGen = uint16(gen) + } + + if ethConf.LightMode { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, ethConf) + }); err != nil { + Fatalf("Failed to register the Ethereum light node service: %v", err) + } + } else { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + fullNode, err := eth.New(ctx, ethConf) + if fullNode != nil && ethConf.LightServ > 0 { + ls, _ := les.NewLesServer(fullNode, ethConf) + fullNode.AddLesServer(ls) + } + return fullNode, err + }); err != nil { + Fatalf("Failed to register the Ethereum full node service: %v", err) + } + } +} + +// RegisterShhService configures Whisper and adds it to the given node. +func RegisterShhService(stack *node.Node) { + if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { + Fatalf("Failed to register the Whisper service: %v", err) + } +} + +// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to +// th egiven node. +func RegisterEthStatsService(stack *node.Node, url string) { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + // Retrieve both eth and les services + var ethServ *eth.Ethereum + ctx.Service(ðServ) + + var lesServ *les.LightEthereum + ctx.Service(&lesServ) + + return ethstats.New(url, ethServ, lesServ) + }); err != nil { + Fatalf("Failed to register the Ethereum Stats service: %v", err) + } +} + +// SetupNetwork configures the system for either the main net or some test network. +func SetupNetwork(ctx *cli.Context) { + params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name)) +} + +// MakeChainConfig reads the chain configuration from the database in ctx.Datadir. +func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig { + db := MakeChainDatabase(ctx, stack) + defer db.Close() + + return MakeChainConfigFromDb(ctx, db) +} + +// MakeChainConfigFromDb reads the chain configuration from the given database. +func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig { + // If the chain is already initialized, use any existing chain configs + config := new(params.ChainConfig) + + genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0) + if genesis != nil { + storedConfig, err := core.GetChainConfig(db, genesis.Hash()) + switch err { + case nil: + config = storedConfig + case core.ChainConfigNotFoundErr: + // No configs found, use empty, will populate below + default: + Fatalf("Could not make chain configuration: %v", err) + } + } + // set chain id in case it's zero. + if config.ChainId == nil { + config.ChainId = new(big.Int) + } + // Check whether we are allowed to set default config params or not: + // - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`) + // - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet) + defaults := genesis == nil || + (genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) || + (genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name)) + + if defaults { + if ctx.GlobalBool(TestNetFlag.Name) { + config = params.TestnetChainConfig + } else { + // Homestead fork + config.HomesteadBlock = params.MainNetHomesteadBlock + // DAO fork + config.DAOForkBlock = params.MainNetDAOForkBlock + config.DAOForkSupport = true + + // DoS reprice fork + config.EIP150Block = params.MainNetHomesteadGasRepriceBlock + config.EIP150Hash = params.MainNetHomesteadGasRepriceHash + + // DoS state cleanup fork + config.EIP155Block = params.MainNetSpuriousDragon + config.EIP158Block = params.MainNetSpuriousDragon + config.ChainId = params.MainNetChainID + } + } + return config +} + +func ChainDbName(ctx *cli.Context) string { + if ctx.GlobalBool(LightModeFlag.Name) { + return "lightchaindata" + } else { + return "chaindata" + } +} + +// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. +func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { + var ( + cache = ctx.GlobalInt(CacheFlag.Name) + handles = MakeDatabaseHandles() + name = ChainDbName(ctx) + ) + + chainDb, err := stack.OpenDatabase(name, cache, handles) + if err != nil { + Fatalf("Could not open database: %v", err) + } + return chainDb +} + +// MakeChain creates a chain manager from set command line flags. +func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { + var err error + chainDb = MakeChainDatabase(ctx, stack) + + if ctx.GlobalBool(TestNetFlag.Name) { + _, err := core.WriteTestNetGenesisBlock(chainDb) + if err != nil { + glog.Fatalln(err) + } + } + + chainConfig := MakeChainConfigFromDb(ctx, chainDb) + + pow := pow.PoW(core.FakePow{}) + if !ctx.GlobalBool(FakePoWFlag.Name) { + pow = ethash.New() + } + chain, err = core.NewBlockChain(chainDb, chainConfig, pow, new(event.TypeMux), vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}) + if err != nil { + Fatalf("Could not start chainmanager: %v", err) + } + return chain, chainDb +} + +// MakeConsolePreloads retrieves the absolute paths for the console JavaScript +// scripts to preload before starting. +func MakeConsolePreloads(ctx *cli.Context) []string { + // Skip preloading if there's nothing to preload + if ctx.GlobalString(PreloadJSFlag.Name) == "" { + return nil + } + // Otherwise resolve absolute paths and return them + preloads := []string{} + + assets := ctx.GlobalString(JSpathFlag.Name) + for _, file := range strings.Split(ctx.GlobalString(PreloadJSFlag.Name), ",") { + preloads = append(preloads, common.AbsolutePath(assets, strings.TrimSpace(file))) + } + return preloads +} diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go b/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go new file mode 100644 index 0000000..d002497 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/cmd/wnode/main.go @@ -0,0 +1,535 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +// This is a simple Whisper node. It could be used as a stand-alone bootstrap node. +// Also, could be used for different test and diagnostics purposes. + +package main + +import ( + "bufio" + "crypto/ecdsa" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/whisper/mailserver" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" + "golang.org/x/crypto/pbkdf2" +) + +const quitCommand = "~Q" +const symKeyName = "da919ea33001b04dfc630522e33078ec0df11" + +// singletons +var ( + server *p2p.Server + shh *whisper.Whisper + done chan struct{} + mailServer mailserver.WMailServer + + input = bufio.NewReader(os.Stdin) +) + +// encryption +var ( + symKey []byte + pub *ecdsa.PublicKey + asymKey *ecdsa.PrivateKey + nodeid *ecdsa.PrivateKey + topic whisper.TopicType + filterID string + symPass string + msPassword string +) + +// cmd arguments +var ( + echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics") + bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections") + forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages") + mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand") + requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server") + asymmetricMode = flag.Bool("a", false, "use asymmetric encryption") + testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") + generateKey = flag.Bool("k", false, "generate and show the private key") + + argVerbosity = flag.Int("verbosity", logger.Warn, "log verbosity level") + argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") + argWorkTime = flag.Uint("work", 5, "work time in seconds") + argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") + argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request") + + argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") + argPub = flag.String("pub", "", "public key for asymmetric encryption") + argDBPath = flag.String("dbpath", "", "path to the server's DB directory") + argIDFile = flag.String("idfile", "", "file name with node id (private key)") + argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") + argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") +) + +func main() { + processArgs() + initialize() + run() +} + +func processArgs() { + flag.Parse() + + if len(*argIDFile) > 0 { + var err error + nodeid, err = crypto.LoadECDSA(*argIDFile) + if err != nil { + utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) + } + } + + const enodePrefix = "enode://" + if len(*argEnode) > 0 { + if (*argEnode)[:len(enodePrefix)] != enodePrefix { + *argEnode = enodePrefix + *argEnode + } + } + + if len(*argTopic) > 0 { + x, err := hex.DecodeString(*argTopic) + if err != nil { + utils.Fatalf("Failed to parse the topic: %s", err) + } + topic = whisper.BytesToTopic(x) + } + + if *asymmetricMode && len(*argPub) > 0 { + pub = crypto.ToECDSAPub(common.FromHex(*argPub)) + if !isKeyValid(pub) { + utils.Fatalf("invalid public key") + } + } + + if *echoMode { + echo() + } +} + +func echo() { + fmt.Printf("ttl = %d \n", *argTTL) + fmt.Printf("workTime = %d \n", *argWorkTime) + fmt.Printf("pow = %f \n", *argPoW) + fmt.Printf("mspow = %f \n", *argServerPoW) + fmt.Printf("ip = %s \n", *argIP) + fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) + fmt.Printf("idfile = %s \n", *argIDFile) + fmt.Printf("dbpath = %s \n", *argDBPath) + fmt.Printf("boot = %s \n", *argEnode) +} + +func initialize() { + glog.SetV(*argVerbosity) + glog.SetToStderr(true) + + done = make(chan struct{}) + var peers []*discover.Node + var err error + + if *generateKey { + key, err := crypto.GenerateKey() + if err != nil { + utils.Fatalf("Failed to generate private key: %s", err) + } + k := hex.EncodeToString(crypto.FromECDSA(key)) + fmt.Printf("Random private key: %s \n", k) + os.Exit(0) + } + + if *testMode { + symPass = "wwww" // ascii code: 0x77777777 + msPassword = "mail server test password" + } + + if *bootstrapMode { + if len(*argIP) == 0 { + argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") + } + } else { + if len(*argEnode) == 0 { + argEnode = scanLineA("Please enter the peer's enode: ") + } + peer := discover.MustParseNode(*argEnode) + peers = append(peers, peer) + } + + if *mailServerMode { + if len(msPassword) == 0 { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + } + shh = whisper.New() + shh.RegisterServer(&mailServer) + mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) + } else { + shh = whisper.New() + } + + asymKey = shh.NewIdentity() + if nodeid == nil { + nodeid = shh.NewIdentity() + } + + maxPeers := 80 + if *bootstrapMode { + maxPeers = 800 + } + + server = &p2p.Server{ + Config: p2p.Config{ + PrivateKey: nodeid, + MaxPeers: maxPeers, + Name: common.MakeName("whisper-go", "5.0"), + Protocols: shh.Protocols(), + ListenAddr: *argIP, + NAT: nat.Any(), + BootstrapNodes: peers, + StaticNodes: peers, + TrustedNodes: peers, + }, + } +} + +func startServer() { + err := server.Start() + if err != nil { + utils.Fatalf("Failed to start Whisper peer: %s.", err) + } + + fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) + fmt.Println(server.NodeInfo().Enode) + + if *bootstrapMode { + configureNode() + fmt.Println("Bootstrap Whisper node started") + } else { + fmt.Println("Whisper node started") + // first see if we can establish connection, then ask for user input + waitForConnection(true) + configureNode() + } + + if !*forwarderMode { + fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) + } +} + +func isKeyValid(k *ecdsa.PublicKey) bool { + return k.X != nil && k.Y != nil +} + +func configureNode() { + var err error + var p2pAccept bool + + if *forwarderMode { + return + } + + if *asymmetricMode { + if len(*argPub) == 0 { + s := scanLine("Please enter the peer's public key: ") + pub = crypto.ToECDSAPub(common.FromHex(s)) + if !isKeyValid(pub) { + utils.Fatalf("Error: invalid public key") + } + } + } + + if *requestMail { + p2pAccept = true + if len(msPassword) == 0 { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + } + } + + if !*asymmetricMode && !*forwarderMode { + if len(symPass) == 0 { + symPass, err = console.Stdin.PromptPassword("Please enter the password: ") + if err != nil { + utils.Fatalf("Failed to read passphrase: %v", err) + } + } + + shh.AddSymKey(symKeyName, []byte(symPass)) + symKey = shh.GetSymKey(symKeyName) + if len(*argTopic) == 0 { + generateTopic([]byte(symPass)) + } + } + + if *mailServerMode { + if len(*argDBPath) == 0 { + argDBPath = scanLineA("Please enter the path to DB file: ") + } + } + + filter := whisper.Filter{ + KeySym: symKey, + KeyAsym: asymKey, + Topics: []whisper.TopicType{topic}, + AcceptP2P: p2pAccept, + } + filterID, err = shh.Watch(&filter) + if err != nil { + utils.Fatalf("Failed to install filter: %s", err) + } + fmt.Printf("Filter is configured for the topic: %x \n", topic) +} + +func generateTopic(password []byte) { + x := pbkdf2.Key(password, password, 8196, 128, sha512.New) + for i := 0; i < len(x); i++ { + topic[i%whisper.TopicLength] ^= x[i] + } +} + +func waitForConnection(timeout bool) { + var cnt int + var connected bool + for !connected { + time.Sleep(time.Millisecond * 50) + connected = server.PeerCount() > 0 + if timeout { + cnt++ + if cnt > 1000 { + utils.Fatalf("Timeout expired, failed to connect") + } + } + } + + fmt.Println("Connected to peer.") +} + +func run() { + defer mailServer.Close() + startServer() + defer server.Stop() + shh.Start(nil) + defer shh.Stop() + + if !*forwarderMode { + go messageLoop() + } + + if *requestMail { + requestExpiredMessagesLoop() + } else { + sendLoop() + } +} + +func sendLoop() { + for { + s := scanLine("") + if s == quitCommand { + fmt.Println("Quit command received") + close(done) + break + } + sendMsg([]byte(s)) + + if *asymmetricMode { + // print your own message for convenience, + // because in asymmetric mode it is impossible to decrypt it + timestamp := time.Now().Unix() + from := crypto.PubkeyToAddress(asymKey.PublicKey) + fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) + } + } +} + +func scanLine(prompt string) string { + if len(prompt) > 0 { + fmt.Print(prompt) + } + txt, err := input.ReadString('\n') + if err != nil { + utils.Fatalf("input error: %s", err) + } + txt = strings.TrimRight(txt, "\n\r") + return txt +} + +func scanLineA(prompt string) *string { + s := scanLine(prompt) + return &s +} + +func scanUint(prompt string) uint32 { + s := scanLine(prompt) + i, err := strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the lower time limit: %s", err) + } + return uint32(i) +} + +func sendMsg(payload []byte) { + params := whisper.MessageParams{ + Src: asymKey, + Dst: pub, + KeySym: symKey, + Payload: payload, + Topic: topic, + TTL: uint32(*argTTL), + PoW: *argPoW, + WorkTime: uint32(*argWorkTime), + } + + msg := whisper.NewSentMessage(¶ms) + envelope, err := msg.Wrap(¶ms) + if err != nil { + fmt.Printf("failed to seal message: %v \n", err) + return + } + + err = shh.Send(envelope) + if err != nil { + fmt.Printf("failed to send message: %v \n", err) + } +} + +func messageLoop() { + f := shh.GetFilter(filterID) + if f == nil { + utils.Fatalf("filter is not installed") + } + + ticker := time.NewTicker(time.Millisecond * 50) + + for { + select { + case <-ticker.C: + messages := f.Retrieve() + for _, msg := range messages { + printMessageInfo(msg) + } + case <-done: + return + } + } +} + +func printMessageInfo(msg *whisper.ReceivedMessage) { + timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics + text := string(msg.Payload) + + var address common.Address + if msg.Src != nil { + address = crypto.PubkeyToAddress(*msg.Src) + } + + if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { + fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself + } else { + fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer + } +} + +func requestExpiredMessagesLoop() { + var key, peerID []byte + var timeLow, timeUpp uint32 + var t string + var xt, empty whisper.TopicType + + err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword)) + if err != nil { + utils.Fatalf("Failed to create symmetric key for mail request: %s", err) + } + key = shh.GetSymKey(mailserver.MailServerKeyName) + peerID = extractIdFromEnode(*argEnode) + shh.MarkPeerTrusted(peerID) + + for { + timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") + timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") + t = scanLine("Please enter the topic (hexadecimal): ") + if len(t) >= whisper.TopicLength*2 { + x, err := hex.DecodeString(t) + if err != nil { + utils.Fatalf("Failed to parse the topic: %s", err) + } + xt = whisper.BytesToTopic(x) + } + if timeUpp == 0 { + timeUpp = 0xFFFFFFFF + } + + data := make([]byte, 8+whisper.TopicLength) + binary.BigEndian.PutUint32(data, timeLow) + binary.BigEndian.PutUint32(data[4:], timeUpp) + copy(data[8:], xt[:]) + if xt == empty { + data = data[:8] + } + + var params whisper.MessageParams + params.PoW = *argServerPoW + params.Payload = data + params.KeySym = key + params.Src = nodeid + params.WorkTime = 5 + + msg := whisper.NewSentMessage(¶ms) + env, err := msg.Wrap(¶ms) + if err != nil { + utils.Fatalf("Wrap failed: %s", err) + } + + err = shh.RequestHistoricMessages(peerID, env) + if err != nil { + utils.Fatalf("Failed to send P2P message: %s", err) + } + + time.Sleep(time.Second * 5) + } +} + +func extractIdFromEnode(s string) []byte { + n, err := discover.ParseNode(s) + if err != nil { + utils.Fatalf("Failed to parse enode: %s", err) + return nil + } + return n.ID[:] +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/.gitignore b/vendor/github.com/ethereum/go-ethereum/common/.gitignore new file mode 100644 index 0000000..f725d58 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/vendor/github.com/ethereum/go-ethereum/common/.travis.yml b/vendor/github.com/ethereum/go-ethereum/common/.travis.yml new file mode 100644 index 0000000..6935907 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - 1.2 diff --git a/vendor/github.com/ethereum/go-ethereum/common/README.md b/vendor/github.com/ethereum/go-ethereum/common/README.md new file mode 100644 index 0000000..adea022 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/README.md @@ -0,0 +1,140 @@ +# common + +[![Build +Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum) + +The common package contains the ethereum utility library. + +# Installation + +As a subdirectory the main go-ethereum repository, you get it with +`go get github.com/ethereum/go-ethereum`. + +# Usage + +## RLP (Recursive Linear Prefix) Encoding + +RLP Encoding is an encoding scheme used by the Ethereum project. It +encodes any native value or list to a string. + +More in depth information about the encoding scheme see the +[Wiki](http://wiki.ethereum.org/index.php/RLP) article. + +```go +rlp := common.Encode("doge") +fmt.Printf("%q\n", rlp) // => "\0x83dog" + +rlp = common.Encode([]interface{}{"dog", "cat"}) +fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat" +decoded := common.Decode(rlp) +fmt.Println(decoded) // => ["dog" "cat"] +``` + +## Patricia Trie + +Patricie Tree is a merkle trie used by the Ethereum project. + +More in depth information about the (modified) Patricia Trie can be +found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree). + +The patricia trie uses a db as backend and could be anything as long as +it satisfies the Database interface found in `common/db.go`. + +```go +db := NewDatabase() + +// db, root +trie := common.NewTrie(db, "") + +trie.Put("puppy", "dog") +trie.Put("horse", "stallion") +trie.Put("do", "verb") +trie.Put("doge", "coin") + +// Look up the key "do" in the trie +out := trie.Get("do") +fmt.Println(out) // => verb + +trie.Delete("puppy") +``` + +The patricia trie, in combination with RLP, provides a robust, +cryptographically authenticated data structure that can be used to store +all (key, value) bindings. + +```go +// ... Create db/trie + +// Note that RLP uses interface slices as list +value := common.Encode([]interface{}{"one", 2, "three", []interface{}{42}}) +// Store the RLP encoded value of the list +trie.Put("mykey", value) +``` + +## Value + +Value is a Generic Value which is used in combination with RLP data or +`([])interface{}` structures. It may serve as a bridge between RLP data +and actual real values and takes care of all the type checking and +casting. Unlike Go's `reflect.Value` it does not panic if it's unable to +cast to the requested value. It simple returns the base value of that +type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc). + +### Creating a new Value + +`NewEmptyValue()` returns a new \*Value with it's initial value set to a +`[]interface{}` + +`AppendList()` appends a list to the current value. + +`Append(v)` appends the value (v) to the current value/list. + +```go +val := common.NewEmptyValue().Append(1).Append("2") +val.AppendList().Append(3) +``` + +### Retrieving values + +`Get(i)` returns the `i` item in the list. + +`Uint()` returns the value as an unsigned int64. + +`Slice()` returns the value as a interface slice. + +`Str()` returns the value as a string. + +`Bytes()` returns the value as a byte slice. + +`Len()` assumes current to be a slice and returns its length. + +`Byte()` returns the value as a single byte. + +```go +val := common.NewValue([]interface{}{1,"2",[]interface{}{3}}) +val.Get(0).Uint() // => 1 +val.Get(1).Str() // => "2" +s := val.Get(2) // => Value([]interface{}{3}) +s.Get(0).Uint() // => 3 +``` + +## Decoding + +Decoding streams of RLP data is simplified + +```go +val := common.NewValueFromBytes(rlpData) +val.Get(0).Uint() +``` + +## Encoding + +Encoding from Value to RLP is done with the `Encode` method. The +underlying value can be anything RLP can encode (int, str, lists, bytes) + +```go +val := common.NewValue([]interface{}{1,"2",[]interface{}{3}}) +rlp := val.Encode() +// Store the rlp data +Store(rlp) +``` diff --git a/vendor/github.com/ethereum/go-ethereum/common/big.go b/vendor/github.com/ethereum/go-ethereum/common/big.go new file mode 100644 index 0000000..4ce87ee --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/big.go @@ -0,0 +1,156 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import "math/big" + +// Common big integers often used +var ( + Big1 = big.NewInt(1) + Big2 = big.NewInt(2) + Big3 = big.NewInt(3) + Big0 = big.NewInt(0) + BigTrue = Big1 + BigFalse = Big0 + Big32 = big.NewInt(32) + Big36 = big.NewInt(36) + Big97 = big.NewInt(97) + Big98 = big.NewInt(98) + Big256 = big.NewInt(0xff) + Big257 = big.NewInt(257) + MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +) + +// Big pow +// +// Returns the power of two big integers +func BigPow(a, b int) *big.Int { + c := new(big.Int) + c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0)) + + return c +} + +// Big +// +// Shortcut for new(big.Int).SetString(..., 0) +func Big(num string) *big.Int { + n := new(big.Int) + n.SetString(num, 0) + + return n +} + +// Bytes2Big +// +func BytesToBig(data []byte) *big.Int { + n := new(big.Int) + n.SetBytes(data) + + return n +} +func Bytes2Big(data []byte) *big.Int { return BytesToBig(data) } +func BigD(data []byte) *big.Int { return BytesToBig(data) } + +func String2Big(num string) *big.Int { + n := new(big.Int) + n.SetString(num, 0) + return n +} + +func BitTest(num *big.Int, i int) bool { + return num.Bit(i) > 0 +} + +// To256 +// +// "cast" the big int to a 256 big int (i.e., limit to) +var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) + +func U256(x *big.Int) *big.Int { + //if x.Cmp(Big0) < 0 { + // return new(big.Int).Add(tt256, x) + // } + + x.And(x, tt256m1) + + return x +} + +func S256(x *big.Int) *big.Int { + if x.Cmp(tt255) < 0 { + return x + } else { + // We don't want to modify x, ever + return new(big.Int).Sub(x, tt256) + } +} + +func FirstBitSet(v *big.Int) int { + for i := 0; i < v.BitLen(); i++ { + if v.Bit(i) > 0 { + return i + } + } + + return v.BitLen() +} + +// Big to bytes +// +// Returns the bytes of a big integer with the size specified by **base** +// Attempts to pad the byte array with zeros. +func BigToBytes(num *big.Int, base int) []byte { + ret := make([]byte, base/8) + + if len(num.Bytes()) > base/8 { + return num.Bytes() + } + + return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) +} + +// Big copy +// +// Creates a copy of the given big integer +func BigCopy(src *big.Int) *big.Int { + return new(big.Int).Set(src) +} + +// Big max +// +// Returns the maximum size big integer +func BigMax(x, y *big.Int) *big.Int { + if x.Cmp(y) < 0 { + return y + } + + return x +} + +// Big min +// +// Returns the minimum size big integer +func BigMin(x, y *big.Int) *big.Int { + if x.Cmp(y) > 0 { + return y + } + + return x +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/big_test.go b/vendor/github.com/ethereum/go-ethereum/common/big_test.go new file mode 100644 index 0000000..4d04a8d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/big_test.go @@ -0,0 +1,89 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "bytes" + "testing" +) + +func TestMisc(t *testing.T) { + a := Big("10") + b := Big("57896044618658097711785492504343953926634992332820282019728792003956564819968") + c := []byte{1, 2, 3, 4} + z := BitTest(a, 1) + + if !z { + t.Error("Expected true got", z) + } + + U256(a) + S256(a) + + U256(b) + S256(b) + + BigD(c) +} + +func TestBigMax(t *testing.T) { + a := Big("10") + b := Big("5") + + max1 := BigMax(a, b) + if max1 != a { + t.Errorf("Expected %d got %d", a, max1) + } + + max2 := BigMax(b, a) + if max2 != a { + t.Errorf("Expected %d got %d", a, max2) + } +} + +func TestBigMin(t *testing.T) { + a := Big("10") + b := Big("5") + + min1 := BigMin(a, b) + if min1 != b { + t.Errorf("Expected %d got %d", b, min1) + } + + min2 := BigMin(b, a) + if min2 != b { + t.Errorf("Expected %d got %d", b, min2) + } +} + +func TestBigCopy(t *testing.T) { + a := Big("10") + b := BigCopy(a) + c := Big("1000000000000") + y := BigToBytes(b, 16) + ybytes := []byte{0, 10} + z := BigToBytes(c, 16) + zbytes := []byte{232, 212, 165, 16, 0} + + if !bytes.Equal(y, ybytes) { + t.Error("Got", ybytes) + } + + if !bytes.Equal(z, zbytes) { + t.Error("Got", zbytes) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/bytes.go b/vendor/github.com/ethereum/go-ethereum/common/bytes.go new file mode 100644 index 0000000..cbceea8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/bytes.go @@ -0,0 +1,266 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package common contains various helper functions. +package common + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "math/big" + "strings" +) + +func ToHex(b []byte) string { + hex := Bytes2Hex(b) + // Prefer output of "0x0" instead of "0x" + if len(hex) == 0 { + hex = "0" + } + return "0x" + hex +} + +func FromHex(s string) []byte { + if len(s) > 1 { + if s[0:2] == "0x" || s[0:2] == "0X" { + s = s[2:] + } + if len(s)%2 == 1 { + s = "0" + s + } + return Hex2Bytes(s) + } + return nil +} + +// Number to bytes +// +// Returns the number in bytes with the specified base +func NumberToBytes(num interface{}, bits int) []byte { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.BigEndian, num) + if err != nil { + fmt.Println("NumberToBytes failed:", err) + } + + return buf.Bytes()[buf.Len()-(bits/8):] +} + +// Bytes to number +// +// Attempts to cast a byte slice to a unsigned integer +func BytesToNumber(b []byte) uint64 { + var number uint64 + + // Make sure the buffer is 64bits + data := make([]byte, 8) + data = append(data[:len(b)], b...) + + buf := bytes.NewReader(data) + err := binary.Read(buf, binary.BigEndian, &number) + if err != nil { + fmt.Println("BytesToNumber failed:", err) + } + + return number +} + +// Read variable int +// +// Read a variable length number in big endian byte order +func ReadVarInt(buff []byte) (ret uint64) { + switch l := len(buff); { + case l > 4: + d := LeftPadBytes(buff, 8) + binary.Read(bytes.NewReader(d), binary.BigEndian, &ret) + case l > 2: + var num uint32 + d := LeftPadBytes(buff, 4) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + case l > 1: + var num uint16 + d := LeftPadBytes(buff, 2) + binary.Read(bytes.NewReader(d), binary.BigEndian, &num) + ret = uint64(num) + default: + var num uint8 + binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) + ret = uint64(num) + } + + return +} + +// Copy bytes +// +// Returns an exact copy of the provided bytes +func CopyBytes(b []byte) (copiedBytes []byte) { + copiedBytes = make([]byte, len(b)) + copy(copiedBytes, b) + + return +} + +func HasHexPrefix(str string) bool { + l := len(str) + return l >= 2 && str[0:2] == "0x" +} + +func IsHex(str string) bool { + l := len(str) + return l >= 4 && l%2 == 0 && str[0:2] == "0x" +} + +func Bytes2Hex(d []byte) string { + return hex.EncodeToString(d) +} + +func Hex2Bytes(str string) []byte { + h, _ := hex.DecodeString(str) + + return h +} + +func Hex2BytesFixed(str string, flen int) []byte { + h, _ := hex.DecodeString(str) + if len(h) == flen { + return h + } else { + if len(h) > flen { + return h[len(h)-flen:] + } else { + hh := make([]byte, flen) + copy(hh[flen-len(h):flen], h[:]) + return hh + } + } +} + +func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) { + if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") { + ret = Hex2Bytes(str[2:]) + } else { + ret = cb(str) + } + + return +} + +func FormatData(data string) []byte { + if len(data) == 0 { + return nil + } + // Simple stupid + d := new(big.Int) + if data[0:1] == "\"" && data[len(data)-1:] == "\"" { + return RightPadBytes([]byte(data[1:len(data)-1]), 32) + } else if len(data) > 1 && data[:2] == "0x" { + d.SetBytes(Hex2Bytes(data[2:])) + } else { + d.SetString(data, 0) + } + + return BigToBytes(d, 256) +} + +func ParseData(data ...interface{}) (ret []byte) { + for _, item := range data { + switch t := item.(type) { + case string: + var str []byte + if IsHex(t) { + str = Hex2Bytes(t[2:]) + } else { + str = []byte(t) + } + + ret = append(ret, RightPadBytes(str, 32)...) + case []byte: + ret = append(ret, LeftPadBytes(t, 32)...) + } + } + + return +} + +func RightPadBytes(slice []byte, l int) []byte { + if l < len(slice) { + return slice + } + + padded := make([]byte, l) + copy(padded[0:len(slice)], slice) + + return padded +} + +func LeftPadBytes(slice []byte, l int) []byte { + if l < len(slice) { + return slice + } + + padded := make([]byte, l) + copy(padded[l-len(slice):], slice) + + return padded +} + +func LeftPadString(str string, l int) string { + if l < len(str) { + return str + } + + zeros := Bytes2Hex(make([]byte, (l-len(str))/2)) + + return zeros + str + +} + +func RightPadString(str string, l int) string { + if l < len(str) { + return str + } + + zeros := Bytes2Hex(make([]byte, (l-len(str))/2)) + + return str + zeros + +} + +func ToAddress(slice []byte) (addr []byte) { + if len(slice) < 20 { + addr = LeftPadBytes(slice, 20) + } else if len(slice) > 20 { + addr = slice[len(slice)-20:] + } else { + addr = slice + } + + addr = CopyBytes(addr) + + return +} + +func ByteSliceToInterface(slice [][]byte) (ret []interface{}) { + for _, i := range slice { + ret = append(ret, i) + } + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/bytes_test.go b/vendor/github.com/ethereum/go-ethereum/common/bytes_test.go new file mode 100644 index 0000000..98d402c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/bytes_test.go @@ -0,0 +1,196 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "bytes" + "testing" + + checker "gopkg.in/check.v1" +) + +type BytesSuite struct{} + +var _ = checker.Suite(&BytesSuite{}) + +func (s *BytesSuite) TestNumberToBytes(c *checker.C) { + // data1 := int(1) + // res1 := NumberToBytes(data1, 16) + // c.Check(res1, checker.Panics) + + var data2 float64 = 3.141592653 + exp2 := []byte{0xe9, 0x38} + res2 := NumberToBytes(data2, 16) + c.Assert(res2, checker.DeepEquals, exp2) +} + +func (s *BytesSuite) TestBytesToNumber(c *checker.C) { + datasmall := []byte{0xe9, 0x38, 0xe9, 0x38} + datalarge := []byte{0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38} + + var expsmall uint64 = 0xe938e938 + var explarge uint64 = 0x0 + + ressmall := BytesToNumber(datasmall) + reslarge := BytesToNumber(datalarge) + + c.Assert(ressmall, checker.Equals, expsmall) + c.Assert(reslarge, checker.Equals, explarge) + +} + +func (s *BytesSuite) TestReadVarInt(c *checker.C) { + data8 := []byte{1, 2, 3, 4, 5, 6, 7, 8} + data4 := []byte{1, 2, 3, 4} + data2 := []byte{1, 2} + data1 := []byte{1} + + exp8 := uint64(72623859790382856) + exp4 := uint64(16909060) + exp2 := uint64(258) + exp1 := uint64(1) + + res8 := ReadVarInt(data8) + res4 := ReadVarInt(data4) + res2 := ReadVarInt(data2) + res1 := ReadVarInt(data1) + + c.Assert(res8, checker.Equals, exp8) + c.Assert(res4, checker.Equals, exp4) + c.Assert(res2, checker.Equals, exp2) + c.Assert(res1, checker.Equals, exp1) +} + +func (s *BytesSuite) TestCopyBytes(c *checker.C) { + data1 := []byte{1, 2, 3, 4} + exp1 := []byte{1, 2, 3, 4} + res1 := CopyBytes(data1) + c.Assert(res1, checker.DeepEquals, exp1) +} + +func (s *BytesSuite) TestIsHex(c *checker.C) { + data1 := "a9e67e" + exp1 := false + res1 := IsHex(data1) + c.Assert(res1, checker.DeepEquals, exp1) + + data2 := "0xa9e67e00" + exp2 := true + res2 := IsHex(data2) + c.Assert(res2, checker.DeepEquals, exp2) + +} + +func (s *BytesSuite) TestParseDataString(c *checker.C) { + res1 := ParseData("hello", "world", "0x0106") + data := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000" + exp1 := Hex2Bytes(data) + c.Assert(res1, checker.DeepEquals, exp1) +} + +func (s *BytesSuite) TestParseDataBytes(c *checker.C) { + data1 := []byte{232, 212, 165, 16, 0} + exp1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0} + + res1 := ParseData(data1) + c.Assert(res1, checker.DeepEquals, exp1) + +} + +func (s *BytesSuite) TestLeftPadBytes(c *checker.C) { + val1 := []byte{1, 2, 3, 4} + exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4} + + res1 := LeftPadBytes(val1, 8) + res2 := LeftPadBytes(val1, 2) + + c.Assert(res1, checker.DeepEquals, exp1) + c.Assert(res2, checker.DeepEquals, val1) +} + +func (s *BytesSuite) TestFormatData(c *checker.C) { + data1 := "" + data2 := "0xa9e67e00" + data3 := "a9e67e" + data4 := "\"a9e67e00\"" + + // exp1 := []byte{} + exp2 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xa9, 0xe6, 0x7e, 00} + exp3 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00} + exp4 := []byte{0x61, 0x39, 0x65, 0x36, 0x37, 0x65, 0x30, 0x30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00} + + res1 := FormatData(data1) + res2 := FormatData(data2) + res3 := FormatData(data3) + res4 := FormatData(data4) + + c.Assert(res1, checker.IsNil) + c.Assert(res2, checker.DeepEquals, exp2) + c.Assert(res3, checker.DeepEquals, exp3) + c.Assert(res4, checker.DeepEquals, exp4) +} + +func (s *BytesSuite) TestRightPadBytes(c *checker.C) { + val := []byte{1, 2, 3, 4} + exp := []byte{1, 2, 3, 4, 0, 0, 0, 0} + + resstd := RightPadBytes(val, 8) + resshrt := RightPadBytes(val, 2) + + c.Assert(resstd, checker.DeepEquals, exp) + c.Assert(resshrt, checker.DeepEquals, val) +} + +func (s *BytesSuite) TestLeftPadString(c *checker.C) { + val := "test" + exp := "\x30\x30\x30\x30" + val + + resstd := LeftPadString(val, 8) + resshrt := LeftPadString(val, 2) + + c.Assert(resstd, checker.Equals, exp) + c.Assert(resshrt, checker.Equals, val) +} + +func (s *BytesSuite) TestRightPadString(c *checker.C) { + val := "test" + exp := val + "\x30\x30\x30\x30" + + resstd := RightPadString(val, 8) + resshrt := RightPadString(val, 2) + + c.Assert(resstd, checker.Equals, exp) + c.Assert(resshrt, checker.Equals, val) +} + +func TestFromHex(t *testing.T) { + input := "0x01" + expected := []byte{1} + result := FromHex(input) + if !bytes.Equal(expected, result) { + t.Errorf("Expected % x got % x", expected, result) + } +} + +func TestFromHexOddLength(t *testing.T) { + input := "0x1" + expected := []byte{1} + result := FromHex(input) + if !bytes.Equal(expected, result) { + t.Errorf("Expected % x got % x", expected, result) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity.go b/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity.go new file mode 100644 index 0000000..d27bddd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity.go @@ -0,0 +1,186 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package compiler wraps the Solidity compiler executable (solc). +package compiler + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os/exec" + "regexp" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + versionRegexp = regexp.MustCompile(`[0-9]+\.[0-9]+\.[0-9]+`) + solcParams = []string{ + "--combined-json", "bin,abi,userdoc,devdoc", + "--add-std", // include standard lib contracts + "--optimize", // code optimizer switched on + } +) + +type Contract struct { + Code string `json:"code"` + Info ContractInfo `json:"info"` +} + +type ContractInfo struct { + Source string `json:"source"` + Language string `json:"language"` + LanguageVersion string `json:"languageVersion"` + CompilerVersion string `json:"compilerVersion"` + CompilerOptions string `json:"compilerOptions"` + AbiDefinition interface{} `json:"abiDefinition"` + UserDoc interface{} `json:"userDoc"` + DeveloperDoc interface{} `json:"developerDoc"` +} + +// Solidity contains information about the solidity compiler. +type Solidity struct { + Path, Version, FullVersion string +} + +// --combined-output format +type solcOutput struct { + Contracts map[string]struct{ Bin, Abi, Devdoc, Userdoc string } + Version string +} + +// SolidityVersion runs solc and parses its version output. +func SolidityVersion(solc string) (*Solidity, error) { + if solc == "" { + solc = "solc" + } + var out bytes.Buffer + cmd := exec.Command(solc, "--version") + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return nil, err + } + s := &Solidity{ + Path: cmd.Path, + FullVersion: out.String(), + Version: versionRegexp.FindString(out.String()), + } + return s, nil +} + +// CompileSolidityString builds and returns all the contracts contained within a source string. +func CompileSolidityString(solc, source string) (map[string]*Contract, error) { + if len(source) == 0 { + return nil, errors.New("solc: empty source string") + } + if solc == "" { + solc = "solc" + } + args := append(solcParams, "--") + cmd := exec.Command(solc, append(args, "-")...) + cmd.Stdin = strings.NewReader(source) + return runsolc(cmd, source) +} + +// CompileSolidity compiles all given Solidity source files. +func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) { + if len(sourcefiles) == 0 { + return nil, errors.New("solc: no source files") + } + source, err := slurpFiles(sourcefiles) + if err != nil { + return nil, err + } + if solc == "" { + solc = "solc" + } + args := append(solcParams, "--") + cmd := exec.Command(solc, append(args, sourcefiles...)...) + return runsolc(cmd, source) +} + +func runsolc(cmd *exec.Cmd, source string) (map[string]*Contract, error) { + var stderr, stdout bytes.Buffer + cmd.Stderr = &stderr + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) + } + var output solcOutput + if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { + return nil, err + } + shortVersion := versionRegexp.FindString(output.Version) + + // Compilation succeeded, assemble and return the contracts. + contracts := make(map[string]*Contract) + for name, info := range output.Contracts { + // Parse the individual compilation results. + var abi interface{} + if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { + return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) + } + var userdoc interface{} + if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { + return nil, fmt.Errorf("solc: error reading user doc: %v", err) + } + var devdoc interface{} + if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { + return nil, fmt.Errorf("solc: error reading dev doc: %v", err) + } + contracts[name] = &Contract{ + Code: "0x" + info.Bin, + Info: ContractInfo{ + Source: source, + Language: "Solidity", + LanguageVersion: shortVersion, + CompilerVersion: shortVersion, + CompilerOptions: strings.Join(solcParams, " "), + AbiDefinition: abi, + UserDoc: userdoc, + DeveloperDoc: devdoc, + }, + } + } + return contracts, nil +} + +func slurpFiles(files []string) (string, error) { + var concat bytes.Buffer + for _, file := range files { + content, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + concat.Write(content) + } + return concat.String(), nil +} + +// SaveInfo serializes info to the given file and returns its Keccak256 hash. +func SaveInfo(info *ContractInfo, filename string) (common.Hash, error) { + infojson, err := json.Marshal(info) + if err != nil { + return common.Hash{}, err + } + contenthash := common.BytesToHash(crypto.Keccak256(infojson)) + return contenthash, ioutil.WriteFile(filename, infojson, 0600) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity_test.go b/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity_test.go new file mode 100644 index 0000000..f166375 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/compiler/solidity_test.go @@ -0,0 +1,106 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package compiler + +import ( + "encoding/json" + "io/ioutil" + "os" + "os/exec" + "path" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + testSource = ` +contract test { + /// @notice Will multiply ` + "`a`" + ` by 7. + function multiply(uint a) returns(uint d) { + return a * 7; + } +} +` + testInfo = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0.1.1","compilerVersion":"0.1.1","compilerOptions":"--binary file --json-abi file --add-std 1","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}` +) + +func skipWithoutSolc(t *testing.T) { + if _, err := exec.LookPath("solc"); err != nil { + t.Skip(err) + } +} + +func TestCompiler(t *testing.T) { + skipWithoutSolc(t) + + contracts, err := CompileSolidityString("", testSource) + if err != nil { + t.Fatalf("error compiling source. result %v: %v", contracts, err) + } + if len(contracts) != 1 { + t.Errorf("one contract expected, got %d", len(contracts)) + } + c, ok := contracts["test"] + if !ok { + t.Fatal("info for contract 'test' not present in result") + } + if c.Code == "" { + t.Error("empty code") + } + if c.Info.Source != testSource { + t.Error("wrong source") + } + if c.Info.CompilerVersion == "" { + t.Error("empty version") + } +} + +func TestCompileError(t *testing.T) { + skipWithoutSolc(t) + + contracts, err := CompileSolidityString("", testSource[4:]) + if err == nil { + t.Errorf("error expected compiling source. got none. result %v", contracts) + } + t.Logf("error: %v", err) +} + +func TestSaveInfo(t *testing.T) { + var cinfo ContractInfo + err := json.Unmarshal([]byte(testInfo), &cinfo) + if err != nil { + t.Errorf("%v", err) + } + filename := path.Join(os.TempDir(), "solctest.info.json") + os.Remove(filename) + cinfohash, err := SaveInfo(&cinfo, filename) + if err != nil { + t.Errorf("error extracting info: %v", err) + } + got, err := ioutil.ReadFile(filename) + if err != nil { + t.Errorf("error reading '%v': %v", filename, err) + } + if string(got) != testInfo { + t.Errorf("incorrect info.json extracted, expected:\n%s\ngot\n%s", testInfo, string(got)) + } + wantHash := common.HexToHash("0x22450a77f0c3ff7a395948d07bc1456881226a1b6325f4189cb5f1254a824080") + if cinfohash != wantHash { + t.Errorf("content hash for info is incorrect. expected %v, got %v", wantHash.Hex(), cinfohash.Hex()) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/debug.go b/vendor/github.com/ethereum/go-ethereum/common/debug.go new file mode 100644 index 0000000..61acd8c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/debug.go @@ -0,0 +1,52 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "os" + "runtime" + "runtime/debug" + "strings" +) + +// Report gives off a warning requesting the user to submit an issue to the github tracker. +func Report(extra ...interface{}) { + fmt.Fprintln(os.Stderr, "You've encountered a sought after, hard to reproduce bug. Please report this to the developers <3 https://github.com/ethereum/go-ethereum/issues") + fmt.Fprintln(os.Stderr, extra...) + + _, file, line, _ := runtime.Caller(1) + fmt.Fprintf(os.Stderr, "%v:%v\n", file, line) + + debug.PrintStack() + + fmt.Fprintln(os.Stderr, "#### BUG! PLEASE REPORT ####") +} + +// PrintDepricationWarning prinst the given string in a box using fmt.Println. +func PrintDepricationWarning(str string) { + line := strings.Repeat("#", len(str)+4) + emptyLine := strings.Repeat(" ", len(str)) + fmt.Printf(` +%s +# %s # +# %s # +# %s # +%s + +`, line, emptyLine, str, emptyLine, line) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/format.go b/vendor/github.com/ethereum/go-ethereum/common/format.go new file mode 100644 index 0000000..fccc299 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/format.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "regexp" + "strings" + "time" +) + +// PrettyDuration is a pretty printed version of a time.Duration value that cuts +// the unnecessary precision off from the formatted textual representation. +type PrettyDuration time.Duration + +var prettyDurationRe = regexp.MustCompile(`\.[0-9]+`) + +// String implements the Stringer interface, allowing pretty printing of duration +// values rounded to three decimals. +func (d PrettyDuration) String() string { + label := fmt.Sprintf("%v", time.Duration(d)) + if match := prettyDurationRe.FindString(label); len(match) > 4 { + label = strings.Replace(label, match, match[:4], 1) + } + return label +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go new file mode 100644 index 0000000..4ec0ee8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil.go @@ -0,0 +1,227 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package hexutil implements hex encoding with 0x prefix. +This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads. + +Encoding Rules + +All hex data must have prefix "0x". + +For byte slices, the hex data must be of even length. An empty byte slice +encodes as "0x". + +Integers are encoded using the least amount of digits (no leading zero digits). Their +encoding may be of uneven length. The number zero encodes as "0x0". +*/ +package hexutil + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "strconv" +) + +const uintBits = 32 << (uint64(^uint(0)) >> 63) + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrMissingPrefix = errors.New("missing 0x prefix for hex data") + ErrSyntax = errors.New("invalid hex") + ErrEmptyNumber = errors.New("hex number has no digits after 0x") + ErrLeadingZero = errors.New("hex number has leading zero digits after 0x") + ErrOddLength = errors.New("hex string has odd length") + ErrUint64Range = errors.New("hex number does not fit into 64 bits") + ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits) +) + +// Decode decodes a hex string with 0x prefix. +func Decode(input string) ([]byte, error) { + if len(input) == 0 { + return nil, ErrEmptyString + } + if !has0xPrefix(input) { + return nil, ErrMissingPrefix + } + return hex.DecodeString(input[2:]) +} + +// MustDecode decodes a hex string with 0x prefix. It panics for invalid input. +func MustDecode(input string) []byte { + dec, err := Decode(input) + if err != nil { + panic(err) + } + return dec +} + +// Encode encodes b as a hex string with 0x prefix. +func Encode(b []byte) string { + enc := make([]byte, len(b)*2+2) + copy(enc, "0x") + hex.Encode(enc[2:], b) + return string(enc) +} + +// DecodeUint64 decodes a hex string with 0x prefix as a quantity. +func DecodeUint64(input string) (uint64, error) { + raw, err := checkNumber(input) + if err != nil { + return 0, err + } + dec, err := strconv.ParseUint(raw, 16, 64) + if err != nil { + err = mapError(err) + } + return dec, err +} + +// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity. +// It panics for invalid input. +func MustDecodeUint64(input string) uint64 { + dec, err := DecodeUint64(input) + if err != nil { + panic(err) + } + return dec +} + +// EncodeUint64 encodes i as a hex string with 0x prefix. +func EncodeUint64(i uint64) string { + enc := make([]byte, 2, 10) + copy(enc, "0x") + return string(strconv.AppendUint(enc, i, 16)) +} + +var bigWordNibbles int + +func init() { + // This is a weird way to compute the number of nibbles required for big.Word. + // The usual way would be to use constant arithmetic but go vet can't handle that. + b, _ := new(big.Int).SetString("FFFFFFFFFF", 16) + switch len(b.Bits()) { + case 1: + bigWordNibbles = 16 + case 2: + bigWordNibbles = 8 + default: + panic("weird big.Word size") + } +} + +// DecodeBig decodes a hex string with 0x prefix as a quantity. +func DecodeBig(input string) (*big.Int, error) { + raw, err := checkNumber(input) + if err != nil { + return nil, err + } + words := make([]big.Word, len(raw)/bigWordNibbles+1) + end := len(raw) + for i := range words { + start := end - bigWordNibbles + if start < 0 { + start = 0 + } + for ri := start; ri < end; ri++ { + nib := decodeNibble(raw[ri]) + if nib == badNibble { + return nil, ErrSyntax + } + words[i] *= 16 + words[i] += big.Word(nib) + } + end = start + } + dec := new(big.Int).SetBits(words) + return dec, nil +} + +// MustDecodeBig decodes a hex string with 0x prefix as a quantity. +// It panics for invalid input. +func MustDecodeBig(input string) *big.Int { + dec, err := DecodeBig(input) + if err != nil { + panic(err) + } + return dec +} + +// EncodeBig encodes bigint as a hex string with 0x prefix. +// The sign of the integer is ignored. +func EncodeBig(bigint *big.Int) string { + nbits := bigint.BitLen() + if nbits == 0 { + return "0x0" + } + return fmt.Sprintf("0x%x", bigint) +} + +func has0xPrefix(input string) bool { + return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') +} + +func checkNumber(input string) (raw string, err error) { + if len(input) == 0 { + return "", ErrEmptyString + } + if !has0xPrefix(input) { + return "", ErrMissingPrefix + } + input = input[2:] + if len(input) == 0 { + return "", ErrEmptyNumber + } + if len(input) > 1 && input[0] == '0' { + return "", ErrLeadingZero + } + return input, nil +} + +const badNibble = ^uint64(0) + +func decodeNibble(in byte) uint64 { + switch { + case in >= '0' && in <= '9': + return uint64(in - '0') + case in >= 'A' && in <= 'F': + return uint64(in - 'A' + 10) + case in >= 'a' && in <= 'f': + return uint64(in - 'a' + 10) + default: + return badNibble + } +} + +func mapError(err error) error { + if err, ok := err.(*strconv.NumError); ok { + switch err.Err { + case strconv.ErrRange: + return ErrUint64Range + case strconv.ErrSyntax: + return ErrSyntax + } + } + if _, ok := err.(hex.InvalidByteError); ok { + return ErrSyntax + } + if err == hex.ErrLength { + return ErrOddLength + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil_test.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil_test.go new file mode 100644 index 0000000..324e9d3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/hexutil_test.go @@ -0,0 +1,187 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package hexutil + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" +) + +type marshalTest struct { + input interface{} + want string +} + +type unmarshalTest struct { + input string + want interface{} + wantErr error +} + +var ( + encodeBytesTests = []marshalTest{ + {[]byte{}, "0x"}, + {[]byte{0}, "0x00"}, + {[]byte{0, 0, 1, 2}, "0x00000102"}, + } + + encodeBigTests = []marshalTest{ + {referenceBig("0"), "0x0"}, + {referenceBig("1"), "0x1"}, + {referenceBig("ff"), "0xff"}, + {referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"}, + {referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"}, + } + + encodeUint64Tests = []marshalTest{ + {uint64(0), "0x0"}, + {uint64(1), "0x1"}, + {uint64(0xff), "0xff"}, + {uint64(0x1122334455667788), "0x1122334455667788"}, + } + + decodeBytesTests = []unmarshalTest{ + // invalid + {input: ``, wantErr: ErrEmptyString}, + {input: `0`, wantErr: ErrMissingPrefix}, + {input: `0x0`, wantErr: hex.ErrLength}, + {input: `0x023`, wantErr: hex.ErrLength}, + {input: `0xxx`, wantErr: hex.InvalidByteError('x')}, + {input: `0x01zz01`, wantErr: hex.InvalidByteError('z')}, + // valid + {input: `0x`, want: []byte{}}, + {input: `0X`, want: []byte{}}, + {input: `0x02`, want: []byte{0x02}}, + {input: `0X02`, want: []byte{0x02}}, + {input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}}, + { + input: `0xffffffffffffffffffffffffffffffffffff`, + want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + } + + decodeBigTests = []unmarshalTest{ + // invalid + {input: `0`, wantErr: ErrMissingPrefix}, + {input: `0x`, wantErr: ErrEmptyNumber}, + {input: `0x01`, wantErr: ErrLeadingZero}, + {input: `0xx`, wantErr: ErrSyntax}, + {input: `0x1zz01`, wantErr: ErrSyntax}, + // valid + {input: `0x0`, want: big.NewInt(0)}, + {input: `0x2`, want: big.NewInt(0x2)}, + {input: `0x2F2`, want: big.NewInt(0x2f2)}, + {input: `0X2F2`, want: big.NewInt(0x2f2)}, + {input: `0x1122aaff`, want: big.NewInt(0x1122aaff)}, + {input: `0xbBb`, want: big.NewInt(0xbbb)}, + {input: `0xfffffffff`, want: big.NewInt(0xfffffffff)}, + { + input: `0x112233445566778899aabbccddeeff`, + want: referenceBig("112233445566778899aabbccddeeff"), + }, + { + input: `0xffffffffffffffffffffffffffffffffffff`, + want: referenceBig("ffffffffffffffffffffffffffffffffffff"), + }, + } + + decodeUint64Tests = []unmarshalTest{ + // invalid + {input: `0`, wantErr: ErrMissingPrefix}, + {input: `0x`, wantErr: ErrEmptyNumber}, + {input: `0x01`, wantErr: ErrLeadingZero}, + {input: `0xfffffffffffffffff`, wantErr: ErrUint64Range}, + {input: `0xx`, wantErr: ErrSyntax}, + {input: `0x1zz01`, wantErr: ErrSyntax}, + // valid + {input: `0x0`, want: uint64(0)}, + {input: `0x2`, want: uint64(0x2)}, + {input: `0x2F2`, want: uint64(0x2f2)}, + {input: `0X2F2`, want: uint64(0x2f2)}, + {input: `0x1122aaff`, want: uint64(0x1122aaff)}, + {input: `0xbbb`, want: uint64(0xbbb)}, + {input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)}, + } +) + +func TestEncode(t *testing.T) { + for _, test := range encodeBytesTests { + enc := Encode(test.input.([]byte)) + if enc != test.want { + t.Errorf("input %x: wrong encoding %s", test.input, enc) + } + } +} + +func TestDecode(t *testing.T) { + for _, test := range decodeBytesTests { + dec, err := Decode(test.input) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if !bytes.Equal(test.want.([]byte), dec) { + t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) + continue + } + } +} + +func TestEncodeBig(t *testing.T) { + for _, test := range encodeBigTests { + enc := EncodeBig(test.input.(*big.Int)) + if enc != test.want { + t.Errorf("input %x: wrong encoding %s", test.input, enc) + } + } +} + +func TestDecodeBig(t *testing.T) { + for _, test := range decodeBigTests { + dec, err := DecodeBig(test.input) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if dec.Cmp(test.want.(*big.Int)) != 0 { + t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) + continue + } + } +} + +func TestEncodeUint64(t *testing.T) { + for _, test := range encodeUint64Tests { + enc := EncodeUint64(test.input.(uint64)) + if enc != test.want { + t.Errorf("input %x: wrong encoding %s", test.input, enc) + } + } +} + +func TestDecodeUint64(t *testing.T) { + for _, test := range decodeUint64Tests { + dec, err := DecodeUint64(test.input) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if dec != test.want.(uint64) { + t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) + continue + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go new file mode 100644 index 0000000..7e4736d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json.go @@ -0,0 +1,266 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package hexutil + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "strconv" +) + +var ( + jsonNull = []byte("null") + jsonZero = []byte(`"0x0"`) + errNonString = errors.New("cannot unmarshal non-string as hex data") + errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer") +) + +// Bytes marshals/unmarshals as a JSON string with 0x prefix. +// The empty slice marshals as "0x". +type Bytes []byte + +// MarshalJSON implements json.Marshaler. +func (b Bytes) MarshalJSON() ([]byte, error) { + result := make([]byte, len(b)*2+4) + copy(result, `"0x`) + hex.Encode(result[3:], b) + result[len(result)-1] = '"' + return result, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Bytes) UnmarshalJSON(input []byte) error { + raw, err := checkJSON(input) + if err != nil { + return err + } + dec := make([]byte, len(raw)/2) + if _, err = hex.Decode(dec, raw); err != nil { + err = mapError(err) + } else { + *b = dec + } + return err +} + +// String returns the hex encoding of b. +func (b Bytes) String() string { + return Encode(b) +} + +// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out +// determines the required input length. This function is commonly used to implement the +// UnmarshalJSON method for fixed-size types: +// +// type Foo [8]byte +// +// func (f *Foo) UnmarshalJSON(input []byte) error { +// return hexutil.UnmarshalJSON("Foo", input, f[:]) +// } +func UnmarshalJSON(typname string, input, out []byte) error { + raw, err := checkJSON(input) + if err != nil { + return err + } + if len(raw)/2 != len(out) { + return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) + } + // Pre-verify syntax before modifying out. + for _, b := range raw { + if decodeNibble(b) == badNibble { + return ErrSyntax + } + } + hex.Decode(out, raw) + return nil +} + +// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as +// "0x0". Negative integers are not supported at this time. Attempting to marshal them +// will return an error. +type Big big.Int + +// MarshalJSON implements json.Marshaler. +func (b *Big) MarshalJSON() ([]byte, error) { + if b == nil { + return jsonNull, nil + } + bigint := (*big.Int)(b) + if bigint.Sign() == -1 { + return nil, errNegativeBigInt + } + nbits := bigint.BitLen() + if nbits == 0 { + return jsonZero, nil + } + enc := fmt.Sprintf(`"0x%x"`, bigint) + return []byte(enc), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Big) UnmarshalJSON(input []byte) error { + raw, err := checkNumberJSON(input) + if err != nil { + return err + } + words := make([]big.Word, len(raw)/bigWordNibbles+1) + end := len(raw) + for i := range words { + start := end - bigWordNibbles + if start < 0 { + start = 0 + } + for ri := start; ri < end; ri++ { + nib := decodeNibble(raw[ri]) + if nib == badNibble { + return ErrSyntax + } + words[i] *= 16 + words[i] += big.Word(nib) + } + end = start + } + var dec big.Int + dec.SetBits(words) + *b = (Big)(dec) + return nil +} + +// ToInt converts b to a big.Int. +func (b *Big) ToInt() *big.Int { + return (*big.Int)(b) +} + +// String returns the hex encoding of b. +func (b *Big) String() string { + return EncodeBig(b.ToInt()) +} + +// Uint64 marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type Uint64 uint64 + +// MarshalJSON implements json.Marshaler. +func (b Uint64) MarshalJSON() ([]byte, error) { + buf := make([]byte, 3, 12) + copy(buf, `"0x`) + buf = strconv.AppendUint(buf, uint64(b), 16) + buf = append(buf, '"') + return buf, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Uint64) UnmarshalJSON(input []byte) error { + raw, err := checkNumberJSON(input) + if err != nil { + return err + } + if len(raw) > 16 { + return ErrUint64Range + } + var dec uint64 + for _, byte := range raw { + nib := decodeNibble(byte) + if nib == badNibble { + return ErrSyntax + } + dec *= 16 + dec += uint64(nib) + } + *b = Uint64(dec) + return nil +} + +// String returns the hex encoding of b. +func (b Uint64) String() string { + return EncodeUint64(uint64(b)) +} + +// Uint marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type Uint uint + +// MarshalJSON implements json.Marshaler. +func (b Uint) MarshalJSON() ([]byte, error) { + return Uint64(b).MarshalJSON() +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *Uint) UnmarshalJSON(input []byte) error { + var u64 Uint64 + err := u64.UnmarshalJSON(input) + if err != nil { + return err + } else if u64 > Uint64(^uint(0)) { + return ErrUintRange + } + *b = Uint(u64) + return nil +} + +// String returns the hex encoding of b. +func (b Uint) String() string { + return EncodeUint64(uint64(b)) +} + +func isString(input []byte) bool { + return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' +} + +func bytesHave0xPrefix(input []byte) bool { + return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') +} + +func checkJSON(input []byte) (raw []byte, err error) { + if !isString(input) { + return nil, errNonString + } + if len(input) == 2 { + return nil, nil // empty strings are allowed + } + if !bytesHave0xPrefix(input[1:]) { + return nil, ErrMissingPrefix + } + input = input[3 : len(input)-1] + if len(input)%2 != 0 { + return nil, ErrOddLength + } + return input, nil +} + +func checkNumberJSON(input []byte) (raw []byte, err error) { + if !isString(input) { + return nil, errNonString + } + input = input[1 : len(input)-1] + if len(input) == 0 { + return nil, nil // empty strings are allowed + } + if !bytesHave0xPrefix(input) { + return nil, ErrMissingPrefix + } + input = input[2:] + if len(input) == 0 { + return nil, ErrEmptyNumber + } + if len(input) > 1 && input[0] == '0' { + return nil, ErrLeadingZero + } + return input, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/hexutil/json_test.go b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json_test.go new file mode 100644 index 0000000..290bf9c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/hexutil/json_test.go @@ -0,0 +1,258 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package hexutil + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" +) + +func checkError(t *testing.T, input string, got, want error) bool { + if got == nil { + if want != nil { + t.Errorf("input %s: got no error, want %q", input, want) + return false + } + return true + } + if want == nil { + t.Errorf("input %s: unexpected error %q", input, got) + } else if got.Error() != want.Error() { + t.Errorf("input %s: got error %q, want %q", input, got, want) + } + return false +} + +func referenceBig(s string) *big.Int { + b, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid") + } + return b +} + +func referenceBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +var unmarshalBytesTests = []unmarshalTest{ + // invalid encoding + {input: "", wantErr: errNonString}, + {input: "null", wantErr: errNonString}, + {input: "10", wantErr: errNonString}, + {input: `"0"`, wantErr: ErrMissingPrefix}, + {input: `"0x0"`, wantErr: ErrOddLength}, + {input: `"0xxx"`, wantErr: ErrSyntax}, + {input: `"0x01zz01"`, wantErr: ErrSyntax}, + + // valid encoding + {input: `""`, want: referenceBytes("")}, + {input: `"0x"`, want: referenceBytes("")}, + {input: `"0x02"`, want: referenceBytes("02")}, + {input: `"0X02"`, want: referenceBytes("02")}, + {input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")}, + { + input: `"0xffffffffffffffffffffffffffffffffffff"`, + want: referenceBytes("ffffffffffffffffffffffffffffffffffff"), + }, +} + +func TestUnmarshalBytes(t *testing.T) { + for _, test := range unmarshalBytesTests { + var v Bytes + err := v.UnmarshalJSON([]byte(test.input)) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if !bytes.Equal(test.want.([]byte), []byte(v)) { + t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want) + continue + } + } +} + +func BenchmarkUnmarshalBytes(b *testing.B) { + input := []byte(`"0x123456789abcdef123456789abcdef"`) + for i := 0; i < b.N; i++ { + var v Bytes + if err := v.UnmarshalJSON(input); err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalBytes(t *testing.T) { + for _, test := range encodeBytesTests { + in := test.input.([]byte) + out, err := Bytes(in).MarshalJSON() + if err != nil { + t.Errorf("%x: %v", in, err) + continue + } + if want := `"` + test.want + `"`; string(out) != want { + t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want) + continue + } + if out := Bytes(in).String(); out != test.want { + t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want) + continue + } + } +} + +var unmarshalBigTests = []unmarshalTest{ + // invalid encoding + {input: "", wantErr: errNonString}, + {input: "null", wantErr: errNonString}, + {input: "10", wantErr: errNonString}, + {input: `"0"`, wantErr: ErrMissingPrefix}, + {input: `"0x"`, wantErr: ErrEmptyNumber}, + {input: `"0x01"`, wantErr: ErrLeadingZero}, + {input: `"0xx"`, wantErr: ErrSyntax}, + {input: `"0x1zz01"`, wantErr: ErrSyntax}, + + // valid encoding + {input: `""`, want: big.NewInt(0)}, + {input: `"0x0"`, want: big.NewInt(0)}, + {input: `"0x2"`, want: big.NewInt(0x2)}, + {input: `"0x2F2"`, want: big.NewInt(0x2f2)}, + {input: `"0X2F2"`, want: big.NewInt(0x2f2)}, + {input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)}, + {input: `"0xbBb"`, want: big.NewInt(0xbbb)}, + {input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)}, + { + input: `"0x112233445566778899aabbccddeeff"`, + want: referenceBig("112233445566778899aabbccddeeff"), + }, + { + input: `"0xffffffffffffffffffffffffffffffffffff"`, + want: referenceBig("ffffffffffffffffffffffffffffffffffff"), + }, +} + +func TestUnmarshalBig(t *testing.T) { + for _, test := range unmarshalBigTests { + var v Big + err := v.UnmarshalJSON([]byte(test.input)) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 { + t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want) + continue + } + } +} + +func BenchmarkUnmarshalBig(b *testing.B) { + input := []byte(`"0x123456789abcdef123456789abcdef"`) + for i := 0; i < b.N; i++ { + var v Big + if err := v.UnmarshalJSON(input); err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalBig(t *testing.T) { + for _, test := range encodeBigTests { + in := test.input.(*big.Int) + out, err := (*Big)(in).MarshalJSON() + if err != nil { + t.Errorf("%d: %v", in, err) + continue + } + if want := `"` + test.want + `"`; string(out) != want { + t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want) + continue + } + if out := (*Big)(in).String(); out != test.want { + t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want) + continue + } + } +} + +var unmarshalUint64Tests = []unmarshalTest{ + // invalid encoding + {input: "", wantErr: errNonString}, + {input: "null", wantErr: errNonString}, + {input: "10", wantErr: errNonString}, + {input: `"0"`, wantErr: ErrMissingPrefix}, + {input: `"0x"`, wantErr: ErrEmptyNumber}, + {input: `"0x01"`, wantErr: ErrLeadingZero}, + {input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range}, + {input: `"0xx"`, wantErr: ErrSyntax}, + {input: `"0x1zz01"`, wantErr: ErrSyntax}, + + // valid encoding + {input: `""`, want: uint64(0)}, + {input: `"0x0"`, want: uint64(0)}, + {input: `"0x2"`, want: uint64(0x2)}, + {input: `"0x2F2"`, want: uint64(0x2f2)}, + {input: `"0X2F2"`, want: uint64(0x2f2)}, + {input: `"0x1122aaff"`, want: uint64(0x1122aaff)}, + {input: `"0xbbb"`, want: uint64(0xbbb)}, + {input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)}, +} + +func TestUnmarshalUint64(t *testing.T) { + for _, test := range unmarshalUint64Tests { + var v Uint64 + err := v.UnmarshalJSON([]byte(test.input)) + if !checkError(t, test.input, err, test.wantErr) { + continue + } + if uint64(v) != test.want.(uint64) { + t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want) + continue + } + } +} + +func BenchmarkUnmarshalUint64(b *testing.B) { + input := []byte(`"0x123456789abcdf"`) + for i := 0; i < b.N; i++ { + var v Uint64 + v.UnmarshalJSON(input) + } +} + +func TestMarshalUint64(t *testing.T) { + for _, test := range encodeUint64Tests { + in := test.input.(uint64) + out, err := Uint64(in).MarshalJSON() + if err != nil { + t.Errorf("%d: %v", in, err) + continue + } + if want := `"` + test.want + `"`; string(out) != want { + t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want) + continue + } + if out := (Uint64)(in).String(); out != test.want { + t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want) + continue + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/icap.go b/vendor/github.com/ethereum/go-ethereum/common/icap.go new file mode 100644 index 0000000..a36e669 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/icap.go @@ -0,0 +1,190 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Spec at https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol + +package common + +import ( + "errors" + "math/big" + "strconv" + "strings" +) + +var ( + Base36Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ICAPLengthError = errors.New("Invalid ICAP length") + ICAPEncodingError = errors.New("Invalid ICAP encoding") + ICAPChecksumError = errors.New("Invalid ICAP checksum") + ICAPCountryCodeError = errors.New("Invalid ICAP country code") + ICAPAssetIdentError = errors.New("Invalid ICAP asset identifier") + ICAPInstCodeError = errors.New("Invalid ICAP institution code") + ICAPClientIdentError = errors.New("Invalid ICAP client identifier") +) + +func ICAPToAddress(s string) (Address, error) { + switch len(s) { + case 35: // "XE" + 2 digit checksum + 31 base-36 chars of address + return parseICAP(s) + case 34: // "XE" + 2 digit checksum + 30 base-36 chars of address + return parseICAP(s) + case 20: // "XE" + 2 digit checksum + 3-char asset identifier + + // 4-char institution identifier + 9-char institution client identifier + return parseIndirectICAP(s) + default: + return Address{}, ICAPLengthError + } +} + +func parseICAP(s string) (Address, error) { + if !strings.HasPrefix(s, "XE") { + return Address{}, ICAPCountryCodeError + } + if err := validCheckSum(s); err != nil { + return Address{}, err + } + // checksum is ISO13616, Ethereum address is base-36 + bigAddr, _ := new(big.Int).SetString(s[4:], 36) + return BigToAddress(bigAddr), nil +} + +func parseIndirectICAP(s string) (Address, error) { + if !strings.HasPrefix(s, "XE") { + return Address{}, ICAPCountryCodeError + } + if s[4:7] != "ETH" { + return Address{}, ICAPAssetIdentError + } + if err := validCheckSum(s); err != nil { + return Address{}, err + } + // TODO: integrate with ICAP namereg + return Address{}, errors.New("not implemented") +} + +func AddressToICAP(a Address) (string, error) { + enc := base36Encode(a.Big()) + // zero padd encoded address to Direct ICAP length if needed + if len(enc) < 30 { + enc = join(strings.Repeat("0", 30-len(enc)), enc) + } + icap := join("XE", checkDigits(enc), enc) + return icap, nil +} + +// TODO: integrate with ICAP namereg when it's available +func AddressToIndirectICAP(a Address, instCode string) (string, error) { + // return addressToIndirectICAP(a, instCode) + return "", errors.New("not implemented") +} + +func addressToIndirectICAP(a Address, instCode string) (string, error) { + // TODO: add addressToClientIdent which grabs client ident from ICAP namereg + //clientIdent := addressToClientIdent(a) + clientIdent := "todo" + return clientIdentToIndirectICAP(instCode, clientIdent) +} + +func clientIdentToIndirectICAP(instCode, clientIdent string) (string, error) { + if len(instCode) != 4 || !validBase36(instCode) { + return "", ICAPInstCodeError + } + if len(clientIdent) != 9 || !validBase36(instCode) { + return "", ICAPClientIdentError + } + + // currently ETH is only valid asset identifier + s := join("ETH", instCode, clientIdent) + return join("XE", checkDigits(s), s), nil +} + +// https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN +func validCheckSum(s string) error { + s = join(s[4:], s[:4]) + expanded, err := iso13616Expand(s) + if err != nil { + return err + } + checkSumNum, _ := new(big.Int).SetString(expanded, 10) + if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 { + return ICAPChecksumError + } + return nil +} + +func checkDigits(s string) string { + expanded, _ := iso13616Expand(strings.Join([]string{s, "XE00"}, "")) + num, _ := new(big.Int).SetString(expanded, 10) + num.Sub(Big98, num.Mod(num, Big97)) + + checkDigits := num.String() + // zero padd checksum + if len(checkDigits) == 1 { + checkDigits = join("0", checkDigits) + } + return checkDigits +} + +// not base-36, but expansion to decimal literal: A = 10, B = 11, ... Z = 35 +func iso13616Expand(s string) (string, error) { + var parts []string + if !validBase36(s) { + return "", ICAPEncodingError + } + for _, c := range s { + i := uint64(c) + if i >= 65 { + parts = append(parts, strconv.FormatUint(uint64(c)-55, 10)) + } else { + parts = append(parts, string(c)) + } + } + return join(parts...), nil +} + +func base36Encode(i *big.Int) string { + var chars []rune + x := new(big.Int) + for { + x.Mod(i, Big36) + chars = append(chars, rune(Base36Chars[x.Uint64()])) + i.Div(i, Big36) + if i.Cmp(Big0) == 0 { + break + } + } + // reverse slice + for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { + chars[i], chars[j] = chars[j], chars[i] + } + return string(chars) +} + +func validBase36(s string) bool { + for _, c := range s { + i := uint64(c) + // 0-9 or A-Z + if i < 48 || (i > 57 && i < 65) || i > 90 { + return false + } + } + return true +} + +func join(s ...string) string { + return strings.Join(s, "") +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/icap_test.go b/vendor/github.com/ethereum/go-ethereum/common/icap_test.go new file mode 100644 index 0000000..6306686 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/icap_test.go @@ -0,0 +1,91 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import "testing" + +/* More test vectors: +https://github.com/ethereum/web3.js/blob/master/test/iban.fromAddress.js +https://github.com/ethereum/web3.js/blob/master/test/iban.toAddress.js +https://github.com/ethereum/web3.js/blob/master/test/iban.isValid.js +https://github.com/ethereum/libethereum/blob/develop/test/libethcore/icap.cpp +*/ + +type icapTest struct { + name string + addr string + icap string +} + +var icapOKTests = []icapTest{ + {"Direct1", "0x52dc504a422f0e2a9e7632a34a50f1a82f8224c7", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O7"}, + {"Direct2", "0x11c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE1222Q908LN1QBBU6XUQSO1OHWJIOS46OO"}, + {"DirectZeroPrefix", "0x00c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"}, + {"DirectDoubleZeroPrefix", "0x0000a5327eab78357cbf2ae8f3d49fd9d90c7d22", "XE0600DQK33XDTYUCRI0KYM5ELAKXDWWF6"}, +} + +var icapInvalidTests = []icapTest{ + {"DirectInvalidCheckSum", "", "XE7438O073KYGTWWZN0F2WZ0R8PX5ZPPZS"}, + {"DirectInvalidCountryCode", "", "XD7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"}, + {"DirectInvalidLength36", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O77"}, + {"DirectInvalidLength33", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2"}, + + {"IndirectInvalidCheckSum", "", "XE35ETHXREGGOPHERSSS"}, + {"IndirectInvalidAssetIdentifier", "", "XE34ETHXREGGOPHERSSS"}, + {"IndirectInvalidLength19", "", "XE34ETHXREGGOPHERSS"}, + {"IndirectInvalidLength21", "", "XE34ETHXREGGOPHERSSSS"}, +} + +func TestICAPOK(t *testing.T) { + for _, test := range icapOKTests { + decodeEncodeTest(HexToAddress(test.addr), test.icap, t) + } +} + +func TestICAPInvalid(t *testing.T) { + for _, test := range icapInvalidTests { + failedDecodingTest(test.icap, t) + } +} + +func decodeEncodeTest(addr0 Address, icap0 string, t *testing.T) { + icap1, err := AddressToICAP(addr0) + if err != nil { + t.Errorf("ICAP encoding failed: %s", err) + } + if icap1 != icap0 { + t.Errorf("ICAP mismatch: have: %s want: %s", icap1, icap0) + } + + addr1, err := ICAPToAddress(icap0) + if err != nil { + t.Errorf("ICAP decoding failed: %s", err) + } + if addr1 != addr0 { + t.Errorf("Address mismatch: have: %x want: %x", addr1, addr0) + } +} + +func failedDecodingTest(icap string, t *testing.T) { + addr, err := ICAPToAddress(icap) + if err == nil { + t.Errorf("Expected ICAP decoding to fail.") + } + if addr != (Address{}) { + t.Errorf("Expected empty Address on failed ICAP decoding.") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/list.go b/vendor/github.com/ethereum/go-ethereum/common/list.go new file mode 100644 index 0000000..07b2f17 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/list.go @@ -0,0 +1,97 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "encoding/json" + "reflect" + "sync" +) + +// The list type is an anonymous slice handler which can be used +// for containing any slice type to use in an environment which +// does not support slice types (e.g., JavaScript, QML) +type List struct { + mut sync.Mutex + val interface{} + list reflect.Value + Length int +} + +// Initialise a new list. Panics if non-slice type is given. +func NewList(t interface{}) *List { + list := reflect.ValueOf(t) + if list.Kind() != reflect.Slice { + panic("list container initialized with a non-slice type") + } + + return &List{sync.Mutex{}, t, list, list.Len()} +} + +func EmptyList() *List { + return NewList([]interface{}{}) +} + +// Get N element from the embedded slice. Returns nil if OOB. +func (self *List) Get(i int) interface{} { + if self.list.Len() > i { + self.mut.Lock() + defer self.mut.Unlock() + + i := self.list.Index(i).Interface() + + return i + } + + return nil +} + +func (self *List) GetAsJson(i int) interface{} { + e := self.Get(i) + + r, _ := json.Marshal(e) + + return string(r) +} + +// Appends value at the end of the slice. Panics when incompatible value +// is given. +func (self *List) Append(v interface{}) { + self.mut.Lock() + defer self.mut.Unlock() + + self.list = reflect.Append(self.list, reflect.ValueOf(v)) + self.Length = self.list.Len() +} + +// Returns the underlying slice as interface. +func (self *List) Interface() interface{} { + return self.list.Interface() +} + +// For JavaScript <3 +func (self *List) ToJSON() string { + // make(T, 0) != nil + list := make([]interface{}, 0) + for i := 0; i < self.Length; i++ { + list = append(list, self.Get(i)) + } + + data, _ := json.Marshal(list) + + return string(data) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/main_test.go b/vendor/github.com/ethereum/go-ethereum/common/main_test.go new file mode 100644 index 0000000..149d099 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/main_test.go @@ -0,0 +1,25 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "testing" + + checker "gopkg.in/check.v1" +) + +func Test(t *testing.T) { checker.TestingT(t) } diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/dist.go b/vendor/github.com/ethereum/go-ethereum/common/math/dist.go new file mode 100644 index 0000000..913fbfb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/dist.go @@ -0,0 +1,96 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +type Summer interface { + Sum(i int) *big.Int + Len() int +} + +func Sum(slice Summer) (sum *big.Int) { + sum = new(big.Int) + + for i := 0; i < slice.Len(); i++ { + sum.Add(sum, slice.Sum(i)) + } + return +} + +type Vector struct { + Gas, Price *big.Int +} + +type VectorsBy func(v1, v2 Vector) bool + +func (self VectorsBy) Sort(vectors []Vector) { + bs := vectorSorter{ + vectors: vectors, + by: self, + } + sort.Sort(bs) +} + +type vectorSorter struct { + vectors []Vector + by func(v1, v2 Vector) bool +} + +func (v vectorSorter) Len() int { return len(v.vectors) } +func (v vectorSorter) Less(i, j int) bool { return v.by(v.vectors[i], v.vectors[j]) } +func (v vectorSorter) Swap(i, j int) { v.vectors[i], v.vectors[j] = v.vectors[j], v.vectors[i] } + +func PriceSort(v1, v2 Vector) bool { return v1.Price.Cmp(v2.Price) < 0 } +func GasSort(v1, v2 Vector) bool { return v1.Gas.Cmp(v2.Gas) < 0 } + +type vectorSummer struct { + vectors []Vector + by func(v Vector) *big.Int +} + +type VectorSum func(v Vector) *big.Int + +func (v VectorSum) Sum(vectors []Vector) *big.Int { + vs := vectorSummer{ + vectors: vectors, + by: v, + } + return Sum(vs) +} + +func (v vectorSummer) Len() int { return len(v.vectors) } +func (v vectorSummer) Sum(i int) *big.Int { return v.by(v.vectors[i]) } + +func GasSum(v Vector) *big.Int { return v.Gas } + +var etherInWei = new(big.Rat).SetInt(common.String2Big("1000000000000000000")) + +func GasPrice(bp, gl, ep *big.Int) *big.Int { + BP := new(big.Rat).SetInt(bp) + GL := new(big.Rat).SetInt(gl) + EP := new(big.Rat).SetInt(ep) + GP := new(big.Rat).Quo(BP, GL) + GP = GP.Quo(GP, EP) + + return GP.Mul(GP, etherInWei).Num() +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/dist_test.go b/vendor/github.com/ethereum/go-ethereum/common/math/dist_test.go new file mode 100644 index 0000000..f5857b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/dist_test.go @@ -0,0 +1,82 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "fmt" + "math/big" + "testing" +) + +type summer struct { + numbers []*big.Int +} + +func (s summer) Len() int { return len(s.numbers) } +func (s summer) Sum(i int) *big.Int { + return s.numbers[i] +} + +func TestSum(t *testing.T) { + summer := summer{numbers: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}} + sum := Sum(summer) + if sum.Cmp(big.NewInt(6)) != 0 { + t.Errorf("got sum = %d, want 6", sum) + } +} + +func TestDist(t *testing.T) { + var vectors = []Vector{ + {big.NewInt(1000), big.NewInt(1234)}, + {big.NewInt(500), big.NewInt(10023)}, + {big.NewInt(1034), big.NewInt(1987)}, + {big.NewInt(1034), big.NewInt(1987)}, + {big.NewInt(8983), big.NewInt(1977)}, + {big.NewInt(98382), big.NewInt(1887)}, + {big.NewInt(12398), big.NewInt(1287)}, + {big.NewInt(12398), big.NewInt(1487)}, + {big.NewInt(12398), big.NewInt(1987)}, + {big.NewInt(12398), big.NewInt(128)}, + {big.NewInt(12398), big.NewInt(1987)}, + {big.NewInt(1398), big.NewInt(187)}, + {big.NewInt(12328), big.NewInt(1927)}, + {big.NewInt(12398), big.NewInt(1987)}, + {big.NewInt(22398), big.NewInt(1287)}, + {big.NewInt(1370), big.NewInt(1981)}, + {big.NewInt(12398), big.NewInt(1957)}, + {big.NewInt(42198), big.NewInt(1987)}, + } + + VectorsBy(GasSort).Sort(vectors) + fmt.Println(vectors) + + BP := big.NewInt(15) + GL := big.NewInt(1000000) + EP := big.NewInt(100) + fmt.Println("BP", BP, "GL", GL, "EP", EP) + GP := GasPrice(BP, GL, EP) + fmt.Println("GP =", GP, "Wei per GU") + + S := len(vectors) / 4 + fmt.Println("L", len(vectors), "S", S) + for i := 1; i <= S*4; i += S { + fmt.Printf("T%d = %v\n", i, vectors[i]) + } + + g := VectorSum(GasSum).Sum(vectors) + fmt.Printf("G = ∑g* (%v)\n", g) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/exp.go b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go new file mode 100644 index 0000000..113b76b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/exp.go @@ -0,0 +1,47 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package math + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// wordSize is the size number of bits in a big.Int Word. +const wordSize = 32 << (uint64(^big.Word(0)) >> 63) + +// Exp implement exponentiation by squaring algorithm. +// +// Exp return a new variable; base and exponent must +// not be changed under any circumstance. +// +// Courtesy @karalabe and @chfast +func Exp(base, exponent *big.Int) *big.Int { + result := big.NewInt(1) + + for _, word := range exponent.Bits() { + for i := 0; i < wordSize; i++ { + if word&1 == 1 { + common.U256(result.Mul(result, base)) + } + common.U256(base.Mul(base, base)) + word >>= 1 + } + } + return result +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/integer.go b/vendor/github.com/ethereum/go-ethereum/common/math/integer.go new file mode 100644 index 0000000..1689b65 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/integer.go @@ -0,0 +1,25 @@ +package math + +import gmath "math" + +/* + * NOTE: The following methods need to be optimised using either bit checking or asm + */ + +// SafeSub returns subtraction result and whether overflow occurred. +func SafeSub(x, y uint64) (uint64, bool) { + return x - y, x < y +} + +// SafeAdd returns the result and whether overflow occurred. +func SafeAdd(x, y uint64) (uint64, bool) { + return x + y, y > gmath.MaxUint64-x +} + +// SafeMul returns multiplication result and whether overflow occurred. +func SafeMul(x, y uint64) (uint64, bool) { + if x == 0 || y == 0 { + return 0, false + } + return x * y, y > gmath.MaxUint64/x +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/math/integer_test.go b/vendor/github.com/ethereum/go-ethereum/common/math/integer_test.go new file mode 100644 index 0000000..198114e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/math/integer_test.go @@ -0,0 +1,50 @@ +package math + +import ( + gmath "math" + "testing" +) + +type operation byte + +const ( + sub operation = iota + add + mul +) + +func TestOverflow(t *testing.T) { + for i, test := range []struct { + x uint64 + y uint64 + overflow bool + op operation + }{ + // add operations + {gmath.MaxUint64, 1, true, add}, + {gmath.MaxUint64 - 1, 1, false, add}, + + // sub operations + {0, 1, true, sub}, + {0, 0, false, sub}, + + // mul operations + {10, 10, false, mul}, + {gmath.MaxUint64, 2, true, mul}, + {gmath.MaxUint64, 1, false, mul}, + } { + var overflows bool + switch test.op { + case sub: + _, overflows = SafeSub(test.x, test.y) + case add: + _, overflows = SafeAdd(test.x, test.y) + case mul: + _, overflows = SafeMul(test.x, test.y) + } + + if test.overflow != overflows { + t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go new file mode 100644 index 0000000..9200525 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go @@ -0,0 +1,30 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// package mclock is a wrapper for a monotonic clock source +package mclock + +import ( + "time" + + "github.com/aristanetworks/goarista/monotime" +) + +type AbsTime time.Duration // absolute monotonic time + +func Now() AbsTime { + return AbsTime(monotime.Now()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/number/int.go b/vendor/github.com/ethereum/go-ethereum/common/number/int.go new file mode 100644 index 0000000..6dab243 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/number/int.go @@ -0,0 +1,197 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package number + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) + +func limitUnsigned256(x *Number) *Number { + x.num.And(x.num, tt256m1) + return x +} + +func limitSigned256(x *Number) *Number { + if x.num.Cmp(tt255) < 0 { + return x + } else { + x.num.Sub(x.num, tt256) + return x + } +} + +// Number function +type Initialiser func(n int64) *Number + +// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations +// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda +// with the new Initialiser. +type Number struct { + num *big.Int + limit func(n *Number) *Number +} + +// Returns a new initialiser for a new *Number without having to expose certain fields +func NewInitialiser(limiter func(*Number) *Number) Initialiser { + return func(n int64) *Number { + return &Number{big.NewInt(n), limiter} + } +} + +// Return a Number with a UNSIGNED limiter up to 256 bits +func Uint256(n int64) *Number { + return &Number{big.NewInt(n), limitUnsigned256} +} + +// Return a Number with a SIGNED limiter up to 256 bits +func Int256(n int64) *Number { + return &Number{big.NewInt(n), limitSigned256} +} + +// Returns a Number with a SIGNED unlimited size +func Big(n int64) *Number { + return &Number{big.NewInt(n), func(x *Number) *Number { return x }} +} + +// Sets i to sum of x+y +func (i *Number) Add(x, y *Number) *Number { + i.num.Add(x.num, y.num) + return i.limit(i) +} + +// Sets i to difference of x-y +func (i *Number) Sub(x, y *Number) *Number { + i.num.Sub(x.num, y.num) + return i.limit(i) +} + +// Sets i to product of x*y +func (i *Number) Mul(x, y *Number) *Number { + i.num.Mul(x.num, y.num) + return i.limit(i) +} + +// Sets i to the quotient prodject of x/y +func (i *Number) Div(x, y *Number) *Number { + i.num.Div(x.num, y.num) + return i.limit(i) +} + +// Sets i to x % y +func (i *Number) Mod(x, y *Number) *Number { + i.num.Mod(x.num, y.num) + return i.limit(i) +} + +// Sets i to x << s +func (i *Number) Lsh(x *Number, s uint) *Number { + i.num.Lsh(x.num, s) + return i.limit(i) +} + +// Sets i to x^y +func (i *Number) Pow(x, y *Number) *Number { + i.num.Exp(x.num, y.num, big.NewInt(0)) + return i.limit(i) +} + +// Setters + +// Set x to i +func (i *Number) Set(x *Number) *Number { + i.num.Set(x.num) + return i.limit(i) +} + +// Set x bytes to i +func (i *Number) SetBytes(x []byte) *Number { + i.num.SetBytes(x) + return i.limit(i) +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +func (i *Number) Cmp(x *Number) int { + return i.num.Cmp(x.num) +} + +// Getters + +// Returns the string representation of i +func (i *Number) String() string { + return i.num.String() +} + +// Returns the byte representation of i +func (i *Number) Bytes() []byte { + return i.num.Bytes() +} + +// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined. +func (i *Number) Uint64() uint64 { + return i.num.Uint64() +} + +// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined. +func (i *Number) Int64() int64 { + return i.num.Int64() +} + +// Returns the signed version of i +func (i *Number) Int256() *Number { + return Int(0).Set(i) +} + +// Returns the unsigned version of i +func (i *Number) Uint256() *Number { + return Uint(0).Set(i) +} + +// Returns the index of the first bit that's set to 1 +func (i *Number) FirstBitSet() int { + for j := 0; j < i.num.BitLen(); j++ { + if i.num.Bit(j) > 0 { + return j + } + } + + return i.num.BitLen() +} + +// Variables + +var ( + Zero = Uint(0) + One = Uint(1) + Two = Uint(2) + MaxUint256 = Uint(0).SetBytes(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + + MinOne = Int(-1) + + // "typedefs" + Uint = Uint256 + Int = Int256 +) diff --git a/vendor/github.com/ethereum/go-ethereum/common/number/uint_test.go b/vendor/github.com/ethereum/go-ethereum/common/number/uint_test.go new file mode 100644 index 0000000..3ab9e4c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/number/uint_test.go @@ -0,0 +1,108 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package number + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestSet(t *testing.T) { + a := Uint(0) + b := Uint(10) + a.Set(b) + if a.num.Cmp(b.num) != 0 { + t.Error("didn't compare", a, b) + } + + c := Uint(0).SetBytes(common.Hex2Bytes("0a")) + if c.num.Cmp(big.NewInt(10)) != 0 { + t.Error("c set bytes failed.") + } +} + +func TestInitialiser(t *testing.T) { + check := false + init := NewInitialiser(func(x *Number) *Number { + check = true + return x + }) + a := init(0).Add(init(1), init(2)) + if a.Cmp(init(3)) != 0 { + t.Error("expected 3. got", a) + } + if !check { + t.Error("expected limiter to be called") + } +} + +func TestGet(t *testing.T) { + a := Uint(10) + if a.Uint64() != 10 { + t.Error("expected to get 10. got", a.Uint64()) + } + + a = Uint(10) + if a.Int64() != 10 { + t.Error("expected to get 10. got", a.Int64()) + } +} + +func TestCmp(t *testing.T) { + a := Uint(10) + b := Uint(10) + c := Uint(11) + + if a.Cmp(b) != 0 { + t.Error("a b == 0 failed", a, b) + } + + if a.Cmp(c) >= 0 { + t.Error("a c < 0 failed", a, c) + } + + if c.Cmp(b) <= 0 { + t.Error("c b > 0 failed", c, b) + } +} + +func TestMaxArith(t *testing.T) { + a := Uint(0).Add(MaxUint256, One) + if a.Cmp(Zero) != 0 { + t.Error("expected max256 + 1 = 0 got", a) + } + + a = Uint(0).Sub(Uint(0), One) + if a.Cmp(MaxUint256) != 0 { + t.Error("expected 0 - 1 = max256 got", a) + } + + a = Int(0).Sub(Int(0), One) + if a.Cmp(MinOne) != 0 { + t.Error("expected 0 - 1 = -1 got", a) + } +} + +func TestConversion(t *testing.T) { + a := Int(-1) + b := a.Uint256() + if b.Cmp(MaxUint256) != 0 { + t.Error("expected -1 => unsigned to return max. got", b) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/path.go b/vendor/github.com/ethereum/go-ethereum/common/path.go new file mode 100644 index 0000000..bd8da86 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/path.go @@ -0,0 +1,47 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "os" + "path/filepath" + "runtime" +) + +// MakeName creates a node name that follows the ethereum convention +// for such names. It adds the operation system name and Go runtime version +// the name. +func MakeName(name, version string) string { + return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version()) +} + +func FileExist(filePath string) bool { + _, err := os.Stat(filePath) + if err != nil && os.IsNotExist(err) { + return false + } + + return true +} + +func AbsolutePath(Datadir string, filename string) string { + if filepath.IsAbs(filename) { + return filename + } + return filepath.Join(Datadir, filename) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/size.go b/vendor/github.com/ethereum/go-ethereum/common/size.go new file mode 100644 index 0000000..9653b36 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/size.go @@ -0,0 +1,89 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "math/big" +) + +type StorageSize float64 + +func (self StorageSize) String() string { + if self > 1000000 { + return fmt.Sprintf("%.2f mB", self/1000000) + } else if self > 1000 { + return fmt.Sprintf("%.2f kB", self/1000) + } else { + return fmt.Sprintf("%.2f B", self) + } +} + +func (self StorageSize) Int64() int64 { + return int64(self) +} + +// The different number of units +var ( + Douglas = BigPow(10, 42) + Einstein = BigPow(10, 21) + Ether = BigPow(10, 18) + Finney = BigPow(10, 15) + Szabo = BigPow(10, 12) + Shannon = BigPow(10, 9) + Babbage = BigPow(10, 6) + Ada = BigPow(10, 3) + Wei = big.NewInt(1) +) + +// +// Currency to string +// Returns a string representing a human readable format +func CurrencyToString(num *big.Int) string { + var ( + fin *big.Int = num + denom string = "Wei" + ) + + switch { + case num.Cmp(Ether) >= 0: + fin = new(big.Int).Div(num, Ether) + denom = "Ether" + case num.Cmp(Finney) >= 0: + fin = new(big.Int).Div(num, Finney) + denom = "Finney" + case num.Cmp(Szabo) >= 0: + fin = new(big.Int).Div(num, Szabo) + denom = "Szabo" + case num.Cmp(Shannon) >= 0: + fin = new(big.Int).Div(num, Shannon) + denom = "Shannon" + case num.Cmp(Babbage) >= 0: + fin = new(big.Int).Div(num, Babbage) + denom = "Babbage" + case num.Cmp(Ada) >= 0: + fin = new(big.Int).Div(num, Ada) + denom = "Ada" + } + + // TODO add comment clarifying expected behavior + if len(fin.String()) > 5 { + return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom) + } + + return fmt.Sprintf("%v %s", fin, denom) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/size_test.go b/vendor/github.com/ethereum/go-ethereum/common/size_test.go new file mode 100644 index 0000000..ce19cab --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/size_test.go @@ -0,0 +1,59 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "math/big" + + checker "gopkg.in/check.v1" +) + +type SizeSuite struct{} + +var _ = checker.Suite(&SizeSuite{}) + +func (s *SizeSuite) TestStorageSizeString(c *checker.C) { + data1 := 2381273 + data2 := 2192 + data3 := 12 + + exp1 := "2.38 mB" + exp2 := "2.19 kB" + exp3 := "12.00 B" + + c.Assert(StorageSize(data1).String(), checker.Equals, exp1) + c.Assert(StorageSize(data2).String(), checker.Equals, exp2) + c.Assert(StorageSize(data3).String(), checker.Equals, exp3) +} + +func (s *SizeSuite) TestCommon(c *checker.C) { + ether := CurrencyToString(BigPow(10, 19)) + finney := CurrencyToString(BigPow(10, 16)) + szabo := CurrencyToString(BigPow(10, 13)) + shannon := CurrencyToString(BigPow(10, 10)) + babbage := CurrencyToString(BigPow(10, 7)) + ada := CurrencyToString(BigPow(10, 4)) + wei := CurrencyToString(big.NewInt(10)) + + c.Assert(ether, checker.Equals, "10 Ether") + c.Assert(finney, checker.Equals, "10 Finney") + c.Assert(szabo, checker.Equals, "10 Szabo") + c.Assert(shannon, checker.Equals, "10 Shannon") + c.Assert(babbage, checker.Equals, "10 Babbage") + c.Assert(ada, checker.Equals, "10 Ada") + c.Assert(wei, checker.Equals, "10 Wei") +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/test_utils.go b/vendor/github.com/ethereum/go-ethereum/common/test_utils.go new file mode 100644 index 0000000..a848642 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/test_utils.go @@ -0,0 +1,53 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "encoding/json" + "fmt" + "io/ioutil" +) + +// LoadJSON reads the given file and unmarshals its content. +func LoadJSON(file string, val interface{}) error { + content, err := ioutil.ReadFile(file) + if err != nil { + return err + } + if err := json.Unmarshal(content, val); err != nil { + if syntaxerr, ok := err.(*json.SyntaxError); ok { + line := findLine(content, syntaxerr.Offset) + return fmt.Errorf("JSON syntax error at %v:%v: %v", file, line, err) + } + return fmt.Errorf("JSON unmarshal error in %v: %v", file, err) + } + return nil +} + +// findLine returns the line number for the given offset into data. +func findLine(data []byte, offset int64) (line int) { + line = 1 + for i, r := range string(data) { + if int64(i) >= offset { + return + } + if r == '\n' { + line++ + } + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/types.go b/vendor/github.com/ethereum/go-ethereum/common/types.go new file mode 100644 index 0000000..8a456e9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/types.go @@ -0,0 +1,165 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "fmt" + "math/big" + "math/rand" + "reflect" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +const ( + HashLength = 32 + AddressLength = 20 +) + +type ( + // Hash represents the 32 byte Keccak256 hash of arbitrary data. + Hash [HashLength]byte + + // Address represents the 20 byte address of an Ethereum account. + Address [AddressLength]byte +) + +func BytesToHash(b []byte) Hash { + var h Hash + h.SetBytes(b) + return h +} +func StringToHash(s string) Hash { return BytesToHash([]byte(s)) } +func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } +func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } + +// Don't use the default 'String' method in case we want to overwrite + +// Get the string representation of the underlying hash +func (h Hash) Str() string { return string(h[:]) } +func (h Hash) Bytes() []byte { return h[:] } +func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) } +func (h Hash) Hex() string { return hexutil.Encode(h[:]) } + +// UnmarshalJSON parses a hash in its hex from to a hash. +func (h *Hash) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalJSON("Hash", input, h[:]) +} + +// Serialize given hash to JSON +func (h Hash) MarshalJSON() ([]byte, error) { + return hexutil.Bytes(h[:]).MarshalJSON() +} + +// Sets the hash to the value of b. If b is larger than len(h) it will panic +func (h *Hash) SetBytes(b []byte) { + if len(b) > len(h) { + b = b[len(b)-HashLength:] + } + + copy(h[HashLength-len(b):], b) +} + +// Set string `s` to h. If s is larger than len(h) it will panic +func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) } + +// Sets h to other +func (h *Hash) Set(other Hash) { + for i, v := range other { + h[i] = v + } +} + +// Generate implements testing/quick.Generator. +func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value { + m := rand.Intn(len(h)) + for i := len(h) - 1; i > m; i-- { + h[i] = byte(rand.Uint32()) + } + return reflect.ValueOf(h) +} + +func EmptyHash(h Hash) bool { + return h == Hash{} +} + +/////////// Address +func BytesToAddress(b []byte) Address { + var a Address + a.SetBytes(b) + return a +} +func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) } +func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } +func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } + +// IsHexAddress verifies whether a string can represent a valid hex-encoded +// Ethereum address or not. +func IsHexAddress(s string) bool { + if len(s) == 2+2*AddressLength && IsHex(s) { + return true + } + if len(s) == 2*AddressLength && IsHex("0x"+s) { + return true + } + return false +} + +// Get the string representation of the underlying address +func (a Address) Str() string { return string(a[:]) } +func (a Address) Bytes() []byte { return a[:] } +func (a Address) Big() *big.Int { return Bytes2Big(a[:]) } +func (a Address) Hash() Hash { return BytesToHash(a[:]) } +func (a Address) Hex() string { return hexutil.Encode(a[:]) } + +// Sets the address to the value of b. If b is larger than len(a) it will panic +func (a *Address) SetBytes(b []byte) { + if len(b) > len(a) { + b = b[len(b)-AddressLength:] + } + copy(a[AddressLength-len(b):], b) +} + +// Set string `s` to a. If s is larger than len(a) it will panic +func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) } + +// Sets a to other +func (a *Address) Set(other Address) { + for i, v := range other { + a[i] = v + } +} + +// Serialize given address to JSON +func (a Address) MarshalJSON() ([]byte, error) { + return hexutil.Bytes(a[:]).MarshalJSON() +} + +// Parse address from raw json data +func (a *Address) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalJSON("Address", input, a[:]) +} + +// PP Pretty Prints a byte slice in the following format: +// hex(value[:4])...(hex[len(value)-4:]) +func PP(value []byte) string { + if len(value) <= 8 { + return Bytes2Hex(value) + } + + return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4]) +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/types_template.go b/vendor/github.com/ethereum/go-ethereum/common/types_template.go new file mode 100644 index 0000000..8048f9c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/types_template.go @@ -0,0 +1,64 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build none +//sed -e 's/_N_/Hash/g' -e 's/_S_/32/g' -e '1d' types_template.go | gofmt -w hash.go + +package common + +import "math/big" + +type _N_ [_S_]byte + +func BytesTo_N_(b []byte) _N_ { + var h _N_ + h.SetBytes(b) + return h +} +func StringTo_N_(s string) _N_ { return BytesTo_N_([]byte(s)) } +func BigTo_N_(b *big.Int) _N_ { return BytesTo_N_(b.Bytes()) } +func HexTo_N_(s string) _N_ { return BytesTo_N_(FromHex(s)) } + +// Don't use the default 'String' method in case we want to overwrite + +// Get the string representation of the underlying hash +func (h _N_) Str() string { return string(h[:]) } +func (h _N_) Bytes() []byte { return h[:] } +func (h _N_) Big() *big.Int { return Bytes2Big(h[:]) } +func (h _N_) Hex() string { return "0x" + Bytes2Hex(h[:]) } + +// Sets the hash to the value of b. If b is larger than len(h) it will panic +func (h *_N_) SetBytes(b []byte) { + // Use the right most bytes + if len(b) > len(h) { + b = b[len(b)-_S_:] + } + + // Reverse the loop + for i := len(b) - 1; i >= 0; i-- { + h[_S_-len(b)+i] = b[i] + } +} + +// Set string `s` to h. If s is larger than len(h) it will panic +func (h *_N_) SetString(s string) { h.SetBytes([]byte(s)) } + +// Sets h to other +func (h *_N_) Set(other _N_) { + for i, v := range other { + h[i] = v + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/types_test.go b/vendor/github.com/ethereum/go-ethereum/common/types_test.go new file mode 100644 index 0000000..e84780f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/common/types_test.go @@ -0,0 +1,97 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func TestBytesConversion(t *testing.T) { + bytes := []byte{5} + hash := BytesToHash(bytes) + + var exp Hash + exp[31] = 5 + + if hash != exp { + t.Errorf("expected %x got %x", exp, hash) + } +} + +func TestHashJsonValidation(t *testing.T) { + var h Hash + var tests = []struct { + Prefix string + Size int + Error string + }{ + {"", 62, hexutil.ErrMissingPrefix.Error()}, + {"0x", 66, "hex string has length 66, want 64 for Hash"}, + {"0x", 63, hexutil.ErrOddLength.Error()}, + {"0x", 0, "hex string has length 0, want 64 for Hash"}, + {"0x", 64, ""}, + {"0X", 64, ""}, + } + for _, test := range tests { + input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"` + err := h.UnmarshalJSON([]byte(input)) + if err == nil { + if test.Error != "" { + t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error) + } + } else { + if err.Error() != test.Error { + t.Errorf("%s: error mismatch: have %q, want %q", input, err, test.Error) + } + } + } +} + +func TestAddressUnmarshalJSON(t *testing.T) { + var a Address + var tests = []struct { + Input string + ShouldErr bool + Output *big.Int + }{ + {"", true, nil}, + {`""`, true, nil}, + {`"0x"`, true, nil}, + {`"0x00"`, true, nil}, + {`"0xG000000000000000000000000000000000000000"`, true, nil}, + {`"0x0000000000000000000000000000000000000000"`, false, big.NewInt(0)}, + {`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)}, + } + for i, test := range tests { + err := a.UnmarshalJSON([]byte(test.Input)) + if err != nil && !test.ShouldErr { + t.Errorf("test #%d: unexpected error: %v", i, err) + } + if err == nil { + if test.ShouldErr { + t.Errorf("test #%d: expected error, got none", i) + } + if a.Big().Cmp(test.Output) != 0 { + t.Errorf("test #%d: address mismatch: have %v, want %v", i, a.Big(), test.Output) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write.go b/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write.go new file mode 100644 index 0000000..0e7ad90 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write.go @@ -0,0 +1,101 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package rle implements the run-length encoding used for Ethereum data. +package rle + +import ( + "bytes" + "errors" + + "github.com/ethereum/go-ethereum/crypto" +) + +const ( + token byte = 0xfe + emptyShaToken = 0xfd + emptyListShaToken = 0xfe + tokenToken = 0xff +) + +var empty = crypto.Keccak256([]byte("")) +var emptyList = crypto.Keccak256([]byte{0x80}) + +func Decompress(dat []byte) ([]byte, error) { + buf := new(bytes.Buffer) + + for i := 0; i < len(dat); i++ { + if dat[i] == token { + if i+1 < len(dat) { + switch dat[i+1] { + case emptyShaToken: + buf.Write(empty) + case emptyListShaToken: + buf.Write(emptyList) + case tokenToken: + buf.WriteByte(token) + default: + buf.Write(make([]byte, int(dat[i+1]-2))) + } + i++ + } else { + return nil, errors.New("error reading bytes. token encountered without proceeding bytes") + } + } else { + buf.WriteByte(dat[i]) + } + } + + return buf.Bytes(), nil +} + +func compressChunk(dat []byte) (ret []byte, n int) { + switch { + case dat[0] == token: + return []byte{token, tokenToken}, 1 + case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0: + j := 0 + for j <= 254 && j < len(dat) { + if dat[j] != 0 { + break + } + j++ + } + return []byte{token, byte(j + 2)}, j + case len(dat) >= 32: + if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) { + return []byte{token, emptyShaToken}, 32 + } else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) { + return []byte{token, emptyListShaToken}, 32 + } + fallthrough + default: + return dat[:1], 1 + } +} + +func Compress(dat []byte) []byte { + buf := new(bytes.Buffer) + + i := 0 + for i < len(dat) { + b, n := compressChunk(dat[i:]) + buf.Write(b) + i += n + } + + return buf.Bytes() +} diff --git a/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write_test.go b/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write_test.go new file mode 100644 index 0000000..20a23a1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/compression/rle/read_write_test.go @@ -0,0 +1,134 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rle + +import ( + "testing" + + checker "gopkg.in/check.v1" +) + +func Test(t *testing.T) { checker.TestingT(t) } + +type CompressionRleSuite struct{} + +var _ = checker.Suite(&CompressionRleSuite{}) + +func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) { + exp := []byte{0xc5, 0xd2, 0x46, 0x1, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x3, 0xc0, 0xe5, 0x0, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x4, 0x5d, 0x85, 0xa4, 0x70} + res, err := Decompress([]byte{token, 0xfd}) + c.Assert(err, checker.IsNil) + c.Assert(res, checker.DeepEquals, exp) + // if bytes.Compare(res, exp) != 0 { + // t.Error("empty sha3", res) + // } + + exp = []byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x1, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21} + res, err = Decompress([]byte{token, 0xfe}) + c.Assert(err, checker.IsNil) + c.Assert(res, checker.DeepEquals, exp) + // if bytes.Compare(res, exp) != 0 { + // t.Error("0x80 sha3", res) + // } + + res, err = Decompress([]byte{token, 0xff}) + c.Assert(err, checker.IsNil) + c.Assert(res, checker.DeepEquals, []byte{token}) + // if bytes.Compare(res, []byte{token}) != 0 { + // t.Error("token", res) + // } + + res, err = Decompress([]byte{token, 12}) + c.Assert(err, checker.IsNil) + c.Assert(res, checker.DeepEquals, make([]byte, 10)) + // if bytes.Compare(res, make([]byte, 10)) != 0 { + // t.Error("10 * zero", res) + // } +} + +// func TestDecompressMulti(t *testing.T) { +// res, err := Decompress([]byte{token, 0xfd, token, 0xfe, token, 12}) +// if err != nil { +// t.Error(err) +// } + +// var exp []byte +// exp = append(exp, crypto.Keccak256([]byte(""))...) +// exp = append(exp, crypto.Keccak256([]byte{0x80})...) +// exp = append(exp, make([]byte, 10)...) + +// if bytes.Compare(res, res) != 0 { +// t.Error("Expected", exp, "result", res) +// } +// } + +// func TestCompressSimple(t *testing.T) { +// res := Compress([]byte{0, 0, 0, 0, 0}) +// if bytes.Compare(res, []byte{token, 7}) != 0 { +// t.Error("5 * zero", res) +// } + +// res = Compress(crypto.Keccak256([]byte(""))) +// if bytes.Compare(res, []byte{token, emptyShaToken}) != 0 { +// t.Error("empty sha", res) +// } + +// res = Compress(crypto.Keccak256([]byte{0x80})) +// if bytes.Compare(res, []byte{token, emptyListShaToken}) != 0 { +// t.Error("empty list sha", res) +// } + +// res = Compress([]byte{token}) +// if bytes.Compare(res, []byte{token, tokenToken}) != 0 { +// t.Error("token", res) +// } +// } + +// func TestCompressMulti(t *testing.T) { +// in := []byte{0, 0, 0, 0, 0} +// in = append(in, crypto.Keccak256([]byte(""))...) +// in = append(in, crypto.Keccak256([]byte{0x80})...) +// in = append(in, token) +// res := Compress(in) + +// exp := []byte{token, 7, token, emptyShaToken, token, emptyListShaToken, token, tokenToken} +// if bytes.Compare(res, exp) != 0 { +// t.Error("expected", exp, "got", res) +// } +// } + +// func TestCompressDecompress(t *testing.T) { +// var in []byte + +// for i := 0; i < 20; i++ { +// in = append(in, []byte{0, 0, 0, 0, 0}...) +// in = append(in, crypto.Keccak256([]byte(""))...) +// in = append(in, crypto.Keccak256([]byte{0x80})...) +// in = append(in, []byte{123, 2, 19, 89, 245, 254, 255, token, 98, 233}...) +// in = append(in, token) +// } + +// c := Compress(in) +// d, err := Decompress(c) +// if err != nil { +// t.Error(err) +// } + +// if bytes.Compare(d, in) != 0 { +// t.Error("multi failed\n", d, "\n", in) +// } +// } diff --git a/vendor/github.com/ethereum/go-ethereum/console/bridge.go b/vendor/github.com/ethereum/go-ethereum/console/bridge.go new file mode 100644 index 0000000..f0c5980 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/bridge.go @@ -0,0 +1,312 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package console + +import ( + "encoding/json" + "fmt" + "io" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rpc" + "github.com/robertkrimen/otto" +) + +// bridge is a collection of JavaScript utility methods to bride the .js runtime +// environment and the Go RPC connection backing the remote method calls. +type bridge struct { + client *rpc.Client // RPC client to execute Ethereum requests through + prompter UserPrompter // Input prompter to allow interactive user feedback + printer io.Writer // Output writer to serialize any display strings to +} + +// newBridge creates a new JavaScript wrapper around an RPC client. +func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge { + return &bridge{ + client: client, + prompter: prompter, + printer: printer, + } +} + +// NewAccount is a wrapper around the personal.newAccount RPC method that uses a +// non-echoing password prompt to acquire the passphrase and executes the original +// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call. +func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) { + var ( + password string + confirm string + err error + ) + switch { + // No password was specified, prompt the user for it + case len(call.ArgumentList) == 0: + if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } + if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil { + throwJSException(err.Error()) + } + if password != confirm { + throwJSException("passphrases don't match!") + } + + // A single string password was specified, use that + case len(call.ArgumentList) == 1 && call.Argument(0).IsString(): + password, _ = call.Argument(0).ToString() + + // Otherwise fail with some error + default: + throwJSException("expected 0 or 1 string argument") + } + // Password acquired, execute the call and return + ret, err := call.Otto.Call("jeth.newAccount", nil, password) + if err != nil { + throwJSException(err.Error()) + } + return ret +} + +// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that +// uses a non-echoing password prompt to acquire the passphrase and executes the +// original RPC method (saved in jeth.unlockAccount) with it to actually execute +// the RPC call. +func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) { + // Make sure we have an account specified to unlock + if !call.Argument(0).IsString() { + throwJSException("first argument must be the account to unlock") + } + account := call.Argument(0) + + // If password is not given or is the null value, prompt the user for it + var passwd otto.Value + + if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() { + fmt.Fprintf(b.printer, "Unlock account %s\n", account) + if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + } else { + if !call.Argument(1).IsString() { + throwJSException("password must be a string") + } + passwd = call.Argument(1) + } + // Third argument is the duration how long the account must be unlocked. + duration := otto.NullValue() + if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() { + if !call.Argument(2).IsNumber() { + throwJSException("unlock duration must be a number") + } + duration = call.Argument(2) + } + // Send the request to the backend and return + val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration) + if err != nil { + throwJSException(err.Error()) + } + return val +} + +// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password +// prompt to acquire the passphrase and executes the original RPC method (saved in +// jeth.sign) with it to actually execute the RPC call. +func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) { + var ( + message = call.Argument(0) + account = call.Argument(1) + passwd = call.Argument(2) + ) + + if !message.IsString() { + throwJSException("first argument must be the message to sign") + } + if !account.IsString() { + throwJSException("second argument must be the account to sign with") + } + + // if the password is not given or null ask the user and ensure password is a string + if passwd.IsUndefined() || passwd.IsNull() { + fmt.Fprintf(b.printer, "Give password for account %s\n", account) + if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + } + if !passwd.IsString() { + throwJSException("third argument must be the password to unlock the account") + } + + // Send the request to the backend and return + val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd) + if err != nil { + throwJSException(err.Error()) + } + return val +} + +// Sleep will block the console for the specified number of seconds. +func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) { + if call.Argument(0).IsNumber() { + sleep, _ := call.Argument(0).ToInteger() + time.Sleep(time.Duration(sleep) * time.Second) + return otto.TrueValue() + } + return throwJSException("usage: sleep()") +} + +// SleepBlocks will block the console for a specified number of new blocks optionally +// until the given timeout is reached. +func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) { + var ( + blocks = int64(0) + sleep = int64(9999999999999999) // indefinitely + ) + // Parse the input parameters for the sleep + nArgs := len(call.ArgumentList) + if nArgs == 0 { + throwJSException("usage: sleepBlocks([, max sleep in seconds])") + } + if nArgs >= 1 { + if call.Argument(0).IsNumber() { + blocks, _ = call.Argument(0).ToInteger() + } else { + throwJSException("expected number as first argument") + } + } + if nArgs >= 2 { + if call.Argument(1).IsNumber() { + sleep, _ = call.Argument(1).ToInteger() + } else { + throwJSException("expected number as second argument") + } + } + // go through the console, this will allow web3 to call the appropriate + // callbacks if a delayed response or notification is received. + blockNumber := func() int64 { + result, err := call.Otto.Run("eth.blockNumber") + if err != nil { + throwJSException(err.Error()) + } + block, err := result.ToInteger() + if err != nil { + throwJSException(err.Error()) + } + return block + } + // Poll the current block number until either it ot a timeout is reached + targetBlockNr := blockNumber() + blocks + deadline := time.Now().Add(time.Duration(sleep) * time.Second) + + for time.Now().Before(deadline) { + if blockNumber() >= targetBlockNr { + return otto.TrueValue() + } + time.Sleep(time.Second) + } + return otto.FalseValue() +} + +type jsonrpcCall struct { + Id int64 + Method string + Params []interface{} +} + +// Send implements the web3 provider "send" method. +func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) { + // Remarshal the request into a Go value. + JSON, _ := call.Otto.Object("JSON") + reqVal, err := JSON.Call("stringify", call.Argument(0)) + if err != nil { + throwJSException(err.Error()) + } + var ( + rawReq = []byte(reqVal.String()) + reqs []jsonrpcCall + batch bool + ) + if rawReq[0] == '[' { + batch = true + json.Unmarshal(rawReq, &reqs) + } else { + batch = false + reqs = make([]jsonrpcCall, 1) + json.Unmarshal(rawReq, &reqs[0]) + } + + // Execute the requests. + resps, _ := call.Otto.Object("new Array()") + for _, req := range reqs { + resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`) + resp.Set("id", req.Id) + var result json.RawMessage + err = b.client.Call(&result, req.Method, req.Params...) + switch err := err.(type) { + case nil: + if result == nil { + // Special case null because it is decoded as an empty + // raw message for some reason. + resp.Set("result", otto.NullValue()) + } else { + resultVal, err := JSON.Call("parse", string(result)) + if err != nil { + setError(resp, -32603, err.Error()) + } else { + resp.Set("result", resultVal) + } + } + case rpc.Error: + setError(resp, err.ErrorCode(), err.Error()) + default: + setError(resp, -32603, err.Error()) + } + resps.Call("push", resp) + } + + // Return the responses either to the callback (if supplied) + // or directly as the return value. + if batch { + response = resps.Value() + } else { + response, _ = resps.Get("0") + } + if fn := call.Argument(1); fn.Class() == "Function" { + fn.Call(otto.NullValue(), otto.NullValue(), response) + return otto.UndefinedValue() + } + return response +} + +func setError(resp *otto.Object, code int, msg string) { + resp.Set("error", map[string]interface{}{"code": code, "message": msg}) +} + +// throwJSException panics on an otto.Value. The Otto VM will recover from the +// Go panic and throw msg as a JavaScript error. +func throwJSException(msg interface{}) otto.Value { + val, err := otto.ToValue(msg) + if err != nil { + glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err) + } + panic(val) +} diff --git a/vendor/github.com/ethereum/go-ethereum/console/console.go b/vendor/github.com/ethereum/go-ethereum/console/console.go new file mode 100644 index 0000000..389d528 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/console.go @@ -0,0 +1,419 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package console + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/signal" + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/ethereum/go-ethereum/internal/jsre" + "github.com/ethereum/go-ethereum/internal/web3ext" + "github.com/ethereum/go-ethereum/rpc" + "github.com/mattn/go-colorable" + "github.com/peterh/liner" + "github.com/robertkrimen/otto" +) + +var ( + passwordRegexp = regexp.MustCompile(`personal.[nus]`) + onlyWhitespace = regexp.MustCompile(`^\s*$`) + exit = regexp.MustCompile(`^\s*exit\s*;*\s*$`) +) + +// HistoryFile is the file within the data directory to store input scrollback. +const HistoryFile = "history" + +// DefaultPrompt is the default prompt line prefix to use for user input querying. +const DefaultPrompt = "> " + +// Config is te collection of configurations to fine tune the behavior of the +// JavaScript console. +type Config struct { + DataDir string // Data directory to store the console history at + DocRoot string // Filesystem path from where to load JavaScript files from + Client *rpc.Client // RPC client to execute Ethereum requests through + Prompt string // Input prompt prefix string (defaults to DefaultPrompt) + Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter) + Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout) + Preload []string // Absolute paths to JavaScript files to preload +} + +// Console is a JavaScript interpreted runtime environment. It is a fully fleged +// JavaScript console attached to a running node via an external or in-process RPC +// client. +type Console struct { + client *rpc.Client // RPC client to execute Ethereum requests through + jsre *jsre.JSRE // JavaScript runtime environment running the interpreter + prompt string // Input prompt prefix string + prompter UserPrompter // Input prompter to allow interactive user feedback + histPath string // Absolute path to the console scrollback history + history []string // Scroll history maintained by the console + printer io.Writer // Output writer to serialize any display strings to +} + +func New(config Config) (*Console, error) { + // Handle unset config values gracefully + if config.Prompter == nil { + config.Prompter = Stdin + } + if config.Prompt == "" { + config.Prompt = DefaultPrompt + } + if config.Printer == nil { + config.Printer = colorable.NewColorableStdout() + } + // Initialize the console and return + console := &Console{ + client: config.Client, + jsre: jsre.New(config.DocRoot, config.Printer), + prompt: config.Prompt, + prompter: config.Prompter, + printer: config.Printer, + histPath: filepath.Join(config.DataDir, HistoryFile), + } + if err := console.init(config.Preload); err != nil { + return nil, err + } + return console, nil +} + +// init retrieves the available APIs from the remote RPC provider and initializes +// the console's JavaScript namespaces based on the exposed modules. +func (c *Console) init(preload []string) error { + // Initialize the JavaScript <-> Go RPC bridge + bridge := newBridge(c.client, c.prompter, c.printer) + c.jsre.Set("jeth", struct{}{}) + + jethObj, _ := c.jsre.Get("jeth") + jethObj.Object().Set("send", bridge.Send) + jethObj.Object().Set("sendAsync", bridge.Send) + + consoleObj, _ := c.jsre.Get("console") + consoleObj.Object().Set("log", c.consoleOutput) + consoleObj.Object().Set("error", c.consoleOutput) + + // Load all the internal utility JavaScript libraries + if err := c.jsre.Compile("bignumber.js", jsre.BigNumber_JS); err != nil { + return fmt.Errorf("bignumber.js: %v", err) + } + if err := c.jsre.Compile("web3.js", jsre.Web3_JS); err != nil { + return fmt.Errorf("web3.js: %v", err) + } + if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil { + return fmt.Errorf("web3 require: %v", err) + } + if _, err := c.jsre.Run("var web3 = new Web3(jeth);"); err != nil { + return fmt.Errorf("web3 provider: %v", err) + } + // Load the supported APIs into the JavaScript runtime environment + apis, err := c.client.SupportedModules() + if err != nil { + return fmt.Errorf("api modules: %v", err) + } + flatten := "var eth = web3.eth; var personal = web3.personal; " + for api := range apis { + if api == "web3" { + continue // manually mapped or ignore + } + if file, ok := web3ext.Modules[api]; ok { + // Load our extension for the module. + if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil { + return fmt.Errorf("%s.js: %v", api, err) + } + flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) + } else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() { + // Enable web3.js built-in extension if available. + flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) + } + } + if _, err = c.jsre.Run(flatten); err != nil { + return fmt.Errorf("namespace flattening: %v", err) + } + // Initialize the global name register (disabled for now) + //c.jsre.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) + + // If the console is in interactive mode, instrument password related methods to query the user + if c.prompter != nil { + // Retrieve the account management object to instrument + personal, err := c.jsre.Get("personal") + if err != nil { + return err + } + // Override the unlockAccount, newAccount and sign methods since these require user interaction. + // Assign these method in the Console the original web3 callbacks. These will be called by the jeth.* + // methods after they got the password from the user and send the original web3 request to the backend. + if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface + if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { + return fmt.Errorf("personal.unlockAccount: %v", err) + } + if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { + return fmt.Errorf("personal.newAccount: %v", err) + } + if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { + return fmt.Errorf("personal.sign: %v", err) + } + obj.Set("unlockAccount", bridge.UnlockAccount) + obj.Set("newAccount", bridge.NewAccount) + obj.Set("sign", bridge.Sign) + } + } + // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer. + admin, err := c.jsre.Get("admin") + if err != nil { + return err + } + if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface + obj.Set("sleepBlocks", bridge.SleepBlocks) + obj.Set("sleep", bridge.Sleep) + } + // Preload any JavaScript files before starting the console + for _, path := range preload { + if err := c.jsre.Exec(path); err != nil { + failure := err.Error() + if ottoErr, ok := err.(*otto.Error); ok { + failure = ottoErr.String() + } + return fmt.Errorf("%s: %v", path, failure) + } + } + // Configure the console's input prompter for scrollback and tab completion + if c.prompter != nil { + if content, err := ioutil.ReadFile(c.histPath); err != nil { + c.prompter.SetHistory(nil) + } else { + c.history = strings.Split(string(content), "\n") + c.prompter.SetHistory(c.history) + } + c.prompter.SetWordCompleter(c.AutoCompleteInput) + } + return nil +} + +// consoleOutput is an override for the console.log and console.error methods to +// stream the output into the configured output stream instead of stdout. +func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value { + output := []string{} + for _, argument := range call.ArgumentList { + output = append(output, fmt.Sprintf("%v", argument)) + } + fmt.Fprintln(c.printer, strings.Join(output, " ")) + return otto.Value{} +} + +// AutoCompleteInput is a pre-assembled word completer to be used by the user +// input prompter to provide hints to the user about the methods available. +func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, string) { + // No completions can be provided for empty inputs + if len(line) == 0 || pos == 0 { + return "", nil, "" + } + // Chunck data to relevant part for autocompletion + // E.g. in case of nested lines eth.getBalance(eth.coinb + start := pos - 1 + for ; start > 0; start-- { + // Skip all methods and namespaces (i.e. including te dot) + if line[start] == '.' || (line[start] >= 'a' && line[start] <= 'z') || (line[start] >= 'A' && line[start] <= 'Z') { + continue + } + // Handle web3 in a special way (i.e. other numbers aren't auto completed) + if start >= 3 && line[start-3:start] == "web3" { + start -= 3 + continue + } + // We've hit an unexpected character, autocomplete form here + start++ + break + } + return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:] +} + +// Welcome show summary of current Geth instance and some metadata about the +// console's available modules. +func (c *Console) Welcome() { + // Print some generic Geth metadata + fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n") + c.jsre.Run(` + console.log("instance: " + web3.version.node); + console.log("coinbase: " + eth.coinbase); + console.log("at block: " + eth.blockNumber + " (" + new Date(1000 * eth.getBlock(eth.blockNumber).timestamp) + ")"); + console.log(" datadir: " + admin.datadir); + `) + // List all the supported modules for the user to call + if apis, err := c.client.SupportedModules(); err == nil { + modules := make([]string, 0, len(apis)) + for api, version := range apis { + modules = append(modules, fmt.Sprintf("%s:%s", api, version)) + } + sort.Strings(modules) + fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " ")) + } + fmt.Fprintln(c.printer) +} + +// Evaluate executes code and pretty prints the result to the specified output +// stream. +func (c *Console) Evaluate(statement string) error { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(c.printer, "[native] error: %v\n", r) + } + }() + return c.jsre.Evaluate(statement, c.printer) +} + +// Interactive starts an interactive user session, where input is propted from +// the configured user prompter. +func (c *Console) Interactive() { + var ( + prompt = c.prompt // Current prompt line (used for multi-line inputs) + indents = 0 // Current number of input indents (used for multi-line inputs) + input = "" // Current user input + scheduler = make(chan string) // Channel to send the next prompt on and receive the input + ) + // Start a goroutine to listen for promt requests and send back inputs + go func() { + for { + // Read the next user input + line, err := c.prompter.PromptInput(<-scheduler) + if err != nil { + // In case of an error, either clear the prompt or fail + if err == liner.ErrPromptAborted { // ctrl-C + prompt, indents, input = c.prompt, 0, "" + scheduler <- "" + continue + } + close(scheduler) + return + } + // User input retrieved, send for interpretation and loop + scheduler <- line + } + }() + // Monitor Ctrl-C too in case the input is empty and we need to bail + abort := make(chan os.Signal, 1) + signal.Notify(abort, os.Interrupt) + + // Start sending prompts to the user and reading back inputs + for { + // Send the next prompt, triggering an input read and process the result + scheduler <- prompt + select { + case <-abort: + // User forcefully quite the console + fmt.Fprintln(c.printer, "caught interrupt, exiting") + return + + case line, ok := <-scheduler: + // User input was returned by the prompter, handle special cases + if !ok || (indents <= 0 && exit.MatchString(line)) { + return + } + if onlyWhitespace.MatchString(line) { + continue + } + // Append the line to the input and check for multi-line interpretation + input += line + "\n" + + indents = countIndents(input) + if indents <= 0 { + prompt = c.prompt + } else { + prompt = strings.Repeat(".", indents*3) + " " + } + // If all the needed lines are present, save the command and run + if indents <= 0 { + if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) { + if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] { + c.history = append(c.history, command) + if c.prompter != nil { + c.prompter.AppendHistory(command) + } + } + } + c.Evaluate(input) + input = "" + } + } + } +} + +// countIndents returns the number of identations for the given input. +// In case of invalid input such as var a = } the result can be negative. +func countIndents(input string) int { + var ( + indents = 0 + inString = false + strOpenChar = ' ' // keep track of the string open char to allow var str = "I'm ...."; + charEscaped = false // keep track if the previous char was the '\' char, allow var str = "abc\"def"; + ) + + for _, c := range input { + switch c { + case '\\': + // indicate next char as escaped when in string and previous char isn't escaping this backslash + if !charEscaped && inString { + charEscaped = true + } + case '\'', '"': + if inString && !charEscaped && strOpenChar == c { // end string + inString = false + } else if !inString && !charEscaped { // begin string + inString = true + strOpenChar = c + } + charEscaped = false + case '{', '(': + if !inString { // ignore brackets when in string, allow var str = "a{"; without indenting + indents++ + } + charEscaped = false + case '}', ')': + if !inString { + indents-- + } + charEscaped = false + default: + charEscaped = false + } + } + + return indents +} + +// Execute runs the JavaScript file specified as the argument. +func (c *Console) Execute(path string) error { + return c.jsre.Exec(path) +} + +// Stop cleans up the console and terminates the runtime envorinment. +func (c *Console) Stop(graceful bool) error { + if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil { + return err + } + if err := os.Chmod(c.histPath, 0600); err != nil { // Force 0600, even if it was different previously + return err + } + c.jsre.Stop(graceful) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/console/console_test.go b/vendor/github.com/ethereum/go-ethereum/console/console_test.go new file mode 100644 index 0000000..d5010b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/console_test.go @@ -0,0 +1,338 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package console + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "math/big" + "os" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/internal/jsre" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" +) + +const ( + testInstance = "console-tester" + testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" +) + +// hookedPrompter implements UserPrompter to simulate use input via channels. +type hookedPrompter struct { + scheduler chan string +} + +func (p *hookedPrompter) PromptInput(prompt string) (string, error) { + // Send the prompt to the tester + select { + case p.scheduler <- prompt: + case <-time.After(time.Second): + return "", errors.New("prompt timeout") + } + // Retrieve the response and feed to the console + select { + case input := <-p.scheduler: + return input, nil + case <-time.After(time.Second): + return "", errors.New("input timeout") + } +} + +func (p *hookedPrompter) PromptPassword(prompt string) (string, error) { + return "", errors.New("not implemented") +} +func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) { + return false, errors.New("not implemented") +} +func (p *hookedPrompter) SetHistory(history []string) {} +func (p *hookedPrompter) AppendHistory(command string) {} +func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} + +// tester is a console test environment for the console tests to operate on. +type tester struct { + workspace string + stack *node.Node + ethereum *eth.Ethereum + console *Console + input *hookedPrompter + output *bytes.Buffer + + lastConfirm string +} + +// newTester creates a test environment based on which the console can operate. +// Please ensure you call Close() on the returned tester to avoid leaks. +func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { + // Create a temporary storage for the node keys and initialize it + workspace, err := ioutil.TempDir("", "console-tester-") + if err != nil { + t.Fatalf("failed to create temporary keystore: %v", err) + } + + // Create a networkless protocol stack and start an Ethereum service within + stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance, NoDiscovery: true}) + if err != nil { + t.Fatalf("failed to create node: %v", err) + } + ethConf := ð.Config{ + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), ChainId: new(big.Int)}, + Etherbase: common.HexToAddress(testAddress), + PowTest: true, + } + if confOverride != nil { + confOverride(ethConf) + } + if err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { + t.Fatalf("failed to register Ethereum protocol: %v", err) + } + // Start the node and assemble the JavaScript console around it + if err = stack.Start(); err != nil { + t.Fatalf("failed to start test stack: %v", err) + } + client, err := stack.Attach() + if err != nil { + t.Fatalf("failed to attach to node: %v", err) + } + prompter := &hookedPrompter{scheduler: make(chan string)} + printer := new(bytes.Buffer) + + console, err := New(Config{ + DataDir: stack.DataDir(), + DocRoot: "testdata", + Client: client, + Prompter: prompter, + Printer: printer, + Preload: []string{"preload.js"}, + }) + if err != nil { + t.Fatalf("failed to create JavaScript console: %v", err) + } + // Create the final tester and return + var ethereum *eth.Ethereum + stack.Service(ðereum) + + return &tester{ + workspace: workspace, + stack: stack, + ethereum: ethereum, + console: console, + input: prompter, + output: printer, + } +} + +// Close cleans up any temporary data folders and held resources. +func (env *tester) Close(t *testing.T) { + if err := env.console.Stop(false); err != nil { + t.Errorf("failed to stop embedded console: %v", err) + } + if err := env.stack.Stop(); err != nil { + t.Errorf("failed to stop embedded node: %v", err) + } + os.RemoveAll(env.workspace) +} + +// Tests that the node lists the correct welcome message, notably that it contains +// the instance name, coinbase account, block number, data directory and supported +// console modules. +func TestWelcome(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + + tester.console.Welcome() + + output := string(tester.output.Bytes()) + if want := "Welcome"; !strings.Contains(output, want) { + t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want) + } + if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) { + t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want) + } + if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) { + t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) + } + if want := "at block: 0"; !strings.Contains(output, want) { + t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want) + } + if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) { + t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) + } +} + +// Tests that JavaScript statement evaluation works as intended. +func TestEvaluate(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + + tester.console.Evaluate("2 + 2") + if output := string(tester.output.Bytes()); !strings.Contains(output, "4") { + t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") + } +} + +// Tests that the console can be used in interactive mode. +func TestInteractive(t *testing.T) { + // Create a tester and run an interactive console in the background + tester := newTester(t, nil) + defer tester.Close(t) + + go tester.console.Interactive() + + // Wait for a promt and send a statement back + select { + case <-tester.input.scheduler: + case <-time.After(time.Second): + t.Fatalf("initial prompt timeout") + } + select { + case tester.input.scheduler <- "2+2": + case <-time.After(time.Second): + t.Fatalf("input feedback timeout") + } + // Wait for the second promt and ensure first statement was evaluated + select { + case <-tester.input.scheduler: + case <-time.After(time.Second): + t.Fatalf("secondary prompt timeout") + } + if output := string(tester.output.Bytes()); !strings.Contains(output, "4") { + t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") + } +} + +// Tests that preloaded JavaScript files have been executed before user is given +// input. +func TestPreload(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + + tester.console.Evaluate("preloaded") + if output := string(tester.output.Bytes()); !strings.Contains(output, "some-preloaded-string") { + t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string") + } +} + +// Tests that JavaScript scripts can be executes from the configured asset path. +func TestExecute(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + + tester.console.Execute("exec.js") + + tester.console.Evaluate("execed") + if output := string(tester.output.Bytes()); !strings.Contains(output, "some-executed-string") { + t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string") + } +} + +// Tests that the JavaScript objects returned by statement executions are properly +// pretty printed instead of just displaing "[object]". +func TestPrettyPrint(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + + tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}") + + // Define some specially formatted fields + var ( + one = jsre.NumberColor("1") + two = jsre.StringColor("\"two\"") + three = jsre.NumberColor("3") + null = jsre.SpecialColor("null") + fun = jsre.FunctionColor("function()") + ) + // Assemble the actual output we're after and verify + want := `{ + int: ` + one + `, + list: [` + three + `, ` + three + `, ` + three + `], + obj: { + null: ` + null + `, + func: ` + fun + ` + }, + string: ` + two + ` +} +` + if output := string(tester.output.Bytes()); output != want { + t.Fatalf("pretty print mismatch: have %s, want %s", output, want) + } +} + +// Tests that the JavaScript exceptions are properly formatted and colored. +func TestPrettyError(t *testing.T) { + tester := newTester(t, nil) + defer tester.Close(t) + tester.console.Evaluate("throw 'hello'") + + want := jsre.ErrorColor("hello") + "\n" + if output := string(tester.output.Bytes()); output != want { + t.Fatalf("pretty error mismatch: have %s, want %s", output, want) + } +} + +// Tests that tests if the number of indents for JS input is calculated correct. +func TestIndenting(t *testing.T) { + testCases := []struct { + input string + expectedIndentCount int + }{ + {`var a = 1;`, 0}, + {`"some string"`, 0}, + {`"some string with (parentesis`, 0}, + {`"some string with newline + ("`, 0}, + {`function v(a,b) {}`, 0}, + {`function f(a,b) { var str = "asd("; };`, 0}, + {`function f(a) {`, 1}, + {`function f(a, function(b) {`, 2}, + {`function f(a, function(b) { + var str = "a)}"; + });`, 0}, + {`function f(a,b) { + var str = "a{b(" + a, ", " + b; + }`, 0}, + {`var str = "\"{"`, 0}, + {`var str = "'("`, 0}, + {`var str = "\\{"`, 0}, + {`var str = "\\\\{"`, 0}, + {`var str = 'a"{`, 0}, + {`var obj = {`, 1}, + {`var obj = { {a:1`, 2}, + {`var obj = { {a:1}`, 1}, + {`var obj = { {a:1}, b:2}`, 0}, + {`var obj = {}`, 0}, + {`var obj = { + a: 1, b: 2 + }`, 0}, + {`var test = }`, -1}, + {`var str = "a\""; var obj = {`, 1}, + } + + for i, tt := range testCases { + counted := countIndents(tt.input) + if counted != tt.expectedIndentCount { + t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/console/prompter.go b/vendor/github.com/ethereum/go-ethereum/console/prompter.go new file mode 100644 index 0000000..6acbfb0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/prompter.go @@ -0,0 +1,165 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package console + +import ( + "fmt" + "strings" + + "github.com/peterh/liner" +) + +// Stdin holds the stdin line reader (also using stdout for printing prompts). +// Only this reader may be used for input because it keeps an internal buffer. +var Stdin = newTerminalPrompter() + +// UserPrompter defines the methods needed by the console to promt the user for +// various types of inputs. +type UserPrompter interface { + // PromptInput displays the given prompt to the user and requests some textual + // data to be entered, returning the input of the user. + PromptInput(prompt string) (string, error) + + // PromptPassword displays the given prompt to the user and requests some textual + // data to be entered, but one which must not be echoed out into the terminal. + // The method returns the input provided by the user. + PromptPassword(prompt string) (string, error) + + // PromptConfirm displays the given prompt to the user and requests a boolean + // choice to be made, returning that choice. + PromptConfirm(prompt string) (bool, error) + + // SetHistory sets the the input scrollback history that the prompter will allow + // the user to scroll back to. + SetHistory(history []string) + + // AppendHistory appends an entry to the scrollback history. It should be called + // if and only if the prompt to append was a valid command. + AppendHistory(command string) + + // SetWordCompleter sets the completion function that the prompter will call to + // fetch completion candidates when the user presses tab. + SetWordCompleter(completer WordCompleter) +} + +// WordCompleter takes the currently edited line with the cursor position and +// returns the completion candidates for the partial word to be completed. If +// the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, +// wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world", +// "Word"}, "!!!") to have "Hello, world!!!". +type WordCompleter func(line string, pos int) (string, []string, string) + +// terminalPrompter is a UserPrompter backed by the liner package. It supports +// prompting the user for various input, among others for non-echoing password +// input. +type terminalPrompter struct { + *liner.State + warned bool + supported bool + normalMode liner.ModeApplier + rawMode liner.ModeApplier +} + +// newTerminalPrompter creates a liner based user input prompter working off the +// standard input and output streams. +func newTerminalPrompter() *terminalPrompter { + p := new(terminalPrompter) + // Get the original mode before calling NewLiner. + // This is usually regular "cooked" mode where characters echo. + normalMode, _ := liner.TerminalMode() + // Turn on liner. It switches to raw mode. + p.State = liner.NewLiner() + rawMode, err := liner.TerminalMode() + if err != nil || !liner.TerminalSupported() { + p.supported = false + } else { + p.supported = true + p.normalMode = normalMode + p.rawMode = rawMode + // Switch back to normal mode while we're not prompting. + normalMode.ApplyMode() + } + p.SetCtrlCAborts(true) + p.SetTabCompletionStyle(liner.TabPrints) + p.SetMultiLineMode(true) + return p +} + +// PromptInput displays the given prompt to the user and requests some textual +// data to be entered, returning the input of the user. +func (p *terminalPrompter) PromptInput(prompt string) (string, error) { + if p.supported { + p.rawMode.ApplyMode() + defer p.normalMode.ApplyMode() + } else { + // liner tries to be smart about printing the prompt + // and doesn't print anything if input is redirected. + // Un-smart it by printing the prompt always. + fmt.Print(prompt) + prompt = "" + defer fmt.Println() + } + return p.State.Prompt(prompt) +} + +// PromptPassword displays the given prompt to the user and requests some textual +// data to be entered, but one which must not be echoed out into the terminal. +// The method returns the input provided by the user. +func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) { + if p.supported { + p.rawMode.ApplyMode() + defer p.normalMode.ApplyMode() + return p.State.PasswordPrompt(prompt) + } + if !p.warned { + fmt.Println("!! Unsupported terminal, password will be echoed.") + p.warned = true + } + // Just as in Prompt, handle printing the prompt here instead of relying on liner. + fmt.Print(prompt) + passwd, err = p.State.Prompt("") + fmt.Println() + return passwd, err +} + +// PromptConfirm displays the given prompt to the user and requests a boolean +// choice to be made, returning that choice. +func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) { + input, err := p.Prompt(prompt + " [y/N] ") + if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" { + return true, nil + } + return false, err +} + +// SetHistory sets the the input scrollback history that the prompter will allow +// the user to scroll back to. +func (p *terminalPrompter) SetHistory(history []string) { + p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n"))) +} + +// AppendHistory appends an entry to the scrollback history. It should be called +// if and only if the prompt to append was a valid command. +func (p *terminalPrompter) AppendHistory(command string) { + p.State.AppendHistory(command) +} + +// SetWordCompleter sets the completion function that the prompter will call to +// fetch completion candidates when the user presses tab. +func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) { + p.State.SetWordCompleter(liner.WordCompleter(completer)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/console/testdata/exec.js b/vendor/github.com/ethereum/go-ethereum/console/testdata/exec.js new file mode 100644 index 0000000..59e34d7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/testdata/exec.js @@ -0,0 +1 @@ +var execed = "some-executed-string"; diff --git a/vendor/github.com/ethereum/go-ethereum/console/testdata/preload.js b/vendor/github.com/ethereum/go-ethereum/console/testdata/preload.js new file mode 100644 index 0000000..5567939 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/console/testdata/preload.js @@ -0,0 +1 @@ +var preloaded = "some-preloaded-string"; diff --git a/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-alpine/Dockerfile b/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-alpine/Dockerfile new file mode 100644 index 0000000..d239129 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-alpine/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:3.5 + +RUN \ + apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ + git clone --depth 1 https://github.com/ethereum/go-ethereum && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apk del go git make gcc musl-dev linux-headers && \ + rm -rf /go-ethereum && rm -rf /var/cache/apk/* + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-ubuntu/Dockerfile b/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-ubuntu/Dockerfile new file mode 100644 index 0000000..c79becb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/containers/docker/develop-ubuntu/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:xenial + +RUN \ + apt-get update && apt-get upgrade -q -y && \ + apt-get install -y --no-install-recommends golang git make gcc libc-dev ca-certificates && \ + git clone --depth 1 https://github.com/ethereum/go-ethereum && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apt-get remove -y golang git make gcc libc-dev && apt autoremove -y && apt-get clean && \ + rm -rf /go-ethereum + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/containers/docker/master-alpine/Dockerfile b/vendor/github.com/ethereum/go-ethereum/containers/docker/master-alpine/Dockerfile new file mode 100644 index 0000000..3c72bc8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/containers/docker/master-alpine/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:3.5 + +RUN \ + apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ + git clone --depth 1 --branch release/1.5 https://github.com/ethereum/go-ethereum && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apk del go git make gcc musl-dev linux-headers && \ + rm -rf /go-ethereum && rm -rf /var/cache/apk/* + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/containers/docker/master-ubuntu/Dockerfile b/vendor/github.com/ethereum/go-ethereum/containers/docker/master-ubuntu/Dockerfile new file mode 100644 index 0000000..877ae94 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/containers/docker/master-ubuntu/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:xenial + +RUN \ + apt-get update && apt-get upgrade -q -y && \ + apt-get install -y --no-install-recommends golang git make gcc libc-dev ca-certificates && \ + git clone --depth 1 --branch release/1.5 https://github.com/ethereum/go-ethereum && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apt-get remove -y golang git make gcc libc-dev && apt autoremove -y && apt-get clean && \ + rm -rf /go-ethereum + +EXPOSE 8545 +EXPOSE 30303 + +ENTRYPOINT ["/geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/containers/vagrant/Vagrantfile b/vendor/github.com/ethereum/go-ethereum/containers/vagrant/Vagrantfile new file mode 100644 index 0000000..5d263eb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/containers/vagrant/Vagrantfile @@ -0,0 +1,29 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.vm.box = "ubuntu/trusty64" + + config.vm.provider "virtualbox" do |vb| + vb.memory = "2048" + end + + config.vm.synced_folder "../../", "/home/vagrant/go/src/github.com/ethereum/go-ethereum" + config.vm.synced_folder ".", "/vagrant", disabled: true + + config.vm.provision "shell", inline: <<-SHELL + sudo apt-get install software-properties-common + sudo add-apt-repository -y ppa:ethereum/ethereum + sudo add-apt-repository -y ppa:ethereum/ethereum-dev + sudo apt-get update + + sudo apt-get install -y build-essential golang git-all + + GOPATH=/home/vagrant/go go get github.com/tools/godep + + sudo chown -R vagrant:vagrant ~vagrant/go + + echo "export GOPATH=/home/vagrant/go" >> ~vagrant/.bashrc + echo "export PATH=\\\$PATH:\\\$GOPATH/bin:/usr/local/go/bin" >> ~vagrant/.bashrc + SHELL +end diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go new file mode 100644 index 0000000..b2b2365 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/api.go @@ -0,0 +1,68 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package chequebook + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +const Version = "1.0" + +var errNoChequebook = errors.New("no chequebook") + +type Api struct { + chequebookf func() *Chequebook +} + +func NewApi(ch func() *Chequebook) *Api { + return &Api{ch} +} + +func (self *Api) Balance() (string, error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Balance().String(), nil +} + +func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) { + ch := self.chequebookf() + if ch == nil { + return nil, errNoChequebook + } + return ch.Issue(beneficiary, amount) +} + +func (self *Api) Cash(cheque *Cheque) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Cash(cheque) +} + +func (self *Api) Deposit(amount *big.Int) (txhash string, err error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Deposit(amount) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go new file mode 100644 index 0000000..d49964f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque.go @@ -0,0 +1,642 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package chequebook package wraps the 'chequebook' Ethereum smart contract. +// +// The functions in this package allow using chequebook for +// issuing, receiving, verifying cheques in ether; (auto)cashing cheques in ether +// as well as (auto)depositing ether to the chequebook contract. +package chequebook + +//go:generate abigen --sol contract/chequebook.sol --pkg contract --out contract/chequebook.go +//go:generate go run ./gencode.go + +import ( + "bytes" + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// TODO(zelig): watch peer solvency and notify of bouncing cheques +// TODO(zelig): enable paying with cheque by signing off + +// Some functionality require interacting with the blockchain: +// * setting current balance on peer's chequebook +// * sending the transaction to cash the cheque +// * depositing ether to the chequebook +// * watching incoming ether + +var ( + gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook + // gasToDeploy = big.NewInt(3000000) +) + +// Backend wraps all methods required for chequebook operation. +type Backend interface { + bind.ContractBackend + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error) +} + +// Cheque represents a payment promise to a single beneficiary. +type Cheque struct { + Contract common.Address // address of chequebook, needed to avoid cross-contract submission + Beneficiary common.Address + Amount *big.Int // cumulative amount of all funds sent + Sig []byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey) +} + +func (self *Cheque) String() string { + return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig) +} + +type Params struct { + ContractCode, ContractAbi string +} + +var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI} + +// Chequebook can create and sign cheques from a single contract to multiple beneficiaries. +// It is the outgoing payment handler for peer to peer micropayments. +type Chequebook struct { + path string // path to chequebook file + prvKey *ecdsa.PrivateKey // private key to sign cheque with + lock sync.Mutex // + backend Backend // blockchain API + quit chan bool // when closed causes autodeposit to stop + owner common.Address // owner address (derived from pubkey) + contract *contract.Chequebook // abigen binding + session *contract.ChequebookSession // abigen binding with Tx Opts + + // persisted fields + balance *big.Int // not synced with blockchain + contractAddr common.Address // contract address + sent map[common.Address]*big.Int //tallies for beneficiarys + + txhash string // tx hash of last deposit tx + threshold *big.Int // threshold that triggers autodeposit if not nil + buffer *big.Int // buffer to keep on top of balance for fork protection +} + +func (self *Chequebook) String() string { + return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey) +} + +// NewChequebook creates a new Chequebook. +func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) { + balance := new(big.Int) + sent := make(map[common.Address]*big.Int) + + chbook, err := contract.NewChequebook(contractAddr, backend) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + + self = &Chequebook{ + prvKey: prvKey, + balance: balance, + contractAddr: contractAddr, + sent: sent, + path: path, + backend: backend, + owner: transactOpts.From, + contract: chbook, + session: session, + } + + if (contractAddr != common.Address{}) { + self.setBalanceFromBlockChain() + glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String()) + } + return +} + +func (self *Chequebook) setBalanceFromBlockChain() { + balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil) + if err != nil { + glog.V(logger.Error).Infof("can't get balance: %v", err) + } else { + self.balance.Set(balance) + } +} + +// LoadChequebook loads a chequebook from disk (file path). +func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) { + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + self, _ = NewChequebook(path, common.Address{}, prvKey, backend) + + err = json.Unmarshal(data, self) + if err != nil { + return nil, err + } + if checkBalance { + self.setBalanceFromBlockChain() + } + + glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path) + + return +} + +// chequebookFile is the JSON representation of a chequebook. +type chequebookFile struct { + Balance string + Contract string + Owner string + Sent map[string]string +} + +// UnmarshalJSON deserialises a chequebook. +func (self *Chequebook) UnmarshalJSON(data []byte) error { + var file chequebookFile + err := json.Unmarshal(data, &file) + if err != nil { + return err + } + _, ok := self.balance.SetString(file.Balance, 10) + if !ok { + return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance) + } + self.contractAddr = common.HexToAddress(file.Contract) + for addr, sent := range file.Sent { + self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10) + if !ok { + return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent) + } + } + return nil +} + +// MarshalJSON serialises a chequebook. +func (self *Chequebook) MarshalJSON() ([]byte, error) { + var file = &chequebookFile{ + Balance: self.balance.String(), + Contract: self.contractAddr.Hex(), + Owner: self.owner.Hex(), + Sent: make(map[string]string), + } + for addr, sent := range self.sent { + file.Sent[addr.Hex()] = sent.String() + } + return json.Marshal(file) +} + +// Save persists the chequebook on disk, remembering balance, contract address and +// cumulative amount of funds sent for each beneficiary. +func (self *Chequebook) Save() (err error) { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path) + + return ioutil.WriteFile(self.path, data, os.ModePerm) +} + +// Stop quits the autodeposit go routine to terminate +func (self *Chequebook) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Issue creates a cheque signed by the chequebook owner's private key. The +// signer commits to a contract (one that they own), a beneficiary and amount. +func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) { + defer self.lock.Unlock() + self.lock.Lock() + + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("amount must be greater than zero (%v)", amount) + } + if self.balance.Cmp(amount) < 0 { + err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance) + } else { + var sig []byte + sent, found := self.sent[beneficiary] + if !found { + sent = new(big.Int) + self.sent[beneficiary] = sent + } + sum := new(big.Int).Set(sent) + sum.Add(sum, amount) + + sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey) + if err == nil { + ch = &Cheque{ + Contract: self.contractAddr, + Beneficiary: beneficiary, + Amount: sum, + Sig: sig, + } + sent.Set(sum) + self.balance.Sub(self.balance, amount) // subtract amount from balance + } + } + + // auto deposit if threshold is set and balance is less then threshold + // note this is called even if issuing cheque fails + // so we reattempt depositing + if self.threshold != nil { + if self.balance.Cmp(self.threshold) < 0 { + send := new(big.Int).Sub(self.buffer, self.balance) + self.deposit(send) + } + } + + return +} + +// Cash is a convenience method to cash any cheque. +func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) { + return ch.Cash(self.session) +} + +// data to sign: contract address, beneficiary, cumulative amount of funds ever sent +func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte { + bigamount := sum.Bytes() + if len(bigamount) > 32 { + return nil + } + var amount32 [32]byte + copy(amount32[32-len(bigamount):32], bigamount) + input := append(contract.Bytes(), beneficiary.Bytes()...) + input = append(input, amount32[:]...) + return crypto.Keccak256(input) +} + +// Balance returns the current balance of the chequebook. +func (self *Chequebook) Balance() *big.Int { + defer self.lock.Unlock() + self.lock.Lock() + return new(big.Int).Set(self.balance) +} + +// Owner returns the owner account of the chequebook. +func (self *Chequebook) Owner() common.Address { + return self.owner +} + +// Address returns the on-chain contract address of the chequebook. +func (self *Chequebook) Address() common.Address { + return self.contractAddr +} + +// Deposit deposits money to the chequebook account. +func (self *Chequebook) Deposit(amount *big.Int) (string, error) { + defer self.lock.Unlock() + self.lock.Lock() + return self.deposit(amount) +} + +// deposit deposits amount to the chequebook account. +// The caller must hold self.lock. +func (self *Chequebook) deposit(amount *big.Int) (string, error) { + // since the amount is variable here, we do not use sessions + depositTransactor := bind.NewKeyedTransactor(self.prvKey) + depositTransactor.Value = amount + chbookRaw := &contract.ChequebookRaw{Contract: self.contract} + tx, err := chbookRaw.Transfer(depositTransactor) + if err != nil { + glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err) + return "", err + } + // assume that transaction is actually successful, we add the amount to balance right away + self.balance.Add(self.balance, amount) + glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer) + return tx.Hash().Hex(), nil +} + +// AutoDeposit (re)sets interval time and amount which triggers sending funds to the +// chequebook. Contract backend needs to be set if threshold is not less than buffer, then +// deposit will be triggered on every new cheque issued. +func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.threshold = threshold + self.buffer = buffer + self.autoDeposit(interval) +} + +// autoDeposit starts a goroutine that periodically sends funds to the chequebook +// contract caller holds the lock the go routine terminates if Chequebook.quit is closed. +func (self *Chequebook) autoDeposit(interval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if threshold >= balance autodeposit after every cheque issued + if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 { + return + } + + ticker := time.NewTicker(interval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.balance.Cmp(self.buffer) < 0 { + amount := new(big.Int).Sub(self.buffer, self.balance) + txhash, err := self.deposit(amount) + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Outbox can issue cheques from a single contract to a single beneficiary. +type Outbox struct { + chequeBook *Chequebook + beneficiary common.Address +} + +// NewOutbox creates an outbox. +func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox { + return &Outbox{chbook, beneficiary} +} + +// Issue creates cheque. +func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) { + return self.chequeBook.Issue(self.beneficiary, amount) +} + +// AutoDeposit enables auto-deposits on the underlying chequebook. +func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + self.chequeBook.AutoDeposit(interval, threshold, buffer) +} + +// Stop helps satisfy the swap.OutPayment interface. +func (self *Outbox) Stop() {} + +// String implements fmt.Stringer. +func (self *Outbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance()) +} + +// Inbox can deposit, verify and cash cheques from a single contract to a single +// beneficiary. It is the incoming payment handler for peer to peer micropayments. +type Inbox struct { + lock sync.Mutex + contract common.Address // peer's chequebook contract + beneficiary common.Address // local peer's receiving address + sender common.Address // local peer's address to send cashing tx from + signer *ecdsa.PublicKey // peer's public key + txhash string // tx hash of last cashing tx + abigen bind.ContractBackend // blockchain API + session *contract.ChequebookSession // abi contract backend with tx opts + quit chan bool // when closed causes autocash to stop + maxUncashed *big.Int // threshold that triggers autocashing + cashed *big.Int // cumulative amount cashed + cheque *Cheque // last cheque, nil if none yet received +} + +// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated +// from blockchain when first cheque is received. +func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) { + if signer == nil { + return nil, fmt.Errorf("signer is null") + } + chbook, err := contract.NewChequebook(contractAddr, abigen) + if err != nil { + return nil, err + } + transactOpts := bind.NewKeyedTransactor(prvKey) + transactOpts.GasLimit = gasToCash + session := &contract.ChequebookSession{ + Contract: chbook, + TransactOpts: *transactOpts, + } + sender := transactOpts.From + + self = &Inbox{ + contract: contractAddr, + beneficiary: beneficiary, + sender: sender, + signer: signer, + session: session, + cashed: new(big.Int).Set(common.Big0), + } + glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer)) + return +} + +func (self *Inbox) String() string { + return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount) +} + +// Stop quits the autocash goroutine. +func (self *Inbox) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.quit != nil { + close(self.quit) + self.quit = nil + } +} + +// Cash attempts to cash the current cheque. +func (self *Inbox) Cash() (txhash string, err error) { + if self.cheque != nil { + txhash, err = self.cheque.Cash(self.session) + glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex()) + self.cashed = self.cheque.Amount + } + return +} + +// AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed +// cheque if maxUncashed is set to 0, then autocash on receipt. +func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) { + defer self.lock.Unlock() + self.lock.Lock() + self.maxUncashed = maxUncashed + self.autoCash(cashInterval) +} + +// autoCash starts a loop that periodically clears the last check +// if the peer is trusted. Clearing period could be 24h or a week. +// +// The caller must hold self.lock. +func (self *Inbox) autoCash(cashInterval time.Duration) { + if self.quit != nil { + close(self.quit) + self.quit = nil + } + // if maxUncashed is set to 0, then autocash on receipt + if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 { + return + } + + ticker := time.NewTicker(cashInterval) + self.quit = make(chan bool) + quit := self.quit + go func() { + FOR: + for { + select { + case <-quit: + break FOR + case <-ticker.C: + self.lock.Lock() + if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 { + txhash, err := self.Cash() + if err == nil { + self.txhash = txhash + } + } + self.lock.Unlock() + } + } + }() + return +} + +// Receive is called to deposit the latest cheque to the incoming Inbox. +// The given promise must be a *Cheque. +func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) { + ch := promise.(*Cheque) + + defer self.lock.Unlock() + self.lock.Lock() + + var sum *big.Int + if self.cheque == nil { + // the sum is checked against the blockchain once a check is received + tally, err := self.session.Sent(self.beneficiary) + if err != nil { + return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err) + } + sum = tally + } else { + sum = self.cheque.Amount + } + + amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum) + var uncashed *big.Int + if err == nil { + self.cheque = ch + + if self.maxUncashed != nil { + uncashed = new(big.Int).Sub(ch.Amount, self.cashed) + if self.maxUncashed.Cmp(uncashed) < 0 { + self.Cash() + } + } + glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed) + } + + return amount, err +} + +// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature. +func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) { + glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum) + if sum == nil { + return nil, fmt.Errorf("invalid amount") + } + + if self.Beneficiary != beneficiary { + return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex()) + } + if self.Contract != contract { + return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex()) + } + + amount := new(big.Int).Set(self.Amount) + if sum != nil { + amount.Sub(amount, sum) + if amount.Cmp(common.Big0) <= 0 { + return nil, fmt.Errorf("incorrect amount: %v <= 0", amount) + } + } + + pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig) + if err != nil { + return nil, fmt.Errorf("invalid signature: %v", err) + } + if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) { + return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) + } + return amount, nil +} + +// v/r/s representation of signature +func sig2vrs(sig []byte) (v byte, r, s [32]byte) { + v = sig[64] + 27 + copy(r[:], sig[:32]) + copy(s[:], sig[32:64]) + return +} + +// Cash cashes the cheque by sending an Ethereum transaction. +func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) { + v, r, s := sig2vrs(self.Sig) + tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s) + if err != nil { + return "", err + } + return tx.Hash().Hex(), nil +} + +// ValidateCode checks that the on-chain code at address matches the expected chequebook +// contract code. This is used to detect suicided chequebooks. +func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) { + code, err := b.CodeAt(ctx, address, nil) + if err != nil { + return false, err + } + return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque_test.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque_test.go new file mode 100644 index 0000000..85d9231 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/cheque_test.go @@ -0,0 +1,531 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package chequebook + +import ( + "crypto/ecdsa" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + key0, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key1, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + key2, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + addr0 = crypto.PubkeyToAddress(key0.PublicKey) + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) +) + +func newTestBackend() *backends.SimulatedBackend { + return backends.NewSimulatedBackend( + core.GenesisAccount{Address: addr0, Balance: big.NewInt(1000000000)}, + core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000000)}, + core.GenesisAccount{Address: addr2, Balance: big.NewInt(1000000000)}, + ) +} + +func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) { + deployTransactor := bind.NewKeyedTransactor(prvKey) + deployTransactor.Value = amount + addr, _, _, err := contract.DeployChequebook(deployTransactor, backend) + if err != nil { + return common.Address{}, err + } + backend.Commit() + return addr, nil +} + +func TestIssueAndReceive(t *testing.T) { + path := filepath.Join(os.TempDir(), "chequebook-test.json") + backend := newTestBackend() + addr0, err := deploy(key0, big.NewInt(0), backend) + if err != nil { + t.Fatalf("deploy contract: expected no error, got %v", err) + } + chbook, err := NewChequebook(path, addr0, key0, backend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + chbook.sent[addr1] = new(big.Int).SetUint64(42) + amount := common.Big1 + + if _, err = chbook.Issue(addr1, amount); err == nil { + t.Fatalf("expected insufficient funds error, got none") + } + + chbook.balance = new(big.Int).Set(common.Big1) + if chbook.Balance().Cmp(common.Big1) != 0 { + t.Fatalf("expected: %v, got %v", "0", chbook.Balance()) + } + + ch, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if chbook.Balance().Cmp(common.Big0) != 0 { + t.Errorf("expected: %v, got %v", "0", chbook.Balance()) + } + + chbox, err := NewInbox(key1, addr0, addr1, &key0.PublicKey, backend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + received, err := chbox.Receive(ch) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if received.Cmp(big.NewInt(43)) != 0 { + t.Errorf("expected: %v, got %v", "43", received) + } + +} + +func TestCheckbookFile(t *testing.T) { + path := filepath.Join(os.TempDir(), "chequebook-test.json") + backend := newTestBackend() + chbook, err := NewChequebook(path, addr0, key0, backend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + chbook.sent[addr1] = new(big.Int).SetUint64(42) + chbook.balance = new(big.Int).Set(common.Big1) + + chbook.Save() + + chbook, err = LoadChequebook(path, key0, backend, false) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if chbook.Balance().Cmp(common.Big1) != 0 { + t.Errorf("expected: %v, got %v", "0", chbook.Balance()) + } + + var ch *Cheque + if ch, err = chbook.Issue(addr1, common.Big1); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if ch.Amount.Cmp(new(big.Int).SetUint64(43)) != 0 { + t.Errorf("expected: %v, got %v", "0", ch.Amount) + } + + err = chbook.Save() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } +} + +func TestVerifyErrors(t *testing.T) { + path0 := filepath.Join(os.TempDir(), "chequebook-test-0.json") + backend := newTestBackend() + contr0, err := deploy(key0, common.Big2, backend) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + chbook0, err := NewChequebook(path0, contr0, key0, backend) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + path1 := filepath.Join(os.TempDir(), "chequebook-test-1.json") + contr1, _ := deploy(key1, common.Big2, backend) + chbook1, err := NewChequebook(path1, contr1, key1, backend) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + chbook0.sent[addr1] = new(big.Int).SetUint64(42) + chbook0.balance = new(big.Int).Set(common.Big2) + chbook1.balance = new(big.Int).Set(common.Big1) + amount := common.Big1 + ch0, err := chbook0.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + time.Sleep(5) + chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + received, err := chbox.Receive(ch0) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if received.Cmp(big.NewInt(43)) != 0 { + t.Errorf("expected: %v, got %v", "43", received) + } + + ch1, err := chbook0.Issue(addr2, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + received, err = chbox.Receive(ch1) + t.Logf("correct error: %v", err) + if err == nil { + t.Fatalf("expected receiver error, got none") + } + + ch2, err := chbook1.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + received, err = chbox.Receive(ch2) + t.Logf("correct error: %v", err) + if err == nil { + t.Fatalf("expected sender error, got none") + } + + _, err = chbook1.Issue(addr1, new(big.Int).SetInt64(-1)) + t.Logf("correct error: %v", err) + if err == nil { + t.Fatalf("expected incorrect amount error, got none") + } + + received, err = chbox.Receive(ch0) + t.Logf("correct error: %v", err) + if err == nil { + t.Fatalf("expected incorrect amount error, got none") + } + +} + +func TestDeposit(t *testing.T) { + path0 := filepath.Join(os.TempDir(), "chequebook-test-0.json") + backend := newTestBackend() + contr0, _ := deploy(key0, new(big.Int), backend) + + chbook, err := NewChequebook(path0, contr0, key0, backend) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + balance := new(big.Int).SetUint64(42) + chbook.Deposit(balance) + backend.Commit() + if chbook.balance.Cmp(balance) != 0 { + t.Fatalf("expected balance %v, got %v", balance, chbook.balance) + } + + amount := common.Big1 + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + exp := new(big.Int).SetUint64(41) + if chbook.balance.Cmp(exp) != 0 { + t.Fatalf("expected balance %v, got %v", exp, chbook.balance) + } + + // autodeposit on each issue + chbook.AutoDeposit(0, balance, balance) + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + if chbook.balance.Cmp(balance) != 0 { + t.Fatalf("expected balance %v, got %v", balance, chbook.balance) + } + + // autodeposit off + chbook.AutoDeposit(0, common.Big0, balance) + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + exp = new(big.Int).SetUint64(40) + if chbook.balance.Cmp(exp) != 0 { + t.Fatalf("expected balance %v, got %v", exp, chbook.balance) + } + + // autodeposit every 10ms if new cheque issued + interval := 30 * time.Millisecond + chbook.AutoDeposit(interval, common.Big1, balance) + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + exp = new(big.Int).SetUint64(38) + if chbook.balance.Cmp(exp) != 0 { + t.Fatalf("expected balance %v, got %v", exp, chbook.balance) + } + + time.Sleep(3 * interval) + backend.Commit() + if chbook.balance.Cmp(balance) != 0 { + t.Fatalf("expected balance %v, got %v", balance, chbook.balance) + } + + exp = new(big.Int).SetUint64(40) + chbook.AutoDeposit(4*interval, exp, balance) + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + time.Sleep(3 * interval) + backend.Commit() + if chbook.balance.Cmp(exp) != 0 { + t.Fatalf("expected balance %v, got %v", exp, chbook.balance) + } + + _, err = chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + time.Sleep(1 * interval) + backend.Commit() + + if chbook.balance.Cmp(balance) != 0 { + t.Fatalf("expected balance %v, got %v", balance, chbook.balance) + } + + chbook.AutoDeposit(1*interval, common.Big0, balance) + chbook.Stop() + + _, err = chbook.Issue(addr1, common.Big1) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + _, err = chbook.Issue(addr1, common.Big2) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + time.Sleep(1 * interval) + backend.Commit() + + exp = new(big.Int).SetUint64(39) + if chbook.balance.Cmp(exp) != 0 { + t.Fatalf("expected balance %v, got %v", exp, chbook.balance) + } + +} + +func TestCash(t *testing.T) { + path := filepath.Join(os.TempDir(), "chequebook-test.json") + backend := newTestBackend() + contr0, _ := deploy(key0, common.Big2, backend) + + chbook, err := NewChequebook(path, contr0, key0, backend) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + chbook.sent[addr1] = new(big.Int).SetUint64(42) + amount := common.Big1 + chbook.balance = new(big.Int).Set(common.Big1) + ch, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + // cashing latest cheque + if _, err = chbox.Receive(ch); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if _, err = ch.Cash(chbook.session); err != nil { + t.Fatal("Cash failed:", err) + } + backend.Commit() + + chbook.balance = new(big.Int).Set(common.Big3) + ch0, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + ch1, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + interval := 10 * time.Millisecond + // setting autocash with interval of 10ms + chbox.AutoCash(interval, nil) + _, err = chbox.Receive(ch0) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + _, err = chbox.Receive(ch1) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + // expBalance := big.NewInt(2) + // gotBalance := backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + // after 3x interval time and 2 cheques received, exactly one cashing tx is sent + time.Sleep(4 * interval) + backend.Commit() + + // expBalance = big.NewInt(4) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + // after stopping autocash no more tx are sent + ch2, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + chbox.Stop() + _, err = chbox.Receive(ch2) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + time.Sleep(2 * interval) + backend.Commit() + // expBalance = big.NewInt(4) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + // autocash below 1 + chbook.balance = big.NewInt(2) + chbox.AutoCash(0, common.Big1) + + ch3, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + // expBalance = big.NewInt(4) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + ch4, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + _, err = chbox.Receive(ch3) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + _, err = chbox.Receive(ch4) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + + // 2 checks of amount 1 received, exactly 1 tx is sent + // expBalance = big.NewInt(6) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + // autochash on receipt when maxUncashed is 0 + chbook.balance = new(big.Int).Set(common.Big2) + chbox.AutoCash(0, common.Big0) + + ch5, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + // expBalance = big.NewInt(5) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + ch6, err := chbook.Issue(addr1, amount) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + _, err = chbox.Receive(ch5) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + // expBalance = big.NewInt(4) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + + _, err = chbox.Receive(ch6) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + backend.Commit() + // expBalance = big.NewInt(6) + // gotBalance = backend.BalanceAt(addr1) + // if gotBalance.Cmp(expBalance) != 0 { + // t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance) + // } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go new file mode 100644 index 0000000..4709015 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.go @@ -0,0 +1,541 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ChequebookABI is the input ABI used to generate the binding from. +const ChequebookABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"sent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]` + +// ChequebookBin is the compiled bytecode used for deploying new contracts. +const ChequebookBin = `0x606060405260008054600160a060020a031916331790556101ff806100246000396000f3606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff` + +// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it. +func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// Chequebook is an auto generated Go binding around an Ethereum contract. +type Chequebook struct { + ChequebookCaller // Read-only binding to the contract + ChequebookTransactor // Write-only binding to the contract +} + +// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract. +type ChequebookCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ChequebookTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ChequebookSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ChequebookSession struct { + Contract *Chequebook // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ChequebookCallerSession struct { + Contract *ChequebookCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ChequebookTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ChequebookTransactorSession struct { + Contract *ChequebookTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ChequebookRaw is an auto generated low-level Go binding around an Ethereum contract. +type ChequebookRaw struct { + Contract *Chequebook // Generic contract binding to access the raw methods on +} + +// ChequebookCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ChequebookCallerRaw struct { + Contract *ChequebookCaller // Generic read-only contract binding to access the raw methods on +} + +// ChequebookTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ChequebookTransactorRaw struct { + Contract *ChequebookTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract. +func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) { + contract, err := bindChequebook(address, backend, backend) + if err != nil { + return nil, err + } + return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil +} + +// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) { + contract, err := bindChequebook(address, caller, nil) + if err != nil { + return nil, err + } + return &ChequebookCaller{contract: contract}, nil +} + +// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract. +func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) { + contract, err := bindChequebook(address, nil, transactor) + if err != nil { + return nil, err + } + return &ChequebookTransactor{contract: contract}, nil +} + +// bindChequebook binds a generic wrapper to an already deployed contract. +func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.ChequebookCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.ChequebookTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Chequebook *ChequebookCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Chequebook.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Chequebook *ChequebookTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Chequebook *ChequebookTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Chequebook.Contract.contract.Transact(opts, method, params...) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCaller) Sent(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _Chequebook.contract.Call(opts, out, "sent", arg0) + return *ret0, err +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Sent is a free data retrieval call binding the contract method 0x7bf786f8. +// +// Solidity: function sent( address) constant returns(uint256) +func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int, error) { + return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Cash is a paid mutator transaction binding the contract method 0xfbf788d6. +// +// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns() +func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { + return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Chequebook.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) { + return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) +} + +// MortalABI is the input ABI used to generate the binding from. +const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]` + +// MortalBin is the compiled bytecode used for deploying new contracts. +const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56` + +// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it. +func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// Mortal is an auto generated Go binding around an Ethereum contract. +type Mortal struct { + MortalCaller // Read-only binding to the contract + MortalTransactor // Write-only binding to the contract +} + +// MortalCaller is an auto generated read-only Go binding around an Ethereum contract. +type MortalCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract. +type MortalTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MortalSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MortalSession struct { + Contract *Mortal // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MortalCallerSession struct { + Contract *MortalCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MortalTransactorSession struct { + Contract *MortalTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MortalRaw is an auto generated low-level Go binding around an Ethereum contract. +type MortalRaw struct { + Contract *Mortal // Generic contract binding to access the raw methods on +} + +// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MortalCallerRaw struct { + Contract *MortalCaller // Generic read-only contract binding to access the raw methods on +} + +// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MortalTransactorRaw struct { + Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewMortal creates a new instance of Mortal, bound to a specific deployed contract. +func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) { + contract, err := bindMortal(address, backend, backend) + if err != nil { + return nil, err + } + return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil +} + +// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract. +func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) { + contract, err := bindMortal(address, caller, nil) + if err != nil { + return nil, err + } + return &MortalCaller{contract: contract}, nil +} + +// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract. +func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) { + contract, err := bindMortal(address, nil, transactor) + if err != nil { + return nil, err + } + return &MortalTransactor{contract: contract}, nil +} + +// bindMortal binds a generic wrapper to an already deployed contract. +func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(MortalABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Mortal.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Mortal.Contract.contract.Transact(opts, method, params...) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Mortal.contract.Transact(opts, "kill") +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5. +// +// Solidity: function kill() returns() +func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) { + return _Mortal.Contract.Kill(&_Mortal.TransactOpts) +} + +// OwnedABI is the input ABI used to generate the binding from. +const OwnedABI = `[{"inputs":[],"type":"constructor"}]` + +// OwnedBin is the compiled bytecode used for deploying new contracts. +const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200` + +// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it. +func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// Owned is an auto generated Go binding around an Ethereum contract. +type Owned struct { + OwnedCaller // Read-only binding to the contract + OwnedTransactor // Write-only binding to the contract +} + +// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract. +type OwnedCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract. +type OwnedTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// OwnedSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type OwnedSession struct { + Contract *Owned // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type OwnedCallerSession struct { + Contract *OwnedCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type OwnedTransactorSession struct { + Contract *OwnedTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract. +type OwnedRaw struct { + Contract *Owned // Generic contract binding to access the raw methods on +} + +// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type OwnedCallerRaw struct { + Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on +} + +// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type OwnedTransactorRaw struct { + Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewOwned creates a new instance of Owned, bound to a specific deployed contract. +func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) { + contract, err := bindOwned(address, backend, backend) + if err != nil { + return nil, err + } + return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil +} + +// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract. +func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) { + contract, err := bindOwned(address, caller, nil) + if err != nil { + return nil, err + } + return &OwnedCaller{contract: contract}, nil +} + +// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract. +func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) { + contract, err := bindOwned(address, nil, transactor) + if err != nil { + return nil, err + } + return &OwnedTransactor{contract: contract}, nil +} + +// bindOwned binds a generic wrapper to an already deployed contract. +func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(OwnedABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Owned.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Owned.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Owned.Contract.contract.Transact(opts, method, params...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol new file mode 100644 index 0000000..eefe6c0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/chequebook.sol @@ -0,0 +1,45 @@ +import "mortal"; + +/// @title Chequebook for Ethereum micropayments +/// @author Daniel A. Nagy +contract chequebook is mortal { + // Cumulative paid amount in wei to each beneficiary + mapping (address => uint256) public sent; + + /// @notice Overdraft event + event Overdraft(address deadbeat); + + /// @notice Cash cheque + /// + /// @param beneficiary beneficiary address + /// @param amount cumulative amount in wei + /// @param sig_v signature parameter v + /// @param sig_r signature parameter r + /// @param sig_s signature parameter s + /// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount + function cash(address beneficiary, uint256 amount, + uint8 sig_v, bytes32 sig_r, bytes32 sig_s) { + // Check if the cheque is old. + // Only cheques that are more recent than the last cashed one are considered. + if(amount <= sent[beneficiary]) return; + // Check the digital signature of the cheque. + bytes32 hash = sha3(address(this), beneficiary, amount); + if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return; + // Attempt sending the difference between the cumulative amount on the cheque + // and the cumulative amount on the last cashed cheque to beneficiary. + if (amount - sent[beneficiary] >= this.balance) { + // update the cumulative amount before sending + sent[beneficiary] = amount; + if (!beneficiary.send(amount - sent[beneficiary])) { + // Upon failure to execute send, revert everything + throw; + } + } else { + // Upon failure, punish owner for writing a bounced cheque. + // owner.sendToDebtorsPrison(); + Overdraft(owner); + // Compensate beneficiary. + suicide(beneficiary); + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go new file mode 100644 index 0000000..b08e04e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/contract/code.go @@ -0,0 +1,5 @@ +package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "0x606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100bd57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bf575b505050505050565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a0390811691161461015a576100a3565b600160a060020a038681166000908152600160205260409020543090911631908603106101b357604060008181208790559051600160a060020a0388169190819081818181818881f1935050505015156100a357610002565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff" diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go new file mode 100644 index 0000000..faa9272 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/chequebook/gencode.go @@ -0,0 +1,71 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build none + +// This program generates contract/code.go, which contains the chequebook code +// after deployment. +package main + +import ( + "fmt" + "io/ioutil" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAccount = core.GenesisAccount{ + Address: crypto.PubkeyToAddress(testKey.PublicKey), + Balance: big.NewInt(500000000000), + } +) + +func main() { + backend := backends.NewSimulatedBackend(testAccount) + auth := bind.NewKeyedTransactor(testKey) + + // Deploy the contract, get the code. + addr, _, _, err := contract.DeployChequebook(auth, backend) + if err != nil { + panic(err) + } + backend.Commit() + code, err := backend.CodeAt(nil, addr, nil) + if err != nil { + panic(err) + } + if len(code) == 0 { + panic("empty code") + } + + // Write the output file. + content := fmt.Sprintf(`package contract + +// ContractDeployedCode is used to detect suicides. This constant needs to be +// updated when the contract code is changed. +const ContractDeployedCode = "%#x" +`, code) + if err := ioutil.WriteFile("contract/code.go", []byte(content), 0644); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/README.md b/vendor/github.com/ethereum/go-ethereum/contracts/ens/README.md new file mode 100644 index 0000000..274fc14 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/README.md @@ -0,0 +1,20 @@ +# Swarm ENS interface + +## Usage + +Full documentation for the Ethereum Name Service [can be found as EIP 137](https://github.com/ethereum/EIPs/issues/137). +This package offers a simple binding that streamlines the registration arbitrary utf8 domain names to swarm content hashes. + +## Development + +The SOL file in contract subdirectory implements the ENS root registry, a simple +first-in-first-served registrar for the root namespace, and a simple resolver contract; +they're used in tests, and can be used to deploy these contracts for your own purposes. + +The solidity source code can be found at [github.com/arachnid/ens/](https://github.com/arachnid/ens/). + +The go bindings for ENS contracts are generated using `abigen` via the go generator: + +```shell +go generate ./contracts/ens +``` diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go new file mode 100644 index 0000000..aca16de --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.go @@ -0,0 +1,921 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package contract + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ENSABI is the input ABI used to generate the binding from. +const ENSABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},{"inputs":[{"name":"owner","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"}]` + +// ENSBin is the compiled bytecode used for deploying new contracts. +const ENSBin = `0x606060405260405160208061032683395060806040525160008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a03191682179055506102c88061005e6000396000f3606060405260e060020a60003504630178b8bf811461004757806302571be31461006e57806306ab5923146100915780631896f70a146100c85780635b0fc9c3146100fc575b005b610130600435600081815260208190526040902060010154600160a060020a03165b919050565b610130600435600081815260208190526040902054600160a060020a0316610069565b6100456004356024356044356000838152602081905260408120548490600160a060020a0390811633919091161461014d57610002565b6100456004356024356000828152602081905260409020548290600160a060020a039081163391909116146101e757610002565b6100456004356024356000828152602081905260409020548290600160a060020a0390811633919091161461025957610002565b60408051600160a060020a03929092168252519081900360200190f35b60408051868152602081810187905282519182900383018220600160a060020a03871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a382600060005060008460001916815260200190815260200160002060005060000160006101000a815481600160a060020a03021916908302179055505050505050565b60408051600160a060020a0384168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a2506000828152602081905260409020600101805473ffffffffffffffffffffffffffffffffffffffff1916821790555050565b60408051600160a060020a0384168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a2506000828152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191682179055505056` + +// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it. +func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.Address) (common.Address, *types.Transaction, *ENS, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend, owner) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// ENS is an auto generated Go binding around an Ethereum contract. +type ENS struct { + ENSCaller // Read-only binding to the contract + ENSTransactor // Write-only binding to the contract +} + +// ENSCaller is an auto generated read-only Go binding around an Ethereum contract. +type ENSCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ENSTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ENSSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ENSSession struct { + Contract *ENS // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ENSCallerSession struct { + Contract *ENSCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ENSTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ENSTransactorSession struct { + Contract *ENSTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ENSRaw is an auto generated low-level Go binding around an Ethereum contract. +type ENSRaw struct { + Contract *ENS // Generic contract binding to access the raw methods on +} + +// ENSCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ENSCallerRaw struct { + Contract *ENSCaller // Generic read-only contract binding to access the raw methods on +} + +// ENSTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ENSTransactorRaw struct { + Contract *ENSTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewENS creates a new instance of ENS, bound to a specific deployed contract. +func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) { + contract, err := bindENS(address, backend, backend) + if err != nil { + return nil, err + } + return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil +} + +// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract. +func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) { + contract, err := bindENS(address, caller, nil) + if err != nil { + return nil, err + } + return &ENSCaller{contract: contract}, nil +} + +// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract. +func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) { + contract, err := bindENS(address, nil, transactor) + if err != nil { + return nil, err + } + return &ENSTransactor{contract: contract}, nil +} + +// bindENS binds a generic wrapper to an already deployed contract. +func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ENSABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.ENSCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.ENSTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ENS *ENSCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ENS.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ENS *ENSTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ENS.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ENS *ENSTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ENS.Contract.contract.Transact(opts, method, params...) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Owner(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "owner", node) + return *ret0, err +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Owner is a free data retrieval call binding the contract method 0x02571be3. +// +// Solidity: function owner(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Owner(node [32]byte) (common.Address, error) { + return _ENS.Contract.Owner(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCaller) Resolver(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _ENS.contract.Call(opts, out, "resolver", node) + return *ret0, err +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// Resolver is a free data retrieval call binding the contract method 0x0178b8bf. +// +// Solidity: function resolver(node bytes32) constant returns(address) +func (_ENS *ENSCallerSession) Resolver(node [32]byte) (common.Address, error) { + return _ENS.Contract.Resolver(&_ENS.CallOpts, node) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetOwner(opts *bind.TransactOpts, node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setOwner", node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3. +// +// Solidity: function setOwner(node bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetOwner(node [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetOwner(&_ENS.TransactOpts, node, owner) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactor) SetResolver(opts *bind.TransactOpts, node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setResolver", node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetResolver is a paid mutator transaction binding the contract method 0x1896f70a. +// +// Solidity: function setResolver(node bytes32, resolver address) returns() +func (_ENS *ENSTransactorSession) SetResolver(node [32]byte, resolver common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetResolver(&_ENS.TransactOpts, node, resolver) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactor) SetSubnodeOwner(opts *bind.TransactOpts, node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.contract.Transact(opts, "setSubnodeOwner", node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// SetSubnodeOwner is a paid mutator transaction binding the contract method 0x06ab5923. +// +// Solidity: function setSubnodeOwner(node bytes32, label bytes32, owner address) returns() +func (_ENS *ENSTransactorSession) SetSubnodeOwner(node [32]byte, label [32]byte, owner common.Address) (*types.Transaction, error) { + return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner) +} + +// FIFSRegistrarABI is the input ABI used to generate the binding from. +const FIFSRegistrarABI = `[{"constant":false,"inputs":[{"name":"subnode","type":"bytes32"},{"name":"owner","type":"address"}],"name":"register","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"node","type":"bytes32"}],"type":"constructor"}]` + +// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts. +const FIFSRegistrarBin = `0x6060604081815280610620833960a090525160805160008054600160a060020a031916831790558160a0610367806100878339018082600160a060020a03168152602001915050604051809103906000f0600160006101000a815481600160a060020a0302191690830217905550806002600050819055505050610232806103ee6000396000f3606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056606060405260e060020a6000350463d22057a9811461001b575b005b61001960043560243560025460408051918252602082810185905260008054835194859003840185207f02571be300000000000000000000000000000000000000000000000000000000865260048601819052935193949193600160a060020a03909116926302571be39260248181019391829003018187876161da5a03f11561000257505060405151915050600160a060020a0381166000148015906100d4575033600160a060020a031681600160a060020a031614155b156100de57610002565b60408051600080546002547f06ab592300000000000000000000000000000000000000000000000000000000845260048401526024830188905230600160a060020a03908116604485015293519316926306ab5923926064818101939291829003018183876161da5a03f11561000257505060008054600154604080517f1896f70a00000000000000000000000000000000000000000000000000000000815260048101889052600160a060020a0392831660248201529051929091169350631896f70a926044828101939192829003018183876161da5a03f11561000257505060008054604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260048101879052600160a060020a0388811660248301529151929091169350635b0fc9c3926044828101939192829003018183876161da5a03f115610002575050505050505056` + +// DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it. +func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(FIFSRegistrarBin), backend, ensAddr, node) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// FIFSRegistrar is an auto generated Go binding around an Ethereum contract. +type FIFSRegistrar struct { + FIFSRegistrarCaller // Read-only binding to the contract + FIFSRegistrarTransactor // Write-only binding to the contract +} + +// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract. +type FIFSRegistrarCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarTransactor is an auto generated write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type FIFSRegistrarSession struct { + Contract *FIFSRegistrar // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type FIFSRegistrarCallerSession struct { + Contract *FIFSRegistrarCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// FIFSRegistrarTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type FIFSRegistrarTransactorSession struct { + Contract *FIFSRegistrarTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// FIFSRegistrarRaw is an auto generated low-level Go binding around an Ethereum contract. +type FIFSRegistrarRaw struct { + Contract *FIFSRegistrar // Generic contract binding to access the raw methods on +} + +// FIFSRegistrarCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type FIFSRegistrarCallerRaw struct { + Contract *FIFSRegistrarCaller // Generic read-only contract binding to access the raw methods on +} + +// FIFSRegistrarTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type FIFSRegistrarTransactorRaw struct { + Contract *FIFSRegistrarTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) { + contract, err := bindFIFSRegistrar(address, backend, backend) + if err != nil { + return nil, err + } + return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil +} + +// NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) { + contract, err := bindFIFSRegistrar(address, caller, nil) + if err != nil { + return nil, err + } + return &FIFSRegistrarCaller{contract: contract}, nil +} + +// NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract. +func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) { + contract, err := bindFIFSRegistrar(address, nil, transactor) + if err != nil { + return nil, err + } + return &FIFSRegistrarTransactor{contract: contract}, nil +} + +// bindFIFSRegistrar binds a generic wrapper to an already deployed contract. +func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.FIFSRegistrarCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.FIFSRegistrarTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_FIFSRegistrar *FIFSRegistrarCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _FIFSRegistrar.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_FIFSRegistrar *FIFSRegistrarTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.contract.Transact(opts, method, params...) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactor) Register(opts *bind.TransactOpts, subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.contract.Transact(opts, "register", subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// Register is a paid mutator transaction binding the contract method 0xd22057a9. +// +// Solidity: function register(subnode bytes32, owner address) returns() +func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(subnode [32]byte, owner common.Address) (*types.Transaction, error) { + return _FIFSRegistrar.Contract.Register(&_FIFSRegistrar.TransactOpts, subnode, owner) +} + +// PublicResolverABI is the input ABI used to generate the binding from. +const PublicResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// PublicResolverBin is the compiled bytecode used for deploying new contracts. +const PublicResolverBin = `0x606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056` + +// DeployPublicResolver deploys a new Ethereum contract, binding an instance of PublicResolver to it. +func DeployPublicResolver(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address) (common.Address, *types.Transaction, *PublicResolver, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(PublicResolverBin), backend, ensAddr) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// PublicResolver is an auto generated Go binding around an Ethereum contract. +type PublicResolver struct { + PublicResolverCaller // Read-only binding to the contract + PublicResolverTransactor // Write-only binding to the contract +} + +// PublicResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type PublicResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PublicResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PublicResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PublicResolverSession struct { + Contract *PublicResolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PublicResolverCallerSession struct { + Contract *PublicResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PublicResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PublicResolverTransactorSession struct { + Contract *PublicResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PublicResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type PublicResolverRaw struct { + Contract *PublicResolver // Generic contract binding to access the raw methods on +} + +// PublicResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PublicResolverCallerRaw struct { + Contract *PublicResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// PublicResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PublicResolverTransactorRaw struct { + Contract *PublicResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPublicResolver creates a new instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolver(address common.Address, backend bind.ContractBackend) (*PublicResolver, error) { + contract, err := bindPublicResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &PublicResolver{PublicResolverCaller: PublicResolverCaller{contract: contract}, PublicResolverTransactor: PublicResolverTransactor{contract: contract}}, nil +} + +// NewPublicResolverCaller creates a new read-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverCaller(address common.Address, caller bind.ContractCaller) (*PublicResolverCaller, error) { + contract, err := bindPublicResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &PublicResolverCaller{contract: contract}, nil +} + +// NewPublicResolverTransactor creates a new write-only instance of PublicResolver, bound to a specific deployed contract. +func NewPublicResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*PublicResolverTransactor, error) { + contract, err := bindPublicResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &PublicResolverTransactor{contract: contract}, nil +} + +// bindPublicResolver binds a generic wrapper to an already deployed contract. +func bindPublicResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(PublicResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.PublicResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.PublicResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PublicResolver *PublicResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _PublicResolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PublicResolver *PublicResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PublicResolver *PublicResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PublicResolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_PublicResolver *PublicResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _PublicResolver.Contract.Addr(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _PublicResolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_PublicResolver *PublicResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_PublicResolver *PublicResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactor) SetAddr(opts *bind.TransactOpts, node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setAddr", node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00. +// +// Solidity: function setAddr(node bytes32, addr address) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetAddr(node [32]byte, addr common.Address) (*types.Transaction, error) { + return _PublicResolver.Contract.SetAddr(&_PublicResolver.TransactOpts, node, addr) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactor) SetContent(opts *bind.TransactOpts, node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.contract.Transact(opts, "setContent", node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// SetContent is a paid mutator transaction binding the contract method 0xc3d014d6. +// +// Solidity: function setContent(node bytes32, hash bytes32) returns() +func (_PublicResolver *PublicResolverTransactorSession) SetContent(node [32]byte, hash [32]byte) (*types.Transaction, error) { + return _PublicResolver.Contract.SetContent(&_PublicResolver.TransactOpts, node, hash) +} + +// ResolverABI is the input ABI used to generate the binding from. +const ResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]` + +// ResolverBin is the compiled bytecode used for deploying new contracts. +const ResolverBin = `0x` + +// DeployResolver deploys a new Ethereum contract, binding an instance of Resolver to it. +func DeployResolver(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Resolver, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ResolverBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// Resolver is an auto generated Go binding around an Ethereum contract. +type Resolver struct { + ResolverCaller // Read-only binding to the contract + ResolverTransactor // Write-only binding to the contract +} + +// ResolverCaller is an auto generated read-only Go binding around an Ethereum contract. +type ResolverCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ResolverTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ResolverSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ResolverSession struct { + Contract *Resolver // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ResolverCallerSession struct { + Contract *ResolverCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ResolverTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ResolverTransactorSession struct { + Contract *ResolverTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ResolverRaw is an auto generated low-level Go binding around an Ethereum contract. +type ResolverRaw struct { + Contract *Resolver // Generic contract binding to access the raw methods on +} + +// ResolverCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ResolverCallerRaw struct { + Contract *ResolverCaller // Generic read-only contract binding to access the raw methods on +} + +// ResolverTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ResolverTransactorRaw struct { + Contract *ResolverTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewResolver creates a new instance of Resolver, bound to a specific deployed contract. +func NewResolver(address common.Address, backend bind.ContractBackend) (*Resolver, error) { + contract, err := bindResolver(address, backend, backend) + if err != nil { + return nil, err + } + return &Resolver{ResolverCaller: ResolverCaller{contract: contract}, ResolverTransactor: ResolverTransactor{contract: contract}}, nil +} + +// NewResolverCaller creates a new read-only instance of Resolver, bound to a specific deployed contract. +func NewResolverCaller(address common.Address, caller bind.ContractCaller) (*ResolverCaller, error) { + contract, err := bindResolver(address, caller, nil) + if err != nil { + return nil, err + } + return &ResolverCaller{contract: contract}, nil +} + +// NewResolverTransactor creates a new write-only instance of Resolver, bound to a specific deployed contract. +func NewResolverTransactor(address common.Address, transactor bind.ContractTransactor) (*ResolverTransactor, error) { + contract, err := bindResolver(address, nil, transactor) + if err != nil { + return nil, err + } + return &ResolverTransactor{contract: contract}, nil +} + +// bindResolver binds a generic wrapper to an already deployed contract. +func bindResolver(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ResolverABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.ResolverCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.ResolverTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Resolver *ResolverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Resolver.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Resolver *ResolverTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Resolver *ResolverTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Resolver.Contract.contract.Transact(opts, method, params...) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCaller) Addr(opts *bind.CallOpts, node [32]byte) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "addr", node) + return *ret0, err +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Addr is a free data retrieval call binding the contract method 0x3b3b57de. +// +// Solidity: function addr(node bytes32) constant returns(ret address) +func (_Resolver *ResolverCallerSession) Addr(node [32]byte) (common.Address, error) { + return _Resolver.Contract.Addr(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCaller) Content(opts *bind.CallOpts, node [32]byte) ([32]byte, error) { + var ( + ret0 = new([32]byte) + ) + out := ret0 + err := _Resolver.contract.Call(opts, out, "content", node) + return *ret0, err +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Content is a free data retrieval call binding the contract method 0x2dff6941. +// +// Solidity: function content(node bytes32) constant returns(ret bytes32) +func (_Resolver *ResolverCallerSession) Content(node [32]byte) ([32]byte, error) { + return _Resolver.Contract.Content(&_Resolver.CallOpts, node) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.contract.Transact(opts, "has", node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} + +// Has is a paid mutator transaction binding the contract method 0x41b9dc2b. +// +// Solidity: function has(node bytes32, kind bytes32) returns(bool) +func (_Resolver *ResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) { + return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol new file mode 100644 index 0000000..114cd73 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/contract/ens.sol @@ -0,0 +1,226 @@ +// Ethereum Name Service contracts by Nick Johnson +// +// To the extent possible under law, the person who associated CC0 with +// ENS contracts has waived all copyright and related or neighboring rights +// to ENS. +// +// You should have received a copy of the CC0 legalcode along with this +// work. If not, see . + +/** + * The ENS registry contract. + */ +contract ENS { + struct Record { + address owner; + address resolver; + } + + mapping(bytes32=>Record) records; + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the owner of a node changes the resolver for that node. + event NewResolver(bytes32 indexed node, address resolver); + + // Permits modifications only by the owner of the specified node. + modifier only_owner(bytes32 node) { + if(records[node].owner != msg.sender) throw; + _ + } + + /** + * Constructs a new ENS registrar, with the provided address as the owner of the root node. + */ + function ENS(address owner) { + records[0].owner = owner; + } + + /** + * Returns the address that owns the specified node. + */ + function owner(bytes32 node) constant returns (address) { + return records[node].owner; + } + + /** + * Returns the address of the resolver for the specified node. + */ + function resolver(bytes32 node) constant returns (address) { + return records[node].resolver; + } + + /** + * Transfers ownership of a node to a new address. May only be called by the current + * owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) only_owner(node) { + Transfer(node, owner); + records[node].owner = owner; + } + + /** + * Transfers ownership of a subnode sha3(node, label) to a new address. May only be + * called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) { + var subnode = sha3(node, label); + NewOwner(node, label, owner); + records[subnode].owner = owner; + } + + /** + * Sets the resolver address for the specified node. + * @param node The node to update. + * @param resolver The address of the resolver. + */ + function setResolver(bytes32 node, address resolver) only_owner(node) { + NewResolver(node, resolver); + records[node].resolver = resolver; + } +} + +/** + * A registrar that allocates subdomains to the first person to claim them. It also deploys + * a simple resolver contract and sets that as the default resolver on new names for + * convenience. + */ +contract FIFSRegistrar { + ENS ens; + PublicResolver defaultResolver; + bytes32 rootNode; + + /** + * Constructor. + * @param ensAddr The address of the ENS registry. + * @param node The node that this registrar administers. + */ + function FIFSRegistrar(address ensAddr, bytes32 node) { + ens = ENS(ensAddr); + defaultResolver = new PublicResolver(ensAddr); + rootNode = node; + } + + /** + * Register a name, or change the owner of an existing registration. + * @param subnode The hash of the label to register. + * @param owner The address of the new owner. + */ + function register(bytes32 subnode, address owner) { + var node = sha3(rootNode, subnode); + var currentOwner = ens.owner(node); + if(currentOwner != 0 && currentOwner != msg.sender) + throw; + + // Temporarily set ourselves as the owner + ens.setSubnodeOwner(rootNode, subnode, this); + // Set up the default resolver + ens.setResolver(node, defaultResolver); + // Set the owner to the real owner + ens.setOwner(node, owner); + } +} + +contract Resolver { + event AddrChanged(bytes32 indexed node, address a); + event ContentChanged(bytes32 indexed node, bytes32 hash); + + function has(bytes32 node, bytes32 kind) returns (bool); + function addr(bytes32 node) constant returns (address ret); + function content(bytes32 node) constant returns (bytes32 ret); +} + +/** + * A simple resolver anyone can use; only allows the owner of a node to set its + * address. + */ +contract PublicResolver is Resolver { + ENS ens; + mapping(bytes32=>address) addresses; + mapping(bytes32=>bytes32) contents; + + modifier only_owner(bytes32 node) { + if(ens.owner(node) != msg.sender) throw; + _ + } + + /** + * Constructor. + * @param ensAddr The ENS registrar contract. + */ + function PublicResolver(address ensAddr) { + ens = ENS(ensAddr); + } + + /** + * Fallback function. + */ + function() { + throw; + } + + /** + * Returns true if the specified node has the specified record type. + * @param node The ENS node to query. + * @param kind The record type name, as specified in EIP137. + * @return True if this resolver has a record of the provided type on the + * provided node. + */ + function has(bytes32 node, bytes32 kind) returns (bool) { + return (kind == "addr" && addresses[node] != 0) || + (kind == "content" && contents[node] != 0); + } + + /** + * Returns the address associated with an ENS node. + * @param node The ENS node to query. + * @return The associated address. + */ + function addr(bytes32 node) constant returns (address ret) { + ret = addresses[node]; + if(ret == 0) + throw; + } + + /** + * Returns the content hash associated with an ENS node. + * @param node The ENS node to query. + * @return The associated content hash. + */ + function content(bytes32 node) constant returns (bytes32 ret) { + ret = contents[node]; + if(ret == 0) + throw; + } + + /** + * Sets the address associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param addr The address to set. + */ + function setAddr(bytes32 node, address addr) only_owner(node) { + addresses[node] = addr; + AddrChanged(node, addr); + } + + /** + * Sets the content hash associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The content hash to set. + */ + function setContent(bytes32 node, bytes32 hash) only_owner(node) { + contents[node] = hash; + ContentChanged(node, hash); + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go new file mode 100644 index 0000000..7806742 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens.go @@ -0,0 +1,178 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ens + +//go:generate abigen --sol contract/ens.sol --pkg contract --out contract/ens.go + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/ens/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// swarm domain name registry and resolver +type ENS struct { + *contract.ENSSession + contractBackend bind.ContractBackend +} + +// NewENS creates a struct exposing convenient high-level operations for interacting with +// the Ethereum Name Service. +func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) (*ENS, error) { + ens, err := contract.NewENS(contractAddr, contractBackend) + if err != nil { + return nil, err + } + + return &ENS{ + &contract.ENSSession{ + Contract: ens, + TransactOpts: *transactOpts, + }, + contractBackend, + }, nil +} + +// DeployENS deploys an instance of the ENS nameservice, with a 'first in first served' root registrar. +func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (*ENS, error) { + // Deploy the ENS registry + ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend, transactOpts.From) + if err != nil { + return nil, err + } + + ens, err := NewENS(transactOpts, ensAddr, contractBackend) + if err != nil { + return nil, err + } + + // Deploy the registrar + regAddr, _, _, err := contract.DeployFIFSRegistrar(transactOpts, contractBackend, ensAddr, [32]byte{}) + if err != nil { + return nil, err + } + + // Set the registrar as owner of the ENS root + _, err = ens.SetOwner([32]byte{}, regAddr) + if err != nil { + return nil, err + } + + return ens, nil +} + +func ensParentNode(name string) (common.Hash, common.Hash) { + parts := strings.SplitN(name, ".", 2) + label := crypto.Keccak256Hash([]byte(parts[0])) + if len(parts) == 1 { + return [32]byte{}, label + } else { + parentNode, parentLabel := ensParentNode(parts[1]) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]), label + } +} + +func ensNode(name string) common.Hash { + parentNode, parentLabel := ensParentNode(name) + return crypto.Keccak256Hash(parentNode[:], parentLabel[:]) +} + +func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) { + resolverAddr, err := self.Resolver(node) + if err != nil { + return nil, err + } + + resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.PublicResolverSession{ + Contract: resolver, + TransactOpts: self.TransactOpts, + }, nil +} + +func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) { + registrarAddr, err := self.Owner(node) + if err != nil { + return nil, err + } + + registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend) + if err != nil { + return nil, err + } + + return &contract.FIFSRegistrarSession{ + Contract: registrar, + TransactOpts: self.TransactOpts, + }, nil +} + +// Resolve is a non-transactional call that returns the content hash associated with a name. +func (self *ENS) Resolve(name string) (common.Hash, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return common.Hash{}, err + } + + ret, err := resolver.Content(node) + if err != nil { + return common.Hash{}, err + } + + return common.BytesToHash(ret[:]), nil +} + +// Register registers a new domain name for the caller, making them the owner of the new name. +// Only works if the registrar for the parent domain implements the FIFS registrar protocol. +func (self *ENS) Register(name string) (*types.Transaction, error) { + parentNode, label := ensParentNode(name) + + registrar, err := self.getRegistrar(parentNode) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return registrar.Contract.Register(&opts, label, self.TransactOpts.From) +} + +// SetContentHash sets the content hash associated with a name. Only works if the caller +// owns the name, and the associated resolver implements a `setContent` function. +func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { + node := ensNode(name) + + resolver, err := self.getResolver(node) + if err != nil { + return nil, err + } + + opts := self.TransactOpts + opts.GasLimit = big.NewInt(200000) + return resolver.Contract.SetContent(&opts, node, hash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens_test.go b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens_test.go new file mode 100644 index 0000000..373ce2e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/ens/ens_test.go @@ -0,0 +1,67 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ens + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + name = "my name on ENS" + hash = crypto.Keccak256Hash([]byte("my content")) + addr = crypto.PubkeyToAddress(key.PublicKey) +) + +func TestENS(t *testing.T) { + contractBackend := backends.NewSimulatedBackend(core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000000)}) + transactOpts := bind.NewKeyedTransactor(key) + // Workaround for bug estimating gas in the call to Register + transactOpts.GasLimit = big.NewInt(1000000) + + ens, err := DeployENS(transactOpts, contractBackend) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + contractBackend.Commit() + + _, err = ens.Register(name) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + contractBackend.Commit() + + _, err = ens.SetContentHash(name, hash) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + contractBackend.Commit() + + vhost, err := ens.Resolve(name) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if vhost != hash { + t.Fatalf("resolve error, expected %v, got %v", hash.Hex(), vhost.Hex()) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go new file mode 100644 index 0000000..6a0b099 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.go @@ -0,0 +1,432 @@ +// This file is an automatically generated Go binding. Do not modify as any +// change will likely be lost upon the next re-generation! + +package release + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// ReleaseOracleABI is the input ABI used to generate the binding from. +const ReleaseOracleABI = `[{"constant":true,"inputs":[],"name":"proposedVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"pass","type":"address[]"},{"name":"fail","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"signers","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"demote","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"authVotes","outputs":[{"name":"promote","type":"address[]"},{"name":"demote","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[],"name":"currentVersion","outputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"},{"name":"time","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"nuke","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"authProposals","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"promote","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"major","type":"uint32"},{"name":"minor","type":"uint32"},{"name":"patch","type":"uint32"},{"name":"commit","type":"bytes20"}],"name":"release","outputs":[],"type":"function"},{"inputs":[{"name":"signers","type":"address[]"}],"type":"constructor"}]` + +// ReleaseOracleBin is the compiled bytecode used for deploying new contracts. +const ReleaseOracleBin = `0x606060405260405161135338038061135383398101604052805101600081516000141561008457600160a060020a0333168152602081905260408120805460ff19166001908117909155805480820180835582818380158290116100ff576000838152602090206100ff9181019083015b8082111561012f5760008155600101610070565b5060005b815181101561011f5760016000600050600084848151811015610002576020908102909101810151600160a060020a03168252810191909152604001600020805460ff1916909117905560018054808201808355828183801582901161013357600083815260209020610133918101908301610070565b5050506000928352506020909120018054600160a060020a031916331790555b50506111df806101746000396000f35b5090565b50505091909060005260206000209001600084848151811015610002575050506020838102850101518154600160a060020a0319161790555060010161008856606060405236156100775760e060020a600035046326db7648811461007957806346f0975a1461019e5780635c3d005d1461020a57806364ed31fe146102935780639d888e861461038d578063bc8fbbf8146103b2578063bf8ecf9c146103fc578063d0e0813a14610468578063d67cbec914610479575b005b610496604080516020818101835260008083528351808301855281815260045460068054875181870281018701909852808852939687968796879691959463ffffffff818116956401000000008304821695604060020a840490921694606060020a938490049093029390926007929184919083018282801561012657602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610107575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561018357602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610164575b50505050509050955095509550955095509550909192939495565b6040805160208181018352600082526001805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020905b8154600160a060020a03168152600191909101906020018083116101e0575b505050505090505b90565b61007760043561066d8160005b600160a060020a033316600090815260208190526040812054819060ff161561070057600160a060020a038416815260026020526040812091505b8154811015610706578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a0316141561075157610700565b6105a26004356040805160208181018352600080835283518083018552818152600160a060020a038616825260028352908490208054855181850281018501909652808652939491939092600184019291849183018282801561032057602002820191906000526020600020905b8154600160a060020a0316815260019190910190602001808311610301575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561037d57602002820191906000526020600020905b8154600160a060020a031681526001919091019060200180831161035e575b5050505050905091509150915091565b61062760006000600060006000600060086000508054905060001415610670576106f1565b6100776106f96000808080805b600160a060020a033316600090815260208190526040812054819060ff16156111b657821580156103f257506006546000145b15610c2e576111b6565b6040805160208181018352600082526003805484518184028101840190955280855261055894928301828280156101ff57602002820191906000526020600020908154600160a060020a03168152600191909101906020018083116101e0575b50505050509050610207565b61007760043561066d816001610217565b6100776004356024356044356064356107008484848460016103bf565b604051808763ffffffff1681526020018663ffffffff1681526020018563ffffffff168152602001846bffffffffffffffffffffffff1916815260200180602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019850505050505050505060405180910390f35b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019250505060405180910390f35b6040518080602001806020018381038352858181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018381038252848181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f15090500194505050505060405180910390f35b6040805163ffffffff9687168152948616602086015292909416838301526bffffffffffffffffffffffff19166060830152608082019290925290519081900360a00190f35b50565b600880546000198101908110156100025760009182526004027ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190508054600182015463ffffffff8281169950640100000000830481169850604060020a8304169650606060020a91829004909102945067ffffffffffffffff16925090505b509091929394565b565b505050505b50505050565b5060005b60018201548110156107595733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a031614156107a357610700565b600101610252565b8154600014801561076e575060018201546000145b156107cb57600380546001810180835582818380158290116107ab578183600052602060002091820191016107ab9190610851565b60010161070a565b5050506000928352506020909120018054600160a060020a031916851790555b821561086957815460018101808455839190828183801582901161089e5760008381526020902061089e918101908301610851565b5050506000928352506020909120018054600160a060020a031916851790555b600160a060020a038416600090815260026020908152604082208054838255818452918320909291610b2f91908101905b808211156108655760008155600101610851565b5090565b816001016000508054806001018281815481835581811511610950578183600052602060002091820191016109509190610851565b5050506000928352506020909120018054600160a060020a031916331790556001548254600290910490116108d257610700565b8280156108f85750600160a060020a03841660009081526020819052604090205460ff16155b1561098757600160a060020a0384166000908152602081905260409020805460ff1916600190811790915580548082018083558281838015829011610800578183600052602060002091820191016108009190610851565b5050506000928352506020909120018054600160a060020a031916331790556001805490830154600290910490116108d257610700565b821580156109ad5750600160a060020a03841660009081526020819052604090205460ff165b156108205750600160a060020a0383166000908152602081905260408120805460ff191690555b6001548110156108205783600160a060020a0316600160005082815481101561000257600091825260209091200154600160a060020a03161415610aa357600180546000198101908110156100025760206000908120929052600180549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a030219169083021790555060016000508054809190600190039090815481835581811511610aab57600083815260209020610aab918101908301610851565b6001016109d4565b5050600060048181556005805467ffffffffffffffff19169055600680548382558184529194509192508290610b05907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5060018201805460008083559182526020909120610b2591810190610851565b5050505050610820565b5060018201805460008083559182526020909120610b4f91810190610851565b506000925050505b6003548110156107005783600160a060020a0316600360005082815481101561000257600091825260209091200154600160a060020a03161415610c2657600380546000198101908110156100025760206000908120929052600380549290910154600160a060020a031691839081101561000257906000526020600020900160006101000a815481600160a060020a0302191690830217905550600360005080548091906001900390908154818355818115116106fb576000838152602090206106fb918101908301610851565b600101610b57565b60065460001415610c8c576004805463ffffffff1916881767ffffffff0000000019166401000000008802176bffffffff00000000000000001916604060020a8702176bffffffffffffffffffffffff16606060020a808704021790555b828015610d08575060045463ffffffff8881169116141580610cc1575060045463ffffffff8781166401000000009092041614155b80610cde575060045463ffffffff868116604060020a9092041614155b80610d085750600454606060020a90819004026bffffffffffffffffffffffff1990811690851614155b15610d12576111b6565b506006905060005b8154811015610d5b578154600160a060020a033316908390839081101561000257600091825260209091200154600160a060020a03161415610da6576111b6565b5060005b6001820154811015610dae5733600160a060020a03168260010160005082815481101561000257600091825260209091200154600160a060020a03161415610de3576111b6565b600101610d1a565b8215610deb578154600181018084558391908281838015829011610e2057600083815260209020610e20918101908301610851565b600101610d5f565b816001016000508054806001018281815481835581811511610ea357818360005260206000209182019101610ea39190610851565b5050506000928352506020909120018054600160a060020a03191633179055600154825460029091049011610e54576111b6565b8215610eda576005805467ffffffffffffffff19164217905560088054600181018083558281838015829011610f2f57600402816004028360005260206000209182019101610f2f9190611048565b5050506000928352506020909120018054600160a060020a03191633179055600180549083015460029091049011610e54576111b6565b600060048181556005805467ffffffffffffffff191690556006805483825581845291929182906111bf907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b5050509190906000526020600020906004020160005060048054825463ffffffff191663ffffffff9182161780845582546401000000009081900483160267ffffffff000000001991909116178084558254604060020a908190049092169091026bffffffff00000000000000001991909116178083558154606060020a908190048102819004026bffffffffffffffffffffffff9190911617825560055460018301805467ffffffffffffffff191667ffffffffffffffff9092169190911790556006805460028401805482825560008281526020902094959491928392918201918582156110a75760005260206000209182015b828111156110a7578254825591600101919060010190611025565b505050506004015b8082111561086557600080825560018201805467ffffffffffffffff191690556002820180548282558183526020832083916110879190810190610851565b506001820180546000808355918252602090912061104091810190610851565b506110cd9291505b80821115610865578054600160a060020a03191681556001016110af565b505060018181018054918401805480835560008381526020902092938301929091821561111b5760005260206000209182015b8281111561111b578254825591600101919060010190611100565b506111279291506110af565b5050600060048181556005805467ffffffffffffffff191690556006805483825581845291975091955090935084925061118691507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f90810190610851565b50600182018054600080835591825260209091206111a691810190610851565b50505050506111b6565b50505050505b50505050505050565b50600182018054600080835591825260209091206111b09181019061085156` + +// DeployReleaseOracle deploys a new Ethereum contract, binding an instance of ReleaseOracle to it. +func DeployReleaseOracle(auth *bind.TransactOpts, backend bind.ContractBackend, signers []common.Address) (common.Address, *types.Transaction, *ReleaseOracle, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ReleaseOracleBin), backend, signers) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// ReleaseOracle is an auto generated Go binding around an Ethereum contract. +type ReleaseOracle struct { + ReleaseOracleCaller // Read-only binding to the contract + ReleaseOracleTransactor // Write-only binding to the contract +} + +// ReleaseOracleCaller is an auto generated read-only Go binding around an Ethereum contract. +type ReleaseOracleCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ReleaseOracleSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ReleaseOracleSession struct { + Contract *ReleaseOracle // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ReleaseOracleCallerSession struct { + Contract *ReleaseOracleCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ReleaseOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ReleaseOracleTransactorSession struct { + Contract *ReleaseOracleTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ReleaseOracleRaw is an auto generated low-level Go binding around an Ethereum contract. +type ReleaseOracleRaw struct { + Contract *ReleaseOracle // Generic contract binding to access the raw methods on +} + +// ReleaseOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ReleaseOracleCallerRaw struct { + Contract *ReleaseOracleCaller // Generic read-only contract binding to access the raw methods on +} + +// ReleaseOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ReleaseOracleTransactorRaw struct { + Contract *ReleaseOracleTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewReleaseOracle creates a new instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracle(address common.Address, backend bind.ContractBackend) (*ReleaseOracle, error) { + contract, err := bindReleaseOracle(address, backend, backend) + if err != nil { + return nil, err + } + return &ReleaseOracle{ReleaseOracleCaller: ReleaseOracleCaller{contract: contract}, ReleaseOracleTransactor: ReleaseOracleTransactor{contract: contract}}, nil +} + +// NewReleaseOracleCaller creates a new read-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleCaller(address common.Address, caller bind.ContractCaller) (*ReleaseOracleCaller, error) { + contract, err := bindReleaseOracle(address, caller, nil) + if err != nil { + return nil, err + } + return &ReleaseOracleCaller{contract: contract}, nil +} + +// NewReleaseOracleTransactor creates a new write-only instance of ReleaseOracle, bound to a specific deployed contract. +func NewReleaseOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*ReleaseOracleTransactor, error) { + contract, err := bindReleaseOracle(address, nil, transactor) + if err != nil { + return nil, err + } + return &ReleaseOracleTransactor{contract: contract}, nil +} + +// bindReleaseOracle binds a generic wrapper to an already deployed contract. +func bindReleaseOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ReleaseOracleABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.ReleaseOracleCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.ReleaseOracleTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ReleaseOracle *ReleaseOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ReleaseOracle.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ReleaseOracle *ReleaseOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ReleaseOracle.Contract.contract.Transact(opts, method, params...) +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthProposals(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "authProposals") + return *ret0, err +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthProposals is a free data retrieval call binding the contract method 0xbf8ecf9c. +// +// Solidity: function authProposals() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthProposals() ([]common.Address, error) { + return _ReleaseOracle.Contract.AuthProposals(&_ReleaseOracle.CallOpts) +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCaller) AuthVotes(opts *bind.CallOpts, user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + ret := new(struct { + Promote []common.Address + Demote []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "authVotes", user) + return *ret, err +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// AuthVotes is a free data retrieval call binding the contract method 0x64ed31fe. +// +// Solidity: function authVotes(user address) constant returns(promote address[], demote address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) AuthVotes(user common.Address) (struct { + Promote []common.Address + Demote []common.Address +}, error) { + return _ReleaseOracle.Contract.AuthVotes(&_ReleaseOracle.CallOpts, user) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCaller) CurrentVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "currentVersion") + return *ret, err +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// CurrentVersion is a free data retrieval call binding the contract method 0x9d888e86. +// +// Solidity: function currentVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, time uint256) +func (_ReleaseOracle *ReleaseOracleCallerSession) CurrentVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Time *big.Int +}, error) { + return _ReleaseOracle.Contract.CurrentVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCaller) ProposedVersion(opts *bind.CallOpts) (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + ret := new(struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address + }) + out := ret + err := _ReleaseOracle.contract.Call(opts, out, "proposedVersion") + return *ret, err +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// ProposedVersion is a free data retrieval call binding the contract method 0x26db7648. +// +// Solidity: function proposedVersion() constant returns(major uint32, minor uint32, patch uint32, commit bytes20, pass address[], fail address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) ProposedVersion() (struct { + Major uint32 + Minor uint32 + Patch uint32 + Commit [20]byte + Pass []common.Address + Fail []common.Address +}, error) { + return _ReleaseOracle.Contract.ProposedVersion(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCaller) Signers(opts *bind.CallOpts) ([]common.Address, error) { + var ( + ret0 = new([]common.Address) + ) + out := ret0 + err := _ReleaseOracle.contract.Call(opts, out, "signers") + return *ret0, err +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Signers is a free data retrieval call binding the contract method 0x46f0975a. +// +// Solidity: function signers() constant returns(address[]) +func (_ReleaseOracle *ReleaseOracleCallerSession) Signers() ([]common.Address, error) { + return _ReleaseOracle.Contract.Signers(&_ReleaseOracle.CallOpts) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Demote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "demote", user) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Demote is a paid mutator transaction binding the contract method 0x5c3d005d. +// +// Solidity: function demote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Demote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Demote(&_ReleaseOracle.TransactOpts, user) +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Nuke(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "nuke") +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Nuke is a paid mutator transaction binding the contract method 0xbc8fbbf8. +// +// Solidity: function nuke() returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Nuke() (*types.Transaction, error) { + return _ReleaseOracle.Contract.Nuke(&_ReleaseOracle.TransactOpts) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Promote(opts *bind.TransactOpts, user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "promote", user) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Promote is a paid mutator transaction binding the contract method 0xd0e0813a. +// +// Solidity: function promote(user address) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Promote(user common.Address) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Promote(&_ReleaseOracle.TransactOpts, user) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactor) Release(opts *bind.TransactOpts, major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.contract.Transact(opts, "release", major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} + +// Release is a paid mutator transaction binding the contract method 0xd67cbec9. +// +// Solidity: function release(major uint32, minor uint32, patch uint32, commit bytes20) returns() +func (_ReleaseOracle *ReleaseOracleTransactorSession) Release(major uint32, minor uint32, patch uint32, commit [20]byte) (*types.Transaction, error) { + return _ReleaseOracle.Contract.Release(&_ReleaseOracle.TransactOpts, major, minor, patch, commit) +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol new file mode 100644 index 0000000..554cf72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract.sol @@ -0,0 +1,249 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// ReleaseOracle is an Ethereum contract to store the current and previous +// versions of the go-ethereum implementation. Its goal is to allow Geth to +// check for new releases automatically without the need to consult a central +// repository. +// +// The contract takes a vote based approach on both assigning authorised signers +// as well as signing off on new Geth releases. +// +// Note, when a signer is demoted, the currently pending release is auto-nuked. +// The reason is to prevent suprises where a demotion actually tilts the votes +// in favor of one voter party and pushing out a new release as a consequence of +// a simple demotion. +contract ReleaseOracle { + // Votes is an internal data structure to count votes on a specific proposal + struct Votes { + address[] pass; // List of signers voting to pass a proposal + address[] fail; // List of signers voting to fail a proposal + } + + // Version is the version details of a particular Geth release + struct Version { + uint32 major; // Major version component of the release + uint32 minor; // Minor version component of the release + uint32 patch; // Patch version component of the release + bytes20 commit; // Git SHA1 commit hash of the release + + uint64 time; // Timestamp of the release approval + Votes votes; // Votes that passed this release + } + + // Oracle authorization details + mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract + address[] voters; // List of addresses currently accepted as signers + + // Various proposals being voted on + mapping(address => Votes) authProps; // Currently running user authorization proposals + address[] authPend; // List of addresses being voted on (map indexes) + + Version verProp; // Currently proposed release being voted on + Version[] releases; // All the positively voted releases + + // isSigner is a modifier to authorize contract transactions. + modifier isSigner() { + if (authorised[msg.sender]) { + _ + } + } + + // Constructor to assign the initial set of signers. + function ReleaseOracle(address[] signers) { + // If no signers were specified, assign the creator as the sole signer + if (signers.length == 0) { + authorised[msg.sender] = true; + voters.push(msg.sender); + return; + } + // Otherwise assign the individual signers one by one + for (uint i = 0; i < signers.length; i++) { + authorised[signers[i]] = true; + voters.push(signers[i]); + } + } + + // signers is an accessor method to retrieve all te signers (public accessor + // generates an indexed one, not a retrieve-all version). + function signers() constant returns(address[]) { + return voters; + } + + // authProposals retrieves the list of addresses that authorization proposals + // are currently being voted on. + function authProposals() constant returns(address[]) { + return authPend; + } + + // authVotes retrieves the current authorization votes for a particular user + // to promote him into the list of signers, or demote him from there. + function authVotes(address user) constant returns(address[] promote, address[] demote) { + return (authProps[user].pass, authProps[user].fail); + } + + // currentVersion retrieves the semantic version, commit hash and release time + // of the currently votec active release. + function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { + if (releases.length == 0) { + return (0, 0, 0, 0, 0); + } + var release = releases[releases.length - 1]; + + return (release.major, release.minor, release.patch, release.commit, release.time); + } + + // proposedVersion retrieves the semantic version, commit hash and the current + // votes for the next proposed release. + function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { + return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); + } + + // promote pitches in on a voting campaign to promote a new user to a signer + // position. + function promote(address user) { + updateSigner(user, true); + } + + // demote pitches in on a voting campaign to demote an authorised user from + // its signer position. + function demote(address user) { + updateSigner(user, false); + } + + // release votes for a particular version to be included as the next release. + function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { + updateRelease(major, minor, patch, commit, true); + } + + // nuke votes for the currently proposed version to not be included as the next + // release. Nuking doesn't require a specific version number for simplicity. + function nuke() { + updateRelease(0, 0, 0, 0, false); + } + + // updateSigner marks a vote for changing the status of an Ethereum user, either + // for or against the user being an authorised signer. + function updateSigner(address user, bool authorize) internal isSigner { + // Gather the current votes and ensure we don't double vote + Votes votes = authProps[user]; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // If no authorization proposal is open, add the user to the index for later lookups + if (votes.pass.length == 0 && votes.fail.length == 0) { + authPend.push(user); + } + // Cast the vote and return if the proposal cannot be resolved yet + if (authorize) { + votes.pass.push(msg.sender); + if (votes.pass.length <= voters.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= voters.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (authorize && !authorised[user]) { + authorised[user] = true; + voters.push(user); + } else if (!authorize && authorised[user]) { + authorised[user] = false; + + for (i = 0; i < voters.length; i++) { + if (voters[i] == user) { + voters[i] = voters[voters.length - 1]; + voters.length--; + + delete verProp; // Nuke any version proposal (no surprise releases!) + break; + } + } + } + // Finally delete the resolved proposal, index and garbage collect + delete authProps[user]; + + for (i = 0; i < authPend.length; i++) { + if (authPend[i] == user) { + authPend[i] = authPend[authPend.length - 1]; + authPend.length--; + break; + } + } + } + + // updateRelease votes for a particular version to be included as the next release, + // or for the currently proposed release to be nuked out. + function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner { + // Skip nuke votes if no proposal is pending + if (!release && verProp.votes.pass.length == 0) { + return; + } + // Mark a new release if no proposal is pending + if (verProp.votes.pass.length == 0) { + verProp.major = major; + verProp.minor = minor; + verProp.patch = patch; + verProp.commit = commit; + } + // Make sure positive votes match the current proposal + if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) { + return; + } + // Gather the current votes and ensure we don't double vote + Votes votes = verProp.votes; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // Cast the vote and return if the proposal cannot be resolved yet + if (release) { + votes.pass.push(msg.sender); + if (votes.pass.length <= voters.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= voters.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (release) { + verProp.time = uint64(now); + releases.push(verProp); + delete verProp; + } else { + delete verProp; + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/release/contract_test.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract_test.go new file mode 100644 index 0000000..11a0399 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/contract_test.go @@ -0,0 +1,374 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package release + +import ( + "crypto/ecdsa" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" +) + +// setupReleaseTest creates a blockchain simulator and deploys a version oracle +// contract for testing. +func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.PrivateKey, *ReleaseOracle, *backends.SimulatedBackend) { + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + + accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}} + for _, key := range prefund { + accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)}) + } + sim := backends.NewSimulatedBackend(accounts...) + + // Deploy a version oracle contract, commit and return + _, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From}) + if err != nil { + t.Fatalf("Failed to deploy version contract: %v", err) + } + sim.Commit() + + return key, oracle, sim +} + +// Tests that the version contract can be deployed and the creator is assigned +// the sole authorized signer. +func TestContractCreation(t *testing.T) { + key, oracle, _ := setupReleaseTest(t) + + owner := crypto.PubkeyToAddress(key.PublicKey) + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Failed to retrieve list of signers: %v", err) + } + if len(signers) != 1 || signers[0] != owner { + t.Fatalf("Initial signer mismatch: have %v, want %v", signers, owner) + } +} + +// Tests that subsequent signers can be promoted, each requiring half plus one +// votes for it to pass through. +func TestSignerPromotion(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Gradually promote the keys, until all are authorized + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + // Check that no votes are accepted from the not yet authed user + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil { + t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err) + } + sim.Commit() + + pend, err := oracle.AuthProposals(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err) + } + if len(pend) != 0 { + t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend)) + } + // Promote with half - 1 voters and check that the user's not yet authorized + for j := 0; j < i/2; j++ { + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) + } + if len(signers) != i { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i) + } + // Promote with the last one needed to pass the promotion + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion completion attempt: %v", i, err) + } + sim.Commit() + + signers, err = oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err) + } + if len(signers) != i+1 { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i+1) + } + } +} + +// Tests that subsequent signers can be demoted, each requiring half plus one +// votes for it to pass through. +func TestSignerDemotion(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers and verify cardinality + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Failed to retrieve list of signers: %v", err) + } + if len(signers) != len(keys) { + t.Fatalf("Signer count mismatch: have %v, want %v", len(signers), len(keys)) + } + // Gradually demote users until we run out of signers + for i := len(keys) - 1; i >= 0; i-- { + // Demote with half - 1 voters and check that the user's not yet dropped + for j := 0; j < (i+1)/2; j++ { + if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion attempt: %v", len(keys)-i, err) + } + } + sim.Commit() + + signers, err := oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err) + } + if len(signers) != i+1 { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i+1) + } + // Demote with the last one needed to pass the demotion + if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[(i+1)/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion completion attempt: %v", i, err) + } + sim.Commit() + + signers, err = oracle.Signers(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err) + } + if len(signers) != i { + t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i) + } + // Check that no votes are accepted from the already demoted users + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil { + t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err) + } + sim.Commit() + + pend, err := oracle.AuthProposals(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err) + } + if len(pend) != 0 { + t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend)) + } + } +} + +// Tests that new versions can be released, honouring both voting rights as well +// as the minimum required vote count. +func TestVersionRelease(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Track the "current release" + var ( + verMajor = uint32(0) + verMinor = uint32(0) + verPatch = uint32(0) + verCommit = [20]byte{} + ) + // Gradually push releases, always requiring more signers than previously + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + // Check that no votes are accepted from the not yet authed user + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[i]), 0, 0, 0, [20]byte{0}); err != nil { + t.Fatalf("Iter #%d: failed invalid release attempt: %v", i, err) + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != 0 { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want 0", i, len(prop.Pass)) + } + // Authorize the user to make releases + for j := 0; j <= i/2; j++ { + if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + + // Propose release with half voters and check that the release does not yet go through + for j := 0; j < (i+1)/2; j++ { + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { + t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err) + } + } + sim.Commit() + + ver, err := oracle.CurrentVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) + } + if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { + t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit) + } + + // Pass the release and check that it became the next version + verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)} + if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { + t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err) + } + sim.Commit() + + ver, err = oracle.CurrentVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err) + } + if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit { + t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit) + } + } +} + +// Tests that proposed versions can be nuked out of existence. +func TestVersionNuking(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 9) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + // Propose releases with more and more keys, always retaining enough users to nuke the proposals + for i := 1; i < (len(keys)+1)/2; i++ { + // Propose release with an initial set of signers + for j := 0; j < i; j++ { + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil { + t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err) + } + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != i { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want %d", i, len(prop.Pass), i) + } + // Nuke the release with half+1 voters + for j := i; j <= i+(len(keys)+1)/2; j++ { + if _, err := oracle.Nuke(bind.NewKeyedTransactor(keys[j])); err != nil { + t.Fatalf("Iter #%d: failed valid nuke attempt: %v", i, err) + } + } + sim.Commit() + + prop, err = oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err) + } + if len(prop.Pass) != 0 || len(prop.Fail) != 0 { + t.Fatalf("Iter #%d: proposal vote count mismatch: have %d/%d pass/fail, want 0/0", i, len(prop.Pass), len(prop.Fail)) + } + } +} + +// Tests that demoting a signer will auto-nuke the currently pending release. +func TestVersionAutoNuke(t *testing.T) { + // Prefund a few accounts to authorize with and create the oracle + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + key, oracle, sim := setupReleaseTest(t, keys...) + + // Authorize all the keys as valid signers + keys = append([]*ecdsa.PrivateKey{key}, keys...) + for i := 1; i < len(keys); i++ { + for j := 0; j <= i/2; j++ { + if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err) + } + } + sim.Commit() + } + // Make a release proposal and check it's existence + if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{4}); err != nil { + t.Fatalf("Failed valid proposal attempt: %v", err) + } + sim.Commit() + + prop, err := oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Failed to retrieve active proposal: %v", err) + } + if len(prop.Pass) != 1 { + t.Fatalf("Proposal vote count mismatch: have %d, want 1", len(prop.Pass)) + } + // Demote a signer and check release proposal deletion + for i := 0; i <= len(keys)/2; i++ { + if _, err := oracle.Demote(bind.NewKeyedTransactor(keys[i]), crypto.PubkeyToAddress(keys[len(keys)-1].PublicKey)); err != nil { + t.Fatalf("Iter #%d: failed valid demotion attempt: %v", i, err) + } + } + sim.Commit() + + prop, err = oracle.ProposedVersion(nil) + if err != nil { + t.Fatalf("Failed to retrieve active proposal: %v", err) + } + if len(prop.Pass) != 0 { + t.Fatalf("Proposal vote count mismatch: have %d, want 0", len(prop.Pass)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go new file mode 100644 index 0000000..cd79112 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/contracts/release/release.go @@ -0,0 +1,162 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package release contains the node service that tracks client releases. +package release + +//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go + +import ( + "fmt" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Interval to check for new releases +const releaseRecheckInterval = time.Hour + +// Config contains the configurations of the release service. +type Config struct { + Oracle common.Address // Ethereum address of the release oracle + Major uint32 // Major version component of the release + Minor uint32 // Minor version component of the release + Patch uint32 // Patch version component of the release + Commit [20]byte // Git SHA1 commit hash of the release +} + +// ReleaseService is a node service that periodically checks the blockchain for +// newly released versions of the client being run and issues a warning to the +// user about it. +type ReleaseService struct { + config Config // Current version to check releases against + oracle *ReleaseOracle // Native binding to the release oracle contract + quit chan chan error // Quit channel to terminate the version checker +} + +// NewReleaseService creates a new service to periodically check for new client +// releases and notify the user of such. +func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { + // Retrieve the Ethereum service dependency to access the blockchain + var apiBackend ethapi.Backend + var ethereum *eth.Ethereum + if err := ctx.Service(ðereum); err == nil { + apiBackend = ethereum.ApiBackend + } else { + var ethereum *les.LightEthereum + if err := ctx.Service(ðereum); err == nil { + apiBackend = ethereum.ApiBackend + } else { + return nil, err + } + } + // Construct the release service + contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) + if err != nil { + return nil, err + } + return &ReleaseService{ + config: config, + oracle: contract, + quit: make(chan chan error), + }, nil +} + +// Protocols returns an empty list of P2P protocols as the release service does +// not have a networking component. +func (r *ReleaseService) Protocols() []p2p.Protocol { return nil } + +// APIs returns an empty list of RPC descriptors as the release service does not +// expose any functioanlity to the outside world. +func (r *ReleaseService) APIs() []rpc.API { return nil } + +// Start spawns the periodic version checker goroutine +func (r *ReleaseService) Start(server *p2p.Server) error { + go r.checker() + return nil +} + +// Stop terminates all goroutines belonging to the service, blocking until they +// are all terminated. +func (r *ReleaseService) Stop() error { + errc := make(chan error) + r.quit <- errc + return <-errc +} + +// checker runs indefinitely in the background, periodically checking for new +// client releases. +func (r *ReleaseService) checker() { + // Set up the timers to periodically check for releases + timer := time.NewTimer(0) // Immediately fire a version check + defer timer.Stop() + + for { + select { + // If the time arrived, check for a new release + case <-timer.C: + // Rechedule the timer before continuing + timer.Reset(releaseRecheckInterval) + + // Retrieve the current version, and handle missing contracts gracefully + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + opts := &bind.CallOpts{Context: ctx} + version, err := r.oracle.CurrentVersion(opts) + if err != nil { + if err == bind.ErrNoCode { + glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle) + continue + } + glog.V(logger.Error).Infof("Failed to retrieve current release: %v", err) + continue + } + // Version was successfully retrieved, notify if newer than ours + if version.Major > r.config.Major || + (version.Major == r.config.Major && version.Minor > r.config.Minor) || + (version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) { + + warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x", + r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) + howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases") + separator := strings.Repeat("-", len(warning)) + + glog.V(logger.Warn).Info(separator) + glog.V(logger.Warn).Info(warning) + glog.V(logger.Warn).Info(howtofix) + glog.V(logger.Warn).Info(separator) + } else { + glog.V(logger.Debug).Infof("Client v%d.%d.%d-%x seems up to date with upstream v%d.%d.%d-%x", + r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) + } + + // If termination was requested, return + case errc := <-r.quit: + errc <- nil + return + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/.gitignore b/vendor/github.com/ethereum/go-ethereum/core/.gitignore new file mode 100644 index 0000000..f725d58 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/vendor/github.com/ethereum/go-ethereum/core/asm.go b/vendor/github.com/ethereum/go-ethereum/core/asm.go new file mode 100644 index 0000000..b2e47b5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/asm.go @@ -0,0 +1,64 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := vm.OpCode(val) + + asm = append(asm, fmt.Sprintf("%04v: %v", pc, op)) + + switch op { + case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, + vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, + vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, + vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, + vm.PUSH30, vm.PUSH31, vm.PUSH32: + pc.Add(pc, common.Big1) + a := int64(op) - int64(vm.PUSH1) + 1 + if int(pc.Int64()+a) > len(script) { + return + } + + data := script[pc.Int64() : pc.Int64()+a] + if len(data) == 0 { + data = []byte{0} + } + asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data)) + + pc.Add(pc, big.NewInt(a-1)) + } + + pc.Add(pc, common.Big1) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/bench_test.go b/vendor/github.com/ethereum/go-ethereum/core/bench_test.go new file mode 100644 index 0000000..59b5ad7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/bench_test.go @@ -0,0 +1,299 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "crypto/ecdsa" + "io/ioutil" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +func BenchmarkInsertChain_empty_memdb(b *testing.B) { + benchInsertChain(b, false, nil) +} +func BenchmarkInsertChain_empty_diskdb(b *testing.B) { + benchInsertChain(b, true, nil) +} +func BenchmarkInsertChain_valueTx_memdb(b *testing.B) { + benchInsertChain(b, false, genValueTx(0)) +} +func BenchmarkInsertChain_valueTx_diskdb(b *testing.B) { + benchInsertChain(b, true, genValueTx(0)) +} +func BenchmarkInsertChain_valueTx_100kB_memdb(b *testing.B) { + benchInsertChain(b, false, genValueTx(100*1024)) +} +func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) { + benchInsertChain(b, true, genValueTx(100*1024)) +} +func BenchmarkInsertChain_uncles_memdb(b *testing.B) { + benchInsertChain(b, false, genUncles) +} +func BenchmarkInsertChain_uncles_diskdb(b *testing.B) { + benchInsertChain(b, true, genUncles) +} +func BenchmarkInsertChain_ring200_memdb(b *testing.B) { + benchInsertChain(b, false, genTxRing(200)) +} +func BenchmarkInsertChain_ring200_diskdb(b *testing.B) { + benchInsertChain(b, true, genTxRing(200)) +} +func BenchmarkInsertChain_ring1000_memdb(b *testing.B) { + benchInsertChain(b, false, genTxRing(1000)) +} +func BenchmarkInsertChain_ring1000_diskdb(b *testing.B) { + benchInsertChain(b, true, genTxRing(1000)) +} + +var ( + // This is the content of the genesis block used by the benchmarks. + benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey) + benchRootFunds = common.BigPow(2, 100) +) + +// genValueTx returns a block generator that includes a single +// value-transfer transaction with n bytes of extra data in each +// block. +func genValueTx(nbytes int) func(int, *BlockGen) { + return func(i int, gen *BlockGen) { + toaddr := common.Address{} + data := make([]byte, nbytes) + gas := IntrinsicGas(data, false, false) + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) + gen.AddTx(tx) + } +} + +var ( + ringKeys = make([]*ecdsa.PrivateKey, 1000) + ringAddrs = make([]common.Address, len(ringKeys)) + bigTxGas = new(big.Int).SetUint64(params.TxGas) +) + +func init() { + ringKeys[0] = benchRootKey + ringAddrs[0] = benchRootAddr + for i := 1; i < len(ringKeys); i++ { + ringKeys[i], _ = crypto.GenerateKey() + ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey) + } +} + +// genTxRing returns a block generator that sends ether in a ring +// among n accounts. This is creates n entries in the state database +// and fills the blocks with many small transactions. +func genTxRing(naccounts int) func(int, *BlockGen) { + from := 0 + return func(i int, gen *BlockGen) { + gas := CalcGasLimit(gen.PrevBlock(i - 1)) + for { + gas.Sub(gas, bigTxGas) + if gas.Cmp(bigTxGas) < 0 { + break + } + to := (from + 1) % naccounts + tx := types.NewTransaction( + gen.TxNonce(ringAddrs[from]), + ringAddrs[to], + benchRootFunds, + bigTxGas, + nil, + nil, + ) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from]) + gen.AddTx(tx) + from = to + } + } +} + +// genUncles generates blocks with two uncle headers. +func genUncles(i int, gen *BlockGen) { + if i >= 6 { + b2 := gen.PrevBlock(i - 6).Header() + b2.Extra = []byte("foo") + gen.AddUncle(b2) + b3 := gen.PrevBlock(i - 6).Header() + b3.Extra = []byte("bar") + gen.AddUncle(b3) + } +} + +func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { + // Create the database in memory or in a temporary directory. + var db ethdb.Database + if !disk { + db, _ = ethdb.NewMemDatabase() + } else { + dir, err := ioutil.TempDir("", "eth-core-bench") + if err != nil { + b.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(dir) + db, err = ethdb.NewLDBDatabase(dir, 128, 128) + if err != nil { + b.Fatalf("cannot create temporary database: %v", err) + } + defer db.Close() + } + + // Generate a chain of b.N blocks using the supplied block + // generator function. + genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds}) + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen) + + // Time the insertion of the new chain. + // State and blocks are stored in the same DB. + evmux := new(event.TypeMux) + chainman, _ := NewBlockChain(db, ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux, vm.Config{}) + defer chainman.Stop() + b.ReportAllocs() + b.ResetTimer() + if i, err := chainman.InsertChain(chain); err != nil { + b.Fatalf("insert error (block %d): %v\n", i, err) + } +} + +func BenchmarkChainRead_header_10k(b *testing.B) { + benchReadChain(b, false, 10000) +} +func BenchmarkChainRead_full_10k(b *testing.B) { + benchReadChain(b, true, 10000) +} +func BenchmarkChainRead_header_100k(b *testing.B) { + benchReadChain(b, false, 100000) +} +func BenchmarkChainRead_full_100k(b *testing.B) { + benchReadChain(b, true, 100000) +} +func BenchmarkChainRead_header_500k(b *testing.B) { + benchReadChain(b, false, 500000) +} +func BenchmarkChainRead_full_500k(b *testing.B) { + benchReadChain(b, true, 500000) +} +func BenchmarkChainWrite_header_10k(b *testing.B) { + benchWriteChain(b, false, 10000) +} +func BenchmarkChainWrite_full_10k(b *testing.B) { + benchWriteChain(b, true, 10000) +} +func BenchmarkChainWrite_header_100k(b *testing.B) { + benchWriteChain(b, false, 100000) +} +func BenchmarkChainWrite_full_100k(b *testing.B) { + benchWriteChain(b, true, 100000) +} +func BenchmarkChainWrite_header_500k(b *testing.B) { + benchWriteChain(b, false, 500000) +} +func BenchmarkChainWrite_full_500k(b *testing.B) { + benchWriteChain(b, true, 500000) +} + +// makeChainForBench writes a given number of headers or empty blocks/receipts +// into a database. +func makeChainForBench(db ethdb.Database, full bool, count uint64) { + var hash common.Hash + for n := uint64(0); n < count; n++ { + header := &types.Header{ + Coinbase: common.Address{}, + Number: big.NewInt(int64(n)), + ParentHash: hash, + Difficulty: big.NewInt(1), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + } + hash = header.Hash() + WriteHeader(db, header) + WriteCanonicalHash(db, hash, n) + WriteTd(db, hash, n, big.NewInt(int64(n+1))) + if full || n == 0 { + block := types.NewBlockWithHeader(header) + WriteBody(db, hash, n, block.Body()) + WriteBlockReceipts(db, hash, n, nil) + } + } +} + +func benchWriteChain(b *testing.B, full bool, count uint64) { + for i := 0; i < b.N; i++ { + dir, err := ioutil.TempDir("", "eth-chain-bench") + if err != nil { + b.Fatalf("cannot create temporary directory: %v", err) + } + db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + if err != nil { + b.Fatalf("error opening database at %v: %v", dir, err) + } + makeChainForBench(db, full, count) + db.Close() + os.RemoveAll(dir) + } +} + +func benchReadChain(b *testing.B, full bool, count uint64) { + dir, err := ioutil.TempDir("", "eth-chain-bench") + if err != nil { + b.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(dir) + + db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + if err != nil { + b.Fatalf("error opening database at %v: %v", dir, err) + } + makeChainForBench(db, full, count) + db.Close() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + db, err := ethdb.NewLDBDatabase(dir, 128, 1024) + if err != nil { + b.Fatalf("error opening database at %v: %v", dir, err) + } + chain, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + if err != nil { + b.Fatalf("error creating chain: %v", err) + } + + for n := uint64(0); n < count; n++ { + header := chain.GetHeaderByNumber(n) + if full { + hash := header.Hash() + GetBody(db, hash, n) + GetBlockReceipts(db, hash, n) + } + } + + db.Close() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/block_validator.go b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go new file mode 100644 index 0000000..ee524b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/block_validator.go @@ -0,0 +1,384 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "gopkg.in/fatih/set.v0" +) + +var ( + ExpDiffPeriod = big.NewInt(100000) + big10 = big.NewInt(10) + bigMinus99 = big.NewInt(-99) +) + +// BlockValidator is responsible for validating block headers, uncles and +// processed state. +// +// BlockValidator implements Validator. +type BlockValidator struct { + config *params.ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + Pow pow.PoW // Proof of work used for validating +} + +// NewBlockValidator returns a new block validator which is safe for re-use +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { + validator := &BlockValidator{ + config: config, + Pow: pow, + bc: blockchain, + } + return validator +} + +// ValidateBlock validates the given block's header and uncles and verifies the +// the block header's transaction and uncle roots. +// +// ValidateBlock does not validate the header's pow. The pow work validated +// separately so we can process them in parallel. +// +// ValidateBlock also validates and makes sure that any previous state (or present) +// state that might or might not be present is checked to make sure that fast +// sync has done it's job proper. This prevents the block validator from accepting +// false positives where a header is present but the state is not. +func (v *BlockValidator) ValidateBlock(block *types.Block) error { + if v.bc.HasBlock(block.Hash()) { + if _, err := state.New(block.Root(), v.bc.chainDb); err == nil { + return &KnownBlockError{block.Number(), block.Hash()} + } + } + parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return ParentError(block.ParentHash()) + } + if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil { + return ParentError(block.ParentHash()) + } + + header := block.Header() + // validate the block header + if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil { + return err + } + // verify the uncles are correctly rewarded + if err := v.VerifyUncles(block, parent); err != nil { + return err + } + + // Verify UncleHash before running other uncle validations + unclesSha := types.CalcUncleHash(block.Uncles()) + if unclesSha != header.UncleHash { + return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha) + } + + // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) + // can be used by light clients to make sure they've received the correct Txs + txSha := types.DeriveSha(block.Transactions()) + if txSha != header.TxHash { + return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha) + } + + return nil +} + +// ValidateState validates the various changes that happen after a state +// transition, such as amount of used gas, the receipt roots and the state root +// itself. ValidateState returns a database batch if the validation was a success +// otherwise nil and an error is returned. +func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { + header := block.Header() + if block.GasUsed().Cmp(usedGas) != 0 { + return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)) + } + // Validate the received block's bloom with the one derived from the generated receipts. + // For valid blocks this should always validate to true. + rbloom := types.CreateBloom(receipts) + if rbloom != header.Bloom { + return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) + } + // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) + receiptSha := types.DeriveSha(receipts) + if receiptSha != header.ReceiptHash { + return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) + } + // Validate the state root against the received state root and throw + // an error if they don't match. + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) + } + return nil +} + +// VerifyUncles verifies the given block's uncles and applies the Ethereum +// consensus rules to the various block headers included; it will return an +// error if any of the included uncle headers were invalid. It returns an error +// if the validation failed. +func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error { + // validate that there are at most 2 uncles included in this block + if len(block.Uncles()) > 2 { + return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) + } + + uncles := set.New() + ancestors := make(map[common.Hash]*types.Block) + for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) { + ancestors[ancestor.Hash()] = ancestor + // Include ancestors uncles in the uncle set. Uncles must be unique. + for _, uncle := range ancestor.Uncles() { + uncles.Add(uncle.Hash()) + } + } + ancestors[block.Hash()] = block + uncles.Add(block.Hash()) + + for i, uncle := range block.Uncles() { + hash := uncle.Hash() + if uncles.Has(hash) { + // Error not unique + return UncleError("uncle[%d](%x) not unique", i, hash[:4]) + } + uncles.Add(hash) + + if ancestors[hash] != nil { + branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) + for h := range ancestors { + branch += fmt.Sprintf(" O - %x\n |\n", h) + } + glog.Infoln(branch) + return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) + } + + if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { + return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) + } + + if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { + return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) + } + } + + return nil +} + +// ValidateHeader validates the given header and, depending on the pow arg, +// checks the proof of work of the given header. Returns an error if the +// validation failed. +func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { + // Short circuit if the parent is missing. + if parent == nil { + return ParentError(header.ParentHash) + } + // Short circuit if the header's already known or its parent is missing + if v.bc.HasHeader(header.Hash()) { + return nil + } + return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) +} + +// Validates a header. Returns an error if the header is invalid. +// +// See YP section 4.3.4. "Block Header Validity" +func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { + if uint64(len(header.Extra)) > params.MaximumExtraDataSize { + return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) + } + + if uncle { + if header.Time.Cmp(common.MaxBig) == 1 { + return BlockTSTooBigErr + } + } else { + if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { + return BlockFutureErr + } + } + if header.Time.Cmp(parent.Time) != 1 { + return BlockEqualTSErr + } + + expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) + if expd.Cmp(header.Difficulty) != 0 { + return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) + } + + a := new(big.Int).Set(parent.GasLimit) + a = a.Sub(a, header.GasLimit) + a.Abs(a) + b := new(big.Int).Set(parent.GasLimit) + b = b.Div(b, params.GasLimitBoundDivisor) + if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { + return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b) + } + + num := new(big.Int).Set(parent.Number) + num.Sub(header.Number, num) + if num.Cmp(big.NewInt(1)) != 0 { + return BlockNumberErr + } + + if checkPow { + // Verify the nonce of the header. Return an error if it's not valid + if !pow.Verify(types.NewBlockWithHeader(header)) { + return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()} + } + } + // If all checks passed, validate the extra-data field for hard forks + if err := ValidateDAOHeaderExtraData(config, header); err != nil { + return err + } + if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { + if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { + return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) + } + } + return nil +} + +// CalcDifficulty is the difficulty adjustment algorithm. It returns +// the difficulty that a new block should have when created at time +// given the parent block's time and difficulty. +func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { + return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) + } else { + return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) + } +} + +func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki + // algorithm: + // diff = (parent_diff + + // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) + // ) + 2^(periodCount - 2) + + bigTime := new(big.Int).SetUint64(time) + bigParentTime := new(big.Int).SetUint64(parentTime) + + // holds intermediate values to make the algo easier to read & audit + x := new(big.Int) + y := new(big.Int) + + // 1 - (block_timestamp -parent_timestamp) // 10 + x.Sub(bigTime, bigParentTime) + x.Div(x, big10) + x.Sub(common.Big1, x) + + // max(1 - (block_timestamp - parent_timestamp) // 10, -99))) + if x.Cmp(bigMinus99) < 0 { + x.Set(bigMinus99) + } + + // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) + y.Div(parentDiff, params.DifficultyBoundDivisor) + x.Mul(y, x) + x.Add(parentDiff, x) + + // minimum difficulty can ever be (before exponential factor) + if x.Cmp(params.MinimumDifficulty) < 0 { + x.Set(params.MinimumDifficulty) + } + + // for the exponential factor + periodCount := new(big.Int).Add(parentNumber, common.Big1) + periodCount.Div(periodCount, ExpDiffPeriod) + + // the exponential factor, commonly referred to as "the bomb" + // diff = diff + 2^(periodCount - 2) + if periodCount.Cmp(common.Big1) > 0 { + y.Sub(periodCount, common.Big2) + y.Exp(common.Big2, y, nil) + x.Add(x, y) + } + + return x +} + +func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + diff := new(big.Int) + adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) + bigTime := new(big.Int) + bigParentTime := new(big.Int) + + bigTime.SetUint64(time) + bigParentTime.SetUint64(parentTime) + + if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { + diff.Add(parentDiff, adjust) + } else { + diff.Sub(parentDiff, adjust) + } + if diff.Cmp(params.MinimumDifficulty) < 0 { + diff.Set(params.MinimumDifficulty) + } + + periodCount := new(big.Int).Add(parentNumber, common.Big1) + periodCount.Div(periodCount, ExpDiffPeriod) + if periodCount.Cmp(common.Big1) > 0 { + // diff = diff + 2^(periodCount - 2) + expDiff := periodCount.Sub(periodCount, common.Big2) + expDiff.Exp(common.Big2, expDiff, nil) + diff.Add(diff, expDiff) + diff = common.BigMax(diff, params.MinimumDifficulty) + } + + return diff +} + +// CalcGasLimit computes the gas limit of the next block after parent. +// The result may be modified by the caller. +// This is miner strategy, not consensus protocol. +func CalcGasLimit(parent *types.Block) *big.Int { + // contrib = (parentGasUsed * 3 / 2) / 1024 + contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3)) + contrib = contrib.Div(contrib, big.NewInt(2)) + contrib = contrib.Div(contrib, params.GasLimitBoundDivisor) + + // decay = parentGasLimit / 1024 -1 + decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) + decay.Sub(decay, big.NewInt(1)) + + /* + strategy: gasLimit of block-to-mine is set based on parent's + gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we + increase it, otherwise lower it (or leave it unchanged if it's right + at that usage) the amount increased/decreased depends on how far away + from parentGasLimit * (2/3) parentGasUsed is. + */ + gl := new(big.Int).Sub(parent.GasLimit(), decay) + gl = gl.Add(gl, contrib) + gl.Set(common.BigMax(gl, params.MinGasLimit)) + + // however, if we're now below the target (TargetGasLimit) we increase the + // limit as much as we can (parentGasLimit / 1024 -1) + if gl.Cmp(params.TargetGasLimit) < 0 { + gl.Add(parent.GasLimit(), decay) + gl.Set(common.BigMin(gl, params.TargetGasLimit)) + } + return gl +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/block_validator_test.go b/vendor/github.com/ethereum/go-ethereum/core/block_validator_test.go new file mode 100644 index 0000000..01931ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/block_validator_test.go @@ -0,0 +1,94 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +func testChainConfig() *params.ChainConfig { + return params.TestChainConfig + //return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} +} + +func proc() (Validator, *BlockChain) { + db, _ := ethdb.NewMemDatabase() + var mux event.TypeMux + + WriteTestNetGenesisBlock(db) + blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux, vm.Config{}) + if err != nil { + fmt.Println(err) + } + return blockchain.validator, blockchain +} + +func TestNumber(t *testing.T) { + _, chain := proc() + + statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) + cfg := testChainConfig() + header := makeHeader(cfg, chain.Genesis(), statedb) + header.Number = big.NewInt(3) + err := ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false) + if err != BlockNumberErr { + t.Errorf("expected block number error, got %q", err) + } + + header = makeHeader(cfg, chain.Genesis(), statedb) + err = ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false) + if err == BlockNumberErr { + t.Errorf("didn't expect block number error") + } +} + +func TestPutReceipt(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + var addr common.Address + addr[0] = 1 + var hash common.Hash + hash[0] = 2 + + receipt := new(types.Receipt) + receipt.Logs = []*types.Log{{ + Address: addr, + Topics: []common.Hash{hash}, + Data: []byte("hi"), + BlockNumber: 42, + TxHash: hash, + TxIndex: 0, + BlockHash: hash, + Index: 0, + }} + + WriteReceipts(db, types.Receipts{receipt}) + receipt = GetReceipt(db, common.Hash{}) + if receipt == nil { + t.Error("expected to get 1 receipt, got none.") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go new file mode 100644 index 0000000..b57eb48 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -0,0 +1,1392 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package core implements the Ethereum consensus protocol. +package core + +import ( + "errors" + "fmt" + "io" + "math/big" + mrand "math/rand" + "runtime" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/hashicorp/golang-lru" +) + +var ( + blockInsertTimer = metrics.NewTimer("chain/inserts") + + ErrNoGenesis = errors.New("Genesis not found in chain") +) + +const ( + bodyCacheLimit = 256 + blockCacheLimit = 256 + maxFutureBlocks = 256 + maxTimeFutureBlocks = 30 + // must be bumped when consensus algorithm is changed, this forces the upgradedb + // command to be run (forces the blocks to be imported again using the new algorithm) + BlockChainVersion = 3 + badBlockLimit = 10 +) + +// BlockChain represents the canonical chain given a database with a genesis +// block. The Blockchain manages chain imports, reverts, chain reorganisations. +// +// Importing blocks in to the block chain happens according to the set of rules +// defined by the two stage Validator. Processing of blocks is done using the +// Processor which processes the included transaction. The validation of the state +// is done in the second part of the Validator. Failing results in aborting of +// the import. +// +// The BlockChain also helps in returning blocks from **any** chain included +// in the database as well as blocks that represents the canonical chain. It's +// important to note that GetBlock can return any block and does not need to be +// included in the canonical one where as GetBlockByNumber always represents the +// canonical chain. +type BlockChain struct { + config *params.ChainConfig // chain & network configuration + + hc *HeaderChain + chainDb ethdb.Database + eventMux *event.TypeMux + genesisBlock *types.Block + + mu sync.RWMutex // global mutex for locking chain operations + chainmu sync.RWMutex // blockchain insertion lock + procmu sync.RWMutex // block processor lock + + checkpoint int // checkpoint counts towards the new checkpoint + currentBlock *types.Block // Current head of the block chain + currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) + + stateCache *state.StateDB // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing + + quit chan struct{} // blockchain quit channel + running int32 // running must be called atomically + // procInterrupt must be atomically called + procInterrupt int32 // interrupt signaler for block processing + wg sync.WaitGroup // chain processing wait group for shutting down + + pow pow.PoW + processor Processor // block processor interface + validator Validator // block and state validator interface + vmConfig vm.Config + + badBlocks *lru.Cache // Bad block cache +} + +// NewBlockChain returns a fully initialised block chain using information +// available in the database. It initialiser the default Ethereum Validator and +// Processor. +func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) { + bodyCache, _ := lru.New(bodyCacheLimit) + bodyRLPCache, _ := lru.New(bodyCacheLimit) + blockCache, _ := lru.New(blockCacheLimit) + futureBlocks, _ := lru.New(maxFutureBlocks) + badBlocks, _ := lru.New(badBlockLimit) + + bc := &BlockChain{ + config: config, + chainDb: chainDb, + eventMux: mux, + quit: make(chan struct{}), + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + pow: pow, + vmConfig: vmConfig, + badBlocks: badBlocks, + } + bc.SetValidator(NewBlockValidator(config, bc, pow)) + bc.SetProcessor(NewStateProcessor(config, bc)) + + gv := func() HeaderValidator { return bc.Validator() } + var err error + bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt) + if err != nil { + return nil, err + } + + bc.genesisBlock = bc.GetBlockByNumber(0) + if bc.genesisBlock == nil { + return nil, ErrNoGenesis + } + + if err := bc.loadLastState(); err != nil { + return nil, err + } + // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain + for hash := range BadHashes { + if header := bc.GetHeaderByHash(hash); header != nil { + // get the canonical block corresponding to the offending header's number + headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64()) + // make sure the headerByNumber (if present) is in our current canonical chain + if headerByNumber != nil && headerByNumber.Hash() == header.Hash() { + glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4]) + bc.SetHead(header.Number.Uint64() - 1) + glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation") + } + } + } + // Take ownership of this particular state + go bc.update() + return bc, nil +} + +func (self *BlockChain) getProcInterrupt() bool { + return atomic.LoadInt32(&self.procInterrupt) == 1 +} + +// loadLastState loads the last known chain state from the database. This method +// assumes that the chain manager mutex is held. +func (self *BlockChain) loadLastState() error { + // Restore the last known head block + head := GetHeadBlockHash(self.chainDb) + if head == (common.Hash{}) { + // Corrupt or empty database, init from scratch + self.Reset() + } else { + if block := self.GetBlockByHash(head); block != nil { + // Block found, set as the current head + self.currentBlock = block + } else { + // Corrupt or empty database, init from scratch + self.Reset() + } + } + // Restore the last known head header + currentHeader := self.currentBlock.Header() + if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) { + if header := self.GetHeaderByHash(head); header != nil { + currentHeader = header + } + } + self.hc.SetCurrentHeader(currentHeader) + // Restore the last known head fast block + self.currentFastBlock = self.currentBlock + if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) { + if block := self.GetBlockByHash(head); block != nil { + self.currentFastBlock = block + } + } + // Initialize a statedb cache to ensure singleton account bloom filter generation + statedb, err := state.New(self.currentBlock.Root(), self.chainDb) + if err != nil { + return err + } + self.stateCache = statedb + + // Issue a status log for the user + headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()) + blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64()) + fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()) + + glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", currentHeader.Number, currentHeader.Hash().Bytes()[:4], headerTd) + glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) + glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) + + return nil +} + +// SetHead rewinds the local chain to a new head. In the case of headers, everything +// above the new head will be deleted and the new one set. In the case of blocks +// though, the head may be further rewound if block bodies are missing (non-archive +// nodes after a fast sync). +func (bc *BlockChain) SetHead(head uint64) { + bc.mu.Lock() + defer bc.mu.Unlock() + + delFn := func(hash common.Hash, num uint64) { + DeleteBody(bc.chainDb, hash, num) + } + bc.hc.SetHead(head, delFn) + + // Clear out any stale content from the caches + bc.bodyCache.Purge() + bc.bodyRLPCache.Purge() + bc.blockCache.Purge() + bc.futureBlocks.Purge() + + // Update all computed fields to the new head + currentHeader := bc.hc.CurrentHeader() + if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() { + bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) + } + if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() { + bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) + } + + if bc.currentBlock == nil { + bc.currentBlock = bc.genesisBlock + } + if bc.currentFastBlock == nil { + bc.currentFastBlock = bc.genesisBlock + } + + if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { + glog.Fatalf("failed to reset head block hash: %v", err) + } + if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { + glog.Fatalf("failed to reset head fast block hash: %v", err) + } + bc.loadLastState() +} + +// FastSyncCommitHead sets the current head block to the one defined by the hash +// irrelevant what the chain contents were prior. +func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { + // Make sure that both the block as well at its state trie exists + block := self.GetBlockByHash(hash) + if block == nil { + return fmt.Errorf("non existent block [%x…]", hash[:4]) + } + if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil { + return err + } + // If all checks out, manually set the head block + self.mu.Lock() + self.currentBlock = block + self.mu.Unlock() + + glog.V(logger.Info).Infof("committed block #%d [%x…] as new head", block.Number(), hash[:4]) + return nil +} + +// GasLimit returns the gas limit of the current HEAD block. +func (self *BlockChain) GasLimit() *big.Int { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentBlock.GasLimit() +} + +// LastBlockHash return the hash of the HEAD block. +func (self *BlockChain) LastBlockHash() common.Hash { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentBlock.Hash() +} + +// CurrentBlock retrieves the current head block of the canonical chain. The +// block is retrieved from the blockchain's internal cache. +func (self *BlockChain) CurrentBlock() *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentBlock +} + +// CurrentFastBlock retrieves the current fast-sync head block of the canonical +// chain. The block is retrieved from the blockchain's internal cache. +func (self *BlockChain) CurrentFastBlock() *types.Block { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.currentFastBlock +} + +// Status returns status information about the current chain such as the HEAD Td, +// the HEAD hash and the hash of the genesis block. +func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64()), self.currentBlock.Hash(), self.genesisBlock.Hash() +} + +// SetProcessor sets the processor required for making state modifications. +func (self *BlockChain) SetProcessor(processor Processor) { + self.procmu.Lock() + defer self.procmu.Unlock() + self.processor = processor +} + +// SetValidator sets the validator which is used to validate incoming blocks. +func (self *BlockChain) SetValidator(validator Validator) { + self.procmu.Lock() + defer self.procmu.Unlock() + self.validator = validator +} + +// Validator returns the current validator. +func (self *BlockChain) Validator() Validator { + self.procmu.RLock() + defer self.procmu.RUnlock() + return self.validator +} + +// Processor returns the current processor. +func (self *BlockChain) Processor() Processor { + self.procmu.RLock() + defer self.procmu.RUnlock() + return self.processor +} + +// AuxValidator returns the auxiliary validator (Proof of work atm) +func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } + +// State returns a new mutable state based on the current HEAD block. +func (self *BlockChain) State() (*state.StateDB, error) { + return self.StateAt(self.CurrentBlock().Root()) +} + +// StateAt returns a new mutable state based on a particular point in time. +func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { + return self.stateCache.New(root) +} + +// Reset purges the entire blockchain, restoring it to its genesis state. +func (bc *BlockChain) Reset() { + bc.ResetWithGenesisBlock(bc.genesisBlock) +} + +// ResetWithGenesisBlock purges the entire blockchain, restoring it to the +// specified genesis state. +func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { + // Dump the entire block chain and purge the caches + bc.SetHead(0) + + bc.mu.Lock() + defer bc.mu.Unlock() + + // Prepare the genesis block and reinitialise the chain + if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { + glog.Fatalf("failed to write genesis block TD: %v", err) + } + if err := WriteBlock(bc.chainDb, genesis); err != nil { + glog.Fatalf("failed to write genesis block: %v", err) + } + bc.genesisBlock = genesis + bc.insert(bc.genesisBlock) + bc.currentBlock = bc.genesisBlock + bc.hc.SetGenesis(bc.genesisBlock.Header()) + bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) + bc.currentFastBlock = bc.genesisBlock +} + +// Export writes the active chain to the given writer. +func (self *BlockChain) Export(w io.Writer) error { + return self.ExportN(w, uint64(0), self.currentBlock.NumberU64()) +} + +// ExportN writes a subset of the active chain to the given writer. +func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { + self.mu.RLock() + defer self.mu.RUnlock() + + if first > last { + return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) + } + + glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1) + + for nr := first; nr <= last; nr++ { + block := self.GetBlockByNumber(nr) + if block == nil { + return fmt.Errorf("export failed on #%d: not found", nr) + } + + if err := block.EncodeRLP(w); err != nil { + return err + } + } + + return nil +} + +// insert injects a new head block into the current block chain. This method +// assumes that the block is indeed a true head. It will also reset the head +// header and the head fast sync block to this very same block if they are older +// or if they are on a different side chain. +// +// Note, this function assumes that the `mu` mutex is held! +func (bc *BlockChain) insert(block *types.Block) { + // If the block is on a side chain or an unknown one, force other heads onto it too + updateHeads := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash() + + // Add the block to the canonical chain number scheme and mark as the head + if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { + glog.Fatalf("failed to insert block number: %v", err) + } + if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to insert head block hash: %v", err) + } + bc.currentBlock = block + + // If the block is better than out head or is on a different chain, force update heads + if updateHeads { + bc.hc.SetCurrentHeader(block.Header()) + + if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil { + glog.Fatalf("failed to insert head fast block hash: %v", err) + } + bc.currentFastBlock = block + } +} + +// Accessors +func (bc *BlockChain) Genesis() *types.Block { + return bc.genesisBlock +} + +// GetBody retrieves a block body (transactions and uncles) from the database by +// hash, caching it if found. +func (self *BlockChain) GetBody(hash common.Hash) *types.Body { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyCache.Get(hash); ok { + body := cached.(*types.Body) + return body + } + body := GetBody(self.chainDb, hash, self.hc.GetBlockNumber(hash)) + if body == nil { + return nil + } + // Cache the found body for next time and return + self.bodyCache.Add(hash, body) + return body +} + +// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, +// caching it if found. +func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyRLPCache.Get(hash); ok { + return cached.(rlp.RawValue) + } + body := GetBodyRLP(self.chainDb, hash, self.hc.GetBlockNumber(hash)) + if len(body) == 0 { + return nil + } + // Cache the found body for next time and return + self.bodyRLPCache.Add(hash, body) + return body +} + +// HasBlock checks if a block is fully present in the database or not, caching +// it if present. +func (bc *BlockChain) HasBlock(hash common.Hash) bool { + return bc.GetBlockByHash(hash) != nil +} + +// HasBlockAndState checks if a block and associated state trie is fully present +// in the database or not, caching it if present. +func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool { + // Check first that the block itself is known + block := bc.GetBlockByHash(hash) + if block == nil { + return false + } + // Ensure the associated state is also present + _, err := state.New(block.Root(), bc.chainDb) + return err == nil +} + +// GetBlock retrieves a block from the database by hash and number, +// caching it if found. +func (self *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { + // Short circuit if the block's already in the cache, retrieve otherwise + if block, ok := self.blockCache.Get(hash); ok { + return block.(*types.Block) + } + block := GetBlock(self.chainDb, hash, number) + if block == nil { + return nil + } + // Cache the found block for next time and return + self.blockCache.Add(block.Hash(), block) + return block +} + +// GetBlockByHash retrieves a block from the database by hash, caching it if found. +func (self *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { + return self.GetBlock(hash, self.hc.GetBlockNumber(hash)) +} + +// GetBlockByNumber retrieves a block from the database by number, caching it +// (associated with its hash) if found. +func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block { + hash := GetCanonicalHash(self.chainDb, number) + if hash == (common.Hash{}) { + return nil + } + return self.GetBlock(hash, number) +} + +// [deprecated by eth/62] +// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. +func (self *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { + number := self.hc.GetBlockNumber(hash) + for i := 0; i < n; i++ { + block := self.GetBlock(hash, number) + if block == nil { + break + } + blocks = append(blocks, block) + hash = block.ParentHash() + number-- + } + return +} + +// GetUnclesInChain retrieves all the uncles from a given block backwards until +// a specific distance is reached. +func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header { + uncles := []*types.Header{} + for i := 0; block != nil && i < length; i++ { + uncles = append(uncles, block.Uncles()...) + block = self.GetBlock(block.ParentHash(), block.NumberU64()-1) + } + return uncles +} + +// Stop stops the blockchain service. If any imports are currently in progress +// it will abort them using the procInterrupt. +func (bc *BlockChain) Stop() { + if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { + return + } + close(bc.quit) + atomic.StoreInt32(&bc.procInterrupt, 1) + + bc.wg.Wait() + + glog.V(logger.Info).Infoln("Chain manager stopped") +} + +func (self *BlockChain) procFutureBlocks() { + blocks := make([]*types.Block, 0, self.futureBlocks.Len()) + for _, hash := range self.futureBlocks.Keys() { + if block, exist := self.futureBlocks.Peek(hash); exist { + blocks = append(blocks, block.(*types.Block)) + } + } + if len(blocks) > 0 { + types.BlockBy(types.Number).Sort(blocks) + + // Insert one by one as chain insertion needs contiguous ancestry between blocks + for i := range blocks { + self.InsertChain(blocks[i : i+1]) + } + } +} + +type WriteStatus byte + +const ( + NonStatTy WriteStatus = iota + CanonStatTy + SplitStatTy + SideStatTy +) + +// Rollback is designed to remove a chain of links from the database that aren't +// certain enough to be valid. +func (self *BlockChain) Rollback(chain []common.Hash) { + self.mu.Lock() + defer self.mu.Unlock() + + for i := len(chain) - 1; i >= 0; i-- { + hash := chain[i] + + currentHeader := self.hc.CurrentHeader() + if currentHeader.Hash() == hash { + self.hc.SetCurrentHeader(self.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)) + } + if self.currentFastBlock.Hash() == hash { + self.currentFastBlock = self.GetBlock(self.currentFastBlock.ParentHash(), self.currentFastBlock.NumberU64()-1) + WriteHeadFastBlockHash(self.chainDb, self.currentFastBlock.Hash()) + } + if self.currentBlock.Hash() == hash { + self.currentBlock = self.GetBlock(self.currentBlock.ParentHash(), self.currentBlock.NumberU64()-1) + WriteHeadBlockHash(self.chainDb, self.currentBlock.Hash()) + } + } +} + +// SetReceiptsData computes all the non-consensus fields of the receipts +func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) { + signer := types.MakeSigner(config, block.Number()) + + transactions, logIndex := block.Transactions(), uint(0) + + for j := 0; j < len(receipts); j++ { + // The transaction hash can be retrieved from the transaction itself + receipts[j].TxHash = transactions[j].Hash() + + tx, _ := transactions[j].AsMessage(signer) + // The contract address can be derived from the transaction itself + if MessageCreatesContract(tx) { + receipts[j].ContractAddress = crypto.CreateAddress(tx.From(), tx.Nonce()) + } + // The used gas can be calculated based on previous receipts + if j == 0 { + receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed) + } else { + receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed) + } + // The derived log fields can simply be set from the block and transaction + for k := 0; k < len(receipts[j].Logs); k++ { + receipts[j].Logs[k].BlockNumber = block.NumberU64() + receipts[j].Logs[k].BlockHash = block.Hash() + receipts[j].Logs[k].TxHash = receipts[j].TxHash + receipts[j].Logs[k].TxIndex = uint(j) + receipts[j].Logs[k].Index = logIndex + logIndex++ + } + } +} + +// InsertReceiptChain attempts to complete an already existing header chain with +// transaction and receipt data. +// XXX should this be moved to the test? +func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { + // Do a sanity check that the provided chain is actually ordered and linked + for i := 1; i < len(blockChain); i++ { + if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() { + // Chain broke ancestry, log a messge (programming error) and skip insertion + failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(), + blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) + + glog.V(logger.Error).Info(failure.Error()) + return 0, failure + } + } + // Pre-checks passed, start the block body and receipt imports + self.wg.Add(1) + defer self.wg.Done() + + // Collect some import statistics to report on + stats := struct{ processed, ignored int32 }{} + start := time.Now() + + // Create the block importing task queue and worker functions + tasks := make(chan int, len(blockChain)) + for i := 0; i < len(blockChain) && i < len(receiptChain); i++ { + tasks <- i + } + close(tasks) + + errs, failed := make([]error, len(tasks)), int32(0) + process := func(worker int) { + for index := range tasks { + block, receipts := blockChain[index], receiptChain[index] + + // Short circuit insertion if shutting down or processing failed + if atomic.LoadInt32(&self.procInterrupt) == 1 { + return + } + if atomic.LoadInt32(&failed) > 0 { + return + } + // Short circuit if the owner header is unknown + if !self.HasHeader(block.Hash()) { + errs[index] = fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4]) + atomic.AddInt32(&failed, 1) + return + } + // Skip if the entire data is already known + if self.HasBlock(block.Hash()) { + atomic.AddInt32(&stats.ignored, 1) + continue + } + // Compute all the non-consensus fields of the receipts + SetReceiptsData(self.config, block, receipts) + // Write all the data out into the database + if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil { + errs[index] = fmt.Errorf("failed to write block body: %v", err) + atomic.AddInt32(&failed, 1) + glog.Fatal(errs[index]) + return + } + if err := WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil { + errs[index] = fmt.Errorf("failed to write block receipts: %v", err) + atomic.AddInt32(&failed, 1) + glog.Fatal(errs[index]) + return + } + if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { + errs[index] = fmt.Errorf("failed to write log blooms: %v", err) + atomic.AddInt32(&failed, 1) + glog.Fatal(errs[index]) + return + } + if err := WriteTransactions(self.chainDb, block); err != nil { + errs[index] = fmt.Errorf("failed to write individual transactions: %v", err) + atomic.AddInt32(&failed, 1) + glog.Fatal(errs[index]) + return + } + if err := WriteReceipts(self.chainDb, receipts); err != nil { + errs[index] = fmt.Errorf("failed to write individual receipts: %v", err) + atomic.AddInt32(&failed, 1) + glog.Fatal(errs[index]) + return + } + atomic.AddInt32(&stats.processed, 1) + } + } + // Start as many worker threads as goroutines allowed + pending := new(sync.WaitGroup) + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + pending.Add(1) + go func(id int) { + defer pending.Done() + process(id) + }(i) + } + pending.Wait() + + // If anything failed, report + if failed > 0 { + for i, err := range errs { + if err != nil { + return i, err + } + } + } + if atomic.LoadInt32(&self.procInterrupt) == 1 { + glog.V(logger.Debug).Infoln("premature abort during receipt chain processing") + return 0, nil + } + // Update the head fast sync block if better + self.mu.Lock() + head := blockChain[len(errs)-1] + if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(self.GetTd(head.Hash(), head.NumberU64())) < 0 { + if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil { + glog.Fatalf("failed to update head fast block hash: %v", err) + } + self.currentFastBlock = head + } + self.mu.Unlock() + + // Report some public statistics so the user has a clue what's going on + first, last := blockChain[0], blockChain[len(blockChain)-1] + + ignored := "" + if stats.ignored > 0 { + ignored = fmt.Sprintf(" (%d ignored)", stats.ignored) + } + glog.V(logger.Info).Infof("imported %4d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored) + + return 0, nil +} + +// WriteBlock writes the block to the chain. +func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err error) { + self.wg.Add(1) + defer self.wg.Done() + + // Calculate the total difficulty of the block + ptd := self.GetTd(block.ParentHash(), block.NumberU64()-1) + if ptd == nil { + return NonStatTy, ParentError(block.ParentHash()) + } + // Make sure no inconsistent state is leaked during insertion + self.mu.Lock() + defer self.mu.Unlock() + + localTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64()) + externTd := new(big.Int).Add(block.Difficulty(), ptd) + + // Irrelevant of the canonical status, write the block itself to the database + if err := self.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil { + glog.Fatalf("failed to write block total difficulty: %v", err) + } + if err := WriteBlock(self.chainDb, block); err != nil { + glog.Fatalf("failed to write block contents: %v", err) + } + + // If the total difficulty is higher than our known, add it to the canonical chain + // Second clause in the if statement reduces the vulnerability to selfish mining. + // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf + if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { + // Reorganise the chain if the parent is not the head block + if block.ParentHash() != self.currentBlock.Hash() { + if err := self.reorg(self.currentBlock, block); err != nil { + return NonStatTy, err + } + } + self.insert(block) // Insert the block as the new head of the chain + status = CanonStatTy + } else { + status = SideStatTy + } + + self.futureBlocks.Remove(block.Hash()) + + return +} + +// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. If an error is returned +// it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go). +func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { + // Do a sanity check that the provided chain is actually ordered and linked + for i := 1; i < len(chain); i++ { + if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() { + // Chain broke ancestry, log a messge (programming error) and skip insertion + failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", + i-1, chain[i-1].NumberU64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4]) + + glog.V(logger.Error).Info(failure.Error()) + return 0, failure + } + } + // Pre-checks passed, start the full block imports + self.wg.Add(1) + defer self.wg.Done() + + self.chainmu.Lock() + defer self.chainmu.Unlock() + + // A queued approach to delivering events. This is generally + // faster than direct delivery and requires much less mutex + // acquiring. + var ( + stats = insertStats{startTime: mclock.Now()} + events = make([]interface{}, 0, len(chain)) + coalescedLogs []*types.Log + nonceChecked = make([]bool, len(chain)) + ) + + // Start the parallel nonce verifier. + nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain) + defer close(nonceAbort) + + for i, block := range chain { + if atomic.LoadInt32(&self.procInterrupt) == 1 { + glog.V(logger.Debug).Infoln("Premature abort during block chain processing") + break + } + bstart := time.Now() + // Wait for block i's nonce to be verified before processing + // its state transition. + for !nonceChecked[i] { + r := <-nonceResults + nonceChecked[r.index] = true + if !r.valid { + block := chain[r.index] + return r.index, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()} + } + } + + if BadHashes[block.Hash()] { + err := BadHashError(block.Hash()) + self.reportBlock(block, nil, err) + return i, err + } + // Stage 1 validation of the block using the chain's validator + // interface. + err := self.Validator().ValidateBlock(block) + if err != nil { + if IsKnownBlockErr(err) { + stats.ignored++ + continue + } + + if err == BlockFutureErr { + // Allow up to MaxFuture second in the future blocks. If this limit + // is exceeded the chain is discarded and processed at a later time + // if given. + max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks) + if block.Time().Cmp(max) == 1 { + return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max) + } + + self.futureBlocks.Add(block.Hash(), block) + stats.queued++ + continue + } + + if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) { + self.futureBlocks.Add(block.Hash(), block) + stats.queued++ + continue + } + + self.reportBlock(block, nil, err) + return i, err + } + // Create a new statedb using the parent block and report an + // error if it fails. + switch { + case i == 0: + err = self.stateCache.Reset(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) + default: + err = self.stateCache.Reset(chain[i-1].Root()) + } + if err != nil { + self.reportBlock(block, nil, err) + return i, err + } + // Process block using the parent state as reference point. + receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.vmConfig) + if err != nil { + self.reportBlock(block, receipts, err) + return i, err + } + // Validate the state using the default validator + err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas) + if err != nil { + self.reportBlock(block, receipts, err) + return i, err + } + // Write state changes to database + _, err = self.stateCache.Commit(self.config.IsEIP158(block.Number())) + if err != nil { + return i, err + } + + // coalesce logs for later processing + coalescedLogs = append(coalescedLogs, logs...) + + if err := WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil { + return i, err + } + + // write the block to the chain and get the status + status, err := self.WriteBlock(block) + if err != nil { + return i, err + } + + switch status { + case CanonStatTy: + if glog.V(logger.Debug) { + glog.Infof("inserted block #%d [%x…] in %9v: %3d txs %7v gas %d uncles.", block.Number(), block.Hash().Bytes()[0:4], common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), block.GasUsed(), len(block.Uncles())) + } + blockInsertTimer.UpdateSince(bstart) + events = append(events, ChainEvent{block, block.Hash(), logs}) + + // This puts transactions in a extra db for rpc + if err := WriteTransactions(self.chainDb, block); err != nil { + return i, err + } + // store the receipts + if err := WriteReceipts(self.chainDb, receipts); err != nil { + return i, err + } + // Write map map bloom filters + if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { + return i, err + } + // Write hash preimages + if err := WritePreimages(self.chainDb, block.NumberU64(), self.stateCache.Preimages()); err != nil { + return i, err + } + case SideStatTy: + if glog.V(logger.Detail) { + glog.Infof("inserted forked block #%d [%x…] (TD=%v) in %9v: %3d txs %d uncles.", block.Number(), block.Hash().Bytes()[0:4], block.Difficulty(), common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), len(block.Uncles())) + } + blockInsertTimer.UpdateSince(bstart) + events = append(events, ChainSideEvent{block}) + + case SplitStatTy: + events = append(events, ChainSplitEvent{block, logs}) + } + + stats.processed++ + if glog.V(logger.Info) { + stats.usedGas += usedGas.Uint64() + stats.report(chain, i) + } + } + + go self.postChainEvents(events, coalescedLogs) + + return 0, nil +} + +// insertStats tracks and reports on block insertion. +type insertStats struct { + queued, processed, ignored int + usedGas uint64 + lastIndex int + startTime mclock.AbsTime +} + +// statsReportLimit is the time limit during import after which we always print +// out progress. This avoids the user wondering what's going on. +const statsReportLimit = 8 * time.Second + +// report prints statistics if some number of blocks have been processed +// or more than a few seconds have passed since the last message. +func (st *insertStats) report(chain []*types.Block, index int) { + // Fetch the timings for the batch + var ( + now = mclock.Now() + elapsed = time.Duration(now) - time.Duration(st.startTime) + ) + // If we're at the last block of the batch or report period reached, log + if index == len(chain)-1 || elapsed >= statsReportLimit { + start, end := chain[st.lastIndex], chain[index] + txcount := countTransactions(chain[st.lastIndex : index+1]) + + var hashes, extra string + if st.queued > 0 || st.ignored > 0 { + extra = fmt.Sprintf(" (%d queued %d ignored)", st.queued, st.ignored) + } + if st.processed > 1 { + hashes = fmt.Sprintf("%x… / %x…", start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) + } else { + hashes = fmt.Sprintf("%x…", end.Hash().Bytes()[:4]) + } + glog.Infof("imported %4d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra) + + *st = insertStats{startTime: now, lastIndex: index} + } +} + +func countTransactions(chain []*types.Block) (c int) { + for _, b := range chain { + c += len(b.Transactions()) + } + return c +} + +// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them +// to be part of the new canonical chain and accumulates potential missing transactions and post an +// event about them +func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { + var ( + newChain types.Blocks + oldChain types.Blocks + commonBlock *types.Block + deletedTxs types.Transactions + deletedLogs []*types.Log + // collectLogs collects the logs that were generated during the + // processing of the block that corresponds with the given hash. + // These logs are later announced as deleted. + collectLogs = func(h common.Hash) { + // Coalesce logs and set 'Removed'. + receipts := GetBlockReceipts(self.chainDb, h, self.hc.GetBlockNumber(h)) + for _, receipt := range receipts { + for _, log := range receipt.Logs { + del := *log + del.Removed = true + deletedLogs = append(deletedLogs, &del) + } + } + } + ) + + // first reduce whoever is higher bound + if oldBlock.NumberU64() > newBlock.NumberU64() { + // reduce old chain + for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) { + oldChain = append(oldChain, oldBlock) + deletedTxs = append(deletedTxs, oldBlock.Transactions()...) + + collectLogs(oldBlock.Hash()) + } + } else { + // reduce new chain and append new chain blocks for inserting later on + for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) { + newChain = append(newChain, newBlock) + } + } + if oldBlock == nil { + return fmt.Errorf("Invalid old chain") + } + if newBlock == nil { + return fmt.Errorf("Invalid new chain") + } + + for { + if oldBlock.Hash() == newBlock.Hash() { + commonBlock = oldBlock + break + } + + oldChain = append(oldChain, oldBlock) + newChain = append(newChain, newBlock) + deletedTxs = append(deletedTxs, oldBlock.Transactions()...) + collectLogs(oldBlock.Hash()) + + oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), self.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) + if oldBlock == nil { + return fmt.Errorf("Invalid old chain") + } + if newBlock == nil { + return fmt.Errorf("Invalid new chain") + } + } + + if oldLen := len(oldChain); oldLen > 63 || glog.V(logger.Debug) { + newLen := len(newChain) + newLast := newChain[0] + newFirst := newChain[newLen-1] + oldLast := oldChain[0] + oldFirst := oldChain[oldLen-1] + glog.Infof("Chain split detected after #%v [%x…]. Reorganising chain (-%v +%v blocks), rejecting #%v-#%v [%x…/%x…] in favour of #%v-#%v [%x…/%x…]", + commonBlock.Number(), commonBlock.Hash().Bytes()[:4], + oldLen, newLen, + oldFirst.Number(), oldLast.Number(), + oldFirst.Hash().Bytes()[:4], oldLast.Hash().Bytes()[:4], + newFirst.Number(), newLast.Number(), + newFirst.Hash().Bytes()[:4], newLast.Hash().Bytes()[:4]) + } + + var addedTxs types.Transactions + // insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly + for _, block := range newChain { + // insert the block in the canonical way, re-writing history + self.insert(block) + // write canonical receipts and transactions + if err := WriteTransactions(self.chainDb, block); err != nil { + return err + } + receipts := GetBlockReceipts(self.chainDb, block.Hash(), block.NumberU64()) + // write receipts + if err := WriteReceipts(self.chainDb, receipts); err != nil { + return err + } + // Write map map bloom filters + if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { + return err + } + addedTxs = append(addedTxs, block.Transactions()...) + } + + // calculate the difference between deleted and added transactions + diff := types.TxDifference(deletedTxs, addedTxs) + // When transactions get deleted from the database that means the + // receipts that were created in the fork must also be deleted + for _, tx := range diff { + DeleteReceipt(self.chainDb, tx.Hash()) + DeleteTransaction(self.chainDb, tx.Hash()) + } + // Must be posted in a goroutine because of the transaction pool trying + // to acquire the chain manager lock + if len(diff) > 0 { + go self.eventMux.Post(RemovedTransactionEvent{diff}) + } + if len(deletedLogs) > 0 { + go self.eventMux.Post(RemovedLogsEvent{deletedLogs}) + } + + if len(oldChain) > 0 { + go func() { + for _, block := range oldChain { + self.eventMux.Post(ChainSideEvent{Block: block}) + } + }() + } + + return nil +} + +// postChainEvents iterates over the events generated by a chain insertion and +// posts them into the event mux. +func (self *BlockChain) postChainEvents(events []interface{}, logs []*types.Log) { + // post event logs for further processing + self.eventMux.Post(logs) + for _, event := range events { + if event, ok := event.(ChainEvent); ok { + // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long + // and in most cases isn't even necessary. + if self.LastBlockHash() == event.Hash { + self.eventMux.Post(ChainHeadEvent{event.Block}) + } + } + // Fire the insertion events individually too + self.eventMux.Post(event) + } +} + +func (self *BlockChain) update() { + futureTimer := time.Tick(5 * time.Second) + for { + select { + case <-futureTimer: + self.procFutureBlocks() + case <-self.quit: + return + } + } +} + +// BadBlockArgs represents the entries in the list returned when bad blocks are queried. +type BadBlockArgs struct { + Hash common.Hash `json:"hash"` + Header *types.Header `json:"header"` +} + +// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network +func (bc *BlockChain) BadBlocks() ([]BadBlockArgs, error) { + headers := make([]BadBlockArgs, 0, bc.badBlocks.Len()) + for _, hash := range bc.badBlocks.Keys() { + if hdr, exist := bc.badBlocks.Peek(hash); exist { + header := hdr.(*types.Header) + headers = append(headers, BadBlockArgs{header.Hash(), header}) + } + } + return headers, nil +} + +// addBadBlock adds a bad block to the bad-block LRU cache +func (bc *BlockChain) addBadBlock(block *types.Block) { + bc.badBlocks.Add(block.Header().Hash(), block.Header()) +} + +// reportBlock logs a bad block error. +func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { + bc.addBadBlock(block) + if glog.V(logger.Error) { + var receiptString string + for _, receipt := range receipts { + receiptString += fmt.Sprintf("\t%v\n", receipt) + } + glog.Errorf(` +########## BAD BLOCK ######### +Chain config: %v + +Number: %v +Hash: 0x%x +%v + +Error: %v +############################## +`, bc.config, block.Number(), block.Hash(), receiptString, err) + } +} + +// InsertHeaderChain attempts to insert the given header chain in to the local +// chain, possibly creating a reorg. If an error is returned, it will return the +// index number of the failing header as well an error describing what went wrong. +// +// The verify parameter can be used to fine tune whether nonce verification +// should be done or not. The reason behind the optional check is because some +// of the header retrieval mechanisms already need to verify nonces, as well as +// because nonces can be verified sparsely, not needing to check each. +func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { + // Make sure only one thread manipulates the chain at once + self.chainmu.Lock() + defer self.chainmu.Unlock() + + self.wg.Add(1) + defer self.wg.Done() + + whFunc := func(header *types.Header) error { + self.mu.Lock() + defer self.mu.Unlock() + + _, err := self.hc.WriteHeader(header) + return err + } + + return self.hc.InsertHeaderChain(chain, checkFreq, whFunc) +} + +// writeHeader writes a header into the local chain, given that its parent is +// already known. If the total difficulty of the newly inserted header becomes +// greater than the current known TD, the canonical chain is re-routed. +// +// Note: This method is not concurrent-safe with inserting blocks simultaneously +// into the chain, as side effects caused by reorganisations cannot be emulated +// without the real blocks. Hence, writing headers directly should only be done +// in two scenarios: pure-header mode of operation (light clients), or properly +// separated header/block phases (non-archive clients). +func (self *BlockChain) writeHeader(header *types.Header) error { + self.wg.Add(1) + defer self.wg.Done() + + self.mu.Lock() + defer self.mu.Unlock() + + _, err := self.hc.WriteHeader(header) + return err +} + +// CurrentHeader retrieves the current head header of the canonical chain. The +// header is retrieved from the HeaderChain's internal cache. +func (self *BlockChain) CurrentHeader() *types.Header { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.hc.CurrentHeader() +} + +// GetTd retrieves a block's total difficulty in the canonical chain from the +// database by hash and number, caching it if found. +func (self *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int { + return self.hc.GetTd(hash, number) +} + +// GetTdByHash retrieves a block's total difficulty in the canonical chain from the +// database by hash, caching it if found. +func (self *BlockChain) GetTdByHash(hash common.Hash) *big.Int { + return self.hc.GetTdByHash(hash) +} + +// GetHeader retrieves a block header from the database by hash and number, +// caching it if found. +func (self *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header { + return self.hc.GetHeader(hash, number) +} + +// GetHeaderByHash retrieves a block header from the database by hash, caching it if +// found. +func (self *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header { + return self.hc.GetHeaderByHash(hash) +} + +// HasHeader checks if a block header is present in the database or not, caching +// it if present. +func (bc *BlockChain) HasHeader(hash common.Hash) bool { + return bc.hc.HasHeader(hash) +} + +// GetBlockHashesFromHash retrieves a number of block hashes starting at a given +// hash, fetching towards the genesis block. +func (self *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + return self.hc.GetBlockHashesFromHash(hash, max) +} + +// GetHeaderByNumber retrieves a block header from the database by number, +// caching it (associated with its hash) if found. +func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { + return self.hc.GetHeaderByNumber(number) +} + +// Config retrieves the blockchain's chain configuration. +func (self *BlockChain) Config() *params.ChainConfig { return self.config } diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain_test.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain_test.go new file mode 100644 index 0000000..99b1556 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain_test.go @@ -0,0 +1,1298 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + "math/rand" + "os" + "path/filepath" + "runtime" + "strconv" + "testing" + "time" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" + "github.com/hashicorp/golang-lru" +) + +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) +} + +func thePow() pow.PoW { + pow, _ := ethash.NewForTesting() + return pow +} + +func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { + var eventMux event.TypeMux + WriteTestNetGenesisBlock(db) + blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux, vm.Config{}) + if err != nil { + t.Error("failed creating blockchain:", err) + t.FailNow() + return nil + } + + return blockchain +} + +// Test fork of length N starting from block i +func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) { + // Copy old chain up to #i into a new db + db, blockchain2, err := newCanonical(i, full) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + // Assert the chains have the same header/block at #i + var hash1, hash2 common.Hash + if full { + hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash() + } else { + hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash() + } + if hash1 != hash2 { + t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) + } + // Extend the newly created chain + var ( + blockChainB []*types.Block + headerChainB []*types.Header + ) + if full { + blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed) + if _, err := blockchain2.InsertChain(blockChainB); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + } else { + headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed) + if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + } + // Sanity check that the forked chain can be imported into the original + var tdPre, tdPost *big.Int + + if full { + tdPre = blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()) + if err := testBlockChainImport(blockChainB, blockchain); err != nil { + t.Fatalf("failed to import forked block chain: %v", err) + } + tdPost = blockchain.GetTdByHash(blockChainB[len(blockChainB)-1].Hash()) + } else { + tdPre = blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()) + if err := testHeaderChainImport(headerChainB, blockchain); err != nil { + t.Fatalf("failed to import forked header chain: %v", err) + } + tdPost = blockchain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) + } + // Compare the total difficulties of the chains + comparator(tdPre, tdPost) +} + +func printChain(bc *BlockChain) { + for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- { + b := bc.GetBlockByNumber(uint64(i)) + fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty()) + } +} + +// testBlockChainImport tries to process a chain of blocks, writing them into +// the database if successful. +func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { + for _, block := range chain { + // Try and process the block + err := blockchain.Validator().ValidateBlock(block) + if err != nil { + if IsKnownBlockErr(err) { + continue + } + return err + } + statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.chainDb) + if err != nil { + return err + } + receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{}) + if err != nil { + blockchain.reportBlock(block, receipts, err) + return err + } + err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas) + if err != nil { + blockchain.reportBlock(block, receipts, err) + return err + } + blockchain.mu.Lock() + WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) + WriteBlock(blockchain.chainDb, block) + statedb.Commit(false) + blockchain.mu.Unlock() + } + return nil +} + +// testHeaderChainImport tries to process a chain of header, writing them into +// the database if successful. +func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error { + for _, header := range chain { + // Try and validate the header + if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeaderByHash(header.ParentHash), false); err != nil { + return err + } + // Manually insert the header into the database, but don't reorganise (allows subsequent testing) + blockchain.mu.Lock() + WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) + WriteHeader(blockchain.chainDb, header) + blockchain.mu.Unlock() + } + return nil +} + +func loadChain(fn string, t *testing.T) (types.Blocks, error) { + fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm) + if err != nil { + return nil, err + } + defer fh.Close() + + var chain types.Blocks + if err := rlp.Decode(fh, &chain); err != nil { + return nil, err + } + + return chain, nil +} + +func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) { + _, err := blockchain.InsertChain(chain) + if err != nil { + fmt.Println(err) + t.FailNow() + } + done <- true +} + +func TestLastBlock(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + bchain := theBlockChain(db, t) + block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0] + bchain.insert(block) + if block.Hash() != GetHeadBlockHash(db) { + t.Errorf("Write/Get HeadBlockHash failed") + } +} + +// Tests that given a starting canonical chain of a given size, it can be extended +// with various length chains. +func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) } +func TestExtendCanonicalBlocks(t *testing.T) { testExtendCanonical(t, true) } + +func testExtendCanonical(t *testing.T, full bool) { + length := 5 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) + } + } + // Start fork from current height + testFork(t, processor, length, 1, full, better) + testFork(t, processor, length, 2, full, better) + testFork(t, processor, length, 5, full, better) + testFork(t, processor, length, 10, full, better) +} + +// Tests that given a starting canonical chain of a given size, creating shorter +// forks do not take canonical ownership. +func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false) } +func TestShorterForkBlocks(t *testing.T) { testShorterFork(t, true) } + +func testShorterFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + worse := func(td1, td2 *big.Int) { + if td2.Cmp(td1) >= 0 { + t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) + } + } + // Sum of numbers must be less than `length` for this to be a shorter fork + testFork(t, processor, 0, 3, full, worse) + testFork(t, processor, 0, 7, full, worse) + testFork(t, processor, 1, 1, full, worse) + testFork(t, processor, 1, 7, full, worse) + testFork(t, processor, 5, 3, full, worse) + testFork(t, processor, 5, 4, full, worse) +} + +// Tests that given a starting canonical chain of a given size, creating longer +// forks do take canonical ownership. +func TestLongerForkHeaders(t *testing.T) { testLongerFork(t, false) } +func TestLongerForkBlocks(t *testing.T) { testLongerFork(t, true) } + +func testLongerFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) + } + } + // Sum of numbers must be greater than `length` for this to be a longer fork + testFork(t, processor, 0, 11, full, better) + testFork(t, processor, 0, 15, full, better) + testFork(t, processor, 1, 10, full, better) + testFork(t, processor, 1, 12, full, better) + testFork(t, processor, 5, 6, full, better) + testFork(t, processor, 5, 8, full, better) +} + +// Tests that given a starting canonical chain of a given size, creating equal +// forks do take canonical ownership. +func TestEqualForkHeaders(t *testing.T) { testEqualFork(t, false) } +func TestEqualForkBlocks(t *testing.T) { testEqualFork(t, true) } + +func testEqualFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + equal := func(td1, td2 *big.Int) { + if td2.Cmp(td1) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) + } + } + // Sum of numbers must be equal to `length` for this to be an equal fork + testFork(t, processor, 0, 10, full, equal) + testFork(t, processor, 1, 9, full, equal) + testFork(t, processor, 2, 8, full, equal) + testFork(t, processor, 5, 5, full, equal) + testFork(t, processor, 6, 4, full, equal) + testFork(t, processor, 9, 1, full, equal) +} + +// Tests that chains missing links do not get accepted by the processor. +func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false) } +func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) } + +func testBrokenChain(t *testing.T, full bool) { + // Make chain starting from genesis + db, blockchain, err := newCanonical(10, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Create a forked chain, and try to insert with a missing link + if full { + chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:] + if err := testBlockChainImport(chain, blockchain); err == nil { + t.Errorf("broken block chain not reported") + } + } else { + chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:] + if err := testHeaderChainImport(chain, blockchain); err == nil { + t.Errorf("broken header chain not reported") + } + } +} + +func TestChainInsertions(t *testing.T) { + t.Skip("Skipped: outdated test files") + + db, _ := ethdb.NewMemDatabase() + + chain1, err := loadChain("valid1", t) + if err != nil { + fmt.Println(err) + t.FailNow() + } + + chain2, err := loadChain("valid2", t) + if err != nil { + fmt.Println(err) + t.FailNow() + } + + blockchain := theBlockChain(db, t) + + const max = 2 + done := make(chan bool, max) + + go insertChain(done, blockchain, chain1, t) + go insertChain(done, blockchain, chain2, t) + + for i := 0; i < max; i++ { + <-done + } + + if chain2[len(chain2)-1].Hash() != blockchain.CurrentBlock().Hash() { + t.Error("chain2 is canonical and shouldn't be") + } + + if chain1[len(chain1)-1].Hash() != blockchain.CurrentBlock().Hash() { + t.Error("chain1 isn't canonical and should be") + } +} + +func TestChainMultipleInsertions(t *testing.T) { + t.Skip("Skipped: outdated test files") + + db, _ := ethdb.NewMemDatabase() + + const max = 4 + chains := make([]types.Blocks, max) + var longest int + for i := 0; i < max; i++ { + var err error + name := "valid" + strconv.Itoa(i+1) + chains[i], err = loadChain(name, t) + if len(chains[i]) >= len(chains[longest]) { + longest = i + } + fmt.Println("loaded", name, "with a length of", len(chains[i])) + if err != nil { + fmt.Println(err) + t.FailNow() + } + } + + blockchain := theBlockChain(db, t) + + done := make(chan bool, max) + for i, chain := range chains { + // XXX the go routine would otherwise reference the same (chain[3]) variable and fail + i := i + chain := chain + go func() { + insertChain(done, blockchain, chain, t) + fmt.Println(i, "done") + }() + } + + for i := 0; i < max; i++ { + <-done + } + + if chains[longest][len(chains[longest])-1].Hash() != blockchain.CurrentBlock().Hash() { + t.Error("Invalid canonical chain") + } +} + +type bproc struct{} + +func (bproc) ValidateBlock(*types.Block) error { return nil } +func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } +func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { + return nil +} +func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) { + return nil, nil, new(big.Int), nil +} + +func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { + blocks := makeBlockChainWithDiff(genesis, d, seed) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + return headers +} + +func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { + var chain []*types.Block + for i, difficulty := range d { + header := &types.Header{ + Coinbase: common.Address{seed}, + Number: big.NewInt(int64(i + 1)), + Difficulty: big.NewInt(int64(difficulty)), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + } + if i == 0 { + header.ParentHash = genesis.Hash() + } else { + header.ParentHash = chain[i-1].Hash() + } + block := types.NewBlockWithHeader(header) + chain = append(chain, block) + } + return chain +} + +func chm(genesis *types.Block, db ethdb.Database) *BlockChain { + var eventMux event.TypeMux + bc := &BlockChain{ + chainDb: db, + genesisBlock: genesis, + eventMux: &eventMux, + pow: FakePow{}, + config: testChainConfig(), + } + valFn := func() HeaderValidator { return bc.Validator() } + bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt) + bc.bodyCache, _ = lru.New(100) + bc.bodyRLPCache, _ = lru.New(100) + bc.blockCache, _ = lru.New(100) + bc.futureBlocks, _ = lru.New(100) + bc.badBlocks, _ = lru.New(10) + bc.SetValidator(bproc{}) + bc.SetProcessor(bproc{}) + bc.ResetWithGenesisBlock(genesis) + + return bc +} + +// Tests that reorganising a long difficult chain after a short easy one +// overwrites the canonical numbers and links in the database. +func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) } +func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) } + +func testReorgLong(t *testing.T, full bool) { + testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10, full) +} + +// Tests that reorganising a short difficult chain after a long easy one +// overwrites the canonical numbers and links in the database. +func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) } +func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) } + +func testReorgShort(t *testing.T, full bool) { + testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11, full) +} + +func testReorg(t *testing.T, first, second []int, td int64, full bool) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Insert an easy and a difficult chain afterwards + if full { + bc.InsertChain(makeBlockChainWithDiff(genesis, first, 11)) + bc.InsertChain(makeBlockChainWithDiff(genesis, second, 22)) + } else { + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1) + } + // Check that the chain is valid number and link wise + if full { + prev := bc.CurrentBlock() + for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { + if prev.ParentHash() != block.Hash() { + t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash()) + } + } + } else { + prev := bc.CurrentHeader() + for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { + if prev.ParentHash != header.Hash() { + t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) + } + } + } + // Make sure the chain total difficulty is the correct one + want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td)) + if full { + if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", have, want) + } + } else { + if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", have, want) + } + } +} + +// Tests that the insertion functions detect banned hashes. +func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) } +func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) } + +func testBadHashes(t *testing.T, full bool) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Create a chain, ban a hash and try to import + var err error + if full { + blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 4}, 10) + BadHashes[blocks[2].Header().Hash()] = true + _, err = bc.InsertChain(blocks) + } else { + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10) + BadHashes[headers[2].Hash()] = true + _, err = bc.InsertHeaderChain(headers, 1) + } + if !IsBadHashError(err) { + t.Errorf("error mismatch: want: BadHashError, have: %v", err) + } +} + +// Tests that bad hashes are detected on boot, and the chain rolled back to a +// good state prior to the bad hash. +func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) } +func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) } + +func testReorgBadHashes(t *testing.T, full bool) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Create a chain, import and ban afterwards + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + + if full { + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import blocks: %v", err) + } + if bc.CurrentBlock().Hash() != blocks[3].Hash() { + t.Errorf("last block hash mismatch: have: %x, want %x", bc.CurrentBlock().Hash(), blocks[3].Header().Hash()) + } + BadHashes[blocks[3].Header().Hash()] = true + defer func() { delete(BadHashes, blocks[3].Header().Hash()) }() + } else { + if _, err := bc.InsertHeaderChain(headers, 1); err != nil { + t.Fatalf("failed to import headers: %v", err) + } + if bc.CurrentHeader().Hash() != headers[3].Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) + } + BadHashes[headers[3].Hash()] = true + defer func() { delete(BadHashes, headers[3].Hash()) }() + } + // Create a new chain manager and check it rolled back the state + ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + if err != nil { + t.Fatalf("failed to create new chain manager: %v", err) + } + if full { + if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() { + t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash()) + } + if blocks[2].Header().GasLimit.Cmp(ncm.GasLimit()) != 0 { + t.Errorf("last block gasLimit mismatch: have: %x, want %x", ncm.GasLimit(), blocks[2].Header().GasLimit) + } + } else { + if ncm.CurrentHeader().Hash() != headers[2].Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) + } + } +} + +// Tests chain insertions in the face of one entity containing an invalid nonce. +func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false) } +func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) } + +func testInsertNonceError(t *testing.T, full bool) { + for i := 1; i < 25 && !t.Failed(); i++ { + // Create a pristine chain and database + db, blockchain, err := newCanonical(0, full) + if err != nil { + t.Fatalf("failed to create pristine chain: %v", err) + } + // Create and insert a chain with a failing nonce + var ( + failAt int + failRes int + failNum uint64 + failHash common.Hash + ) + if full { + blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0) + + failAt = rand.Int() % len(blocks) + failNum = blocks[failAt].NumberU64() + failHash = blocks[failAt].Hash() + + blockchain.pow = failPow{failNum} + + failRes, err = blockchain.InsertChain(blocks) + } else { + headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0) + + failAt = rand.Int() % len(headers) + failNum = headers[failAt].Number.Uint64() + failHash = headers[failAt].Hash() + + blockchain.pow = failPow{failNum} + blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum}) + + failRes, err = blockchain.InsertHeaderChain(headers, 1) + } + // Check that the returned error indicates the nonce failure. + if failRes != failAt { + t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) + } + if !IsBlockNonceErr(err) { + t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err) + } + nerr := err.(*BlockNonceErr) + if nerr.Number.Uint64() != failNum { + t.Errorf("test %d: number mismatch: have %v, want %v", i, nerr.Number, failNum) + } + if nerr.Hash != failHash { + t.Errorf("test %d: hash mismatch: have %x, want %x", i, nerr.Hash[:4], failHash[:4]) + } + // Check that all no blocks after the failing block have been inserted. + for j := 0; j < i-failAt; j++ { + if full { + if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil { + t.Errorf("test %d: invalid block in chain: %v", i, block) + } + } else { + if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil { + t.Errorf("test %d: invalid header in chain: %v", i, header) + } + } + } + } +} + +// Tests that fast importing a block chain produces the same chain data as the +// classical full block processing. +func TestFastVsFullChains(t *testing.T) { + // Configure and generate a sample block chain + var ( + gendb, _ = ethdb.NewMemDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + genesis = GenesisBlockForTesting(gendb, address, funds) + signer = types.NewEIP155Signer(big.NewInt(1)) + ) + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) { + block.SetCoinbase(common.Address{0x00}) + + // If the block number is multiple of 3, send a few bonus transactions to the miner + if i%3 == 2 { + for j := 0; j < i%4+1; j++ { + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key) + if err != nil { + panic(err) + } + block.AddTx(tx) + } + } + // If the block number is a multiple of 5, add a few bonus uncles to the block + if i%5 == 5 { + block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) + } + }) + // Import the chain as an archive node for the comparison baseline + archiveDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) + + archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + + if n, err := archive.InsertChain(blocks); err != nil { + t.Fatalf("failed to process block %d: %v", n, err) + } + // Fast import the chain as a non-archive node to test + fastDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) + fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + if n, err := fast.InsertHeaderChain(headers, 1); err != nil { + t.Fatalf("failed to insert header %d: %v", n, err) + } + if n, err := fast.InsertReceiptChain(blocks, receipts); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + // Iterate over all chain data components, and cross reference + for i := 0; i < len(blocks); i++ { + num, hash := blocks[i].NumberU64(), blocks[i].Hash() + + if ftd, atd := fast.GetTdByHash(hash), archive.GetTdByHash(hash); ftd.Cmp(atd) != 0 { + t.Errorf("block #%d [%x]: td mismatch: have %v, want %v", num, hash, ftd, atd) + } + if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() { + t.Errorf("block #%d [%x]: header mismatch: have %v, want %v", num, hash, fheader, aheader) + } + if fblock, ablock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash); fblock.Hash() != ablock.Hash() { + t.Errorf("block #%d [%x]: block mismatch: have %v, want %v", num, hash, fblock, ablock) + } else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(ablock.Transactions()) { + t.Errorf("block #%d [%x]: transactions mismatch: have %v, want %v", num, hash, fblock.Transactions(), ablock.Transactions()) + } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) { + t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles()) + } + if freceipts, areceipts := GetBlockReceipts(fastDb, hash, GetBlockNumber(fastDb, hash)), GetBlockReceipts(archiveDb, hash, GetBlockNumber(archiveDb, hash)); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) { + t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts) + } + } + // Check that the canonical chains are the same between the databases + for i := 0; i < len(blocks)+1; i++ { + if fhash, ahash := GetCanonicalHash(fastDb, uint64(i)), GetCanonicalHash(archiveDb, uint64(i)); fhash != ahash { + t.Errorf("block #%d: canonical hash mismatch: have %v, want %v", i, fhash, ahash) + } + } +} + +// Tests that various import methods move the chain head pointers to the correct +// positions. +func TestLightVsFastVsFullChainHeads(t *testing.T) { + // Configure and generate a sample block chain + var ( + gendb, _ = ethdb.NewMemDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + genesis = GenesisBlockForTesting(gendb, address, funds) + ) + height := uint64(1024) + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil) + + // Configure a subchain to roll back + remove := []common.Hash{} + for _, block := range blocks[height/2:] { + remove = append(remove, block.Hash()) + } + // Create a small assertion method to check the three heads + assert := func(t *testing.T, kind string, chain *BlockChain, header uint64, fast uint64, block uint64) { + if num := chain.CurrentBlock().NumberU64(); num != block { + t.Errorf("%s head block mismatch: have #%v, want #%v", kind, num, block) + } + if num := chain.CurrentFastBlock().NumberU64(); num != fast { + t.Errorf("%s head fast-block mismatch: have #%v, want #%v", kind, num, fast) + } + if num := chain.CurrentHeader().Number.Uint64(); num != header { + t.Errorf("%s head header mismatch: have #%v, want #%v", kind, num, header) + } + } + // Import the chain as an archive node and ensure all pointers are updated + archiveDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) + + archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + + if n, err := archive.InsertChain(blocks); err != nil { + t.Fatalf("failed to process block %d: %v", n, err) + } + assert(t, "archive", archive, height, height, height) + archive.Rollback(remove) + assert(t, "archive", archive, height/2, height/2, height/2) + + // Import the chain as a non-archive node and ensure all pointers are updated + fastDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) + fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + if n, err := fast.InsertHeaderChain(headers, 1); err != nil { + t.Fatalf("failed to insert header %d: %v", n, err) + } + if n, err := fast.InsertReceiptChain(blocks, receipts); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + assert(t, "fast", fast, height, height, 0) + fast.Rollback(remove) + assert(t, "fast", fast, height/2, height/2, 0) + + // Import the chain as a light node and ensure all pointers are updated + lightDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) + light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{}) + + if n, err := light.InsertHeaderChain(headers, 1); err != nil { + t.Fatalf("failed to insert header %d: %v", n, err) + } + assert(t, "light", light, height, 0, 0) + light.Rollback(remove) + assert(t, "light", light, height/2, 0, 0) +} + +// Tests that chain reorganisations handle transaction removals and reinsertions. +func TestChainTxReorgs(t *testing.T) { + params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. + params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. + + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + addr3 = crypto.PubkeyToAddress(key3.PublicKey) + db, _ = ethdb.NewMemDatabase() + signer = types.NewEIP155Signer(big.NewInt(1)) + ) + genesis := WriteGenesisBlockForTesting(db, + GenesisAccount{addr1, big.NewInt(1000000)}, + GenesisAccount{addr2, big.NewInt(1000000)}, + GenesisAccount{addr3, big.NewInt(1000000)}, + ) + // Create two transactions shared between the chains: + // - postponed: transaction included at a later block in the forked chain + // - swapped: transaction included at the same block number in the forked chain + postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) + swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) + + // Create two transactions that will be dropped by the forked chain: + // - pastDrop: transaction dropped retroactively from a past block + // - freshDrop: transaction dropped exactly at the block where the reorg is detected + var pastDrop, freshDrop *types.Transaction + + // Create three transactions that will be added in the forked chain: + // - pastAdd: transaction added before the reorganization is detected + // - freshAdd: transaction added at the exact block the reorg is detected + // - futureAdd: transaction added after the reorg has already finished + var pastAdd, freshAdd, futureAdd *types.Transaction + + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) { + switch i { + case 0: + pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) + + gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point + gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork + + case 2: + freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) + + gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point + gen.AddTx(swapped) // This transaction will be swapped out at the exact height + + gen.OffsetTime(9) // Lower the block difficulty to simulate a weaker chain + } + }) + // Import the chain. This runs all block validation rules. + evmux := &event.TypeMux{} + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{}) + if i, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert original chain[%d]: %v", i, err) + } + + // overwrite the old chain + chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) { + switch i { + case 0: + pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) + gen.AddTx(pastAdd) // This transaction needs to be injected during reorg + + case 2: + gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain + gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain + + freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) + gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time + + case 3: + futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) + gen.AddTx(futureAdd) // This transaction will be added after a full reorg + } + }) + if _, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert forked chain: %v", err) + } + + // removed tx + for i, tx := range (types.Transactions{pastDrop, freshDrop}) { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn) + } + if GetReceipt(db, tx.Hash()) != nil { + t.Errorf("drop %d: receipt found while shouldn't have been", i) + } + } + // added tx + for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil { + t.Errorf("add %d: expected tx to be found", i) + } + if GetReceipt(db, tx.Hash()) == nil { + t.Errorf("add %d: expected receipt to be found", i) + } + } + // shared tx + for i, tx := range (types.Transactions{postponed, swapped}) { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil { + t.Errorf("share %d: expected tx to be found", i) + } + if GetReceipt(db, tx.Hash()) == nil { + t.Errorf("share %d: expected receipt to be found", i) + } + } +} + +func TestLogReorgs(t *testing.T) { + params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. + params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. + + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + db, _ = ethdb.NewMemDatabase() + // this code generates a log + code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") + signer = types.NewEIP155Signer(big.NewInt(1)) + ) + genesis := WriteGenesisBlockForTesting(db, + GenesisAccount{addr1, big.NewInt(10000000000000)}, + ) + + evmux := &event.TypeMux{} + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{}) + + subs := evmux.Subscribe(RemovedLogsEvent{}) + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) { + if i == 1 { + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code), signer, key1) + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + gen.AddTx(tx) + } + }) + if _, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert chain: %v", err) + } + + chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {}) + if _, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert forked chain: %v", err) + } + + ev := <-subs.Chan() + if len(ev.Data.(RemovedLogsEvent).Logs) == 0 { + t.Error("expected logs") + } +} + +func TestReorgSideEvent(t *testing.T) { + var ( + db, _ = ethdb.NewMemDatabase() + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + genesis = WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(10000000000000)}) + signer = types.NewEIP155Signer(big.NewInt(1)) + ) + + evmux := &event.TypeMux{} + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{}) + + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {}) + if _, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert chain: %v", err) + } + + replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) { + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1) + if i == 2 { + gen.OffsetTime(-1) + } + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + gen.AddTx(tx) + }) + subs := evmux.Subscribe(ChainSideEvent{}) + if _, err := blockchain.InsertChain(replacementBlocks); err != nil { + t.Fatalf("failed to insert chain: %v", err) + } + + // first two block of the secondary chain are for a brief moment considered + // side chains because up to that point the first one is considered the + // heavier chain. + expectedSideHashes := map[common.Hash]bool{ + replacementBlocks[0].Hash(): true, + replacementBlocks[1].Hash(): true, + chain[0].Hash(): true, + chain[1].Hash(): true, + chain[2].Hash(): true, + } + + i := 0 + + const timeoutDura = 10 * time.Second + timeout := time.NewTimer(timeoutDura) +done: + for { + select { + case ev := <-subs.Chan(): + block := ev.Data.(ChainSideEvent).Block + if _, ok := expectedSideHashes[block.Hash()]; !ok { + t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash()) + } + i++ + + if i == len(expectedSideHashes) { + timeout.Stop() + + break done + } + timeout.Reset(timeoutDura) + + case <-timeout.C: + t.Fatal("Timeout. Possibly not all blocks were triggered for sideevent") + } + } + + // make sure no more events are fired + select { + case e := <-subs.Chan(): + t.Errorf("unexpected event fired: %v", e) + case <-time.After(250 * time.Millisecond): + } + +} + +// Tests if the canonical block can be fetched from the database during chain insertion. +func TestCanonicalBlockRetrieval(t *testing.T) { + var ( + db, _ = ethdb.NewMemDatabase() + genesis = WriteGenesisBlockForTesting(db) + ) + + evmux := &event.TypeMux{} + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{}) + + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {}) + + for i := range chain { + go func(block *types.Block) { + // try to retrieve a block by its canonical hash and see if the block data can be retrieved. + for { + ch := GetCanonicalHash(db, block.NumberU64()) + if ch == (common.Hash{}) { + continue // busy wait for canonical hash to be written + } + if ch != block.Hash() { + t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) + } + fb := GetBlock(db, ch, block.NumberU64()) + if fb == nil { + t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) + } + if fb.Hash() != block.Hash() { + t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex()) + } + return + } + }(chain[i]) + + blockchain.InsertChain(types.Blocks{chain[i]}) + } +} + +func TestEIP155Transition(t *testing.T) { + // Configure and generate a sample block chain + var ( + db, _ = ethdb.NewMemDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + deleteAddr = common.Address{1} + genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)}) + config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} + mux event.TypeMux + ) + + blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux, vm.Config{}) + blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) { + var ( + tx *types.Transaction + err error + basicTx = func(signer types.Signer) (*types.Transaction, error) { + return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key) + } + ) + switch i { + case 0: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + case 2: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + + tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + case 3: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + + tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + } + }) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } + block := blockchain.GetBlockByNumber(1) + if block.Transactions()[0].Protected() { + t.Error("Expected block[0].txs[0] to not be replay protected") + } + + block = blockchain.GetBlockByNumber(3) + if block.Transactions()[0].Protected() { + t.Error("Expected block[3].txs[0] to not be replay protected") + } + if !block.Transactions()[1].Protected() { + t.Error("Expected block[3].txs[1] to be replay protected") + } + if _, err := blockchain.InsertChain(blocks[4:]); err != nil { + t.Fatal(err) + } + + // generate an invalid chain id transaction + config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} + blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) { + var ( + tx *types.Transaction + err error + basicTx = func(signer types.Signer) (*types.Transaction, error) { + return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key) + } + ) + switch i { + case 0: + tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2))) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + } + }) + _, err := blockchain.InsertChain(blocks) + if err != types.ErrInvalidChainId { + t.Error("expected error:", types.ErrInvalidChainId) + } +} + +func TestEIP161AccountRemoval(t *testing.T) { + // Configure and generate a sample block chain + var ( + db, _ = ethdb.NewMemDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + theAddr = common.Address{1} + genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}) + config = ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: big.NewInt(2), + } + mux event.TypeMux + + blockchain, _ = NewBlockChain(db, config, FakePow{}, &mux, vm.Config{}) + ) + blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) { + var ( + tx *types.Transaction + err error + signer = types.NewEIP155Signer(config.ChainId) + ) + switch i { + case 0: + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key) + case 1: + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key) + case 2: + tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key) + } + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + }) + // account must exist pre eip 161 + if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil { + t.Fatal(err) + } + if !blockchain.stateCache.Exist(theAddr) { + t.Error("expected account to exist") + } + + // account needs to be deleted post eip 161 + if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil { + t.Fatal(err) + } + if blockchain.stateCache.Exist(theAddr) { + t.Error("account should not expect") + } + + // account musn't be created post eip 161 + if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil { + t.Fatal(err) + } + if blockchain.stateCache.Exist(theAddr) { + t.Error("account should not expect") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/blocks.go b/vendor/github.com/ethereum/go-ethereum/core/blocks.go new file mode 100644 index 0000000..cf8c865 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/blocks.go @@ -0,0 +1,25 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import "github.com/ethereum/go-ethereum/common" + +// Set of manually tracked bad hashes (usually hard forks) +var BadHashes = map[common.Hash]bool{ + common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true, + common.HexToHash("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f"): true, +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go new file mode 100644 index 0000000..8b3b015 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go @@ -0,0 +1,292 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" +) + +/* + * TODO: move this to another package. + */ + +// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. +func MakeChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + } +} + +// FakePow is a non-validating proof of work implementation. +// It returns true from Verify for any block. +type FakePow struct{} + +func (f FakePow) Search(block pow.Block, stop <-chan struct{}, index int) (uint64, []byte) { + return 0, nil +} +func (f FakePow) Verify(block pow.Block) bool { return true } +func (f FakePow) GetHashrate() int64 { return 0 } +func (f FakePow) Turbo(bool) {} + +// So we can deterministically seed different blockchains +var ( + canonicalSeed = 1 + forkSeed = 2 +) + +// BlockGen creates blocks for testing. +// See GenerateChain for a detailed explanation. +type BlockGen struct { + i int + parent *types.Block + chain []*types.Block + header *types.Header + statedb *state.StateDB + + gasPool *GasPool + txs []*types.Transaction + receipts []*types.Receipt + uncles []*types.Header + + config *params.ChainConfig +} + +// SetCoinbase sets the coinbase of the generated block. +// It can be called at most once. +func (b *BlockGen) SetCoinbase(addr common.Address) { + if b.gasPool != nil { + if len(b.txs) > 0 { + panic("coinbase must be set before adding transactions") + } + panic("coinbase can only be set once") + } + b.header.Coinbase = addr + b.gasPool = new(GasPool).AddGas(b.header.GasLimit) +} + +// SetExtra sets the extra data field of the generated block. +func (b *BlockGen) SetExtra(data []byte) { + b.header.Extra = data +} + +// AddTx adds a transaction to the generated block. If no coinbase has +// been set, the block's coinbase is set to the zero address. +// +// AddTx panics if the transaction cannot be executed. In addition to +// the protocol-imposed limitations (gas limit, etc.), there are some +// further limitations on the content of transactions that can be +// added. Notably, contract code relying on the BLOCKHASH instruction +// will panic during execution. +func (b *BlockGen) AddTx(tx *types.Transaction) { + if b.gasPool == nil { + b.SetCoinbase(common.Address{}) + } + b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) + receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + if err != nil { + panic(err) + } + b.txs = append(b.txs, tx) + b.receipts = append(b.receipts, receipt) +} + +// Number returns the block number of the block being generated. +func (b *BlockGen) Number() *big.Int { + return new(big.Int).Set(b.header.Number) +} + +// AddUncheckedReceipts forcefully adds a receipts to the block without a +// backing transaction. +// +// AddUncheckedReceipts will cause consensus failures when used during real +// chain processing. This is best used in conjunction with raw block insertion. +func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) { + b.receipts = append(b.receipts, receipt) +} + +// TxNonce returns the next valid transaction nonce for the +// account at addr. It panics if the account does not exist. +func (b *BlockGen) TxNonce(addr common.Address) uint64 { + if !b.statedb.Exist(addr) { + panic("account does not exist") + } + return b.statedb.GetNonce(addr) +} + +// AddUncle adds an uncle header to the generated block. +func (b *BlockGen) AddUncle(h *types.Header) { + b.uncles = append(b.uncles, h) +} + +// PrevBlock returns a previously generated block by number. It panics if +// num is greater or equal to the number of the block being generated. +// For index -1, PrevBlock returns the parent block given to GenerateChain. +func (b *BlockGen) PrevBlock(index int) *types.Block { + if index >= b.i { + panic("block index out of range") + } + if index == -1 { + return b.parent + } + return b.chain[index] +} + +// OffsetTime modifies the time instance of a block, implicitly changing its +// associated difficulty. It's useful to test scenarios where forking is not +// tied to chain length directly. +func (b *BlockGen) OffsetTime(seconds int64) { + b.header.Time.Add(b.header.Time, new(big.Int).SetInt64(seconds)) + if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { + panic("block time out of range") + } + b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) +} + +// GenerateChain creates a chain of n blocks. The first block's +// parent will be the provided parent. db is used to store +// intermediate states and should contain the parent's state trie. +// +// The generator function is called with a new block generator for +// every block. Any transactions and uncles added to the generator +// become part of the block. If gen is nil, the blocks will be empty +// and their coinbase will be the zero address. +// +// Blocks created by GenerateChain do not contain valid proof of work +// values. Inserting them into BlockChain requires use of FakePow or +// a similar non-validating proof of work implementation. +func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { + blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) + genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { + b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config} + + // Mutate the state and block according to any hard-fork specs + if config == nil { + config = MakeChainConfig() + } + if daoBlock := config.DAOForkBlock; daoBlock != nil { + limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) + if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 { + if config.DAOForkSupport { + h.Extra = common.CopyBytes(params.DAOForkBlockExtra) + } + } + } + if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 { + ApplyDAOHardFork(statedb) + } + // Execute any user modifications to the block and finalize it + if gen != nil { + gen(i, b) + } + AccumulateRewards(statedb, h, b.uncles) + root, err := statedb.Commit(config.IsEIP158(h.Number)) + if err != nil { + panic(fmt.Sprintf("state write error: %v", err)) + } + h.Root = root + return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts + } + for i := 0; i < n; i++ { + statedb, err := state.New(parent.Root(), db) + if err != nil { + panic(err) + } + header := makeHeader(config, parent, statedb) + block, receipt := genblock(i, header, statedb) + blocks[i] = block + receipts[i] = receipt + parent = block + } + return blocks, receipts +} + +func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header { + var time *big.Int + if parent.Time() == nil { + time = big.NewInt(10) + } else { + time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds + } + return &types.Header{ + Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), + ParentHash: parent.Hash(), + Coinbase: parent.Coinbase(), + Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), + GasLimit: CalcGasLimit(parent), + GasUsed: new(big.Int), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: time, + } +} + +// newCanonical creates a chain database, and injects a deterministic canonical +// chain. Depending on the full flag, if creates either a full block chain or a +// header only chain. +func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { + // Create the new chain database + db, _ := ethdb.NewMemDatabase() + evmux := &event.TypeMux{} + + // Initialize a fresh chain with only a genesis block + genesis, _ := WriteTestNetGenesisBlock(db) + + blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux, vm.Config{}) + // Create and inject the requested chain + if n == 0 { + return db, blockchain, nil + } + if full { + // Full block-chain requested + blocks := makeBlockChain(genesis, n, db, canonicalSeed) + _, err := blockchain.InsertChain(blocks) + return db, blockchain, err + } + // Header-only chain requested + headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) + _, err := blockchain.InsertHeaderChain(headers, 1) + return db, blockchain, err +} + +// makeHeaderChain creates a deterministic chain of headers rooted at parent. +func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { + blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, db, seed) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + return headers +} + +// makeBlockChain creates a deterministic chain of blocks rooted at parent. +func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block { + blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) + }) + return blocks +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_makers_test.go b/vendor/github.com/ethereum/go-ethereum/core/chain_makers_test.go new file mode 100644 index 0000000..5c563de --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_makers_test.go @@ -0,0 +1,101 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +func ExampleGenerateChain() { + params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. + params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. + + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + addr3 = crypto.PubkeyToAddress(key3.PublicKey) + db, _ = ethdb.NewMemDatabase() + signer = types.HomesteadSigner{} + ) + + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + } + // Ensure that key1 has some funds in the genesis block. + genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)}) + + // This call generates a chain of 5 blocks. The function runs for + // each block and adds different features to gen based on the + // block index. + chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) { + switch i { + case 0: + // In block 1, addr1 sends addr2 some ether. + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1) + gen.AddTx(tx) + case 1: + // In block 2, addr1 sends some more ether to addr2. + // addr2 passes it on to addr3. + tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1) + tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) + gen.AddTx(tx1) + gen.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by addr3. + gen.SetCoinbase(addr3) + gen.SetExtra([]byte("yeehaw")) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := gen.PrevBlock(1).Header() + b2.Extra = []byte("foo") + gen.AddUncle(b2) + b3 := gen.PrevBlock(2).Header() + b3.Extra = []byte("foo") + gen.AddUncle(b3) + } + }) + + // Import the chain. This runs all block validation rules. + evmux := &event.TypeMux{} + blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux, vm.Config{}) + if i, err := blockchain.InsertChain(chain); err != nil { + fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err) + return + } + + state, _ := blockchain.State() + fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number()) + fmt.Println("balance of addr1:", state.GetBalance(addr1)) + fmt.Println("balance of addr2:", state.GetBalance(addr2)) + fmt.Println("balance of addr3:", state.GetBalance(addr3)) + // Output: + // last block: #5 + // balance of addr1: 989000 + // balance of addr2: 10000 + // balance of addr3: 19687500000000001000 +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_pow.go b/vendor/github.com/ethereum/go-ethereum/core/chain_pow.go new file mode 100644 index 0000000..c3b5788 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_pow.go @@ -0,0 +1,87 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "runtime" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/pow" +) + +// nonceCheckResult contains the result of a nonce verification. +type nonceCheckResult struct { + index int // Index of the item verified from an input array + valid bool // Result of the nonce verification +} + +// verifyNoncesFromHeaders starts a concurrent header nonce verification, +// returning a quit channel to abort the operations and a results channel +// to retrieve the async verifications. +func verifyNoncesFromHeaders(checker pow.PoW, headers []*types.Header) (chan<- struct{}, <-chan nonceCheckResult) { + items := make([]pow.Block, len(headers)) + for i, header := range headers { + items[i] = types.NewBlockWithHeader(header) + } + return verifyNonces(checker, items) +} + +// verifyNoncesFromBlocks starts a concurrent block nonce verification, +// returning a quit channel to abort the operations and a results channel +// to retrieve the async verifications. +func verifyNoncesFromBlocks(checker pow.PoW, blocks []*types.Block) (chan<- struct{}, <-chan nonceCheckResult) { + items := make([]pow.Block, len(blocks)) + for i, block := range blocks { + items[i] = block + } + return verifyNonces(checker, items) +} + +// verifyNonces starts a concurrent nonce verification, returning a quit channel +// to abort the operations and a results channel to retrieve the async checks. +func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan nonceCheckResult) { + // Spawn as many workers as allowed threads + workers := runtime.GOMAXPROCS(0) + if len(items) < workers { + workers = len(items) + } + // Create a task channel and spawn the verifiers + tasks := make(chan int, workers) + results := make(chan nonceCheckResult, len(items)) // Buffered to make sure all workers stop + for i := 0; i < workers; i++ { + go func() { + for index := range tasks { + results <- nonceCheckResult{index: index, valid: checker.Verify(items[index])} + } + }() + } + // Feed item indices to the workers until done or aborted + abort := make(chan struct{}) + go func() { + defer close(tasks) + + for i := range items { + select { + case tasks <- i: + continue + case <-abort: + return + } + } + }() + return abort, results +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_pow_test.go b/vendor/github.com/ethereum/go-ethereum/core/chain_pow_test.go new file mode 100644 index 0000000..1400b16 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_pow_test.go @@ -0,0 +1,234 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "runtime" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" +) + +// failPow is a non-validating proof of work implementation, that returns true +// from Verify for all but one block. +type failPow struct { + failing uint64 +} + +func (pow failPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) { + return 0, nil +} +func (pow failPow) Verify(block pow.Block) bool { return block.NumberU64() != pow.failing } +func (pow failPow) GetHashrate() int64 { return 0 } +func (pow failPow) Turbo(bool) {} + +// delayedPow is a non-validating proof of work implementation, that returns true +// from Verify for all blocks, but delays them the configured amount of time. +type delayedPow struct { + delay time.Duration +} + +func (pow delayedPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) { + return 0, nil +} +func (pow delayedPow) Verify(block pow.Block) bool { time.Sleep(pow.delay); return true } +func (pow delayedPow) GetHashrate() int64 { return 0 } +func (pow delayedPow) Turbo(bool) {} + +// Tests that simple POW verification works, for both good and bad blocks. +func TestPowVerification(t *testing.T) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) + ) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + // Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces + for i := 0; i < len(blocks); i++ { + for j, full := range []bool{true, false} { + for k, valid := range []bool{true, false} { + var results <-chan nonceCheckResult + + switch { + case full && valid: + _, results = verifyNoncesFromBlocks(FakePow{}, []*types.Block{blocks[i]}) + case full && !valid: + _, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]}) + case !full && valid: + _, results = verifyNoncesFromHeaders(FakePow{}, []*types.Header{headers[i]}) + case !full && !valid: + _, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]}) + } + // Wait for the verification result + select { + case result := <-results: + if result.index != 0 { + t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index) + } + if result.valid != valid { + t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid) + } + case <-time.After(time.Second): + t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result) + case <-time.After(25 * time.Millisecond): + } + } + } + } +} + +// Tests that concurrent POW verification works, for both good and bad blocks. +func TestPowConcurrentVerification2(t *testing.T) { testPowConcurrentVerification(t, 2) } +func TestPowConcurrentVerification8(t *testing.T) { testPowConcurrentVerification(t, 8) } +func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) } + +func testPowConcurrentVerification(t *testing.T, threads int) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) + ) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + // Set the number of threads to verify on + old := runtime.GOMAXPROCS(threads) + defer runtime.GOMAXPROCS(old) + + // Run the POW checker for the entire block chain at once both for a valid and + // also an invalid chain (enough if one is invalid, last but one (arbitrary)). + for i, full := range []bool{true, false} { + for j, valid := range []bool{true, false} { + var results <-chan nonceCheckResult + + switch { + case full && valid: + _, results = verifyNoncesFromBlocks(FakePow{}, blocks) + case full && !valid: + _, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks) + case !full && valid: + _, results = verifyNoncesFromHeaders(FakePow{}, headers) + case !full && !valid: + _, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers) + } + // Wait for all the verification results + checks := make(map[int]bool) + for k := 0; k < len(blocks); k++ { + select { + case result := <-results: + if _, ok := checks[result.index]; ok { + t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index) + } + if result.index < 0 || result.index >= len(blocks) { + t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1) + } + checks[result.index] = result.valid + + case <-time.After(time.Second): + t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) + } + } + // Check nonce check validity + for k := 0; k < len(blocks); k++ { + want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail + if checks[k] != want { + t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want) + } + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) + case <-time.After(25 * time.Millisecond): + } + } + } +} + +// Tests that aborting a POW validation indeed prevents further checks from being +// run, as well as checks that no left-over goroutines are leaked. +func TestPowConcurrentAbortion2(t *testing.T) { testPowConcurrentAbortion(t, 2) } +func TestPowConcurrentAbortion8(t *testing.T) { testPowConcurrentAbortion(t, 8) } +func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) } + +func testPowConcurrentAbortion(t *testing.T, threads int) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil) + ) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + // Set the number of threads to verify on + old := runtime.GOMAXPROCS(threads) + defer runtime.GOMAXPROCS(old) + + // Run the POW checker for the entire block chain at once + for i, full := range []bool{true, false} { + var abort chan<- struct{} + var results <-chan nonceCheckResult + + // Start the verifications and immediately abort + if full { + abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks) + } else { + abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers) + } + close(abort) + + // Deplete the results channel + verified := make(map[int]struct{}) + for depleted := false; !depleted; { + select { + case result := <-results: + verified[result.index] = struct{}{} + case <-time.After(50 * time.Millisecond): + depleted = true + } + } + // Check that abortion was honored by not processing too many POWs + if len(verified) > 2*threads { + t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads) + } + // Check that there are no gaps in the results + for j := 0; j < len(verified); j++ { + if _, ok := verified[j]; !ok { + t.Errorf("test %d.%d: gap found in verification results", i, j) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/dao.go b/vendor/github.com/ethereum/go-ethereum/core/dao.go new file mode 100644 index 0000000..7e376e6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/dao.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "bytes" + "math/big" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +// ValidateDAOHeaderExtraData validates the extra-data field of a block header to +// ensure it conforms to DAO hard-fork rules. +// +// DAO hard-fork extension to the header validity: +// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range +// with the fork specific extra-data set +// b) if the node is pro-fork, require blocks in the specific range to have the +// unique extra-data set. +func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { + // Short circuit validation if the node doesn't care about the DAO fork + if config.DAOForkBlock == nil { + return nil + } + // Make sure the block is within the fork's modified extra-data range + limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange) + if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 { + return nil + } + // Depending whether we support or oppose the fork, validate the extra-data contents + if config.DAOForkSupport { + if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) { + return ValidationError("DAO pro-fork bad block extra-data: 0x%x", header.Extra) + } + } else { + if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { + return ValidationError("DAO no-fork bad block extra-data: 0x%x", header.Extra) + } + } + // All ok, header has the same extra-data we expect + return nil +} + +// ApplyDAOHardFork modifies the state database according to the DAO hard-fork +// rules, transferring all balances of a set of DAO accounts to a single refund +// contract. +func ApplyDAOHardFork(statedb *state.StateDB) { + // Retrieve the contract to refund balances into + if !statedb.Exist(params.DAORefundContract) { + statedb.CreateAccount(params.DAORefundContract) + } + + // Move every DAO account and extra-balance account funds into the refund contract + for _, addr := range params.DAODrainList { + statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) + statedb.SetBalance(addr, new(big.Int)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/dao_test.go b/vendor/github.com/ethereum/go-ethereum/core/dao_test.go new file mode 100644 index 0000000..b8b4c71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/dao_test.go @@ -0,0 +1,133 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +// Tests that DAO-fork enabled clients can properly filter out fork-commencing +// blocks based on their extradata fields. +func TestDAOForkRangeExtradata(t *testing.T) { + forkBlock := big.NewInt(32) + + // Generate a common prefix for both pro-forkers and non-forkers + db, _ := ethdb.NewMemDatabase() + genesis := WriteGenesisBlockForTesting(db) + prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) + + // Create the concurrent, conflicting two nodes + proDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(proDb) + proConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true} + proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + conDb, _ := ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(conDb) + conConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false} + conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + if _, err := proBc.InsertChain(prefix); err != nil { + t.Fatalf("pro-fork: failed to import chain prefix: %v", err) + } + if _, err := conBc.InsertChain(prefix); err != nil { + t.Fatalf("con-fork: failed to import chain prefix: %v", err) + } + // Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks + for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { + // Create a pro-fork block, and try to feed into the no-fork chain + db, _ = ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(db) + bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) + for j := 0; j < len(blocks)/2; j++ { + blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] + } + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import contra-fork chain for expansion: %v", err) + } + blocks, _ = GenerateChain(proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := conBc.InsertChain(blocks); err == nil { + t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) + } + // Create a proper no-fork block for the contra-forker + blocks, _ = GenerateChain(conConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := conBc.InsertChain(blocks); err != nil { + t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err) + } + // Create a no-fork block, and try to feed into the pro-fork chain + db, _ = ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(db) + bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) + for j := 0; j < len(blocks)/2; j++ { + blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] + } + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import pro-fork chain for expansion: %v", err) + } + blocks, _ = GenerateChain(conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := proBc.InsertChain(blocks); err == nil { + t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) + } + // Create a proper pro-fork block for the pro-forker + blocks, _ = GenerateChain(proConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := proBc.InsertChain(blocks); err != nil { + t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err) + } + } + // Verify that contra-forkers accept pro-fork extra-datas after forking finishes + db, _ = ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(db) + bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) + for j := 0; j < len(blocks)/2; j++ { + blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] + } + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import contra-fork chain for expansion: %v", err) + } + blocks, _ = GenerateChain(proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := conBc.InsertChain(blocks); err != nil { + t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) + } + // Verify that pro-forkers accept contra-fork extra-datas after forking finishes + db, _ = ethdb.NewMemDatabase() + WriteGenesisBlockForTesting(db) + bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) + + blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) + for j := 0; j < len(blocks)/2; j++ { + blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] + } + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import pro-fork chain for expansion: %v", err) + } + blocks, _ = GenerateChain(conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {}) + if _, err := proBc.InsertChain(blocks); err != nil { + t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/database_util.go b/vendor/github.com/ethereum/go-ethereum/core/database_util.go new file mode 100644 index 0000000..e83d5d5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/database_util.go @@ -0,0 +1,701 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + headHeaderKey = []byte("LastHeader") + headBlockKey = []byte("LastBlock") + headFastKey = []byte("LastFast") + + headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header + tdSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td + numSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + numSuffix -> hash + blockHashPrefix = []byte("H") // blockHashPrefix + hash -> num (uint64 big endian) + bodyPrefix = []byte("b") // bodyPrefix + num (uint64 big endian) + hash -> block body + blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts + preimagePrefix = "secure-key-" // preimagePrefix + hash -> preimage + + txMetaSuffix = []byte{0x01} + receiptsPrefix = []byte("receipts-") + + mipmapPre = []byte("mipmap-log-bloom-") + MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000} + + configPrefix = []byte("ethereum-config-") // config prefix for the db + + // used by old (non-sequential keys) db, now only used for conversion + oldBlockPrefix = []byte("block-") + oldHeaderSuffix = []byte("-header") + oldTdSuffix = []byte("-td") // headerPrefix + num (uint64 big endian) + hash + tdSuffix -> td + oldBodySuffix = []byte("-body") + oldBlockNumPrefix = []byte("block-num-") + oldBlockReceiptsPrefix = []byte("receipts-block-") + oldBlockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] + + ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error + + mipmapBloomMu sync.Mutex // protect against race condition when updating mipmap blooms + + preimageCounter = metrics.NewCounter("db/preimage/total") + preimageHitCounter = metrics.NewCounter("db/preimage/hits") +) + +// encodeBlockNumber encodes a block number as big endian uint64 +func encodeBlockNumber(number uint64) []byte { + enc := make([]byte, 8) + binary.BigEndian.PutUint64(enc, number) + return enc +} + +// GetCanonicalHash retrieves a hash assigned to a canonical block number. +func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash { + data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...)) + if len(data) == 0 { + data, _ = db.Get(append(oldBlockNumPrefix, big.NewInt(int64(number)).Bytes()...)) + if len(data) == 0 { + return common.Hash{} + } + } + return common.BytesToHash(data) +} + +// missingNumber is returned by GetBlockNumber if no header with the +// given block hash has been stored in the database +const missingNumber = uint64(0xffffffffffffffff) + +// GetBlockNumber returns the block number assigned to a block hash +// if the corresponding header is present in the database +func GetBlockNumber(db ethdb.Database, hash common.Hash) uint64 { + data, _ := db.Get(append(blockHashPrefix, hash.Bytes()...)) + if len(data) != 8 { + data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) + if len(data) == 0 { + return missingNumber + } + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + glog.Fatalf("failed to decode block header: %v", err) + } + return header.Number.Uint64() + } + return binary.BigEndian.Uint64(data) +} + +// GetHeadHeaderHash retrieves the hash of the current canonical head block's +// header. The difference between this and GetHeadBlockHash is that whereas the +// last block hash is only updated upon a full block import, the last header +// hash is updated already at header import, allowing head tracking for the +// light synchronization mechanism. +func GetHeadHeaderHash(db ethdb.Database) common.Hash { + data, _ := db.Get(headHeaderKey) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + +// GetHeadBlockHash retrieves the hash of the current canonical head block. +func GetHeadBlockHash(db ethdb.Database) common.Hash { + data, _ := db.Get(headBlockKey) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + +// GetHeadFastBlockHash retrieves the hash of the current canonical head block during +// fast synchronization. The difference between this and GetHeadBlockHash is that +// whereas the last block hash is only updated upon a full block import, the last +// fast hash is updated when importing pre-processed blocks. +func GetHeadFastBlockHash(db ethdb.Database) common.Hash { + data, _ := db.Get(headFastKey) + if len(data) == 0 { + return common.Hash{} + } + return common.BytesToHash(data) +} + +// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil +// if the header's not found. +func GetHeaderRLP(db ethdb.Database, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Get(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) + if len(data) == 0 { + data, _ = db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) + } + return data +} + +// GetHeader retrieves the block header corresponding to the hash, nil if none +// found. +func GetHeader(db ethdb.Database, hash common.Hash, number uint64) *types.Header { + data := GetHeaderRLP(db, hash, number) + if len(data) == 0 { + return nil + } + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + glog.V(logger.Error).Infof("invalid block header RLP for hash %x: %v", hash, err) + return nil + } + return header +} + +// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. +func GetBodyRLP(db ethdb.Database, hash common.Hash, number uint64) rlp.RawValue { + data, _ := db.Get(append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) + if len(data) == 0 { + data, _ = db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldBodySuffix...)) + } + return data +} + +// GetBody retrieves the block body (transactons, uncles) corresponding to the +// hash, nil if none found. +func GetBody(db ethdb.Database, hash common.Hash, number uint64) *types.Body { + data := GetBodyRLP(db, hash, number) + if len(data) == 0 { + return nil + } + body := new(types.Body) + if err := rlp.Decode(bytes.NewReader(data), body); err != nil { + glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err) + return nil + } + return body +} + +// GetTd retrieves a block's total difficulty corresponding to the hash, nil if +// none found. +func GetTd(db ethdb.Database, hash common.Hash, number uint64) *big.Int { + data, _ := db.Get(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash[:]...), tdSuffix...)) + if len(data) == 0 { + data, _ = db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldTdSuffix...)) + if len(data) == 0 { + return nil + } + } + td := new(big.Int) + if err := rlp.Decode(bytes.NewReader(data), td); err != nil { + glog.V(logger.Error).Infof("invalid block total difficulty RLP for hash %x: %v", hash, err) + return nil + } + return td +} + +// GetBlock retrieves an entire block corresponding to the hash, assembling it +// back from the stored header and body. If either the header or body could not +// be retrieved nil is returned. +// +// Note, due to concurrent download of header and block body the header and thus +// canonical hash can be stored in the database but the body data not (yet). +func GetBlock(db ethdb.Database, hash common.Hash, number uint64) *types.Block { + // Retrieve the block header and body contents + header := GetHeader(db, hash, number) + if header == nil { + return nil + } + body := GetBody(db, hash, number) + if body == nil { + return nil + } + // Reassemble the block and return + return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) +} + +// GetBlockReceipts retrieves the receipts generated by the transactions included +// in a block given by its hash. +func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types.Receipts { + data, _ := db.Get(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash[:]...)) + if len(data) == 0 { + data, _ = db.Get(append(oldBlockReceiptsPrefix, hash.Bytes()...)) + if len(data) == 0 { + return nil + } + } + storageReceipts := []*types.ReceiptForStorage{} + if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { + glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) + return nil + } + receipts := make(types.Receipts, len(storageReceipts)) + for i, receipt := range storageReceipts { + receipts[i] = (*types.Receipt)(receipt) + } + return receipts +} + +// GetTransaction retrieves a specific transaction from the database, along with +// its added positional metadata. +func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { + // Retrieve the transaction itself from the database + data, _ := db.Get(hash.Bytes()) + if len(data) == 0 { + return nil, common.Hash{}, 0, 0 + } + var tx types.Transaction + if err := rlp.DecodeBytes(data, &tx); err != nil { + return nil, common.Hash{}, 0, 0 + } + // Retrieve the blockchain positional metadata + data, _ = db.Get(append(hash.Bytes(), txMetaSuffix...)) + if len(data) == 0 { + return nil, common.Hash{}, 0, 0 + } + var meta struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + } + if err := rlp.DecodeBytes(data, &meta); err != nil { + return nil, common.Hash{}, 0, 0 + } + return &tx, meta.BlockHash, meta.BlockIndex, meta.Index +} + +// GetReceipt returns a receipt by hash +func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { + data, _ := db.Get(append(receiptsPrefix, txHash[:]...)) + if len(data) == 0 { + return nil + } + var receipt types.ReceiptForStorage + err := rlp.DecodeBytes(data, &receipt) + if err != nil { + glog.V(logger.Debug).Infoln("GetReceipt err:", err) + } + return (*types.Receipt)(&receipt) +} + +// WriteCanonicalHash stores the canonical hash for the given block number. +func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error { + key := append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...) + if err := db.Put(key, hash.Bytes()); err != nil { + glog.Fatalf("failed to store number to hash mapping into database: %v", err) + } + return nil +} + +// WriteHeadHeaderHash stores the head header's hash. +func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error { + if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { + glog.Fatalf("failed to store last header's hash into database: %v", err) + } + return nil +} + +// WriteHeadBlockHash stores the head block's hash. +func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error { + if err := db.Put(headBlockKey, hash.Bytes()); err != nil { + glog.Fatalf("failed to store last block's hash into database: %v", err) + } + return nil +} + +// WriteHeadFastBlockHash stores the fast head block's hash. +func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error { + if err := db.Put(headFastKey, hash.Bytes()); err != nil { + glog.Fatalf("failed to store last fast block's hash into database: %v", err) + } + return nil +} + +// WriteHeader serializes a block header into the database. +func WriteHeader(db ethdb.Database, header *types.Header) error { + data, err := rlp.EncodeToBytes(header) + if err != nil { + return err + } + hash := header.Hash().Bytes() + num := header.Number.Uint64() + encNum := encodeBlockNumber(num) + key := append(blockHashPrefix, hash...) + if err := db.Put(key, encNum); err != nil { + glog.Fatalf("failed to store hash to number mapping into database: %v", err) + } + key = append(append(headerPrefix, encNum...), hash...) + if err := db.Put(key, data); err != nil { + glog.Fatalf("failed to store header into database: %v", err) + } + glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, hash[:4]) + return nil +} + +// WriteBody serializes the body of a block into the database. +func WriteBody(db ethdb.Database, hash common.Hash, number uint64, body *types.Body) error { + data, err := rlp.EncodeToBytes(body) + if err != nil { + return err + } + return WriteBodyRLP(db, hash, number, data) +} + +// WriteBodyRLP writes a serialized body of a block into the database. +func WriteBodyRLP(db ethdb.Database, hash common.Hash, number uint64, rlp rlp.RawValue) error { + key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...) + if err := db.Put(key, rlp); err != nil { + glog.Fatalf("failed to store block body into database: %v", err) + } + glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4]) + return nil +} + +// WriteTd serializes the total difficulty of a block into the database. +func WriteTd(db ethdb.Database, hash common.Hash, number uint64, td *big.Int) error { + data, err := rlp.EncodeToBytes(td) + if err != nil { + return err + } + key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...) + if err := db.Put(key, data); err != nil { + glog.Fatalf("failed to store block total difficulty into database: %v", err) + } + glog.V(logger.Debug).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td) + return nil +} + +// WriteBlock serializes a block into the database, header and body separately. +func WriteBlock(db ethdb.Database, block *types.Block) error { + // Store the body first to retain database consistency + if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil { + return err + } + // Store the header too, signaling full block ownership + if err := WriteHeader(db, block.Header()); err != nil { + return err + } + return nil +} + +// WriteBlockReceipts stores all the transaction receipts belonging to a block +// as a single receipt slice. This is used during chain reorganisations for +// rescheduling dropped transactions. +func WriteBlockReceipts(db ethdb.Database, hash common.Hash, number uint64, receipts types.Receipts) error { + // Convert the receipts into their storage form and serialize them + storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) + for i, receipt := range receipts { + storageReceipts[i] = (*types.ReceiptForStorage)(receipt) + } + bytes, err := rlp.EncodeToBytes(storageReceipts) + if err != nil { + return err + } + // Store the flattened receipt slice + key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) + if err := db.Put(key, bytes); err != nil { + glog.Fatalf("failed to store block receipts into database: %v", err) + } + glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4]) + return nil +} + +// WriteTransactions stores the transactions associated with a specific block +// into the given database. Beside writing the transaction, the function also +// stores a metadata entry along with the transaction, detailing the position +// of this within the blockchain. +func WriteTransactions(db ethdb.Database, block *types.Block) error { + batch := db.NewBatch() + + // Iterate over each transaction and encode it with its metadata + for i, tx := range block.Transactions() { + // Encode and queue up the transaction for storage + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + if err := batch.Put(tx.Hash().Bytes(), data); err != nil { + return err + } + // Encode and queue up the transaction metadata for storage + meta := struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + }{ + BlockHash: block.Hash(), + BlockIndex: block.NumberU64(), + Index: uint64(i), + } + data, err = rlp.EncodeToBytes(meta) + if err != nil { + return err + } + if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil { + return err + } + } + // Write the scheduled data into the database + if err := batch.Write(); err != nil { + glog.Fatalf("failed to store transactions into database: %v", err) + } + return nil +} + +// WriteReceipt stores a single transaction receipt into the database. +func WriteReceipt(db ethdb.Database, receipt *types.Receipt) error { + storageReceipt := (*types.ReceiptForStorage)(receipt) + data, err := rlp.EncodeToBytes(storageReceipt) + if err != nil { + return err + } + return db.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data) +} + +// WriteReceipts stores a batch of transaction receipts into the database. +func WriteReceipts(db ethdb.Database, receipts types.Receipts) error { + batch := db.NewBatch() + + // Iterate over all the receipts and queue them for database injection + for _, receipt := range receipts { + storageReceipt := (*types.ReceiptForStorage)(receipt) + data, err := rlp.EncodeToBytes(storageReceipt) + if err != nil { + return err + } + if err := batch.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data); err != nil { + return err + } + } + // Write the scheduled data into the database + if err := batch.Write(); err != nil { + glog.Fatalf("failed to store receipts into database: %v", err) + } + return nil +} + +// DeleteCanonicalHash removes the number to hash canonical mapping. +func DeleteCanonicalHash(db ethdb.Database, number uint64) { + db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...)) +} + +// DeleteHeader removes all block header data associated with a hash. +func DeleteHeader(db ethdb.Database, hash common.Hash, number uint64) { + db.Delete(append(blockHashPrefix, hash.Bytes()...)) + db.Delete(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) +} + +// DeleteBody removes all block body data associated with a hash. +func DeleteBody(db ethdb.Database, hash common.Hash, number uint64) { + db.Delete(append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) +} + +// DeleteTd removes all block total difficulty data associated with a hash. +func DeleteTd(db ethdb.Database, hash common.Hash, number uint64) { + db.Delete(append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...)) +} + +// DeleteBlock removes all block data associated with a hash. +func DeleteBlock(db ethdb.Database, hash common.Hash, number uint64) { + DeleteBlockReceipts(db, hash, number) + DeleteHeader(db, hash, number) + DeleteBody(db, hash, number) + DeleteTd(db, hash, number) +} + +// DeleteBlockReceipts removes all receipt data associated with a block hash. +func DeleteBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) { + db.Delete(append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)) +} + +// DeleteTransaction removes all transaction data associated with a hash. +func DeleteTransaction(db ethdb.Database, hash common.Hash) { + db.Delete(hash.Bytes()) + db.Delete(append(hash.Bytes(), txMetaSuffix...)) +} + +// DeleteReceipt removes all receipt data associated with a transaction hash. +func DeleteReceipt(db ethdb.Database, hash common.Hash) { + db.Delete(append(receiptsPrefix, hash.Bytes()...)) +} + +// [deprecated by the header/block split, remove eventually] +// GetBlockByHashOld returns the old combined block corresponding to the hash +// or nil if not found. This method is only used by the upgrade mechanism to +// access the old combined block representation. It will be dropped after the +// network transitions to eth/63. +func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block { + data, _ := db.Get(append(oldBlockHashPrefix, hash[:]...)) + if len(data) == 0 { + return nil + } + var block types.StorageBlock + if err := rlp.Decode(bytes.NewReader(data), &block); err != nil { + glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err) + return nil + } + return (*types.Block)(&block) +} + +// returns a formatted MIP mapped key by adding prefix, canonical number and level +// +// ex. fn(98, 1000) = (prefix || 1000 || 0) +func mipmapKey(num, level uint64) []byte { + lkey := make([]byte, 8) + binary.BigEndian.PutUint64(lkey, level) + key := new(big.Int).SetUint64(num / level * level) + + return append(mipmapPre, append(lkey, key.Bytes()...)...) +} + +// WriteMapmapBloom writes each address included in the receipts' logs to the +// MIP bloom bin. +func WriteMipmapBloom(db ethdb.Database, number uint64, receipts types.Receipts) error { + mipmapBloomMu.Lock() + defer mipmapBloomMu.Unlock() + + batch := db.NewBatch() + for _, level := range MIPMapLevels { + key := mipmapKey(number, level) + bloomDat, _ := db.Get(key) + bloom := types.BytesToBloom(bloomDat) + for _, receipt := range receipts { + for _, log := range receipt.Logs { + bloom.Add(log.Address.Big()) + } + } + batch.Put(key, bloom.Bytes()) + } + if err := batch.Write(); err != nil { + return fmt.Errorf("mipmap write fail for: %d: %v", number, err) + } + return nil +} + +// GetMipmapBloom returns a bloom filter using the number and level as input +// parameters. For available levels see MIPMapLevels. +func GetMipmapBloom(db ethdb.Database, number, level uint64) types.Bloom { + bloomDat, _ := db.Get(mipmapKey(number, level)) + return types.BytesToBloom(bloomDat) +} + +// PreimageTable returns a Database instance with the key prefix for preimage entries. +func PreimageTable(db ethdb.Database) ethdb.Database { + return ethdb.NewTable(db, preimagePrefix) +} + +// WritePreimages writes the provided set of preimages to the database. `number` is the +// current block number, and is used for debug messages only. +func WritePreimages(db ethdb.Database, number uint64, preimages map[common.Hash][]byte) error { + table := PreimageTable(db) + batch := table.NewBatch() + hitCount := 0 + for hash, preimage := range preimages { + if _, err := table.Get(hash.Bytes()); err != nil { + batch.Put(hash.Bytes(), preimage) + hitCount += 1 + } + } + preimageCounter.Inc(int64(len(preimages))) + preimageHitCounter.Inc(int64(hitCount)) + if hitCount > 0 { + if err := batch.Write(); err != nil { + return fmt.Errorf("preimage write fail for block %d: %v", number, err) + } + glog.V(logger.Debug).Infof("%d preimages in block %d, including %d new", len(preimages), number, hitCount) + } + return nil +} + +// GetBlockChainVersion reads the version number from db. +func GetBlockChainVersion(db ethdb.Database) int { + var vsn uint + enc, _ := db.Get([]byte("BlockchainVersion")) + rlp.DecodeBytes(enc, &vsn) + return int(vsn) +} + +// WriteBlockChainVersion writes vsn as the version number to db. +func WriteBlockChainVersion(db ethdb.Database, vsn int) { + enc, _ := rlp.EncodeToBytes(uint(vsn)) + db.Put([]byte("BlockchainVersion"), enc) +} + +// WriteChainConfig writes the chain config settings to the database. +func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error { + // short circuit and ignore if nil config. GetChainConfig + // will return a default. + if cfg == nil { + return nil + } + + jsonChainConfig, err := json.Marshal(cfg) + if err != nil { + return err + } + + return db.Put(append(configPrefix, hash[:]...), jsonChainConfig) +} + +// GetChainConfig will fetch the network settings based on the given hash. +func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) { + jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...)) + if len(jsonChainConfig) == 0 { + return nil, ChainConfigNotFoundErr + } + + var config params.ChainConfig + if err := json.Unmarshal(jsonChainConfig, &config); err != nil { + return nil, err + } + + return &config, nil +} + +// FindCommonAncestor returns the last common ancestor of two block headers +func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header { + for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) + if a == nil { + return nil + } + } + for an := a.Number.Uint64(); an < b.Number.Uint64(); { + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) + if b == nil { + return nil + } + } + for a.Hash() != b.Hash() { + a = GetHeader(db, a.ParentHash, a.Number.Uint64()-1) + if a == nil { + return nil + } + b = GetHeader(db, b.ParentHash, b.Number.Uint64()-1) + if b == nil { + return nil + } + } + return a +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/database_util_test.go b/vendor/github.com/ethereum/go-ethereum/core/database_util_test.go new file mode 100644 index 0000000..d96aa71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/database_util_test.go @@ -0,0 +1,605 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +type diffTest struct { + ParentTimestamp uint64 + ParentDifficulty *big.Int + CurrentTimestamp uint64 + CurrentBlocknumber *big.Int + CurrentDifficulty *big.Int +} + +func (d *diffTest) UnmarshalJSON(b []byte) (err error) { + var ext struct { + ParentTimestamp string + ParentDifficulty string + CurrentTimestamp string + CurrentBlocknumber string + CurrentDifficulty string + } + if err := json.Unmarshal(b, &ext); err != nil { + return err + } + + d.ParentTimestamp = common.String2Big(ext.ParentTimestamp).Uint64() + d.ParentDifficulty = common.String2Big(ext.ParentDifficulty) + d.CurrentTimestamp = common.String2Big(ext.CurrentTimestamp).Uint64() + d.CurrentBlocknumber = common.String2Big(ext.CurrentBlocknumber) + d.CurrentDifficulty = common.String2Big(ext.CurrentDifficulty) + + return nil +} + +func TestCalcDifficulty(t *testing.T) { + file, err := os.Open("../tests/files/BasicTests/difficulty.json") + if err != nil { + t.Fatal(err) + } + defer file.Close() + + tests := make(map[string]diffTest) + err = json.NewDecoder(file).Decode(&tests) + if err != nil { + t.Fatal(err) + } + + config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} + for name, test := range tests { + number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) + diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) + if diff.Cmp(test.CurrentDifficulty) != 0 { + t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) + } + } +} + +// Tests block header storage and retrieval operations. +func TestHeaderStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + // Create a test header to move around the database and make sure it's really new + header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")} + if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil { + t.Fatalf("Non existent header returned: %v", entry) + } + // Write and verify the header in the database + if err := WriteHeader(db, header); err != nil { + t.Fatalf("Failed to write header into database: %v", err) + } + if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil { + t.Fatalf("Stored header not found") + } else if entry.Hash() != header.Hash() { + t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header) + } + if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { + t.Fatalf("Stored header RLP not found") + } else { + hasher := sha3.NewKeccak256() + hasher.Write(entry) + + if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() { + t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header) + } + } + // Delete the header and verify the execution + DeleteHeader(db, header.Hash(), header.Number.Uint64()) + if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil { + t.Fatalf("Deleted header returned: %v", entry) + } +} + +// Tests block body storage and retrieval operations. +func TestBodyStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + // Create a test body to move around the database and make sure it's really new + body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} + + hasher := sha3.NewKeccak256() + rlp.Encode(hasher, body) + hash := common.BytesToHash(hasher.Sum(nil)) + + if entry := GetBody(db, hash, 0); entry != nil { + t.Fatalf("Non existent body returned: %v", entry) + } + // Write and verify the body in the database + if err := WriteBody(db, hash, 0, body); err != nil { + t.Fatalf("Failed to write body into database: %v", err) + } + if entry := GetBody(db, hash, 0); entry == nil { + t.Fatalf("Stored body not found") + } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { + t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) + } + if entry := GetBodyRLP(db, hash, 0); entry == nil { + t.Fatalf("Stored body RLP not found") + } else { + hasher := sha3.NewKeccak256() + hasher.Write(entry) + + if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash { + t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body) + } + } + // Delete the body and verify the execution + DeleteBody(db, hash, 0) + if entry := GetBody(db, hash, 0); entry != nil { + t.Fatalf("Deleted body returned: %v", entry) + } +} + +// Tests block storage and retrieval operations. +func TestBlockStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + // Create a test block to move around the database and make sure it's really new + block := types.NewBlockWithHeader(&types.Header{ + Extra: []byte("test block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + }) + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Non existent block returned: %v", entry) + } + if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Non existent header returned: %v", entry) + } + if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Non existent body returned: %v", entry) + } + // Write and verify the block in the database + if err := WriteBlock(db, block); err != nil { + t.Fatalf("Failed to write block into database: %v", err) + } + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil { + t.Fatalf("Stored block not found") + } else if entry.Hash() != block.Hash() { + t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) + } + if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil { + t.Fatalf("Stored header not found") + } else if entry.Hash() != block.Header().Hash() { + t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header()) + } + if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil { + t.Fatalf("Stored body not found") + } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { + t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) + } + // Delete the block and verify the execution + DeleteBlock(db, block.Hash(), block.NumberU64()) + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Deleted block returned: %v", entry) + } + if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Deleted header returned: %v", entry) + } + if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Deleted body returned: %v", entry) + } +} + +// Tests that partial block contents don't get reassembled into full blocks. +func TestPartialBlockStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + block := types.NewBlockWithHeader(&types.Header{ + Extra: []byte("test block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + }) + // Store a header and check that it's not recognized as a block + if err := WriteHeader(db, block.Header()); err != nil { + t.Fatalf("Failed to write header into database: %v", err) + } + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Non existent block returned: %v", entry) + } + DeleteHeader(db, block.Hash(), block.NumberU64()) + + // Store a body and check that it's not recognized as a block + if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil { + t.Fatalf("Failed to write body into database: %v", err) + } + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil { + t.Fatalf("Non existent block returned: %v", entry) + } + DeleteBody(db, block.Hash(), block.NumberU64()) + + // Store a header and a body separately and check reassembly + if err := WriteHeader(db, block.Header()); err != nil { + t.Fatalf("Failed to write header into database: %v", err) + } + if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil { + t.Fatalf("Failed to write body into database: %v", err) + } + if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil { + t.Fatalf("Stored block not found") + } else if entry.Hash() != block.Hash() { + t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) + } +} + +// Tests block total difficulty storage and retrieval operations. +func TestTdStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + // Create a test TD to move around the database and make sure it's really new + hash, td := common.Hash{}, big.NewInt(314) + if entry := GetTd(db, hash, 0); entry != nil { + t.Fatalf("Non existent TD returned: %v", entry) + } + // Write and verify the TD in the database + if err := WriteTd(db, hash, 0, td); err != nil { + t.Fatalf("Failed to write TD into database: %v", err) + } + if entry := GetTd(db, hash, 0); entry == nil { + t.Fatalf("Stored TD not found") + } else if entry.Cmp(td) != 0 { + t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td) + } + // Delete the TD and verify the execution + DeleteTd(db, hash, 0) + if entry := GetTd(db, hash, 0); entry != nil { + t.Fatalf("Deleted TD returned: %v", entry) + } +} + +// Tests that canonical numbers can be mapped to hashes and retrieved. +func TestCanonicalMappingStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + // Create a test canonical number and assinged hash to move around + hash, number := common.Hash{0: 0xff}, uint64(314) + if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) { + t.Fatalf("Non existent canonical mapping returned: %v", entry) + } + // Write and verify the TD in the database + if err := WriteCanonicalHash(db, hash, number); err != nil { + t.Fatalf("Failed to write canonical mapping into database: %v", err) + } + if entry := GetCanonicalHash(db, number); entry == (common.Hash{}) { + t.Fatalf("Stored canonical mapping not found") + } else if entry != hash { + t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash) + } + // Delete the TD and verify the execution + DeleteCanonicalHash(db, number) + if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) { + t.Fatalf("Deleted canonical mapping returned: %v", entry) + } +} + +// Tests that head headers and head blocks can be assigned, individually. +func TestHeadStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")}) + blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")}) + blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")}) + + // Check that no head entries are in a pristine database + if entry := GetHeadHeaderHash(db); entry != (common.Hash{}) { + t.Fatalf("Non head header entry returned: %v", entry) + } + if entry := GetHeadBlockHash(db); entry != (common.Hash{}) { + t.Fatalf("Non head block entry returned: %v", entry) + } + if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) { + t.Fatalf("Non fast head block entry returned: %v", entry) + } + // Assign separate entries for the head header and block + if err := WriteHeadHeaderHash(db, blockHead.Hash()); err != nil { + t.Fatalf("Failed to write head header hash: %v", err) + } + if err := WriteHeadBlockHash(db, blockFull.Hash()); err != nil { + t.Fatalf("Failed to write head block hash: %v", err) + } + if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil { + t.Fatalf("Failed to write fast head block hash: %v", err) + } + // Check that both heads are present, and different (i.e. two heads maintained) + if entry := GetHeadHeaderHash(db); entry != blockHead.Hash() { + t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash()) + } + if entry := GetHeadBlockHash(db); entry != blockFull.Hash() { + t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash()) + } + if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() { + t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash()) + } +} + +// Tests that transactions and associated metadata can be stored and retrieved. +func TestTransactionStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11}) + tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22}) + tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33}) + txs := []*types.Transaction{tx1, tx2, tx3} + + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + + // Check that no transactions entries are in a pristine database + for i, tx := range txs { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn) + } + } + // Insert all the transactions into the database, and verify contents + if err := WriteTransactions(db, block); err != nil { + t.Fatalf("failed to write transactions: %v", err) + } + for i, tx := range txs { + if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { + t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) + } else { + if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { + t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) + } + if tx.String() != txn.String() { + t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx) + } + } + } + // Delete the transactions and check purge + for i, tx := range txs { + DeleteTransaction(db, tx.Hash()) + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn) + } + } +} + +// Tests that receipts can be stored and retrieved. +func TestReceiptStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + receipt1 := &types.Receipt{ + PostState: []byte{0x01}, + CumulativeGasUsed: big.NewInt(1), + Logs: []*types.Log{ + {Address: common.BytesToAddress([]byte{0x11})}, + {Address: common.BytesToAddress([]byte{0x01, 0x11})}, + }, + TxHash: common.BytesToHash([]byte{0x11, 0x11}), + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: big.NewInt(111111), + } + receipt2 := &types.Receipt{ + PostState: []byte{0x02}, + CumulativeGasUsed: big.NewInt(2), + Logs: []*types.Log{ + {Address: common.BytesToAddress([]byte{0x22})}, + {Address: common.BytesToAddress([]byte{0x02, 0x22})}, + }, + TxHash: common.BytesToHash([]byte{0x22, 0x22}), + ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), + GasUsed: big.NewInt(222222), + } + receipts := []*types.Receipt{receipt1, receipt2} + + // Check that no receipt entries are in a pristine database + for i, receipt := range receipts { + if r := GetReceipt(db, receipt.TxHash); r != nil { + t.Fatalf("receipt #%d [%x]: non existent receipt returned: %v", i, receipt.TxHash, r) + } + } + // Insert all the receipts into the database, and verify contents + if err := WriteReceipts(db, receipts); err != nil { + t.Fatalf("failed to write receipts: %v", err) + } + for i, receipt := range receipts { + if r := GetReceipt(db, receipt.TxHash); r == nil { + t.Fatalf("receipt #%d [%x]: receipt not found", i, receipt.TxHash) + } else { + rlpHave, _ := rlp.EncodeToBytes(r) + rlpWant, _ := rlp.EncodeToBytes(receipt) + + if !bytes.Equal(rlpHave, rlpWant) { + t.Fatalf("receipt #%d [%x]: receipt mismatch: have %v, want %v", i, receipt.TxHash, r, receipt) + } + } + } + // Delete the receipts and check purge + for i, receipt := range receipts { + DeleteReceipt(db, receipt.TxHash) + if r := GetReceipt(db, receipt.TxHash); r != nil { + t.Fatalf("receipt #%d [%x]: deleted receipt returned: %v", i, receipt.TxHash, r) + } + } +} + +// Tests that receipts associated with a single block can be stored and retrieved. +func TestBlockReceiptStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + receipt1 := &types.Receipt{ + PostState: []byte{0x01}, + CumulativeGasUsed: big.NewInt(1), + Logs: []*types.Log{ + {Address: common.BytesToAddress([]byte{0x11})}, + {Address: common.BytesToAddress([]byte{0x01, 0x11})}, + }, + TxHash: common.BytesToHash([]byte{0x11, 0x11}), + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: big.NewInt(111111), + } + receipt2 := &types.Receipt{ + PostState: []byte{0x02}, + CumulativeGasUsed: big.NewInt(2), + Logs: []*types.Log{ + {Address: common.BytesToAddress([]byte{0x22})}, + {Address: common.BytesToAddress([]byte{0x02, 0x22})}, + }, + TxHash: common.BytesToHash([]byte{0x22, 0x22}), + ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), + GasUsed: big.NewInt(222222), + } + receipts := []*types.Receipt{receipt1, receipt2} + + // Check that no receipt entries are in a pristine database + hash := common.BytesToHash([]byte{0x03, 0x14}) + if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 { + t.Fatalf("non existent receipts returned: %v", rs) + } + // Insert the receipt slice into the database and check presence + if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil { + t.Fatalf("failed to write block receipts: %v", err) + } + if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 { + t.Fatalf("no receipts returned") + } else { + for i := 0; i < len(receipts); i++ { + rlpHave, _ := rlp.EncodeToBytes(rs[i]) + rlpWant, _ := rlp.EncodeToBytes(receipts[i]) + + if !bytes.Equal(rlpHave, rlpWant) { + t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i]) + } + } + } + // Delete the receipt slice and check purge + DeleteBlockReceipts(db, hash, 0) + if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 { + t.Fatalf("deleted receipts returned: %v", rs) + } +} + +func TestMipmapBloom(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + receipt1 := new(types.Receipt) + receipt1.Logs = []*types.Log{ + {Address: common.BytesToAddress([]byte("test"))}, + {Address: common.BytesToAddress([]byte("address"))}, + } + receipt2 := new(types.Receipt) + receipt2.Logs = []*types.Log{ + {Address: common.BytesToAddress([]byte("test"))}, + {Address: common.BytesToAddress([]byte("address1"))}, + } + + WriteMipmapBloom(db, 1, types.Receipts{receipt1}) + WriteMipmapBloom(db, 2, types.Receipts{receipt2}) + + for _, level := range MIPMapLevels { + bloom := GetMipmapBloom(db, 2, level) + if !bloom.Test(new(big.Int).SetBytes([]byte("address1"))) { + t.Error("expected test to be included on level:", level) + } + } + + // reset + db, _ = ethdb.NewMemDatabase() + receipt := new(types.Receipt) + receipt.Logs = []*types.Log{ + {Address: common.BytesToAddress([]byte("test"))}, + } + WriteMipmapBloom(db, 999, types.Receipts{receipt1}) + + receipt = new(types.Receipt) + receipt.Logs = []*types.Log{ + {Address: common.BytesToAddress([]byte("test 1"))}, + } + WriteMipmapBloom(db, 1000, types.Receipts{receipt}) + + bloom := GetMipmapBloom(db, 1000, 1000) + if bloom.TestBytes([]byte("test")) { + t.Error("test should not have been included") + } +} + +func TestMipmapChain(t *testing.T) { + dir, err := ioutil.TempDir("", "mipmap") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + var ( + db, _ = ethdb.NewLDBDatabase(dir, 0, 0) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = common.BytesToAddress([]byte("jeff")) + + hash1 = common.BytesToHash([]byte("topic1")) + ) + defer db.Close() + + genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)}) + chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) { + var receipts types.Receipts + switch i { + case 1: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{{Address: addr, Topics: []common.Hash{hash1}}} + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + case 1000: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{{Address: addr2}} + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + + } + + // store the receipts + err := WriteReceipts(db, receipts) + if err != nil { + t.Fatal(err) + } + WriteMipmapBloom(db, uint64(i+1), receipts) + }) + for i, block := range chain { + WriteBlock(db, block) + if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := WriteHeadBlockHash(db, block.Hash()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), receipts[i]); err != nil { + t.Fatal("error writing block receipts:", err) + } + } + + bloom := GetMipmapBloom(db, 0, 1000) + if bloom.TestBytes(addr2[:]) { + t.Error("address was included in bloom and should not have") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go new file mode 100644 index 0000000..08fd432 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/default_genesis.go @@ -0,0 +1,28 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +// defaultGenesisBlock is a gzip compressed dump of the official default Ethereum +// genesis block. +const defaultGenesisBlock = "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" + +// defaultTestnetGenesisBlock is a gzip compressed dump of the official default Ethereum +// test network genesis block (currently Ropsten). +const defaultTestnetGenesisBlock = "QlpoOTFBWSZTWezl5v8AGI59gHRQABBcBH/0FGQ/795qUAN+PcGHAoQMVIwm0zCSp+ihiAANB6gqp+mmp6eMmqo0AADRpoABgAGjTQBk0BoMgNAbSqNAyDQyaBoNAyAGBUkhNNNA0JGmgD0g0AekxJtVIgTtRupd52vqSSSVfG2ALQBhhdbCIgmAgUiIJnEQTviQF8kkN1IOKiRVVFVSVXHT1cHLHdiSQBlAAG2JtCtNgCS5IEB6dNtP782Z729Tbt3Z6+uFr7u3w79widyYSMAhtlL5XeGZWdwEBNdFm+Hh5gVRBoslgksAMkUKmYlSJRTxBsQAAKKlnd3WjPWrNd66NR7WAu6AQL40vqlny1We+p3l1QThy4gRd2lSVjEKISLDFrVJ1UtjPEPctVSZGjBTJWRzpdgRmCCprJWGe3DK9wvz4eGvdv1ZZcMvxGgdakeMAVEeFywVQWLWCrUWg60Y9ZLoAuZ0uSJJUELb8N2e7j05d3Tlz7vRwgk+LisedJALLVUgFjLGzfv5SEGPLlhhjpx2aaO8T87whcYDmgPLVDBI3RF1TLnzvymWtN732u93GiAGiBdm5s1q9Haru7+yCLJAk3wBDTtrGeMVh3iJmXHo+OBAsqoDOZwjKM3d3mZmcHdBJiSVouSKVkDg+L4vSzNRqs7u6NEaIiUKoYxERGfTWIhoiHwQFESGzta21qu1qtZ3cAXQshVAUybTLbWttWh3iHLoDJEgkACRVCzbeX774Z6558+mfDTdz058efTpDbXai+tddbQBbn/YA9fwxy8/Pq3HFW7deL8W447duuyI2RGqIAAA9SOqHeXzbRtGiXeHiYBMggAAIyQh/f5vW96Xvvu7b7777yMURRHFE6EkEnxRIiOlY2ijs7RV4gYIB0R4LmgB49xg0Oz0bVr2a95u4AZE6rTjl2w58Cntu1ZV6e3Lw3ne2RegHw7R58NdbtVlJEkuqRV2WKa/hpbPIx3bIyX43aNWmezrCBry2ePzkA8reSwB/xdyRThQkOzl5v8=" + +// defaultDevnetGenesisBlockis a gzip compressed dump of a dev Ethereum network genesis block. +const defaultDevnetGenesisBlock = "QlpoOTFBWSZTWb66siUAAmldgEERWAR/8AAEP6eOakACGu6ULVwyRNGKeBCbUaAGIJUDJpT2lP1BQAAANqp6jTUxMIxGmAJgFVTAp4mjSaoAxAB4cLqQ6aBgTRPcAECErBIZ9PyZJw5IYvQPWlcXDXnhaC9buNtJaAFApAgwkUIRBmBY6EhTY8/6ZXse6lLD1Eh3sfas7VHnxse2LZeXpME9fADujvYB4Wr9cgQA+Wq0gRnrfVmgVwIiIrgfON+c4EhrApvQT2LjUh6y3x41XXOxriA89W52MDId9R7vasE3pfWWATQF7nAEAWRb861EU6M9SmBTET1UUGuBmgxzewhmUe4Rj4jSvgRFRTYgI8WzzcQn3nNr6x3GW94Yp1xG4kOoC/MxDhbF8uMahuMTmKyEep0SwLaxquoT3F9X0NxmouMWhxTAlxHQoN872vbYJS/Wrls2LuptmbKr4R6BJ/oJfPNpq+H362F5ZyhNQBxXep4ZOr94ylFM6UfOeUq37kFeGzXOle3LKItkvJ70OKqEqzujic72rnpZHym6b3xSONoWjYzRzIUbOKSUgdVtEhtvUg24asA2cy1zFzsfnj8CXe0Bdi7kinChIX11ZEo=" diff --git a/vendor/github.com/ethereum/go-ethereum/core/error.go b/vendor/github.com/ethereum/go-ethereum/core/error.go new file mode 100644 index 0000000..0ba506f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/error.go @@ -0,0 +1,203 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + BlockNumberErr = errors.New("block number invalid") + BlockFutureErr = errors.New("block time is in the future") + BlockTSTooBigErr = errors.New("block time too big") + BlockEqualTSErr = errors.New("block time stamp equal to previous") +) + +// Parent error. In case a parent is unknown this error will be thrown +// by the block manager +type ParentErr struct { + Message string +} + +func (err *ParentErr) Error() string { + return err.Message +} + +func ParentError(hash common.Hash) error { + return &ParentErr{Message: fmt.Sprintf("Block's parent unknown %x", hash)} +} + +func IsParentErr(err error) bool { + _, ok := err.(*ParentErr) + return ok +} + +type UncleErr struct { + Message string +} + +func (err *UncleErr) Error() string { + return err.Message +} + +func UncleError(format string, v ...interface{}) error { + return &UncleErr{Message: fmt.Sprintf(format, v...)} +} + +func IsUncleErr(err error) bool { + _, ok := err.(*UncleErr) + return ok +} + +// Block validation error. If any validation fails, this error will be thrown +type ValidationErr struct { + Message string +} + +func (err *ValidationErr) Error() string { + return err.Message +} + +func ValidationError(format string, v ...interface{}) *ValidationErr { + return &ValidationErr{Message: fmt.Sprintf(format, v...)} +} + +func IsValidationErr(err error) bool { + _, ok := err.(*ValidationErr) + return ok +} + +type NonceErr struct { + Message string + Is, Exp uint64 +} + +func (err *NonceErr) Error() string { + return err.Message +} + +func NonceError(is, exp uint64) *NonceErr { + return &NonceErr{Message: fmt.Sprintf("Transaction w/ invalid nonce. tx=%d state=%d)", is, exp), Is: is, Exp: exp} +} + +func IsNonceErr(err error) bool { + _, ok := err.(*NonceErr) + return ok +} + +// BlockNonceErr indicates that a block's nonce is invalid. +type BlockNonceErr struct { + Number *big.Int + Hash common.Hash + Nonce uint64 +} + +func (err *BlockNonceErr) Error() string { + return fmt.Sprintf("nonce for #%d [%x…] is invalid (got %d)", err.Number, err.Hash, err.Nonce) +} + +// IsBlockNonceErr returns true for invalid block nonce errors. +func IsBlockNonceErr(err error) bool { + _, ok := err.(*BlockNonceErr) + return ok +} + +type InvalidTxErr struct { + Message string +} + +func (err *InvalidTxErr) Error() string { + return err.Message +} + +func InvalidTxError(err error) *InvalidTxErr { + return &InvalidTxErr{fmt.Sprintf("%v", err)} +} + +func IsInvalidTxErr(err error) bool { + _, ok := err.(*InvalidTxErr) + return ok +} + +type TDError struct { + a, b *big.Int +} + +func (self *TDError) Error() string { + return fmt.Sprintf("incoming chain has a lower or equal TD (%v <= %v)", self.a, self.b) +} +func IsTDError(e error) bool { + _, ok := e.(*TDError) + return ok +} + +type KnownBlockError struct { + number *big.Int + hash common.Hash +} + +func (self *KnownBlockError) Error() string { + return fmt.Sprintf("block %v already known (%x)", self.number, self.hash[0:4]) +} +func IsKnownBlockErr(e error) bool { + _, ok := e.(*KnownBlockError) + return ok +} + +type ValueTransferError struct { + message string +} + +func ValueTransferErr(str string, v ...interface{}) *ValueTransferError { + return &ValueTransferError{fmt.Sprintf(str, v...)} +} + +func (self *ValueTransferError) Error() string { + return self.message +} +func IsValueTransferErr(e error) bool { + _, ok := e.(*ValueTransferError) + return ok +} + +type BadHashError common.Hash + +func (h BadHashError) Error() string { + return fmt.Sprintf("Found known bad hash in chain %x", h[:]) +} + +func IsBadHashError(err error) bool { + _, ok := err.(BadHashError) + return ok +} + +type GasLimitErr struct { + Have, Want *big.Int +} + +func IsGasLimitErr(err error) bool { + _, ok := err.(*GasLimitErr) + return ok +} + +func (err *GasLimitErr) Error() string { + return fmt.Sprintf("GasLimit reached. Have %d gas, transaction requires %d", err.Have, err.Want) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/events.go b/vendor/github.com/ethereum/go-ethereum/core/events.go new file mode 100644 index 0000000..31ad836 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/events.go @@ -0,0 +1,80 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// TxPreEvent is posted when a transaction enters the transaction pool. +type TxPreEvent struct{ Tx *types.Transaction } + +// TxPostEvent is posted when a transaction has been processed. +type TxPostEvent struct{ Tx *types.Transaction } + +// PendingLogsEvent is posted pre mining and notifies of pending logs. +type PendingLogsEvent struct { + Logs []*types.Log +} + +// PendingStateEvent is posted pre mining and notifies of pending state changes. +type PendingStateEvent struct{} + +// NewMinedBlockEvent is posted when a block has been imported. +type NewMinedBlockEvent struct{ Block *types.Block } + +// RemovedTransactionEvent is posted when a reorg happens +type RemovedTransactionEvent struct{ Txs types.Transactions } + +// RemovedLogEvent is posted when a reorg happens +type RemovedLogsEvent struct{ Logs []*types.Log } + +// ChainSplit is posted when a new head is detected +type ChainSplitEvent struct { + Block *types.Block + Logs []*types.Log +} + +type ChainEvent struct { + Block *types.Block + Hash common.Hash + Logs []*types.Log +} + +type ChainSideEvent struct { + Block *types.Block +} + +type PendingBlockEvent struct { + Block *types.Block + Logs []*types.Log +} + +type ChainUncleEvent struct { + Block *types.Block +} + +type ChainHeadEvent struct{ Block *types.Block } + +type GasPriceChanged struct{ Price *big.Int } + +// Mining operation events +type StartMining struct{} +type TopMining struct{} diff --git a/vendor/github.com/ethereum/go-ethereum/core/evm.go b/vendor/github.com/ethereum/go-ethereum/core/evm.go new file mode 100644 index 0000000..6a57130 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/evm.go @@ -0,0 +1,73 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// BlockFetcher retrieves headers by their hash +type HeaderFetcher interface { + // GetHeader returns the hash corresponding to their hash + GetHeader(common.Hash, uint64) *types.Header +} + +// NewEVMContext creates a new context for use in the EVM. +func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { + return vm.Context{ + CanTransfer: CanTransfer, + Transfer: Transfer, + GetHash: GetHashFn(header, chain), + + Origin: msg.From(), + Coinbase: header.Coinbase, + BlockNumber: new(big.Int).Set(header.Number), + Time: new(big.Int).Set(header.Time), + Difficulty: new(big.Int).Set(header.Difficulty), + GasLimit: new(big.Int).Set(header.GasLimit), + GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + +// GetHashFn returns a GetHashFunc which retrieves header hashes by number +func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash { + return func(n uint64) common.Hash { + for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { + if header.Number.Uint64() == n { + return header.Hash() + } + } + + return common.Hash{} + } +} + +// CanTransfer checks wether there are enough funds in the address' account to make a transfer. +// This does not take the necessary gas in to account to make the transfer valid. +func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 +} + +// Transfer subtracts amount from sender and adds amount to recipient using the given Db +func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/fees.go b/vendor/github.com/ethereum/go-ethereum/core/fees.go new file mode 100644 index 0000000..0bb26f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/fees.go @@ -0,0 +1,23 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" +) + +var BlockReward *big.Int = big.NewInt(5e+18) diff --git a/vendor/github.com/ethereum/go-ethereum/core/filter_test.go b/vendor/github.com/ethereum/go-ethereum/core/filter_test.go new file mode 100644 index 0000000..58e71e3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/filter_test.go @@ -0,0 +1,17 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core diff --git a/vendor/github.com/ethereum/go-ethereum/core/gaspool.go b/vendor/github.com/ethereum/go-ethereum/core/gaspool.go new file mode 100644 index 0000000..f1c64c9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/gaspool.go @@ -0,0 +1,46 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import "math/big" + +// GasPool tracks the amount of gas available during +// execution of the transactions in a block. +// The zero value is a pool with zero gas available. +type GasPool big.Int + +// AddGas makes gas available for execution. +func (gp *GasPool) AddGas(amount *big.Int) *GasPool { + i := (*big.Int)(gp) + i.Add(i, amount) + return gp +} + +// SubGas deducts the given amount from the pool if enough gas is +// available and returns an error otherwise. +func (gp *GasPool) SubGas(amount *big.Int) error { + i := (*big.Int)(gp) + if i.Cmp(amount) < 0 { + return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} + } + i.Sub(i, amount) + return nil +} + +func (gp *GasPool) String() string { + return (*big.Int)(gp).String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/genesis.go b/vendor/github.com/ethereum/go-ethereum/core/genesis.go new file mode 100644 index 0000000..b94b5af --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/genesis.go @@ -0,0 +1,216 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "compress/bzip2" + "compress/gzip" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +// WriteGenesisBlock writes the genesis block to the database as block number 0 +func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, error) { + contents, err := ioutil.ReadAll(reader) + if err != nil { + return nil, err + } + + var genesis struct { + ChainConfig *params.ChainConfig `json:"config"` + Nonce string + Timestamp string + ParentHash string + ExtraData string + GasLimit string + Difficulty string + Mixhash string + Coinbase string + Alloc map[string]struct { + Code string + Storage map[string]string + Balance string + Nonce string + } + } + + if err := json.Unmarshal(contents, &genesis); err != nil { + return nil, err + } + + // creating with empty hash always works + statedb, _ := state.New(common.Hash{}, chainDb) + for addr, account := range genesis.Alloc { + address := common.HexToAddress(addr) + statedb.AddBalance(address, common.String2Big(account.Balance)) + statedb.SetCode(address, common.FromHex(account.Code)) + statedb.SetNonce(address, common.String2Big(account.Nonce).Uint64()) + for key, value := range account.Storage { + statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) + } + } + root, stateBatch := statedb.CommitBatch(false) + + difficulty := common.String2Big(genesis.Difficulty) + block := types.NewBlock(&types.Header{ + Nonce: types.EncodeNonce(common.String2Big(genesis.Nonce).Uint64()), + Time: common.String2Big(genesis.Timestamp), + ParentHash: common.HexToHash(genesis.ParentHash), + Extra: common.FromHex(genesis.ExtraData), + GasLimit: common.String2Big(genesis.GasLimit), + Difficulty: difficulty, + MixDigest: common.HexToHash(genesis.Mixhash), + Coinbase: common.HexToAddress(genesis.Coinbase), + Root: root, + }, nil, nil, nil) + + if block := GetBlock(chainDb, block.Hash(), block.NumberU64()); block != nil { + glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number") + err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + if err != nil { + return nil, err + } + return block, nil + } + + if err := stateBatch.Write(); err != nil { + return nil, fmt.Errorf("cannot write state: %v", err) + } + if err := WriteTd(chainDb, block.Hash(), block.NumberU64(), difficulty); err != nil { + return nil, err + } + if err := WriteBlock(chainDb, block); err != nil { + return nil, err + } + if err := WriteBlockReceipts(chainDb, block.Hash(), block.NumberU64(), nil); err != nil { + return nil, err + } + if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil { + return nil, err + } + if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil { + return nil, err + } + if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil { + return nil, err + } + + return block, nil +} + +// GenesisBlockForTesting creates a block in which addr has the given wei balance. +// The state trie of the block is written to db. the passed db needs to contain a state root +func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { + statedb, _ := state.New(common.Hash{}, db) + obj := statedb.GetOrNewStateObject(addr) + obj.SetBalance(balance) + root, err := statedb.Commit(false) + if err != nil { + panic(fmt.Sprintf("cannot write state: %v", err)) + } + block := types.NewBlock(&types.Header{ + Difficulty: params.GenesisDifficulty, + GasLimit: params.GenesisGasLimit, + Root: root, + }, nil, nil, nil) + return block +} + +type GenesisAccount struct { + Address common.Address + Balance *big.Int +} + +func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block { + accountJson := "{" + for i, account := range accounts { + if i != 0 { + accountJson += "," + } + accountJson += fmt.Sprintf(`"0x%x":{"balance":"0x%x"}`, account.Address, account.Balance.Bytes()) + } + accountJson += "}" + + testGenesis := fmt.Sprintf(`{ + "nonce":"0x%x", + "gasLimit":"0x%x", + "difficulty":"0x%x", + "alloc": %s +}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), accountJson) + block, _ := WriteGenesisBlock(db, strings.NewReader(testGenesis)) + return block +} + +// WriteDefaultGenesisBlock assembles the official Ethereum genesis block and +// writes it - along with all associated state - into a chain database. +func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { + return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock())) +} + +// WriteTestNetGenesisBlock assembles the test network genesis block and +// writes it - along with all associated state - into a chain database. +func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { + return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock())) +} + +// DefaultGenesisBlock assembles a JSON string representing the default Ethereum +// genesis block. +func DefaultGenesisBlock() string { + reader, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultGenesisBlock))) + if err != nil { + panic(fmt.Sprintf("failed to access default genesis: %v", err)) + } + blob, err := ioutil.ReadAll(reader) + if err != nil { + panic(fmt.Sprintf("failed to load default genesis: %v", err)) + } + return string(blob) +} + +// DefaultTestnetGenesisBlock assembles a JSON string representing the default Ethereum +// test network genesis block. +func DefaultTestnetGenesisBlock() string { + reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock))) + blob, err := ioutil.ReadAll(reader) + if err != nil { + panic(fmt.Sprintf("failed to load default genesis: %v", err)) + } + return string(blob) +} + +// DevGenesisBlock assembles a JSON string representing a local dev genesis block. +func DevGenesisBlock() string { + reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultDevnetGenesisBlock))) + blob, err := ioutil.ReadAll(reader) + if err != nil { + panic(fmt.Sprintf("failed to load dev genesis: %v", err)) + } + return string(blob) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go new file mode 100644 index 0000000..1dc1893 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go @@ -0,0 +1,532 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + crand "crypto/rand" + "fmt" + "math" + "math/big" + mrand "math/rand" + "runtime" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/hashicorp/golang-lru" +) + +const ( + headerCacheLimit = 512 + tdCacheLimit = 1024 + numberCacheLimit = 2048 +) + +// HeaderChain implements the basic block header chain logic that is shared by +// core.BlockChain and light.LightChain. It is not usable in itself, only as +// a part of either structure. +// It is not thread safe either, the encapsulating chain structures should do +// the necessary mutex locking/unlocking. +type HeaderChain struct { + config *params.ChainConfig + + chainDb ethdb.Database + genesisHeader *types.Header + + currentHeader *types.Header // Current head of the header chain (may be above the block chain!) + currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time) + + headerCache *lru.Cache // Cache for the most recent block headers + tdCache *lru.Cache // Cache for the most recent block total difficulties + numberCache *lru.Cache // Cache for the most recent block numbers + + procInterrupt func() bool + + rand *mrand.Rand + getValidator getHeaderValidatorFn +} + +// getHeaderValidatorFn returns a HeaderValidator interface +type getHeaderValidatorFn func() HeaderValidator + +// NewHeaderChain creates a new HeaderChain structure. +// getValidator should return the parent's validator +// procInterrupt points to the parent's interrupt semaphore +// wg points to the parent's shutdown wait group +func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { + headerCache, _ := lru.New(headerCacheLimit) + tdCache, _ := lru.New(tdCacheLimit) + numberCache, _ := lru.New(numberCacheLimit) + + // Seed a fast but crypto originating random generator + seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + return nil, err + } + + hc := &HeaderChain{ + config: config, + chainDb: chainDb, + headerCache: headerCache, + tdCache: tdCache, + numberCache: numberCache, + procInterrupt: procInterrupt, + rand: mrand.New(mrand.NewSource(seed.Int64())), + getValidator: getValidator, + } + + hc.genesisHeader = hc.GetHeaderByNumber(0) + if hc.genesisHeader == nil { + genesisBlock, err := WriteDefaultGenesisBlock(chainDb) + if err != nil { + return nil, err + } + glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") + hc.genesisHeader = genesisBlock.Header() + } + + hc.currentHeader = hc.genesisHeader + if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) { + if chead := hc.GetHeaderByHash(head); chead != nil { + hc.currentHeader = chead + } + } + hc.currentHeaderHash = hc.currentHeader.Hash() + + return hc, nil +} + +// GetBlockNumber retrieves the block number belonging to the given hash +// from the cache or database +func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 { + if cached, ok := hc.numberCache.Get(hash); ok { + return cached.(uint64) + } + number := GetBlockNumber(hc.chainDb, hash) + if number != missingNumber { + hc.numberCache.Add(hash, number) + } + return number +} + +// WriteHeader writes a header into the local chain, given that its parent is +// already known. If the total difficulty of the newly inserted header becomes +// greater than the current known TD, the canonical chain is re-routed. +// +// Note: This method is not concurrent-safe with inserting blocks simultaneously +// into the chain, as side effects caused by reorganisations cannot be emulated +// without the real blocks. Hence, writing headers directly should only be done +// in two scenarios: pure-header mode of operation (light clients), or properly +// separated header/block phases (non-archive clients). +func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) { + // Cache some values to prevent constant recalculation + var ( + hash = header.Hash() + number = header.Number.Uint64() + ) + // Calculate the total difficulty of the header + ptd := hc.GetTd(header.ParentHash, number-1) + if ptd == nil { + return NonStatTy, ParentError(header.ParentHash) + } + localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64()) + externTd := new(big.Int).Add(header.Difficulty, ptd) + + // Irrelevant of the canonical status, write the td and header to the database + if err := hc.WriteTd(hash, number, externTd); err != nil { + glog.Fatalf("failed to write header total difficulty: %v", err) + } + if err := WriteHeader(hc.chainDb, header); err != nil { + glog.Fatalf("failed to write header contents: %v", err) + } + + // If the total difficulty is higher than our known, add it to the canonical chain + // Second clause in the if statement reduces the vulnerability to selfish mining. + // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf + if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { + // Delete any canonical number assignments above the new head + for i := number + 1; ; i++ { + hash := GetCanonicalHash(hc.chainDb, i) + if hash == (common.Hash{}) { + break + } + DeleteCanonicalHash(hc.chainDb, i) + } + // Overwrite any stale canonical number assignments + var ( + headHash = header.ParentHash + headNumber = header.Number.Uint64() - 1 + headHeader = hc.GetHeader(headHash, headNumber) + ) + for GetCanonicalHash(hc.chainDb, headNumber) != headHash { + WriteCanonicalHash(hc.chainDb, headHash, headNumber) + + headHash = headHeader.ParentHash + headNumber = headHeader.Number.Uint64() - 1 + headHeader = hc.GetHeader(headHash, headNumber) + } + + // Extend the canonical chain with the new header + if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil { + glog.Fatalf("failed to insert header number: %v", err) + } + if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { + glog.Fatalf("failed to insert head header hash: %v", err) + } + + hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header) + + status = CanonStatTy + } else { + status = SideStatTy + } + + hc.headerCache.Add(hash, header) + hc.numberCache.Add(hash, number) + + return +} + +// WhCallback is a callback function for inserting individual headers. +// A callback is used for two reasons: first, in a LightChain, status should be +// processed and light chain events sent, while in a BlockChain this is not +// necessary since chain events are sent after inserting blocks. Second, the +// header writes should be protected by the parent chain mutex individually. +type WhCallback func(*types.Header) error + +// InsertHeaderChain attempts to insert the given header chain in to the local +// chain, possibly creating a reorg. If an error is returned, it will return the +// index number of the failing header as well an error describing what went wrong. +// +// The verify parameter can be used to fine tune whether nonce verification +// should be done or not. The reason behind the optional check is because some +// of the header retrieval mechanisms already need to verfy nonces, as well as +// because nonces can be verified sparsely, not needing to check each. +func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) { + // Do a sanity check that the provided chain is actually ordered and linked + for i := 1; i < len(chain); i++ { + if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { + // Chain broke ancestry, log a messge (programming error) and skip insertion + failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", + i-1, chain[i-1].Number.Uint64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].Number.Uint64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash.Bytes()[:4]) + + glog.V(logger.Error).Info(failure.Error()) + return 0, failure + } + } + // Collect some import statistics to report on + stats := struct{ processed, ignored int }{} + start := time.Now() + + // Generate the list of headers that should be POW verified + verify := make([]bool, len(chain)) + for i := 0; i < len(verify)/checkFreq; i++ { + index := i*checkFreq + hc.rand.Intn(checkFreq) + if index >= len(verify) { + index = len(verify) - 1 + } + verify[index] = true + } + verify[len(verify)-1] = true // Last should always be verified to avoid junk + + // Create the header verification task queue and worker functions + tasks := make(chan int, len(chain)) + for i := 0; i < len(chain); i++ { + tasks <- i + } + close(tasks) + + errs, failed := make([]error, len(tasks)), int32(0) + process := func(worker int) { + for index := range tasks { + header, hash := chain[index], chain[index].Hash() + + // Short circuit insertion if shutting down or processing failed + if hc.procInterrupt() { + return + } + if atomic.LoadInt32(&failed) > 0 { + return + } + // Short circuit if the header is bad or already known + if BadHashes[hash] { + errs[index] = BadHashError(hash) + atomic.AddInt32(&failed, 1) + return + } + if hc.HasHeader(hash) { + continue + } + // Verify that the header honors the chain parameters + checkPow := verify[index] + + var err error + if index == 0 { + err = hc.getValidator().ValidateHeader(header, hc.GetHeader(header.ParentHash, header.Number.Uint64()-1), checkPow) + } else { + err = hc.getValidator().ValidateHeader(header, chain[index-1], checkPow) + } + if err != nil { + errs[index] = err + atomic.AddInt32(&failed, 1) + return + } + } + } + // Start as many worker threads as goroutines allowed + pending := new(sync.WaitGroup) + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + pending.Add(1) + go func(id int) { + defer pending.Done() + process(id) + }(i) + } + pending.Wait() + + // If anything failed, report + if failed > 0 { + for i, err := range errs { + if err != nil { + return i, err + } + } + } + // All headers passed verification, import them into the database + for i, header := range chain { + // Short circuit insertion if shutting down + if hc.procInterrupt() { + glog.V(logger.Debug).Infoln("premature abort during header chain processing") + break + } + hash := header.Hash() + + // If the header's already known, skip it, otherwise store + if hc.HasHeader(hash) { + stats.ignored++ + continue + } + if err := writeHeader(header); err != nil { + return i, err + } + stats.processed++ + } + // Report some public statistics so the user has a clue what's going on + first, last := chain[0], chain[len(chain)-1] + + ignored := "" + if stats.ignored > 0 { + ignored = fmt.Sprintf(" (%d ignored)", stats.ignored) + } + glog.V(logger.Info).Infof("imported %4d headers%s in %9v. #%v [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) + + return 0, nil +} + +// GetBlockHashesFromHash retrieves a number of block hashes starting at a given +// hash, fetching towards the genesis block. +func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + // Get the origin header from which to fetch + header := hc.GetHeaderByHash(hash) + if header == nil { + return nil + } + // Iterate the headers until enough is collected or the genesis reached + chain := make([]common.Hash, 0, max) + for i := uint64(0); i < max; i++ { + next := header.ParentHash + if header = hc.GetHeader(next, header.Number.Uint64()-1); header == nil { + break + } + chain = append(chain, next) + if header.Number.Cmp(common.Big0) == 0 { + break + } + } + return chain +} + +// GetTd retrieves a block's total difficulty in the canonical chain from the +// database by hash and number, caching it if found. +func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int { + // Short circuit if the td's already in the cache, retrieve otherwise + if cached, ok := hc.tdCache.Get(hash); ok { + return cached.(*big.Int) + } + td := GetTd(hc.chainDb, hash, number) + if td == nil { + return nil + } + // Cache the found body for next time and return + hc.tdCache.Add(hash, td) + return td +} + +// GetTdByHash retrieves a block's total difficulty in the canonical chain from the +// database by hash, caching it if found. +func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int { + return hc.GetTd(hash, hc.GetBlockNumber(hash)) +} + +// WriteTd stores a block's total difficulty into the database, also caching it +// along the way. +func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error { + if err := WriteTd(hc.chainDb, hash, number, td); err != nil { + return err + } + hc.tdCache.Add(hash, new(big.Int).Set(td)) + return nil +} + +// GetHeader retrieves a block header from the database by hash and number, +// caching it if found. +func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { + // Short circuit if the header's already in the cache, retrieve otherwise + if header, ok := hc.headerCache.Get(hash); ok { + return header.(*types.Header) + } + header := GetHeader(hc.chainDb, hash, number) + if header == nil { + return nil + } + // Cache the found header for next time and return + hc.headerCache.Add(hash, header) + return header +} + +// GetHeaderByHash retrieves a block header from the database by hash, caching it if +// found. +func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { + return hc.GetHeader(hash, hc.GetBlockNumber(hash)) +} + +// HasHeader checks if a block header is present in the database or not, caching +// it if present. +func (hc *HeaderChain) HasHeader(hash common.Hash) bool { + return hc.GetHeaderByHash(hash) != nil +} + +// GetHeaderByNumber retrieves a block header from the database by number, +// caching it (associated with its hash) if found. +func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header { + hash := GetCanonicalHash(hc.chainDb, number) + if hash == (common.Hash{}) { + return nil + } + return hc.GetHeader(hash, number) +} + +// CurrentHeader retrieves the current head header of the canonical chain. The +// header is retrieved from the HeaderChain's internal cache. +func (hc *HeaderChain) CurrentHeader() *types.Header { + return hc.currentHeader +} + +// SetCurrentHeader sets the current head header of the canonical chain. +func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { + if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { + glog.Fatalf("failed to insert head header hash: %v", err) + } + hc.currentHeader = head + hc.currentHeaderHash = head.Hash() +} + +// DeleteCallback is a callback function that is called by SetHead before +// each header is deleted. +type DeleteCallback func(common.Hash, uint64) + +// SetHead rewinds the local chain to a new head. Everything above the new head +// will be deleted and the new one set. +func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { + height := uint64(0) + if hc.currentHeader != nil { + height = hc.currentHeader.Number.Uint64() + } + + for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head { + hash := hc.currentHeader.Hash() + num := hc.currentHeader.Number.Uint64() + if delFn != nil { + delFn(hash, num) + } + DeleteHeader(hc.chainDb, hash, num) + DeleteTd(hc.chainDb, hash, num) + hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash, hc.currentHeader.Number.Uint64()-1) + } + // Roll back the canonical chain numbering + for i := height; i > head; i-- { + DeleteCanonicalHash(hc.chainDb, i) + } + // Clear out any stale content from the caches + hc.headerCache.Purge() + hc.tdCache.Purge() + hc.numberCache.Purge() + + if hc.currentHeader == nil { + hc.currentHeader = hc.genesisHeader + } + hc.currentHeaderHash = hc.currentHeader.Hash() + + if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { + glog.Fatalf("failed to reset head header hash: %v", err) + } +} + +// SetGenesis sets a new genesis block header for the chain +func (hc *HeaderChain) SetGenesis(head *types.Header) { + hc.genesisHeader = head +} + +// headerValidator is responsible for validating block headers +// +// headerValidator implements HeaderValidator. +type headerValidator struct { + config *params.ChainConfig + hc *HeaderChain // Canonical header chain + Pow pow.PoW // Proof of work used for validating +} + +// NewBlockValidator returns a new block validator which is safe for re-use +func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { + return &headerValidator{ + config: config, + Pow: pow, + hc: chain, + } +} + +// ValidateHeader validates the given header and, depending on the pow arg, +// checks the proof of work of the given header. Returns an error if the +// validation failed. +func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { + // Short circuit if the parent is missing. + if parent == nil { + return ParentError(header.ParentHash) + } + // Short circuit if the header's already known or its parent missing + if v.hc.HasHeader(header.Hash()) { + return nil + } + return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/helper_test.go b/vendor/github.com/ethereum/go-ethereum/core/helper_test.go new file mode 100644 index 0000000..fd6a549 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/helper_test.go @@ -0,0 +1,96 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "container/list" + "fmt" + + "github.com/ethereum/go-ethereum/core/types" + // "github.com/ethereum/go-ethereum/crypto" + + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" +) + +// Implement our EthTest Manager +type TestManager struct { + // stateManager *StateManager + eventMux *event.TypeMux + + db ethdb.Database + txPool *TxPool + blockChain *BlockChain + Blocks []*types.Block +} + +func (s *TestManager) IsListening() bool { + return false +} + +func (s *TestManager) IsMining() bool { + return false +} + +func (s *TestManager) PeerCount() int { + return 0 +} + +func (s *TestManager) Peers() *list.List { + return list.New() +} + +func (s *TestManager) BlockChain() *BlockChain { + return s.blockChain +} + +func (tm *TestManager) TxPool() *TxPool { + return tm.txPool +} + +// func (tm *TestManager) StateManager() *StateManager { +// return tm.stateManager +// } + +func (tm *TestManager) EventMux() *event.TypeMux { + return tm.eventMux +} + +// func (tm *TestManager) KeyManager() *crypto.KeyManager { +// return nil +// } + +func (tm *TestManager) Db() ethdb.Database { + return tm.db +} + +func NewTestManager() *TestManager { + db, err := ethdb.NewMemDatabase() + if err != nil { + fmt.Println("Could not create mem-db, failing") + return nil + } + + testManager := &TestManager{} + testManager.eventMux = new(event.TypeMux) + testManager.db = db + // testManager.txPool = NewTxPool(testManager) + // testManager.blockChain = NewBlockChain(testManager) + // testManager.stateManager = NewStateManager(testManager) + + return testManager +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/dump.go b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go new file mode 100644 index 0000000..8294d61 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/dump.go @@ -0,0 +1,80 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "encoding/json" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +type DumpAccount struct { + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + Root string `json:"root"` + CodeHash string `json:"codeHash"` + Code string `json:"code"` + Storage map[string]string `json:"storage"` +} + +type Dump struct { + Root string `json:"root"` + Accounts map[string]DumpAccount `json:"accounts"` +} + +func (self *StateDB) RawDump() Dump { + dump := Dump{ + Root: common.Bytes2Hex(self.trie.Root()), + Accounts: make(map[string]DumpAccount), + } + + it := self.trie.Iterator() + for it.Next() { + addr := self.trie.GetKey(it.Key) + var data Account + if err := rlp.DecodeBytes(it.Value, &data); err != nil { + panic(err) + } + + obj := newObject(nil, common.BytesToAddress(addr), data, nil) + account := DumpAccount{ + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: common.Bytes2Hex(data.Root[:]), + CodeHash: common.Bytes2Hex(data.CodeHash), + Code: common.Bytes2Hex(obj.Code(self.db)), + Storage: make(map[string]string), + } + storageIt := obj.getTrie(self.db).Iterator() + for storageIt.Next() { + account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) + } + dump.Accounts[common.Bytes2Hex(addr)] = account + } + return dump +} + +func (self *StateDB) Dump() []byte { + json, err := json.MarshalIndent(self.RawDump(), "", " ") + if err != nil { + fmt.Println("dump err", err) + } + + return json +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go new file mode 100644 index 0000000..170aec9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/iterator.go @@ -0,0 +1,159 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// NodeIterator is an iterator to traverse the entire state trie post-order, +// including all of the contract code and contract state tries. +type NodeIterator struct { + state *StateDB // State being iterated + + stateIt trie.NodeIterator // Primary iterator for the global state trie + dataIt trie.NodeIterator // Secondary iterator for the data trie of a contract + + accountHash common.Hash // Hash of the node containing the account + codeHash common.Hash // Hash of the contract source code + code []byte // Source code associated with a contract + + Hash common.Hash // Hash of the current entry being iterated (nil if not standalone) + Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) + + Error error // Failure set in case of an internal error in the iterator +} + +// NewNodeIterator creates an post-order state node iterator. +func NewNodeIterator(state *StateDB) *NodeIterator { + return &NodeIterator{ + state: state, + } +} + +// Next moves the iterator to the next node, returning whether there are any +// further nodes. In case of an internal error this method returns false and +// sets the Error field to the encountered failure. +func (it *NodeIterator) Next() bool { + // If the iterator failed previously, don't do anything + if it.Error != nil { + return false + } + // Otherwise step forward with the iterator and report any errors + if err := it.step(); err != nil { + it.Error = err + return false + } + return it.retrieve() +} + +// step moves the iterator to the next entry of the state trie. +func (it *NodeIterator) step() error { + // Abort if we reached the end of the iteration + if it.state == nil { + return nil + } + // Initialize the iterator if we've just started + if it.stateIt == nil { + it.stateIt = it.state.trie.NodeIterator() + } + // If we had data nodes previously, we surely have at least state nodes + if it.dataIt != nil { + if cont := it.dataIt.Next(true); !cont { + if it.dataIt.Error() != nil { + return it.dataIt.Error() + } + it.dataIt = nil + } + return nil + } + // If we had source code previously, discard that + if it.code != nil { + it.code = nil + return nil + } + // Step to the next state trie node, terminating if we're out of nodes + if cont := it.stateIt.Next(true); !cont { + if it.stateIt.Error() != nil { + return it.stateIt.Error() + } + it.state, it.stateIt = nil, nil + return nil + } + // If the state trie node is an internal entry, leave as is + if !it.stateIt.Leaf() { + return nil + } + // Otherwise we've reached an account node, initiate data iteration + var account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte + } + if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil { + return err + } + dataTrie, err := trie.New(account.Root, it.state.db) + if err != nil { + return err + } + it.dataIt = trie.NewNodeIterator(dataTrie) + if !it.dataIt.Next(true) { + it.dataIt = nil + } + if !bytes.Equal(account.CodeHash, emptyCodeHash) { + it.codeHash = common.BytesToHash(account.CodeHash) + it.code, err = it.state.db.Get(account.CodeHash) + if err != nil { + return fmt.Errorf("code %x: %v", account.CodeHash, err) + } + } + it.accountHash = it.stateIt.Parent() + return nil +} + +// retrieve pulls and caches the current state entry the iterator is traversing. +// The method returns whether there are any more data left for inspection. +func (it *NodeIterator) retrieve() bool { + // Clear out any previously set values + it.Hash = common.Hash{} + + // If the iteration's done, return no available data + if it.state == nil { + return false + } + // Otherwise retrieve the current entry + switch { + case it.dataIt != nil: + it.Hash, it.Parent = it.dataIt.Hash(), it.dataIt.Parent() + if it.Parent == (common.Hash{}) { + it.Parent = it.accountHash + } + case it.code != nil: + it.Hash, it.Parent = it.codeHash, it.accountHash + case it.stateIt != nil: + it.Hash, it.Parent = it.stateIt.Hash(), it.stateIt.Parent() + } + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/iterator_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/iterator_test.go new file mode 100644 index 0000000..aa9c5b7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/iterator_test.go @@ -0,0 +1,57 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +// Tests that the node iterator indeed walks over the entire database contents. +func TestNodeIteratorCoverage(t *testing.T) { + // Create some arbitrary test state to iterate + db, root, _ := makeTestState() + + state, err := New(root, db) + if err != nil { + t.Fatalf("failed to create state trie at %x: %v", root, err) + } + // Gather all the node hashes found by the iterator + hashes := make(map[common.Hash]struct{}) + for it := NewNodeIterator(state); it.Next(); { + if it.Hash != (common.Hash{}) { + hashes[it.Hash] = struct{}{} + } + } + // Cross check the hashes and the database itself + for hash := range hashes { + if _, err := db.Get(hash.Bytes()); err != nil { + t.Errorf("failed to retrieve reported node %x: %v", hash, err) + } + } + for _, key := range db.(*ethdb.MemDatabase).Keys() { + if bytes.HasPrefix(key, []byte("secure-key-")) { + continue + } + if _, ok := hashes[common.BytesToHash(key)]; !ok { + t.Errorf("state entry not reported %x", key) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/journal.go b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go new file mode 100644 index 0000000..5cd4147 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/journal.go @@ -0,0 +1,136 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type journalEntry interface { + undo(*StateDB) +} + +type journal []journalEntry + +type ( + // Changes to the account trie. + createObjectChange struct { + account *common.Address + } + resetObjectChange struct { + prev *stateObject + } + suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance *big.Int + } + + // Changes to individual accounts. + balanceChange struct { + account *common.Address + prev *big.Int + } + nonceChange struct { + account *common.Address + prev uint64 + } + storageChange struct { + account *common.Address + key, prevalue common.Hash + } + codeChange struct { + account *common.Address + prevcode, prevhash []byte + } + + // Changes to other state values. + refundChange struct { + prev *big.Int + } + addLogChange struct { + txhash common.Hash + } + addPreimageChange struct { + hash common.Hash + } + touchChange struct { + account *common.Address + prev bool + } +) + +func (ch createObjectChange) undo(s *StateDB) { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) +} + +func (ch resetObjectChange) undo(s *StateDB) { + s.setStateObject(ch.prev) +} + +func (ch suicideChange) undo(s *StateDB) { + obj := s.getStateObject(*ch.account) + if obj != nil { + obj.suicided = ch.prev + obj.setBalance(ch.prevbalance) + } +} + +var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") + +func (ch touchChange) undo(s *StateDB) { + if !ch.prev && *ch.account != ripemd { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) + } +} + +func (ch balanceChange) undo(s *StateDB) { + s.getStateObject(*ch.account).setBalance(ch.prev) +} + +func (ch nonceChange) undo(s *StateDB) { + s.getStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch codeChange) undo(s *StateDB) { + s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch storageChange) undo(s *StateDB) { + s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch refundChange) undo(s *StateDB) { + s.refund = ch.prev +} + +func (ch addLogChange) undo(s *StateDB) { + logs := s.logs[ch.txhash] + if len(logs) == 1 { + delete(s.logs, ch.txhash) + } else { + s.logs[ch.txhash] = logs[:len(logs)-1] + } +} + +func (ch addPreimageChange) undo(s *StateDB) { + delete(s.preimages, ch.hash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/main_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/main_test.go new file mode 100644 index 0000000..cd96610 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/main_test.go @@ -0,0 +1,25 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "testing" + + checker "gopkg.in/check.v1" +) + +func Test(t *testing.T) { checker.TestingT(t) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go new file mode 100644 index 0000000..1390ef4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state.go @@ -0,0 +1,143 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +type account struct { + stateObject *stateObject + nstart uint64 + nonces []bool +} + +type ManagedState struct { + *StateDB + + mu sync.RWMutex + + accounts map[common.Address]*account +} + +// ManagedState returns a new managed state with the statedb as it's backing layer +func ManageState(statedb *StateDB) *ManagedState { + return &ManagedState{ + StateDB: statedb.Copy(), + accounts: make(map[common.Address]*account), + } +} + +// SetState sets the backing layer of the managed state +func (ms *ManagedState) SetState(statedb *StateDB) { + ms.mu.Lock() + defer ms.mu.Unlock() + ms.StateDB = statedb +} + +// RemoveNonce removed the nonce from the managed state and all future pending nonces +func (ms *ManagedState) RemoveNonce(addr common.Address, n uint64) { + if ms.hasAccount(addr) { + ms.mu.Lock() + defer ms.mu.Unlock() + + account := ms.getAccount(addr) + if n-account.nstart <= uint64(len(account.nonces)) { + reslice := make([]bool, n-account.nstart) + copy(reslice, account.nonces[:n-account.nstart]) + account.nonces = reslice + } + } +} + +// NewNonce returns the new canonical nonce for the managed account +func (ms *ManagedState) NewNonce(addr common.Address) uint64 { + ms.mu.Lock() + defer ms.mu.Unlock() + + account := ms.getAccount(addr) + for i, nonce := range account.nonces { + if !nonce { + return account.nstart + uint64(i) + } + } + account.nonces = append(account.nonces, true) + + return uint64(len(account.nonces)-1) + account.nstart +} + +// GetNonce returns the canonical nonce for the managed or unmanaged account. +// +// Because GetNonce mutates the DB, we must take a write lock. +func (ms *ManagedState) GetNonce(addr common.Address) uint64 { + ms.mu.Lock() + defer ms.mu.Unlock() + + if ms.hasAccount(addr) { + account := ms.getAccount(addr) + return uint64(len(account.nonces)) + account.nstart + } else { + return ms.StateDB.GetNonce(addr) + } +} + +// SetNonce sets the new canonical nonce for the managed state +func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) { + ms.mu.Lock() + defer ms.mu.Unlock() + + so := ms.GetOrNewStateObject(addr) + so.SetNonce(nonce) + + ms.accounts[addr] = newAccount(so) +} + +// HasAccount returns whether the given address is managed or not +func (ms *ManagedState) HasAccount(addr common.Address) bool { + ms.mu.RLock() + defer ms.mu.RUnlock() + return ms.hasAccount(addr) +} + +func (ms *ManagedState) hasAccount(addr common.Address) bool { + _, ok := ms.accounts[addr] + return ok +} + +// populate the managed state +func (ms *ManagedState) getAccount(addr common.Address) *account { + if account, ok := ms.accounts[addr]; !ok { + so := ms.GetOrNewStateObject(addr) + ms.accounts[addr] = newAccount(so) + } else { + // Always make sure the state account nonce isn't actually higher + // than the tracked one. + so := ms.StateDB.getStateObject(addr) + if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() { + ms.accounts[addr] = newAccount(so) + } + + } + + return ms.accounts[addr] +} + +func newAccount(so *stateObject) *account { + return &account{so, so.Nonce(), nil} +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/managed_state_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state_test.go new file mode 100644 index 0000000..ea5737a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/managed_state_test.go @@ -0,0 +1,124 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +var addr = common.BytesToAddress([]byte("test")) + +func create() (*ManagedState, *account) { + db, _ := ethdb.NewMemDatabase() + statedb, _ := New(common.Hash{}, db) + ms := ManageState(statedb) + ms.StateDB.SetNonce(addr, 100) + ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr)) + return ms, ms.accounts[addr] +} + +func TestNewNonce(t *testing.T) { + ms, _ := create() + + nonce := ms.NewNonce(addr) + if nonce != 100 { + t.Error("expected nonce 100. got", nonce) + } + + nonce = ms.NewNonce(addr) + if nonce != 101 { + t.Error("expected nonce 101. got", nonce) + } +} + +func TestRemove(t *testing.T) { + ms, account := create() + + nn := make([]bool, 10) + for i := range nn { + nn[i] = true + } + account.nonces = append(account.nonces, nn...) + + i := uint64(5) + ms.RemoveNonce(addr, account.nstart+i) + if len(account.nonces) != 5 { + t.Error("expected", i, "'th index to be false") + } +} + +func TestReuse(t *testing.T) { + ms, account := create() + + nn := make([]bool, 10) + for i := range nn { + nn[i] = true + } + account.nonces = append(account.nonces, nn...) + + i := uint64(5) + ms.RemoveNonce(addr, account.nstart+i) + nonce := ms.NewNonce(addr) + if nonce != 105 { + t.Error("expected nonce to be 105. got", nonce) + } +} + +func TestRemoteNonceChange(t *testing.T) { + ms, account := create() + nn := make([]bool, 10) + for i := range nn { + nn[i] = true + } + account.nonces = append(account.nonces, nn...) + ms.NewNonce(addr) + + ms.StateDB.stateObjects[addr].data.Nonce = 200 + nonce := ms.NewNonce(addr) + if nonce != 200 { + t.Error("expected nonce after remote update to be", 200, "got", nonce) + } + ms.NewNonce(addr) + ms.NewNonce(addr) + ms.NewNonce(addr) + ms.StateDB.stateObjects[addr].data.Nonce = 200 + nonce = ms.NewNonce(addr) + if nonce != 204 { + t.Error("expected nonce after remote update to be", 204, "got", nonce) + } +} + +func TestSetNonce(t *testing.T) { + ms, _ := create() + + var addr common.Address + ms.SetNonce(addr, 10) + + if ms.GetNonce(addr) != 10 { + t.Error("Expected nonce of 10, got", ms.GetNonce(addr)) + } + + addr[0] = 1 + ms.StateDB.SetNonce(addr, 1) + + if ms.GetNonce(addr) != 1 { + t.Error("Expected nonce of 1, got", ms.GetNonce(addr)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go new file mode 100644 index 0000000..4fb69b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go @@ -0,0 +1,383 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +var emptyCodeHash = crypto.Keccak256(nil) + +type Code []byte + +func (self Code) String() string { + return string(self) //strings.Join(Disassemble(self), " ") +} + +type Storage map[common.Hash]common.Hash + +func (self Storage) String() (str string) { + for key, value := range self { + str += fmt.Sprintf("%X : %X\n", key, value) + } + + return +} + +func (self Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range self { + cpy[key] = value + } + + return cpy +} + +// stateObject represents an Ethereum account which is being modified. +// +// The usage pattern is as follows: +// First you need to obtain a state object. +// Account values can be accessed and modified through the object. +// Finally, call CommitTrie to write the modified storage trie into a database. +type stateObject struct { + address common.Address // Ethereum address of this account + data Account + db *StateDB + + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error + + // Write caches. + trie *trie.SecureTrie // storage trie, which becomes non-nil on first access + code Code // contract bytecode, which gets set when code is loaded + + cachedStorage Storage // Storage entry cache to avoid duplicate reads + dirtyStorage Storage // Storage entries that need to be flushed to disk + + // Cache flags. + // When an object is marked suicided it will be delete from the trie + // during the "update" phase of the state transition. + dirtyCode bool // true if the code was updated + suicided bool + touched bool + deleted bool + onDirty func(addr common.Address) // Callback method to mark a state object newly dirty +} + +// empty returns whether the account is considered empty. +func (s *stateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + +// Account is the Ethereum consensus representation of accounts. +// These objects are stored in the main account trie. +type Account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash // merkle root of the storage trie + CodeHash []byte +} + +// newObject creates a state object. +func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject { + if data.Balance == nil { + data.Balance = new(big.Int) + } + if data.CodeHash == nil { + data.CodeHash = emptyCodeHash + } + return &stateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty} +} + +// EncodeRLP implements rlp.Encoder. +func (c *stateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, c.data) +} + +// setError remembers the first non-nil error it is called with. +func (self *stateObject) setError(err error) { + if self.dbErr == nil { + self.dbErr = err + } +} + +func (self *stateObject) markSuicided() { + self.suicided = true + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance()) + } +} + +func (c *stateObject) touch() { + c.db.journal = append(c.db.journal, touchChange{ + account: &c.address, + prev: c.touched, + }) + if c.onDirty != nil { + c.onDirty(c.Address()) + c.onDirty = nil + } + c.touched = true +} + +func (c *stateObject) getTrie(db trie.Database) *trie.SecureTrie { + if c.trie == nil { + var err error + c.trie, err = trie.NewSecure(c.data.Root, db, 0) + if err != nil { + c.trie, _ = trie.NewSecure(common.Hash{}, db, 0) + c.setError(fmt.Errorf("can't create storage trie: %v", err)) + } + } + return c.trie +} + +// GetState returns a value in account storage. +func (self *stateObject) GetState(db trie.Database, key common.Hash) common.Hash { + value, exists := self.cachedStorage[key] + if exists { + return value + } + // Load from DB in case it is missing. + if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 { + _, content, _, err := rlp.Split(enc) + if err != nil { + self.setError(err) + } + value.SetBytes(content) + } + if (value != common.Hash{}) { + self.cachedStorage[key] = value + } + return value +} + +// SetState updates a value in account storage. +func (self *stateObject) SetState(db trie.Database, key, value common.Hash) { + self.db.journal = append(self.db.journal, storageChange{ + account: &self.address, + key: key, + prevalue: self.GetState(db, key), + }) + self.setState(key, value) +} + +func (self *stateObject) setState(key, value common.Hash) { + self.cachedStorage[key] = value + self.dirtyStorage[key] = value + + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +// updateTrie writes cached storage modifications into the object's storage trie. +func (self *stateObject) updateTrie(db trie.Database) { + tr := self.getTrie(db) + for key, value := range self.dirtyStorage { + delete(self.dirtyStorage, key) + if (value == common.Hash{}) { + tr.Delete(key[:]) + continue + } + // Encoding []byte cannot fail, ok to ignore the error. + v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + tr.Update(key[:], v) + } +} + +// UpdateRoot sets the trie root to the current root hash of +func (self *stateObject) updateRoot(db trie.Database) { + self.updateTrie(db) + self.data.Root = self.trie.Hash() +} + +// CommitTrie the storage trie of the object to dwb. +// This updates the trie root. +func (self *stateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error { + self.updateTrie(db) + if self.dbErr != nil { + return self.dbErr + } + root, err := self.trie.CommitTo(dbw) + if err == nil { + self.data.Root = root + } + return err +} + +// AddBalance removes amount from c's balance. +// It is used to add funds to the destination account of a transfer. +func (c *stateObject) AddBalance(amount *big.Int) { + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. + if amount.Cmp(common.Big0) == 0 { + if c.empty() { + c.touch() + } + + return + } + c.SetBalance(new(big.Int).Add(c.Balance(), amount)) + + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) + } +} + +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. +func (c *stateObject) SubBalance(amount *big.Int) { + if amount.Cmp(common.Big0) == 0 { + return + } + c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) + + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount) + } +} + +func (self *stateObject) SetBalance(amount *big.Int) { + self.db.journal = append(self.db.journal, balanceChange{ + account: &self.address, + prev: new(big.Int).Set(self.data.Balance), + }) + self.setBalance(amount) +} + +func (self *stateObject) setBalance(amount *big.Int) { + self.data.Balance = amount + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +// Return the gas back to the origin. Used by the Virtual machine or Closures +func (c *stateObject) ReturnGas(gas *big.Int) {} + +func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject { + stateObject := newObject(db, self.address, self.data, onDirty) + stateObject.trie = self.trie + stateObject.code = self.code + stateObject.dirtyStorage = self.dirtyStorage.Copy() + stateObject.cachedStorage = self.dirtyStorage.Copy() + stateObject.suicided = self.suicided + stateObject.dirtyCode = self.dirtyCode + stateObject.deleted = self.deleted + return stateObject +} + +// +// Attribute accessors +// + +// Returns the address of the contract/account +func (c *stateObject) Address() common.Address { + return c.address +} + +// Code returns the contract code associated with this object, if any. +func (self *stateObject) Code(db trie.Database) []byte { + if self.code != nil { + return self.code + } + if bytes.Equal(self.CodeHash(), emptyCodeHash) { + return nil + } + code, err := db.Get(self.CodeHash()) + if err != nil { + self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err)) + } + self.code = code + return code +} + +func (self *stateObject) SetCode(codeHash common.Hash, code []byte) { + prevcode := self.Code(self.db.db) + self.db.journal = append(self.db.journal, codeChange{ + account: &self.address, + prevhash: self.CodeHash(), + prevcode: prevcode, + }) + self.setCode(codeHash, code) +} + +func (self *stateObject) setCode(codeHash common.Hash, code []byte) { + self.code = code + self.data.CodeHash = codeHash[:] + self.dirtyCode = true + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +func (self *stateObject) SetNonce(nonce uint64) { + self.db.journal = append(self.db.journal, nonceChange{ + account: &self.address, + prev: self.data.Nonce, + }) + self.setNonce(nonce) +} + +func (self *stateObject) setNonce(nonce uint64) { + self.data.Nonce = nonce + if self.onDirty != nil { + self.onDirty(self.Address()) + self.onDirty = nil + } +} + +func (self *stateObject) CodeHash() []byte { + return self.data.CodeHash +} + +func (self *stateObject) Balance() *big.Int { + return self.data.Balance +} + +func (self *stateObject) Nonce() uint64 { + return self.data.Nonce +} + +// Never called, but must be present to allow stateObject to be used +// as a vm.Account interface that also satisfies the vm.ContractRef +// interface. Interfaces are awesome. +func (self *stateObject) Value() *big.Int { + panic("Value on stateObject should never be called") +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_test.go new file mode 100644 index 0000000..3bc63c1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_test.go @@ -0,0 +1,237 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "math/big" + "testing" + + checker "gopkg.in/check.v1" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" +) + +type StateSuite struct { + state *StateDB +} + +var _ = checker.Suite(&StateSuite{}) + +var toAddr = common.BytesToAddress + +func (s *StateSuite) TestDump(c *checker.C) { + // generate a few entries + obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01})) + obj1.AddBalance(big.NewInt(22)) + obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02})) + obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) + obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02})) + obj3.SetBalance(big.NewInt(44)) + + // write some of them to the trie + s.state.updateStateObject(obj1) + s.state.updateStateObject(obj2) + s.state.Commit(false) + + // check that dump contains the state objects that are in trie + got := string(s.state.Dump()) + want := `{ + "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "22", + "nonce": 0, + "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "code": "", + "storage": {} + }, + "0000000000000000000000000000000000000002": { + "balance": "44", + "nonce": 0, + "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "code": "", + "storage": {} + }, + "0000000000000000000000000000000000000102": { + "balance": "0", + "nonce": 0, + "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3", + "code": "03030303030303", + "storage": {} + } + } +}` + if got != want { + c.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want) + } +} + +func (s *StateSuite) SetUpTest(c *checker.C) { + db, _ := ethdb.NewMemDatabase() + s.state, _ = New(common.Hash{}, db) +} + +func TestNull(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + + address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") + state.CreateAccount(address) + //value := common.FromHex("0x823140710bf13990e4500136726d8b55") + var value common.Hash + state.SetState(address, common.Hash{}, value) + state.Commit(false) + value = state.GetState(address, common.Hash{}) + if !common.EmptyHash(value) { + t.Errorf("expected empty hash. got %x", value) + } +} + +func (s *StateSuite) TestSnapshot(c *checker.C) { + stateobjaddr := toAddr([]byte("aa")) + var storageaddr common.Hash + data1 := common.BytesToHash([]byte{42}) + data2 := common.BytesToHash([]byte{43}) + + // set initial state object value + s.state.SetState(stateobjaddr, storageaddr, data1) + // get snapshot of current state + snapshot := s.state.Snapshot() + + // set new state object value + s.state.SetState(stateobjaddr, storageaddr, data2) + // restore snapshot + s.state.RevertToSnapshot(snapshot) + + // get state storage value + res := s.state.GetState(stateobjaddr, storageaddr) + + c.Assert(data1, checker.DeepEquals, res) +} + +func TestSnapshotEmpty(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + state.RevertToSnapshot(state.Snapshot()) +} + +// use testing instead of checker because checker does not support +// printing/logging in tests (-check.vv does not work) +func TestSnapshot2(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + + stateobjaddr0 := toAddr([]byte("so0")) + stateobjaddr1 := toAddr([]byte("so1")) + var storageaddr common.Hash + + data0 := common.BytesToHash([]byte{17}) + data1 := common.BytesToHash([]byte{18}) + + state.SetState(stateobjaddr0, storageaddr, data0) + state.SetState(stateobjaddr1, storageaddr, data1) + + // db, trie are already non-empty values + so0 := state.getStateObject(stateobjaddr0) + so0.SetBalance(big.NewInt(42)) + so0.SetNonce(43) + so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) + so0.suicided = false + so0.deleted = false + state.setStateObject(so0) + + root, _ := state.Commit(false) + state.Reset(root) + + // and one with deleted == true + so1 := state.getStateObject(stateobjaddr1) + so1.SetBalance(big.NewInt(52)) + so1.SetNonce(53) + so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) + so1.suicided = true + so1.deleted = true + state.setStateObject(so1) + + so1 = state.getStateObject(stateobjaddr1) + if so1 != nil { + t.Fatalf("deleted object not nil when getting") + } + + snapshot := state.Snapshot() + state.RevertToSnapshot(snapshot) + + so0Restored := state.getStateObject(stateobjaddr0) + // Update lazily-loaded values before comparing. + so0Restored.GetState(db, storageaddr) + so0Restored.Code(db) + // non-deleted is equal (restored) + compareStateObjects(so0Restored, so0, t) + + // deleted should be nil, both before and after restore of state copy + so1Restored := state.getStateObject(stateobjaddr1) + if so1Restored != nil { + t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored) + } +} + +func compareStateObjects(so0, so1 *stateObject, t *testing.T) { + if so0.Address() != so1.Address() { + t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address) + } + if so0.Balance().Cmp(so1.Balance()) != 0 { + t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance()) + } + if so0.Nonce() != so1.Nonce() { + t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce()) + } + if so0.data.Root != so1.data.Root { + t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:]) + } + if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) { + t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) + } + if !bytes.Equal(so0.code, so1.code) { + t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) + } + + if len(so1.cachedStorage) != len(so0.cachedStorage) { + t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage)) + } + for k, v := range so1.cachedStorage { + if so0.cachedStorage[k] != v { + t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v) + } + } + for k, v := range so0.cachedStorage { + if so1.cachedStorage[k] != v { + t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) + } + } + + if so0.suicided != so1.suicided { + t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided) + } + if so0.deleted != so1.deleted { + t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go new file mode 100644 index 0000000..cae2dc4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -0,0 +1,664 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package state provides a caching layer atop the Ethereum state trie. +package state + +import ( + "fmt" + "math/big" + "sort" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" +) + +// Trie cache generation limit after which to evic trie nodes from memory. +var MaxTrieCacheGen = uint16(120) + +const ( + // Number of past tries to keep. This value is chosen such that + // reasonable chain reorg depths will hit an existing trie. + maxPastTries = 12 + + // Number of codehash->size associations to keep. + codeSizeCacheSize = 100000 +) + +type revision struct { + id int + journalIndex int +} + +// StateDBs within the ethereum protocol are used to store anything +// within the merkle trie. StateDBs take care of caching and storing +// nested states. It's the general query interface to retrieve: +// * Contracts +// * Accounts +type StateDB struct { + db ethdb.Database + trie *trie.SecureTrie + pastTries []*trie.SecureTrie + codeSizeCache *lru.Cache + + // This map holds 'live' objects, which will get modified while processing a state transition. + stateObjects map[common.Address]*stateObject + stateObjectsDirty map[common.Address]struct{} + + // The refund counter, also used by state transitioning. + refund *big.Int + + thash, bhash common.Hash + txIndex int + logs map[common.Hash][]*types.Log + logSize uint + + preimages map[common.Hash][]byte + + // Journal of state modifications. This is the backbone of + // Snapshot and RevertToSnapshot. + journal journal + validRevisions []revision + nextRevisionId int + + lock sync.Mutex +} + +// Create a new state from a given trie +func New(root common.Hash, db ethdb.Database) (*StateDB, error) { + tr, err := trie.NewSecure(root, db, MaxTrieCacheGen) + if err != nil { + return nil, err + } + csc, _ := lru.New(codeSizeCacheSize) + return &StateDB{ + db: db, + trie: tr, + codeSizeCache: csc, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), + }, nil +} + +// New creates a new statedb by reusing any journalled tries to avoid costly +// disk io. +func (self *StateDB) New(root common.Hash) (*StateDB, error) { + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) + if err != nil { + return nil, err + } + return &StateDB{ + db: self.db, + trie: tr, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), + }, nil +} + +// Reset clears out all emphemeral state objects from the state db, but keeps +// the underlying state trie to avoid reloading data for the next operations. +func (self *StateDB) Reset(root common.Hash) error { + self.lock.Lock() + defer self.lock.Unlock() + + tr, err := self.openTrie(root) + if err != nil { + return err + } + self.trie = tr + self.stateObjects = make(map[common.Address]*stateObject) + self.stateObjectsDirty = make(map[common.Address]struct{}) + self.thash = common.Hash{} + self.bhash = common.Hash{} + self.txIndex = 0 + self.logs = make(map[common.Hash][]*types.Log) + self.logSize = 0 + self.preimages = make(map[common.Hash][]byte) + self.clearJournalAndRefund() + + return nil +} + +// openTrie creates a trie. It uses an existing trie if one is available +// from the journal if available. +func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { + for i := len(self.pastTries) - 1; i >= 0; i-- { + if self.pastTries[i].Hash() == root { + tr := *self.pastTries[i] + return &tr, nil + } + } + return trie.NewSecure(root, self.db, MaxTrieCacheGen) +} + +func (self *StateDB) pushTrie(t *trie.SecureTrie) { + self.lock.Lock() + defer self.lock.Unlock() + + if len(self.pastTries) >= maxPastTries { + copy(self.pastTries, self.pastTries[1:]) + self.pastTries[len(self.pastTries)-1] = t + } else { + self.pastTries = append(self.pastTries, t) + } +} + +func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { + self.thash = thash + self.bhash = bhash + self.txIndex = ti +} + +func (self *StateDB) AddLog(log *types.Log) { + self.journal = append(self.journal, addLogChange{txhash: self.thash}) + + log.TxHash = self.thash + log.BlockHash = self.bhash + log.TxIndex = uint(self.txIndex) + log.Index = self.logSize + self.logs[self.thash] = append(self.logs[self.thash], log) + self.logSize++ +} + +func (self *StateDB) GetLogs(hash common.Hash) []*types.Log { + return self.logs[hash] +} + +func (self *StateDB) Logs() []*types.Log { + var logs []*types.Log + for _, lgs := range self.logs { + logs = append(logs, lgs...) + } + return logs +} + +// AddPreimage records a SHA3 preimage seen by the VM. +func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) { + if _, ok := self.preimages[hash]; !ok { + self.journal = append(self.journal, addPreimageChange{hash: hash}) + pi := make([]byte, len(preimage)) + copy(pi, preimage) + self.preimages[hash] = pi + } +} + +// Preimages returns a list of SHA3 preimages that have been submitted. +func (self *StateDB) Preimages() map[common.Hash][]byte { + return self.preimages +} + +func (self *StateDB) AddRefund(gas *big.Int) { + self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)}) + self.refund.Add(self.refund, gas) +} + +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (self *StateDB) Exist(addr common.Address) bool { + return self.getStateObject(addr) != nil +} + +// Empty returns whether the state object is either non-existent +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (self *StateDB) Empty(addr common.Address) bool { + so := self.getStateObject(addr) + return so == nil || so.empty() +} + +// Retrieve the balance from the given address or 0 if object not found +func (self *StateDB) GetBalance(addr common.Address) *big.Int { + stateObject := self.getStateObject(addr) + if stateObject != nil { + return stateObject.Balance() + } + return common.Big0 +} + +func (self *StateDB) GetNonce(addr common.Address) uint64 { + stateObject := self.getStateObject(addr) + if stateObject != nil { + return stateObject.Nonce() + } + + return 0 +} + +func (self *StateDB) GetCode(addr common.Address) []byte { + stateObject := self.getStateObject(addr) + if stateObject != nil { + code := stateObject.Code(self.db) + key := common.BytesToHash(stateObject.CodeHash()) + self.codeSizeCache.Add(key, len(code)) + return code + } + return nil +} + +func (self *StateDB) GetCodeSize(addr common.Address) int { + stateObject := self.getStateObject(addr) + if stateObject == nil { + return 0 + } + key := common.BytesToHash(stateObject.CodeHash()) + if cached, ok := self.codeSizeCache.Get(key); ok { + return cached.(int) + } + size := len(stateObject.Code(self.db)) + if stateObject.dbErr == nil { + self.codeSizeCache.Add(key, size) + } + return size +} + +func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := self.getStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.CodeHash()) +} + +func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { + stateObject := self.getStateObject(a) + if stateObject != nil { + return stateObject.GetState(self.db, b) + } + return common.Hash{} +} + +func (self *StateDB) HasSuicided(addr common.Address) bool { + stateObject := self.getStateObject(addr) + if stateObject != nil { + return stateObject.suicided + } + return false +} + +/* + * SETTERS + */ + +// AddBalance adds amount to the account associated with addr +func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.AddBalance(amount) + } +} + +// SubBalance subtracts amount from the account associated with addr +func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + +func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetBalance(amount) + } +} + +func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetNonce(nonce) + } +} + +func (self *StateDB) SetCode(addr common.Address, code []byte) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetCode(crypto.Keccak256Hash(code), code) + } +} + +func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetState(self.db, key, value) + } +} + +// Suicide marks the given account as suicided. +// This clears the account balance. +// +// The account's state object is still available until the state is committed, +// getStateObject will return a non-nil account after Suicide. +func (self *StateDB) Suicide(addr common.Address) bool { + stateObject := self.getStateObject(addr) + if stateObject == nil { + return false + } + self.journal = append(self.journal, suicideChange{ + account: &addr, + prev: stateObject.suicided, + prevbalance: new(big.Int).Set(stateObject.Balance()), + }) + stateObject.markSuicided() + stateObject.data.Balance = new(big.Int) + return true +} + +// +// Setting, updating & deleting state object methods +// + +// updateStateObject writes the given object to the trie. +func (self *StateDB) updateStateObject(stateObject *stateObject) { + addr := stateObject.Address() + data, err := rlp.EncodeToBytes(stateObject) + if err != nil { + panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) + } + self.trie.Update(addr[:], data) +} + +// deleteStateObject removes the given object from the state trie. +func (self *StateDB) deleteStateObject(stateObject *stateObject) { + stateObject.deleted = true + addr := stateObject.Address() + self.trie.Delete(addr[:]) +} + +// Retrieve a state object given my the address. Returns nil if not found. +func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { + // Prefer 'live' objects. + if obj := self.stateObjects[addr]; obj != nil { + if obj.deleted { + return nil + } + return obj + } + + // Load the object from the database. + enc := self.trie.Get(addr[:]) + if len(enc) == 0 { + return nil + } + var data Account + if err := rlp.DecodeBytes(enc, &data); err != nil { + glog.Errorf("can't decode object at %x: %v", addr[:], err) + return nil + } + // Insert into the live set. + obj := newObject(self, addr, data, self.MarkStateObjectDirty) + self.setStateObject(obj) + return obj +} + +func (self *StateDB) setStateObject(object *stateObject) { + self.stateObjects[object.Address()] = object +} + +// Retrieve a state object or create a new state object if nil +func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { + stateObject := self.getStateObject(addr) + if stateObject == nil || stateObject.deleted { + stateObject, _ = self.createObject(addr) + } + return stateObject +} + +// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly +// state object cache iteration to find a handful of modified ones. +func (self *StateDB) MarkStateObjectDirty(addr common.Address) { + self.stateObjectsDirty[addr] = struct{}{} +} + +// createObject creates a new state object. If there is an existing account with +// the given address, it is overwritten and returned as the second return value. +func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { + prev = self.getStateObject(addr) + newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty) + newobj.setNonce(0) // sets the object to dirty + if prev == nil { + if glog.V(logger.Debug) { + glog.Infof("(+) %x\n", addr) + } + self.journal = append(self.journal, createObjectChange{account: &addr}) + } else { + self.journal = append(self.journal, resetObjectChange{prev: prev}) + } + self.setStateObject(newobj) + return newobj, prev +} + +// CreateAccount explicitly creates a state object. If a state object with the address +// already exists the balance is carried over to the new account. +// +// CreateAccount is called during the EVM CREATE operation. The situation might arise that +// a contract does the following: +// +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// +// Carrying over the balance ensures that Ether doesn't disappear. +func (self *StateDB) CreateAccount(addr common.Address) { + new, prev := self.createObject(addr) + if prev != nil { + new.setBalance(prev.data.Balance) + } +} + +func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { + so := db.getStateObject(addr) + if so == nil { + return + } + + // When iterating over the storage check the cache first + for h, value := range so.cachedStorage { + cb(h, value) + } + + it := so.getTrie(db.db).Iterator() + for it.Next() { + // ignore cached values + key := common.BytesToHash(db.trie.GetKey(it.Key)) + if _, ok := so.cachedStorage[key]; !ok { + cb(key, common.BytesToHash(it.Value)) + } + } +} + +// Copy creates a deep, independent copy of the state. +// Snapshots of the copied state cannot be applied to the copy. +func (self *StateDB) Copy() *StateDB { + self.lock.Lock() + defer self.lock.Unlock() + + // Copy all the basic fields, initialize the memory ones + state := &StateDB{ + db: self.db, + trie: self.trie, + pastTries: self.pastTries, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + refund: new(big.Int).Set(self.refund), + logs: make(map[common.Hash][]*types.Log, len(self.logs)), + logSize: self.logSize, + preimages: make(map[common.Hash][]byte), + } + // Copy the dirty states, logs, and preimages + for addr := range self.stateObjectsDirty { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) + state.stateObjectsDirty[addr] = struct{}{} + } + for hash, logs := range self.logs { + state.logs[hash] = make([]*types.Log, len(logs)) + copy(state.logs[hash], logs) + } + for hash, preimage := range self.preimages { + state.preimages[hash] = preimage + } + return state +} + +// Snapshot returns an identifier for the current revision of the state. +func (self *StateDB) Snapshot() int { + id := self.nextRevisionId + self.nextRevisionId++ + self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)}) + return id +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (self *StateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(self.validRevisions), func(i int) bool { + return self.validRevisions[i].id >= revid + }) + if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := self.validRevisions[idx].journalIndex + + // Replay the journal to undo changes. + for i := len(self.journal) - 1; i >= snapshot; i-- { + self.journal[i].undo(self) + } + self.journal = self.journal[:snapshot] + + // Remove invalidated snapshots from the stack. + self.validRevisions = self.validRevisions[:idx] +} + +// GetRefund returns the current value of the refund counter. +// The return value must not be modified by the caller and will become +// invalid at the next call to AddRefund. +func (self *StateDB) GetRefund() *big.Int { + return self.refund +} + +// IntermediateRoot computes the current root hash of the state trie. +// It is called in between transactions to get the root hash that +// goes into transaction receipts. +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + for addr := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { + s.deleteStateObject(stateObject) + } else { + stateObject.updateRoot(s.db) + s.updateStateObject(stateObject) + } + } + // Invalidate journal because reverting across transactions is not allowed. + s.clearJournalAndRefund() + return s.trie.Hash() +} + +// DeleteSuicides flags the suicided objects for deletion so that it +// won't be referenced again when called / queried up on. +// +// DeleteSuicides should not be used for consensus related updates +// under any circumstances. +func (s *StateDB) DeleteSuicides() { + // Reset refund so that any used-gas calculations can use this method. + s.clearJournalAndRefund() + + for addr := range s.stateObjectsDirty { + stateObject := s.stateObjects[addr] + + // If the object has been removed by a suicide + // flag the object as deleted. + if stateObject.suicided { + stateObject.deleted = true + } + delete(s.stateObjectsDirty, addr) + } +} + +// Commit commits all state changes to the database. +func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { + root, batch := s.CommitBatch(deleteEmptyObjects) + return root, batch.Write() +} + +// CommitBatch commits all state changes to a write batch but does not +// execute the batch. It is used to validate state changes against +// the root hash stored in a block. +func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { + batch = s.db.NewBatch() + root, _ = s.commit(batch, deleteEmptyObjects) + + glog.V(logger.Debug).Infof("Trie cache stats: %d misses, %d unloads", trie.CacheMisses(), trie.CacheUnloads()) + return root, batch +} + +func (s *StateDB) clearJournalAndRefund() { + s.journal = nil + s.validRevisions = s.validRevisions[:0] + s.refund = new(big.Int) +} + +func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { + defer s.clearJournalAndRefund() + + // Commit objects to the trie. + for addr, stateObject := range s.stateObjects { + _, isDirty := s.stateObjectsDirty[addr] + switch { + case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): + // If the object has been removed, don't bother syncing it + // and just mark it for deletion in the trie. + s.deleteStateObject(stateObject) + case isDirty: + // Write any contract code associated with the state object + if stateObject.code != nil && stateObject.dirtyCode { + if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { + return common.Hash{}, err + } + stateObject.dirtyCode = false + } + // Write any storage changes in the state object to its storage trie. + if err := stateObject.CommitTrie(s.db, dbw); err != nil { + return common.Hash{}, err + } + // Update the object in the main account trie. + s.updateStateObject(stateObject) + } + delete(s.stateObjectsDirty, addr) + } + // Write trie changes. + root, err = s.trie.CommitTo(dbw) + if err == nil { + s.pushTrie(s.trie) + } + return root, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb_test.go new file mode 100644 index 0000000..597de3b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb_test.go @@ -0,0 +1,375 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "math/big" + "math/rand" + "reflect" + "strings" + "testing" + "testing/quick" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" +) + +// Tests that updating a state trie does not leak any database writes prior to +// actually committing the state. +func TestUpdateLeaks(t *testing.T) { + // Create an empty state database + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + + // Update it with some accounts + for i := byte(0); i < 255; i++ { + addr := common.BytesToAddress([]byte{i}) + state.AddBalance(addr, big.NewInt(int64(11*i))) + state.SetNonce(addr, uint64(42*i)) + if i%2 == 0 { + state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) + } + if i%3 == 0 { + state.SetCode(addr, []byte{i, i, i, i, i}) + } + state.IntermediateRoot(false) + } + // Ensure that no data was leaked into the database + for _, key := range db.Keys() { + value, _ := db.Get(key) + t.Errorf("State leaked into database: %x -> %x", key, value) + } +} + +// Tests that no intermediate state of an object is stored into the database, +// only the one right before the commit. +func TestIntermediateLeaks(t *testing.T) { + // Create two state databases, one transitioning to the final state, the other final from the beginning + transDb, _ := ethdb.NewMemDatabase() + finalDb, _ := ethdb.NewMemDatabase() + transState, _ := New(common.Hash{}, transDb) + finalState, _ := New(common.Hash{}, finalDb) + + modify := func(state *StateDB, addr common.Address, i, tweak byte) { + state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) + state.SetNonce(addr, uint64(42*i+tweak)) + if i%2 == 0 { + state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) + state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak}) + } + if i%3 == 0 { + state.SetCode(addr, []byte{i, i, i, i, i, tweak}) + } + } + + // Modify the transient state. + for i := byte(0); i < 255; i++ { + modify(transState, common.Address{byte(i)}, i, 0) + } + // Write modifications to trie. + transState.IntermediateRoot(false) + + // Overwrite all the data with new values in the transient database. + for i := byte(0); i < 255; i++ { + modify(transState, common.Address{byte(i)}, i, 99) + modify(finalState, common.Address{byte(i)}, i, 99) + } + + // Commit and cross check the databases. + if _, err := transState.Commit(false); err != nil { + t.Fatalf("failed to commit transition state: %v", err) + } + if _, err := finalState.Commit(false); err != nil { + t.Fatalf("failed to commit final state: %v", err) + } + for _, key := range finalDb.Keys() { + if _, err := transDb.Get(key); err != nil { + val, _ := finalDb.Get(key) + t.Errorf("entry missing from the transition database: %x -> %x", key, val) + } + } + for _, key := range transDb.Keys() { + if _, err := finalDb.Get(key); err != nil { + val, _ := transDb.Get(key) + t.Errorf("extra entry in the transition database: %x -> %x", key, val) + } + } +} + +func TestSnapshotRandom(t *testing.T) { + t.Skip("@fjl fix me please") + config := &quick.Config{MaxCount: 1000} + err := quick.Check((*snapshotTest).run, config) + if cerr, ok := err.(*quick.CheckError); ok { + test := cerr.In[0].(*snapshotTest) + t.Errorf("%v:\n%s", test.err, test) + } else if err != nil { + t.Error(err) + } +} + +// A snapshotTest checks that reverting StateDB snapshots properly undoes all changes +// captured by the snapshot. Instances of this test with pseudorandom content are created +// by Generate. +// +// The test works as follows: +// +// A new state is created and all actions are applied to it. Several snapshots are taken +// in between actions. The test then reverts each snapshot. For each snapshot the actions +// leading up to it are replayed on a fresh, empty state. The behaviour of all public +// accessor methods on the reverted state must match the return value of the equivalent +// methods on the replayed state. +type snapshotTest struct { + addrs []common.Address // all account addresses + actions []testAction // modifications to the state + snapshots []int // actions indexes at which snapshot is taken + err error // failure details are reported through this field +} + +type testAction struct { + name string + fn func(testAction, *StateDB) + args []int64 + noAddr bool +} + +// newTestAction creates a random action that changes state. +func newTestAction(addr common.Address, r *rand.Rand) testAction { + actions := []testAction{ + { + name: "SetBalance", + fn: func(a testAction, s *StateDB) { + s.SetBalance(addr, big.NewInt(a.args[0])) + }, + args: make([]int64, 1), + }, + { + name: "AddBalance", + fn: func(a testAction, s *StateDB) { + s.AddBalance(addr, big.NewInt(a.args[0])) + }, + args: make([]int64, 1), + }, + { + name: "SetNonce", + fn: func(a testAction, s *StateDB) { + s.SetNonce(addr, uint64(a.args[0])) + }, + args: make([]int64, 1), + }, + { + name: "SetState", + fn: func(a testAction, s *StateDB) { + var key, val common.Hash + binary.BigEndian.PutUint16(key[:], uint16(a.args[0])) + binary.BigEndian.PutUint16(val[:], uint16(a.args[1])) + s.SetState(addr, key, val) + }, + args: make([]int64, 2), + }, + { + name: "SetCode", + fn: func(a testAction, s *StateDB) { + code := make([]byte, 16) + binary.BigEndian.PutUint64(code, uint64(a.args[0])) + binary.BigEndian.PutUint64(code[8:], uint64(a.args[1])) + s.SetCode(addr, code) + }, + args: make([]int64, 2), + }, + { + name: "CreateAccount", + fn: func(a testAction, s *StateDB) { + s.CreateAccount(addr) + }, + }, + { + name: "Suicide", + fn: func(a testAction, s *StateDB) { + s.Suicide(addr) + }, + }, + { + name: "AddRefund", + fn: func(a testAction, s *StateDB) { + s.AddRefund(big.NewInt(a.args[0])) + }, + args: make([]int64, 1), + noAddr: true, + }, + { + name: "AddLog", + fn: func(a testAction, s *StateDB) { + data := make([]byte, 2) + binary.BigEndian.PutUint16(data, uint16(a.args[0])) + s.AddLog(&types.Log{Address: addr, Data: data}) + }, + args: make([]int64, 1), + }, + } + action := actions[r.Intn(len(actions))] + var nameargs []string + if !action.noAddr { + nameargs = append(nameargs, addr.Hex()) + } + for _, i := range action.args { + action.args[i] = rand.Int63n(100) + nameargs = append(nameargs, fmt.Sprint(action.args[i])) + } + action.name += strings.Join(nameargs, ", ") + return action +} + +// Generate returns a new snapshot test of the given size. All randomness is +// derived from r. +func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value { + // Generate random actions. + addrs := make([]common.Address, 50) + for i := range addrs { + addrs[i][0] = byte(i) + } + actions := make([]testAction, size) + for i := range actions { + addr := addrs[r.Intn(len(addrs))] + actions[i] = newTestAction(addr, r) + } + // Generate snapshot indexes. + nsnapshots := int(math.Sqrt(float64(size))) + if size > 0 && nsnapshots == 0 { + nsnapshots = 1 + } + snapshots := make([]int, nsnapshots) + snaplen := len(actions) / nsnapshots + for i := range snapshots { + // Try to place the snapshots some number of actions apart from each other. + snapshots[i] = (i * snaplen) + r.Intn(snaplen) + } + return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil}) +} + +func (test *snapshotTest) String() string { + out := new(bytes.Buffer) + sindex := 0 + for i, action := range test.actions { + if len(test.snapshots) > sindex && i == test.snapshots[sindex] { + fmt.Fprintf(out, "---- snapshot %d ----\n", sindex) + sindex++ + } + fmt.Fprintf(out, "%4d: %s\n", i, action.name) + } + return out.String() +} + +func (test *snapshotTest) run() bool { + // Run all actions and create snapshots. + var ( + db, _ = ethdb.NewMemDatabase() + state, _ = New(common.Hash{}, db) + snapshotRevs = make([]int, len(test.snapshots)) + sindex = 0 + ) + for i, action := range test.actions { + if len(test.snapshots) > sindex && i == test.snapshots[sindex] { + snapshotRevs[sindex] = state.Snapshot() + sindex++ + } + action.fn(action, state) + } + + // Revert all snapshots in reverse order. Each revert must yield a state + // that is equivalent to fresh state with all actions up the snapshot applied. + for sindex--; sindex >= 0; sindex-- { + checkstate, _ := New(common.Hash{}, db) + for _, action := range test.actions[:test.snapshots[sindex]] { + action.fn(action, checkstate) + } + state.RevertToSnapshot(snapshotRevs[sindex]) + if err := test.checkEqual(state, checkstate); err != nil { + test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err) + return false + } + } + return true +} + +// checkEqual checks that methods of state and checkstate return the same values. +func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { + for _, addr := range test.addrs { + var err error + checkeq := func(op string, a, b interface{}) bool { + if err == nil && !reflect.DeepEqual(a, b) { + err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b) + return false + } + return true + } + // Check basic accessor methods. + checkeq("Exist", state.Exist(addr), checkstate.Exist(addr)) + checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr)) + checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr)) + checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr)) + checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) + checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) + checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) + // Check storage. + if obj := state.getStateObject(addr); obj != nil { + state.ForEachStorage(addr, func(key, val common.Hash) bool { + return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) + }) + checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool { + return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) + }) + } + if err != nil { + return err + } + } + + if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 { + return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", + state.GetRefund(), checkstate.GetRefund()) + } + if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { + return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", + state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) + } + return nil +} + +func TestTouchDelete(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + state.GetOrNewStateObject(common.Address{}) + root, _ := state.Commit(false) + state.Reset(root) + + snapshot := state.Snapshot() + state.AddBalance(common.Address{}, new(big.Int)) + if len(state.stateObjectsDirty) != 1 { + t.Fatal("expected one dirty state object") + } + + state.RevertToSnapshot(snapshot) + if len(state.stateObjectsDirty) != 0 { + t.Fatal("expected no dirty state object") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/sync.go b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go new file mode 100644 index 0000000..8456a81 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/sync.go @@ -0,0 +1,71 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// StateSync is the main state synchronisation scheduler, which provides yet the +// unknown state hashes to retrieve, accepts node data associated with said hashes +// and reconstructs the state database step by step until all is done. +type StateSync trie.TrieSync + +// NewStateSync create a new state trie download scheduler. +func NewStateSync(root common.Hash, database trie.DatabaseReader) *StateSync { + var syncer *trie.TrieSync + + callback := func(leaf []byte, parent common.Hash) error { + var obj struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte + } + if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil { + return err + } + syncer.AddSubTrie(obj.Root, 64, parent, nil) + syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent) + + return nil + } + syncer = trie.NewTrieSync(root, database, callback) + return (*StateSync)(syncer) +} + +// Missing retrieves the known missing nodes from the state trie for retrieval. +func (s *StateSync) Missing(max int) []common.Hash { + return (*trie.TrieSync)(s).Missing(max) +} + +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *StateSync) Process(list []trie.SyncResult, dbw trie.DatabaseWriter) (bool, int, error) { + return (*trie.TrieSync)(s).Process(list, dbw) +} + +// Pending returns the number of state entries currently pending for download. +func (s *StateSync) Pending() int { + return (*trie.TrieSync)(s).Pending() +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/sync_test.go b/vendor/github.com/ethereum/go-ethereum/core/state/sync_test.go new file mode 100644 index 0000000..43d146e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state/sync_test.go @@ -0,0 +1,323 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/trie" +) + +// testAccount is the data associated with an account used by the state tests. +type testAccount struct { + address common.Address + balance *big.Int + nonce uint64 + code []byte +} + +// makeTestState create a sample test state to test node-wise reconstruction. +func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { + // Create an empty state + db, _ := ethdb.NewMemDatabase() + state, _ := New(common.Hash{}, db) + + // Fill it with some arbitrary data + accounts := []*testAccount{} + for i := byte(0); i < 96; i++ { + obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) + acc := &testAccount{address: common.BytesToAddress([]byte{i})} + + obj.AddBalance(big.NewInt(int64(11 * i))) + acc.balance = big.NewInt(int64(11 * i)) + + obj.SetNonce(uint64(42 * i)) + acc.nonce = uint64(42 * i) + + if i%3 == 0 { + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}) + acc.code = []byte{i, i, i, i, i} + } + state.updateStateObject(obj) + accounts = append(accounts, acc) + } + root, _ := state.Commit(false) + + // Return the generated state + return db, root, accounts +} + +// checkStateAccounts cross references a reconstructed state with an expected +// account array. +func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) { + // Check root availability and state contents + state, err := New(root, db) + if err != nil { + t.Fatalf("failed to create state trie at %x: %v", root, err) + } + if err := checkStateConsistency(db, root); err != nil { + t.Fatalf("inconsistent state trie at %x: %v", root, err) + } + for i, acc := range accounts { + if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 { + t.Errorf("account %d: balance mismatch: have %v, want %v", i, balance, acc.balance) + } + if nonce := state.GetNonce(acc.address); nonce != acc.nonce { + t.Errorf("account %d: nonce mismatch: have %v, want %v", i, nonce, acc.nonce) + } + if code := state.GetCode(acc.address); !bytes.Equal(code, acc.code) { + t.Errorf("account %d: code mismatch: have %x, want %x", i, code, acc.code) + } + } +} + +// checkStateConsistency checks that all nodes in a state trie are indeed present. +func checkStateConsistency(db ethdb.Database, root common.Hash) error { + // Create and iterate a state trie rooted in a sub-node + if _, err := db.Get(root.Bytes()); err != nil { + return nil // Consider a non existent state consistent + } + state, err := New(root, db) + if err != nil { + return err + } + it := NewNodeIterator(state) + for it.Next() { + } + return it.Error +} + +// Tests that an empty state is not scheduled for syncing. +func TestEmptyStateSync(t *testing.T) { + empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + db, _ := ethdb.NewMemDatabase() + if req := NewStateSync(empty, db).Missing(1); len(req) != 0 { + t.Errorf("content requested for empty state: %v", req) + } +} + +// Tests that given a root hash, a state can sync iteratively on a single thread, +// requesting retrieval tasks and returning all of them in one go. +func TestIterativeStateSyncIndividual(t *testing.T) { testIterativeStateSync(t, 1) } +func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t, 100) } + +func testIterativeStateSync(t *testing.T, batch int) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + queue := append([]common.Hash{}, sched.Missing(batch)...) + for len(queue) > 0 { + results := make([]trie.SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = trie.SyncResult{Hash: hash, Data: data} + } + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = append(queue[:0], sched.Missing(batch)...) + } + // Cross check that the two states are in sync + checkStateAccounts(t, dstDb, srcRoot, srcAccounts) +} + +// Tests that the trie scheduler can correctly reconstruct the state even if only +// partial results are returned, and the others sent only later. +func TestIterativeDelayedStateSync(t *testing.T) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + queue := append([]common.Hash{}, sched.Missing(0)...) + for len(queue) > 0 { + // Sync only half of the scheduled nodes + results := make([]trie.SyncResult, len(queue)/2+1) + for i, hash := range queue[:len(results)] { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = trie.SyncResult{Hash: hash, Data: data} + } + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = append(queue[len(results):], sched.Missing(0)...) + } + // Cross check that the two states are in sync + checkStateAccounts(t, dstDb, srcRoot, srcAccounts) +} + +// Tests that given a root hash, a trie can sync iteratively on a single thread, +// requesting retrieval tasks and returning all of them in one go, however in a +// random order. +func TestIterativeRandomStateSyncIndividual(t *testing.T) { testIterativeRandomStateSync(t, 1) } +func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomStateSync(t, 100) } + +func testIterativeRandomStateSync(t *testing.T, batch int) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + queue := make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(batch) { + queue[hash] = struct{}{} + } + for len(queue) > 0 { + // Fetch all the queued nodes in a random order + results := make([]trie.SyncResult, 0, len(queue)) + for hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results = append(results, trie.SyncResult{Hash: hash, Data: data}) + } + // Feed the retrieved results back and queue new tasks + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(batch) { + queue[hash] = struct{}{} + } + } + // Cross check that the two states are in sync + checkStateAccounts(t, dstDb, srcRoot, srcAccounts) +} + +// Tests that the trie scheduler can correctly reconstruct the state even if only +// partial results are returned (Even those randomly), others sent only later. +func TestIterativeRandomDelayedStateSync(t *testing.T) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + queue := make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(0) { + queue[hash] = struct{}{} + } + for len(queue) > 0 { + // Sync only half of the scheduled nodes, even those in random order + results := make([]trie.SyncResult, 0, len(queue)/2+1) + for hash := range queue { + delete(queue, hash) + + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results = append(results, trie.SyncResult{Hash: hash, Data: data}) + + if len(results) >= cap(results) { + break + } + } + // Feed the retrieved results back and queue new tasks + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + for _, hash := range sched.Missing(0) { + queue[hash] = struct{}{} + } + } + // Cross check that the two states are in sync + checkStateAccounts(t, dstDb, srcRoot, srcAccounts) +} + +// Tests that at any point in time during a sync, only complete sub-tries are in +// the database. +func TestIncompleteStateSync(t *testing.T) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + added := []common.Hash{} + queue := append([]common.Hash{}, sched.Missing(1)...) + for len(queue) > 0 { + // Fetch a batch of state nodes + results := make([]trie.SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = trie.SyncResult{Hash: hash, Data: data} + } + // Process each of the state nodes + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + for _, result := range results { + added = append(added, result.Hash) + } + // Check that all known sub-tries in the synced state is complete + for _, root := range added { + // Skim through the accounts and make sure the root hash is not a code node + codeHash := false + for _, acc := range srcAccounts { + if root == crypto.Keccak256Hash(acc.code) { + codeHash = true + break + } + } + // If the root is a real trie node, check consistency + if !codeHash { + if err := checkStateConsistency(dstDb, root); err != nil { + t.Fatalf("state inconsistent: %v", err) + } + } + } + // Fetch the next batch to retrieve + queue = append(queue[:0], sched.Missing(1)...) + } + // Sanity check that removing any node from the database is detected + for _, node := range added[1:] { + key := node.Bytes() + value, _ := dstDb.Get(key) + + dstDb.Delete(key) + if err := checkStateConsistency(dstDb, added[0]); err == nil { + t.Fatalf("trie inconsistency not caught, missing: %x", key) + } + dstDb.Put(key, value) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go new file mode 100644 index 0000000..6485e9a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go @@ -0,0 +1,148 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +var ( + big8 = big.NewInt(8) + big32 = big.NewInt(32) +) + +// StateProcessor is a basic Processor, which takes care of transitioning +// state from one point to another. +// +// StateProcessor implements Processor. +type StateProcessor struct { + config *params.ChainConfig + bc *BlockChain +} + +// NewStateProcessor initialises a new StateProcessor. +func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor { + return &StateProcessor{ + config: config, + bc: bc, + } +} + +// Process processes the state changes according to the Ethereum rules by running +// the transaction messages using the statedb and applying any rewards to both +// the processor (coinbase) and any included uncles. +// +// Process returns the receipts and logs accumulated during the process and +// returns the amount of gas that was used in the process. If any of the +// transactions failed to execute due to insufficient gas it will return an error. +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) { + var ( + receipts types.Receipts + totalUsedGas = big.NewInt(0) + err error + header = block.Header() + allLogs []*types.Log + gp = new(GasPool).AddGas(block.GasLimit()) + ) + // Mutate the the block and state according to any hard-fork specs + if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { + ApplyDAOHardFork(statedb) + } + // Iterate over and process the individual transactions + for i, tx := range block.Transactions() { + //fmt.Println("tx:", i) + statedb.StartRecord(tx.Hash(), block.Hash(), i) + receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) + if err != nil { + return nil, nil, nil, err + } + receipts = append(receipts, receipt) + allLogs = append(allLogs, receipt.Logs...) + } + AccumulateRewards(statedb, header, block.Uncles()) + + return receipts, allLogs, totalUsedGas, err +} + +// ApplyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) { + msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) + if err != nil { + return nil, nil, err + } + // Create a new context to be used in the EVM environment + context := NewEVMContext(msg, header, bc) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := vm.NewEVM(context, statedb, config, cfg) + // Apply the transaction to the current state (included in the env) + _, gas, err := ApplyMessage(vmenv, msg, gp) + if err != nil { + return nil, nil, err + } + + // Update the state with pending changes + usedGas.Add(usedGas, gas) + // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + // based on the eip phase, we're passing wether the root touch-delete accounts. + receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) + receipt.TxHash = tx.Hash() + receipt.GasUsed = new(big.Int).Set(gas) + // if the transaction created a contract, store the creation address in the receipt. + if msg.To() == nil { + receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) + } + + // Set the receipt logs and create a bloom for filtering + receipt.Logs = statedb.GetLogs(tx.Hash()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + + glog.V(logger.Debug).Infoln(receipt) + + return receipt, gas, err +} + +// AccumulateRewards credits the coinbase of the given block with the +// mining reward. The total reward consists of the static block reward +// and rewards for included uncles. The coinbase of each uncle block is +// also rewarded. +func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { + reward := new(big.Int).Set(BlockReward) + r := new(big.Int) + for _, uncle := range uncles { + r.Add(uncle.Number, big8) + r.Sub(r, header.Number) + r.Mul(r, BlockReward) + r.Div(r, big8) + statedb.AddBalance(uncle.Coinbase, r) + + r.Div(BlockReward, big32) + reward.Add(reward, r) + } + statedb.AddBalance(header.Coinbase, reward) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go new file mode 100644 index 0000000..6acc784 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go @@ -0,0 +1,296 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +var ( + Big0 = big.NewInt(0) + errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas") +) + +/* +The State Transitioning Model + +A state transition is a change made when a transaction is applied to the current world state +The state transitioning model does all all the necessary work to work out a valid new state root. + +1) Nonce handling +2) Pre pay gas +3) Create a new state object if the recipient is \0*32 +4) Value transfer +== If contract creation == + 4a) Attempt to run transaction data + 4b) If valid, use result as code for the new state object +== end == +5) Run Script section +6) Derive new state root +*/ +type StateTransition struct { + gp *GasPool + msg Message + gas uint64 + gasPrice *big.Int + initialGas *big.Int + value *big.Int + data []byte + state vm.StateDB + + evm *vm.EVM +} + +// Message represents a message sent to a contract. +type Message interface { + From() common.Address + //FromFrontier() (common.Address, error) + To() *common.Address + + GasPrice() *big.Int + Gas() *big.Int + Value() *big.Int + + Nonce() uint64 + CheckNonce() bool + Data() []byte +} + +func MessageCreatesContract(msg Message) bool { + return msg.To() == nil +} + +// IntrinsicGas computes the 'intrinsic gas' for a message +// with the given data. +// +// TODO convert to uint64 +func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { + igas := new(big.Int) + if contractCreation && homestead { + igas.SetUint64(params.TxGasContractCreation) + } else { + igas.SetUint64(params.TxGas) + } + if len(data) > 0 { + var nz int64 + for _, byt := range data { + if byt != 0 { + nz++ + } + } + m := big.NewInt(nz) + m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas)) + igas.Add(igas, m) + m.SetInt64(int64(len(data)) - nz) + m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas)) + igas.Add(igas, m) + } + return igas +} + +// NewStateTransition initialises and returns a new state transition object. +func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { + return &StateTransition{ + gp: gp, + evm: evm, + msg: msg, + gasPrice: msg.GasPrice(), + initialGas: new(big.Int), + value: msg.Value(), + data: msg.Data(), + state: evm.StateDB, + } +} + +// ApplyMessage computes the new state by applying the given message +// against the old state within the environment. +// +// ApplyMessage returns the bytes returned by any EVM execution (if it took place), +// the gas used (which includes gas refunds) and an error if it failed. An error always +// indicates a core error meaning that the message would always fail for that particular +// state and would never be accepted within a block. +func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) { + st := NewStateTransition(evm, msg, gp) + + ret, _, gasUsed, err := st.TransitionDb() + return ret, gasUsed, err +} + +func (self *StateTransition) from() vm.AccountRef { + f := self.msg.From() + if !self.state.Exist(f) { + self.state.CreateAccount(f) + } + return vm.AccountRef(f) +} + +func (self *StateTransition) to() vm.AccountRef { + if self.msg == nil { + return vm.AccountRef{} + } + to := self.msg.To() + if to == nil { + return vm.AccountRef{} // contract creation + } + + reference := vm.AccountRef(*to) + if !self.state.Exist(*to) { + self.state.CreateAccount(*to) + } + return reference +} + +func (self *StateTransition) useGas(amount uint64) error { + if self.gas < amount { + return vm.ErrOutOfGas + } + self.gas -= amount + + return nil +} + +func (self *StateTransition) buyGas() error { + mgas := self.msg.Gas() + if mgas.BitLen() > 64 { + return vm.ErrOutOfGas + } + + mgval := new(big.Int).Mul(mgas, self.gasPrice) + + var ( + state = self.state + sender = self.from() + ) + if state.GetBalance(sender.Address()).Cmp(mgval) < 0 { + return errInsufficientBalanceForGas + } + if err := self.gp.SubGas(mgas); err != nil { + return err + } + self.gas += mgas.Uint64() + + self.initialGas.Set(mgas) + state.SubBalance(sender.Address(), mgval) + return nil +} + +func (self *StateTransition) preCheck() (err error) { + msg := self.msg + sender := self.from() + + // Make sure this transaction's nonce is correct + if msg.CheckNonce() { + if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() { + return NonceError(msg.Nonce(), n) + } + } + + // Pre-pay gas + if err = self.buyGas(); err != nil { + if IsGasLimitErr(err) { + return err + } + return InvalidTxError(err) + } + + return nil +} + +// TransitionDb will transition the state by applying the current message and returning the result +// including the required gas for the operation as well as the used gas. It returns an error if it +// failed. An error indicates a consensus issue. +func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) { + if err = self.preCheck(); err != nil { + return + } + msg := self.msg + sender := self.from() // err checked in preCheck + + homestead := self.evm.ChainConfig().IsHomestead(self.evm.BlockNumber) + contractCreation := MessageCreatesContract(msg) + // Pay intrinsic gas + // TODO convert to uint64 + intrinsicGas := IntrinsicGas(self.data, contractCreation, homestead) + if intrinsicGas.BitLen() > 64 { + return nil, nil, nil, InvalidTxError(vm.ErrOutOfGas) + } + + if err = self.useGas(intrinsicGas.Uint64()); err != nil { + return nil, nil, nil, InvalidTxError(err) + } + + var ( + evm = self.evm + // vm errors do not effect consensus and are therefor + // not assigned to err, except for insufficient balance + // error. + vmerr error + ) + if contractCreation { + ret, _, self.gas, vmerr = evm.Create(sender, self.data, self.gas, self.value) + } else { + // Increment the nonce for the next transaction + self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) + ret, self.gas, vmerr = evm.Call(sender, self.to().Address(), self.data, self.gas, self.value) + } + if vmerr != nil { + glog.V(logger.Debug).Infoln("vm returned with error:", err) + // The only possible consensus-error would be if there wasn't + // sufficient balance to make the transfer happen. The first + // balance transfer may never fail. + if vmerr == vm.ErrInsufficientBalance { + return nil, nil, nil, InvalidTxError(vmerr) + } + } + + requiredGas = new(big.Int).Set(self.gasUsed()) + + self.refundGas() + self.state.AddBalance(self.evm.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + + return ret, requiredGas, self.gasUsed(), err +} + +func (self *StateTransition) refundGas() { + // Return eth for remaining gas to the sender account, + // exchanged at the original rate. + sender := self.from() // err already checked + remaining := new(big.Int).Mul(new(big.Int).SetUint64(self.gas), self.gasPrice) + self.state.AddBalance(sender.Address(), remaining) + + // Apply refund counter, capped to half of the used gas. + uhalf := remaining.Div(self.gasUsed(), common.Big2) + refund := common.BigMin(uhalf, self.state.GetRefund()) + self.gas += refund.Uint64() + + self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) + + // Also return remaining gas to the block gas counter so it is + // available for the next transaction. + self.gp.AddGas(new(big.Int).SetUint64(self.gas)) +} + +func (self *StateTransition) gasUsed() *big.Int { + return new(big.Int).Sub(self.initialGas, new(big.Int).SetUint64(self.gas)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_list.go b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go new file mode 100644 index 0000000..535cb9d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_list.go @@ -0,0 +1,342 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "container/heap" + "math" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/core/types" +) + +// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for +// retrieving sorted transactions from the possibly gapped future queue. +type nonceHeap []uint64 + +func (h nonceHeap) Len() int { return len(h) } +func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *nonceHeap) Push(x interface{}) { + *h = append(*h, x.(uint64)) +} + +func (h *nonceHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// txSortedMap is a nonce->transaction hash map with a heap based index to allow +// iterating over the contents in a nonce-incrementing way. +type txSortedMap struct { + items map[uint64]*types.Transaction // Hash map storing the transaction data + index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) + cache types.Transactions // Cache of the transactions already sorted +} + +// newTxSortedMap creates a new sorted transaction map. +func newTxSortedMap() *txSortedMap { + return &txSortedMap{ + items: make(map[uint64]*types.Transaction), + index: &nonceHeap{}, + } +} + +// Get retrieves the current transactions associated with the given nonce. +func (m *txSortedMap) Get(nonce uint64) *types.Transaction { + return m.items[nonce] +} + +// Put inserts a new transaction into the map, also updating the map's nonce +// index. If a transaction already exists with the same nonce, it's overwritten. +func (m *txSortedMap) Put(tx *types.Transaction) { + nonce := tx.Nonce() + if m.items[nonce] == nil { + heap.Push(m.index, nonce) + } + m.items[nonce], m.cache = tx, nil +} + +// Forward removes all transactions from the map with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (m *txSortedMap) Forward(threshold uint64) types.Transactions { + var removed types.Transactions + + // Pop off heap items until the threshold is reached + for m.index.Len() > 0 && (*m.index)[0] < threshold { + nonce := heap.Pop(m.index).(uint64) + removed = append(removed, m.items[nonce]) + delete(m.items, nonce) + } + // If we had a cached order, shift the front + if m.cache != nil { + m.cache = m.cache[len(removed):] + } + return removed +} + +// Filter iterates over the list of transactions and removes all of them for which +// the specified function evaluates to true. +func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { + var removed types.Transactions + + // Collect all the transactions to filter out + for nonce, tx := range m.items { + if filter(tx) { + removed = append(removed, tx) + delete(m.items, nonce) + } + } + // If transactions were removed, the heap and cache are ruined + if len(removed) > 0 { + *m.index = make([]uint64, 0, len(m.items)) + for nonce := range m.items { + *m.index = append(*m.index, nonce) + } + heap.Init(m.index) + + m.cache = nil + } + return removed +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (m *txSortedMap) Cap(threshold int) types.Transactions { + // Short circuit if the number of items is under the limit + if len(m.items) <= threshold { + return nil + } + // Otherwise gather and drop the highest nonce'd transactions + var drops types.Transactions + + sort.Sort(*m.index) + for size := len(m.items); size > threshold; size-- { + drops = append(drops, m.items[(*m.index)[size-1]]) + delete(m.items, (*m.index)[size-1]) + } + *m.index = (*m.index)[:threshold] + heap.Init(m.index) + + // If we had a cache, shift the back + if m.cache != nil { + m.cache = m.cache[:len(m.cache)-len(drops)] + } + return drops +} + +// Remove deletes a transaction from the maintained map, returning whether the +// transaction was found. +func (m *txSortedMap) Remove(nonce uint64) bool { + // Short circuit if no transaction is present + _, ok := m.items[nonce] + if !ok { + return false + } + // Otherwise delete the transaction and fix the heap index + for i := 0; i < m.index.Len(); i++ { + if (*m.index)[i] == nonce { + heap.Remove(m.index, i) + break + } + } + delete(m.items, nonce) + m.cache = nil + + return true +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (m *txSortedMap) Ready(start uint64) types.Transactions { + // Short circuit if no transactions are available + if m.index.Len() == 0 || (*m.index)[0] > start { + return nil + } + // Otherwise start accumulating incremental transactions + var ready types.Transactions + for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { + ready = append(ready, m.items[next]) + delete(m.items, next) + heap.Pop(m.index) + } + m.cache = nil + + return ready +} + +// Len returns the length of the transaction map. +func (m *txSortedMap) Len() int { + return len(m.items) +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (m *txSortedMap) Flatten() types.Transactions { + // If the sorting was not cached yet, create and cache it + if m.cache == nil { + m.cache = make(types.Transactions, 0, len(m.items)) + for _, tx := range m.items { + m.cache = append(m.cache, tx) + } + sort.Sort(types.TxByNonce(m.cache)) + } + // Copy the cache to prevent accidental modifications + txs := make(types.Transactions, len(m.cache)) + copy(txs, m.cache) + return txs +} + +// txList is a "list" of transactions belonging to an account, sorted by account +// nonce. The same type can be used both for storing contiguous transactions for +// the executable/pending queue; and for storing gapped transactions for the non- +// executable/future queue, with minor behavioral changes. +type txList struct { + strict bool // Whether nonces are strictly continuous or not + txs *txSortedMap // Heap indexed sorted hash map of the transactions + costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) +} + +// newTxList create a new transaction list for maintaining nonce-indexable fast, +// gapped, sortable transaction lists. +func newTxList(strict bool) *txList { + return &txList{ + strict: strict, + txs: newTxSortedMap(), + costcap: new(big.Int), + } +} + +// Add tries to insert a new transaction into the list, returning whether the +// transaction was accepted, and if yes, any previous transaction it replaced. +// +// If the new transaction is accepted into the list, the lists' cost threshold +// is also potentially updated. +func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) { + // If there's an older better transaction, abort + old := l.txs.Get(tx.Nonce()) + if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 { + return false, nil + } + // Otherwise overwrite the old transaction with the current one + l.txs.Put(tx) + if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { + l.costcap = cost + } + return true, old +} + +// Forward removes all transactions from the list with a nonce lower than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. +func (l *txList) Forward(threshold uint64) types.Transactions { + return l.txs.Forward(threshold) +} + +// Filter removes all transactions from the list with a cost higher than the +// provided threshold. Every removed transaction is returned for any post-removal +// maintenance. Strict-mode invalidated transactions are also returned. +// +// This method uses the cached costcap to quickly decide if there's even a point +// in calculating all the costs or if the balance covers all. If the threshold is +// lower than the costcap, the costcap will be reset to a new high after removing +// expensive the too transactions. +func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) { + // If all transactions are below the threshold, short circuit + if l.costcap.Cmp(threshold) <= 0 { + return nil, nil + } + l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold + + // Filter out all the transactions above the account's funds + removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 }) + + // If the list was strict, filter anything above the lowest nonce + var invalids types.Transactions + if l.strict && len(removed) > 0 { + lowest := uint64(math.MaxUint64) + for _, tx := range removed { + if nonce := tx.Nonce(); lowest > nonce { + lowest = nonce + } + } + invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) + } + return removed, invalids +} + +// Cap places a hard limit on the number of items, returning all transactions +// exceeding that limit. +func (l *txList) Cap(threshold int) types.Transactions { + return l.txs.Cap(threshold) +} + +// Remove deletes a transaction from the maintained list, returning whether the +// transaction was found, and also returning any transaction invalidated due to +// the deletion (strict mode only). +func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { + // Remove the transaction from the set + nonce := tx.Nonce() + if removed := l.txs.Remove(nonce); !removed { + return false, nil + } + // In strict mode, filter out non-executable transactions + if l.strict { + return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) + } + return true, nil +} + +// Ready retrieves a sequentially increasing list of transactions starting at the +// provided nonce that is ready for processing. The returned transactions will be +// removed from the list. +// +// Note, all transactions with nonces lower than start will also be returned to +// prevent getting into and invalid state. This is not something that should ever +// happen but better to be self correcting than failing! +func (l *txList) Ready(start uint64) types.Transactions { + return l.txs.Ready(start) +} + +// Len returns the length of the transaction list. +func (l *txList) Len() int { + return l.txs.Len() +} + +// Empty returns whether the list of transactions is empty or not. +func (l *txList) Empty() bool { + return l.Len() == 0 +} + +// Flatten creates a nonce-sorted slice of transactions based on the loosely +// sorted internal representation. The result of the sorting is cached in case +// it's requested again before any modifications are made to the contents. +func (l *txList) Flatten() types.Transactions { + return l.txs.Flatten() +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_list_test.go b/vendor/github.com/ethereum/go-ethereum/core/tx_list_test.go new file mode 100644 index 0000000..92b2119 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_list_test.go @@ -0,0 +1,52 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// Tests that transactions can be added to strict lists and list contents and +// nonce boundaries are correctly maintained. +func TestStrictTxListAdd(t *testing.T) { + // Generate a list of transactions to insert + key, _ := crypto.GenerateKey() + + txs := make(types.Transactions, 1024) + for i := 0; i < len(txs); i++ { + txs[i] = transaction(uint64(i), new(big.Int), key) + } + // Insert the transactions in a random order + list := newTxList(true) + for _, v := range rand.Perm(len(txs)) { + list.Add(txs[v]) + } + // Verify internal state + if len(list.txs.items) != len(txs) { + t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs)) + } + for i, tx := range txs { + if list.txs.items[tx.Nonce()] != tx { + t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go new file mode 100644 index 0000000..987b43d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go @@ -0,0 +1,766 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "errors" + "fmt" + "math/big" + "sort" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" + "gopkg.in/karalabe/cookiejar.v2/collections/prque" +) + +var ( + // Transaction Pool Errors + ErrInvalidSender = errors.New("Invalid sender") + ErrNonce = errors.New("Nonce too low") + ErrCheap = errors.New("Gas price too low for acceptance") + ErrBalance = errors.New("Insufficient balance") + ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value") + ErrIntrinsicGas = errors.New("Intrinsic gas too low") + ErrGasLimit = errors.New("Exceeds block gas limit") + ErrNegativeValue = errors.New("Negative value") +) + +var ( + minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address + maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft) + maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address + maxQueuedInTotal = uint64(1024) // Max limit of queued transactions from all accounts + maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued + evictionInterval = time.Minute // Time interval to check for evictable transactions +) + +var ( + // Metrics for the pending pool + pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard") + pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace") + pendingRLCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting + pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds + + // Metrics for the queued pool + queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard") + queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace") + queuedRLCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting + queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds + + // General tx metrics + invalidTxCounter = metrics.NewCounter("txpool/invalid") +) + +type stateFn func() (*state.StateDB, error) + +// TxPool contains all currently known transactions. Transactions +// enter the pool when they are received from the network or submitted +// locally. They exit the pool when they are included in the blockchain. +// +// The pool separates processable transactions (which can be applied to the +// current state) and future transactions. Transactions move between those +// two states over time as they are received and processed. +type TxPool struct { + config *params.ChainConfig + currentState stateFn // The state function which will allow us to do some pre checks + pendingState *state.ManagedState + gasLimit func() *big.Int // The current gas limit function callback + minGasPrice *big.Int + eventMux *event.TypeMux + events *event.TypeMuxSubscription + localTx *txSet + signer types.Signer + mu sync.RWMutex + + pending map[common.Address]*txList // All currently processable transactions + queue map[common.Address]*txList // Queued but non-processable transactions + all map[common.Hash]*types.Transaction // All transactions to allow lookups + beats map[common.Address]time.Time // Last heartbeat from each known account + + wg sync.WaitGroup // for shutdown sync + quit chan struct{} + + homestead bool +} + +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { + pool := &TxPool{ + config: config, + signer: types.NewEIP155Signer(config.ChainId), + pending: make(map[common.Address]*txList), + queue: make(map[common.Address]*txList), + all: make(map[common.Hash]*types.Transaction), + beats: make(map[common.Address]time.Time), + eventMux: eventMux, + currentState: currentStateFn, + gasLimit: gasLimitFn, + minGasPrice: new(big.Int), + pendingState: nil, + localTx: newTxSet(), + events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), + quit: make(chan struct{}), + } + + pool.resetState() + + pool.wg.Add(2) + go pool.eventLoop() + go pool.expirationLoop() + + return pool +} + +func (pool *TxPool) eventLoop() { + defer pool.wg.Done() + + // Track chain events. When a chain events occurs (new chain canon block) + // we need to know the new state. The new state will help us determine + // the nonces in the managed state + for ev := range pool.events.Chan() { + switch ev := ev.Data.(type) { + case ChainHeadEvent: + pool.mu.Lock() + if ev.Block != nil { + if pool.config.IsHomestead(ev.Block.Number()) { + pool.homestead = true + } + } + + pool.resetState() + pool.mu.Unlock() + case GasPriceChanged: + pool.mu.Lock() + pool.minGasPrice = ev.Price + pool.mu.Unlock() + case RemovedTransactionEvent: + pool.AddBatch(ev.Txs) + } + } +} + +func (pool *TxPool) resetState() { + currentState, err := pool.currentState() + if err != nil { + glog.V(logger.Error).Infof("Failed to get current state: %v", err) + return + } + managedState := state.ManageState(currentState) + if err != nil { + glog.V(logger.Error).Infof("Failed to get managed state: %v", err) + return + } + pool.pendingState = managedState + + // validate the pool of pending transactions, this will remove + // any transactions that have been included in the block or + // have been invalidated because of another transaction (e.g. + // higher gas price) + pool.demoteUnexecutables(currentState) + + // Update all accounts to the latest known pending nonce + for addr, list := range pool.pending { + txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway + pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1) + } + // Check the queue and move transactions over to the pending if possible + // or remove those that have become invalid + pool.promoteExecutables(currentState) +} + +func (pool *TxPool) Stop() { + pool.events.Unsubscribe() + close(pool.quit) + pool.wg.Wait() + glog.V(logger.Info).Infoln("Transaction pool stopped") +} + +func (pool *TxPool) State() *state.ManagedState { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.pendingState +} + +// Stats retrieves the current pool stats, namely the number of pending and the +// number of queued (non-executable) transactions. +func (pool *TxPool) Stats() (pending int, queued int) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + for _, list := range pool.pending { + pending += list.Len() + } + for _, list := range pool.queue { + queued += list.Len() + } + return +} + +// Content retrieves the data content of the transaction pool, returning all the +// pending as well as queued transactions, grouped by account and sorted by nonce. +func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() + } + queued := make(map[common.Address]types.Transactions) + for addr, list := range pool.queue { + queued[addr] = list.Flatten() + } + return pending, queued +} + +// Pending retrieves all currently processable transactions, groupped by origin +// account and sorted by nonce. The returned transaction set is a copy and can be +// freely modified by calling code. +func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) { + pool.mu.Lock() + defer pool.mu.Unlock() + + state, err := pool.currentState() + if err != nil { + return nil, err + } + + // check queue first + pool.promoteExecutables(state) + + // invalidate any txs + pool.demoteUnexecutables(state) + + pending := make(map[common.Address]types.Transactions) + for addr, list := range pool.pending { + pending[addr] = list.Flatten() + } + return pending, nil +} + +// SetLocal marks a transaction as local, skipping gas price +// check against local miner minimum in the future +func (pool *TxPool) SetLocal(tx *types.Transaction) { + pool.mu.Lock() + defer pool.mu.Unlock() + pool.localTx.add(tx.Hash()) +} + +// validateTx checks whether a transaction is valid according +// to the consensus rules. +func (pool *TxPool) validateTx(tx *types.Transaction) error { + local := pool.localTx.contains(tx.Hash()) + // Drop transactions under our own minimal accepted gas price + if !local && pool.minGasPrice.Cmp(tx.GasPrice()) > 0 { + return ErrCheap + } + + currentState, err := pool.currentState() + if err != nil { + return err + } + + from, err := types.Sender(pool.signer, tx) + if err != nil { + return ErrInvalidSender + } + // Last but not least check for nonce errors + if currentState.GetNonce(from) > tx.Nonce() { + return ErrNonce + } + + // Check the transaction doesn't exceed the current + // block limit gas. + if pool.gasLimit().Cmp(tx.Gas()) < 0 { + return ErrGasLimit + } + + // Transactions can't be negative. This may never happen + // using RLP decoded transactions but may occur if you create + // a transaction using the RPC for example. + if tx.Value().Cmp(common.Big0) < 0 { + return ErrNegativeValue + } + + // Transactor should have enough funds to cover the costs + // cost == V + GP * GL + if currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { + return ErrInsufficientFunds + } + + intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) + if tx.Gas().Cmp(intrGas) < 0 { + return ErrIntrinsicGas + } + + return nil +} + +// add validates a transaction and inserts it into the non-executable queue for +// later pending promotion and execution. +func (pool *TxPool) add(tx *types.Transaction) error { + // If the transaction is already known, discard it + hash := tx.Hash() + if pool.all[hash] != nil { + return fmt.Errorf("Known transaction: %x", hash[:4]) + } + // Otherwise ensure basic validation passes and queue it up + if err := pool.validateTx(tx); err != nil { + invalidTxCounter.Inc(1) + return err + } + pool.enqueueTx(hash, tx) + + // Print a log message if low enough level is set + if glog.V(logger.Debug) { + rcpt := "[NEW_CONTRACT]" + if to := tx.To(); to != nil { + rcpt = common.Bytes2Hex(to[:4]) + } + from, _ := types.Sender(pool.signer, tx) // from already verified during tx validation + glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash) + } + return nil +} + +// enqueueTx inserts a new transaction into the non-executable transaction queue. +// +// Note, this method assumes the pool lock is held! +func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) { + // Try to insert the transaction into the future queue + from, _ := types.Sender(pool.signer, tx) // already validated + if pool.queue[from] == nil { + pool.queue[from] = newTxList(false) + } + inserted, old := pool.queue[from].Add(tx) + if !inserted { + queuedDiscardCounter.Inc(1) + return // An older transaction was better, discard this + } + // Discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + queuedReplaceCounter.Inc(1) + } + pool.all[hash] = tx +} + +// promoteTx adds a transaction to the pending (processable) list of transactions. +// +// Note, this method assumes the pool lock is held! +func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) { + // Try to insert the transaction into the pending queue + if pool.pending[addr] == nil { + pool.pending[addr] = newTxList(true) + } + list := pool.pending[addr] + + inserted, old := list.Add(tx) + if !inserted { + // An older transaction was better, discard this + delete(pool.all, hash) + pendingDiscardCounter.Inc(1) + return + } + // Otherwise discard any previous transaction and mark this + if old != nil { + delete(pool.all, old.Hash()) + pendingReplaceCounter.Inc(1) + } + pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests) + + // Set the potentially new pending nonce and notify any subsystems of the new tx + pool.beats[addr] = time.Now() + pool.pendingState.SetNonce(addr, tx.Nonce()+1) + go pool.eventMux.Post(TxPreEvent{tx}) +} + +// Add queues a single transaction in the pool if it is valid. +func (pool *TxPool) Add(tx *types.Transaction) error { + pool.mu.Lock() + defer pool.mu.Unlock() + + if err := pool.add(tx); err != nil { + return err + } + + state, err := pool.currentState() + if err != nil { + return err + } + + pool.promoteExecutables(state) + + return nil +} + +// AddBatch attempts to queue a batch of transactions. +func (pool *TxPool) AddBatch(txs []*types.Transaction) error { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + if err := pool.add(tx); err != nil { + glog.V(logger.Debug).Infoln("tx error:", err) + } + } + + state, err := pool.currentState() + if err != nil { + return err + } + + pool.promoteExecutables(state) + + return nil +} + +// Get returns a transaction if it is contained in the pool +// and nil otherwise. +func (pool *TxPool) Get(hash common.Hash) *types.Transaction { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.all[hash] +} + +// Remove removes the transaction with the given hash from the pool. +func (pool *TxPool) Remove(hash common.Hash) { + pool.mu.Lock() + defer pool.mu.Unlock() + + pool.removeTx(hash) +} + +// RemoveBatch removes all given transactions from the pool. +func (pool *TxPool) RemoveBatch(txs types.Transactions) { + pool.mu.Lock() + defer pool.mu.Unlock() + + for _, tx := range txs { + pool.removeTx(tx.Hash()) + } +} + +// removeTx removes a single transaction from the queue, moving all subsequent +// transactions back to the future queue. +func (pool *TxPool) removeTx(hash common.Hash) { + // Fetch the transaction we wish to delete + tx, ok := pool.all[hash] + if !ok { + return + } + addr, _ := types.Sender(pool.signer, tx) // already validated during insertion + + // Remove it from the list of known transactions + delete(pool.all, hash) + + // Remove the transaction from the pending lists and reset the account nonce + if pending := pool.pending[addr]; pending != nil { + if removed, invalids := pending.Remove(tx); removed { + // If no more transactions are left, remove the list + if pending.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) + } else { + // Otherwise postpone any invalidated transactions + for _, tx := range invalids { + pool.enqueueTx(tx.Hash(), tx) + } + } + // Update the account nonce if needed + if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce { + pool.pendingState.SetNonce(addr, tx.Nonce()) + } + } + } + // Transaction is in the future queue + if future := pool.queue[addr]; future != nil { + future.Remove(tx) + if future.Empty() { + delete(pool.queue, addr) + } + } +} + +// promoteExecutables moves transactions that have become processable from the +// future queue to the set of pending transactions. During this process, all +// invalidated transactions (low nonce, low balance) are deleted. +func (pool *TxPool) promoteExecutables(state *state.StateDB) { + // Iterate over all accounts and promote any executable transactions + queued := uint64(0) + for addr, list := range pool.queue { + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(state.GetNonce(addr)) { + if glog.V(logger.Debug) { + glog.Infof("Removed old queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + } + // Drop all transactions that are too costly (low balance) + drops, _ := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Debug) { + glog.Infof("Removed unpayable queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + queuedNofundsCounter.Inc(1) + } + // Gather all executable transactions and promote them + for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) { + if glog.V(logger.Debug) { + glog.Infof("Promoting queued transaction: %v", tx) + } + pool.promoteTx(addr, tx.Hash(), tx) + } + // Drop all transactions over the allowed limit + for _, tx := range list.Cap(int(maxQueuedPerAccount)) { + if glog.V(logger.Debug) { + glog.Infof("Removed cap-exceeding queued transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + queuedRLCounter.Inc(1) + } + queued += uint64(list.Len()) + + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.queue, addr) + } + } + // If the pending limit is overflown, start equalizing allowances + pending := uint64(0) + for _, list := range pool.pending { + pending += uint64(list.Len()) + } + if pending > maxPendingTotal { + pendingBeforeCap := pending + // Assemble a spam order to penalize large transactors first + spammers := prque.New() + for addr, list := range pool.pending { + // Only evict transactions from high rollers + if uint64(list.Len()) > minPendingPerAccount { + // Skip local accounts as pools should maintain backlogs for themselves + for _, tx := range list.txs.items { + if !pool.localTx.contains(tx.Hash()) { + spammers.Push(addr, float32(list.Len())) + } + break // Checking on transaction for locality is enough + } + } + } + // Gradually drop transactions from offenders + offenders := []common.Address{} + for pending > maxPendingTotal && !spammers.Empty() { + // Retrieve the next offender if not local address + offender, _ := spammers.Pop() + offenders = append(offenders, offender.(common.Address)) + + // Equalize balances until all the same or below threshold + if len(offenders) > 1 { + // Calculate the equalization threshold for all current offenders + threshold := pool.pending[offender.(common.Address)].Len() + + // Iteratively reduce all offenders until below limit or threshold reached + for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold { + for i := 0; i < len(offenders)-1; i++ { + list := pool.pending[offenders[i]] + list.Cap(list.Len() - 1) + pending-- + } + } + } + } + // If still above threshold, reduce to limit or min allowance + if pending > maxPendingTotal && len(offenders) > 0 { + for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount { + for _, addr := range offenders { + list := pool.pending[addr] + list.Cap(list.Len() - 1) + pending-- + } + } + } + pendingRLCounter.Inc(int64(pendingBeforeCap - pending)) + } + // If we've queued more transactions than the hard limit, drop oldest ones + if queued > maxQueuedInTotal { + // Sort all accounts with queued transactions by heartbeat + addresses := make(addresssByHeartbeat, 0, len(pool.queue)) + for addr := range pool.queue { + addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) + } + sort.Sort(addresses) + + // Drop transactions until the total is below the limit + for drop := queued - maxQueuedInTotal; drop > 0; { + addr := addresses[len(addresses)-1] + list := pool.queue[addr.address] + + addresses = addresses[:len(addresses)-1] + + // Drop all transactions if they are less than the overflow + if size := uint64(list.Len()); size <= drop { + for _, tx := range list.Flatten() { + pool.removeTx(tx.Hash()) + } + drop -= size + queuedRLCounter.Inc(int64(size)) + continue + } + // Otherwise drop only last few transactions + txs := list.Flatten() + for i := len(txs) - 1; i >= 0 && drop > 0; i-- { + pool.removeTx(txs[i].Hash()) + drop-- + queuedRLCounter.Inc(1) + } + } + } +} + +// demoteUnexecutables removes invalid and processed transactions from the pools +// executable/pending queue and any subsequent transactions that become unexecutable +// are moved back into the future queue. +func (pool *TxPool) demoteUnexecutables(state *state.StateDB) { + // Iterate over all accounts and demote any non-executable transactions + for addr, list := range pool.pending { + nonce := state.GetNonce(addr) + + // Drop all transactions that are deemed too old (low nonce) + for _, tx := range list.Forward(nonce) { + if glog.V(logger.Debug) { + glog.Infof("Removed old pending transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + } + // Drop all transactions that are too costly (low balance), and queue any invalids back for later + drops, invalids := list.Filter(state.GetBalance(addr)) + for _, tx := range drops { + if glog.V(logger.Debug) { + glog.Infof("Removed unpayable pending transaction: %v", tx) + } + delete(pool.all, tx.Hash()) + pendingNofundsCounter.Inc(1) + } + for _, tx := range invalids { + if glog.V(logger.Debug) { + glog.Infof("Demoting pending transaction: %v", tx) + } + pool.enqueueTx(tx.Hash(), tx) + } + // Delete the entire queue entry if it became empty. + if list.Empty() { + delete(pool.pending, addr) + delete(pool.beats, addr) + } + } +} + +// expirationLoop is a loop that periodically iterates over all accounts with +// queued transactions and drop all that have been inactive for a prolonged amount +// of time. +func (pool *TxPool) expirationLoop() { + defer pool.wg.Done() + + evict := time.NewTicker(evictionInterval) + defer evict.Stop() + + for { + select { + case <-evict.C: + pool.mu.Lock() + for addr := range pool.queue { + if time.Since(pool.beats[addr]) > maxQueuedLifetime { + for _, tx := range pool.queue[addr].Flatten() { + pool.removeTx(tx.Hash()) + } + } + } + pool.mu.Unlock() + + case <-pool.quit: + return + } + } +} + +// addressByHeartbeat is an account address tagged with its last activity timestamp. +type addressByHeartbeat struct { + address common.Address + heartbeat time.Time +} + +type addresssByHeartbeat []addressByHeartbeat + +func (a addresssByHeartbeat) Len() int { return len(a) } +func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) } +func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// txSet represents a set of transaction hashes in which entries +// are automatically dropped after txSetDuration time +type txSet struct { + txMap map[common.Hash]struct{} + txOrd map[uint64]txOrdType + addPtr, delPtr uint64 +} + +const txSetDuration = time.Hour * 2 + +// txOrdType represents an entry in the time-ordered list of transaction hashes +type txOrdType struct { + hash common.Hash + time time.Time +} + +// newTxSet creates a new transaction set +func newTxSet() *txSet { + return &txSet{ + txMap: make(map[common.Hash]struct{}), + txOrd: make(map[uint64]txOrdType), + } +} + +// contains returns true if the set contains the given transaction hash +// (not thread safe, should be called from a locked environment) +func (self *txSet) contains(hash common.Hash) bool { + _, ok := self.txMap[hash] + return ok +} + +// add adds a transaction hash to the set, then removes entries older than txSetDuration +// (not thread safe, should be called from a locked environment) +func (self *txSet) add(hash common.Hash) { + self.txMap[hash] = struct{}{} + now := time.Now() + self.txOrd[self.addPtr] = txOrdType{hash: hash, time: now} + self.addPtr++ + delBefore := now.Add(-txSetDuration) + for self.delPtr < self.addPtr && self.txOrd[self.delPtr].time.Before(delBefore) { + delete(self.txMap, self.txOrd[self.delPtr].hash) + delete(self.txOrd, self.delPtr) + self.delPtr++ + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_pool_test.go b/vendor/github.com/ethereum/go-ethereum/core/tx_pool_test.go new file mode 100644 index 0000000..98a34b7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool_test.go @@ -0,0 +1,884 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "crypto/ecdsa" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" +) + +func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction { + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil), types.HomesteadSigner{}, key) + return tx +} + +func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + + key, _ := crypto.GenerateKey() + newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + newPool.resetState() + + return newPool, key +} + +func deriveSender(tx *types.Transaction) (common.Address, error) { + return types.Sender(types.HomesteadSigner{}, tx) +} + +// This test simulates a scenario where a new block is imported during a +// state reset and tests whether the pending state is in sync with the +// block head event that initiated the resetState(). +func TestStateChangeDuringPoolReset(t *testing.T) { + var ( + db, _ = ethdb.NewMemDatabase() + key, _ = crypto.GenerateKey() + address = crypto.PubkeyToAddress(key.PublicKey) + mux = new(event.TypeMux) + statedb, _ = state.New(common.Hash{}, db) + trigger = false + ) + + // setup pool with 2 transaction in it + statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether)) + + tx0 := transaction(0, big.NewInt(100000), key) + tx1 := transaction(1, big.NewInt(100000), key) + + // stateFunc is used multiple times to reset the pending state. + // when simulate is true it will create a state that indicates + // that tx0 and tx1 are included in the chain. + stateFunc := func() (*state.StateDB, error) { + // delay "state change" by one. The tx pool fetches the + // state multiple times and by delaying it a bit we simulate + // a state change between those fetches. + stdb := statedb + if trigger { + statedb, _ = state.New(common.Hash{}, db) + // simulate that the new head block included tx0 and tx1 + statedb.SetNonce(address, 2) + statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether)) + trigger = false + } + return stdb, nil + } + + gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) } + + txpool := NewTxPool(testChainConfig(), mux, stateFunc, gasLimitFunc) + txpool.resetState() + + nonce := txpool.State().GetNonce(address) + if nonce != 0 { + t.Fatalf("Invalid nonce, want 0, got %d", nonce) + } + + txpool.AddBatch(types.Transactions{tx0, tx1}) + + nonce = txpool.State().GetNonce(address) + if nonce != 2 { + t.Fatalf("Invalid nonce, want 2, got %d", nonce) + } + + // trigger state change in the background + trigger = true + + txpool.resetState() + + pendingTx, err := txpool.Pending() + if err != nil { + t.Fatalf("Could not fetch pending transactions: %v", err) + } + + for addr, txs := range pendingTx { + t.Logf("%0x: %d\n", addr, len(txs)) + } + + nonce = txpool.State().GetNonce(address) + if nonce != 2 { + t.Fatalf("Invalid nonce, want 2, got %d", nonce) + } +} + +func TestInvalidTransactions(t *testing.T) { + pool, key := setupTxPool() + + tx := transaction(0, big.NewInt(100), key) + from, _ := deriveSender(tx) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) + if err := pool.Add(tx); err != ErrInsufficientFunds { + t.Error("expected", ErrInsufficientFunds) + } + + balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) + currentState.AddBalance(from, balance) + if err := pool.Add(tx); err != ErrIntrinsicGas { + t.Error("expected", ErrIntrinsicGas, "got", err) + } + + currentState.SetNonce(from, 1) + currentState.AddBalance(from, big.NewInt(0xffffffffffffff)) + tx = transaction(0, big.NewInt(100000), key) + if err := pool.Add(tx); err != ErrNonce { + t.Error("expected", ErrNonce) + } + + tx = transaction(1, big.NewInt(100000), key) + pool.minGasPrice = big.NewInt(1000) + if err := pool.Add(tx); err != ErrCheap { + t.Error("expected", ErrCheap, "got", err) + } + + pool.SetLocal(tx) + if err := pool.Add(tx); err != nil { + t.Error("expected", nil, "got", err) + } +} + +func TestTransactionQueue(t *testing.T) { + pool, key := setupTxPool() + tx := transaction(0, big.NewInt(100), key) + from, _ := deriveSender(tx) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1000)) + pool.resetState() + pool.enqueueTx(tx.Hash(), tx) + + pool.promoteExecutables(currentState) + if len(pool.pending) != 1 { + t.Error("expected valid txs to be 1 is", len(pool.pending)) + } + + tx = transaction(1, big.NewInt(100), key) + from, _ = deriveSender(tx) + currentState.SetNonce(from, 2) + pool.enqueueTx(tx.Hash(), tx) + pool.promoteExecutables(currentState) + if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok { + t.Error("expected transaction to be in tx pool") + } + + if len(pool.queue) > 0 { + t.Error("expected transaction queue to be empty. is", len(pool.queue)) + } + + pool, key = setupTxPool() + tx1 := transaction(0, big.NewInt(100), key) + tx2 := transaction(10, big.NewInt(100), key) + tx3 := transaction(11, big.NewInt(100), key) + from, _ = deriveSender(tx1) + currentState, _ = pool.currentState() + currentState.AddBalance(from, big.NewInt(1000)) + pool.resetState() + + pool.enqueueTx(tx1.Hash(), tx1) + pool.enqueueTx(tx2.Hash(), tx2) + pool.enqueueTx(tx3.Hash(), tx3) + + pool.promoteExecutables(currentState) + + if len(pool.pending) != 1 { + t.Error("expected tx pool to be 1, got", len(pool.pending)) + } + if pool.queue[from].Len() != 2 { + t.Error("expected len(queue) == 2, got", pool.queue[from].Len()) + } +} + +func TestRemoveTx(t *testing.T) { + pool, key := setupTxPool() + tx := transaction(0, big.NewInt(100), key) + from, _ := deriveSender(tx) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) + + pool.enqueueTx(tx.Hash(), tx) + pool.promoteTx(from, tx.Hash(), tx) + if len(pool.queue) != 1 { + t.Error("expected queue to be 1, got", len(pool.queue)) + } + if len(pool.pending) != 1 { + t.Error("expected pending to be 1, got", len(pool.pending)) + } + pool.Remove(tx.Hash()) + if len(pool.queue) > 0 { + t.Error("expected queue to be 0, got", len(pool.queue)) + } + if len(pool.pending) > 0 { + t.Error("expected pending to be 0, got", len(pool.pending)) + } +} + +func TestNegativeValue(t *testing.T) { + pool, key := setupTxPool() + + tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil), types.HomesteadSigner{}, key) + from, _ := deriveSender(tx) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1)) + if err := pool.Add(tx); err != ErrNegativeValue { + t.Error("expected", ErrNegativeValue, "got", err) + } +} + +func TestTransactionChainFork(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + resetState := func() { + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + pool.currentState = func() (*state.StateDB, error) { return statedb, nil } + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) + pool.resetState() + } + resetState() + + tx := transaction(0, big.NewInt(100000), key) + if err := pool.add(tx); err != nil { + t.Error("didn't expect error", err) + } + pool.RemoveBatch([]*types.Transaction{tx}) + + // reset the pool's internal state + resetState() + if err := pool.add(tx); err != nil { + t.Error("didn't expect error", err) + } +} + +func TestTransactionDoubleNonce(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + resetState := func() { + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + pool.currentState = func() (*state.StateDB, error) { return statedb, nil } + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) + pool.resetState() + } + resetState() + + signer := types.HomesteadSigner{} + tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil), signer, key) + tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil), signer, key) + tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil), signer, key) + + // Add the first two transaction, ensure higher priced stays only + if err := pool.add(tx1); err != nil { + t.Error("didn't expect error", err) + } + if err := pool.add(tx2); err != nil { + t.Error("didn't expect error", err) + } + state, _ := pool.currentState() + pool.promoteExecutables(state) + if pool.pending[addr].Len() != 1 { + t.Error("expected 1 pending transactions, got", pool.pending[addr].Len()) + } + if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() { + t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash()) + } + // Add the thid transaction and ensure it's not saved (smaller price) + if err := pool.add(tx3); err != nil { + t.Error("didn't expect error", err) + } + pool.promoteExecutables(state) + if pool.pending[addr].Len() != 1 { + t.Error("expected 1 pending transactions, got", pool.pending[addr].Len()) + } + if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() { + t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash()) + } + // Ensure the total transaction count is correct + if len(pool.all) != 1 { + t.Error("expected 1 total transactions, got", len(pool.all)) + } +} + +func TestMissingNonce(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + currentState, _ := pool.currentState() + currentState.AddBalance(addr, big.NewInt(100000000000000)) + tx := transaction(1, big.NewInt(100000), key) + if err := pool.add(tx); err != nil { + t.Error("didn't expect error", err) + } + if len(pool.pending) != 0 { + t.Error("expected 0 pending transactions, got", len(pool.pending)) + } + if pool.queue[addr].Len() != 1 { + t.Error("expected 1 queued transaction, got", pool.queue[addr].Len()) + } + if len(pool.all) != 1 { + t.Error("expected 1 total transactions, got", len(pool.all)) + } +} + +func TestNonceRecovery(t *testing.T) { + const n = 10 + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + currentState, _ := pool.currentState() + currentState.SetNonce(addr, n) + currentState.AddBalance(addr, big.NewInt(100000000000000)) + pool.resetState() + tx := transaction(n, big.NewInt(100000), key) + if err := pool.Add(tx); err != nil { + t.Error(err) + } + // simulate some weird re-order of transactions and missing nonce(s) + currentState.SetNonce(addr, n-1) + pool.resetState() + if fn := pool.pendingState.GetNonce(addr); fn != n+1 { + t.Errorf("expected nonce to be %d, got %d", n+1, fn) + } +} + +func TestRemovedTxEvent(t *testing.T) { + pool, key := setupTxPool() + tx := transaction(0, big.NewInt(1000000), key) + from, _ := deriveSender(tx) + currentState, _ := pool.currentState() + currentState.AddBalance(from, big.NewInt(1000000000000)) + pool.resetState() + pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}}) + pool.eventMux.Post(ChainHeadEvent{nil}) + if pool.pending[from].Len() != 1 { + t.Error("expected 1 pending tx, got", pool.pending[from].Len()) + } + if len(pool.all) != 1 { + t.Error("expected 1 total transactions, got", len(pool.all)) + } +} + +// Tests that if an account runs out of funds, any pending and queued transactions +// are dropped. +func TestTransactionDropping(t *testing.T) { + // Create a test account and fund it + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000)) + + // Add some pending and some queued transactions + var ( + tx0 = transaction(0, big.NewInt(100), key) + tx1 = transaction(1, big.NewInt(200), key) + tx10 = transaction(10, big.NewInt(100), key) + tx11 = transaction(11, big.NewInt(200), key) + ) + pool.promoteTx(account, tx0.Hash(), tx0) + pool.promoteTx(account, tx1.Hash(), tx1) + pool.enqueueTx(tx10.Hash(), tx10) + pool.enqueueTx(tx11.Hash(), tx11) + + // Check that pre and post validations leave the pool as is + if pool.pending[account].Len() != 2 { + t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2) + } + if pool.queue[account].Len() != 2 { + t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2) + } + if len(pool.all) != 4 { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4) + } + pool.resetState() + if pool.pending[account].Len() != 2 { + t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2) + } + if pool.queue[account].Len() != 2 { + t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2) + } + if len(pool.all) != 4 { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4) + } + // Reduce the balance of the account, and check that invalidated transactions are dropped + state.AddBalance(account, big.NewInt(-750)) + pool.resetState() + + if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok { + t.Errorf("funded pending transaction missing: %v", tx0) + } + if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok { + t.Errorf("out-of-fund pending transaction present: %v", tx1) + } + if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok { + t.Errorf("funded queued transaction missing: %v", tx10) + } + if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok { + t.Errorf("out-of-fund queued transaction present: %v", tx11) + } + if len(pool.all) != 2 { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2) + } +} + +// Tests that if a transaction is dropped from the current pending pool (e.g. out +// of fund), all consecutive (still valid, but not executable) transactions are +// postponed back into the future queue to prevent broadcasting them. +func TestTransactionPostponing(t *testing.T) { + // Create a test account and fund it + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000)) + + // Add a batch consecutive pending transactions for validation + txns := []*types.Transaction{} + for i := 0; i < 100; i++ { + var tx *types.Transaction + if i%2 == 0 { + tx = transaction(uint64(i), big.NewInt(100), key) + } else { + tx = transaction(uint64(i), big.NewInt(500), key) + } + pool.promoteTx(account, tx.Hash(), tx) + txns = append(txns, tx) + } + // Check that pre and post validations leave the pool as is + if pool.pending[account].Len() != len(txns) { + t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) + } + if len(pool.queue) != 0 { + t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) + } + if len(pool.all) != len(txns) { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) + } + pool.resetState() + if pool.pending[account].Len() != len(txns) { + t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) + } + if len(pool.queue) != 0 { + t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) + } + if len(pool.all) != len(txns) { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) + } + // Reduce the balance of the account, and check that transactions are reorganised + state.AddBalance(account, big.NewInt(-750)) + pool.resetState() + + if _, ok := pool.pending[account].txs.items[txns[0].Nonce()]; !ok { + t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0]) + } + if _, ok := pool.queue[account].txs.items[txns[0].Nonce()]; ok { + t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txns[0]) + } + for i, tx := range txns[1:] { + if i%2 == 1 { + if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { + t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx) + } + if _, ok := pool.queue[account].txs.items[tx.Nonce()]; !ok { + t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx) + } + } else { + if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { + t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx) + } + if _, ok := pool.queue[account].txs.items[tx.Nonce()]; ok { + t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx) + } + } + } + if len(pool.all) != len(txns)/2 { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)/2) + } +} + +// Tests that if the transaction count belonging to a single account goes above +// some threshold, the higher transactions are dropped to prevent DOS attacks. +func TestTransactionQueueAccountLimiting(t *testing.T) { + // Create a test account and fund it + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + pool.resetState() + + // Keep queuing up transactions and make sure all above a limit are dropped + for i := uint64(1); i <= maxQueuedPerAccount+5; i++ { + if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { + t.Fatalf("tx %d: failed to add transaction: %v", i, err) + } + if len(pool.pending) != 0 { + t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0) + } + if i <= maxQueuedPerAccount { + if pool.queue[account].Len() != int(i) { + t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i) + } + } else { + if pool.queue[account].Len() != int(maxQueuedPerAccount) { + t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), maxQueuedPerAccount) + } + } + } + if len(pool.all) != int(maxQueuedPerAccount) { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount) + } +} + +// Tests that if the transaction count belonging to multiple accounts go above +// some threshold, the higher transactions are dropped to prevent DOS attacks. +func TestTransactionQueueGlobalLimiting(t *testing.T) { + // Reduce the queue limits to shorten test time + defer func(old uint64) { maxQueuedInTotal = old }(maxQueuedInTotal) + maxQueuedInTotal = maxQueuedPerAccount * 3 + + // Create the pool to test the limit enforcement with + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + + pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool.resetState() + + // Create a number of test accounts and fund them + state, _ := pool.currentState() + + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + // Generate and queue a batch of transactions + nonces := make(map[common.Address]uint64) + + txs := make(types.Transactions, 0, 3*maxQueuedInTotal) + for len(txs) < cap(txs) { + key := keys[rand.Intn(len(keys))] + addr := crypto.PubkeyToAddress(key.PublicKey) + + txs = append(txs, transaction(nonces[addr]+1, big.NewInt(100000), key)) + nonces[addr]++ + } + // Import the batch and verify that limits have been enforced + pool.AddBatch(txs) + + queued := 0 + for addr, list := range pool.queue { + if list.Len() > int(maxQueuedPerAccount) { + t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), maxQueuedPerAccount) + } + queued += list.Len() + } + if queued > int(maxQueuedInTotal) { + t.Fatalf("total transactions overflow allowance: %d > %d", queued, maxQueuedInTotal) + } +} + +// Tests that if an account remains idle for a prolonged amount of time, any +// non-executable transactions queued up are dropped to prevent wasting resources +// on shuffling them around. +func TestTransactionQueueTimeLimiting(t *testing.T) { + // Reduce the queue limits to shorten test time + defer func(old time.Duration) { maxQueuedLifetime = old }(maxQueuedLifetime) + defer func(old time.Duration) { evictionInterval = old }(evictionInterval) + maxQueuedLifetime = time.Second + evictionInterval = time.Second + + // Create a test account and fund it + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + + // Queue up a batch of transactions + for i := uint64(1); i <= maxQueuedPerAccount; i++ { + if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { + t.Fatalf("tx %d: failed to add transaction: %v", i, err) + } + } + // Wait until at least two expiration cycles hit and make sure the transactions are gone + time.Sleep(2 * evictionInterval) + if len(pool.queue) > 0 { + t.Fatalf("old transactions remained after eviction") + } +} + +// Tests that even if the transaction count belonging to a single account goes +// above some threshold, as long as the transactions are executable, they are +// accepted. +func TestTransactionPendingLimiting(t *testing.T) { + // Create a test account and fund it + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + pool.resetState() + + // Keep queuing up transactions and make sure all above a limit are dropped + for i := uint64(0); i < maxQueuedPerAccount+5; i++ { + if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil { + t.Fatalf("tx %d: failed to add transaction: %v", i, err) + } + if pool.pending[account].Len() != int(i)+1 { + t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, pool.pending[account].Len(), i+1) + } + if len(pool.queue) != 0 { + t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0) + } + } + if len(pool.all) != int(maxQueuedPerAccount+5) { + t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount+5) + } +} + +// Tests that the transaction limits are enforced the same way irrelevant whether +// the transactions are added one by one or in batches. +func TestTransactionQueueLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 1) } +func TestTransactionPendingLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 0) } + +func testTransactionLimitingEquivalency(t *testing.T, origin uint64) { + // Add a batch of transactions to a pool one by one + pool1, key1 := setupTxPool() + account1, _ := deriveSender(transaction(0, big.NewInt(0), key1)) + state1, _ := pool1.currentState() + state1.AddBalance(account1, big.NewInt(1000000)) + + for i := uint64(0); i < maxQueuedPerAccount+5; i++ { + if err := pool1.Add(transaction(origin+i, big.NewInt(100000), key1)); err != nil { + t.Fatalf("tx %d: failed to add transaction: %v", i, err) + } + } + // Add a batch of transactions to a pool in one big batch + pool2, key2 := setupTxPool() + account2, _ := deriveSender(transaction(0, big.NewInt(0), key2)) + state2, _ := pool2.currentState() + state2.AddBalance(account2, big.NewInt(1000000)) + + txns := []*types.Transaction{} + for i := uint64(0); i < maxQueuedPerAccount+5; i++ { + txns = append(txns, transaction(origin+i, big.NewInt(100000), key2)) + } + pool2.AddBatch(txns) + + // Ensure the batch optimization honors the same pool mechanics + if len(pool1.pending) != len(pool2.pending) { + t.Errorf("pending transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.pending), len(pool2.pending)) + } + if len(pool1.queue) != len(pool2.queue) { + t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue)) + } + if len(pool1.all) != len(pool2.all) { + t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", len(pool1.all), len(pool2.all)) + } +} + +// Tests that if the transaction count belonging to multiple accounts go above +// some hard threshold, the higher transactions are dropped to prevent DOS +// attacks. +func TestTransactionPendingGlobalLimiting(t *testing.T) { + // Reduce the queue limits to shorten test time + defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal) + maxPendingTotal = minPendingPerAccount * 10 + + // Create the pool to test the limit enforcement with + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + + pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool.resetState() + + // Create a number of test accounts and fund them + state, _ := pool.currentState() + + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + // Generate and queue a batch of transactions + nonces := make(map[common.Address]uint64) + + txs := types.Transactions{} + for _, key := range keys { + addr := crypto.PubkeyToAddress(key.PublicKey) + for j := 0; j < int(maxPendingTotal)/len(keys)*2; j++ { + txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key)) + nonces[addr]++ + } + } + // Import the batch and verify that limits have been enforced + pool.AddBatch(txs) + + pending := 0 + for _, list := range pool.pending { + pending += list.Len() + } + if pending > int(maxPendingTotal) { + t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, maxPendingTotal) + } +} + +// Tests that if the transaction count belonging to multiple accounts go above +// some hard threshold, if they are under the minimum guaranteed slot count then +// the transactions are still kept. +func TestTransactionPendingMinimumAllowance(t *testing.T) { + // Reduce the queue limits to shorten test time + defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal) + maxPendingTotal = 0 + + // Create the pool to test the limit enforcement with + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + + pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool.resetState() + + // Create a number of test accounts and fund them + state, _ := pool.currentState() + + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) + } + // Generate and queue a batch of transactions + nonces := make(map[common.Address]uint64) + + txs := types.Transactions{} + for _, key := range keys { + addr := crypto.PubkeyToAddress(key.PublicKey) + for j := 0; j < int(minPendingPerAccount)*2; j++ { + txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key)) + nonces[addr]++ + } + } + // Import the batch and verify that limits have been enforced + pool.AddBatch(txs) + + for addr, list := range pool.pending { + if list.Len() != int(minPendingPerAccount) { + t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), minPendingPerAccount) + } + } +} + +// Benchmarks the speed of validating the contents of the pending queue of the +// transaction pool. +func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) } +func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) } +func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) } + +func benchmarkPendingDemotion(b *testing.B, size int) { + // Add a batch of transactions to a pool one by one + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + + for i := 0; i < size; i++ { + tx := transaction(uint64(i), big.NewInt(100000), key) + pool.promoteTx(account, tx.Hash(), tx) + } + // Benchmark the speed of pool validation + b.ResetTimer() + for i := 0; i < b.N; i++ { + pool.demoteUnexecutables(state) + } +} + +// Benchmarks the speed of scheduling the contents of the future queue of the +// transaction pool. +func BenchmarkFuturePromotion100(b *testing.B) { benchmarkFuturePromotion(b, 100) } +func BenchmarkFuturePromotion1000(b *testing.B) { benchmarkFuturePromotion(b, 1000) } +func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 10000) } + +func benchmarkFuturePromotion(b *testing.B, size int) { + // Add a batch of transactions to a pool one by one + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + + for i := 0; i < size; i++ { + tx := transaction(uint64(1+i), big.NewInt(100000), key) + pool.enqueueTx(tx.Hash(), tx) + } + // Benchmark the speed of pool validation + b.ResetTimer() + for i := 0; i < b.N; i++ { + pool.promoteExecutables(state) + } +} + +// Benchmarks the speed of iterative transaction insertion. +func BenchmarkPoolInsert(b *testing.B) { + // Generate a batch of transactions to enqueue into the pool + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + + txs := make(types.Transactions, b.N) + for i := 0; i < b.N; i++ { + txs[i] = transaction(uint64(i), big.NewInt(100000), key) + } + // Benchmark importing the transactions into the queue + b.ResetTimer() + for _, tx := range txs { + pool.Add(tx) + } +} + +// Benchmarks the speed of batched transaction insertion. +func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100) } +func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000) } +func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000) } + +func benchmarkPoolBatchInsert(b *testing.B, size int) { + // Generate a batch of transactions to enqueue into the pool + pool, key := setupTxPool() + account, _ := deriveSender(transaction(0, big.NewInt(0), key)) + state, _ := pool.currentState() + state.AddBalance(account, big.NewInt(1000000)) + + batches := make([]types.Transactions, b.N) + for i := 0; i < b.N; i++ { + batches[i] = make(types.Transactions, size) + for j := 0; j < size; j++ { + batches[i][j] = transaction(uint64(size*i+j), big.NewInt(100000), key) + } + } + // Benchmark importing the transactions into the queue + b.ResetTimer() + for _, batch := range batches { + pool.AddBatch(batch) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types.go b/vendor/github.com/ethereum/go-ethereum/core/types.go new file mode 100644 index 0000000..7fd6589 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types.go @@ -0,0 +1,62 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// Validator is an interface which defines the standard for block validation. +// +// The validator is responsible for validating incoming block or, if desired, +// validates headers for fast validation. +// +// ValidateBlock validates the given block and should return an error if it +// failed to do so and should be used for "full" validation. +// +// ValidateHeader validates the given header and parent and returns an error +// if it failed to do so. +// +// ValidateState validates the given statedb and optionally the receipts and +// gas used. The implementer should decide what to do with the given input. +type Validator interface { + HeaderValidator + ValidateBlock(block *types.Block) error + ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error +} + +// HeaderValidator is an interface for validating headers only +// +// ValidateHeader validates the given header and parent and returns an error +// if it failed to do so. +type HeaderValidator interface { + ValidateHeader(header, parent *types.Header, checkPow bool) error +} + +// Processor is an interface for processing blocks using a given initial state. +// +// Process takes the block to be processed and the statedb upon which the +// initial state is based. It should return the receipts generated, amount +// of gas used in the process and return an error if any of the internal rules +// failed. +type Processor interface { + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/block.go b/vendor/github.com/ethereum/go-ethereum/core/types/block.go new file mode 100644 index 0000000..1a2a1f2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block.go @@ -0,0 +1,519 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package types contains data types related to Ethereum consensus. +package types + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "sort" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + EmptyRootHash = DeriveSha(Transactions{}) + EmptyUncleHash = CalcUncleHash(nil) +) + +var ( + errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header") + errMissingHeaderFields = errors.New("missing required JSON block header fields") + errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes") +) + +// A BlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a sufficient amount of computation has been carried +// out on a block. +type BlockNonce [8]byte + +// EncodeNonce converts the given integer to a block nonce. +func EncodeNonce(i uint64) BlockNonce { + var n BlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +// Uint64 returns the integer value of a block nonce. +func (n BlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +// MarshalJSON implements json.Marshaler +func (n BlockNonce) MarshalJSON() ([]byte, error) { + return hexutil.Bytes(n[:]).MarshalJSON() +} + +// UnmarshalJSON implements json.Unmarshaler +func (n *BlockNonce) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalJSON("BlockNonce", input, n[:]) +} + +// Header represents a block header in the Ethereum blockchain. +type Header struct { + ParentHash common.Hash // Hash to the previous block + UncleHash common.Hash // Uncles of this block + Coinbase common.Address // The coin base address + Root common.Hash // Block Trie state + TxHash common.Hash // Tx sha + ReceiptHash common.Hash // Receipt sha + Bloom Bloom // Bloom + Difficulty *big.Int // Difficulty for the current block + Number *big.Int // The block number + GasLimit *big.Int // Gas limit + GasUsed *big.Int // Gas used + Time *big.Int // Creation time + Extra []byte // Extra data + MixDigest common.Hash // for quick difficulty verification + Nonce BlockNonce +} + +type jsonHeader struct { + ParentHash *common.Hash `json:"parentHash"` + UncleHash *common.Hash `json:"sha3Uncles"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot"` + TxHash *common.Hash `json:"transactionsRoot"` + ReceiptHash *common.Hash `json:"receiptsRoot"` + Bloom *Bloom `json:"logsBloom"` + Difficulty *hexutil.Big `json:"difficulty"` + Number *hexutil.Big `json:"number"` + GasLimit *hexutil.Big `json:"gasLimit"` + GasUsed *hexutil.Big `json:"gasUsed"` + Time *hexutil.Big `json:"timestamp"` + Extra *hexutil.Bytes `json:"extraData"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` +} + +// Hash returns the block hash of the header, which is simply the keccak256 hash of its +// RLP encoding. +func (h *Header) Hash() common.Hash { + return rlpHash(h) +} + +// HashNoNonce returns the hash which is used as input for the proof-of-work search. +func (h *Header) HashNoNonce() common.Hash { + return rlpHash([]interface{}{ + h.ParentHash, + h.UncleHash, + h.Coinbase, + h.Root, + h.TxHash, + h.ReceiptHash, + h.Bloom, + h.Difficulty, + h.Number, + h.GasLimit, + h.GasUsed, + h.Time, + h.Extra, + }) +} + +// MarshalJSON encodes headers into the web3 RPC response block format. +func (h *Header) MarshalJSON() ([]byte, error) { + return json.Marshal(&jsonHeader{ + ParentHash: &h.ParentHash, + UncleHash: &h.UncleHash, + Coinbase: &h.Coinbase, + Root: &h.Root, + TxHash: &h.TxHash, + ReceiptHash: &h.ReceiptHash, + Bloom: &h.Bloom, + Difficulty: (*hexutil.Big)(h.Difficulty), + Number: (*hexutil.Big)(h.Number), + GasLimit: (*hexutil.Big)(h.GasLimit), + GasUsed: (*hexutil.Big)(h.GasUsed), + Time: (*hexutil.Big)(h.Time), + Extra: (*hexutil.Bytes)(&h.Extra), + MixDigest: &h.MixDigest, + Nonce: &h.Nonce, + }) +} + +// UnmarshalJSON decodes headers from the web3 RPC response block format. +func (h *Header) UnmarshalJSON(input []byte) error { + var dec jsonHeader + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + // Ensure that all fields are set. MixDigest is checked separately because + // it is a recent addition to the spec (as of August 2016) and older RPC server + // implementations might not provide it. + if dec.MixDigest == nil { + return errMissingHeaderMixDigest + } + if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil || + dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil || + dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil || + dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil || + dec.Extra == nil || dec.Nonce == nil { + return errMissingHeaderFields + } + // Assign all values. + h.ParentHash = *dec.ParentHash + h.UncleHash = *dec.UncleHash + h.Coinbase = *dec.Coinbase + h.Root = *dec.Root + h.TxHash = *dec.TxHash + h.ReceiptHash = *dec.ReceiptHash + h.Bloom = *dec.Bloom + h.Difficulty = (*big.Int)(dec.Difficulty) + h.Number = (*big.Int)(dec.Number) + h.GasLimit = (*big.Int)(dec.GasLimit) + h.GasUsed = (*big.Int)(dec.GasUsed) + h.Time = (*big.Int)(dec.Time) + h.Extra = *dec.Extra + h.MixDigest = *dec.MixDigest + h.Nonce = *dec.Nonce + return nil +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} + +// Body is a simple (mutable, non-safe) data container for storing and moving +// a block's data contents (transactions and uncles) together. +type Body struct { + Transactions []*Transaction + Uncles []*Header +} + +// Block represents an entire block in the Ethereum blockchain. +type Block struct { + header *Header + uncles []*Header + transactions Transactions + + // caches + hash atomic.Value + size atomic.Value + + // Td is used by package core to store the total difficulty + // of the chain up to and including the block. + td *big.Int + + // These fields are used by package eth to track + // inter-peer block relay. + ReceivedAt time.Time + ReceivedFrom interface{} +} + +// DeprecatedTd is an old relic for extracting the TD of a block. It is in the +// code solely to facilitate upgrading the database from the old format to the +// new, after which it should be deleted. Do not use! +func (b *Block) DeprecatedTd() *big.Int { + return b.td +} + +// [deprecated by eth/63] +// StorageBlock defines the RLP encoding of a Block stored in the +// state database. The StorageBlock encoding contains fields that +// would otherwise need to be recomputed. +type StorageBlock Block + +// "external" block encoding. used for eth protocol, etc. +type extblock struct { + Header *Header + Txs []*Transaction + Uncles []*Header +} + +// [deprecated by eth/63] +// "storage" block encoding. used for database. +type storageblock struct { + Header *Header + Txs []*Transaction + Uncles []*Header + TD *big.Int +} + +// NewBlock creates a new block. The input data is copied, +// changes to header and to the field values will not affect the +// block. +// +// The values of TxHash, UncleHash, ReceiptHash and Bloom in header +// are ignored and set to values derived from the given txs, uncles +// and receipts. +func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { + b := &Block{header: CopyHeader(header), td: new(big.Int)} + + // TODO: panic if len(txs) != len(receipts) + if len(txs) == 0 { + b.header.TxHash = EmptyRootHash + } else { + b.header.TxHash = DeriveSha(Transactions(txs)) + b.transactions = make(Transactions, len(txs)) + copy(b.transactions, txs) + } + + if len(receipts) == 0 { + b.header.ReceiptHash = EmptyRootHash + } else { + b.header.ReceiptHash = DeriveSha(Receipts(receipts)) + b.header.Bloom = CreateBloom(receipts) + } + + if len(uncles) == 0 { + b.header.UncleHash = EmptyUncleHash + } else { + b.header.UncleHash = CalcUncleHash(uncles) + b.uncles = make([]*Header, len(uncles)) + for i := range uncles { + b.uncles[i] = CopyHeader(uncles[i]) + } + } + + return b +} + +// NewBlockWithHeader creates a block with the given header data. The +// header data is copied, changes to header and to the field values +// will not affect the block. +func NewBlockWithHeader(header *Header) *Block { + return &Block{header: CopyHeader(header)} +} + +// CopyHeader creates a deep copy of a block header to prevent side effects from +// modifying a header variable. +func CopyHeader(h *Header) *Header { + cpy := *h + if cpy.Time = new(big.Int); h.Time != nil { + cpy.Time.Set(h.Time) + } + if cpy.Difficulty = new(big.Int); h.Difficulty != nil { + cpy.Difficulty.Set(h.Difficulty) + } + if cpy.Number = new(big.Int); h.Number != nil { + cpy.Number.Set(h.Number) + } + if cpy.GasLimit = new(big.Int); h.GasLimit != nil { + cpy.GasLimit.Set(h.GasLimit) + } + if cpy.GasUsed = new(big.Int); h.GasUsed != nil { + cpy.GasUsed.Set(h.GasUsed) + } + if len(h.Extra) > 0 { + cpy.Extra = make([]byte, len(h.Extra)) + copy(cpy.Extra, h.Extra) + } + return &cpy +} + +// DecodeRLP decodes the Ethereum +func (b *Block) DecodeRLP(s *rlp.Stream) error { + var eb extblock + _, size, _ := s.Kind() + if err := s.Decode(&eb); err != nil { + return err + } + b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs + b.size.Store(common.StorageSize(rlp.ListSize(size))) + return nil +} + +// EncodeRLP serializes b into the Ethereum RLP block format. +func (b *Block) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, extblock{ + Header: b.header, + Txs: b.transactions, + Uncles: b.uncles, + }) +} + +// [deprecated by eth/63] +func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { + var sb storageblock + if err := s.Decode(&sb); err != nil { + return err + } + b.header, b.uncles, b.transactions, b.td = sb.Header, sb.Uncles, sb.Txs, sb.TD + return nil +} + +// TODO: copies + +func (b *Block) Uncles() []*Header { return b.uncles } +func (b *Block) Transactions() Transactions { return b.transactions } + +func (b *Block) Transaction(hash common.Hash) *Transaction { + for _, transaction := range b.transactions { + if transaction.Hash() == hash { + return transaction + } + } + return nil +} + +func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } +func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) } +func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) } +func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } +func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) } + +func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } +func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } +func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } +func (b *Block) Bloom() Bloom { return b.header.Bloom } +func (b *Block) Coinbase() common.Address { return b.header.Coinbase } +func (b *Block) Root() common.Hash { return b.header.Root } +func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } +func (b *Block) TxHash() common.Hash { return b.header.TxHash } +func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } +func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } +func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } + +func (b *Block) Header() *Header { return CopyHeader(b.header) } + +// Body returns the non-header content of the block. +func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } + +func (b *Block) HashNoNonce() common.Hash { + return b.header.HashNoNonce() +} + +func (b *Block) Size() common.StorageSize { + if size := b.size.Load(); size != nil { + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, b) + b.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +type writeCounter common.StorageSize + +func (c *writeCounter) Write(b []byte) (int, error) { + *c += writeCounter(len(b)) + return len(b), nil +} + +func CalcUncleHash(uncles []*Header) common.Hash { + return rlpHash(uncles) +} + +// WithMiningResult returns a new block with the data from b +// where nonce and mix digest are set to the provided values. +func (b *Block) WithMiningResult(nonce BlockNonce, mixDigest common.Hash) *Block { + cpy := *b.header + cpy.Nonce = nonce + cpy.MixDigest = mixDigest + return &Block{ + header: &cpy, + transactions: b.transactions, + uncles: b.uncles, + } +} + +// WithBody returns a new block with the given transaction and uncle contents. +func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { + block := &Block{ + header: CopyHeader(b.header), + transactions: make([]*Transaction, len(transactions)), + uncles: make([]*Header, len(uncles)), + } + copy(block.transactions, transactions) + for i := range uncles { + block.uncles[i] = CopyHeader(uncles[i]) + } + return block +} + +// Hash returns the keccak256 hash of b's header. +// The hash is computed on the first call and cached thereafter. +func (b *Block) Hash() common.Hash { + if hash := b.hash.Load(); hash != nil { + return hash.(common.Hash) + } + v := rlpHash(b.header) + b.hash.Store(v) + return v +} + +func (b *Block) String() string { + str := fmt.Sprintf(`Block(#%v): Size: %v { +MinerHash: %x +%v +Transactions: +%v +Uncles: +%v +} +`, b.Number(), b.Size(), b.header.HashNoNonce(), b.header, b.transactions, b.uncles) + return str +} + +func (h *Header) String() string { + return fmt.Sprintf(`Header(%x): +[ + ParentHash: %x + UncleHash: %x + Coinbase: %x + Root: %x + TxSha %x + ReceiptSha: %x + Bloom: %x + Difficulty: %v + Number: %v + GasLimit: %v + GasUsed: %v + Time: %v + Extra: %s + MixDigest: %x + Nonce: %x +]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce) +} + +type Blocks []*Block + +type BlockBy func(b1, b2 *Block) bool + +func (self BlockBy) Sort(blocks Blocks) { + bs := blockSorter{ + blocks: blocks, + by: self, + } + sort.Sort(bs) +} + +type blockSorter struct { + blocks Blocks + by func(b1, b2 *Block) bool +} + +func (self blockSorter) Len() int { return len(self.blocks) } +func (self blockSorter) Swap(i, j int) { + self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +} +func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/block_test.go b/vendor/github.com/ethereum/go-ethereum/core/types/block_test.go new file mode 100644 index 0000000..93435ca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/block_test.go @@ -0,0 +1,70 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +// from bcValidBlockTest.json, "SimpleTx" +func TestBlockEncoding(t *testing.T) { + blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0") + var block Block + if err := rlp.DecodeBytes(blockEnc, &block); err != nil { + t.Fatal("decode error: ", err) + } + + check := func(f string, got, want interface{}) { + if !reflect.DeepEqual(got, want) { + t.Errorf("%s mismatch: got %v, want %v", f, got, want) + } + } + check("Difficulty", block.Difficulty(), big.NewInt(131072)) + check("GasLimit", block.GasLimit(), big.NewInt(3141592)) + check("GasUsed", block.GasUsed(), big.NewInt(21000)) + check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) + check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) + check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) + check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e")) + check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) + check("Time", block.Time(), big.NewInt(1426516743)) + check("Size", block.Size(), common.StorageSize(len(blockEnc))) + + tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil) + + tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) + fmt.Println(block.Transactions()[0].Hash()) + fmt.Println(tx1.data) + fmt.Println(tx1.Hash()) + check("len(Transactions)", len(block.Transactions()), 1) + check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) + + ourBlockEnc, err := rlp.EncodeToBytes(&block) + if err != nil { + t.Fatal("encode error: ", err) + } + if !bytes.Equal(ourBlockEnc, blockEnc) { + t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go new file mode 100644 index 0000000..32aa47a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9.go @@ -0,0 +1,130 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" +) + +type bytesBacked interface { + Bytes() []byte +} + +const bloomLength = 256 + +// Bloom represents a 256 bit bloom filter. +type Bloom [bloomLength]byte + +// BytesToBloom converts a byte slice to a bloom filter. +// It panics if b is not of suitable size. +func BytesToBloom(b []byte) Bloom { + var bloom Bloom + bloom.SetBytes(b) + return bloom +} + +// SetBytes sets the content of b to the given bytes. +// It panics if d is not of suitable size. +func (b *Bloom) SetBytes(d []byte) { + if len(b) < len(d) { + panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) + } + copy(b[bloomLength-len(d):], d) +} + +// Add adds d to the filter. Future calls of Test(d) will return true. +func (b *Bloom) Add(d *big.Int) { + bin := new(big.Int).SetBytes(b[:]) + bin.Or(bin, bloom9(d.Bytes())) + b.SetBytes(bin.Bytes()) +} + +// Big converts b to a big integer. +func (b Bloom) Big() *big.Int { + return common.Bytes2Big(b[:]) +} + +func (b Bloom) Bytes() []byte { + return b[:] +} + +func (b Bloom) Test(test *big.Int) bool { + return BloomLookup(b, test) +} + +func (b Bloom) TestBytes(test []byte) bool { + return b.Test(common.BytesToBig(test)) +} + +// MarshalJSON encodes b as a hex string with 0x prefix. +func (b Bloom) MarshalJSON() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalJSON() +} + +// UnmarshalJSON b as a hex string with 0x prefix. +func (b *Bloom) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalJSON("Bloom", input, b[:]) +} + +func CreateBloom(receipts Receipts) Bloom { + bin := new(big.Int) + for _, receipt := range receipts { + bin.Or(bin, LogsBloom(receipt.Logs)) + } + + return BytesToBloom(bin.Bytes()) +} + +func LogsBloom(logs []*Log) *big.Int { + bin := new(big.Int) + for _, log := range logs { + bin.Or(bin, bloom9(log.Address.Bytes())) + for _, b := range log.Topics { + bin.Or(bin, bloom9(b[:])) + } + } + + return bin +} + +func bloom9(b []byte) *big.Int { + b = crypto.Keccak256(b[:]) + + r := new(big.Int) + + for i := 0; i < 6; i += 2 { + t := big.NewInt(1) + b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047 + r.Or(r, t.Lsh(t, b)) + } + + return r +} + +var Bloom9 = bloom9 + +func BloomLookup(bin Bloom, topic bytesBacked) bool { + bloom := bin.Big() + cmp := bloom9(topic.Bytes()[:]) + + return bloom.And(bloom, cmp).Cmp(cmp) == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/bloom9_test.go b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9_test.go new file mode 100644 index 0000000..a28ac0e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/bloom9_test.go @@ -0,0 +1,81 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "math/big" + "testing" +) + +func TestBloom(t *testing.T) { + positive := []string{ + "testtest", + "test", + "hallo", + "other", + } + negative := []string{ + "tes", + "lo", + } + + var bloom Bloom + for _, data := range positive { + bloom.Add(new(big.Int).SetBytes([]byte(data))) + } + + for _, data := range positive { + if !bloom.TestBytes([]byte(data)) { + t.Error("expected", data, "to test true") + } + } + for _, data := range negative { + if bloom.TestBytes([]byte(data)) { + t.Error("did not expect", data, "to test true") + } + } +} + +/* +import ( + "testing" + + "github.com/ethereum/go-ethereum/core/state" +) + +func TestBloom9(t *testing.T) { + testCase := []byte("testtest") + bin := LogsBloom([]state.Log{ + {testCase, [][]byte{[]byte("hellohello")}, nil}, + }).Bytes() + res := BloomLookup(bin, testCase) + + if !res { + t.Errorf("Bloom lookup failed") + } +} + + +func TestAddress(t *testing.T) { + block := &Block{} + block.Coinbase = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") + fmt.Printf("%x\n", crypto.Keccak256(block.Coinbase)) + + bin := CreateBloom(block) + fmt.Printf("bin = %x\n", common.LeftPadBytes(bin, 64)) +} +*/ diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/derive_sha.go b/vendor/github.com/ethereum/go-ethereum/core/types/derive_sha.go new file mode 100644 index 0000000..00c42c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/derive_sha.go @@ -0,0 +1,41 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +type DerivableList interface { + Len() int + GetRlp(i int) []byte +} + +func DeriveSha(list DerivableList) common.Hash { + keybuf := new(bytes.Buffer) + trie := new(trie.Trie) + for i := 0; i < list.Len(); i++ { + keybuf.Reset() + rlp.Encode(keybuf, uint(i)) + trie.Update(keybuf.Bytes(), list.GetRlp(i)) + } + return trie.Hash() +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/log.go b/vendor/github.com/ethereum/go-ethereum/core/types/log.go new file mode 100644 index 0000000..7efb06b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/log.go @@ -0,0 +1,184 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "encoding/json" + "errors" + "fmt" + "io" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rlp" +) + +var errMissingLogFields = errors.New("missing required JSON log fields") + +// Log represents a contract log event. These events are generated by the LOG opcode and +// stored/indexed by the node. +type Log struct { + // Consensus fields. + Address common.Address // address of the contract that generated the event + Topics []common.Hash // list of topics provided by the contract. + Data []byte // supplied by the contract, usually ABI-encoded + + // Derived fields. These fields are filled in by the node + // but not secured by consensus. + BlockNumber uint64 // block in which the transaction was included + TxHash common.Hash // hash of the transaction + TxIndex uint // index of the transaction in the block + BlockHash common.Hash // hash of the block in which the transaction was included + Index uint // index of the log in the receipt + + // The Removed field is true if this log was reverted due to a chain reorganisation. + // You must pay attention to this field if you receive logs through a filter query. + Removed bool +} + +type rlpLog struct { + Address common.Address + Topics []common.Hash + Data []byte +} + +type rlpStorageLog struct { + Address common.Address + Topics []common.Hash + Data []byte + BlockNumber uint64 + TxHash common.Hash + TxIndex uint + BlockHash common.Hash + Index uint +} + +type jsonLog struct { + Address *common.Address `json:"address"` + Topics *[]common.Hash `json:"topics"` + Data *hexutil.Bytes `json:"data"` + BlockNumber *hexutil.Uint64 `json:"blockNumber"` + TxIndex *hexutil.Uint `json:"transactionIndex"` + TxHash *common.Hash `json:"transactionHash"` + BlockHash *common.Hash `json:"blockHash"` + Index *hexutil.Uint `json:"logIndex"` + Removed bool `json:"removed"` +} + +// EncodeRLP implements rlp.Encoder. +func (l *Log) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) +} + +// DecodeRLP implements rlp.Decoder. +func (l *Log) DecodeRLP(s *rlp.Stream) error { + var dec rlpLog + err := s.Decode(&dec) + if err == nil { + l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data + } + return err +} + +func (l *Log) String() string { + return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) +} + +// MarshalJSON implements json.Marshaler. +func (l *Log) MarshalJSON() ([]byte, error) { + jslog := &jsonLog{ + Address: &l.Address, + Topics: &l.Topics, + Data: (*hexutil.Bytes)(&l.Data), + TxIndex: (*hexutil.Uint)(&l.TxIndex), + TxHash: &l.TxHash, + Index: (*hexutil.Uint)(&l.Index), + Removed: l.Removed, + } + // Set block information for mined logs. + if (l.BlockHash != common.Hash{}) { + jslog.BlockHash = &l.BlockHash + jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber) + } + return json.Marshal(jslog) +} + +// UnmarshalJSON implements json.Umarshaler. +func (l *Log) UnmarshalJSON(input []byte) error { + var dec jsonLog + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil || dec.Topics == nil || dec.Data == nil || + dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil { + return errMissingLogFields + } + declog := Log{ + Address: *dec.Address, + Topics: *dec.Topics, + Data: *dec.Data, + TxHash: *dec.TxHash, + TxIndex: uint(*dec.TxIndex), + Index: uint(*dec.Index), + Removed: dec.Removed, + } + // Block information may be missing if the log is received through + // the pending log filter, so it's handled specially here. + if dec.BlockHash != nil && dec.BlockNumber != nil { + declog.BlockHash = *dec.BlockHash + declog.BlockNumber = uint64(*dec.BlockNumber) + } + *l = declog + return nil +} + +// LogForStorage is a wrapper around a Log that flattens and parses the entire content of +// a log including non-consensus fields. +type LogForStorage Log + +// EncodeRLP implements rlp.Encoder. +func (l *LogForStorage) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpStorageLog{ + Address: l.Address, + Topics: l.Topics, + Data: l.Data, + BlockNumber: l.BlockNumber, + TxHash: l.TxHash, + TxIndex: l.TxIndex, + BlockHash: l.BlockHash, + Index: l.Index, + }) +} + +// DecodeRLP implements rlp.Decoder. +func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { + var dec rlpStorageLog + err := s.Decode(&dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + BlockNumber: dec.BlockNumber, + TxHash: dec.TxHash, + TxIndex: dec.TxIndex, + BlockHash: dec.BlockHash, + Index: dec.Index, + } + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/log_test.go b/vendor/github.com/ethereum/go-ethereum/core/types/log_test.go new file mode 100644 index 0000000..bf742cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/log_test.go @@ -0,0 +1,131 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var unmarshalLogTests = map[string]struct { + input string + want *Log + wantError error +}{ + "ok": { + input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, + want: &Log{ + Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), + BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), + BlockNumber: 2019236, + Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000001a055690d9db80000"), + Index: 2, + TxIndex: 3, + TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), + Topics: []common.Hash{ + common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"), + }, + }, + }, + "empty data": { + input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, + want: &Log{ + Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), + BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), + BlockNumber: 2019236, + Data: []byte{}, + Index: 2, + TxIndex: 3, + TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), + Topics: []common.Hash{ + common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"), + }, + }, + }, + "missing block fields (pending logs)": { + input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","data":"0x","logIndex":"0x0","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, + want: &Log{ + Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), + BlockHash: common.Hash{}, + BlockNumber: 0, + Data: []byte{}, + Index: 0, + TxIndex: 3, + TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), + Topics: []common.Hash{ + common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + }, + }, + }, + "Removed: true": { + input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3","removed":true}`, + want: &Log{ + Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), + BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), + BlockNumber: 2019236, + Data: []byte{}, + Index: 2, + TxIndex: 3, + TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), + Topics: []common.Hash{ + common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + }, + Removed: true, + }, + }, + "missing data": { + input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, + wantError: errMissingLogFields, + }, +} + +func TestUnmarshalLog(t *testing.T) { + dumper := spew.ConfigState{DisableMethods: true, Indent: " "} + for name, test := range unmarshalLogTests { + var log *Log + err := json.Unmarshal([]byte(test.input), &log) + checkError(t, name, err, test.wantError) + if test.wantError == nil && err == nil { + if !reflect.DeepEqual(log, test.want) { + t.Errorf("test %q:\nGOT %sWANT %s", name, dumper.Sdump(log), dumper.Sdump(test.want)) + } + } + } +} + +func checkError(t *testing.T, testname string, got, want error) bool { + if got == nil { + if want != nil { + t.Errorf("test %q: got no error, want %q", testname, want) + return false + } + return true + } + if want == nil { + t.Errorf("test %q: unexpected error %q", testname, got) + } else if got.Error() != want.Error() { + t.Errorf("test %q: got error %q, want %q", testname, got, want) + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go new file mode 100644 index 0000000..0a6a35e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/receipt.go @@ -0,0 +1,191 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + errMissingReceiptPostState = errors.New("missing post state root in JSON receipt") + errMissingReceiptFields = errors.New("missing required JSON receipt fields") +) + +// Receipt represents the results of a transaction. +type Receipt struct { + // Consensus fields + PostState []byte + CumulativeGasUsed *big.Int + Bloom Bloom + Logs []*Log + + // Implementation fields (don't reorder!) + TxHash common.Hash + ContractAddress common.Address + GasUsed *big.Int +} + +type jsonReceipt struct { + PostState *common.Hash `json:"root"` + CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"` + Bloom *Bloom `json:"logsBloom"` + Logs []*Log `json:"logs"` + TxHash *common.Hash `json:"transactionHash"` + ContractAddress *common.Address `json:"contractAddress"` + GasUsed *hexutil.Big `json:"gasUsed"` +} + +// NewReceipt creates a barebone transaction receipt, copying the init fields. +func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt { + return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)} +} + +// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt +// into an RLP stream. +func (r *Receipt) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs}) +} + +// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt +// from an RLP stream. +func (r *Receipt) DecodeRLP(s *rlp.Stream) error { + var receipt struct { + PostState []byte + CumulativeGasUsed *big.Int + Bloom Bloom + Logs []*Log + } + if err := s.Decode(&receipt); err != nil { + return err + } + r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom, receipt.Logs + return nil +} + +// MarshalJSON encodes receipts into the web3 RPC response block format. +func (r *Receipt) MarshalJSON() ([]byte, error) { + root := common.BytesToHash(r.PostState) + + return json.Marshal(&jsonReceipt{ + PostState: &root, + CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed), + Bloom: &r.Bloom, + Logs: r.Logs, + TxHash: &r.TxHash, + ContractAddress: &r.ContractAddress, + GasUsed: (*hexutil.Big)(r.GasUsed), + }) +} + +// UnmarshalJSON decodes the web3 RPC receipt format. +func (r *Receipt) UnmarshalJSON(input []byte) error { + var dec jsonReceipt + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + // Ensure that all fields are set. PostState is checked separately because it is a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide it. Note that ContractAddress is not checked because it can be null. + if dec.PostState == nil { + return errMissingReceiptPostState + } + if dec.CumulativeGasUsed == nil || dec.Bloom == nil || + dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil { + return errMissingReceiptFields + } + *r = Receipt{ + PostState: (*dec.PostState)[:], + CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed), + Bloom: *dec.Bloom, + Logs: dec.Logs, + TxHash: *dec.TxHash, + GasUsed: (*big.Int)(dec.GasUsed), + } + if dec.ContractAddress != nil { + r.ContractAddress = *dec.ContractAddress + } + return nil +} + +// String implements the Stringer interface. +func (r *Receipt) String() string { + return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs) +} + +// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the +// entire content of a receipt, as opposed to only the consensus fields originally. +type ReceiptForStorage Receipt + +// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt +// into an RLP stream. +func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { + logs := make([]*LogForStorage, len(r.Logs)) + for i, log := range r.Logs { + logs[i] = (*LogForStorage)(log) + } + return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) +} + +// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation +// fields of a receipt from an RLP stream. +func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { + var receipt struct { + PostState []byte + CumulativeGasUsed *big.Int + Bloom Bloom + TxHash common.Hash + ContractAddress common.Address + Logs []*LogForStorage + GasUsed *big.Int + } + if err := s.Decode(&receipt); err != nil { + return err + } + // Assign the consensus fields + r.PostState, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom + r.Logs = make([]*Log, len(receipt.Logs)) + for i, log := range receipt.Logs { + r.Logs[i] = (*Log)(log) + } + // Assign the implementation fields + r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed + + return nil +} + +// Receipts is a wrapper around a Receipt array to implement DerivableList. +type Receipts []*Receipt + +// Len returns the number of receipts in this list. +func (r Receipts) Len() int { return len(r) } + +// GetRlp returns the RLP encoding of one receipt from the list. +func (r Receipts) GetRlp(i int) []byte { + bytes, err := rlp.EncodeToBytes(r[i]) + if err != nil { + panic(err) + } + return bytes +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go new file mode 100644 index 0000000..9382acb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction.go @@ -0,0 +1,516 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "container/heap" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +var ErrInvalidSig = errors.New("invalid transaction v, r, s values") + +var ( + errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields") + errMissingTxFields = errors.New("missing required JSON transaction fields") + errNoSigner = errors.New("missing signing methods") +) + +// deriveSigner makes a *best* guess about which signer to use. +func deriveSigner(V *big.Int) Signer { + if V.BitLen() > 0 && isProtectedV(V) { + return EIP155Signer{chainId: deriveChainId(V)} + } else { + return HomesteadSigner{} + } +} + +type Transaction struct { + data txdata + // caches + hash atomic.Value + size atomic.Value + from atomic.Value +} + +type txdata struct { + AccountNonce uint64 + Price, GasLimit *big.Int + Recipient *common.Address `rlp:"nil"` // nil means contract creation + Amount *big.Int + Payload []byte + V *big.Int // signature + R, S *big.Int // signature +} + +type jsonTransaction struct { + Hash *common.Hash `json:"hash"` + AccountNonce *hexutil.Uint64 `json:"nonce"` + Price *hexutil.Big `json:"gasPrice"` + GasLimit *hexutil.Big `json:"gas"` + Recipient *common.Address `json:"to"` + Amount *hexutil.Big `json:"value"` + Payload *hexutil.Bytes `json:"input"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` +} + +func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) +} + +func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +} + +func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { + if len(data) > 0 { + data = common.CopyBytes(data) + } + d := txdata{ + AccountNonce: nonce, + Recipient: to, + Payload: data, + Amount: new(big.Int), + GasLimit: new(big.Int), + Price: new(big.Int), + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + if amount != nil { + d.Amount.Set(amount) + } + if gasLimit != nil { + d.GasLimit.Set(gasLimit) + } + if gasPrice != nil { + d.Price.Set(gasPrice) + } + + return &Transaction{data: d} +} + +func pickSigner(rules params.Rules) Signer { + var signer Signer + switch { + case rules.IsEIP155: + signer = NewEIP155Signer(rules.ChainId) + case rules.IsHomestead: + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// ChainId returns which chain id this transaction was signed for (if at all) +func (tx *Transaction) ChainId() *big.Int { + return deriveChainId(tx.data.V) +} + +// Protected returns whether the transaction is protected from replay protection. +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.data.V) +} + +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 are considered unprotected + return true +} + +// DecodeRLP implements rlp.Encoder +func (tx *Transaction) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &tx.data) +} + +// DecodeRLP implements rlp.Decoder +func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + err := s.Decode(&tx.data) + if err == nil { + tx.size.Store(common.StorageSize(rlp.ListSize(size))) + } + + return err +} + +// MarshalJSON encodes transactions into the web3 RPC response block format. +func (tx *Transaction) MarshalJSON() ([]byte, error) { + hash := tx.Hash() + + return json.Marshal(&jsonTransaction{ + Hash: &hash, + AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce), + Price: (*hexutil.Big)(tx.data.Price), + GasLimit: (*hexutil.Big)(tx.data.GasLimit), + Recipient: tx.data.Recipient, + Amount: (*hexutil.Big)(tx.data.Amount), + Payload: (*hexutil.Bytes)(&tx.data.Payload), + V: (*hexutil.Big)(tx.data.V), + R: (*hexutil.Big)(tx.data.R), + S: (*hexutil.Big)(tx.data.S), + }) +} + +// UnmarshalJSON decodes the web3 RPC transaction format. +func (tx *Transaction) UnmarshalJSON(input []byte) error { + var dec jsonTransaction + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + // Ensure that all fields are set. V, R, S are checked separately because they're a + // recent addition to the RPC spec (as of August 2016) and older implementations might + // not provide them. Note that Recipient is not checked because it can be missing for + // contract creations. + if dec.V == nil || dec.R == nil || dec.S == nil { + return errMissingTxSignatureFields + } + + var V byte + if isProtectedV((*big.Int)(dec.V)) { + chainId := deriveChainId((*big.Int)(dec.V)).Uint64() + V = byte(dec.V.ToInt().Uint64() - 35 - 2*chainId) + } else { + V = byte(((*big.Int)(dec.V)).Uint64() - 27) + } + if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) { + return ErrInvalidSig + } + + if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil { + return errMissingTxFields + } + // Assign the fields. This is not atomic but reusing transactions + // for decoding isn't thread safe anyway. + *tx = Transaction{} + tx.data = txdata{ + AccountNonce: uint64(*dec.AccountNonce), + Recipient: dec.Recipient, + Amount: (*big.Int)(dec.Amount), + GasLimit: (*big.Int)(dec.GasLimit), + Price: (*big.Int)(dec.Price), + Payload: *dec.Payload, + V: (*big.Int)(dec.V), + R: (*big.Int)(dec.R), + S: (*big.Int)(dec.S), + } + return nil +} + +func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } +func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) } +func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } +func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } +func (tx *Transaction) CheckNonce() bool { return true } + +// To returns the recipient address of the transaction. +// It returns nil if the transaction is a contract creation. +func (tx *Transaction) To() *common.Address { + if tx.data.Recipient == nil { + return nil + } else { + to := *tx.data.Recipient + return &to + } +} + +// Hash hashes the RLP encoding of tx. +// It uniquely identifies the transaction. +func (tx *Transaction) Hash() common.Hash { + if hash := tx.hash.Load(); hash != nil { + return hash.(common.Hash) + } + v := rlpHash(tx) + tx.hash.Store(v) + return v +} + +// SigHash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (tx *Transaction) SigHash(signer Signer) common.Hash { + return signer.Hash(tx) +} + +func (tx *Transaction) Size() common.StorageSize { + if size := tx.size.Load(); size != nil { + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, &tx.data) + tx.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +// AsMessage returns the transaction as a core.Message. +// +// AsMessage requires a signer to derive the sender. +// +// XXX Rename message to something less arbitrary? +func (tx *Transaction) AsMessage(s Signer) (Message, error) { + msg := Message{ + nonce: tx.data.AccountNonce, + price: new(big.Int).Set(tx.data.Price), + gasLimit: new(big.Int).Set(tx.data.GasLimit), + to: tx.data.Recipient, + amount: tx.data.Amount, + data: tx.data.Payload, + checkNonce: true, + } + + var err error + msg.from, err = Sender(s, tx) + return msg, err +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { + return signer.WithSignature(tx, sig) +} + +// Cost returns amount + gasprice * gaslimit. +func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit) + total.Add(total, tx.data.Amount) + return total +} + +func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { + return tx.data.V, tx.data.R, tx.data.S +} + +func (tx *Transaction) String() string { + var from, to string + if tx.data.V != nil { + // make a best guess about the signer and use that to derive + // the sender. + signer := deriveSigner(tx.data.V) + if f, err := Sender(signer, tx); err != nil { // derive but don't cache + from = "[invalid sender: invalid sig]" + } else { + from = fmt.Sprintf("%x", f[:]) + } + } else { + from = "[invalid sender: nil V field]" + } + + if tx.data.Recipient == nil { + to = "[contract creation]" + } else { + to = fmt.Sprintf("%x", tx.data.Recipient[:]) + } + enc, _ := rlp.EncodeToBytes(&tx.data) + return fmt.Sprintf(` + TX(%x) + Contract: %v + From: %s + To: %s + Nonce: %v + GasPrice: %#x + GasLimit %#x + Value: %#x + Data: 0x%x + V: %#x + R: %#x + S: %#x + Hex: %x +`, + tx.Hash(), + len(tx.data.Recipient) == 0, + from, + to, + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Amount, + tx.data.Payload, + tx.data.V, + tx.data.R, + tx.data.S, + enc, + ) +} + +// Transaction slice type for basic sorting. +type Transactions []*Transaction + +// Len returns the length of s +func (s Transactions) Len() int { return len(s) } + +// Swap swaps the i'th and the j'th element in s +func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// GetRlp implements Rlpable and returns the i'th element of s in rlp +func (s Transactions) GetRlp(i int) []byte { + enc, _ := rlp.EncodeToBytes(s[i]) + return enc +} + +// Returns a new set t which is the difference between a to b +func TxDifference(a, b Transactions) (keep Transactions) { + keep = make(Transactions, 0, len(a)) + + remove := make(map[common.Hash]struct{}) + for _, tx := range b { + remove[tx.Hash()] = struct{}{} + } + + for _, tx := range a { + if _, ok := remove[tx.Hash()]; !ok { + keep = append(keep, tx) + } + } + + return keep +} + +// TxByNonce implements the sort interface to allow sorting a list of transactions +// by their nonces. This is usually only useful for sorting transactions from a +// single account, otherwise a nonce comparison doesn't make much sense. +type TxByNonce Transactions + +func (s TxByNonce) Len() int { return len(s) } +func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce } +func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// TxByPrice implements both the sort and the heap interface, making it useful +// for all at once sorting as well as individually adding and removing elements. +type TxByPrice Transactions + +func (s TxByPrice) Len() int { return len(s) } +func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 } +func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s *TxByPrice) Push(x interface{}) { + *s = append(*s, x.(*Transaction)) +} + +func (s *TxByPrice) Pop() interface{} { + old := *s + n := len(old) + x := old[n-1] + *s = old[0 : n-1] + return x +} + +// TransactionsByPriceAndNonce represents a set of transactions that can return +// transactions in a profit-maximising sorted order, while supporting removing +// entire batches of transactions for non-executable accounts. +type TransactionsByPriceAndNonce struct { + txs map[common.Address]Transactions // Per account nonce-sorted list of transactions + heads TxByPrice // Next transaction for each unique account (price heap) +} + +// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve +// price sorted transactions in a nonce-honouring way. +// +// Note, the input map is reowned so the caller should not interact any more with +// if after providng it to the constructor. +func NewTransactionsByPriceAndNonce(txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { + // Initialize a price based heap with the head transactions + heads := make(TxByPrice, 0, len(txs)) + for acc, accTxs := range txs { + heads = append(heads, accTxs[0]) + txs[acc] = accTxs[1:] + } + heap.Init(&heads) + + // Assemble and return the transaction set + return &TransactionsByPriceAndNonce{ + txs: txs, + heads: heads, + } +} + +// Peek returns the next transaction by price. +func (t *TransactionsByPriceAndNonce) Peek() *Transaction { + if len(t.heads) == 0 { + return nil + } + return t.heads[0] +} + +// Shift replaces the current best head with the next one from the same account. +func (t *TransactionsByPriceAndNonce) Shift() { + signer := deriveSigner(t.heads[0].data.V) + // derive signer but don't cache. + acc, _ := Sender(signer, t.heads[0]) // we only sort valid txs so this cannot fail + if txs, ok := t.txs[acc]; ok && len(txs) > 0 { + t.heads[0], t.txs[acc] = txs[0], txs[1:] + heap.Fix(&t.heads, 0) + } else { + heap.Pop(&t.heads) + } +} + +// Pop removes the best transaction, *not* replacing it with the next one from +// the same account. This should be used when a transaction cannot be executed +// and hence all subsequent ones should be discarded from the same account. +func (t *TransactionsByPriceAndNonce) Pop() { + heap.Pop(&t.heads) +} + +// Message is a fully derived transaction and implements core.Message +// +// NOTE: In a future PR this will be removed. +type Message struct { + to *common.Address + from common.Address + nonce uint64 + amount, price, gasLimit *big.Int + data []byte + checkNonce bool +} + +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte, checkNonce bool) Message { + return Message{ + from: from, + to: to, + nonce: nonce, + amount: amount, + price: price, + gasLimit: gasLimit, + data: data, + checkNonce: checkNonce, + } +} + +func (m Message) From() common.Address { return m.from } +func (m Message) To() *common.Address { return m.to } +func (m Message) GasPrice() *big.Int { return m.price } +func (m Message) Value() *big.Int { return m.amount } +func (m Message) Gas() *big.Int { return m.gasLimit } +func (m Message) Nonce() uint64 { return m.nonce } +func (m Message) Data() []byte { return m.data } +func (m Message) CheckNonce() bool { return m.checkNonce } diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go new file mode 100644 index 0000000..7d7b63e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go @@ -0,0 +1,312 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +var ErrInvalidChainId = errors.New("invalid chaid id for signer") + +// sigCache is used to cache the derived sender and contains +// the signer used to derive it. +type sigCache struct { + signer Signer + from common.Address +} + +// MakeSigner returns a Signer based on the given chain config and block number. +func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { + var signer Signer + switch { + case config.IsEIP155(blockNumber): + signer = NewEIP155Signer(config.ChainId) + case config.IsHomestead(blockNumber): + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// SignTx signs the transaction using the given signer and private key +func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// Sender derives the sender from the tx using the signer derivation +// functions. + +// Sender returns the address derived from the signature (V, R, S) using secp256k1 +// elliptic curve and an error if it failed deriving or upon an incorrect +// signature. +// +// Sender may cache the address, allowing it to be used regardless of +// signing method. The cache is invalidated if the cached signer does +// not match the signer used in the current call. +func Sender(signer Signer, tx *Transaction) (common.Address, error) { + if sc := tx.from.Load(); sc != nil { + sigCache := sc.(sigCache) + // If the signer used to derive from in a previous + // call is not the same as used current, invalidate + // the cache. + if sigCache.signer.Equal(signer) { + return sigCache.from, nil + } + } + + pubkey, err := signer.PublicKey(tx) + if err != nil { + return common.Address{}, err + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) + tx.from.Store(sigCache{signer: signer, from: addr}) + return addr, nil +} + +type Signer interface { + // Hash returns the rlp encoded hash for signatures + Hash(tx *Transaction) common.Hash + // PubilcKey returns the public key derived from the signature + PublicKey(tx *Transaction) ([]byte, error) + // WithSignature returns a copy of the transaction with the given signature. + // The signature must be encoded in [R || S || V] format where V is 0 or 1. + WithSignature(tx *Transaction, sig []byte) (*Transaction, error) + // Checks for equality on the signers + Equal(Signer) bool +} + +// EIP155Transaction implements TransactionInterface using the +// EIP155 rules +type EIP155Signer struct { + HomesteadSigner + + chainId, chainIdMul *big.Int +} + +func NewEIP155Signer(chainId *big.Int) EIP155Signer { + return EIP155Signer{ + chainId: chainId, + chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), + } +} + +func (s EIP155Signer) Equal(s2 Signer) bool { + eip155, ok := s2.(EIP155Signer) + return ok && eip155.chainId.Cmp(s.chainId) == 0 +} + +func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { + // if the transaction is not protected fall back to homestead signer + if !tx.Protected() { + return (HomesteadSigner{}).PublicKey(tx) + } + + if tx.ChainId().Cmp(s.chainId) != 0 { + return nil, ErrInvalidChainId + } + + V := byte(new(big.Int).Sub(tx.data.V, s.chainIdMul).Uint64() - 35) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + // encode the signature in uncompressed format + R, S := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(R):32], R) + copy(sig[64-len(S):64], S) + sig[64] = V + + // recover the public key from the signature + hash := s.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// WithSignature returns a new transaction with the given signature. This signature +// needs to be in the [R || S || V] format where V is 0 or 1. +func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) + } + + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + if s.chainId.BitLen() > 0 { + cpy.data.V = big.NewInt(int64(sig[64] + 35)) + cpy.data.V.Add(cpy.data.V, s.chainIdMul) + } + return cpy, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s EIP155Signer) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + s.chainId, uint(0), uint(0), + }) +} + +// HomesteadTransaction implements TransactionInterface using the +// homestead rules. +type HomesteadSigner struct{ FrontierSigner } + +func (s HomesteadSigner) Equal(s2 Signer) bool { + _, ok := s2.(HomesteadSigner) + return ok +} + +// WithSignature returns a new transaction with the given signature. This signature +// needs to be in the [R || S || V] format where V is 0 or 1. +func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27}) + return cpy, nil +} + +func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + V := byte(tx.data.V.Uint64() - 27) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V + + // recover the public key from the snature + hash := hs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +type FrontierSigner struct{} + +func (s FrontierSigner) Equal(s2 Signer) bool { + _, ok := s2.(FrontierSigner) + return ok +} + +// WithSignature returns a new transaction with the given signature. This signature +// needs to be in the [R || S || V] format where V is 0 or 1. +func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27}) + return cpy, nil +} + +// Hash returns the hash to be sned by the sender. +// It does not uniquely identify the transaction. +func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + }) +} + +func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + + V := byte(tx.data.V.Uint64() - 27) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V + + // recover the public key from the snature + hash := fs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing_test.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing_test.go new file mode 100644 index 0000000..3216fcf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing_test.go @@ -0,0 +1,138 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +func TestEIP155Signing(t *testing.T) { + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + signer := NewEIP155Signer(big.NewInt(18)) + tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key) + if err != nil { + t.Fatal(err) + } + + from, err := Sender(signer, tx) + if err != nil { + t.Fatal(err) + } + if from != addr { + t.Errorf("exected from and address to be equal. Got %x want %x", from, addr) + } +} + +func TestEIP155ChainId(t *testing.T) { + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + signer := NewEIP155Signer(big.NewInt(18)) + tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key) + if err != nil { + t.Fatal(err) + } + if !tx.Protected() { + t.Fatal("expected tx to be protected") + } + + if tx.ChainId().Cmp(signer.chainId) != 0 { + t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId()) + } + + tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil) + tx, err = SignTx(tx, HomesteadSigner{}, key) + if err != nil { + t.Fatal(err) + } + + if tx.Protected() { + t.Error("didn't expect tx to be protected") + } + + if tx.ChainId().BitLen() > 0 { + t.Error("expected chain id to be 0 got", tx.ChainId()) + } +} + +func TestEIP155SigningVitalik(t *testing.T) { + // Test vectors come from http://vitalik.ca/files/eip155_testvec.txt + for i, test := range []struct { + txRlp, addr string + }{ + {"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"}, + {"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"}, + {"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"}, + {"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"}, + {"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"}, + {"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"}, + {"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"}, + {"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"}, + {"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"}, + {"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"}, + } { + signer := NewEIP155Signer(big.NewInt(1)) + + var tx *Transaction + err := rlp.DecodeBytes(common.Hex2Bytes(test.txRlp), &tx) + if err != nil { + t.Errorf("%d: %v", i, err) + continue + } + + from, err := Sender(signer, tx) + if err != nil { + t.Errorf("%d: %v", i, err) + continue + } + + addr := common.HexToAddress(test.addr) + if from != addr { + t.Errorf("%d: expected %x got %x", i, addr, from) + } + + } +} + +func TestChainId(t *testing.T) { + key, _ := defaultTestKey() + + tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil) + + var err error + tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key) + if err != nil { + t.Fatal(err) + } + + _, err = Sender(NewEIP155Signer(big.NewInt(2)), tx) + if err != ErrInvalidChainId { + t.Error("expected error:", ErrInvalidChainId) + } + + _, err = Sender(NewEIP155Signer(big.NewInt(1)), tx) + if err != nil { + t.Error("expected no error") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_test.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_test.go new file mode 100644 index 0000000..6e4519c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_test.go @@ -0,0 +1,234 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + "crypto/ecdsa" + "encoding/json" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +// The values in those tests are from the Transaction Tests +// at github.com/ethereum/tests. +var ( + emptyTx = NewTransaction( + 0, + common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), + big.NewInt(0), big.NewInt(0), big.NewInt(0), + nil, + ) + + rightvrsTx, _ = NewTransaction( + 3, + common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"), + big.NewInt(10), + big.NewInt(2000), + big.NewInt(1), + common.FromHex("5544"), + ).WithSignature( + HomesteadSigner{}, + common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"), + ) +) + +func TestTransactionSigHash(t *testing.T) { + if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { + t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash()) + } + if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") { + t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash()) + } +} + +func TestTransactionEncode(t *testing.T) { + txb, err := rlp.EncodeToBytes(rightvrsTx) + if err != nil { + t.Fatalf("encode error: %v", err) + } + should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") + if !bytes.Equal(txb, should) { + t.Errorf("encoded RLP mismatch, got %x", txb) + } +} + +func decodeTx(data []byte) (*Transaction, error) { + var tx Transaction + t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx) + + return t, err +} + +func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { + key := crypto.ToECDSA(common.Hex2Bytes("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")) + addr := crypto.PubkeyToAddress(key.PublicKey) + return key, addr +} + +func TestRecipientEmpty(t *testing.T) { + _, addr := defaultTestKey() + tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) + if err != nil { + t.Error(err) + t.FailNow() + } + + from, err := Sender(HomesteadSigner{}, tx) + if err != nil { + t.Error(err) + t.FailNow() + } + if addr != from { + t.Error("derived address doesn't match") + } +} + +func TestRecipientNormal(t *testing.T) { + _, addr := defaultTestKey() + + tx, err := decodeTx(common.Hex2Bytes("f85d80808094000000000000000000000000000000000000000080011ca0527c0d8f5c63f7b9f41324a7c8a563ee1190bcbf0dac8ab446291bdbf32f5c79a0552c4ef0a09a04395074dab9ed34d3fbfb843c2f2546cc30fe89ec143ca94ca6")) + if err != nil { + t.Error(err) + t.FailNow() + } + + from, err := Sender(HomesteadSigner{}, tx) + if err != nil { + t.Error(err) + t.FailNow() + } + + if addr != from { + t.Error("derived address doesn't match") + } +} + +// Tests that transactions can be correctly sorted according to their price in +// decreasing order, but at the same time with increasing nonces when issued by +// the same account. +func TestTransactionPriceNonceSort(t *testing.T) { + // Generate a batch of accounts to start with + keys := make([]*ecdsa.PrivateKey, 25) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + + signer := HomesteadSigner{} + // Generate a batch of transactions with overlapping values, but shifted nonces + groups := map[common.Address]Transactions{} + for start, key := range keys { + addr := crypto.PubkeyToAddress(key.PublicKey) + for i := 0; i < 25; i++ { + tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil), signer, key) + groups[addr] = append(groups[addr], tx) + } + } + // Sort the transactions and cross check the nonce ordering + txset := NewTransactionsByPriceAndNonce(groups) + + txs := Transactions{} + for { + if tx := txset.Peek(); tx != nil { + txs = append(txs, tx) + txset.Shift() + } + break + } + for i, txi := range txs { + fromi, _ := Sender(signer, txi) + + // Make sure the nonce order is valid + for j, txj := range txs[i+1:] { + fromj, _ := Sender(signer, txj) + + if fromi == fromj && txi.Nonce() > txj.Nonce() { + t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce()) + } + } + // Find the previous and next nonce of this account + prev, next := i-1, i+1 + for j := i - 1; j >= 0; j-- { + if fromj, _ := Sender(signer, txs[j]); fromi == fromj { + prev = j + break + } + } + for j := i + 1; j < len(txs); j++ { + if fromj, _ := Sender(signer, txs[j]); fromi == fromj { + next = j + break + } + } + // Make sure that in between the neighbor nonces, the transaction is correctly positioned price wise + for j := prev + 1; j < next; j++ { + fromj, _ := Sender(signer, txs[j]) + if j < i && txs[j].GasPrice().Cmp(txi.GasPrice()) < 0 { + t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice()) + } + if j > i && txs[j].GasPrice().Cmp(txi.GasPrice()) > 0 { + t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) > tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice()) + } + } + } +} + +// TestTransactionJSON tests serializing/de-serializing to/from JSON. +func TestTransactionJSON(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("could not generate key: %v", err) + } + signer := NewEIP155Signer(common.Big1) + + for i := uint64(0); i < 25; i++ { + var tx *Transaction + switch i % 2 { + case 0: + tx = NewTransaction(i, common.Address{1}, common.Big0, common.Big1, common.Big2, []byte("abcdef")) + case 1: + tx = NewContractCreation(i, common.Big0, common.Big1, common.Big2, []byte("abcdef")) + } + + tx, err := SignTx(tx, signer, key) + if err != nil { + t.Fatalf("could not sign transaction: %v", err) + } + + data, err := json.Marshal(tx) + if err != nil { + t.Errorf("json.Marshal failed: %v", err) + } + + var parsedTx *Transaction + if err := json.Unmarshal(data, &parsedTx); err != nil { + t.Errorf("json.Unmarshal failed: %v", err) + } + + // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S + if tx.Hash() != parsedTx.Hash() { + t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx) + } + if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 { + t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId()) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/analysis.go b/vendor/github.com/ethereum/go-ethereum/core/vm/analysis.go new file mode 100644 index 0000000..a0f6158 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/analysis.go @@ -0,0 +1,63 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var bigMaxUint64 = new(big.Int).SetUint64(^uint64(0)) + +// destinations stores one map per contract (keyed by hash of code). +// The maps contain an entry for each location of a JUMPDEST +// instruction. +type destinations map[common.Hash]map[uint64]struct{} + +// has checks whether code has a JUMPDEST at dest. +func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool { + // PC cannot go beyond len(code) and certainly can't be bigger than 64bits. + // Don't bother checking for JUMPDEST in that case. + if dest.Cmp(bigMaxUint64) > 0 { + return false + } + m, analysed := d[codehash] + if !analysed { + m = jumpdests(code) + d[codehash] = m + } + _, ok := m[dest.Uint64()] + return ok +} + +// jumpdests creates a map that contains an entry for each +// PC location that is a JUMPDEST instruction. +func jumpdests(code []byte) map[uint64]struct{} { + m := make(map[uint64]struct{}) + for pc := uint64(0); pc < uint64(len(code)); pc++ { + var op OpCode = OpCode(code[pc]) + switch op { + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + a := uint64(op) - uint64(PUSH1) + 1 + pc += a + case JUMPDEST: + m[pc] = struct{}{} + } + } + return m +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/asm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/asm.go new file mode 100644 index 0000000..d7dbde5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/asm.go @@ -0,0 +1,61 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Disassemble disassembles the byte code and returns the string +// representation (human readable opcodes). +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := OpCode(val) + + asm = append(asm, fmt.Sprintf("%v", op)) + + switch op { + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + pc.Add(pc, common.Big1) + a := int64(op) - int64(PUSH1) + 1 + if int(pc.Int64()+a) > len(script) { + return nil + } + + data := script[pc.Int64() : pc.Int64()+a] + if len(data) == 0 { + data = []byte{0} + } + asm = append(asm, fmt.Sprintf("0x%x", data)) + + pc.Add(pc, big.NewInt(a-1)) + } + + pc.Add(pc, common.Big1) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/common.go b/vendor/github.com/ethereum/go-ethereum/core/vm/common.go new file mode 100644 index 0000000..b7b9a6a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/common.go @@ -0,0 +1,63 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + U256 = common.U256 // Shortcut to common.U256 + S256 = common.S256 // Shortcut to common.S256 +) + +// calculates the memory size required for a step +func calcMemSize(off, l *big.Int) *big.Int { + if l.Cmp(common.Big0) == 0 { + return common.Big0 + } + + return new(big.Int).Add(off, l) +} + +// getData returns a slice from the data based on the start and size and pads +// up to size with zero's. This function is overflow safe. +func getData(data []byte, start, size *big.Int) []byte { + dlen := big.NewInt(int64(len(data))) + + s := common.BigMin(start, dlen) + e := common.BigMin(new(big.Int).Add(s, size), dlen) + return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) +} + +// bigUint64 returns the integer casted to a uint64 and returns whether it +// overflowed in the process. +func bigUint64(v *big.Int) (uint64, bool) { + return v.Uint64(), v.BitLen() > 64 +} + +// toWordSize returns the ceiled word size required for memory expansion. +func toWordSize(size uint64) uint64 { + if size > math.MaxUint64-31 { + return math.MaxUint64/32 + 1 + } + + return (size + 31) / 32 +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go new file mode 100644 index 0000000..66748e8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/contract.go @@ -0,0 +1,153 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// ContractRef is a reference to the contract's backing object +type ContractRef interface { + Address() common.Address +} + +// AccountRef implements ContractRef. +// +// Account references are used during EVM initialisation and +// it's primary use is to fetch addresses. Removing this object +// proves difficult because of the cached jump destinations which +// are fetched from the parent contract (i.e. the caller), which +// is a ContractRef. +type AccountRef common.Address + +// Address casts AccountRef to a Address +func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } + +// Contract represents an ethereum contract in the state database. It contains +// the the contract code, calling arguments. Contract implements ContractRef +type Contract struct { + // CallerAddress is the result of the caller which initialised this + // contract. However when the "call method" is delegated this value + // needs to be initialised to that of the caller's caller. + CallerAddress common.Address + caller ContractRef + self ContractRef + + jumpdests destinations // result of JUMPDEST analysis. + + Code []byte + CodeHash common.Hash + CodeAddr *common.Address + Input []byte + + Gas uint64 + value *big.Int + + Args []byte + + DelegateCall bool +} + +// NewContract returns a new contract environment for the execution of EVM. +func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { + c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} + + if parent, ok := caller.(*Contract); ok { + // Reuse JUMPDEST analysis from parent context if available. + c.jumpdests = parent.jumpdests + } else { + c.jumpdests = make(destinations) + } + + // Gas should be a pointer so it can safely be reduced through the run + // This pointer will be off the state transition + c.Gas = gas + // ensures a value is set + c.value = value + + return c +} + +// AsDelegate sets the contract to be a delegate call and returns the current +// contract (for chaining calls) +func (c *Contract) AsDelegate() *Contract { + c.DelegateCall = true + // NOTE: caller must, at all times be a contract. It should never happen + // that caller is something other than a Contract. + parent := c.caller.(*Contract) + c.CallerAddress = parent.CallerAddress + c.value = parent.value + + return c +} + +// GetOp returns the n'th element in the contract's byte array +func (c *Contract) GetOp(n uint64) OpCode { + return OpCode(c.GetByte(n)) +} + +// GetByte returns the n'th byte in the contract's byte array +func (c *Contract) GetByte(n uint64) byte { + if n < uint64(len(c.Code)) { + return c.Code[n] + } + + return 0 +} + +// Caller returns the caller of the contract. +// +// Caller will recursively call caller when the contract is a delegate +// call, including that of caller's caller. +func (c *Contract) Caller() common.Address { + return c.CallerAddress +} + +// UseGas attempts the use gas and subtracts it and returns true on success +func (c *Contract) UseGas(gas uint64) (ok bool) { + if c.Gas < gas { + return false + } + c.Gas -= gas + return true +} + +// Address returns the contracts address +func (c *Contract) Address() common.Address { + return c.self.Address() +} + +// Value returns the contracts value (sent to it from it's caller) +func (c *Contract) Value() *big.Int { + return c.value +} + +// SetCode sets the code to the contract +func (self *Contract) SetCode(hash common.Hash, code []byte) { + self.Code = code + self.CodeHash = hash +} + +// SetCallCode sets the code of the contract and address of the backing data +// object +func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { + self.Code = code + self.CodeHash = hash + self.CodeAddr = addr +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go b/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go new file mode 100644 index 0000000..1447c2c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go @@ -0,0 +1,136 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "crypto/sha256" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "golang.org/x/crypto/ripemd160" +) + +// Precompiled contract is the basic interface for native Go contracts. The implementation +// requires a deterministic gas count based on the input size of the Run method of the +// contract. +type PrecompiledContract interface { + RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte) []byte // Run runs the precompiled contract +} + +// Precompiled contains the default set of ethereum contracts +var PrecompiledContracts = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, +} + +// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go +func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { + gas := p.RequiredGas(len(input)) + if contract.UseGas(gas) { + ret = p.Run(input) + + return ret, nil + } else { + return nil, ErrOutOfGas + } +} + +// ECRECOVER implemented as a native contract +type ecrecover struct{} + +func (c *ecrecover) RequiredGas(inputSize int) uint64 { + return params.EcrecoverGas +} + +func (c *ecrecover) Run(in []byte) []byte { + const ecRecoverInputLength = 128 + + in = common.RightPadBytes(in, ecRecoverInputLength) + // "in" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + + r := common.BytesToBig(in[64:96]) + s := common.BytesToBig(in[96:128]) + v := in[63] - 27 + + // tighter sig s values in homestead only apply to tx sigs + if common.Bytes2Big(in[32:63]).BitLen() > 0 || !crypto.ValidateSignatureValues(v, r, s, false) { + glog.V(logger.Detail).Infof("ECRECOVER error: v, r or s value invalid") + return nil + } + // v needs to be at the end for libsecp256k1 + pubKey, err := crypto.Ecrecover(in[:32], append(in[64:128], v)) + // make sure the public key is a valid one + if err != nil { + glog.V(logger.Detail).Infoln("ECRECOVER error: ", err) + return nil + } + + // the first byte of pubkey is bitcoin heritage + return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32) +} + +// SHA256 implemented as a native contract +type sha256hash struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *sha256hash) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.Sha256WordGas + params.Sha256Gas +} +func (c *sha256hash) Run(in []byte) []byte { + h := sha256.Sum256(in) + return h[:] +} + +// RIPMED160 implemented as a native contract +type ripemd160hash struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *ripemd160hash) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas +} +func (c *ripemd160hash) Run(in []byte) []byte { + ripemd := ripemd160.New() + ripemd.Write(in) + return common.LeftPadBytes(ripemd.Sum(nil), 32) +} + +// data copy implemented as a native contract +type dataCopy struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *dataCopy) RequiredGas(inputSize int) uint64 { + return uint64(inputSize+31)/32*params.IdentityWordGas + params.IdentityGas +} +func (c *dataCopy) Run(in []byte) []byte { + return in +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/disasm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/disasm.go new file mode 100644 index 0000000..2bfea5c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/disasm.go @@ -0,0 +1,37 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "fmt" + +func Disasm(code []byte) []string { + var out []string + for pc := uint64(0); pc < uint64(len(code)); pc++ { + op := OpCode(code[pc]) + out = append(out, op.String()) + + switch op { + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + a := uint64(op) - uint64(PUSH1) + 1 + out = append(out, fmt.Sprintf("0x%x", code[pc+1:pc+1+a])) + + pc += a + } + } + + return out +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/doc.go b/vendor/github.com/ethereum/go-ethereum/core/vm/doc.go new file mode 100644 index 0000000..239be2c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/doc.go @@ -0,0 +1,35 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package vm implements the Ethereum Virtual Machine. + +The vm package implements two EVMs, a byte code VM and a JIT VM. The BC +(Byte Code) VM loops over a set of bytes and executes them according to the set +of rules defined in the Ethereum yellow paper. When the BC VM is invoked it +invokes the JIT VM in a separate goroutine and compiles the byte code in JIT +instructions. + +The JIT VM, when invoked, loops around a set of pre-defined instructions until +it either runs of gas, causes an internal error, returns or stops. + +The JIT optimiser attempts to pre-compile instructions in to chunks or segments +such as multiple PUSH operations and static JUMPs. It does this by analysing the +opcodes and attempts to match certain regions to known sets. Whenever the +optimiser finds said segments it creates a new instruction and replaces the +first occurrence in the sequence. +*/ +package vm diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go new file mode 100644 index 0000000..69c7d6a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/errors.go @@ -0,0 +1,27 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "errors" + +var ( + ErrOutOfGas = errors.New("out of gas") + ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas") + ErrDepth = errors.New("max call depth exceeded") + ErrTraceLimitReached = errors.New("the number of logs reached the specified limit") + ErrInsufficientBalance = errors.New("insufficient balance for transfer") +) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/evm.go b/vendor/github.com/ethereum/go-ethereum/core/vm/evm.go new file mode 100644 index 0000000..d1fac6c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/evm.go @@ -0,0 +1,297 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +type ( + CanTransferFunc func(StateDB, common.Address, *big.Int) bool + TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + // GetHashFunc returns the nth block hash in the blockchain + // and is used by the BLOCKHASH EVM op code. + GetHashFunc func(uint64) common.Hash +) + +// Context provides the EVM with auxiliary information. Once provided it shouldn't be modified. +type Context struct { + // CanTransfer returns whether the account contains + // sufficient ether to transfer the value + CanTransfer CanTransferFunc + // Transfer transfers ether from one account to the other + Transfer TransferFunc + // GetHash returns the hash corresponding to n + GetHash GetHashFunc + + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE + + // Block information + Coinbase common.Address // Provides information for COINBASE + GasLimit *big.Int // Provides information for GASLIMIT + BlockNumber *big.Int // Provides information for NUMBER + Time *big.Int // Provides information for TIME + Difficulty *big.Int // Provides information for DIFFICULTY +} + +// EVM provides information about external sources for the EVM +// +// The EVM should never be reused and is not thread safe. +type EVM struct { + // Context provides auxiliary blockchain related information + Context + // StateDB gives access to the underlying state + StateDB StateDB + // Depth is the current call stack + depth int + + // chainConfig contains information about the current chain + chainConfig *params.ChainConfig + // virtual machine configuration options used to initialise the + // evm. + vmConfig Config + // global (to this context) ethereum virtual machine + // used throughout the execution of the tx. + interpreter *Interpreter + // abort is used to abort the EVM calling operations + // NOTE: must be set atomically + abort int32 +} + +// NewEVM retutrns a new EVM evmironment. +func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { + evm := &EVM{ + Context: ctx, + StateDB: statedb, + vmConfig: vmConfig, + chainConfig: chainConfig, + } + + evm.interpreter = NewInterpreter(evm, vmConfig) + return evm +} + +// Cancel cancels any running EVM operation. This may be called concurrently and it's safe to be +// called multiple times. +func (evm *EVM) Cancel() { + atomic.StoreInt32(&evm.abort, 1) +} + +// Call executes the contract associated with the addr with the given input as parameters. It also handles any +// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in +// case of an execution error or failed value transfer. +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth + } + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, gas, ErrInsufficientBalance + } + + var ( + to = AccountRef(addr) + snapshot = evm.StateDB.Snapshot() + ) + if !evm.StateDB.Exist(addr) { + if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 { + return nil, gas, nil + } + + evm.StateDB.CreateAccount(addr) + } + evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped evmironment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + ret, err = evm.interpreter.Run(contract, input) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil { + contract.UseGas(contract.Gas) + + evm.StateDB.RevertToSnapshot(snapshot) + } + return ret, contract.Gas, err +} + +// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any +// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in +// case of an execution error or failed value transfer. +// +// CallCode differs from Call in the sense that it executes the given address' code with the caller as context. +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth + } + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, gas, ErrInsufficientBalance + } + + var ( + snapshot = evm.StateDB.Snapshot() + to = AccountRef(caller.Address()) + ) + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped evmironment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + ret, err = evm.interpreter.Run(contract, input) + if err != nil { + contract.UseGas(contract.Gas) + + evm.StateDB.RevertToSnapshot(snapshot) + } + + return ret, contract.Gas, err +} + +// DelegateCall executes the contract associated with the addr with the given input as parameters. +// It reverses the state in case of an execution error. +// +// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context +// and the caller is set to the caller of the caller. +func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth + } + + var ( + snapshot = evm.StateDB.Snapshot() + to = AccountRef(caller.Address()) + ) + + // Iinitialise a new contract and make initialise the delegate values + contract := NewContract(caller, to, nil, gas).AsDelegate() + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + ret, err = evm.interpreter.Run(contract, input) + if err != nil { + contract.UseGas(contract.Gas) + + evm.StateDB.RevertToSnapshot(snapshot) + } + + return ret, contract.Gas, err +} + +// Create creates a new contract using code as deployment code. +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, common.Address{}, gas, nil + } + + // Depth check execution. Fail if we're trying to execute above the + // limit. + if evm.depth > int(params.CallCreateDepth) { + return nil, common.Address{}, gas, ErrDepth + } + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, common.Address{}, gas, ErrInsufficientBalance + } + + // Create a new account on the state + nonce := evm.StateDB.GetNonce(caller.Address()) + evm.StateDB.SetNonce(caller.Address(), nonce+1) + + snapshot := evm.StateDB.Snapshot() + contractAddr = crypto.CreateAddress(caller.Address(), nonce) + evm.StateDB.CreateAccount(contractAddr) + if evm.ChainConfig().IsEIP158(evm.BlockNumber) { + evm.StateDB.SetNonce(contractAddr, 1) + } + evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value) + + // initialise a new contract and set the code that is to be used by the + // E The contract is a scoped evmironment for this execution context + // only. + contract := NewContract(caller, AccountRef(contractAddr), value, gas) + contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) + + ret, err = evm.interpreter.Run(contract, nil) + + // check whether the max code size has been exceeded + maxCodeSizeExceeded := len(ret) > params.MaxCodeSize + // if the contract creation ran successfully and no errors were returned + // calculate the gas required to store the code. If the code could not + // be stored due to not enough gas set an error and let it be handled + // by the error checking condition below. + if err == nil && !maxCodeSizeExceeded { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if contract.UseGas(createDataGas) { + evm.StateDB.SetCode(contractAddr, ret) + } else { + err = ErrCodeStoreOutOfGas + } + } + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if maxCodeSizeExceeded || + (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { + evm.StateDB.RevertToSnapshot(snapshot) + + // Nothing should be returned when an error is thrown. + return nil, contractAddr, 0, err + } + // If the vm returned with an error the return value should be set to nil. + // This isn't consensus critical but merely to for behaviour reasons such as + // tests, RPC calls, etc. + if err != nil { + ret = nil + } + + return ret, contractAddr, contract.Gas, err +} + +// ChainConfig returns the evmironment's chain configuration +func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +// Interpreter returns the EVM interpreter +func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go new file mode 100644 index 0000000..dd64d5f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas.go @@ -0,0 +1,58 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) + +const ( + GasQuickStep uint64 = 2 + GasFastestStep uint64 = 3 + GasFastStep uint64 = 5 + GasMidStep uint64 = 8 + GasSlowStep uint64 = 10 + GasExtStep uint64 = 20 + + GasReturn uint64 = 0 + GasStop uint64 = 0 + GasContractByte uint64 = 200 +) + +// calcGas returns the actual gas cost of the call. +// +// The cost of gas was changed during the homestead price change HF. To allow for EIP150 +// to be implemented. The returned gas is gas - base * 63 / 64. +func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) { + if gasTable.CreateBySuicide > 0 { + availableGas = availableGas - base + gas := availableGas - availableGas/64 + // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 + // is smaller than the requested amount. Therefor we return the new gas instead + // of returning an error. + if callCost.BitLen() > 64 || gas < callCost.Uint64() { + return gas, nil + } + } + if callCost.BitLen() > 64 { + return 0, errGasUintOverflow + } + + return callCost.Uint64(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go new file mode 100644 index 0000000..fba1eb0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go @@ -0,0 +1,416 @@ +package vm + +import ( + gmath "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/params" +) + +// memoryGasCosts calculates the quadratic gas for memory expansion. It does so +// only for the memory region that is expanded, not the total memory. +func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { + // The maximum that will fit in a uint64 is max_word_count - 1 + // anything above that will result in an overflow. + if newMemSize > gmath.MaxUint64-32 { + return 0, errGasUintOverflow + } + + if newMemSize == 0 { + return 0, nil + } + + newMemSizeWords := toWordSize(newMemSize) + newMemSize = newMemSizeWords * 32 + + if newMemSize > uint64(mem.Len()) { + square := newMemSizeWords * newMemSizeWords + linCoef := newMemSizeWords * params.MemoryGas + quadCoef := square / params.QuadCoeffDiv + newTotalFee := linCoef + quadCoef + + fee := newTotalFee - mem.lastGasCost + mem.lastGasCost = newTotalFee + + return fee, nil + } + return 0, nil +} + +func constGasFunc(gas uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gas, nil + } +} + +func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + + words, overflow := bigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + + if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, words); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + y, x = stack.Back(1), stack.Back(0) + val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + ) + // This checks for 3 scenario's and calculates gas accordingly + // 1. From a zero-value address to a non-zero value (NEW VALUE) + // 2. From a non-zero value address to a zero-value address (DELETE) + // 3. From a non-zero to a non-zero (CHANGE) + if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { + // 0 => non 0 + return params.SstoreSetGas, nil + } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { + evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas)) + + return params.SstoreClearGas, nil + } else { + // non 0 => non 0 (or 0 => 0) + return params.SstoreResetGas, nil + } +} + +func makeGasLog(n uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + requestedSize, overflow := bigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } + + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow { + return 0, errGasUintOverflow + } + + var memorySizeGas uint64 + if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil + } +} + +func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := bigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := bigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := bigUint64(stack.Back(3)) + if overflow { + return 0, errGasUintOverflow + } + + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.Balance, nil +} + +func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.ExtcodeSize, nil +} + +func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.SLoad, nil +} + +func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) + + var ( + gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas + overflow bool + ) + if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + gas = gt.Calls + transfersValue = stack.Back(2).BitLen() > 0 + address = common.BigToAddress(stack.Back(1)) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + ) + if eip158 { + if evm.StateDB.Empty(address) && transfersValue { + gas += params.CallNewAccountGas + } + } else if !evm.StateDB.Exist(address) { + gas += params.CallNewAccountGas + } + if transfersValue { + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow + } + + cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. This information is otherwise lost due to the dependency on *current* + // available gas. + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas := gt.Calls + if stack.Back(2).BitLen() > 0 { + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow + } + + cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. This information is otherwise lost due to the dependency on *current* + // available gas. + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return memoryGasCost(mem, memorySize) +} + +func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var gas uint64 + // EIP150 homestead gas reprice fork: + if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + gas = gt.Suicide + var ( + address = common.BigToAddress(stack.Back(0)) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + ) + + if eip158 { + // if empty and transfers value + if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 { + gas += gt.CreateBySuicide + } + } else if !evm.StateDB.Exist(address) { + gas += gt.CreateBySuicide + } + } + + if !evm.StateDB.HasSuicided(contract.Address()) { + evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas)) + } + return gas, nil +} + +func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { + return 0, errGasUintOverflow + } + + cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + // Replace the stack item with the new gas calculation. This means that + // either the original item is left on the stack or the item is replaced by: + // (availableGas - gas) * 63 / 64 + // We replace the stack item so that it's available when the opCall instruction is + // called. + stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) + + if gas, overflow = math.SafeAdd(gas, cg); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} + +func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} + +func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table_test.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table_test.go new file mode 100644 index 0000000..cceb892 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table_test.go @@ -0,0 +1,24 @@ +package vm + +import ( + "math" + "testing" +) + +func TestMemoryGasCost(t *testing.T) { + size := uint64(math.MaxUint64 - 64) + _, err := memoryGasCost(&Memory{}, size) + if err != nil { + t.Error("didn't expect error:", err) + } + + _, err = memoryGasCost(&Memory{}, size+32) + if err != nil { + t.Error("didn't expect error:", err) + } + + _, err = memoryGasCost(&Memory{}, size+33) + if err == nil { + t.Error("expected error") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go new file mode 100644 index 0000000..39e5c05 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -0,0 +1,736 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +var bigZero = new(big.Int) + +func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(U256(x.Add(x, y))) + + evm.interpreter.intPool.put(y) + + return nil, nil +} + +func opSub(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(U256(x.Sub(x, y))) + + evm.interpreter.intPool.put(y) + + return nil, nil +} + +func opMul(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(U256(x.Mul(x, y))) + + evm.interpreter.intPool.put(y) + + return nil, nil +} + +func opDiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + if y.Cmp(common.Big0) != 0 { + stack.push(U256(x.Div(x, y))) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(y) + + return nil, nil +} + +func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := S256(stack.pop()), S256(stack.pop()) + if y.Cmp(common.Big0) == 0 { + stack.push(new(big.Int)) + return nil, nil + } else { + n := new(big.Int) + if evm.interpreter.intPool.get().Mul(x, y).Cmp(common.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + res := x.Div(x.Abs(x), y.Abs(y)) + res.Mul(res, n) + + stack.push(U256(res)) + } + evm.interpreter.intPool.put(y) + return nil, nil +} + +func opMod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + if y.Cmp(common.Big0) == 0 { + stack.push(new(big.Int)) + } else { + stack.push(U256(x.Mod(x, y))) + } + evm.interpreter.intPool.put(y) + return nil, nil +} + +func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := S256(stack.pop()), S256(stack.pop()) + + if y.Cmp(common.Big0) == 0 { + stack.push(new(big.Int)) + } else { + n := new(big.Int) + if x.Cmp(common.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + res := x.Mod(x.Abs(x), y.Abs(y)) + res.Mul(res, n) + + stack.push(U256(res)) + } + evm.interpreter.intPool.put(y) + return nil, nil +} + +func opExp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + base, exponent := stack.pop(), stack.pop() + stack.push(math.Exp(base, exponent)) + + evm.interpreter.intPool.put(base, exponent) + + return nil, nil +} + +func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + back := stack.pop() + if back.Cmp(big.NewInt(31)) < 0 { + bit := uint(back.Uint64()*8 + 7) + num := stack.pop() + mask := back.Lsh(common.Big1, bit) + mask.Sub(mask, common.Big1) + if common.BitTest(num, int(bit)) { + num.Or(num, mask.Not(mask)) + } else { + num.And(num, mask) + } + + stack.push(U256(num)) + } + + evm.interpreter.intPool.put(back) + return nil, nil +} + +func opNot(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x := stack.pop() + stack.push(U256(x.Not(x))) + return nil, nil +} + +func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + if x.Cmp(y) < 0 { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(x, y) + return nil, nil +} + +func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + if x.Cmp(y) > 0 { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(x, y) + return nil, nil +} + +func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := S256(stack.pop()), S256(stack.pop()) + if x.Cmp(S256(y)) < 0 { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(x, y) + return nil, nil +} + +func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := S256(stack.pop()), S256(stack.pop()) + if x.Cmp(y) > 0 { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(x, y) + return nil, nil +} + +func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + if x.Cmp(y) == 0 { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(x, y) + return nil, nil +} + +func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x := stack.pop() + if x.Cmp(common.Big0) > 0 { + stack.push(new(big.Int)) + } else { + stack.push(evm.interpreter.intPool.get().SetUint64(1)) + } + + evm.interpreter.intPool.put(x) + return nil, nil +} + +func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(x.And(x, y)) + + evm.interpreter.intPool.put(y) + return nil, nil +} +func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(x.Or(x, y)) + + evm.interpreter.intPool.put(y) + return nil, nil +} +func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y := stack.pop(), stack.pop() + stack.push(x.Xor(x, y)) + + evm.interpreter.intPool.put(y) + return nil, nil +} +func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + th, val := stack.pop(), stack.pop() + if th.Cmp(big.NewInt(32)) < 0 { + byte := evm.interpreter.intPool.get().SetInt64(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) + stack.push(byte) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(th, val) + return nil, nil +} +func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y, z := stack.pop(), stack.pop(), stack.pop() + if z.Cmp(bigZero) > 0 { + add := x.Add(x, y) + add.Mod(add, z) + stack.push(U256(add)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(y, z) + return nil, nil +} +func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + x, y, z := stack.pop(), stack.pop(), stack.pop() + if z.Cmp(bigZero) > 0 { + mul := x.Mul(x, y) + mul.Mod(mul, z) + stack.push(U256(mul)) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(y, z) + return nil, nil +} + +func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + offset, size := stack.pop(), stack.pop() + data := memory.Get(offset.Int64(), size.Int64()) + hash := crypto.Keccak256(data) + + if evm.vmConfig.EnablePreimageRecording { + evm.StateDB.AddPreimage(common.BytesToHash(hash), data) + } + + stack.push(common.BytesToBig(hash)) + + evm.interpreter.intPool.put(offset, size) + return nil, nil +} + +func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(common.Bytes2Big(contract.Address().Bytes())) + return nil, nil +} + +func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + addr := common.BigToAddress(stack.pop()) + balance := evm.StateDB.GetBalance(addr) + + stack.push(new(big.Int).Set(balance)) + return nil, nil +} + +func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.Origin.Big()) + return nil, nil +} + +func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(contract.Caller().Big()) + return nil, nil +} + +func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().Set(contract.value)) + return nil, nil +} + +func opCalldataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32))) + return nil, nil +} + +func opCalldataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) + return nil, nil +} + +func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + mOff = stack.pop() + cOff = stack.pop() + l = stack.pop() + ) + memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l)) + + evm.interpreter.intPool.put(mOff, cOff, l) + return nil, nil +} + +func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + a := stack.pop() + + addr := common.BigToAddress(a) + a.SetInt64(int64(evm.StateDB.GetCodeSize(addr))) + stack.push(a) + + return nil, nil +} + +func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code))) + stack.push(l) + return nil, nil +} + +func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + mOff = stack.pop() + cOff = stack.pop() + l = stack.pop() + ) + codeCopy := getData(contract.Code, cOff, l) + + memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + + evm.interpreter.intPool.put(mOff, cOff, l) + return nil, nil +} + +func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + addr = common.BigToAddress(stack.pop()) + mOff = stack.pop() + cOff = stack.pop() + l = stack.pop() + ) + codeCopy := getData(evm.StateDB.GetCode(addr), cOff, l) + + memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + + evm.interpreter.intPool.put(mOff, cOff, l) + + return nil, nil +} + +func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice)) + return nil, nil +} + +func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + num := stack.pop() + + n := evm.interpreter.intPool.get().Sub(evm.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(evm.BlockNumber) < 0 { + stack.push(evm.GetHash(num.Uint64()).Big()) + } else { + stack.push(new(big.Int)) + } + + evm.interpreter.intPool.put(num, n) + return nil, nil +} + +func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.Coinbase.Big()) + return nil, nil +} + +func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.Time))) + return nil, nil +} + +func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.BlockNumber))) + return nil, nil +} + +func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.Difficulty))) + return nil, nil +} + +func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(U256(new(big.Int).Set(evm.GasLimit))) + return nil, nil +} + +func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + evm.interpreter.intPool.put(stack.pop()) + return nil, nil +} + +func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + offset := stack.pop() + val := common.BigD(memory.Get(offset.Int64(), 32)) + stack.push(val) + + evm.interpreter.intPool.put(offset) + return nil, nil +} + +func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // pop value of the stack + mStart, val := stack.pop(), stack.pop() + memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) + + evm.interpreter.intPool.put(mStart, val) + return nil, nil +} + +func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + off, val := stack.pop().Int64(), stack.pop().Int64() + memory.store[off] = byte(val & 0xff) + + return nil, nil +} + +func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + loc := common.BigToHash(stack.pop()) + val := evm.StateDB.GetState(contract.Address(), loc).Big() + stack.push(val) + return nil, nil +} + +func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + loc := common.BigToHash(stack.pop()) + val := stack.pop() + evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + + evm.interpreter.intPool.put(val) + return nil, nil +} + +func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + pos := stack.pop() + if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { + nop := contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + *pc = pos.Uint64() + + evm.interpreter.intPool.put(pos) + return nil, nil +} +func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + pos, cond := stack.pop(), stack.pop() + if cond.Cmp(common.BigTrue) >= 0 { + if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { + nop := contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + *pc = pos.Uint64() + } else { + *pc++ + } + + evm.interpreter.intPool.put(pos, cond) + return nil, nil +} +func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return nil, nil +} + +func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetUint64(*pc)) + return nil, nil +} + +func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len()))) + return nil, nil +} + +func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas)) + return nil, nil +} + +func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + value = stack.pop() + offset, size = stack.pop(), stack.pop() + input = memory.Get(offset.Int64(), size.Int64()) + gas = contract.Gas + ) + if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + gas -= gas / 64 + } + + contract.UseGas(gas) + _, addr, returnGas, suberr := evm.Create(contract, input, gas, value) + // Push item on the stack based on the returned error. If the ruleset is + // homestead we must check for CodeStoreOutOfGasError (homestead only + // rule) and treat as an error, if the ruleset is frontier we must + // ignore this error and pretend the operation was successful. + if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas { + stack.push(new(big.Int)) + } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { + stack.push(new(big.Int)) + } else { + stack.push(addr.Big()) + } + contract.Gas += returnGas + + evm.interpreter.intPool.put(value, offset, size) + + return nil, nil +} + +func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + gas := stack.pop().Uint64() + // pop gas and value of the stack. + addr, value := stack.pop(), stack.pop() + value = U256(value) + // pop input size and offset + inOffset, inSize := stack.pop(), stack.pop() + // pop return size and offset + retOffset, retSize := stack.pop(), stack.pop() + + address := common.BigToAddress(addr) + + // Get the arguments from the memory + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + if value.BitLen() > 0 { + gas += params.CallStipend + } + + ret, returnGas, err := evm.Call(contract, address, args, gas, value) + if err != nil { + stack.push(new(big.Int)) + } else { + stack.push(big.NewInt(1)) + + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) + return nil, nil +} + +func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + gas := stack.pop().Uint64() + // pop gas and value of the stack. + addr, value := stack.pop(), stack.pop() + value = U256(value) + // pop input size and offset + inOffset, inSize := stack.pop(), stack.pop() + // pop return size and offset + retOffset, retSize := stack.pop(), stack.pop() + + address := common.BigToAddress(addr) + + // Get the arguments from the memory + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + if value.BitLen() > 0 { + gas += params.CallStipend + } + + ret, returnGas, err := evm.CallCode(contract, address, args, gas, value) + if err != nil { + stack.push(new(big.Int)) + + } else { + stack.push(big.NewInt(1)) + + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) + return nil, nil +} + +func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // if not homestead return an error. DELEGATECALL is not supported + // during pre-homestead. + if !evm.ChainConfig().IsHomestead(evm.BlockNumber) { + return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) + } + + gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + + toAddr := common.BigToAddress(to) + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas) + if err != nil { + stack.push(new(big.Int)) + } else { + stack.push(big.NewInt(1)) + memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) + } + contract.Gas += returnGas + + evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) + return nil, nil +} + +func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + offset, size := stack.pop(), stack.pop() + ret := memory.GetPtr(offset.Int64(), size.Int64()) + + evm.interpreter.intPool.put(offset, size) + return ret, nil +} + +func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return nil, nil +} + +func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + balance := evm.StateDB.GetBalance(contract.Address()) + evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) + + evm.StateDB.Suicide(contract.Address()) + + return nil, nil +} + +// following functions are used by the instruction jump table + +// make log instruction function +func makeLog(size int) executionFunc { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + topics := make([]common.Hash, size) + mStart, mSize := stack.pop(), stack.pop() + for i := 0; i < size; i++ { + topics[i] = common.BigToHash(stack.pop()) + } + + d := memory.Get(mStart.Int64(), mSize.Int64()) + evm.StateDB.AddLog(&types.Log{ + Address: contract.Address(), + Topics: topics, + Data: d, + // This is a non-consensus field, but assigned here because + // core/state doesn't know the current block number. + BlockNumber: evm.BlockNumber.Uint64(), + }) + + evm.interpreter.intPool.put(mStart, mSize) + return nil, nil + } +} + +// make push instruction function +func makePush(size uint64, bsize *big.Int) executionFunc { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + byts := getData(contract.Code, evm.interpreter.intPool.get().SetUint64(*pc+1), bsize) + stack.push(common.Bytes2Big(byts)) + *pc += size + return nil, nil + } +} + +// make push instruction function +func makeDup(size int64) executionFunc { + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.dup(int(size)) + return nil, nil + } +} + +// make swap instruction function +func makeSwap(size int64) executionFunc { + // switch n + 1 otherwise n would be swapped with n + size += 1 + return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.swap(int(size)) + return nil, nil + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier.go b/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier.go new file mode 100644 index 0000000..61c83ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier.go @@ -0,0 +1,15 @@ +// +build VERIFY_EVM_INTEGER_POOL + +package vm + +import "fmt" + +const verifyPool = true + +func verifyIntegerPool(ip *intPool) { + for i, item := range ip.pool.data { + if item.Cmp(checkVal) != 0 { + panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i)) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier_empty.go b/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier_empty.go new file mode 100644 index 0000000..982f8c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/int_pool_verifier_empty.go @@ -0,0 +1,7 @@ +// +build !VERIFY_EVM_INTEGER_POOL + +package vm + +const verifyPool = false + +func verifyIntegerPool(ip *intPool) {} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go b/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go new file mode 100644 index 0000000..4d8ece4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/interface.go @@ -0,0 +1,78 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// StateDB is an EVM database for full state querying. +type StateDB interface { + CreateAccount(common.Address) + + SubBalance(common.Address, *big.Int) + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + + GetCodeHash(common.Address) common.Hash + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + GetCodeSize(common.Address) int + + AddRefund(*big.Int) + GetRefund() *big.Int + + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Suicide(common.Address) bool + HasSuicided(common.Address) bool + + // Exist reports whether the given account exists in state. + // Notably this should also return true for suicided accounts. + Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool + + RevertToSnapshot(int) + Snapshot() int + + AddLog(*types.Log) + AddPreimage(common.Hash, []byte) + + ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) +} + +// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM +// depends on this context being implemented for doing subcalls and initialising new EVM contracts. +type CallContext interface { + // Call another contract + Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Take another's contract code and execute within our own context + CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Same as CallCode except sender and value is propagated from parent to child scope + DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) + // Create a new contract + Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go b/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go new file mode 100644 index 0000000..46c6bef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go @@ -0,0 +1,208 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" + "math/big" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +// Config are the configuration options for the Interpreter +type Config struct { + // Debug enabled debugging Interpreter options + Debug bool + // EnableJit enabled the JIT VM + EnableJit bool + // ForceJit forces the JIT VM + ForceJit bool + // Tracer is the op code logger + Tracer Tracer + // NoRecursion disabled Interpreter call, callcode, + // delegate call and create. + NoRecursion bool + // Disable gas metering + DisableGasMetering bool + // Enable recording of SHA3/keccak preimages + EnablePreimageRecording bool + // JumpTable contains the EVM instruction table. This + // may me left uninitialised and will be set the default + // table. + JumpTable [256]operation +} + +// Interpreter is used to run Ethereum based contracts and will utilise the +// passed environment to query external sources for state information. +// The Interpreter will run the byte code VM or JIT VM based on the passed +// configuration. +type Interpreter struct { + env *EVM + cfg Config + gasTable params.GasTable + intPool *intPool +} + +// NewInterpreter returns a new instance of the Interpreter. +func NewInterpreter(env *EVM, cfg Config) *Interpreter { + // We use the STOP instruction whether to see + // the jump table was initialised. If it was not + // we'll set the default jump table. + if !cfg.JumpTable[STOP].valid { + cfg.JumpTable = defaultJumpTable + } + + return &Interpreter{ + env: env, + cfg: cfg, + gasTable: env.ChainConfig().GasTable(env.BlockNumber), + intPool: newIntPool(), + } +} + +// Run loops and evaluates the contract's code with the given input data +func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { + evm.env.depth++ + defer func() { evm.env.depth-- }() + + if contract.CodeAddr != nil { + if p := PrecompiledContracts[*contract.CodeAddr]; p != nil { + return RunPrecompiledContract(p, input, contract) + } + } + + // Don't bother with the execution if there's no code. + if len(contract.Code) == 0 { + return nil, nil + } + + codehash := contract.CodeHash // codehash is used when doing jump dest caching + if codehash == (common.Hash{}) { + codehash = crypto.Keccak256Hash(contract.Code) + } + + var ( + op OpCode // current opcode + mem = NewMemory() // bound memory + stack = newstack() // local stack + // For optimisation reason we're using uint64 as the program counter. + // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible. + pc = uint64(0) // program counter + cost uint64 + ) + contract.Input = input + + // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. + defer func() { + if err != nil && evm.cfg.Debug { + // XXX For debugging + //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err) + // TODO update the tracer + g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + } + }() + + if glog.V(logger.Debug) { + glog.Infof("evm running: %x\n", codehash[:4]) + tstart := time.Now() + defer func() { + glog.Infof("evm done: %x. time: %v\n", codehash[:4], time.Since(tstart)) + }() + } + + // The Interpreter main run loop (contextual). This loop runs until either an + // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during + // the execution of one of the operations or until the evm.done is set by + // the parent context.Context. + for atomic.LoadInt32(&evm.env.abort) == 0 { + // Get the memory location of pc + op = contract.GetOp(pc) + + // get the operation from the jump table matching the opcode + operation := evm.cfg.JumpTable[op] + + // if the op is invalid abort the process and return an error + if !operation.valid { + return nil, fmt.Errorf("invalid opcode %x", op) + } + + // validate the stack and make sure there enough stack items available + // to perform the operation + if err := operation.validateStack(stack); err != nil { + return nil, err + } + + var memorySize uint64 + // calculate the new memory size and expand the memory to fit + // the operation + if operation.memorySize != nil { + memSize, overflow := bigUint64(operation.memorySize(stack)) + if overflow { + return nil, errGasUintOverflow + } + // memory is expanded in words of 32 bytes. Gas + // is also calculated in words. + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, errGasUintOverflow + } + } + + if !evm.cfg.DisableGasMetering { + // consume the gas and return an error if not enough gas is available. + // cost is explicitly set so that the capture state defer method cas get the proper cost + cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) + if err != nil || !contract.UseGas(cost) { + return nil, ErrOutOfGas + } + } + if memorySize > 0 { + mem.Resize(memorySize) + } + + if evm.cfg.Debug { + g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + } + // XXX For debugging + //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len()) + + // execute the operation + res, err := operation.execute(&pc, evm.env, contract, mem, stack) + // verifyPool is a build flag. Pool verification makes sure the integrity + // of the integer pool by comparing values to a default value. + if verifyPool { + verifyIntegerPool(evm.intPool) + } + switch { + case err != nil: + return nil, err + case operation.halts: + return res, nil + case !operation.jumps: + pc++ + } + } + return nil, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/intpool.go b/vendor/github.com/ethereum/go-ethereum/core/vm/intpool.go new file mode 100644 index 0000000..4f1228e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/intpool.go @@ -0,0 +1,49 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "math/big" + +var checkVal = big.NewInt(-42) + +// intPool is a pool of big integers that +// can be reused for all big.Int operations. +type intPool struct { + pool *Stack +} + +func newIntPool() *intPool { + return &intPool{pool: newstack()} +} + +func (p *intPool) get() *big.Int { + if p.pool.len() > 0 { + return p.pool.pop() + } + return new(big.Int) +} +func (p *intPool) put(is ...*big.Int) { + for _, i := range is { + // verifyPool is a build flag. Pool verification makes sure the integrity + // of the integer pool by comparing values to a default value. + if verifyPool { + i.Set(checkVal) + } + + p.pool.push(i) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go new file mode 100644 index 0000000..ed30100 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go @@ -0,0 +1,862 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/params" +) + +type ( + executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) + gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + stackValidationFunc func(*Stack) error + memorySizeFunc func(*Stack) *big.Int +) + +var errGasUintOverflow = errors.New("gas uint64 overflow") + +type operation struct { + // op is the operation function + execute executionFunc + // gasCost is the gas function and returns the gas required for execution + gasCost gasFunc + // validateStack validates the stack (size) for the operation + validateStack stackValidationFunc + // memorySize returns the memory size required for the operation + memorySize memorySizeFunc + // halts indicates whether the operation shoult halt further execution + // and return + halts bool + // jumps indicates whether operation made a jump. This prevents the program + // counter from further incrementing. + jumps bool + // valid is used to check whether the retrieved operation is valid and known + valid bool +} + +var defaultJumpTable = NewJumpTable() + +func NewJumpTable() [256]operation { + return [256]operation{ + STOP: { + execute: opStop, + gasCost: constGasFunc(0), + validateStack: makeStackFunc(0, 0), + halts: true, + valid: true, + }, + ADD: { + execute: opAdd, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + MUL: { + execute: opMul, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SUB: { + execute: opSub, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + DIV: { + execute: opDiv, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SDIV: { + execute: opSdiv, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + MOD: { + execute: opMod, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SMOD: { + execute: opSmod, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + ADDMOD: { + execute: opAddmod, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(3, 1), + valid: true, + }, + MULMOD: { + execute: opMulmod, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(3, 1), + valid: true, + }, + EXP: { + execute: opExp, + gasCost: gasExp, + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SIGNEXTEND: { + execute: opSignExtend, + gasCost: constGasFunc(GasFastStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + LT: { + execute: opLt, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + GT: { + execute: opGt, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SLT: { + execute: opSlt, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SGT: { + execute: opSgt, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + EQ: { + execute: opEq, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + ISZERO: { + execute: opIszero, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(1, 1), + valid: true, + }, + AND: { + execute: opAnd, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + XOR: { + execute: opXor, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + OR: { + execute: opOr, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + NOT: { + execute: opNot, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(1, 1), + valid: true, + }, + BYTE: { + execute: opByte, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(2, 1), + valid: true, + }, + SHA3: { + execute: opSha3, + gasCost: gasSha3, + validateStack: makeStackFunc(2, 1), + memorySize: memorySha3, + valid: true, + }, + ADDRESS: { + execute: opAddress, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + BALANCE: { + execute: opBalance, + gasCost: gasBalance, + validateStack: makeStackFunc(1, 1), + valid: true, + }, + ORIGIN: { + execute: opOrigin, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + CALLER: { + execute: opCaller, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + CALLVALUE: { + execute: opCallValue, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + CALLDATALOAD: { + execute: opCalldataLoad, + gasCost: constGasFunc(GasFastestStep), + validateStack: makeStackFunc(1, 1), + valid: true, + }, + CALLDATASIZE: { + execute: opCalldataSize, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + CALLDATACOPY: { + execute: opCalldataCopy, + gasCost: gasCalldataCopy, + validateStack: makeStackFunc(3, 0), + memorySize: memoryCalldataCopy, + valid: true, + }, + CODESIZE: { + execute: opCodeSize, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + CODECOPY: { + execute: opCodeCopy, + gasCost: gasCodeCopy, + validateStack: makeStackFunc(3, 0), + memorySize: memoryCodeCopy, + valid: true, + }, + GASPRICE: { + execute: opGasprice, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + EXTCODESIZE: { + execute: opExtCodeSize, + gasCost: gasExtCodeSize, + validateStack: makeStackFunc(1, 1), + valid: true, + }, + EXTCODECOPY: { + execute: opExtCodeCopy, + gasCost: gasExtCodeCopy, + validateStack: makeStackFunc(4, 0), + memorySize: memoryExtCodeCopy, + valid: true, + }, + BLOCKHASH: { + execute: opBlockhash, + gasCost: constGasFunc(GasExtStep), + validateStack: makeStackFunc(1, 1), + valid: true, + }, + COINBASE: { + execute: opCoinbase, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + TIMESTAMP: { + execute: opTimestamp, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + NUMBER: { + execute: opNumber, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + DIFFICULTY: { + execute: opDifficulty, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + GASLIMIT: { + execute: opGasLimit, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + POP: { + execute: opPop, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(1, 0), + valid: true, + }, + MLOAD: { + execute: opMload, + gasCost: gasMLoad, + validateStack: makeStackFunc(1, 1), + memorySize: memoryMLoad, + valid: true, + }, + MSTORE: { + execute: opMstore, + gasCost: gasMStore, + validateStack: makeStackFunc(2, 0), + memorySize: memoryMStore, + valid: true, + }, + MSTORE8: { + execute: opMstore8, + gasCost: gasMStore8, + memorySize: memoryMStore8, + validateStack: makeStackFunc(2, 0), + + valid: true, + }, + SLOAD: { + execute: opSload, + gasCost: gasSLoad, + validateStack: makeStackFunc(1, 1), + valid: true, + }, + SSTORE: { + execute: opSstore, + gasCost: gasSStore, + validateStack: makeStackFunc(2, 0), + valid: true, + }, + JUMP: { + execute: opJump, + gasCost: constGasFunc(GasMidStep), + validateStack: makeStackFunc(1, 0), + jumps: true, + valid: true, + }, + JUMPI: { + execute: opJumpi, + gasCost: constGasFunc(GasSlowStep), + validateStack: makeStackFunc(2, 0), + jumps: true, + valid: true, + }, + PC: { + execute: opPc, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + MSIZE: { + execute: opMsize, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + GAS: { + execute: opGas, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, + JUMPDEST: { + execute: opJumpdest, + gasCost: constGasFunc(params.JumpdestGas), + validateStack: makeStackFunc(0, 0), + valid: true, + }, + PUSH1: { + execute: makePush(1, big.NewInt(1)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH2: { + execute: makePush(2, big.NewInt(2)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH3: { + execute: makePush(3, big.NewInt(3)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH4: { + execute: makePush(4, big.NewInt(4)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH5: { + execute: makePush(5, big.NewInt(5)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH6: { + execute: makePush(6, big.NewInt(6)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH7: { + execute: makePush(7, big.NewInt(7)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH8: { + execute: makePush(8, big.NewInt(8)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH9: { + execute: makePush(9, big.NewInt(9)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH10: { + execute: makePush(10, big.NewInt(10)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH11: { + execute: makePush(11, big.NewInt(11)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH12: { + execute: makePush(12, big.NewInt(12)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH13: { + execute: makePush(13, big.NewInt(13)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH14: { + execute: makePush(14, big.NewInt(14)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH15: { + execute: makePush(15, big.NewInt(15)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH16: { + execute: makePush(16, big.NewInt(16)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH17: { + execute: makePush(17, big.NewInt(17)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH18: { + execute: makePush(18, big.NewInt(18)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH19: { + execute: makePush(19, big.NewInt(19)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH20: { + execute: makePush(20, big.NewInt(20)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH21: { + execute: makePush(21, big.NewInt(21)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH22: { + execute: makePush(22, big.NewInt(22)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH23: { + execute: makePush(23, big.NewInt(23)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH24: { + execute: makePush(24, big.NewInt(24)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH25: { + execute: makePush(25, big.NewInt(25)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH26: { + execute: makePush(26, big.NewInt(26)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH27: { + execute: makePush(27, big.NewInt(27)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH28: { + execute: makePush(28, big.NewInt(28)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH29: { + execute: makePush(29, big.NewInt(29)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH30: { + execute: makePush(30, big.NewInt(30)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH31: { + execute: makePush(31, big.NewInt(31)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + PUSH32: { + execute: makePush(32, big.NewInt(32)), + gasCost: gasPush, + validateStack: makeStackFunc(0, 1), + valid: true, + }, + DUP1: { + execute: makeDup(1), + gasCost: gasDup, + validateStack: makeDupStackFunc(1), + valid: true, + }, + DUP2: { + execute: makeDup(2), + gasCost: gasDup, + validateStack: makeDupStackFunc(2), + valid: true, + }, + DUP3: { + execute: makeDup(3), + gasCost: gasDup, + validateStack: makeDupStackFunc(3), + valid: true, + }, + DUP4: { + execute: makeDup(4), + gasCost: gasDup, + validateStack: makeDupStackFunc(4), + valid: true, + }, + DUP5: { + execute: makeDup(5), + gasCost: gasDup, + validateStack: makeDupStackFunc(5), + valid: true, + }, + DUP6: { + execute: makeDup(6), + gasCost: gasDup, + validateStack: makeDupStackFunc(6), + valid: true, + }, + DUP7: { + execute: makeDup(7), + gasCost: gasDup, + validateStack: makeDupStackFunc(7), + valid: true, + }, + DUP8: { + execute: makeDup(8), + gasCost: gasDup, + validateStack: makeDupStackFunc(8), + valid: true, + }, + DUP9: { + execute: makeDup(9), + gasCost: gasDup, + validateStack: makeDupStackFunc(9), + valid: true, + }, + DUP10: { + execute: makeDup(10), + gasCost: gasDup, + validateStack: makeDupStackFunc(10), + valid: true, + }, + DUP11: { + execute: makeDup(11), + gasCost: gasDup, + validateStack: makeDupStackFunc(11), + valid: true, + }, + DUP12: { + execute: makeDup(12), + gasCost: gasDup, + validateStack: makeDupStackFunc(12), + valid: true, + }, + DUP13: { + execute: makeDup(13), + gasCost: gasDup, + validateStack: makeDupStackFunc(13), + valid: true, + }, + DUP14: { + execute: makeDup(14), + gasCost: gasDup, + validateStack: makeDupStackFunc(14), + valid: true, + }, + DUP15: { + execute: makeDup(15), + gasCost: gasDup, + validateStack: makeDupStackFunc(15), + valid: true, + }, + DUP16: { + execute: makeDup(16), + gasCost: gasDup, + validateStack: makeDupStackFunc(16), + valid: true, + }, + SWAP1: { + execute: makeSwap(1), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(2), + valid: true, + }, + SWAP2: { + execute: makeSwap(2), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(3), + valid: true, + }, + SWAP3: { + execute: makeSwap(3), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(4), + valid: true, + }, + SWAP4: { + execute: makeSwap(4), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(5), + valid: true, + }, + SWAP5: { + execute: makeSwap(5), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(6), + valid: true, + }, + SWAP6: { + execute: makeSwap(6), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(7), + valid: true, + }, + SWAP7: { + execute: makeSwap(7), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(8), + valid: true, + }, + SWAP8: { + execute: makeSwap(8), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(9), + valid: true, + }, + SWAP9: { + execute: makeSwap(9), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(10), + valid: true, + }, + SWAP10: { + execute: makeSwap(10), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(11), + valid: true, + }, + SWAP11: { + execute: makeSwap(11), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(12), + valid: true, + }, + SWAP12: { + execute: makeSwap(12), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(13), + valid: true, + }, + SWAP13: { + execute: makeSwap(13), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(14), + valid: true, + }, + SWAP14: { + execute: makeSwap(14), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(15), + valid: true, + }, + SWAP15: { + execute: makeSwap(15), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(16), + valid: true, + }, + SWAP16: { + execute: makeSwap(16), + gasCost: gasSwap, + validateStack: makeSwapStackFunc(17), + valid: true, + }, + LOG0: { + execute: makeLog(0), + gasCost: makeGasLog(0), + validateStack: makeStackFunc(2, 0), + memorySize: memoryLog, + valid: true, + }, + LOG1: { + execute: makeLog(1), + gasCost: makeGasLog(1), + validateStack: makeStackFunc(3, 0), + memorySize: memoryLog, + valid: true, + }, + LOG2: { + execute: makeLog(2), + gasCost: makeGasLog(2), + validateStack: makeStackFunc(4, 0), + memorySize: memoryLog, + valid: true, + }, + LOG3: { + execute: makeLog(3), + gasCost: makeGasLog(3), + validateStack: makeStackFunc(5, 0), + memorySize: memoryLog, + valid: true, + }, + LOG4: { + execute: makeLog(4), + gasCost: makeGasLog(4), + validateStack: makeStackFunc(6, 0), + memorySize: memoryLog, + valid: true, + }, + CREATE: { + execute: opCreate, + gasCost: gasCreate, + validateStack: makeStackFunc(3, 1), + memorySize: memoryCreate, + valid: true, + }, + CALL: { + execute: opCall, + gasCost: gasCall, + validateStack: makeStackFunc(7, 1), + memorySize: memoryCall, + valid: true, + }, + CALLCODE: { + execute: opCallCode, + gasCost: gasCallCode, + validateStack: makeStackFunc(7, 1), + memorySize: memoryCall, + valid: true, + }, + RETURN: { + execute: opReturn, + gasCost: gasReturn, + validateStack: makeStackFunc(2, 0), + memorySize: memoryReturn, + halts: true, + valid: true, + }, + DELEGATECALL: { + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: makeStackFunc(6, 1), + memorySize: memoryDelegateCall, + valid: true, + }, + SELFDESTRUCT: { + execute: opSuicide, + gasCost: gasSuicide, + validateStack: makeStackFunc(1, 0), + halts: true, + valid: true, + }, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go new file mode 100644 index 0000000..3845b10 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger.go @@ -0,0 +1,211 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" + "math/big" + "os" + "unicode" + + "github.com/ethereum/go-ethereum/common" +) + +type Storage map[common.Hash]common.Hash + +func (self Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range self { + cpy[key] = value + } + + return cpy +} + +// LogConfig are the configuration options for structured logger the EVM +type LogConfig struct { + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + FullStorage bool // show full storage (slow) + Limit int // maximum length of output, but zero means unlimited +} + +// StructLog is emitted to the EVM each cycle and lists information about the current internal state +// prior to the execution of the statement. +type StructLog struct { + Pc uint64 + Op OpCode + Gas *big.Int + GasCost *big.Int + Memory []byte + Stack []*big.Int + Storage map[common.Hash]common.Hash + Depth int + Err error +} + +// Tracer is used to collect execution traces from an EVM transaction +// execution. CaptureState is called for each step of the VM with the +// current VM state. +// Note that reference types are actual VM data structures; make copies +// if you need to retain them beyond the current call. +type Tracer interface { + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error +} + +// StructLogger is an EVM state logger and implements Tracer. +// +// StructLogger can capture state based on the given Log configuration and also keeps +// a track record of modified storage which is used in reporting snapshots of the +// contract their storage. +type StructLogger struct { + cfg LogConfig + + logs []StructLog + changedValues map[common.Address]Storage +} + +// NewLogger returns a new logger +func NewStructLogger(cfg *LogConfig) *StructLogger { + logger := &StructLogger{ + changedValues: make(map[common.Address]Storage), + } + if cfg != nil { + logger.cfg = *cfg + } + return logger +} + +// captureState logs a new structured log message and pushes it out to the environment +// +// captureState also tracks SSTORE ops to track dirty values. +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + // check if already accumulated the specified number of logs + if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { + return ErrTraceLimitReached + } + + // initialise new changed values storage container for this contract + // if not present. + if l.changedValues[contract.Address()] == nil { + l.changedValues[contract.Address()] = make(Storage) + } + + // capture SSTORE opcodes and determine the changed value and store + // it in the local storage container. NOTE: we do not need to do any + // range checks here because that's already handler prior to calling + // this function. + switch op { + case SSTORE: + var ( + value = common.BigToHash(stack.data[stack.len()-2]) + address = common.BigToHash(stack.data[stack.len()-1]) + ) + l.changedValues[contract.Address()][address] = value + } + + // copy a snapstot of the current memory state to a new buffer + var mem []byte + if !l.cfg.DisableMemory { + mem = make([]byte, len(memory.Data())) + copy(mem, memory.Data()) + } + + // copy a snapshot of the current stack state to a new buffer + var stck []*big.Int + if !l.cfg.DisableStack { + stck = make([]*big.Int, len(stack.Data())) + for i, item := range stack.Data() { + stck[i] = new(big.Int).Set(item) + } + } + + // Copy the storage based on the settings specified in the log config. If full storage + // is disabled (default) we can use the simple Storage.Copy method, otherwise we use + // the state object to query for all values (slow process). + var storage Storage + if !l.cfg.DisableStorage { + if l.cfg.FullStorage { + storage = make(Storage) + // Get the contract account and loop over each storage entry. This may involve looping over + // the trie and is a very expensive process. + + env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool { + storage[key] = value + // Return true, indicating we'd like to continue. + return true + }) + } else { + // copy a snapshot of the current storage to a new container. + storage = l.changedValues[contract.Address()].Copy() + } + } + // create a new snaptshot of the EVM. + log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err} + + l.logs = append(l.logs, log) + return nil +} + +// StructLogs returns a list of captured log entries +func (l *StructLogger) StructLogs() []StructLog { + return l.logs +} + +// StdErrFormat formats a slice of StructLogs to human readable format +func StdErrFormat(logs []StructLog) { + fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs)) + for _, log := range logs { + fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v", log.Pc, log.Op, log.Gas, log.GasCost) + if log.Err != nil { + fmt.Fprintf(os.Stderr, " ERROR: %v", log.Err) + } + fmt.Fprintf(os.Stderr, "\n") + + fmt.Fprintln(os.Stderr, "STACK =", len(log.Stack)) + + for i := len(log.Stack) - 1; i >= 0; i-- { + fmt.Fprintf(os.Stderr, "%04d: %x\n", len(log.Stack)-i-1, common.LeftPadBytes(log.Stack[i].Bytes(), 32)) + } + + const maxMem = 10 + addr := 0 + fmt.Fprintln(os.Stderr, "MEM =", len(log.Memory)) + for i := 0; i+16 <= len(log.Memory) && addr < maxMem; i += 16 { + data := log.Memory[i : i+16] + str := fmt.Sprintf("%04d: % x ", addr*16, data) + for _, r := range data { + if r == 0 { + str += "." + } else if unicode.IsPrint(rune(r)) { + str += fmt.Sprintf("%s", string(r)) + } else { + str += "?" + } + } + addr++ + fmt.Fprintln(os.Stderr, str) + } + + fmt.Fprintln(os.Stderr, "STORAGE =", len(log.Storage)) + for h, item := range log.Storage { + fmt.Fprintf(os.Stderr, "%x: %x\n", h, item) + } + fmt.Fprintln(os.Stderr) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/logger_test.go b/vendor/github.com/ethereum/go-ethereum/core/vm/logger_test.go new file mode 100644 index 0000000..e755a18 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/logger_test.go @@ -0,0 +1,94 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +type dummyContractRef struct { + calledForEach bool +} + +func (dummyContractRef) ReturnGas(*big.Int) {} +func (dummyContractRef) Address() common.Address { return common.Address{} } +func (dummyContractRef) Value() *big.Int { return new(big.Int) } +func (dummyContractRef) SetCode(common.Hash, []byte) {} +func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) { + d.calledForEach = true +} +func (d *dummyContractRef) SubBalance(amount *big.Int) {} +func (d *dummyContractRef) AddBalance(amount *big.Int) {} +func (d *dummyContractRef) SetBalance(*big.Int) {} +func (d *dummyContractRef) SetNonce(uint64) {} +func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } + +type dummyStateDB struct { + NoopStateDB + ref *dummyContractRef +} + +func TestStoreCapture(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) + logger = NewStructLogger(nil) + mem = NewMemory() + stack = newstack() + contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) + ) + stack.push(big.NewInt(1)) + stack.push(big.NewInt(0)) + + var index common.Hash + + logger.CaptureState(env, 0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + if len(logger.changedValues[contract.Address()]) == 0 { + t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) + } + + exp := common.BigToHash(big.NewInt(1)) + if logger.changedValues[contract.Address()][index] != exp { + t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index]) + } +} + +func TestStorageCapture(t *testing.T) { + t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it") + var ( + ref = &dummyContractRef{} + contract = NewContract(ref, ref, new(big.Int), 0) + env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) + logger = NewStructLogger(nil) + mem = NewMemory() + stack = newstack() + ) + + logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + if ref.calledForEach { + t.Error("didn't expect for each to be called") + } + + logger = NewStructLogger(&LogConfig{FullStorage: true}) + logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + if !ref.calledForEach { + t.Error("expected for each to be called") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go b/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go new file mode 100644 index 0000000..99a84d2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go @@ -0,0 +1,104 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "fmt" + +// Memory implements a simple memory model for the ethereum virtual machine. +type Memory struct { + store []byte + lastGasCost uint64 +} + +func NewMemory() *Memory { + return &Memory{} +} + +// Set sets offset + size to value +func (m *Memory) Set(offset, size uint64, value []byte) { + // length of store may never be less than offset + size. + // The store should be resized PRIOR to setting the memory + if size > uint64(len(m.store)) { + panic("INVALID memory: store empty") + } + + // It's possible the offset is greater than 0 and size equals 0. This is because + // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP) + if size > 0 { + copy(m.store[offset:offset+size], value) + } +} + +// Resize resizes the memory to size +func (m *Memory) Resize(size uint64) { + if uint64(m.Len()) < size { + m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) + } +} + +// Get returns offset + size as a new slice +func (self *Memory) Get(offset, size int64) (cpy []byte) { + if size == 0 { + return nil + } + + if len(self.store) > int(offset) { + cpy = make([]byte, size) + copy(cpy, self.store[offset:offset+size]) + + return + } + + return +} + +// GetPtr returns the offset + size +func (self *Memory) GetPtr(offset, size int64) []byte { + if size == 0 { + return nil + } + + if len(self.store) > int(offset) { + return self.store[offset : offset+size] + } + + return nil +} + +// Len returns the length of the backing slice +func (m *Memory) Len() int { + return len(m.store) +} + +// Data returns the backing slice +func (m *Memory) Data() []byte { + return m.store +} + +func (m *Memory) Print() { + fmt.Printf("### mem %d bytes ###\n", len(m.store)) + if len(m.store) > 0 { + addr := 0 + for i := 0; i+32 <= len(m.store); i += 32 { + fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) + addr++ + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("####################") +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/memory_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/memory_table.go new file mode 100644 index 0000000..4db9948 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/memory_table.go @@ -0,0 +1,68 @@ +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +func memorySha3(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), stack.Back(1)) +} + +func memoryCalldataCopy(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), stack.Back(2)) +} + +func memoryCodeCopy(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), stack.Back(2)) +} + +func memoryExtCodeCopy(stack *Stack) *big.Int { + return calcMemSize(stack.Back(1), stack.Back(3)) +} + +func memoryMLoad(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), big.NewInt(32)) +} + +func memoryMStore8(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), big.NewInt(1)) +} + +func memoryMStore(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), big.NewInt(32)) +} + +func memoryCreate(stack *Stack) *big.Int { + return calcMemSize(stack.Back(1), stack.Back(2)) +} + +func memoryCall(stack *Stack) *big.Int { + x := calcMemSize(stack.Back(5), stack.Back(6)) + y := calcMemSize(stack.Back(3), stack.Back(4)) + + return common.BigMax(x, y) +} + +func memoryCallCode(stack *Stack) *big.Int { + x := calcMemSize(stack.Back(5), stack.Back(6)) + y := calcMemSize(stack.Back(3), stack.Back(4)) + + return common.BigMax(x, y) +} +func memoryDelegateCall(stack *Stack) *big.Int { + x := calcMemSize(stack.Back(4), stack.Back(5)) + y := calcMemSize(stack.Back(2), stack.Back(3)) + + return common.BigMax(x, y) +} + +func memoryReturn(stack *Stack) *big.Int { + return calcMemSize(stack.Back(0), stack.Back(1)) +} + +func memoryLog(stack *Stack) *big.Int { + mSize, mStart := stack.Back(1), stack.Back(0) + return calcMemSize(mStart, mSize) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go b/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go new file mode 100644 index 0000000..2a04a95 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/noop.go @@ -0,0 +1,70 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool { + return true +} +func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {} + +type NoopEVMCallContext struct{} + +func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return nil, nil +} +func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + return nil, nil +} +func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + return nil, common.Address{}, nil +} +func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + return nil, nil +} + +type NoopStateDB struct{} + +func (NoopStateDB) CreateAccount(common.Address) {} +func (NoopStateDB) SubBalance(common.Address, *big.Int) {} +func (NoopStateDB) AddBalance(common.Address, *big.Int) {} +func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil } +func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 } +func (NoopStateDB) SetNonce(common.Address, uint64) {} +func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} } +func (NoopStateDB) GetCode(common.Address) []byte { return nil } +func (NoopStateDB) SetCode(common.Address, []byte) {} +func (NoopStateDB) GetCodeSize(common.Address) int { return 0 } +func (NoopStateDB) AddRefund(*big.Int) {} +func (NoopStateDB) GetRefund() *big.Int { return nil } +func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} } +func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {} +func (NoopStateDB) Suicide(common.Address) bool { return false } +func (NoopStateDB) HasSuicided(common.Address) bool { return false } +func (NoopStateDB) Exist(common.Address) bool { return false } +func (NoopStateDB) Empty(common.Address) bool { return false } +func (NoopStateDB) RevertToSnapshot(int) {} +func (NoopStateDB) Snapshot() int { return 0 } +func (NoopStateDB) AddLog(*types.Log) {} +func (NoopStateDB) AddPreimage(common.Hash, []byte) {} +func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go new file mode 100644 index 0000000..d4ba7f1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/opcodes.go @@ -0,0 +1,509 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" +) + +// OpCode is an EVM opcode +type OpCode byte + +func (op OpCode) IsPush() bool { + switch op { + case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + return true + } + return false +} + +func (op OpCode) IsStaticJump() bool { + return op == JUMP +} + +const ( + // 0x0 range - arithmetic ops + STOP OpCode = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + EXP + SIGNEXTEND +) + +const ( + LT OpCode = iota + 0x10 + GT + SLT + SGT + EQ + ISZERO + AND + OR + XOR + NOT + BYTE + + SHA3 = 0x20 +) + +const ( + // 0x30 range - closure state + ADDRESS OpCode = 0x30 + iota + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY +) + +const ( + + // 0x40 range - block operations + BLOCKHASH OpCode = 0x40 + iota + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT +) + +const ( + // 0x50 range - 'storage' and execution + POP OpCode = 0x50 + iota + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + JUMP + JUMPI + PC + MSIZE + GAS + JUMPDEST +) + +const ( + // 0x60 range + PUSH1 OpCode = 0x60 + iota + PUSH2 + PUSH3 + PUSH4 + PUSH5 + PUSH6 + PUSH7 + PUSH8 + PUSH9 + PUSH10 + PUSH11 + PUSH12 + PUSH13 + PUSH14 + PUSH15 + PUSH16 + PUSH17 + PUSH18 + PUSH19 + PUSH20 + PUSH21 + PUSH22 + PUSH23 + PUSH24 + PUSH25 + PUSH26 + PUSH27 + PUSH28 + PUSH29 + PUSH30 + PUSH31 + PUSH32 + DUP1 + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 + SWAP1 + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 +) + +const ( + LOG0 OpCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +// unofficial opcodes used for parsing +const ( + PUSH OpCode = 0xb0 + iota + DUP + SWAP +) + +const ( + // 0xf0 range - closures + CREATE OpCode = 0xf0 + iota + CALL + CALLCODE + RETURN + DELEGATECALL + + SELFDESTRUCT = 0xff +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + // 0x0 range - arithmetic ops + STOP: "STOP", + ADD: "ADD", + MUL: "MUL", + SUB: "SUB", + DIV: "DIV", + SDIV: "SDIV", + MOD: "MOD", + SMOD: "SMOD", + EXP: "EXP", + NOT: "NOT", + LT: "LT", + GT: "GT", + SLT: "SLT", + SGT: "SGT", + EQ: "EQ", + ISZERO: "ISZERO", + SIGNEXTEND: "SIGNEXTEND", + + // 0x10 range - bit ops + AND: "AND", + OR: "OR", + XOR: "XOR", + BYTE: "BYTE", + ADDMOD: "ADDMOD", + MULMOD: "MULMOD", + + // 0x20 range - crypto + SHA3: "SHA3", + + // 0x30 range - closure state + ADDRESS: "ADDRESS", + BALANCE: "BALANCE", + ORIGIN: "ORIGIN", + CALLER: "CALLER", + CALLVALUE: "CALLVALUE", + CALLDATALOAD: "CALLDATALOAD", + CALLDATASIZE: "CALLDATASIZE", + CALLDATACOPY: "CALLDATACOPY", + CODESIZE: "CODESIZE", + CODECOPY: "CODECOPY", + GASPRICE: "GASPRICE", + + // 0x40 range - block operations + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + EXTCODESIZE: "EXTCODESIZE", + EXTCODECOPY: "EXTCODECOPY", + + // 0x50 range - 'storage' and execution + POP: "POP", + //DUP: "DUP", + //SWAP: "SWAP", + MLOAD: "MLOAD", + MSTORE: "MSTORE", + MSTORE8: "MSTORE8", + SLOAD: "SLOAD", + SSTORE: "SSTORE", + JUMP: "JUMP", + JUMPI: "JUMPI", + PC: "PC", + MSIZE: "MSIZE", + GAS: "GAS", + JUMPDEST: "JUMPDEST", + + // 0x60 range - push + PUSH1: "PUSH1", + PUSH2: "PUSH2", + PUSH3: "PUSH3", + PUSH4: "PUSH4", + PUSH5: "PUSH5", + PUSH6: "PUSH6", + PUSH7: "PUSH7", + PUSH8: "PUSH8", + PUSH9: "PUSH9", + PUSH10: "PUSH10", + PUSH11: "PUSH11", + PUSH12: "PUSH12", + PUSH13: "PUSH13", + PUSH14: "PUSH14", + PUSH15: "PUSH15", + PUSH16: "PUSH16", + PUSH17: "PUSH17", + PUSH18: "PUSH18", + PUSH19: "PUSH19", + PUSH20: "PUSH20", + PUSH21: "PUSH21", + PUSH22: "PUSH22", + PUSH23: "PUSH23", + PUSH24: "PUSH24", + PUSH25: "PUSH25", + PUSH26: "PUSH26", + PUSH27: "PUSH27", + PUSH28: "PUSH28", + PUSH29: "PUSH29", + PUSH30: "PUSH30", + PUSH31: "PUSH31", + PUSH32: "PUSH32", + + DUP1: "DUP1", + DUP2: "DUP2", + DUP3: "DUP3", + DUP4: "DUP4", + DUP5: "DUP5", + DUP6: "DUP6", + DUP7: "DUP7", + DUP8: "DUP8", + DUP9: "DUP9", + DUP10: "DUP10", + DUP11: "DUP11", + DUP12: "DUP12", + DUP13: "DUP13", + DUP14: "DUP14", + DUP15: "DUP15", + DUP16: "DUP16", + + SWAP1: "SWAP1", + SWAP2: "SWAP2", + SWAP3: "SWAP3", + SWAP4: "SWAP4", + SWAP5: "SWAP5", + SWAP6: "SWAP6", + SWAP7: "SWAP7", + SWAP8: "SWAP8", + SWAP9: "SWAP9", + SWAP10: "SWAP10", + SWAP11: "SWAP11", + SWAP12: "SWAP12", + SWAP13: "SWAP13", + SWAP14: "SWAP14", + SWAP15: "SWAP15", + SWAP16: "SWAP16", + LOG0: "LOG0", + LOG1: "LOG1", + LOG2: "LOG2", + LOG3: "LOG3", + LOG4: "LOG4", + + // 0xf0 range + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + CALLCODE: "CALLCODE", + DELEGATECALL: "DELEGATECALL", + SELFDESTRUCT: "SELFDESTRUCT", + + PUSH: "PUSH", + DUP: "DUP", + SWAP: "SWAP", +} + +func (o OpCode) String() string { + str := opCodeToString[o] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode 0x%x", int(o)) + } + + return str +} + +var stringToOp = map[string]OpCode{ + "STOP": STOP, + "ADD": ADD, + "MUL": MUL, + "SUB": SUB, + "DIV": DIV, + "SDIV": SDIV, + "MOD": MOD, + "SMOD": SMOD, + "EXP": EXP, + "NOT": NOT, + "LT": LT, + "GT": GT, + "SLT": SLT, + "SGT": SGT, + "EQ": EQ, + "ISZERO": ISZERO, + "SIGNEXTEND": SIGNEXTEND, + "AND": AND, + "OR": OR, + "XOR": XOR, + "BYTE": BYTE, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "SHA3": SHA3, + "ADDRESS": ADDRESS, + "BALANCE": BALANCE, + "ORIGIN": ORIGIN, + "CALLER": CALLER, + "CALLVALUE": CALLVALUE, + "CALLDATALOAD": CALLDATALOAD, + "CALLDATASIZE": CALLDATASIZE, + "CALLDATACOPY": CALLDATACOPY, + "DELEGATECALL": DELEGATECALL, + "CODESIZE": CODESIZE, + "CODECOPY": CODECOPY, + "GASPRICE": GASPRICE, + "BLOCKHASH": BLOCKHASH, + "COINBASE": COINBASE, + "TIMESTAMP": TIMESTAMP, + "NUMBER": NUMBER, + "DIFFICULTY": DIFFICULTY, + "GASLIMIT": GASLIMIT, + "EXTCODESIZE": EXTCODESIZE, + "EXTCODECOPY": EXTCODECOPY, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "SLOAD": SLOAD, + "SSTORE": SSTORE, + "JUMP": JUMP, + "JUMPI": JUMPI, + "PC": PC, + "MSIZE": MSIZE, + "GAS": GAS, + "JUMPDEST": JUMPDEST, + "PUSH1": PUSH1, + "PUSH2": PUSH2, + "PUSH3": PUSH3, + "PUSH4": PUSH4, + "PUSH5": PUSH5, + "PUSH6": PUSH6, + "PUSH7": PUSH7, + "PUSH8": PUSH8, + "PUSH9": PUSH9, + "PUSH10": PUSH10, + "PUSH11": PUSH11, + "PUSH12": PUSH12, + "PUSH13": PUSH13, + "PUSH14": PUSH14, + "PUSH15": PUSH15, + "PUSH16": PUSH16, + "PUSH17": PUSH17, + "PUSH18": PUSH18, + "PUSH19": PUSH19, + "PUSH20": PUSH20, + "PUSH21": PUSH21, + "PUSH22": PUSH22, + "PUSH23": PUSH23, + "PUSH24": PUSH24, + "PUSH25": PUSH25, + "PUSH26": PUSH26, + "PUSH27": PUSH27, + "PUSH28": PUSH28, + "PUSH29": PUSH29, + "PUSH30": PUSH30, + "PUSH31": PUSH31, + "PUSH32": PUSH32, + "DUP1": DUP1, + "DUP2": DUP2, + "DUP3": DUP3, + "DUP4": DUP4, + "DUP5": DUP5, + "DUP6": DUP6, + "DUP7": DUP7, + "DUP8": DUP8, + "DUP9": DUP9, + "DUP10": DUP10, + "DUP11": DUP11, + "DUP12": DUP12, + "DUP13": DUP13, + "DUP14": DUP14, + "DUP15": DUP15, + "DUP16": DUP16, + "SWAP1": SWAP1, + "SWAP2": SWAP2, + "SWAP3": SWAP3, + "SWAP4": SWAP4, + "SWAP5": SWAP5, + "SWAP6": SWAP6, + "SWAP7": SWAP7, + "SWAP8": SWAP8, + "SWAP9": SWAP9, + "SWAP10": SWAP10, + "SWAP11": SWAP11, + "SWAP12": SWAP12, + "SWAP13": SWAP13, + "SWAP14": SWAP14, + "SWAP15": SWAP15, + "SWAP16": SWAP16, + "LOG0": LOG0, + "LOG1": LOG1, + "LOG2": LOG2, + "LOG3": LOG3, + "LOG4": LOG4, + "CREATE": CREATE, + "CALL": CALL, + "RETURN": RETURN, + "CALLCODE": CALLCODE, + "SELFDESTRUCT": SELFDESTRUCT, +} + +func StringToOp(str string) OpCode { + return stringToOp[str] +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/doc.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/doc.go new file mode 100644 index 0000000..a3b464a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/doc.go @@ -0,0 +1,18 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package runtime provides a basic execution model for executing EVM code. +package runtime diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go new file mode 100644 index 0000000..9aa88e6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/env.go @@ -0,0 +1,44 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package runtime + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" +) + +func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM { + context := vm.Context{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: func(uint64) common.Hash { return common.Hash{} }, + + Origin: cfg.Origin, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: new(big.Int).SetUint64(cfg.GasLimit), + GasPrice: new(big.Int), + } + + return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/fuzz.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/fuzz.go new file mode 100644 index 0000000..de5b0f4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/fuzz.go @@ -0,0 +1,20 @@ +// +build gofuzz + +package runtime + +// Fuzz is the basic entry point for the go-fuzz tool +// +// This returns 1 for valid parsable/runable code, 0 +// for invalid opcode. +func Fuzz(input []byte) int { + _, _, err := Execute(input, input, &Config{ + GasLimit: 3000000, + }) + + // invalid opcode + if err != nil && len(err.Error()) > 6 && string(err.Error()[:7]) == "invalid" { + return 0 + } + + return 1 +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go new file mode 100644 index 0000000..9426562 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime.go @@ -0,0 +1,174 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package runtime + +import ( + "math" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +// Config is a basic type specifying certain configuration flags for running +// the EVM. +type Config struct { + ChainConfig *params.ChainConfig + Difficulty *big.Int + Origin common.Address + Coinbase common.Address + BlockNumber *big.Int + Time *big.Int + GasLimit uint64 + GasPrice *big.Int + Value *big.Int + DisableJit bool // "disable" so it's enabled by default + Debug bool + EVMConfig vm.Config + + State *state.StateDB + GetHashFn func(n uint64) common.Hash +} + +// sets defaults on the config +func setDefaults(cfg *Config) { + if cfg.ChainConfig == nil { + cfg.ChainConfig = ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + } + } + + if cfg.Difficulty == nil { + cfg.Difficulty = new(big.Int) + } + if cfg.Time == nil { + cfg.Time = big.NewInt(time.Now().Unix()) + } + if cfg.GasLimit == 0 { + cfg.GasLimit = math.MaxUint64 + } + if cfg.GasPrice == nil { + cfg.GasPrice = new(big.Int) + } + if cfg.Value == nil { + cfg.Value = new(big.Int) + } + if cfg.BlockNumber == nil { + cfg.BlockNumber = new(big.Int) + } + if cfg.GetHashFn == nil { + cfg.GetHashFn = func(n uint64) common.Hash { + return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) + } + } +} + +// Execute executes the code using the input as call data during the execution. +// It returns the EVM's return value, the new state and an error if it failed. +// +// Executes sets up a in memory, temporarily, environment for the execution of +// the given code. It enabled the JIT by default and make sure that it's restored +// to it's original state afterwards. +func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { + if cfg == nil { + cfg = new(Config) + } + setDefaults(cfg) + + if cfg.State == nil { + db, _ := ethdb.NewMemDatabase() + cfg.State, _ = state.New(common.Hash{}, db) + } + var ( + address = common.StringToAddress("contract") + vmenv = NewEnv(cfg, cfg.State) + sender = vm.AccountRef(cfg.Origin) + ) + cfg.State.CreateAccount(address) + // set the receiver's (the executing contract) code for execution. + cfg.State.SetCode(address, code) + // Call the code with the given configuration. + ret, _, err := vmenv.Call( + sender, + common.StringToAddress("contract"), + input, + cfg.GasLimit, + cfg.Value, + ) + + return ret, cfg.State, err +} + +// Create executes the code using the EVM create method +func Create(input []byte, cfg *Config) ([]byte, common.Address, error) { + if cfg == nil { + cfg = new(Config) + } + setDefaults(cfg) + + if cfg.State == nil { + db, _ := ethdb.NewMemDatabase() + cfg.State, _ = state.New(common.Hash{}, db) + } + var ( + vmenv = NewEnv(cfg, cfg.State) + sender = vm.AccountRef(cfg.Origin) + ) + + // Call the code with the given configuration. + code, address, _, err := vmenv.Create( + sender, + input, + cfg.GasLimit, + cfg.Value, + ) + return code, address, err +} + +// Call executes the code given by the contract's address. It will return the +// EVM's return value or an error if it failed. +// +// Call, unlike Execute, requires a config and also requires the State field to +// be set. +func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { + setDefaults(cfg) + + vmenv := NewEnv(cfg, cfg.State) + + sender := cfg.State.GetOrNewStateObject(cfg.Origin) + // Call the code with the given configuration. + ret, _, err := vmenv.Call( + sender, + address, + input, + cfg.GasLimit, + cfg.Value, + ) + + return ret, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_example_test.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_example_test.go new file mode 100644 index 0000000..b7d0ddc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_example_test.go @@ -0,0 +1,34 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package runtime_test + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm/runtime" +) + +func ExampleExecute() { + ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + if err != nil { + fmt.Println(err) + } + fmt.Println(ret) + // Output: + // [96 96 96 64 82 96 8 86 91 0] +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_test.go b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_test.go new file mode 100644 index 0000000..8ad74a8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/runtime/runtime_test.go @@ -0,0 +1,151 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package runtime + +import ( + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" +) + +func TestDefaults(t *testing.T) { + cfg := new(Config) + setDefaults(cfg) + + if cfg.Difficulty == nil { + t.Error("expected difficulty to be non nil") + } + + if cfg.Time == nil { + t.Error("expected time to be non nil") + } + if cfg.GasLimit == 0 { + t.Error("didn't expect gaslimit to be zero") + } + if cfg.GasPrice == nil { + t.Error("expected time to be non nil") + } + if cfg.Value == nil { + t.Error("expected time to be non nil") + } + if cfg.GetHashFn == nil { + t.Error("expected time to be non nil") + } + if cfg.BlockNumber == nil { + t.Error("expected block number to be non nil") + } +} + +func TestEVM(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("crashed with: %v", r) + } + }() + + Execute([]byte{ + byte(vm.DIFFICULTY), + byte(vm.TIMESTAMP), + byte(vm.GASLIMIT), + byte(vm.PUSH1), + byte(vm.ORIGIN), + byte(vm.BLOCKHASH), + byte(vm.COINBASE), + }, nil, nil) +} + +func TestExecute(t *testing.T) { + ret, _, err := Execute([]byte{ + byte(vm.PUSH1), 10, + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + byte(vm.PUSH1), 32, + byte(vm.PUSH1), 0, + byte(vm.RETURN), + }, nil, nil) + if err != nil { + t.Fatal("didn't expect error", err) + } + + num := common.BytesToBig(ret) + if num.Cmp(big.NewInt(10)) != 0 { + t.Error("Expected 10, got", num) + } +} + +func TestCall(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + state, _ := state.New(common.Hash{}, db) + address := common.HexToAddress("0x0a") + state.SetCode(address, []byte{ + byte(vm.PUSH1), 10, + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + byte(vm.PUSH1), 32, + byte(vm.PUSH1), 0, + byte(vm.RETURN), + }) + + ret, err := Call(address, nil, &Config{State: state}) + if err != nil { + t.Fatal("didn't expect error", err) + } + + num := common.BytesToBig(ret) + if num.Cmp(big.NewInt(10)) != 0 { + t.Error("Expected 10, got", num) + } +} + +func BenchmarkCall(b *testing.B) { + var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` + + var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056") + + abi, err := abi.JSON(strings.NewReader(definition)) + if err != nil { + b.Fatal(err) + } + + cpurchase, err := abi.Pack("confirmPurchase") + if err != nil { + b.Fatal(err) + } + creceived, err := abi.Pack("confirmReceived") + if err != nil { + b.Fatal(err) + } + refund, err := abi.Pack("refund") + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < 400; j++ { + Execute(code, cpurchase, nil) + Execute(code, creceived, nil) + Execute(code, refund, nil) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go new file mode 100644 index 0000000..2d1b7bb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/stack.go @@ -0,0 +1,93 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "fmt" + "math/big" +) + +// stack is an object for basic stack operations. Items popped to the stack are +// expected to be changed and modified. stack does not take care of adding newly +// initialised objects. +type Stack struct { + data []*big.Int +} + +func newstack() *Stack { + return &Stack{} +} + +func (st *Stack) Data() []*big.Int { + return st.data +} + +func (st *Stack) push(d *big.Int) { + // NOTE push limit (1024) is checked in baseCheck + //stackItem := new(big.Int).Set(d) + //st.data = append(st.data, stackItem) + st.data = append(st.data, d) +} +func (st *Stack) pushN(ds ...*big.Int) { + st.data = append(st.data, ds...) +} + +func (st *Stack) pop() (ret *big.Int) { + ret = st.data[len(st.data)-1] + st.data = st.data[:len(st.data)-1] + return +} + +func (st *Stack) len() int { + return len(st.data) +} + +func (st *Stack) swap(n int) { + st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] +} + +func (st *Stack) dup(n int) { + st.push(new(big.Int).Set(st.data[st.len()-n])) +} + +func (st *Stack) peek() *big.Int { + return st.data[st.len()-1] +} + +// Back returns the n'th item in stack +func (st *Stack) Back(n int) *big.Int { + return st.data[st.len()-n-1] +} + +func (st *Stack) require(n int) error { + if st.len() < n { + return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n) + } + return nil +} + +func (st *Stack) Print() { + fmt.Println("### stack ###") + if len(st.data) > 0 { + for i, val := range st.data { + fmt.Printf("%-3d %v\n", i, val) + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("#############") +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go new file mode 100644 index 0000000..ddc41fe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/stack_table.go @@ -0,0 +1,28 @@ +package vm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/params" +) + +func makeStackFunc(pop, push int) stackValidationFunc { + return func(stack *Stack) error { + if err := stack.require(pop); err != nil { + return err + } + + if stack.len()+push-pop > int(params.StackLimit) { + return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit) + } + return nil + } +} + +func makeDupStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, n+1) +} + +func makeSwapStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, n) +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit.go new file mode 100644 index 0000000..eb3acfb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit.go @@ -0,0 +1,389 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build evmjit + +package vm + +/* + +void* evmjit_create(); +int evmjit_run(void* _jit, void* _data, void* _env); +void evmjit_destroy(void* _jit); + +// Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib +// More: https://github.com/ethereum/evmjit +#cgo LDFLAGS: -levmjit +*/ +import "C" + +/* +import ( + "bytes" + "errors" + "fmt" + "math/big" + "unsafe" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +type JitVm struct { + env EVM + me ContextRef + callerAddr []byte + price *big.Int + data RuntimeData +} + +type i256 [32]byte + +type RuntimeData struct { + gas int64 + gasPrice int64 + callData *byte + callDataSize uint64 + address i256 + caller i256 + origin i256 + callValue i256 + coinBase i256 + difficulty i256 + gasLimit i256 + number uint64 + timestamp int64 + code *byte + codeSize uint64 + codeHash i256 +} + +func hash2llvm(h []byte) i256 { + var m i256 + copy(m[len(m)-len(h):], h) // right aligned copy + return m +} + +func llvm2hash(m *i256) []byte { + return C.GoBytes(unsafe.Pointer(m), C.int(len(m))) +} + +func llvm2hashRef(m *i256) []byte { + return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)] +} + +func address2llvm(addr []byte) i256 { + n := hash2llvm(addr) + bswap(&n) + return n +} + +// bswap swap bytes of the 256-bit integer on LLVM side +// TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations +func bswap(m *i256) *i256 { + for i, l := 0, len(m); i < l/2; i++ { + m[i], m[l-i-1] = m[l-i-1], m[i] + } + return m +} + +func trim(m []byte) []byte { + skip := 0 + for i := 0; i < len(m); i++ { + if m[i] == 0 { + skip++ + } else { + break + } + } + return m[skip:] +} + +func getDataPtr(m []byte) *byte { + var p *byte + if len(m) > 0 { + p = &m[0] + } + return p +} + +func big2llvm(n *big.Int) i256 { + m := hash2llvm(n.Bytes()) + bswap(&m) + return m +} + +func llvm2big(m *i256) *big.Int { + n := big.NewInt(0) + for i := 0; i < len(m); i++ { + b := big.NewInt(int64(m[i])) + b.Lsh(b, uint(i)*8) + n.Add(n, b) + } + return n +} + +// llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC) +// User must ensure that referenced memory is available to Go until the data is copied or not needed any more +func llvm2bytesRef(data *byte, length uint64) []byte { + if length == 0 { + return nil + } + if data == nil { + panic("Unexpected nil data pointer") + } + return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length] +} + +func untested(condition bool, message string) { + if condition { + panic("Condition `" + message + "` tested. Remove assert.") + } +} + +func assert(condition bool, message string) { + if !condition { + panic("Assert `" + message + "` failed!") + } +} + +func NewJitVm(env EVM) *JitVm { + return &JitVm{env: env} +} + +func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) { + // TODO: depth is increased but never checked by VM. VM should not know about it at all. + self.env.SetDepth(self.env.Depth() + 1) + + // TODO: Move it to Env.Call() or sth + if Precompiled[string(me.Address())] != nil { + // if it's address of precompiled contract + // fallback to standard VM + stdVm := New(self.env) + return stdVm.Run(me, caller, code, value, gas, price, callData) + } + + if self.me != nil { + panic("JitVm.Run() can be called only once per JitVm instance") + } + + self.me = me + self.callerAddr = caller.Address() + self.price = price + + self.data.gas = gas.Int64() + self.data.gasPrice = price.Int64() + self.data.callData = getDataPtr(callData) + self.data.callDataSize = uint64(len(callData)) + self.data.address = address2llvm(self.me.Address()) + self.data.caller = address2llvm(caller.Address()) + self.data.origin = address2llvm(self.env.Origin()) + self.data.callValue = big2llvm(value) + self.data.coinBase = address2llvm(self.env.Coinbase()) + self.data.difficulty = big2llvm(self.env.Difficulty()) + self.data.gasLimit = big2llvm(self.env.GasLimit()) + self.data.number = self.env.BlockNumber().Uint64() + self.data.timestamp = self.env.Time() + self.data.code = getDataPtr(code) + self.data.codeSize = uint64(len(code)) + self.data.codeHash = hash2llvm(crypto.Keccak256(code)) // TODO: Get already computed hash? + + jit := C.evmjit_create() + retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self)) + + if retCode < 0 { + err = errors.New("OOG from JIT") + gas.SetInt64(0) // Set gas to 0, JIT does not bother + } else { + gas.SetInt64(self.data.gas) + if retCode == 1 { // RETURN + ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize)) + } else if retCode == 2 { // SUICIDE + // TODO: Suicide support logic should be moved to Env to be shared by VM implementations + state := self.Env().State() + receiverAddr := llvm2hashRef(bswap(&self.data.address)) + receiver := state.GetOrNewStateObject(receiverAddr) + balance := state.GetBalance(me.Address()) + receiver.AddBalance(balance) + state.Delete(me.Address()) + } + } + + C.evmjit_destroy(jit) + return +} + +func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine { + return self +} + +func (self *JitVm) Endl() VirtualMachine { + return self +} + +func (self *JitVm) Env() EVM { + return self.env +} + +//export env_sha3 +func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) { + data := llvm2bytesRef(dataPtr, length) + hash := crypto.Keccak256(data) + result := (*i256)(resultPtr) + *result = hash2llvm(hash) +} + +//export env_sstore +func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) { + vm := (*JitVm)(vmPtr) + index := llvm2hash(bswap((*i256)(indexPtr))) + value := llvm2hash(bswap((*i256)(valuePtr))) + value = trim(value) + if len(value) == 0 { + prevValue := vm.env.State().GetState(vm.me.Address(), index) + if len(prevValue) != 0 { + vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund) + } + } + + vm.env.State().SetState(vm.me.Address(), index, value) +} + +//export env_sload +func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) { + vm := (*JitVm)(vmPtr) + index := llvm2hash(bswap((*i256)(indexPtr))) + value := vm.env.State().GetState(vm.me.Address(), index) + result := (*i256)(resultPtr) + *result = hash2llvm(value) + bswap(result) +} + +//export env_balance +func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + addr := llvm2hash((*i256)(_addr)) + balance := vm.Env().State().GetBalance(addr) + result := (*i256)(_result) + *result = big2llvm(balance) +} + +//export env_blockhash +func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + number := llvm2big((*i256)(_number)) + result := (*i256)(_result) + + currNumber := vm.Env().BlockNumber() + limit := big.NewInt(0).Sub(currNumber, big.NewInt(256)) + if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 { + hash := vm.Env().GetHash(uint64(number.Int64())) + *result = hash2llvm(hash) + } else { + *result = i256{} + } +} + +//export env_call +func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool { + vm := (*JitVm)(_vm) + + //fmt.Printf("env_call (depth %d)\n", vm.Env().Depth()) + + defer func() { + if r := recover(); r != nil { + fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r) + } + }() + + balance := vm.Env().State().GetBalance(vm.me.Address()) + value := llvm2big((*i256)(_value)) + + if balance.Cmp(value) >= 0 { + receiveAddr := llvm2hash((*i256)(_receiveAddr)) + inData := C.GoBytes(inDataPtr, C.int(inDataLen)) + outData := llvm2bytesRef(outDataPtr, outDataLen) + codeAddr := llvm2hash((*i256)(_codeAddr)) + gas := big.NewInt(*_gas) + var out []byte + var err error + if bytes.Equal(codeAddr, receiveAddr) { + out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value) + } else { + out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value) + } + *_gas = gas.Int64() + if err == nil { + copy(outData, out) + return true + } + } + + return false +} + +//export env_create +func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) { + vm := (*JitVm)(_vm) + + value := llvm2big((*i256)(_value)) + initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance + result := (*i256)(_result) + *result = i256{} + + gas := big.NewInt(*_gas) + ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value) + if suberr == nil { + dataGas := big.NewInt(int64(len(ret))) // TODO: Not the best design. env.Create can do it, it has the reference to gas counter + dataGas.Mul(dataGas, params.CreateDataGas) + gas.Sub(gas, dataGas) + *result = hash2llvm(ref.Address()) + } + *_gas = gas.Int64() +} + +//export env_log +func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) { + vm := (*JitVm)(_vm) + + data := C.GoBytes(dataPtr, C.int(dataLen)) + + topics := make([][]byte, 0, 4) + if _topic1 != nil { + topics = append(topics, llvm2hash((*i256)(_topic1))) + } + if _topic2 != nil { + topics = append(topics, llvm2hash((*i256)(_topic2))) + } + if _topic3 != nil { + topics = append(topics, llvm2hash((*i256)(_topic3))) + } + if _topic4 != nil { + topics = append(topics, llvm2hash((*i256)(_topic4))) + } + + vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64())) +} + +//export env_extcode +func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte { + vm := (*JitVm)(_vm) + addr := llvm2hash((*i256)(_addr)) + code := vm.Env().State().GetCode(addr) + *o_size = uint64(len(code)) + return getDataPtr(code) +}*/ diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go new file mode 100644 index 0000000..44b60ab --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/vm_jit_fake.go @@ -0,0 +1,19 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !evmjit + +package vm diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go new file mode 100644 index 0000000..9d67d82 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go @@ -0,0 +1,169 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package crypto + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "errors" + "io" + "io/ioutil" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2)) +) + +func Keccak256(data ...[]byte) []byte { + d := sha3.NewKeccak256() + for _, b := range data { + d.Write(b) + } + return d.Sum(nil) +} + +func Keccak256Hash(data ...[]byte) (h common.Hash) { + d := sha3.NewKeccak256() + for _, b := range data { + d.Write(b) + } + d.Sum(h[:0]) + return h +} + +// Deprecated: For backward compatibility as other packages depend on these +func Sha3Hash(data ...[]byte) common.Hash { return Keccak256Hash(data...) } + +// Creates an ethereum address given the bytes and the nonce +func CreateAddress(b common.Address, nonce uint64) common.Address { + data, _ := rlp.EncodeToBytes([]interface{}{b, nonce}) + return common.BytesToAddress(Keccak256(data)[12:]) +} + +// ToECDSA creates a private key with the given D value. +func ToECDSA(prv []byte) *ecdsa.PrivateKey { + if len(prv) == 0 { + return nil + } + + priv := new(ecdsa.PrivateKey) + priv.PublicKey.Curve = S256() + priv.D = common.BigD(prv) + priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(prv) + return priv +} + +func FromECDSA(prv *ecdsa.PrivateKey) []byte { + if prv == nil { + return nil + } + return prv.D.Bytes() +} + +func ToECDSAPub(pub []byte) *ecdsa.PublicKey { + if len(pub) == 0 { + return nil + } + x, y := elliptic.Unmarshal(S256(), pub) + return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y} +} + +func FromECDSAPub(pub *ecdsa.PublicKey) []byte { + if pub == nil || pub.X == nil || pub.Y == nil { + return nil + } + return elliptic.Marshal(S256(), pub.X, pub.Y) +} + +// HexToECDSA parses a secp256k1 private key. +func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) { + b, err := hex.DecodeString(hexkey) + if err != nil { + return nil, errors.New("invalid hex string") + } + if len(b) != 32 { + return nil, errors.New("invalid length, need 256 bits") + } + return ToECDSA(b), nil +} + +// LoadECDSA loads a secp256k1 private key from the given file. +// The key data is expected to be hex-encoded. +func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { + buf := make([]byte, 64) + fd, err := os.Open(file) + if err != nil { + return nil, err + } + defer fd.Close() + if _, err := io.ReadFull(fd, buf); err != nil { + return nil, err + } + + key, err := hex.DecodeString(string(buf)) + if err != nil { + return nil, err + } + + return ToECDSA(key), nil +} + +// SaveECDSA saves a secp256k1 private key to the given file with +// restrictive permissions. The key data is saved hex-encoded. +func SaveECDSA(file string, key *ecdsa.PrivateKey) error { + k := hex.EncodeToString(FromECDSA(key)) + return ioutil.WriteFile(file, []byte(k), 0600) +} + +func GenerateKey() (*ecdsa.PrivateKey, error) { + return ecdsa.GenerateKey(S256(), rand.Reader) +} + +// ValidateSignatureValues verifies whether the signature values are valid with +// the given chain rules. The v value is assumed to be either 0 or 1. +func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool { + if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 { + return false + } + // reject upper range of s values (ECDSA malleability) + // see discussion in secp256k1/libsecp256k1/include/secp256k1.h + if homestead && s.Cmp(secp256k1_halfN) > 0 { + return false + } + // Frontier: allow s to be in full N range + return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1) +} + +func PubkeyToAddress(p ecdsa.PublicKey) common.Address { + pubBytes := FromECDSAPub(&p) + return common.BytesToAddress(Keccak256(pubBytes[1:])[12:]) +} + +func zeroBytes(bytes []byte) { + for i := range bytes { + bytes[i] = 0 + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/crypto_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/crypto_test.go new file mode 100644 index 0000000..e518ac2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/crypto_test.go @@ -0,0 +1,218 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package crypto + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "fmt" + "io/ioutil" + "math/big" + "os" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" +) + +var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791" +var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032" + +// These tests are sanity checks. +// They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256 +// and that the sha3 library uses keccak-f permutation. +func TestSha3Hash(t *testing.T) { + msg := []byte("abc") + exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45") + checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp) +} + +func BenchmarkSha3(b *testing.B) { + a := []byte("hello world") + amount := 1000000 + start := time.Now() + for i := 0; i < amount; i++ { + Keccak256(a) + } + + fmt.Println(amount, ":", time.Since(start)) +} + +func TestSign(t *testing.T) { + key, _ := HexToECDSA(testPrivHex) + addr := common.HexToAddress(testAddrHex) + + msg := Keccak256([]byte("foo")) + sig, err := Sign(msg, key) + if err != nil { + t.Errorf("Sign error: %s", err) + } + recoveredPub, err := Ecrecover(msg, sig) + if err != nil { + t.Errorf("ECRecover error: %s", err) + } + pubKey := ToECDSAPub(recoveredPub) + recoveredAddr := PubkeyToAddress(*pubKey) + if addr != recoveredAddr { + t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr) + } + + // should be equal to SigToPub + recoveredPub2, err := SigToPub(msg, sig) + if err != nil { + t.Errorf("ECRecover error: %s", err) + } + recoveredAddr2 := PubkeyToAddress(*recoveredPub2) + if addr != recoveredAddr2 { + t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr2) + } +} + +func TestInvalidSign(t *testing.T) { + if _, err := Sign(make([]byte, 1), nil); err == nil { + t.Errorf("expected sign with hash 1 byte to error") + } + if _, err := Sign(make([]byte, 33), nil); err == nil { + t.Errorf("expected sign with hash 33 byte to error") + } +} + +func TestNewContractAddress(t *testing.T) { + key, _ := HexToECDSA(testPrivHex) + addr := common.HexToAddress(testAddrHex) + genAddr := PubkeyToAddress(key.PublicKey) + // sanity check before using addr to create contract address + checkAddr(t, genAddr, addr) + + caddr0 := CreateAddress(addr, 0) + caddr1 := CreateAddress(addr, 1) + caddr2 := CreateAddress(addr, 2) + checkAddr(t, common.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0) + checkAddr(t, common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1) + checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2) +} + +func TestLoadECDSAFile(t *testing.T) { + keyBytes := common.FromHex(testPrivHex) + fileName0 := "test_key0" + fileName1 := "test_key1" + checkKey := func(k *ecdsa.PrivateKey) { + checkAddr(t, PubkeyToAddress(k.PublicKey), common.HexToAddress(testAddrHex)) + loadedKeyBytes := FromECDSA(k) + if !bytes.Equal(loadedKeyBytes, keyBytes) { + t.Fatalf("private key mismatch: want: %x have: %x", keyBytes, loadedKeyBytes) + } + } + + ioutil.WriteFile(fileName0, []byte(testPrivHex), 0600) + defer os.Remove(fileName0) + + key0, err := LoadECDSA(fileName0) + if err != nil { + t.Fatal(err) + } + checkKey(key0) + + // again, this time with SaveECDSA instead of manual save: + err = SaveECDSA(fileName1, key0) + if err != nil { + t.Fatal(err) + } + defer os.Remove(fileName1) + + key1, err := LoadECDSA(fileName1) + if err != nil { + t.Fatal(err) + } + checkKey(key1) +} + +func TestValidateSignatureValues(t *testing.T) { + check := func(expected bool, v byte, r, s *big.Int) { + if ValidateSignatureValues(v, r, s, false) != expected { + t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected) + } + } + minusOne := big.NewInt(-1) + one := common.Big1 + zero := common.Big0 + secp256k1nMinus1 := new(big.Int).Sub(secp256k1_N, common.Big1) + + // correct v,r,s + check(true, 0, one, one) + check(true, 1, one, one) + // incorrect v, correct r,s, + check(false, 2, one, one) + check(false, 3, one, one) + + // incorrect v, combinations of incorrect/correct r,s at lower limit + check(false, 2, zero, zero) + check(false, 2, zero, one) + check(false, 2, one, zero) + check(false, 2, one, one) + + // correct v for any combination of incorrect r,s + check(false, 0, zero, zero) + check(false, 0, zero, one) + check(false, 0, one, zero) + + check(false, 1, zero, zero) + check(false, 1, zero, one) + check(false, 1, one, zero) + + // correct sig with max r,s + check(true, 0, secp256k1nMinus1, secp256k1nMinus1) + // correct v, combinations of incorrect r,s at upper limit + check(false, 0, secp256k1_N, secp256k1nMinus1) + check(false, 0, secp256k1nMinus1, secp256k1_N) + check(false, 0, secp256k1_N, secp256k1_N) + + // current callers ensures r,s cannot be negative, but let's test for that too + // as crypto package could be used stand-alone + check(false, 0, minusOne, one) + check(false, 0, one, minusOne) +} + +func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) { + sum := f(msg) + if !bytes.Equal(exp, sum) { + t.Fatalf("hash %s mismatch: want: %x have: %x", name, exp, sum) + } +} + +func checkAddr(t *testing.T, addr0, addr1 common.Address) { + if addr0 != addr1 { + t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1) + } +} + +// test to help Python team with integration of libsecp256k1 +// skip but keep it after they are done +func TestPythonIntegration(t *testing.T) { + kh := "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032" + k0, _ := HexToECDSA(kh) + + msg0 := Keccak256([]byte("foo")) + sig0, _ := Sign(msg0, k0) + + msg1 := common.FromHex("00000000000000000000000000000000") + sig1, _ := Sign(msg0, k0) + + t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0) + t.Logf("msg: %x, privkey: %s sig: %x\n", msg1, kh, sig1) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/.gitignore b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/.gitignore new file mode 100644 index 0000000..802b674 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +*~ diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/LICENSE b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/LICENSE new file mode 100644 index 0000000..e1ed19a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2013 Kyle Isom +Copyright (c) 2012 The Go Authors. 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/README b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/README new file mode 100644 index 0000000..2650c7b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/README @@ -0,0 +1,94 @@ +# NOTE + +This implementation is direct fork of Kylom's implementation. I claim no authorship over this code apart from some minor modifications. +Please be aware this code **has not yet been reviewed**. + +ecies implements the Elliptic Curve Integrated Encryption Scheme. + +The package is designed to be compliant with the appropriate NIST +standards, and therefore doesn't support the full SEC 1 algorithm set. + + +STATUS: + +ecies should be ready for use. The ASN.1 support is only complete so +far as to supported the listed algorithms before. + + +CAVEATS + +1. CMAC support is currently not present. + + +SUPPORTED ALGORITHMS + + SYMMETRIC CIPHERS HASH FUNCTIONS + AES128 SHA-1 + AES192 SHA-224 + AES256 SHA-256 + SHA-384 + ELLIPTIC CURVE SHA-512 + P256 + P384 KEY DERIVATION FUNCTION + P521 NIST SP 800-65a Concatenation KDF + +Curve P224 isn't supported because it does not provide a minimum security +level of AES128 with HMAC-SHA1. According to NIST SP 800-57, the security +level of P224 is 112 bits of security. Symmetric ciphers use CTR-mode; +message tags are computed using HMAC- function. + + +CURVE SELECTION + +According to NIST SP 800-57, the following curves should be selected: + + +----------------+-------+ + | SYMMETRIC SIZE | CURVE | + +----------------+-------+ + | 128-bit | P256 | + +----------------+-------+ + | 192-bit | P384 | + +----------------+-------+ + | 256-bit | P521 | + +----------------+-------+ + + +TODO + +1. Look at serialising the parameters with the SEC 1 ASN.1 module. +2. Validate ASN.1 formats with SEC 1. + + +TEST VECTORS + +The only test vectors I've found so far date from 1993, predating AES +and including only 163-bit curves. Therefore, there are no published +test vectors to compare to. + + +LICENSE + +ecies is released under the same license as the Go source code. See the +LICENSE file for details. + + +REFERENCES + +* SEC (Standard for Efficient Cryptography) 1, version 2.0: Elliptic + Curve Cryptography; Certicom, May 2009. + http://www.secg.org/sec1-v2.pdf +* GEC (Guidelines for Efficient Cryptography) 2, version 0.3: Test + Vectors for SEC 1; Certicom, September 1999. + http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf +* NIST SP 800-56a: Recommendation for Pair-Wise Key Establishment Schemes + Using Discrete Logarithm Cryptography. National Institute of Standards + and Technology, May 2007. + http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf +* Suite B Implementer’s Guide to NIST SP 800-56A. National Security + Agency, July 28, 2009. + http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf +* NIST SP 800-57: Recommendation for Key Management – Part 1: General + (Revision 3). National Institute of Standards and Technology, July + 2012. + http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/asn1.go b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/asn1.go new file mode 100644 index 0000000..d3e77d8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/asn1.go @@ -0,0 +1,584 @@ +// Copyright (c) 2013 Kyle Isom +// Copyright (c) 2012 The Go Authors. 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 THE COPYRIGHT +// OWNER 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. + +package ecies + +import ( + "bytes" + "crypto" + "crypto/elliptic" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/asn1" + "encoding/pem" + "fmt" + "hash" + "math/big" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +var ( + secgScheme = []int{1, 3, 132, 1} + shaScheme = []int{2, 16, 840, 1, 101, 3, 4, 2} + ansiX962Scheme = []int{1, 2, 840, 10045} + x963Scheme = []int{1, 2, 840, 63, 0} +) + +var ErrInvalidPrivateKey = fmt.Errorf("ecies: invalid private key") + +func doScheme(base, v []int) asn1.ObjectIdentifier { + var oidInts asn1.ObjectIdentifier + oidInts = append(oidInts, base...) + return append(oidInts, v...) +} + +// curve OID code taken from crypto/x509, including +// - oidNameCurve* +// - namedCurveFromOID +// - oidFromNamedCurve +// RFC 5480, 2.1.1.1. Named Curve +// +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } +// +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// +// NB: secp256r1 is equivalent to prime256v1 +type secgNamedCurve asn1.ObjectIdentifier + +var ( + secgNamedCurveS256 = secgNamedCurve{1, 3, 132, 0, 10} + secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7} + secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34} + secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35} + rawCurveP256 = []byte{6, 8, 4, 2, 1, 3, 4, 7, 2, 2, 0, 6, 6, 1, 3, 1, 7} + rawCurveP384 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 4} + rawCurveP521 = []byte{6, 5, 4, 3, 1, 2, 9, 4, 0, 3, 5} +) + +func rawCurve(curve elliptic.Curve) []byte { + switch curve { + case elliptic.P256(): + return rawCurveP256 + case elliptic.P384(): + return rawCurveP384 + case elliptic.P521(): + return rawCurveP521 + default: + return nil + } +} + +func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool { + if len(curve) != len(curve2) { + return false + } + for i := range curve { + if curve[i] != curve2[i] { + return false + } + } + return true +} + +func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { + switch { + case curve.Equal(secgNamedCurveS256): + return ethcrypto.S256() + case curve.Equal(secgNamedCurveP256): + return elliptic.P256() + case curve.Equal(secgNamedCurveP384): + return elliptic.P384() + case curve.Equal(secgNamedCurveP521): + return elliptic.P521() + } + return nil +} + +func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) { + switch curve { + case elliptic.P256(): + return secgNamedCurveP256, true + case elliptic.P384(): + return secgNamedCurveP384, true + case elliptic.P521(): + return secgNamedCurveP521, true + case ethcrypto.S256(): + return secgNamedCurveS256, true + } + + return nil, false +} + +// asnAlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type asnAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.RawValue `asn1:"optional"` +} + +func (a asnAlgorithmIdentifier) Cmp(b asnAlgorithmIdentifier) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type asnHashFunction asnAlgorithmIdentifier + +var ( + oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} + oidSHA224 = doScheme(shaScheme, []int{4}) + oidSHA256 = doScheme(shaScheme, []int{1}) + oidSHA384 = doScheme(shaScheme, []int{2}) + oidSHA512 = doScheme(shaScheme, []int{3}) +) + +func hashFromOID(oid asn1.ObjectIdentifier) func() hash.Hash { + switch { + case oid.Equal(oidSHA1): + return sha1.New + case oid.Equal(oidSHA224): + return sha256.New224 + case oid.Equal(oidSHA256): + return sha256.New + case oid.Equal(oidSHA384): + return sha512.New384 + case oid.Equal(oidSHA512): + return sha512.New + } + return nil +} + +func oidFromHash(hash crypto.Hash) (asn1.ObjectIdentifier, bool) { + switch hash { + case crypto.SHA1: + return oidSHA1, true + case crypto.SHA224: + return oidSHA224, true + case crypto.SHA256: + return oidSHA256, true + case crypto.SHA384: + return oidSHA384, true + case crypto.SHA512: + return oidSHA512, true + default: + return nil, false + } +} + +var ( + asnAlgoSHA1 = asnHashFunction{ + Algorithm: oidSHA1, + } + asnAlgoSHA224 = asnHashFunction{ + Algorithm: oidSHA224, + } + asnAlgoSHA256 = asnHashFunction{ + Algorithm: oidSHA256, + } + asnAlgoSHA384 = asnHashFunction{ + Algorithm: oidSHA384, + } + asnAlgoSHA512 = asnHashFunction{ + Algorithm: oidSHA512, + } +) + +// type ASNasnSubjectPublicKeyInfo struct { +// +// } +// + +type asnSubjectPublicKeyInfo struct { + Algorithm asn1.ObjectIdentifier + PublicKey asn1.BitString + Supplements ecpksSupplements `asn1:"optional"` +} + +type asnECPKAlgorithms struct { + Type asn1.ObjectIdentifier +} + +var idPublicKeyType = doScheme(ansiX962Scheme, []int{2}) +var idEcPublicKey = doScheme(idPublicKeyType, []int{1}) +var idEcPublicKeySupplemented = doScheme(idPublicKeyType, []int{0}) + +func curveToRaw(curve elliptic.Curve) (rv asn1.RawValue, ok bool) { + switch curve { + case elliptic.P256(), elliptic.P384(), elliptic.P521(): + raw := rawCurve(curve) + return asn1.RawValue{ + Tag: 30, + Bytes: raw[2:], + FullBytes: raw, + }, true + default: + return rv, false + } +} + +func asnECPublicKeyType(curve elliptic.Curve) (algo asnAlgorithmIdentifier, ok bool) { + raw, ok := curveToRaw(curve) + if !ok { + return + } else { + return asnAlgorithmIdentifier{Algorithm: idEcPublicKey, + Parameters: raw}, true + } +} + +type asnECPrivKeyVer int + +var asnECPrivKeyVer1 asnECPrivKeyVer = 1 + +type asnPrivateKey struct { + Version asnECPrivKeyVer + Private []byte + Curve secgNamedCurve `asn1:"optional"` + Public asn1.BitString +} + +var asnECDH = doScheme(secgScheme, []int{12}) + +type asnECDHAlgorithm asnAlgorithmIdentifier + +var ( + dhSinglePass_stdDH_sha1kdf = asnECDHAlgorithm{ + Algorithm: doScheme(x963Scheme, []int{2}), + } + dhSinglePass_stdDH_sha256kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 1}), + } + dhSinglePass_stdDH_sha384kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 2}), + } + dhSinglePass_stdDH_sha224kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 0}), + } + dhSinglePass_stdDH_sha512kdf = asnECDHAlgorithm{ + Algorithm: doScheme(secgScheme, []int{11, 3}), + } +) + +func (a asnECDHAlgorithm) Cmp(b asnECDHAlgorithm) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +// asnNISTConcatenation is the only supported KDF at this time. +type asnKeyDerivationFunction asnAlgorithmIdentifier + +var asnNISTConcatenationKDF = asnKeyDerivationFunction{ + Algorithm: doScheme(secgScheme, []int{17, 1}), +} + +func (a asnKeyDerivationFunction) Cmp(b asnKeyDerivationFunction) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +var eciesRecommendedParameters = doScheme(secgScheme, []int{7}) +var eciesSpecifiedParameters = doScheme(secgScheme, []int{8}) + +type asnECIESParameters struct { + KDF asnKeyDerivationFunction `asn1:"optional"` + Sym asnSymmetricEncryption `asn1:"optional"` + MAC asnMessageAuthenticationCode `asn1:"optional"` +} + +type asnSymmetricEncryption asnAlgorithmIdentifier + +var ( + aes128CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 0}), + } + aes192CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 1}), + } + aes256CTRinECIES = asnSymmetricEncryption{ + Algorithm: doScheme(secgScheme, []int{21, 2}), + } +) + +func (a asnSymmetricEncryption) Cmp(b asnSymmetricEncryption) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type asnMessageAuthenticationCode asnAlgorithmIdentifier + +var ( + hmacFull = asnMessageAuthenticationCode{ + Algorithm: doScheme(secgScheme, []int{22}), + } +) + +func (a asnMessageAuthenticationCode) Cmp(b asnMessageAuthenticationCode) bool { + if len(a.Algorithm) != len(b.Algorithm) { + return false + } + for i := range a.Algorithm { + if a.Algorithm[i] != b.Algorithm[i] { + return false + } + } + return true +} + +type ecpksSupplements struct { + ECDomain secgNamedCurve + ECCAlgorithms eccAlgorithmSet +} + +type eccAlgorithmSet struct { + ECDH asnECDHAlgorithm `asn1:"optional"` + ECIES asnECIESParameters `asn1:"optional"` +} + +func marshalSubjectPublicKeyInfo(pub *PublicKey) (subj asnSubjectPublicKeyInfo, err error) { + subj.Algorithm = idEcPublicKeySupplemented + curve, ok := oidFromNamedCurve(pub.Curve) + if !ok { + err = ErrInvalidPublicKey + return + } + subj.Supplements.ECDomain = curve + if pub.Params != nil { + subj.Supplements.ECCAlgorithms.ECDH = paramsToASNECDH(pub.Params) + subj.Supplements.ECCAlgorithms.ECIES = paramsToASNECIES(pub.Params) + } + pubkey := elliptic.Marshal(pub.Curve, pub.X, pub.Y) + subj.PublicKey = asn1.BitString{ + BitLength: len(pubkey) * 8, + Bytes: pubkey, + } + return +} + +// Encode a public key to DER format. +func MarshalPublic(pub *PublicKey) ([]byte, error) { + subj, err := marshalSubjectPublicKeyInfo(pub) + if err != nil { + return nil, err + } + return asn1.Marshal(subj) +} + +// Decode a DER-encoded public key. +func UnmarshalPublic(in []byte) (pub *PublicKey, err error) { + var subj asnSubjectPublicKeyInfo + + if _, err = asn1.Unmarshal(in, &subj); err != nil { + return + } + if !subj.Algorithm.Equal(idEcPublicKeySupplemented) { + err = ErrInvalidPublicKey + return + } + pub = new(PublicKey) + pub.Curve = namedCurveFromOID(subj.Supplements.ECDomain) + x, y := elliptic.Unmarshal(pub.Curve, subj.PublicKey.Bytes) + if x == nil { + err = ErrInvalidPublicKey + return + } + pub.X = x + pub.Y = y + pub.Params = new(ECIESParams) + asnECIEStoParams(subj.Supplements.ECCAlgorithms.ECIES, pub.Params) + asnECDHtoParams(subj.Supplements.ECCAlgorithms.ECDH, pub.Params) + if pub.Params == nil { + if pub.Params = ParamsFromCurve(pub.Curve); pub.Params == nil { + err = ErrInvalidPublicKey + } + } + return +} + +func marshalPrivateKey(prv *PrivateKey) (ecprv asnPrivateKey, err error) { + ecprv.Version = asnECPrivKeyVer1 + ecprv.Private = prv.D.Bytes() + + var ok bool + ecprv.Curve, ok = oidFromNamedCurve(prv.PublicKey.Curve) + if !ok { + err = ErrInvalidPrivateKey + return + } + + var pub []byte + if pub, err = MarshalPublic(&prv.PublicKey); err != nil { + return + } else { + ecprv.Public = asn1.BitString{ + BitLength: len(pub) * 8, + Bytes: pub, + } + } + return +} + +// Encode a private key to DER format. +func MarshalPrivate(prv *PrivateKey) ([]byte, error) { + ecprv, err := marshalPrivateKey(prv) + if err != nil { + return nil, err + } + return asn1.Marshal(ecprv) +} + +// Decode a private key from a DER-encoded format. +func UnmarshalPrivate(in []byte) (prv *PrivateKey, err error) { + var ecprv asnPrivateKey + + if _, err = asn1.Unmarshal(in, &ecprv); err != nil { + return + } else if ecprv.Version != asnECPrivKeyVer1 { + err = ErrInvalidPrivateKey + return + } + + privateCurve := namedCurveFromOID(ecprv.Curve) + if privateCurve == nil { + err = ErrInvalidPrivateKey + return + } + + prv = new(PrivateKey) + prv.D = new(big.Int).SetBytes(ecprv.Private) + + if pub, err := UnmarshalPublic(ecprv.Public.Bytes); err != nil { + return nil, err + } else { + prv.PublicKey = *pub + } + + return +} + +// Export a public key to PEM format. +func ExportPublicPEM(pub *PublicKey) (out []byte, err error) { + der, err := MarshalPublic(pub) + if err != nil { + return + } + + var block pem.Block + block.Type = "ELLIPTIC CURVE PUBLIC KEY" + block.Bytes = der + + buf := new(bytes.Buffer) + err = pem.Encode(buf, &block) + if err != nil { + return + } else { + out = buf.Bytes() + } + return +} + +// Export a private key to PEM format. +func ExportPrivatePEM(prv *PrivateKey) (out []byte, err error) { + der, err := MarshalPrivate(prv) + if err != nil { + return + } + + var block pem.Block + block.Type = "ELLIPTIC CURVE PRIVATE KEY" + block.Bytes = der + + buf := new(bytes.Buffer) + err = pem.Encode(buf, &block) + if err != nil { + return + } else { + out = buf.Bytes() + } + return +} + +// Import a PEM-encoded public key. +func ImportPublicPEM(in []byte) (pub *PublicKey, err error) { + p, _ := pem.Decode(in) + if p == nil || p.Type != "ELLIPTIC CURVE PUBLIC KEY" { + return nil, ErrInvalidPublicKey + } + + pub, err = UnmarshalPublic(p.Bytes) + return +} + +// Import a PEM-encoded private key. +func ImportPrivatePEM(in []byte) (prv *PrivateKey, err error) { + p, _ := pem.Decode(in) + if p == nil || p.Type != "ELLIPTIC CURVE PRIVATE KEY" { + return nil, ErrInvalidPrivateKey + } + + prv, err = UnmarshalPrivate(p.Bytes) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies.go b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies.go new file mode 100644 index 0000000..2a16f20 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies.go @@ -0,0 +1,364 @@ +// Copyright (c) 2013 Kyle Isom +// Copyright (c) 2012 The Go Authors. 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 THE COPYRIGHT +// OWNER 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. + +package ecies + +import ( + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/subtle" + "fmt" + "hash" + "io" + "math/big" +) + +var ( + ErrImport = fmt.Errorf("ecies: failed to import key") + ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve") + ErrInvalidParams = fmt.Errorf("ecies: invalid ECIES parameters") + ErrInvalidPublicKey = fmt.Errorf("ecies: invalid public key") + ErrSharedKeyIsPointAtInfinity = fmt.Errorf("ecies: shared key is point at infinity") + ErrSharedKeyTooBig = fmt.Errorf("ecies: shared key params are too big") +) + +// PublicKey is a representation of an elliptic curve public key. +type PublicKey struct { + X *big.Int + Y *big.Int + elliptic.Curve + Params *ECIESParams +} + +// Export an ECIES public key as an ECDSA public key. +func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey { + return &ecdsa.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y} +} + +// Import an ECDSA public key as an ECIES public key. +func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey { + return &PublicKey{ + X: pub.X, + Y: pub.Y, + Curve: pub.Curve, + Params: ParamsFromCurve(pub.Curve), + } +} + +// PrivateKey is a representation of an elliptic curve private key. +type PrivateKey struct { + PublicKey + D *big.Int +} + +// Export an ECIES private key as an ECDSA private key. +func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey { + pub := &prv.PublicKey + pubECDSA := pub.ExportECDSA() + return &ecdsa.PrivateKey{PublicKey: *pubECDSA, D: prv.D} +} + +// Import an ECDSA private key as an ECIES private key. +func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey { + pub := ImportECDSAPublic(&prv.PublicKey) + return &PrivateKey{*pub, prv.D} +} + +// Generate an elliptic curve public / private keypair. If params is nil, +// the recommended default parameters for the key will be chosen. +func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) { + pb, x, y, err := elliptic.GenerateKey(curve, rand) + if err != nil { + return + } + prv = new(PrivateKey) + prv.PublicKey.X = x + prv.PublicKey.Y = y + prv.PublicKey.Curve = curve + prv.D = new(big.Int).SetBytes(pb) + if params == nil { + params = ParamsFromCurve(curve) + } + prv.PublicKey.Params = params + return +} + +// MaxSharedKeyLength returns the maximum length of the shared key the +// public key can produce. +func MaxSharedKeyLength(pub *PublicKey) int { + return (pub.Curve.Params().BitSize + 7) / 8 +} + +// ECDH key agreement method used to establish secret keys for encryption. +func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) { + if prv.PublicKey.Curve != pub.Curve { + return nil, ErrInvalidCurve + } + if skLen+macLen > MaxSharedKeyLength(pub) { + return nil, ErrSharedKeyTooBig + } + + x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes()) + if x == nil { + return nil, ErrSharedKeyIsPointAtInfinity + } + + sk = make([]byte, skLen+macLen) + skBytes := x.Bytes() + copy(sk[len(sk)-len(skBytes):], skBytes) + return sk, nil +} + +var ( + ErrKeyDataTooLong = fmt.Errorf("ecies: can't supply requested key data") + ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long") + ErrInvalidMessage = fmt.Errorf("ecies: invalid message") +) + +var ( + big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil) + big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1)) +) + +func incCounter(ctr []byte) { + if ctr[3]++; ctr[3] != 0 { + return + } else if ctr[2]++; ctr[2] != 0 { + return + } else if ctr[1]++; ctr[1] != 0 { + return + } else if ctr[0]++; ctr[0] != 0 { + return + } + return +} + +// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1). +func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) { + if s1 == nil { + s1 = make([]byte, 0) + } + + reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8) + if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 { + fmt.Println(big2To32M1) + return nil, ErrKeyDataTooLong + } + + counter := []byte{0, 0, 0, 1} + k = make([]byte, 0) + + for i := 0; i <= reps; i++ { + hash.Write(counter) + hash.Write(z) + hash.Write(s1) + k = append(k, hash.Sum(nil)...) + hash.Reset() + incCounter(counter) + } + + k = k[:kdLen] + return +} + +// messageTag computes the MAC of a message (called the tag) as per +// SEC 1, 3.5. +func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte { + mac := hmac.New(hash, km) + mac.Write(msg) + mac.Write(shared) + tag := mac.Sum(nil) + return tag +} + +// Generate an initialisation vector for CTR mode. +func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) { + iv = make([]byte, params.BlockSize) + _, err = io.ReadFull(rand, iv) + return +} + +// symEncrypt carries out CTR encryption using the block cipher specified in the +// parameters. +func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) { + c, err := params.Cipher(key) + if err != nil { + return + } + + iv, err := generateIV(params, rand) + if err != nil { + return + } + ctr := cipher.NewCTR(c, iv) + + ct = make([]byte, len(m)+params.BlockSize) + copy(ct, iv) + ctr.XORKeyStream(ct[params.BlockSize:], m) + return +} + +// symDecrypt carries out CTR decryption using the block cipher specified in +// the parameters +func symDecrypt(rand io.Reader, params *ECIESParams, key, ct []byte) (m []byte, err error) { + c, err := params.Cipher(key) + if err != nil { + return + } + + ctr := cipher.NewCTR(c, ct[:params.BlockSize]) + + m = make([]byte, len(ct)-params.BlockSize) + ctr.XORKeyStream(m, ct[params.BlockSize:]) + return +} + +// Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1. +// +// s1 and s2 contain shared information that is not part of the resulting +// ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the +// shared information parameters aren't being used, they should be nil. +func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) { + params := pub.Params + if params == nil { + if params = ParamsFromCurve(pub.Curve); params == nil { + err = ErrUnsupportedECIESParameters + return + } + } + R, err := GenerateKey(rand, pub.Curve, params) + if err != nil { + return + } + + hash := params.Hash() + z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen) + if err != nil { + return + } + K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + if err != nil { + return + } + Ke := K[:params.KeyLen] + Km := K[params.KeyLen:] + hash.Write(Km) + Km = hash.Sum(nil) + hash.Reset() + + em, err := symEncrypt(rand, params, Ke, m) + if err != nil || len(em) <= params.BlockSize { + return + } + + d := messageTag(params.Hash, Km, em, s2) + + Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y) + ct = make([]byte, len(Rb)+len(em)+len(d)) + copy(ct, Rb) + copy(ct[len(Rb):], em) + copy(ct[len(Rb)+len(em):], d) + return +} + +// Decrypt decrypts an ECIES ciphertext. +func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) { + if len(c) == 0 { + return nil, ErrInvalidMessage + } + params := prv.PublicKey.Params + if params == nil { + if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil { + err = ErrUnsupportedECIESParameters + return + } + } + hash := params.Hash() + + var ( + rLen int + hLen int = hash.Size() + mStart int + mEnd int + ) + + switch c[0] { + case 2, 3, 4: + rLen = ((prv.PublicKey.Curve.Params().BitSize + 7) / 4) + if len(c) < (rLen + hLen + 1) { + err = ErrInvalidMessage + return + } + default: + err = ErrInvalidPublicKey + return + } + + mStart = rLen + mEnd = len(c) - hLen + + R := new(PublicKey) + R.Curve = prv.PublicKey.Curve + R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen]) + if R.X == nil { + err = ErrInvalidPublicKey + return + } + if !R.Curve.IsOnCurve(R.X, R.Y) { + err = ErrInvalidCurve + return + } + + z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) + if err != nil { + return + } + + K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen) + if err != nil { + return + } + + Ke := K[:params.KeyLen] + Km := K[params.KeyLen:] + hash.Write(Km) + Km = hash.Sum(nil) + hash.Reset() + + d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) + if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { + err = ErrInvalidMessage + return + } + + m, err = symDecrypt(rand, params, Ke, c[mStart:mEnd]) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies_test.go new file mode 100644 index 0000000..7c454aa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/ecies_test.go @@ -0,0 +1,659 @@ +// Copyright (c) 2013 Kyle Isom +// Copyright (c) 2012 The Go Authors. 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 THE COPYRIGHT +// OWNER 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. + +package ecies + +import ( + "bytes" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/crypto" +) + +var dumpEnc bool + +func init() { + flDump := flag.Bool("dump", false, "write encrypted test message to file") + flag.Parse() + dumpEnc = *flDump +} + +// Ensure the KDF generates appropriately sized keys. +func TestKDF(t *testing.T) { + msg := []byte("Hello, world") + h := sha256.New() + + k, err := concatKDF(h, msg, nil, 64) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + if len(k) != 64 { + fmt.Printf("KDF: generated key is the wrong size (%d instead of 64\n", + len(k)) + t.FailNow() + } +} + +var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match") + +// cmpParams compares a set of ECIES parameters. We assume, as per the +// docs, that AES is the only supported symmetric encryption algorithm. +func cmpParams(p1, p2 *ECIESParams) bool { + if p1.hashAlgo != p2.hashAlgo { + return false + } else if p1.KeyLen != p2.KeyLen { + return false + } else if p1.BlockSize != p2.BlockSize { + return false + } + return true +} + +// cmpPublic returns true if the two public keys represent the same pojnt. +func cmpPublic(pub1, pub2 PublicKey) bool { + if pub1.X == nil || pub1.Y == nil { + fmt.Println(ErrInvalidPublicKey.Error()) + return false + } + if pub2.X == nil || pub2.Y == nil { + fmt.Println(ErrInvalidPublicKey.Error()) + return false + } + pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y) + pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y) + + return bytes.Equal(pub1Out, pub2Out) +} + +// cmpPrivate returns true if the two private keys are the same. +func cmpPrivate(prv1, prv2 *PrivateKey) bool { + if prv1 == nil || prv1.D == nil { + return false + } else if prv2 == nil || prv2.D == nil { + return false + } else if prv1.D.Cmp(prv2.D) != 0 { + return false + } else { + return cmpPublic(prv1.PublicKey, prv2.PublicKey) + } +} + +// Validate the ECDH component. +func TestSharedKey(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2 + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(sk1, sk2) { + fmt.Println(ErrBadSharedKeys.Error()) + t.FailNow() + } +} + +func TestSharedKeyPadding(t *testing.T) { + // sanity checks + prv0 := hexKey("1adf5c18167d96a1f9a0b1ef63be8aa27eaf6032c233b2b38f7850cf5b859fd9") + prv1 := hexKey("0097a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a") + x0, _ := new(big.Int).SetString("1a8ed022ff7aec59dc1b440446bdda5ff6bcb3509a8b109077282b361efffbd8", 16) + x1, _ := new(big.Int).SetString("6ab3ac374251f638d0abb3ef596d1dc67955b507c104e5f2009724812dc027b8", 16) + y0, _ := new(big.Int).SetString("e040bd480b1deccc3bc40bd5b1fdcb7bfd352500b477cb9471366dbd4493f923", 16) + y1, _ := new(big.Int).SetString("8ad915f2b503a8be6facab6588731fefeb584fd2dfa9a77a5e0bba1ec439e4fa", 16) + + if prv0.PublicKey.X.Cmp(x0) != 0 { + t.Errorf("mismatched prv0.X:\nhave: %x\nwant: %x\n", prv0.PublicKey.X.Bytes(), x0.Bytes()) + } + if prv0.PublicKey.Y.Cmp(y0) != 0 { + t.Errorf("mismatched prv0.Y:\nhave: %x\nwant: %x\n", prv0.PublicKey.Y.Bytes(), y0.Bytes()) + } + if prv1.PublicKey.X.Cmp(x1) != 0 { + t.Errorf("mismatched prv1.X:\nhave: %x\nwant: %x\n", prv1.PublicKey.X.Bytes(), x1.Bytes()) + } + if prv1.PublicKey.Y.Cmp(y1) != 0 { + t.Errorf("mismatched prv1.Y:\nhave: %x\nwant: %x\n", prv1.PublicKey.Y.Bytes(), y1.Bytes()) + } + + // test shared secret generation + sk1, err := prv0.GenerateShared(&prv1.PublicKey, 16, 16) + if err != nil { + fmt.Println(err.Error()) + } + + sk2, err := prv1.GenerateShared(&prv0.PublicKey, 16, 16) + if err != nil { + t.Fatal(err.Error()) + } + + if !bytes.Equal(sk1, sk2) { + t.Fatal(ErrBadSharedKeys.Error()) + } +} + +// Verify that the key generation code fails when too much key data is +// requested. +func TestTooBigSharedKey(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + _, err = prv1.GenerateShared(&prv2.PublicKey, 32, 32) + if err != ErrSharedKeyTooBig { + fmt.Println("ecdh: shared key should be too large for curve") + t.FailNow() + } + + _, err = prv2.GenerateShared(&prv1.PublicKey, 32, 32) + if err != ErrSharedKeyTooBig { + fmt.Println("ecdh: shared key should be too large for curve") + t.FailNow() + } +} + +// Ensure a public key can be successfully marshalled and unmarshalled, and +// that the decoded key is the same as the original. +func TestMarshalPublic(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + t.Fatalf("GenerateKey error: %s", err) + } + + out, err := MarshalPublic(&prv.PublicKey) + if err != nil { + t.Fatalf("MarshalPublic error: %s", err) + } + + pub, err := UnmarshalPublic(out) + if err != nil { + t.Fatalf("UnmarshalPublic error: %s", err) + } + + if !cmpPublic(prv.PublicKey, *pub) { + t.Fatal("ecies: failed to unmarshal public key") + } +} + +// Ensure that a private key can be encoded into DER format, and that +// the resulting key is properly parsed back into a public key. +func TestMarshalPrivate(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := MarshalPrivate(prv) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.out", out, 0644) + } + + prv2, err := UnmarshalPrivate(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !cmpPrivate(prv, prv2) { + fmt.Println("ecdh: private key import failed") + t.FailNow() + } +} + +// Ensure that a private key can be successfully encoded to PEM format, and +// the resulting key is properly parsed back in. +func TestPrivatePEM(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := ExportPrivatePEM(prv) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.key", out, 0644) + } + + prv2, err := ImportPrivatePEM(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } else if !cmpPrivate(prv, prv2) { + fmt.Println("ecdh: import from PEM failed") + t.FailNow() + } +} + +// Ensure that a public key can be successfully encoded to PEM format, and +// the resulting key is properly parsed back in. +func TestPublicPEM(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := ExportPublicPEM(&prv.PublicKey) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if dumpEnc { + ioutil.WriteFile("test.pem", out, 0644) + } + + pub2, err := ImportPublicPEM(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } else if !cmpPublic(prv.PublicKey, *pub2) { + fmt.Println("ecdh: import from PEM failed") + t.FailNow() + } +} + +// Benchmark the generation of P256 keys. +func BenchmarkGenerateKeyP256(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Benchmark the generation of P256 shared keys. +func BenchmarkGenSharedKeyP256(b *testing.B) { + prv, err := GenerateKey(rand.Reader, elliptic.P256(), nil) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Benchmark the generation of S256 shared keys. +func BenchmarkGenSharedKeyS256(b *testing.B) { + prv, err := GenerateKey(rand.Reader, crypto.S256(), nil) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Verify that an encrypted message can be successfully decrypted. +func TestEncryptDecrypt(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Println("ecies: plaintext doesn't match message") + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err == nil { + fmt.Println("ecies: encryption should not have succeeded") + t.FailNow() + } +} + +func TestDecryptShared2(t *testing.T) { + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + t.Fatal(err) + } + message := []byte("Hello, world.") + shared2 := []byte("shared data 2") + ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, shared2) + if err != nil { + t.Fatal(err) + } + + // Check that decrypting with correct shared data works. + pt, err := prv.Decrypt(rand.Reader, ct, nil, shared2) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pt, message) { + t.Fatal("ecies: plaintext doesn't match message") + } + + // Decrypting without shared data or incorrect shared data fails. + if _, err = prv.Decrypt(rand.Reader, ct, nil, nil); err == nil { + t.Fatal("ecies: decrypting without shared data didn't fail") + } + if _, err = prv.Decrypt(rand.Reader, ct, nil, []byte("garbage")); err == nil { + t.Fatal("ecies: decrypting with incorrect shared data didn't fail") + } +} + +// TestMarshalEncryption validates the encode/decode produces a valid +// ECIES encryption key. +func TestMarshalEncryption(t *testing.T) { + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + out, err := MarshalPrivate(prv1) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + prv2, err := UnmarshalPrivate(out) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Println("ecies: plaintext doesn't match message") + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + +} + +type testCase struct { + Curve elliptic.Curve + Name string + Expected bool +} + +var testCases = []testCase{ + { + Curve: elliptic.P256(), + Name: "P256", + Expected: true, + }, + { + Curve: elliptic.P384(), + Name: "P384", + Expected: true, + }, + { + Curve: elliptic.P521(), + Name: "P521", + Expected: true, + }, +} + +// Test parameter selection for each curve, and that P224 fails automatic +// parameter selection (see README for a discussion of P224). Ensures that +// selecting a set of parameters automatically for the given curve works. +func TestParamSelection(t *testing.T) { + for _, c := range testCases { + testParamSelection(t, c) + } +} + +func testParamSelection(t *testing.T, c testCase) { + params := ParamsFromCurve(c.Curve) + if params == nil && c.Expected { + fmt.Printf("%s (%s)\n", ErrInvalidParams.Error(), c.Name) + t.FailNow() + } else if params != nil && !c.Expected { + fmt.Printf("ecies: parameters should be invalid (%s)\n", + c.Name) + t.FailNow() + } + + prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + fmt.Printf("%s (%s)\n", err.Error(), c.Name) + t.FailNow() + } + + if !bytes.Equal(pt, message) { + fmt.Printf("ecies: plaintext doesn't match message (%s)\n", + c.Name) + t.FailNow() + } + + _, err = prv1.Decrypt(rand.Reader, ct, nil, nil) + if err == nil { + fmt.Printf("ecies: encryption should not have succeeded (%s)\n", + c.Name) + t.FailNow() + } + +} + +// Ensure that the basic public key validation in the decryption operation +// works. +func TestBasicKeyValidation(t *testing.T) { + badBytes := []byte{0, 1, 5, 6, 7, 8, 9} + + prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, nil) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + for _, b := range badBytes { + ct[0] = b + _, err := prv.Decrypt(rand.Reader, ct, nil, nil) + if err != ErrInvalidPublicKey { + fmt.Println("ecies: validated an invalid key") + t.FailNow() + } + } +} + +func TestBox(t *testing.T) { + prv1 := hexKey("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f") + prv2 := hexKey("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a") + pub2 := &prv2.PublicKey + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, pub2, message, nil, nil) + if err != nil { + t.Fatal(err) + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pt, message) { + t.Fatal("ecies: plaintext doesn't match message") + } + if _, err = prv1.Decrypt(rand.Reader, ct, nil, nil); err == nil { + t.Fatal("ecies: encryption should not have succeeded") + } +} + +// Verify GenerateShared against static values - useful when +// debugging changes in underlying libs +func TestSharedKeyStatic(t *testing.T) { + prv1 := hexKey("7ebbc6a8358bc76dd73ebc557056702c8cfc34e5cfcd90eb83af0347575fd2ad") + prv2 := hexKey("6a3d6396903245bba5837752b9e0348874e72db0c4e11e9c485a81b4ea4353b9") + + skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2 + + sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(sk1, sk2) { + fmt.Println(ErrBadSharedKeys.Error()) + t.FailNow() + } + + sk, _ := hex.DecodeString("167ccc13ac5e8a26b131c3446030c60fbfac6aa8e31149d0869f93626a4cdf62") + if !bytes.Equal(sk1, sk) { + t.Fatalf("shared secret mismatch: want: %x have: %x", sk, sk1) + } +} + +func hexKey(prv string) *PrivateKey { + key, err := crypto.HexToECDSA(prv) + if err != nil { + panic(err) + } + return ImportECDSA(key) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/ecies/params.go b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/params.go new file mode 100644 index 0000000..826d90c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/ecies/params.go @@ -0,0 +1,210 @@ +// Copyright (c) 2013 Kyle Isom +// Copyright (c) 2012 The Go Authors. 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 THE COPYRIGHT +// OWNER 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. + +package ecies + +// This file contains parameters for ECIES encryption, specifying the +// symmetric encryption and HMAC parameters. + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + "fmt" + "hash" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +var ( + DefaultCurve = ethcrypto.S256() + ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") + ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") +) + +type ECIESParams struct { + Hash func() hash.Hash // hash function + hashAlgo crypto.Hash + Cipher func([]byte) (cipher.Block, error) // symmetric cipher + BlockSize int // block size of symmetric cipher + KeyLen int // length of symmetric key +} + +// Standard ECIES parameters: +// * ECIES using AES128 and HMAC-SHA-256-16 +// * ECIES using AES256 and HMAC-SHA-256-32 +// * ECIES using AES256 and HMAC-SHA-384-48 +// * ECIES using AES256 and HMAC-SHA-512-64 + +var ( + ECIES_AES128_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 16, + } + + ECIES_AES256_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA384 = &ECIESParams{ + Hash: sha512.New384, + hashAlgo: crypto.SHA384, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA512 = &ECIESParams{ + Hash: sha512.New, + hashAlgo: crypto.SHA512, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } +) + +var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ + ethcrypto.S256(): ECIES_AES128_SHA256, + elliptic.P256(): ECIES_AES128_SHA256, + elliptic.P384(): ECIES_AES256_SHA384, + elliptic.P521(): ECIES_AES256_SHA512, +} + +func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { + paramsFromCurve[curve] = params +} + +// ParamsFromCurve selects parameters optimal for the selected elliptic curve. +// Only the curves P256, P384, and P512 are supported. +func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) { + return paramsFromCurve[curve] + + /* + switch curve { + case elliptic.P256(): + return ECIES_AES128_SHA256 + case elliptic.P384(): + return ECIES_AES256_SHA384 + case elliptic.P521(): + return ECIES_AES256_SHA512 + default: + return nil + } + */ +} + +// ASN.1 encode the ECIES parameters relevant to the encryption operations. +func paramsToASNECIES(params *ECIESParams) (asnParams asnECIESParameters) { + if nil == params { + return + } + asnParams.KDF = asnNISTConcatenationKDF + asnParams.MAC = hmacFull + switch params.KeyLen { + case 16: + asnParams.Sym = aes128CTRinECIES + case 24: + asnParams.Sym = aes192CTRinECIES + case 32: + asnParams.Sym = aes256CTRinECIES + } + return +} + +// ASN.1 encode the ECIES parameters relevant to ECDH. +func paramsToASNECDH(params *ECIESParams) (algo asnECDHAlgorithm) { + switch params.hashAlgo { + case crypto.SHA224: + algo = dhSinglePass_stdDH_sha224kdf + case crypto.SHA256: + algo = dhSinglePass_stdDH_sha256kdf + case crypto.SHA384: + algo = dhSinglePass_stdDH_sha384kdf + case crypto.SHA512: + algo = dhSinglePass_stdDH_sha512kdf + } + return +} + +// ASN.1 decode the ECIES parameters relevant to the encryption stage. +func asnECIEStoParams(asnParams asnECIESParameters, params *ECIESParams) { + if !asnParams.KDF.Cmp(asnNISTConcatenationKDF) { + params = nil + return + } else if !asnParams.MAC.Cmp(hmacFull) { + params = nil + return + } + + switch { + case asnParams.Sym.Cmp(aes128CTRinECIES): + params.KeyLen = 16 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + case asnParams.Sym.Cmp(aes192CTRinECIES): + params.KeyLen = 24 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + case asnParams.Sym.Cmp(aes256CTRinECIES): + params.KeyLen = 32 + params.BlockSize = 16 + params.Cipher = aes.NewCipher + default: + params = nil + } +} + +// ASN.1 decode the ECIES parameters relevant to ECDH. +func asnECDHtoParams(asnParams asnECDHAlgorithm, params *ECIESParams) { + if asnParams.Cmp(dhSinglePass_stdDH_sha224kdf) { + params.hashAlgo = crypto.SHA224 + params.Hash = sha256.New224 + } else if asnParams.Cmp(dhSinglePass_stdDH_sha256kdf) { + params.hashAlgo = crypto.SHA256 + params.Hash = sha256.New + } else if asnParams.Cmp(dhSinglePass_stdDH_sha384kdf) { + params.hashAlgo = crypto.SHA384 + params.Hash = sha512.New384 + } else if asnParams.Cmp(dhSinglePass_stdDH_sha512kdf) { + params.hashAlgo = crypto.SHA512 + params.Hash = sha512.New + } else { + params = nil + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/randentropy/rand_entropy.go b/vendor/github.com/ethereum/go-ethereum/crypto/randentropy/rand_entropy.go new file mode 100644 index 0000000..539d3ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/randentropy/rand_entropy.go @@ -0,0 +1,42 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package randentropy + +import ( + crand "crypto/rand" + "io" +) + +var Reader io.Reader = &randEntropy{} + +type randEntropy struct { +} + +func (*randEntropy) Read(bytes []byte) (n int, err error) { + readBytes := GetEntropyCSPRNG(len(bytes)) + copy(bytes, readBytes) + return len(bytes), nil +} + +func GetEntropyCSPRNG(n int) []byte { + mainBuff := make([]byte, n) + _, err := io.ReadFull(crand.Reader, mainBuff) + if err != nil { + panic("reading from crypto/rand failed: " + err.Error()) + } + return mainBuff +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/.gitignore b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/.gitignore new file mode 100644 index 0000000..802b674 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +*~ diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve.go b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve.go new file mode 100644 index 0000000..61cad54 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve.go @@ -0,0 +1,310 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. 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. +// * The name of ThePiachu may not 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 THE COPYRIGHT +// OWNER 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. + +package secp256k1 + +import ( + "crypto/elliptic" + "math/big" + "sync" + "unsafe" +) + +/* +#include "libsecp256k1/include/secp256k1.h" +extern int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar); +*/ +import "C" + +// This code is from https://github.com/ThePiachu/GoBit and implements +// several Koblitz elliptic curves over prime fields. +// +// The curve methods, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, +// z1) where x = x1/z1² and y = y1/z1³. The greatest speedups come +// when the whole calculation can be performed within the transform +// (as in ScalarMult and ScalarBaseMult). But even for Add and Double, +// it's faster to apply and reverse the transform than to operate in +// affine coordinates. + +// A BitCurve represents a Koblitz Curve with a=0. +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +type BitCurve struct { + P *big.Int // the order of the underlying field + N *big.Int // the order of the base point + B *big.Int // the constant of the BitCurve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +func (BitCurve *BitCurve) Params() *elliptic.CurveParams { + return &elliptic.CurveParams{ + P: BitCurve.P, + N: BitCurve.N, + B: BitCurve.B, + Gx: BitCurve.Gx, + Gy: BitCurve.Gy, + BitSize: BitCurve.BitSize, + } +} + +// IsOnBitCurve returns true if the given (x,y) lies on the BitCurve. +func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ + b + y2 := new(big.Int).Mul(y, y) //y² + y2.Mod(y2, BitCurve.P) //y²%P + + x3 := new(big.Int).Mul(x, x) //x² + x3.Mul(x3, x) //x³ + + x3.Add(x3, BitCurve.B) //x³+B + x3.Mod(x3, BitCurve.P) //(x³+B)%P + + return x3.Cmp(y2) == 0 +} + +//TODO: double check if the function is okay +// affineFromJacobian reverses the Jacobian transform. See the comment at the +// top of the file. +func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, BitCurve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, BitCurve.P) + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, BitCurve.P) + return +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, BitCurve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, BitCurve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, BitCurve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, BitCurve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, BitCurve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, BitCurve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, BitCurve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, BitCurve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + // Ensure scalar is exactly 32 bytes. We pad always, even if + // scalar is 32 bytes long, to avoid a timing side channel. + if len(scalar) > 32 { + panic("can't handle scalars > 256 bits") + } + // NOTE: potential timing issue + padded := make([]byte, 32) + copy(padded[32-len(scalar):], scalar) + scalar = padded + + // Do the multiplication in C, updating point. + point := make([]byte, 64) + readBits(point[:32], Bx) + readBits(point[32:], By) + pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) + scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) + res := C.secp256k1_pubkey_scalar_mul(context, pointPtr, scalarPtr) + + // Unpack the result and clear temporaries. + x := new(big.Int).SetBytes(point[:32]) + y := new(big.Int).SetBytes(point[32:]) + for i := range point { + point[i] = 0 + } + for i := range padded { + scalar[i] = 0 + } + if res != 1 { + return nil, nil + } + return x, y +} + +// ScalarBaseMult returns k*G, where G is the base point of the group and k is +// an integer in big-endian form. +func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k) +} + +// Marshal converts a point into the form specified in section 4.3.6 of ANSI +// X9.62. +func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (BitCurve.BitSize + 7) >> 3 + + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point + + xBytes := x.Bytes() + copy(ret[1+byteLen-len(xBytes):], xBytes) + yBytes := y.Bytes() + copy(ret[1+2*byteLen-len(yBytes):], yBytes) + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (BitCurve.BitSize + 7) >> 3 + if len(data) != 1+2*byteLen { + return + } + if data[0] != 4 { // uncompressed form + return + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return +} + +var ( + initonce sync.Once + theCurve *BitCurve +) + +// S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1) +func S256() *BitCurve { + initonce.Do(func() { + // See SEC 2 section 2.7.1 + // curve parameters taken from: + // http://www.secg.org/collateral/sec2_final.pdf + theCurve = new(BitCurve) + theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) + theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) + theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) + theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) + theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) + theCurve.BitSize = 256 + }) + return theCurve +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve_test.go new file mode 100644 index 0000000..d915ee8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/curve_test.go @@ -0,0 +1,39 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package secp256k1 + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" +) + +func TestReadBits(t *testing.T) { + check := func(input string) { + want, _ := hex.DecodeString(input) + int, _ := new(big.Int).SetString(input, 16) + buf := make([]byte, len(want)) + readBits(buf, int) + if !bytes.Equal(buf, want) { + t.Errorf("have: %x\nwant: %x", buf, want) + } + } + check("000000000000000000000000000000000000000000000000000000FEFCF3F8F0") + check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0") + check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0") +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/ext.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/ext.h new file mode 100644 index 0000000..ee759fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/ext.h @@ -0,0 +1,87 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// secp256k1_context_create_sign_verify creates a context for signing and signature verification. +static secp256k1_context* secp256k1_context_create_sign_verify() { + return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); +} + +// secp256k1_ecdsa_recover_pubkey recovers the public key of an encoded compact signature. +// +// Returns: 1: recovery was successful +// 0: recovery was not successful +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL) +// In: sigdata: pointer to a 65-byte signature with the recovery id at the end (cannot be NULL) +// msgdata: pointer to a 32-byte message (cannot be NULL) +static int secp256k1_ecdsa_recover_pubkey( + const secp256k1_context* ctx, + unsigned char *pubkey_out, + const unsigned char *sigdata, + const unsigned char *msgdata +) { + secp256k1_ecdsa_recoverable_signature sig; + secp256k1_pubkey pubkey; + + if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) { + return 0; + } + if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) { + return 0; + } + size_t outputlen = 65; + return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED); +} + +// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time. +// +// Returns: 1: multiplication was successful +// 0: scalar was invalid (zero or overflow) +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: point: the multiplied point (usually secret) +// In: point: pointer to a 64-byte public point, +// encoded as two 256bit big-endian numbers. +// scalar: a 32-byte scalar with which to multiply the point +int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_fe feX, feY; + secp256k1_gej res; + secp256k1_ge ge; + secp256k1_scalar s; + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + (void)ctx; + + secp256k1_fe_set_b32(&feX, point); + secp256k1_fe_set_b32(&feY, point+32); + secp256k1_ge_set_xy(&ge, &feX, &feY); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + secp256k1_ecmult_const(&res, &ge, &s); + secp256k1_ge_set_gej(&ge, &res); + /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */ + secp256k1_fe_normalize(&ge.x); + secp256k1_fe_normalize(&ge.y); + secp256k1_fe_get_b32(point, &ge.x); + secp256k1_fe_get_b32(point+32, &ge.y); + ret = 1; + } + secp256k1_scalar_clear(&s); + return ret; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.gitignore b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.gitignore new file mode 100644 index 0000000..87fea16 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.gitignore @@ -0,0 +1,49 @@ +bench_inv +bench_ecdh +bench_sign +bench_verify +bench_schnorr_verify +bench_recover +bench_internal +tests +exhaustive_tests +gen_context +*.exe +*.so +*.a +!.gitignore + +Makefile +configure +.libs/ +Makefile.in +aclocal.m4 +autom4te.cache/ +config.log +config.status +*.tar.gz +*.la +libtool +.deps/ +.dirstamp +*.lo +*.o +*~ +src/libsecp256k1-config.h +src/libsecp256k1-config.h.in +src/ecmult_static_context.h +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver +src/stamp-h1 +libsecp256k1.pc diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.travis.yml b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.travis.yml new file mode 100644 index 0000000..2439529 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/.travis.yml @@ -0,0 +1,69 @@ +language: c +sudo: false +addons: + apt: + packages: libgmp-dev +compiler: + - clang + - gcc +cache: + directories: + - src/java/guava/ +env: + global: + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no + - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar + matrix: + - SCALAR=32bit RECOVERY=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=64bit + - FIELD=64bit RECOVERY=yes + - FIELD=64bit ENDOMORPHISM=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ASM=x86_64 + - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 + - FIELD=32bit ENDOMORPHISM=yes + - BIGNUM=no + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no STATICPRECOMPUTATION=no + - BUILD=distcheck + - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC + - EXTRAFLAGS=CFLAGS=-O0 + - BUILD=check-java ECDH=yes EXPERIMENTAL=yes +matrix: + fast_finish: true + include: + - compiler: clang + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 + - compiler: clang + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 +before_install: mkdir -p `dirname $GUAVA_JAR` +install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi +before_script: ./autogen.sh +script: + - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi + - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD +os: linux diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/COPYING b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/COPYING new file mode 100644 index 0000000..4522a59 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2013 Pieter Wuille + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/Makefile.am b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/Makefile.am new file mode 100644 index 0000000..c071fbe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/Makefile.am @@ -0,0 +1,177 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 + +lib_LTLIBRARIES = libsecp256k1.la +if USE_JNI +JNI_LIB = libsecp256k1_jni.la +noinst_LTLIBRARIES = $(JNI_LIB) +else +JNI_LIB = +endif +include_HEADERS = include/secp256k1.h +noinst_HEADERS = +noinst_HEADERS += src/scalar.h +noinst_HEADERS += src/scalar_4x64.h +noinst_HEADERS += src/scalar_8x32.h +noinst_HEADERS += src/scalar_low.h +noinst_HEADERS += src/scalar_impl.h +noinst_HEADERS += src/scalar_4x64_impl.h +noinst_HEADERS += src/scalar_8x32_impl.h +noinst_HEADERS += src/scalar_low_impl.h +noinst_HEADERS += src/group.h +noinst_HEADERS += src/group_impl.h +noinst_HEADERS += src/num_gmp.h +noinst_HEADERS += src/num_gmp_impl.h +noinst_HEADERS += src/ecdsa.h +noinst_HEADERS += src/ecdsa_impl.h +noinst_HEADERS += src/eckey.h +noinst_HEADERS += src/eckey_impl.h +noinst_HEADERS += src/ecmult.h +noinst_HEADERS += src/ecmult_impl.h +noinst_HEADERS += src/ecmult_const.h +noinst_HEADERS += src/ecmult_const_impl.h +noinst_HEADERS += src/ecmult_gen.h +noinst_HEADERS += src/ecmult_gen_impl.h +noinst_HEADERS += src/num.h +noinst_HEADERS += src/num_impl.h +noinst_HEADERS += src/field_10x26.h +noinst_HEADERS += src/field_10x26_impl.h +noinst_HEADERS += src/field_5x52.h +noinst_HEADERS += src/field_5x52_impl.h +noinst_HEADERS += src/field_5x52_int128_impl.h +noinst_HEADERS += src/field_5x52_asm_impl.h +noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h +noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h +noinst_HEADERS += src/util.h +noinst_HEADERS += src/testrand.h +noinst_HEADERS += src/testrand_impl.h +noinst_HEADERS += src/hash.h +noinst_HEADERS += src/hash_impl.h +noinst_HEADERS += src/field.h +noinst_HEADERS += src/field_impl.h +noinst_HEADERS += src/bench.h +noinst_HEADERS += contrib/lax_der_parsing.h +noinst_HEADERS += contrib/lax_der_parsing.c +noinst_HEADERS += contrib/lax_der_privatekey_parsing.h +noinst_HEADERS += contrib/lax_der_privatekey_parsing.c + +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libsecp256k1.pc + +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +endif +endif + +libsecp256k1_la_SOURCES = src/secp256k1.c +libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) + +libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c +libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) + +noinst_PROGRAMS = +if USE_BENCHMARK +noinst_PROGRAMS += bench_verify bench_sign bench_internal +bench_verify_SOURCES = src/bench_verify.c +bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_sign_SOURCES = src/bench_sign.c +bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_internal_SOURCES = src/bench_internal.c +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +endif + +TESTS = +if USE_TESTS +noinst_PROGRAMS += tests +tests_SOURCES = src/tests.c +tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +if !ENABLE_COVERAGE +tests_CPPFLAGS += -DVERIFY +endif +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +tests_LDFLAGS = -static +TESTS += tests +endif + +if USE_EXHAUSTIVE_TESTS +noinst_PROGRAMS += exhaustive_tests +exhaustive_tests_SOURCES = src/tests_exhaustive.c +exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES) +if !ENABLE_COVERAGE +exhaustive_tests_CPPFLAGS += -DVERIFY +endif +exhaustive_tests_LDADD = $(SECP_LIBS) +exhaustive_tests_LDFLAGS = -static +TESTS += exhaustive_tests +endif + +JAVAROOT=src/java +JAVAORG=org/bitcoin +JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar +CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA) +JAVA_FILES= \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \ + $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java + +if USE_JNI + +$(JAVA_GUAVA): + @echo Guava is missing. Fetch it via: \ + wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@) + @false + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java + $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test + +endif +endif + +if USE_ECMULT_STATIC_PRECOMPUTATION +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function + +gen_context_OBJECTS = gen_context.o +gen_context_BIN = gen_context$(BUILD_EXEEXT) +gen_%.o: src/gen_%.c + $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ + +$(gen_context_BIN): $(gen_context_OBJECTS) + $(CC_FOR_BUILD) $^ -o $@ + +$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h +$(tests_OBJECTS): src/ecmult_static_context.h +$(bench_internal_OBJECTS): src/ecmult_static_context.h + +src/ecmult_static_context.h: $(gen_context_BIN) + ./$(gen_context_BIN) + +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java +endif + +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) + +if ENABLE_MODULE_ECDH +include src/modules/ecdh/Makefile.am.include +endif + +if ENABLE_MODULE_RECOVERY +include src/modules/recovery/Makefile.am.include +endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/README.md b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/README.md new file mode 100644 index 0000000..8cd344e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/README.md @@ -0,0 +1,61 @@ +libsecp256k1 +============ + +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) + +Optimized C library for EC operations on curve secp256k1. + +This library is a work in progress and is being used to research best practices. Use at your own risk. + +Features: +* secp256k1 ECDSA signing/verification and key generation. +* Adding/multiplying private/public keys. +* Serialization/parsing of private keys, public keys, signatures. +* Constant time, constant memory access signing and pubkey generation. +* Derandomized DSA (via RFC6979 or with a caller provided function.) +* Very efficient implementation. + +Implementation details +---------------------- + +* General + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") +* Field operations + * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). + * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). + * Using 10 26-bit limbs. + * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). +* Scalar operations + * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. + * Using 4 64-bit limbs (relying on __int128 support in the compiler). + * Using 8 32-bit limbs. +* Group operations + * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). + * Use addition between points in Jacobian and affine coordinates where possible. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. +* Point multiplication for verification (a*P + b*G). + * Use wNAF notation for point multiplicands. + * Use a much larger window for multiples of G, using precomputed multiples. + * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. + * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. +* Point multiplication for signing + * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. + +Build steps +----------- + +libsecp256k1 is built using autotools: + + $ ./autogen.sh + $ ./configure + $ make + $ ./tests + $ sudo make install # optional diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/TODO b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/TODO new file mode 100644 index 0000000..a300e1c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/TODO @@ -0,0 +1,3 @@ +* Unit tests for fieldelem/groupelem, including ones intended to + trigger fieldelem's boundary cases. +* Complete constant-time operations for signing/keygen diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/autogen.sh b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/autogen.sh new file mode 100755 index 0000000..65286b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +autoreconf -if --warnings=all diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 0000000..1fc3627 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_JNI_INCLUDE_DIR +# +# DESCRIPTION +# +# AX_JNI_INCLUDE_DIR finds include directories needed for compiling +# programs using the JNI interface. +# +# JNI include directories are usually in the Java distribution. This is +# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in +# that order. When this macro completes, a list of directories is left in +# the variable JNI_INCLUDE_DIRS. +# +# Example usage follows: +# +# AX_JNI_INCLUDE_DIR +# +# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS +# do +# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" +# done +# +# If you want to force a specific compiler: +# +# - at the configure.in level, set JAVAC=yourcompiler before calling +# AX_JNI_INCLUDE_DIR +# +# - at the configure level, setenv JAVAC +# +# Note: This macro can work with the autoconf M4 macros for Java programs. +# This particular macro is not part of the original set of macros. +# +# LICENSE +# +# Copyright (c) 2008 Don Anderson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) +AC_DEFUN([AX_JNI_INCLUDE_DIR],[ + +JNI_INCLUDE_DIRS="" + +if test "x$JAVA_HOME" != x; then + _JTOPDIR="$JAVA_HOME" +else + if test "x$JAVAC" = x; then + JAVAC=javac + fi + AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) + if test "x$_ACJNI_JAVAC" = xno; then + AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME]) + fi + _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") + _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` +fi + +case "$host_os" in + darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + _JINC="$_JTOPDIR/Headers";; + *) _JINC="$_JTOPDIR/include";; +esac +_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) +_AS_ECHO_LOG([_JINC=$_JINC]) + +# On Mac OS X 10.6.4, jni.h is a symlink: +# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h +# -> ../../CurrentJDK/Headers/jni.h. + +AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, +[ +if test -f "$_JINC/jni.h"; then + ac_cv_jni_header_path="$_JINC" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" +else + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + ac_cv_jni_header_path="$_JTOPDIR/include" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" + else + ac_cv_jni_header_path=none + fi +fi +]) + + + +# get the likely subdirectories for system specific java includes +case "$host_os" in +bsdi*) _JNI_INC_SUBDIRS="bsdos";; +darwin*) _JNI_INC_SUBDIRS="darwin";; +freebsd*) _JNI_INC_SUBDIRS="freebsd";; +linux*) _JNI_INC_SUBDIRS="linux genunix";; +osf*) _JNI_INC_SUBDIRS="alpha";; +solaris*) _JNI_INC_SUBDIRS="solaris";; +mingw*) _JNI_INC_SUBDIRS="win32";; +cygwin*) _JNI_INC_SUBDIRS="win32";; +*) _JNI_INC_SUBDIRS="genunix";; +esac + +if test "x$ac_cv_jni_header_path" != "xnone"; then + # add any subdirectories that are present + for JINCSUBDIR in $_JNI_INC_SUBDIRS + do + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi + done +fi +]) + +# _ACJNI_FOLLOW_SYMLINKS +# Follows symbolic links on , +# finally setting variable _ACJNI_FOLLOWED +# ---------------------------------------- +AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ +# find the include directory relative to the javac executable +_cur="$1" +while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do + AC_MSG_CHECKING([symlink for $_cur]) + _slink=`ls -ld "$_cur" | sed 's/.* -> //'` + case "$_slink" in + /*) _cur="$_slink";; + # 'X' avoids triggering unwanted echo options. + *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; + esac + AC_MSG_RESULT([$_cur]) +done +_ACJNI_FOLLOWED="$_cur" +])# _ACJNI diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 0000000..77fd346 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_EXEEXT])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_cpp], ac_build_cpp)dnl +pushdef([ac_compile], ac_build_compile)dnl +pushdef([ac_link], ac_build_link)dnl + +save_cross_compiling=$cross_compiling +save_ac_tool_prefix=$ac_tool_prefix +cross_compiling=no +ac_tool_prefix= + +AC_PROG_CC +AC_PROG_CPP +AC_EXEEXT + +ac_tool_prefix=$save_ac_tool_prefix +cross_compiling=$save_cross_compiling + +dnl Restore the old definitions +dnl +popdef([ac_link])dnl +popdef([ac_compile])dnl +popdef([ac_cpp])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([LDFLAGS])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl Finally, set Makefile variables +dnl +BUILD_EXEEXT=$ac_build_exeext +BUILD_OBJEXT=$ac_build_objext +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 new file mode 100644 index 0000000..b74acb8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 @@ -0,0 +1,69 @@ +dnl libsecp25k1 helper checks +AC_DEFUN([SECP_INT128_CHECK],[ +has_int128=$ac_cv_type___int128 +]) + +dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. +AC_DEFUN([SECP_64BIT_ASM_CHECK],[ +AC_MSG_CHECKING(for x86_64 assembly availability) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include ]],[[ + uint64_t a = 11, tmp; + __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); + ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) +AC_MSG_RESULT([$has_64bit_asm]) +]) + +dnl +AC_DEFUN([SECP_OPENSSL_CHECK],[ + has_libcrypto=no + m4_ifdef([PKG_CHECK_MODULES],[ + PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no]) + if test x"$has_libcrypto" = x"yes"; then + TEMP_LIBS="$LIBS" + LIBS="$LIBS $CRYPTO_LIBS" + AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no]) + LIBS="$TEMP_LIBS" + fi + ]) + if test x$has_libcrypto = xno; then + AC_CHECK_HEADER(openssl/crypto.h,[ + AC_CHECK_LIB(crypto, main,[ + has_libcrypto=yes + CRYPTO_LIBS=-lcrypto + AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed]) + ]) + ]) + LIBS= + fi +if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then + AC_MSG_CHECKING(for EC functions in libcrypto) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + #include ]],[[ + EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1); + ECDSA_sign(0, NULL, 0, NULL, NULL, eckey); + ECDSA_verify(0, NULL, 0, NULL, 0, eckey); + EC_KEY_free(eckey); + ECDSA_SIG *sig_openssl; + sig_openssl = ECDSA_SIG_new(); + (void)sig_openssl->r; + ECDSA_SIG_free(sig_openssl); + ]])],[has_openssl_ec=yes],[has_openssl_ec=no]) + AC_MSG_RESULT([$has_openssl_ec]) +fi +]) + +dnl +AC_DEFUN([SECP_GMP_CHECK],[ +if test x"$has_gmp" != x"yes"; then + CPPFLAGS_TEMP="$CPPFLAGS" + CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS" + LIBS_TEMP="$LIBS" + LIBS="$GMP_LIBS $LIBS" + AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])]) + CPPFLAGS="$CPPFLAGS_TEMP" + LIBS="$LIBS_TEMP" +fi +]) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/configure.ac b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/configure.ac new file mode 100644 index 0000000..e5fcbcb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/configure.ac @@ -0,0 +1,493 @@ +AC_PREREQ([2.60]) +AC_INIT([libsecp256k1],[0.1]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AC_CANONICAL_HOST +AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) +AH_TOP([#define LIBSECP256K1_CONFIG_H]) +AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) +LT_INIT + +dnl make the compilation flags quiet unless V=1 is used +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +PKG_PROG_PKG_CONFIG + +AC_PATH_TOOL(AR, ar) +AC_PATH_TOOL(RANLIB, ranlib) +AC_PATH_TOOL(STRIP, strip) +AX_PROG_CC_FOR_BUILD + +if test "x$CFLAGS" = "x"; then + CFLAGS="-g" +fi + +AM_PROG_CC_C_O + +AC_PROG_CC_C89 +if test x"$ac_cv_prog_cc_c89" = x"no"; then + AC_MSG_ERROR([c89 compiler support required]) +fi +AM_PROG_AS + +case $host_os in + *darwin*) + if test x$cross_compiling != xyes; then + AC_PATH_PROG([BREW],brew,) + if test x$BREW != x; then + dnl These Homebrew packages may be keg-only, meaning that they won't be found + dnl in expected paths because they may conflict with system files. Ask + dnl Homebrew where each one is located, then adjust paths accordingly. + + openssl_prefix=`$BREW --prefix openssl 2>/dev/null` + gmp_prefix=`$BREW --prefix gmp 2>/dev/null` + if test x$openssl_prefix != x; then + PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" + export PKG_CONFIG_PATH + fi + if test x$gmp_prefix != x; then + GMP_CPPFLAGS="-I$gmp_prefix/include" + GMP_LIBS="-L$gmp_prefix/lib" + fi + else + AC_PATH_PROG([PORT],port,) + dnl if homebrew isn't installed and macports is, add the macports default paths + dnl as a last resort. + if test x$PORT != x; then + CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + fi + fi + fi + ;; +esac + +CFLAGS="$CFLAGS -W" + +warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings" +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $warn_CFLAGS" +AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden" +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +AC_ARG_ENABLE(benchmark, + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + [use_benchmark=$enableval], + [use_benchmark=no]) + +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + [enable_coverage=$enableval], + [enable_coverage=no]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + [use_tests=$enableval], + [use_tests=yes]) + +AC_ARG_ENABLE(openssl_tests, + AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), + [enable_openssl_tests=$enableval], + [enable_openssl_tests=auto]) + +AC_ARG_ENABLE(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + +AC_ARG_ENABLE(exhaustive_tests, + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), + [use_exhaustive_tests=$enableval], + [use_exhaustive_tests=yes]) + +AC_ARG_ENABLE(endomorphism, + AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), + [use_endomorphism=$enableval], + [use_endomorphism=no]) + +AC_ARG_ENABLE(ecmult_static_precomputation, + AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), + [use_ecmult_static_precomputation=$enableval], + [use_ecmult_static_precomputation=auto]) + +AC_ARG_ENABLE(module_ecdh, + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), + [enable_module_ecdh=$enableval], + [enable_module_ecdh=no]) + +AC_ARG_ENABLE(module_recovery, + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), + [enable_module_recovery=$enableval], + [enable_module_recovery=no]) + +AC_ARG_ENABLE(jni, + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]), + [use_jni=$enableval], + [use_jni=auto]) + +AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], +[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) + +AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], +[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) + +AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], +[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) + +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] +[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) + +AC_CHECK_TYPES([__int128]) + +AC_MSG_CHECKING([for __builtin_expect]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ], + [ AC_MSG_RESULT([no]) + ]) + +if test x"$enable_coverage" = x"yes"; then + AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) + CFLAGS="$CFLAGS -O0 --coverage" + LDFLAGS="--coverage" +else + CFLAGS="$CFLAGS -O3" +fi + +if test x"$use_ecmult_static_precomputation" != x"no"; then + save_cross_compiling=$cross_compiling + cross_compiling=no + TEMP_CC="$CC" + CC="$CC_FOR_BUILD" + AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([], [return 0])], + [working_native_cc=yes], + [working_native_cc=no],[dnl]) + CC="$TEMP_CC" + cross_compiling=$save_cross_compiling + + if test x"$working_native_cc" = x"no"; then + set_precomp=no + if test x"$use_ecmult_static_precomputation" = x"yes"; then + AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + else + AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + +if test x"$req_asm" = x"auto"; then + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" = x"yes"; then + set_asm=x86_64 + fi + if test x"$set_asm" = x; then + set_asm=no + fi +else + set_asm=$req_asm + case $set_asm in + x86_64) + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" != x"yes"; then + AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) + fi + ;; + arm) + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid assembly optimization selection]) + ;; + esac +fi + +if test x"$req_field" = x"auto"; then + if test x"set_asm" = x"x86_64"; then + set_field=64bit + fi + if test x"$set_field" = x; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_field=64bit + fi + fi + if test x"$set_field" = x; then + set_field=32bit + fi +else + set_field=$req_field + case $set_field in + 64bit) + if test x"$set_asm" != x"x86_64"; then + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available]) + fi + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid field implementation selection]) + ;; + esac +fi + +if test x"$req_scalar" = x"auto"; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_scalar=64bit + fi + if test x"$set_scalar" = x; then + set_scalar=32bit + fi +else + set_scalar=$req_scalar + case $set_scalar in + 64bit) + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available]) + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid scalar implementation selected]) + ;; + esac +fi + +if test x"$req_bignum" = x"auto"; then + SECP_GMP_CHECK + if test x"$has_gmp" = x"yes"; then + set_bignum=gmp + fi + + if test x"$set_bignum" = x; then + set_bignum=no + fi +else + set_bignum=$req_bignum + case $set_bignum in + gmp) + SECP_GMP_CHECK + if test x"$has_gmp" != x"yes"; then + AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available]) + fi + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid bignum implementation selection]) + ;; + esac +fi + +# select assembly optimization +use_external_asm=no + +case $set_asm in +x86_64) + AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) + ;; +arm) + use_external_asm=yes + ;; +no) + ;; +*) + AC_MSG_ERROR([invalid assembly optimizations]) + ;; +esac + +# select field implementation +case $set_field in +64bit) + AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation]) + ;; +32bit) + AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation]) + ;; +*) + AC_MSG_ERROR([invalid field implementation]) + ;; +esac + +# select bignum implementation +case $set_bignum in +gmp) + AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed]) + AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num]) + AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation]) + ;; +no) + AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation]) + AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation]) + ;; +*) + AC_MSG_ERROR([invalid bignum implementation]) + ;; +esac + +#select scalar implementation +case $set_scalar in +64bit) + AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation]) + ;; +32bit) + AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation]) + ;; +*) + AC_MSG_ERROR([invalid scalar implementation]) + ;; +esac + +if test x"$use_tests" = x"yes"; then + SECP_OPENSSL_CHECK + if test x"$has_openssl_ec" = x"yes"; then + if test x"$enable_openssl_tests" != x"no"; then + AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) + SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" + SECP_TEST_LIBS="$CRYPTO_LIBS" + + case $host in + *mingw*) + SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" + ;; + esac + fi + else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available]) + fi + fi +else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled]) + fi +fi + +if test x"$use_jni" != x"no"; then + AX_JNI_INCLUDE_DIR + have_jni_dependencies=yes + if test x"$enable_module_ecdh" = x"no"; then + have_jni_dependencies=no + fi + if test "x$JNI_INCLUDE_DIRS" = "x"; then + have_jni_dependencies=no + fi + if test "x$have_jni_dependencies" = "xno"; then + if test x"$use_jni" = x"yes"; then + AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.]) + fi + AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) + use_jni=no + else + use_jni=yes + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do + JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR" + done + fi +fi + +if test x"$set_bignum" = x"gmp"; then + SECP_LIBS="$SECP_LIBS $GMP_LIBS" + SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS" +fi + +if test x"$use_endomorphism" = x"yes"; then + AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) +fi + +if test x"$set_precomp" = x"yes"; then + AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) +fi + +if test x"$enable_module_ecdh" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +fi + +if test x"$enable_module_recovery" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) +fi + +AC_C_BIGENDIAN() + +if test x"$use_external_asm" = x"yes"; then + AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) +fi + +AC_MSG_NOTICE([Using static precomputation: $set_precomp]) +AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) +AC_MSG_NOTICE([Using field implementation: $set_field]) +AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) +AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) +AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) +AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage]) +AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) +AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) +AC_MSG_NOTICE([Using jni: $use_jni]) + +if test x"$enable_experimental" = x"yes"; then + AC_MSG_NOTICE([******]) + AC_MSG_NOTICE([WARNING: experimental build]) + AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) + AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([******]) +else + if test x"$enable_module_ecdh" = x"yes"; then + AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$set_asm" = x"arm"; then + AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) + fi +fi + +AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) +AC_CONFIG_FILES([Makefile libsecp256k1.pc]) +AC_SUBST(JNI_INCLUDES) +AC_SUBST(SECP_INCLUDES) +AC_SUBST(SECP_LIBS) +AC_SUBST(SECP_TEST_LIBS) +AC_SUBST(SECP_TEST_INCLUDES) +AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) +AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) +AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) +AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) + +dnl make sure nothing new is exported so that we don't break the cache +PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" +unset PKG_CONFIG_PATH +PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" + +AC_OUTPUT diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c new file mode 100644 index 0000000..5b141a9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.c @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_parsing.h" + +int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + size_t rpos, rlen, spos, slen; + size_t pos = 0; + size_t lenbyte; + unsigned char tmpsig[64] = {0}; + int overflow = 0; + + /* Hack to initialize sig with a correctly-parsed but invalid signature. */ + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + + /* Sequence tag byte */ + if (pos == inputlen || input[pos] != 0x30) { + return 0; + } + pos++; + + /* Sequence length bytes */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + pos += lenbyte; + } + + /* Integer tag byte for R */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for R */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + rlen = 0; + while (lenbyte > 0) { + rlen = (rlen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + rlen = lenbyte; + } + if (rlen > inputlen - pos) { + return 0; + } + rpos = pos; + pos += rlen; + + /* Integer tag byte for S */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for S */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + slen = 0; + while (lenbyte > 0) { + slen = (slen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + slen = lenbyte; + } + if (slen > inputlen - pos) { + return 0; + } + spos = pos; + pos += slen; + + /* Ignore leading zeroes in R */ + while (rlen > 0 && input[rpos] == 0) { + rlen--; + rpos++; + } + /* Copy R value */ + if (rlen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 32 - rlen, input + rpos, rlen); + } + + /* Ignore leading zeroes in S */ + while (slen > 0 && input[spos] == 0) { + slen--; + spos++; + } + /* Copy S value */ + if (slen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 64 - slen, input + spos, slen); + } + + if (!overflow) { + overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + if (overflow) { + memset(tmpsig, 0, 64); + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + return 1; +} + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h new file mode 100644 index 0000000..6d27871 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_parsing.h @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file defines a function that parses DER with various errors and + * violations. This is not a part of the library itself, because the allowed + * violations are chosen arbitrarily and do not follow or establish any + * standard. + * + * In many places it matters that different implementations do not only accept + * the same set of valid signatures, but also reject the same set of signatures. + * The only means to accomplish that is by strictly obeying a standard, and not + * accepting anything else. + * + * Nonetheless, sometimes there is a need for compatibility with systems that + * use signatures which do not strictly obey DER. The snippet below shows how + * certain violations are easily supported. You may need to adapt it. + * + * Do not use this for new systems. Use well-defined DER or compact signatures + * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and + * secp256k1_ecdsa_signature_parse_compact). + * + * The supported violations are: + * - All numbers are parsed as nonnegative integers, even though X.609-0207 + * section 8.3.3 specifies that integers are always encoded as two's + * complement. + * - Integers can have length 0, even though section 8.3.1 says they can't. + * - Integers with overly long padding are accepted, violation section + * 8.3.2. + * - 127-byte long length descriptors are accepted, even though section + * 8.1.3.5.c says that they are not. + * - Trailing garbage data inside or after the signature is ignored. + * - The length descriptor of the sequence is ignored. + * + * Compared to for example OpenSSL, many violations are NOT supported: + * - Using overly long tag descriptors for the sequence or integers inside, + * violating section 8.1.2.2. + * - Encoding primitive integers as constructed values, violating section + * 8.3.1. + */ + +#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Parse a signature in "lax DER" format + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. In addition, it will accept signatures + * which violate the DER spec in various ways. Its purpose is to allow + * validation of the Bitcoin blockchain, which includes non-DER signatures + * from before the network rules were updated to enforce DER. Note that + * the set of supported violations is a strict subset of what OpenSSL will + * accept. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +int ecdsa_signature_parse_der_lax( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c new file mode 100644 index 0000000..c2e63b4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_privatekey_parsing.h" + +int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) { + const unsigned char *end = privkey + privkeylen; + int lenb = 0; + int len = 0; + memset(out32, 0, 32); + /* sequence header */ + if (end < privkey+1 || *privkey != 0x30) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end < privkey+1 || !(*privkey & 0x80)) { + return 0; + } + lenb = *privkey & ~0x80; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end < privkey+lenb) { + return 0; + } + /* sequence length */ + len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); + privkey += lenb; + if (end < privkey+len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) { + return 0; + } + memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); + if (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 33; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11, + 0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10, + 0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 65; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } + return 1; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h new file mode 100644 index 0000000..2fd088f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h @@ -0,0 +1,90 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file contains code snippets that parse DER private keys with + * various errors and violations. This is not a part of the library + * itself, because the allowed violations are chosen arbitrarily and + * do not follow or establish any standard. + * + * It also contains code to serialize private keys in a compatible + * manner. + * + * These functions are meant for compatibility with applications + * that require BER encoded keys. When working with secp256k1-specific + * code, the simple 32-byte private keys normally used by the + * library are sufficient. + */ + +#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Export a private key in DER format. + * + * Returns: 1 if the private key was valid. + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: privkey: pointer to an array for storing the private key in BER. + * Should have space for 279 bytes, and cannot be NULL. + * privkeylen: Pointer to an int where the length of the private key in + * privkey will be stored. + * In: seckey: pointer to a 32-byte secret key to export. + * compressed: 1 if the key should be exported in + * compressed format, 0 otherwise + * + * This function is purely meant for compatibility with applications that + * require BER encoded keys. When working with secp256k1-specific code, the + * simple 32-byte private keys are sufficient. + * + * Note that this function does not guarantee correct DER output. It is + * guaranteed to be parsable by secp256k1_ec_privkey_import_der + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der( + const secp256k1_context* ctx, + unsigned char *privkey, + size_t *privkeylen, + const unsigned char *seckey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. + * Returns: 1 if a private key was extracted. + * Args: ctx: pointer to a context object (cannot be NULL). + * Out: seckey: pointer to a 32-byte array for storing the private key. + * (cannot be NULL). + * In: privkey: pointer to a private key in DER format (cannot be NULL). + * privkeylen: length of the DER private key pointed to be privkey. + * + * This function will accept more than just strict DER, and even allow some BER + * violations. The public key stored inside the DER-encoded private key is not + * verified for correctness, nor are the curve parameters. Use this function + * only if you know in advance it is supposed to contain a secp256k1 private + * key. + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *privkey, + size_t privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1.h new file mode 100644 index 0000000..f268e30 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1.h @@ -0,0 +1,577 @@ +#ifndef _SECP256K1_ +# define _SECP256K1_ + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/* These rules specify the order of arguments in API calls: + * + * 1. Context pointers go first, followed by output arguments, combined + * output/input arguments, and finally input-only arguments. + * 2. Array lengths always immediately the follow the argument whose length + * they describe, even if this violates rule 1. + * 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated + * later go first. This means: signatures, public nonces, private nonces, + * messages, public keys, secret keys, tweaks. + * 4. Arguments that are not data pointers go last, from more complex to less + * complex: function pointers, algorithm names, messages, void pointers, + * counts, flags, booleans. + * 5. Opaque data pointers follow the function pointer they are to be passed to. + */ + +/** Opaque data structure that holds context information (precomputed tables etc.). + * + * The purpose of context structures is to cache large precomputed data tables + * that are expensive to construct, and also to maintain the randomization data + * for blinding. + * + * Do not create a new context object for each operation, as construction is + * far slower than all other API calls (~100 times slower than an ECDSA + * verification). + * + * A constructed context can safely be used from multiple threads + * simultaneously, but API call that take a non-const pointer to a context + * need exclusive access to it. In particular this is the case for + * secp256k1_context_destroy and secp256k1_context_randomize. + * + * Regarding randomization, either do it once at creation time (in which case + * you do not need any locking for the other calls), or use a read-write lock. + */ +typedef struct secp256k1_context_struct secp256k1_context; + +/** Opaque data structure that holds a parsed and valid public key. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey; + +/** Opaque data structured that holds a parsed ECDSA signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_serialize_* functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_signature; + +/** A pointer to a function to deterministically generate a nonce. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * data: Arbitrary data pointer that is passed through. + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the algorithm, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + void *data, + unsigned int attempt +); + +# if !defined(SECP256K1_GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define SECP256K1_GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 +# endif +# endif + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +#ifndef SECP256K1_API +# if defined(_WIN32) +# ifdef SECP256K1_BUILD +# define SECP256K1_API __declspec(dllexport) +# else +# define SECP256K1_API +# endif +# elif defined(__GNUC__) && defined(SECP256K1_BUILD) +# define SECP256K1_API __attribute__ ((visibility ("default"))) +# else +# define SECP256K1_API +# endif +#endif + +/**Warning attributes + * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out + * some paranoid null checks. */ +# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) +# else +# define SECP256K1_WARN_UNUSED_RESULT +# endif +# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) +# else +# define SECP256K1_ARG_NONNULL(_x) +# endif + +/** All flags' lower 8 bits indicate what they're for. Do not use directly. */ +#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) +#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) +#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1) +/** The higher bits contain the actual data. Do not use directly. */ +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) + +/** Flags to pass to secp256k1_context_create. */ +#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) +#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) +#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) + +/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */ +#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) +#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) + +/** Create a secp256k1 context object. + * + * Returns: a newly created context object. + * In: flags: which parts of the context to initialize. + */ +SECP256K1_API secp256k1_context* secp256k1_context_create( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Copies a secp256k1 context object. + * + * Returns: a newly created context object. + * Args: ctx: an existing context to copy (cannot be NULL) + */ +SECP256K1_API secp256k1_context* secp256k1_context_clone( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object. + * + * The context pointer may not be used afterwards. + * Args: ctx: an existing context to destroy (cannot be NULL) + */ +SECP256K1_API void secp256k1_context_destroy( + secp256k1_context* ctx +); + +/** Set a callback function to be called when an illegal argument is passed to + * an API call. It will only trigger for violations that are mentioned + * explicitly in the header. + * + * The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * to cause a crash, though its return value and output arguments are + * undefined. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an illegal argument is + * passed to the API, taking a message and an opaque pointer + * (NULL restores a default handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_illegal_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Set a callback function to be called when an internal consistency check + * fails. The default is crashing. + * + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see secp256k1_context_set_illegal_callback + * for that). After this callback returns, anything may happen, including + * crashing. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an internal error occurs, + * taking a message and an opaque pointer (NULL restores a default + * handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_error_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Parse a variable-length public key into the pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * Args: ctx: a secp256k1 context object. + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * In: input: pointer to a serialized public key + * inputlen: length of the array pointed to by input + * + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * byte 0x06 or 0x07) format public keys. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( + const secp256k1_context* ctx, + secp256k1_pubkey* pubkey, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a pubkey object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * compressed==1) byte array to place the serialized key + * in. + * In/Out: outputlen: a pointer to an integer which is initially set to the + * size of output, and is overwritten with the written + * size. + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key. + * flags: SECP256K1_EC_COMPRESSED if serialization should be in + * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. + */ +SECP256K1_API int secp256k1_ec_pubkey_serialize( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_pubkey* pubkey, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Parse an ECDSA signature in compact (64 bytes) format. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to the 64-byte array to parse + * + * The signature must consist of a 32-byte big endian R value, followed by a + * 32-byte big endian S value. If R or S fall outside of [0..order-1], the + * encoding is invalid. R and S with value 0 are allowed in the encoding. + * + * After the call, sig will always be initialized. If parsing failed or R or + * S are zero, the resulting sig value is guaranteed to fail validation for any + * message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a DER ECDSA signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_der( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in DER format. + * + * Returns: 1 if enough space was available to serialize, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: output: a pointer to an array to store the DER serialization + * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * should be set to the length of output. After the call + * it will be set to the length of the serialization (even + * if 0 was returned). + * In: sig: a pointer to an initialized signature object + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Serialize an ECDSA signature in compact (64 byte) format. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array to store the compact serialization + * In: sig: a pointer to an initialized signature object + * + * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify an ECDSA signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + * + * To avoid accepting malleable signatures, only ECDSA signatures in lower-S + * form are accepted. + * + * If you need to accept ECDSA signatures from sources that do not obey this + * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to + * validation, but be aware that doing so results in malleable signatures. + * + * For details, see the comments for that function. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Convert a signature to a normalized lower-S form. + * + * Returns: 1 if sigin was not normalized, 0 if it already was. + * Args: ctx: a secp256k1 context object + * Out: sigout: a pointer to a signature to fill with the normalized form, + * or copy if the input was already normalized. (can be NULL if + * you're only interested in whether the input was already + * normalized). + * In: sigin: a pointer to a signature to check/normalize (cannot be NULL, + * can be identical to sigout) + * + * With ECDSA a third-party can forge a second distinct signature of the same + * message, given a single initial signature, but without knowing the key. This + * is done by negating the S value modulo the order of the curve, 'flipping' + * the sign of the random point R which is not included in the signature. + * + * Forgery of the same message isn't universally problematic, but in systems + * where message malleability or uniqueness of signatures is important this can + * cause issues. This forgery can be blocked by all verifiers forcing signers + * to use a normalized form. + * + * The lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to verify, + * making it a good choice. Security of always using lower-S is assured because + * anyone can trivially modify a signature after the fact to enforce this + * property anyway. + * + * The lower S value is always between 0x1 and + * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive. + * + * No other forms of ECDSA malleability are known and none seem likely, but + * there is no formal proof that ECDSA, even with this additional restriction, + * is free of other malleability. Commonly used serialization schemes will also + * accept various non-unique encodings, so care should be taken when this + * property is required for an application. + * + * The secp256k1_ecdsa_sign function will by default create signatures in the + * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case + * signatures come from a system that cannot enforce this property, + * secp256k1_ecdsa_signature_normalize must be called before verification. + */ +SECP256K1_API int secp256k1_ecdsa_signature_normalize( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sigout, + const secp256k1_ecdsa_signature *sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); + +/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; + +/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; + +/** Create an ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * + * The created signature is always in lower-S form. See + * secp256k1_ecdsa_signature_normalize for more details. + */ +SECP256K1_API int secp256k1_ecdsa_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify an ECDSA secret key. + * + * Returns: 1: secret key is valid + * 0: secret key is invalid + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seckey: pointer to a 32-byte secret key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + const secp256k1_context* ctx, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Compute the public key for a secret key. + * + * Returns: 1: secret was valid, public key stores + * 0: secret was invalid, try again + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: pubkey: pointer to the created public key (cannot be NULL) + * In: seckey: pointer to a 32-byte private key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by adding tweak to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting private key + * would be invalid (only when the tweak is the complement of the + * private key). 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by adding tweak times the generator to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting public key + * would be invalid (only when the tweak is the complement of the + * corresponding private key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key object. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by multiplying it by a tweak. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by multiplying it by a tweak value. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key obkect. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates the context randomization. + * Returns: 1: randomization successfully updated + * 0: error + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + secp256k1_context* ctx, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1); + +/** Add a number of public keys together. + * Returns: 1: the sum of the public keys is valid. + * 0: the sum of the public keys is not valid. + * Args: ctx: pointer to a context object + * Out: out: pointer to a public key object for placing the resulting public key + * (cannot be NULL) + * In: ins: pointer to array of pointers to public keys (cannot be NULL) + * n: the number of public keys to add together (must be at least 1) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_pubkey *out, + const secp256k1_pubkey * const * ins, + size_t n +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h new file mode 100644 index 0000000..4b84d7a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_ecdh.h @@ -0,0 +1,31 @@ +#ifndef _SECP256K1_ECDH_ +# define _SECP256K1_ECDH_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Compute an EC Diffie-Hellman secret in constant time + * Returns: 1: exponentiation was successful + * 0: scalar was invalid (zero or overflow) + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: result: a 32-byte array which will be populated by an ECDH + * secret computed from the point and scalar + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * privkey: a 32-byte scalar with which to multiply the point + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context* ctx, + unsigned char *result, + const secp256k1_pubkey *pubkey, + const unsigned char *privkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h new file mode 100644 index 0000000..0553797 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/include/secp256k1_recovery.h @@ -0,0 +1,110 @@ +#ifndef _SECP256K1_RECOVERY_ +# define _SECP256K1_RECOVERY_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Opaque data structured that holds a parsed ECDSA signature, + * supporting pubkey recovery. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 65 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + * + * Furthermore, it is guaranteed that identical signatures (including their + * recoverability) will have identical representation, so they can be + * memcmp'ed. + */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_recoverable_signature; + +/** Parse a compact ECDSA signature (64 bytes + recovery id). + * + * Returns: 1 when the signature could be parsed, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to a 64-byte compact signature + * recid: the recovery id (0, 1, 2 or 3) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature* sig, + const unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Convert a recoverable signature into a normal signature. + * + * Returns: 1 + * Out: sig: a pointer to a normal signature (cannot be NULL). + * In: sigin: a pointer to a recoverable signature (cannot be NULL). + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const secp256k1_ecdsa_recoverable_signature* sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in compact format (64 bytes + recovery id). + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array of the compact signature (cannot be NULL) + * recid: a pointer to an integer to hold the recovery id (can be NULL). + * In: sig: a pointer to an initialized signature object (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + int *recid, + const secp256k1_ecdsa_recoverable_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Create a recoverable ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_sign_recoverable( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an ECDSA public key from a signature. + * + * Returns: 1: public key successfully recovered (which guarantees a correct signature). + * 0: otherwise. + * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL) + * Out: pubkey: pointer to the recovered public key (cannot be NULL) + * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in new file mode 100644 index 0000000..a0d006f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/libsecp256k1.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsecp256k1 +Description: Optimized C library for EC operations on curve secp256k1 +URL: https://github.com/bitcoin-core/secp256k1 +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs.private: @SECP_LIBS@ +Libs: -L${libdir} -lsecp256k1 + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/obj/.gitignore b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/obj/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/group_prover.sage b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/group_prover.sage new file mode 100644 index 0000000..ab580c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# exectured. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# as we assume that all constraints in it are complementary with each other. +# +# Based on the sage verification scripts used in the Explicit-Formulas Database +# by Tanja Lange and others, see http://hyperelliptic.org/EFD + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +def conflicts(R, con): + """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" + zero = R.ideal(map(numerator, con.zero)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(f) + return nonzero + + +def prove_nonzero(R, exprs, assume): + """Check whether an expression is provably nonzero, given assumptions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + return (False, [exprs[expr]]) + allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) + for (f, n) in allexprs.factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in numerator(expr).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in zero.reduce(numerator(expr)).factor(): + if f not in nonzero: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +def prove_zero(R, exprs, assume): + """Check whether all of the passed expressions are provably zero, given assumptions""" + r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) + if not r: + return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) + zero = R.ideal(map(numerator, assume.zero)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +def describe_extra(R, assume, assumeExtra): + """Describe what assumptions are added, given existing assumptions""" + zerox = assume.zero.copy() + zerox.update(assumeExtra.zero) + zero = R.ideal(map(numerator, assume.zero)) + zeroextra = R.ideal(map(numerator, zerox)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + nzr = zeroextra.reduce(numerator(nz)) + if nzr not in zeroextra: + for (f,n) in nzr.factor(): + if zeroextra.reduce(f) not in nonzero: + ret.add("%s != 0" % zeroextra.reduce(f)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage new file mode 100644 index 0000000..a97e732 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +load("weierstrass_prover.sage") + +def formula_secp256k1_gej_double_var(a): + """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" + rz = a.Z * a.Y + rz = rz * 2 + t1 = a.X^2 + t1 = t1 * 3 + t2 = t1^2 + t3 = a.Y^2 + t3 = t3 * 2 + t4 = t3^2 + t4 = t4 * 2 + t3 = t3 * a.X + rx = t3 + rx = rx * 4 + rx = -rx + rx = rx + t2 + t2 = -t2 + t3 = t3 * 6 + t3 = t3 + t2 + ry = t1 * t3 + t2 = -t4 + ry = ry + t2 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r) + if branch == 3: + return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if (branch == 2): + r = formula_secp256k1_gej_double_var(a) + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if (branch == 3): + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_zinv_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_zinv_var""" + bzinv = b.Z^(-1) + if branch == 0: + return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a) + if branch == 1: + bzinv2 = bzinv^2 + bzinv3 = bzinv2 * bzinv + rx = b.X * bzinv2 + ry = b.Y * bzinv3 + rz = 1 + return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if branch == 3: + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + zeroes.update({m : 'm_zero'}) + else: + nonzeroes.update({m : 'm_nonzero'}) + if (branch & 2) != 0: + zeroes.update({rr : 'rr_zero'}) + else: + nonzeroes.update({rr : 'rr_nonzero'}) + rr_alt = s1 + rr_alt = rr_alt * 2 + m_alt = m_alt + u1 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + n = n^2 + if degenerate: + n = m + t = rr_alt^2 + rz = a.Z * m_alt + infinity = False + if (branch & 8) != 0: + if not a_infinity: + infinity = True + zeroes.update({rz : 'r.z=0'}) + else: + nonzeroes.update({rz : 'r.z!=0'}) + rz = rz * 2 + q = -q + t = t + q + rx = t + t = t * 2 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_old(branch, a, b): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) + +if __name__ == "__main__": + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + + if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage new file mode 100644 index 0000000..03ef2ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field""" + F = Integers(p) + print "Formula %s on Z%i:" % (name, p) + points = [] + for x in xrange(0, p): + for y in xrange(0, p): + point = affinepoint(F(x), F(y)) + r, e = concrete_verify(on_weierstrass_curve(A, B, point)) + if r: + points.append(point) + + for za in xrange(1, p): + for zb in xrange(1, p): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(2): + pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia) + pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib) + for branch in xrange(0, branches): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + r, e = concrete_verify(assumeLaw) + if r: + if match: + print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity) + else: + match = True + r, e = concrete_verify(require) + if not r: + print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e) + print + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + pC.X = lift(pC.X) + pC.Y = lift(pC.Y) + pC.Z = lift(pC.Z) + pC.Infinity = lift(pC.Infinity) + + for key in laws_jacobian_weierstrass: + res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + + for key in res: + print " %s:" % key + val = res[key] + for x in val: + if x[0] is not None: + print " branch %i: %s" % (x[1], x[0]) + + print diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 0000000..5df561f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm: +/********************************************************************** + * Copyright (c) 2014 Wladimir J. van der Laan * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the the even passes + +*/ + + .syntax unified + .arch armv7-a + @ eabi attributes - see readelf -A + .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes + .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no + .eabi_attribute 10, 0 @ Tag_FP_arch = none + .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte + .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 + .text + + @ Field constants + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/basic-config.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/basic-config.h new file mode 100644 index 0000000..c4c16eb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/basic-config.h @@ -0,0 +1,32 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BASIC_CONFIG_ +#define _SECP256K1_BASIC_CONFIG_ + +#ifdef USE_BASIC_CONFIG + +#undef USE_ASM_X86_64 +#undef USE_ENDOMORPHISM +#undef USE_FIELD_10X26 +#undef USE_FIELD_5X52 +#undef USE_FIELD_INV_BUILTIN +#undef USE_FIELD_INV_NUM +#undef USE_NUM_GMP +#undef USE_NUM_NONE +#undef USE_SCALAR_4X64 +#undef USE_SCALAR_8X32 +#undef USE_SCALAR_INV_BUILTIN +#undef USE_SCALAR_INV_NUM + +#define USE_NUM_NONE 1 +#define USE_FIELD_INV_BUILTIN 1 +#define USE_SCALAR_INV_BUILTIN 1 +#define USE_FIELD_10X26 1 +#define USE_SCALAR_8X32 1 + +#endif // USE_BASIC_CONFIG +#endif // _SECP256K1_BASIC_CONFIG_ diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench.h new file mode 100644 index 0000000..3a71b4a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench.h @@ -0,0 +1,66 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BENCH_H_ +#define _SECP256K1_BENCH_H_ + +#include +#include +#include "sys/time.h" + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +void print_number(double x) { + double y = x; + int c = 0; + if (y < 0.0) { + y = -y; + } + while (y < 100.0) { + y *= 10.0; + c++; + } + printf("%.*f", c, x); +} + +void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { + int i; + double min = HUGE_VAL; + double sum = 0.0; + double max = 0.0; + for (i = 0; i < count; i++) { + double begin, total; + if (setup != NULL) { + setup(data); + } + begin = gettimedouble(); + benchmark(data); + total = gettimedouble() - begin; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } + sum += total; + } + printf("%s: min ", name); + print_number(min * 1000000.0 / iter); + printf("us / avg "); + print_number((sum / count) * 1000000.0 / iter); + printf("us / max "); + print_number(max * 1000000.0 / iter); + printf("us\n"); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c new file mode 100644 index 0000000..cde5e2d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_ecdh.c @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char scalar[32]; +} bench_ecdh_t; + +static void bench_ecdh_setup(void* arg) { + int i; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + + /* create a context with no capabilities */ + data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + for (i = 0; i < 32; i++) { + data->scalar[i] = i + 1; + } + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ecdh(void* arg) { + int i; + unsigned char res[32]; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + } +} + +int main(void) { + bench_ecdh_t data; + + run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_internal.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_internal.c new file mode 100644 index 0000000..0809f77 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_internal.c @@ -0,0 +1,382 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "util.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "field_impl.h" +#include "group_impl.h" +#include "scalar_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_impl.h" +#include "bench.h" +#include "secp256k1.c" + +typedef struct { + secp256k1_scalar scalar_x, scalar_y; + secp256k1_fe fe_x, fe_y; + secp256k1_ge ge_x, ge_y; + secp256k1_gej gej_x, gej_y; + unsigned char data[64]; + int wnaf[256]; +} bench_inv_t; + +void bench_setup(void* arg) { + bench_inv_t *data = (bench_inv_t*)arg; + + static const unsigned char init_x[32] = { + 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, + 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, + 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, + 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83 + }; + + static const unsigned char init_y[32] = { + 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83, + 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5, + 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9, + 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3 + }; + + secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL); + secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL); + secp256k1_fe_set_b32(&data->fe_x, init_x); + secp256k1_fe_set_b32(&data->fe_y, init_y); + CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0)); + CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1)); + secp256k1_gej_set_ge(&data->gej_x, &data->ge_x); + secp256k1_gej_set_ge(&data->gej_y, &data->ge_y); + memcpy(data->data, init_x, 32); + memcpy(data->data + 32, init_y, 32); +} + +void bench_scalar_add(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_negate(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +#ifdef USE_ENDOMORPHISM +void bench_scalar_split(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_scalar l, r; + secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} +#endif + +void bench_scalar_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_field_normalize(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize(&data->fe_x); + } +} + +void bench_field_normalize_weak(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize_weak(&data->fe_x); + } +} + +void bench_field_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); + } +} + +void bench_field_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_sqr(&data->fe_x, &data->fe_x); + } +} + +void bench_field_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_sqrt(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_group_double_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); + } +} + +void bench_group_add_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); + } +} + +void bench_group_add_affine(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y); + } +} + +void bench_group_add_affine_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); + } +} + +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); + } +} + +void bench_ecmult_wnaf(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_wnaf_const(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + + +void bench_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_sha256_t sha; + + for (i = 0; i < 20000; i++) { + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data->data, 32); + secp256k1_sha256_finalize(&sha, data->data); + } +} + +void bench_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_hmac_sha256_t hmac; + + for (i = 0; i < 20000; i++) { + secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); + secp256k1_hmac_sha256_write(&hmac, data->data, 32); + secp256k1_hmac_sha256_finalize(&hmac, data->data); + } +} + +void bench_rfc6979_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_rfc6979_hmac_sha256_t rng; + + for (i = 0; i < 20000; i++) { + secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); + secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32); + } +} + +void bench_context_verify(void* arg) { + int i; + (void)arg; + for (i = 0; i < 20; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + } +} + +void bench_context_sign(void* arg) { + int i; + (void)arg; + for (i = 0; i < 200; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN)); + } +} + +#ifndef USE_NUM_NONE +void bench_num_jacobi(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_num nx, norder; + + secp256k1_scalar_get_num(&nx, &data->scalar_x); + secp256k1_scalar_order_get_num(&norder); + secp256k1_scalar_get_num(&norder, &data->scalar_y); + + for (i = 0; i < 200000; i++) { + secp256k1_num_jacobi(&nx, &norder); + } +} +#endif + +int have_flag(int argc, char** argv, char *flag) { + char** argm = argv + argc; + argv++; + if (argv == argm) { + return 1; + } + while (argv != NULL && argv != argm) { + if (strcmp(*argv, flag) == 0) { + return 1; + } + argv++; + } + return 0; +} + +int main(int argc, char **argv) { + bench_inv_t data; + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000); +#ifdef USE_ENDOMORPHISM + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000); +#endif + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000); + + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20); + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200); + +#ifndef USE_NUM_NONE + if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000); +#endif + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_recover.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_recover.c new file mode 100644 index 0000000..6489378 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_recover.c @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "include/secp256k1_recovery.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char sig[64]; +} bench_recover_t; + +void bench_recover(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + secp256k1_pubkey pubkey; + unsigned char pubkeyc[33]; + + for (i = 0; i < 20000; i++) { + int j; + size_t pubkeylen = 33; + secp256k1_ecdsa_recoverable_signature sig; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(data->ctx, &sig, data->sig, i % 2)); + CHECK(secp256k1_ecdsa_recover(data->ctx, &pubkey, &sig, data->msg)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pubkeyc, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + for (j = 0; j < 32; j++) { + data->sig[j + 32] = data->msg[j]; /* Move former message to S. */ + data->msg[j] = data->sig[j]; /* Move former R to message. */ + data->sig[j] = pubkeyc[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ + } + } +} + +void bench_recover_setup(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (i = 0; i < 64; i++) { + data->sig[i] = 65 + i; + } +} + +int main(void) { + bench_recover_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + + run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_schnorr_verify.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_schnorr_verify.c new file mode 100644 index 0000000..5f137dd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_schnorr_verify.c @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorr.h" +#include "util.h" +#include "bench.h" + +typedef struct { + unsigned char key[32]; + unsigned char sig[64]; + unsigned char pubkey[33]; + size_t pubkeylen; +} benchmark_schnorr_sig_t; + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + benchmark_schnorr_sig_t sigs[64]; + int numsigs; +} benchmark_schnorr_verify_t; + +static void benchmark_schnorr_init(void* arg) { + int i, k; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (k = 0; k < data->numsigs; k++) { + secp256k1_pubkey pubkey; + for (i = 0; i < 32; i++) { + data->sigs[k].key[i] = 33 + i + k; + } + secp256k1_schnorr_sign(data->ctx, data->sigs[k].sig, data->msg, data->sigs[k].key, NULL, NULL); + data->sigs[k].pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->sigs[k].key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + } +} + +static void benchmark_schnorr_verify(void* arg) { + int i; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 20000 / data->numsigs; i++) { + secp256k1_pubkey pubkey; + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen)); + CHECK(secp256k1_schnorr_verify(data->ctx, data->sigs[0].sig, data->msg, &pubkey) == ((i & 0xFF) == 0)); + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + } +} + + + +int main(void) { + benchmark_schnorr_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.numsigs = 1; + run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_sign.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_sign.c new file mode 100644 index 0000000..ed7224d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_sign.c @@ -0,0 +1,56 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char msg[32]; + unsigned char key[32]; +} bench_sign_t; + +static void bench_sign_setup(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = i + 1; + } + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_sign(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + unsigned char sig[74]; + for (i = 0; i < 20000; i++) { + size_t siglen = 74; + int j; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_sign(data->ctx, &signature, data->msg, data->key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data->ctx, sig, &siglen, &signature)); + for (j = 0; j < 32; j++) { + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; + } + } +} + +int main(void) { + bench_sign_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + + run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_verify.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_verify.c new file mode 100644 index 0000000..418defa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/bench_verify.c @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include +#include +#include +#endif + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char sig[72]; + size_t siglen; + unsigned char pubkey[33]; + size_t pubkeylen; +#ifdef ENABLE_OPENSSL_TESTS + EC_GROUP* ec_group; +#endif +} benchmark_verify_t; + +static void benchmark_verify(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(data->ctx, &sig, data->sig, data->siglen) == 1); + CHECK(secp256k1_ecdsa_verify(data->ctx, &sig, data->msg, &pubkey) == (i == 0)); + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} + +#ifdef ENABLE_OPENSSL_TESTS +static void benchmark_verify_openssl(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + { + EC_KEY *pkey = EC_KEY_new(); + const unsigned char *pubkey = &data->pubkey[0]; + int result; + + CHECK(pkey != NULL); + result = EC_KEY_set_group(pkey, data->ec_group); + CHECK(result); + result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL; + CHECK(result); + result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0); + CHECK(result); + EC_KEY_free(pkey); + } + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} +#endif + +int main(void) { + int i; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + benchmark_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + for (i = 0; i < 32; i++) { + data.msg[i] = 1 + i; + } + for (i = 0; i < 32; i++) { + data.key[i] = 33 + i; + } + data.siglen = 72; + CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig)); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key)); + data.pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + + run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000); +#ifdef ENABLE_OPENSSL_TESTS + data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); + run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); + EC_GROUP_free(data.ec_group); +#endif + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa.h new file mode 100644 index 0000000..54ae101 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa.h @@ -0,0 +1,21 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECDSA_ +#define _SECP256K1_ECDSA_ + +#include + +#include "scalar.h" +#include "group.h" +#include "ecmult.h" + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size); +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s); +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h new file mode 100644 index 0000000..453bb11 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecdsa_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_ECDSA_IMPL_H_ +#define _SECP256K1_ECDSA_IMPL_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "ecdsa.h" + +/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 + * sage: for t in xrange(1023, -1, -1): + * .. p = 2**256 - 2**32 - t + * .. if p.is_prime(): + * .. print '%x'%p + * .. break + * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) + * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + */ +static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL +); + +/** Difference between field and order, values 'p' and 'n' values defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) + * '14551231950b75fc4402da1722fc9baee' + */ +static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( + 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL +); + +static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { + int lenleft, b1; + size_t ret = 0; + if (*sigp >= sigend) { + return -1; + } + b1 = *((*sigp)++); + if (b1 == 0xFF) { + /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ + return -1; + } + if ((b1 & 0x80) == 0) { + /* X.690-0207 8.1.3.4 short form length octets */ + return b1; + } + if (b1 == 0x80) { + /* Indefinite length is not allowed in DER. */ + return -1; + } + /* X.690-207 8.1.3.5 long form length octets */ + lenleft = b1 & 0x7F; + if (lenleft > sigend - *sigp) { + return -1; + } + if (**sigp == 0) { + /* Not the shortest possible length encoding. */ + return -1; + } + if ((size_t)lenleft > sizeof(size_t)) { + /* The resulting length would exceed the range of a size_t, so + * certainly longer than the passed array size. + */ + return -1; + } + while (lenleft > 0) { + if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { + } + ret = (ret << 8) | **sigp; + if (ret + lenleft > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return -1; + } + (*sigp)++; + lenleft--; + } + if (ret < 128) { + /* Not the shortest possible length encoding. */ + return -1; + } + return ret; +} + +static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { + int overflow = 0; + unsigned char ra[32] = {0}; + int rlen; + + if (*sig == sigend || **sig != 0x02) { + /* Not a primitive integer (X.690-0207 8.3.1). */ + return 0; + } + (*sig)++; + rlen = secp256k1_der_read_len(sig, sigend); + if (rlen <= 0 || (*sig) + rlen > sigend) { + /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ + return 0; + } + if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { + /* Excessive 0x00 padding. */ + return 0; + } + if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { + /* Excessive 0xFF padding. */ + return 0; + } + if ((**sig & 0x80) == 0x80) { + /* Negative. */ + overflow = 1; + } + while (rlen > 0 && **sig == 0) { + /* Skip leading zero bytes */ + rlen--; + (*sig)++; + } + if (rlen > 32) { + overflow = 1; + } + if (!overflow) { + memcpy(ra + 32 - rlen, *sig, rlen); + secp256k1_scalar_set_b32(r, ra, &overflow); + } + if (overflow) { + secp256k1_scalar_set_int(r, 0); + } + (*sig) += rlen; + return 1; +} + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { + const unsigned char *sigend = sig + size; + int rlen; + if (sig == sigend || *(sig++) != 0x30) { + /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ + return 0; + } + rlen = secp256k1_der_read_len(&sig, sigend); + if (rlen < 0 || sig + rlen > sigend) { + /* Tuple exceeds bounds */ + return 0; + } + if (sig + rlen != sigend) { + /* Garbage after tuple. */ + return 0; + } + + if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { + return 0; + } + if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { + return 0; + } + + if (sig != sigend) { + /* Trailing garbage inside tuple. */ + return 0; + } + + return 1; +} + +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { + unsigned char r[33] = {0}, s[33] = {0}; + unsigned char *rp = r, *sp = s; + size_t lenR = 33, lenS = 33; + secp256k1_scalar_get_b32(&r[1], ar); + secp256k1_scalar_get_b32(&s[1], as); + while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } + while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } + if (*size < 6+lenS+lenR) { + *size = 6 + lenS + lenR; + return 0; + } + *size = 6 + lenS + lenR; + sig[0] = 0x30; + sig[1] = 4 + lenS + lenR; + sig[2] = 0x02; + sig[3] = lenR; + memcpy(sig+4, rp, lenR); + sig[4+lenR] = 0x02; + sig[5+lenR] = lenS; + memcpy(sig+lenR+6, sp, lenS); + return 1; +} + +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { + unsigned char c[32]; + secp256k1_scalar sn, u1, u2; +#if !defined(EXHAUSTIVE_TEST_ORDER) + secp256k1_fe xr; +#endif + secp256k1_gej pubkeyj; + secp256k1_gej pr; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_inverse_var(&sn, sigs); + secp256k1_scalar_mul(&u1, &sn, message); + secp256k1_scalar_mul(&u2, &sn, sigr); + secp256k1_gej_set_ge(&pubkeyj, pubkey); + secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&pr)) { + return 0; + } + +#if defined(EXHAUSTIVE_TEST_ORDER) +{ + secp256k1_scalar computed_r; + secp256k1_ge pr_ge; + secp256k1_ge_set_gej(&pr_ge, &pr); + secp256k1_fe_normalize(&pr_ge.x); + + secp256k1_fe_get_b32(c, &pr_ge.x); + secp256k1_scalar_set_b32(&computed_r, c, NULL); + return secp256k1_scalar_eq(sigr, &computed_r); +} +#else + secp256k1_scalar_get_b32(c, sigr); + secp256k1_fe_set_b32(&xr, c); + + /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) + * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), + * compute the remainder modulo n, and compare it to xr. However: + * + * xr == X(pr) mod n + * <=> exists h. (xr + h * n < p && xr + h * n == X(pr)) + * [Since 2 * n > p, h can only be 0 or 1] + * <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr)) + * [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p] + * <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p) + * [Multiplying both sides of the equations by pr.z^2 mod p] + * <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x) + * + * Thus, we can avoid the inversion, but we have to check both cases separately. + * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. + */ + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + /* xr + n >= p, so we can skip testing the second case. */ + return 0; + } + secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + return 0; +#endif +} + +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { + unsigned char b[32]; + secp256k1_gej rp; + secp256k1_ge r; + secp256k1_scalar n; + int overflow = 0; + + secp256k1_ecmult_gen(ctx, &rp, nonce); + secp256k1_ge_set_gej(&r, &rp); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_normalize(&r.y); + secp256k1_fe_get_b32(b, &r.x); + secp256k1_scalar_set_b32(sigr, b, &overflow); + /* These two conditions should be checked before calling */ + VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr)); + VERIFY_CHECK(overflow == 0); + + if (recid) { + /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log + * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria. + */ + *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0); + } + secp256k1_scalar_mul(&n, sigr, seckey); + secp256k1_scalar_add(&n, &n, message); + secp256k1_scalar_inverse(sigs, nonce); + secp256k1_scalar_mul(sigs, sigs, &n); + secp256k1_scalar_clear(&n); + secp256k1_gej_clear(&rp); + secp256k1_ge_clear(&r); + if (secp256k1_scalar_is_zero(sigs)) { + return 0; + } + if (secp256k1_scalar_is_high(sigs)) { + secp256k1_scalar_negate(sigs, sigs); + if (recid) { + *recid ^= 1; + } + } + return 1; +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey.h new file mode 100644 index 0000000..42739a3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey.h @@ -0,0 +1,25 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_ +#define _SECP256K1_ECKEY_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed); + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey_impl.h new file mode 100644 index 0000000..ce38071 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/eckey_impl.h @@ -0,0 +1,99 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_IMPL_H_ +#define _SECP256K1_ECKEY_IMPL_H_ + +#include "eckey.h" + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { + if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + secp256k1_fe x; + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { + secp256k1_fe x, y; + if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + return 0; + } + secp256k1_ge_set_xy(elem, &x, &y); + if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + return 0; + } + return secp256k1_ge_is_valid_var(elem); + } else { + return 0; + } +} + +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { + if (secp256k1_ge_is_infinity(elem)) { + return 0; + } + secp256k1_fe_normalize_var(&elem->x); + secp256k1_fe_normalize_var(&elem->y); + secp256k1_fe_get_b32(&pub[1], &elem->x); + if (compressed) { + *size = 33; + pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + } else { + *size = 65; + pub[0] = 0x04; + secp256k1_fe_get_b32(&pub[33], &elem->y); + } + return 1; +} + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + secp256k1_scalar_add(key, key, tweak); + if (secp256k1_scalar_is_zero(key)) { + return 0; + } + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_gej pt; + secp256k1_scalar one; + secp256k1_gej_set_ge(&pt, key); + secp256k1_scalar_set_int(&one, 1); + secp256k1_ecmult(ctx, &pt, &pt, &one, tweak); + + if (secp256k1_gej_is_infinity(&pt)) { + return 0; + } + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_mul(key, key, tweak); + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_scalar zero; + secp256k1_gej pt; + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_set_int(&zero, 0); + secp256k1_gej_set_ge(&pt, key); + secp256k1_ecmult(ctx, &pt, &pt, tweak, &zero); + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult.h new file mode 100644 index 0000000..2048413 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult.h @@ -0,0 +1,31 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_ +#define _SECP256K1_ECMULT_ + +#include "num.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*P + b*G: */ + secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ +#endif +} secp256k1_ecmult_context; + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); + +/** Double multiply: R = na*A + ng*G */ +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const.h new file mode 100644 index 0000000..2b00976 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_ +#define _SECP256K1_ECMULT_CONST_ + +#include "scalar.h" +#include "group.h" + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h new file mode 100644 index 0000000..0db314c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_const_impl.h @@ -0,0 +1,239 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_IMPL_ +#define _SECP256K1_ECMULT_CONST_IMPL_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#endif +#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w)) + +/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ +#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ + int m; \ + int abs_n = (n) * (((n) > 0) * 2 - 1); \ + int idx_n = abs_n / 2; \ + secp256k1_fe neg_y; \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ + for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \ + /* This loop is used to avoid secret data in array indices. See + * the comment in ecmult_gen_impl.h for rationale. */ \ + secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ + secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ + } \ + (r)->infinity = 0; \ + secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) + * with the following guarantees: + * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) + * - each wnaf[i] is nonzero + * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * + * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar + * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) + * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003 + * + * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 + */ +static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { + int global_sign; + int skew = 0; + int word = 0; + + /* 1 2 3 */ + int u_last; + int u; + + int flip; + int bit; + secp256k1_scalar neg_s; + int not_neg_one; + /* Note that we cannot handle even numbers by negating them to be odd, as is + * done in other implementations, since if our scalars were specified to have + * width < 256 for performance reasons, their negations would have width 256 + * and we'd lose any performance benefit. Instead, we use a technique from + * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) + * or 2 (for odd) to the number we are encoding, returning a skew value indicating + * this, and having the caller compensate after doing the multiplication. */ + + /* Negative numbers will be negated to keep their bit representation below the maximum width */ + flip = secp256k1_scalar_is_high(&s); + /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ + bit = flip ^ !secp256k1_scalar_is_even(&s); + /* We check for negative one, since adding 2 to it will cause an overflow */ + secp256k1_scalar_negate(&neg_s, &s); + not_neg_one = !secp256k1_scalar_is_one(&neg_s); + secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); + /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects + * that we added two to it and flipped it. In fact for -1 these operations are + * identical. We only flipped, but since skewing is required (in the sense that + * the skew must be 1 or 2, never zero) and flipping is not, we need to change + * our flags to claim that we only skewed. */ + global_sign = secp256k1_scalar_cond_negate(&s, flip); + global_sign *= not_neg_one * 2 - 1; + skew = 1 << bit; + + /* 4 */ + u_last = secp256k1_scalar_shr_int(&s, w); + while (word * w < WNAF_BITS) { + int sign; + int even; + + /* 4.1 4.4 */ + u = secp256k1_scalar_shr_int(&s, w); + /* 4.2 */ + even = ((u & 1) == 0); + sign = 2 * (u_last > 0) - 1; + u += sign * even; + u_last -= sign * even * (1 << w); + + /* 4.3, adapted for global sign change */ + wnaf[word++] = u_last * global_sign; + + u_last = u; + } + wnaf[word] = u * global_sign; + + VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); + VERIFY_CHECK(word == WNAF_SIZE(w)); + return skew; +} + + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + int skew_1; + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; + int skew_lam; + secp256k1_scalar q_1, q_lam; +#endif + + int i; + secp256k1_scalar sc = *scalar; + + /* build wnaf representation for q. */ +#ifdef USE_ENDOMORPHISM + /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); + skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1); + skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1); +#else + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1); +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + */ + secp256k1_gej_set_ge(r, a); + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_fe_normalize_weak(&pre_a[i].y); + } +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } +#endif + + /* first loop iteration (separated out so we can directly set r, rather + * than having it start at infinity, get doubled several times, then have + * its new value added to it) */ + i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); + secp256k1_gej_set_ge(r, &tmpa); +#ifdef USE_ENDOMORPHISM + i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + /* remaining loop iterations */ + for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) { + int n; + int j; + for (j = 0; j < WINDOW_A - 1; ++j) { + secp256k1_gej_double_nonzero(r, r, NULL); + } + + n = wnaf_1[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#ifdef USE_ENDOMORPHISM + n = wnaf_lam[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + } + + secp256k1_fe_mul(&r->z, &r->z, &Z); + + { + /* Correct for wNAF skew */ + secp256k1_ge correction = *a; + secp256k1_ge_storage correction_1_stor; +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage correction_lam_stor; +#endif + secp256k1_ge_storage a2_stor; + secp256k1_gej tmpj; + secp256k1_gej_set_ge(&tmpj, &correction); + secp256k1_gej_double_var(&tmpj, &tmpj, NULL); + secp256k1_ge_set_gej(&correction, &tmpj); + secp256k1_ge_to_storage(&correction_1_stor, a); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_to_storage(&correction_lam_stor, a); +#endif + secp256k1_ge_to_storage(&a2_stor, &correction); + + /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ + secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); +#endif + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#ifdef USE_ENDOMORPHISM + secp256k1_ge_from_storage(&correction, &correction_lam_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_ge_mul_lambda(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); +#endif + } +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen.h new file mode 100644 index 0000000..eb2cc9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen.h @@ -0,0 +1,43 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_ +#define _SECP256K1_ECMULT_GEN_ + +#include "scalar.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*G: + * To harden against timing attacks, use the following mechanism: + * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. + * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where: + * * U_i = U * 2^i (for i=0..62) + * * U_i = U * (1-2^63) (for i=63) + * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0. + * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is + * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63). + * None of the resulting prec group elements have a known scalar, and neither do any of + * the intermediate sums while computing a*G. + */ + secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_scalar blind; + secp256k1_gej initial; +} secp256k1_ecmult_gen_context; + +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); + +/** Multiply with the generator: R = a*G */ +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); + +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h new file mode 100644 index 0000000..35f2546 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_gen_impl.h @@ -0,0 +1,210 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ +#define _SECP256K1_ECMULT_GEN_IMPL_H_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_gen.h" +#include "hash_impl.h" +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION +#include "ecmult_static_context.h" +#endif +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_ge prec[1024]; + secp256k1_gej gj; + secp256k1_gej nums_gej; + int i, j; +#endif + + if (ctx->prec != NULL) { + return; + } +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ + { + static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; + secp256k1_fe nums_x; + secp256k1_ge nums_ge; + int r; + r = secp256k1_fe_set_b32(&nums_x, nums_b32); + (void)r; + VERIFY_CHECK(r); + r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); + (void)r; + VERIFY_CHECK(r); + secp256k1_gej_set_ge(&nums_gej, &nums_ge); + /* Add G to make the bits in x uniformly distributed. */ + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); + } + + /* compute prec. */ + { + secp256k1_gej precj[1024]; /* Jacobian versions of prec. */ + secp256k1_gej gbase; + secp256k1_gej numsbase; + gbase = gj; /* 16^j * G */ + numsbase = nums_gej; /* 2^j * nums. */ + for (j = 0; j < 64; j++) { + /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ + precj[j*16] = numsbase; + for (i = 1; i < 16; i++) { + secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + } + /* Multiply gbase by 16. */ + for (i = 0; i < 4; i++) { + secp256k1_gej_double_var(&gbase, &gbase, NULL); + } + /* Multiply numbase by 2. */ + secp256k1_gej_double_var(&numsbase, &numsbase, NULL); + if (j == 62) { + /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ + secp256k1_gej_neg(&numsbase, &numsbase); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); + } + } + secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); + } + for (j = 0; j < 64; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + } + } +#else + (void)cb; + ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; +#endif + secp256k1_ecmult_gen_blind(ctx, NULL); +} + +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); +#else + (void)cb; + dst->prec = src->prec; +#endif + dst->initial = src->initial; + dst->blind = src->blind; + } +} + +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + free(ctx->prec); +#endif + secp256k1_scalar_clear(&ctx->blind); + secp256k1_gej_clear(&ctx->initial); + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + secp256k1_scalar gnb; + int bits; + int i, j; + memset(&adds, 0, sizeof(adds)); + *r = ctx->initial; + /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ + secp256k1_scalar_add(&gnb, gn, &ctx->blind); + add.infinity = 0; + for (j = 0; j < 64; j++) { + bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); + for (i = 0; i < 16; i++) { + /** This uses a conditional move to avoid any secret data in array indexes. + * _Any_ use of secret indexes has been demonstrated to result in timing + * sidechannels, even when the cache-line access patterns are uniform. + * See also: + * "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe + * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and + * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, + * by Dag Arne Osvik, Adi Shamir, and Eran Tromer + * (http://www.tau.ac.il/~tromer/papers/cache.pdf) + */ + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); + secp256k1_scalar_clear(&gnb); +} + +/* Setup blinding values for secp256k1_ecmult_gen. */ +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { + secp256k1_scalar b; + secp256k1_gej gb; + secp256k1_fe s; + unsigned char nonce32[32]; + secp256k1_rfc6979_hmac_sha256_t rng; + int retry; + unsigned char keydata[64] = {0}; + if (seed32 == NULL) { + /* When seed is NULL, reset the initial point and blinding value. */ + secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); + secp256k1_gej_neg(&ctx->initial, &ctx->initial); + secp256k1_scalar_set_int(&ctx->blind, 1); + } + /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ + secp256k1_scalar_get_b32(nonce32, &ctx->blind); + /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, + * and guards against weak or adversarial seeds. This is a simpler and safer interface than + * asking the caller for blinding values directly and expecting them to retry on failure. + */ + memcpy(keydata, nonce32, 32); + if (seed32 != NULL) { + memcpy(keydata + 32, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + memset(keydata, 0, sizeof(keydata)); + /* Retry for out of range results to achieve uniformity. */ + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + retry = !secp256k1_fe_set_b32(&s, nonce32); + retry |= secp256k1_fe_is_zero(&s); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ + /* Randomize the projection to defend against multiplier sidechannels. */ + secp256k1_gej_rescale(&ctx->initial, &s); + secp256k1_fe_clear(&s); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + secp256k1_scalar_set_b32(&b, nonce32, &retry); + /* A blinding value of 0 works, but would undermine the projection hardening. */ + retry |= secp256k1_scalar_is_zero(&b); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + memset(nonce32, 0, 32); + secp256k1_ecmult_gen(ctx, &gb, &b); + secp256k1_scalar_negate(&b, &b); + ctx->blind = b; + ctx->initial = gb; + secp256k1_scalar_clear(&b); + secp256k1_gej_clear(&gb); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h new file mode 100644 index 0000000..4e40104 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/ecmult_impl.h @@ -0,0 +1,406 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_IMPL_H_ +#define _SECP256K1_ECMULT_IMPL_H_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * affine-isomorphism stuff which tracks z-ratios) */ +# if EXHAUSTIVE_TEST_ORDER > 128 +# define WINDOW_A 5 +# define WINDOW_G 8 +# elif EXHAUSTIVE_TEST_ORDER > 8 +# define WINDOW_A 4 +# define WINDOW_G 4 +# else +# define WINDOW_A 2 +# define WINDOW_G 2 +# endif +#else +/* optimal for 128-bit and 256-bit exponents. */ +#define WINDOW_A 5 +/** larger numbers may result in slightly better performance, at the cost of + exponentially larger precomputed tables. */ +#ifdef USE_ENDOMORPHISM +/** Two tables for window size 15: 1.375 MiB. */ +#define WINDOW_G 15 +#else +/** One table for window size 16: 1.375 MiB. */ +#define WINDOW_G 16 +#endif +#endif + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain + * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will + * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. + * Prej's Z values are undefined, except for the last value. + */ +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge a_ge, d_ge; + int i; + + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* + * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate + * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); + prej[0].x = a_ge.x; + prej[0].y = a_ge.y; + prej[0].z = a->z; + prej[0].infinity = 0; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + } + + /* + * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only + * the final point's z coordinate is actually used though, so just update that. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); +} + +/** Fill a table 'pre' with precomputed odd multiples of a. + * + * There are two versions of this function: + * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its + * resulting point set to a single constant Z denominator, stores the X and Y + * coordinates as ge_storage points in pre, and stores the global Z in rz. + * It only operates on tables sized for WINDOW_A wnaf multiples. + * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its + * resulting point set to actually affine points, and stores those in pre. + * It operates on tables of any size, but uses heap-allocated temporaries. + * + * To compute a*P + b*G, we compute a table for P using the first function, + * and for G using the second (which requires an inverse, but it only needs to + * happen once). + */ +static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +} + +static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { + secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); + secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); + secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); + int i; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); + /* Convert them in batch to affine coordinates. */ + secp256k1_ge_set_table_gej_var(prea, prej, zr, n); + /* Convert them to compact storage form. */ + for (i = 0; i < n; i++) { + secp256k1_ge_to_storage(&pre[i], &prea[i]); + } + + free(prea); + free(prej); + free(zr); +} + +/** The following two macro retrieves a particular odd multiple from a table + * of precomputed multiples. */ +#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + *(r) = (pre)[((n)-1)/2]; \ + } else { \ + secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ + } \ +} while(0) + +#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ + } else { \ + secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ + secp256k1_ge_neg((r), (r)); \ + } \ +} while(0) + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { + ctx->pre_g = NULL; +#ifdef USE_ENDOMORPHISM + ctx->pre_g_128 = NULL; +#endif +} + +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { + secp256k1_gej gj; + + if (ctx->pre_g != NULL) { + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* precompute the tables with odd multiples */ + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); + +#ifdef USE_ENDOMORPHISM + { + secp256k1_gej g_128j; + int i; + + ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* calculate 2^128*generator */ + g_128j = gj; + for (i = 0; i < 128; i++) { + secp256k1_gej_double_var(&g_128j, &g_128j, NULL); + } + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); + } +#endif +} + +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { + if (src->pre_g == NULL) { + dst->pre_g = NULL; + } else { + size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g, src->pre_g, size); + } +#ifdef USE_ENDOMORPHISM + if (src->pre_g_128 == NULL) { + dst->pre_g_128 = NULL; + } else { + size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g_128, src->pre_g_128, size); + } +#endif +} + +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { + return ctx->pre_g != NULL; +} + +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { + free(ctx->pre_g); +#ifdef USE_ENDOMORPHISM + free(ctx->pre_g_128); +#endif + secp256k1_ecmult_context_init(ctx); +} + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), + * with the following guarantees: + * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) + * - two non-zero entries in wnaf are separated by at least w-1 zeroes. + * - the number of set values in wnaf is returned. This number is at most 256, and at most one more + * than the number of bits in the (absolute value) of the input. + */ +static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { + secp256k1_scalar s = *a; + int last_set_bit = -1; + int bit = 0; + int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); + + if (secp256k1_scalar_get_bits(&s, 255, 1)) { + secp256k1_scalar_negate(&s, &s); + sign = -1; + } + + while (bit < len) { + int now; + int word; + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + bit++; + continue; + } + + now = w; + if (now > len - bit) { + now = len - bit; + } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + + bit += now; + } +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#endif + return last_set_bit + 1; +} + +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_scalar na_1, na_lam; + /* Splitted G factors. */ + secp256k1_scalar ng_1, ng_128; + int wnaf_na_1[130]; + int wnaf_na_lam[130]; + int bits_na_1; + int bits_na_lam; + int wnaf_ng_1[129]; + int bits_ng_1; + int wnaf_ng_128[129]; + int bits_ng_128; +#else + int wnaf_na[256]; + int bits_na; + int wnaf_ng[256]; + int bits_ng; +#endif + int i; + int bits; + +#ifdef USE_ENDOMORPHISM + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&na_1, &na_lam, na); + + /* build wnaf representation for na_1 and na_lam. */ + bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A); + bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A); + VERIFY_CHECK(bits_na_1 <= 130); + VERIFY_CHECK(bits_na_lam <= 130); + bits = bits_na_1; + if (bits_na_lam > bits) { + bits = bits_na_lam; + } +#else + /* build wnaf representation for na. */ + bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); + bits = bits_na; +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a); + +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } + + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } +#else + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); + if (bits_ng > bits) { + bits = bits_ng; + } +#endif + + secp256k1_gej_set_infinity(r); + + for (i = bits - 1; i >= 0; i--) { + int n; + secp256k1_gej_double_var(r, r, NULL); +#ifdef USE_ENDOMORPHISM + if (i < bits_na_1 && (n = wnaf_na_1[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_na_lam && (n = wnaf_na_lam[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } + if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#else + if (i < bits_na && (n = wnaf_na[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng && (n = wnaf_ng[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field.h new file mode 100644 index 0000000..bbb1ee8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field.h @@ -0,0 +1,132 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_ +#define _SECP256K1_FIELD_ + +/** Field element module. + * + * Field elements can be represented in several ways, but code accessing + * it (and implementations) need to take certain properties into account: + * - Each field element can be normalized or not. + * - Each field element has a magnitude, which represents how far away + * its representation is away from normalization. Normalized elements + * always have a magnitude of 1, but a magnitude of 1 doesn't imply + * normality. + */ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_FIELD_10X26) +#include "field_10x26.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52.h" +#else +#error "Please select field implementation" +#endif + +#include "util.h" + +/** Normalize a field element. */ +static void secp256k1_fe_normalize(secp256k1_fe *r); + +/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ +static void secp256k1_fe_normalize_weak(secp256k1_fe *r); + +/** Normalize a field element, without constant-time guarantee. */ +static void secp256k1_fe_normalize_var(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r); + +/** Set a field element equal to a small integer. Resulting field element is normalized. */ +static void secp256k1_fe_set_int(secp256k1_fe *r, int a); + +/** Sets a field element equal to zero, initializing all fields. */ +static void secp256k1_fe_clear(secp256k1_fe *a); + +/** Verify whether a field element is zero. Requires the input to be normalized. */ +static int secp256k1_fe_is_zero(const secp256k1_fe *a); + +/** Check the "oddness" of a field element. Requires the input to be normalized. */ +static int secp256k1_fe_is_odd(const secp256k1_fe *a); + +/** Compare two field elements. Requires magnitude-1 inputs. */ +static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Same as secp256k1_fe_equal, but may be variable time. */ +static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Compare two field elements. Requires both inputs to be normalized */ +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); + +/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input + * as an argument. The magnitude of the output is one higher. */ +static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); + +/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that + * small integer. */ +static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); + +/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); + +/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); + +/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); + +/** If a has a square root, it is computed in r and 1 is returned. If a does not + * have a square root, the root of its negation is computed and 0 is returned. + * The input's magnitude can be at most 8. The output magnitude is 1 (but not + * guaranteed to be normalized). The result in r will always be a square + * itself. */ +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); + +/** Checks whether a field element is a quadratic residue. */ +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); + +/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be + * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); + +/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); + +/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be + * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and + * outputs must not overlap in memory. */ +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len); + +/** Convert a field element to the storage type. */ +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); + +/** Convert a field element back from the storage type. */ +static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26.h new file mode 100644 index 0000000..61ee1e0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..9, elem[i]*2^26) mod n */ + uint32_t n[10]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) & 0x3FFFFFFUL, \ + (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ + (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ + (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ + (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ + (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ + (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ + (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ + (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ + (((uint32_t)d7) >> 10) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint32_t n[8]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h new file mode 100644 index 0000000..5fb092f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_10x26_impl.h @@ -0,0 +1,1140 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#include "util.h" +#include "num.h" +#include "field.h" + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint32_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + r &= (d[0] <= 0x3FFFFFFUL * m); + r &= (d[1] <= 0x3FFFFFFUL * m); + r &= (d[2] <= 0x3FFFFFFUL * m); + r &= (d[3] <= 0x3FFFFFFUL * m); + r &= (d[4] <= 0x3FFFFFFUL * m); + r &= (d[5] <= 0x3FFFFFFUL * m); + r &= (d[6] <= 0x3FFFFFFUL * m); + r &= (d[7] <= 0x3FFFFFFUL * m); + r &= (d[8] <= 0x3FFFFFFUL * m); + r &= (d[9] <= 0x03FFFFFUL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 32); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[9] == 0x03FFFFFUL)) { + uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; + if (mid == 0x3FFFFFFUL) { + r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); + } + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + if (x) { + t0 += 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint32_t z0, z1; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t z0, z1; + uint32_t x; + + t0 = r->n[0]; + t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + x = t9 >> 22; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0x3FFFFFFUL; + z1 = z0 ^ 0x3D0UL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + t4 = r->n[4]; + t5 = r->n[5]; + t6 = r->n[6]; + t7 = r->n[7]; + t8 = r->n[8]; + + t9 &= 0x03FFFFFUL; + t1 += (x << 6); + + t1 += (t0 >> 26); + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint32_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<10; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 9; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + r->n[limb] |= (uint32_t)((a[31-i] >> (2*j)) & 0x3) << shift; + } + } + if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + c |= ((a->n[limb] >> shift) & 0x3) << (2 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[4]; + r->n[5] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[5]; + r->n[6] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[6]; + r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; + r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; + r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; + r->n[5] *= a; + r->n[6] *= a; + r->n[7] *= a; + r->n[8] *= a; + r->n[9] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; + r->n[5] += a->n[5]; + r->n[6] += a->n[6]; + r->n[7] += a->n[7]; + r->n[8] += a->n[8]; + r->n[9] += a->n[9]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); + +#else + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + VERIFY_BITS(b[0], 30); + VERIFY_BITS(b[1], 30); + VERIFY_BITS(b[2], 30); + VERIFY_BITS(b[3], 30); + VERIFY_BITS(b[4], 30); + VERIFY_BITS(b[5], 30); + VERIFY_BITS(b[6], 30); + VERIFY_BITS(b[7], 30); + VERIFY_BITS(b[8], 30); + VERIFY_BITS(b[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)a[0] * b[9] + + (uint64_t)a[1] * b[8] + + (uint64_t)a[2] * b[7] + + (uint64_t)a[3] * b[6] + + (uint64_t)a[4] * b[5] + + (uint64_t)a[5] * b[4] + + (uint64_t)a[6] * b[3] + + (uint64_t)a[7] * b[2] + + (uint64_t)a[8] * b[1] + + (uint64_t)a[9] * b[0]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * b[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)a[1] * b[9] + + (uint64_t)a[2] * b[8] + + (uint64_t)a[3] * b[7] + + (uint64_t)a[4] * b[6] + + (uint64_t)a[5] * b[5] + + (uint64_t)a[6] * b[4] + + (uint64_t)a[7] * b[3] + + (uint64_t)a[8] * b[2] + + (uint64_t)a[9] * b[1]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)a[0] * b[1] + + (uint64_t)a[1] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)a[2] * b[9] + + (uint64_t)a[3] * b[8] + + (uint64_t)a[4] * b[7] + + (uint64_t)a[5] * b[6] + + (uint64_t)a[6] * b[5] + + (uint64_t)a[7] * b[4] + + (uint64_t)a[8] * b[3] + + (uint64_t)a[9] * b[2]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)a[0] * b[2] + + (uint64_t)a[1] * b[1] + + (uint64_t)a[2] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)a[3] * b[9] + + (uint64_t)a[4] * b[8] + + (uint64_t)a[5] * b[7] + + (uint64_t)a[6] * b[6] + + (uint64_t)a[7] * b[5] + + (uint64_t)a[8] * b[4] + + (uint64_t)a[9] * b[3]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[3] + + (uint64_t)a[1] * b[2] + + (uint64_t)a[2] * b[1] + + (uint64_t)a[3] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)a[4] * b[9] + + (uint64_t)a[5] * b[8] + + (uint64_t)a[6] * b[7] + + (uint64_t)a[7] * b[6] + + (uint64_t)a[8] * b[5] + + (uint64_t)a[9] * b[4]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[4] + + (uint64_t)a[1] * b[3] + + (uint64_t)a[2] * b[2] + + (uint64_t)a[3] * b[1] + + (uint64_t)a[4] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[5] * b[9] + + (uint64_t)a[6] * b[8] + + (uint64_t)a[7] * b[7] + + (uint64_t)a[8] * b[6] + + (uint64_t)a[9] * b[5]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[5] + + (uint64_t)a[1] * b[4] + + (uint64_t)a[2] * b[3] + + (uint64_t)a[3] * b[2] + + (uint64_t)a[4] * b[1] + + (uint64_t)a[5] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[6] * b[9] + + (uint64_t)a[7] * b[8] + + (uint64_t)a[8] * b[7] + + (uint64_t)a[9] * b[6]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[6] + + (uint64_t)a[1] * b[5] + + (uint64_t)a[2] * b[4] + + (uint64_t)a[3] * b[3] + + (uint64_t)a[4] * b[2] + + (uint64_t)a[5] * b[1] + + (uint64_t)a[6] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[7] * b[9] + + (uint64_t)a[8] * b[8] + + (uint64_t)a[9] * b[7]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[7] + + (uint64_t)a[1] * b[6] + + (uint64_t)a[2] * b[5] + + (uint64_t)a[3] * b[4] + + (uint64_t)a[4] * b[3] + + (uint64_t)a[5] * b[2] + + (uint64_t)a[6] * b[1] + + (uint64_t)a[7] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[8] * b[9] + + (uint64_t)a[9] * b[8]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[8] + + (uint64_t)a[1] * b[7] + + (uint64_t)a[2] * b[6] + + (uint64_t)a[3] * b[5] + + (uint64_t)a[4] * b[4] + + (uint64_t)a[5] * b[3] + + (uint64_t)a[6] * b[2] + + (uint64_t)a[7] * b[1] + + (uint64_t)a[8] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * b[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t0, t1, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)(a[0]*2) * a[9] + + (uint64_t)(a[1]*2) * a[8] + + (uint64_t)(a[2]*2) * a[7] + + (uint64_t)(a[3]*2) * a[6] + + (uint64_t)(a[4]*2) * a[5]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * a[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)(a[1]*2) * a[9] + + (uint64_t)(a[2]*2) * a[8] + + (uint64_t)(a[3]*2) * a[7] + + (uint64_t)(a[4]*2) * a[6] + + (uint64_t)a[5] * a[5]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)(a[0]*2) * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)(a[2]*2) * a[9] + + (uint64_t)(a[3]*2) * a[8] + + (uint64_t)(a[4]*2) * a[7] + + (uint64_t)(a[5]*2) * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[2] + + (uint64_t)a[1] * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)(a[3]*2) * a[9] + + (uint64_t)(a[4]*2) * a[8] + + (uint64_t)(a[5]*2) * a[7] + + (uint64_t)a[6] * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[3] + + (uint64_t)(a[1]*2) * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)(a[4]*2) * a[9] + + (uint64_t)(a[5]*2) * a[8] + + (uint64_t)(a[6]*2) * a[7]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[4] + + (uint64_t)(a[1]*2) * a[3] + + (uint64_t)a[2] * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[5]*2) * a[9] + + (uint64_t)(a[6]*2) * a[8] + + (uint64_t)a[7] * a[7]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[5] + + (uint64_t)(a[1]*2) * a[4] + + (uint64_t)(a[2]*2) * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[6]*2) * a[9] + + (uint64_t)(a[7]*2) * a[8]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[6] + + (uint64_t)(a[1]*2) * a[5] + + (uint64_t)(a[2]*2) * a[4] + + (uint64_t)a[3] * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[7]*2) * a[9] + + (uint64_t)a[8] * a[8]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[7] + + (uint64_t)(a[1]*2) * a[6] + + (uint64_t)(a[2]*2) * a[5] + + (uint64_t)(a[3]*2) * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[8]*2) * a[9]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[8] + + (uint64_t)(a[1]*2) * a[7] + + (uint64_t)(a[2]*2) * a[6] + + (uint64_t)(a[3]*2) * a[5] + + (uint64_t)a[4] * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * a[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} +#endif + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); + r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); + r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 26; + r->n[1] = a->n[1] >> 6 | a->n[2] << 20; + r->n[2] = a->n[2] >> 12 | a->n[3] << 14; + r->n[3] = a->n[3] >> 18 | a->n[4] << 8; + r->n[4] = a->n[4] >> 24 | a->n[5] << 2 | a->n[6] << 28; + r->n[5] = a->n[6] >> 4 | a->n[7] << 22; + r->n[6] = a->n[7] >> 10 | a->n[8] << 16; + r->n[7] = a->n[8] >> 16 | a->n[9] << 10; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0x3FFFFFFUL; + r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); + r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); + r->n[3] = a->n[2] >> 14 | ((a->n[3] << 18) & 0x3FFFFFFUL); + r->n[4] = a->n[3] >> 8 | ((a->n[4] << 24) & 0x3FFFFFFUL); + r->n[5] = (a->n[4] >> 2) & 0x3FFFFFFUL; + r->n[6] = a->n[4] >> 28 | ((a->n[5] << 4) & 0x3FFFFFFUL); + r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); + r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); + r->n[9] = a->n[7] >> 10; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52.h new file mode 100644 index 0000000..8e69a56 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..4, elem[i]*2^52) mod n */ + uint64_t n[5]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ + ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ + ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ + ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ + ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint64_t n[4]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ + (d0) | (((uint64_t)(d1)) << 32), \ + (d2) | (((uint64_t)(d3)) << 32), \ + (d4) | (((uint64_t)(d5)) << 32), \ + (d6) | (((uint64_t)(d7)) << 32) \ +}} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h new file mode 100644 index 0000000..98cc004 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h @@ -0,0 +1,502 @@ +/********************************************************************** + * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * Changelog: + * - March 2013, Diederik Huys: original version + * - November 2014, Pieter Wuille: updated to use Peter Dettman's parallel multiplication algorithm + * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly + */ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * r15:rcx = d + * r10-r14 = a0-a4 + * rbx = b + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + + /* d += a3 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rcx\n" + "movq %%rdx,%%r15\n" + /* d += a2 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d = a0 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c = a4 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* d += a4 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a0 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* t4 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a4 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* u0 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a1 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a4 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a2 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a1 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b2 (last use of %%r10 = a0) */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0), t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* d += a4 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rcx only) */ + "shrdq $52,%%r15,%%rcx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rcx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "b"(b), "D"(r) +: "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * rcx:rbx = d + * r10-r14 = a0-a4 + * r15 = M (0xfffffffffffff) + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + "movq $0xfffffffffffff,%%r15\n" + + /* d = (a0*2) * a3 */ + "leaq (%%r10,%%r10,1),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rbx\n" + "movq %%rdx,%%rcx\n" + /* d += (a1*2) * a2 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c = a4 * a4 */ + "movq %%r14,%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* a4 *= 2 */ + "addq %%r14,%%r14\n" + /* d += a0 * a4 */ + "movq %%r10,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d+= (a1*2) * a3 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a2 * a2 */ + "movq %%r12,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* t4 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * a0 */ + "movq %%r10,%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a1 * a4 */ + "movq %%r11,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += (a2*2) * a3 */ + "leaq (%%r12,%%r12,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* u0 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* a0 *= 2 */ + "addq %%r10,%%r10\n" + /* c += a0 * a1 */ + "movq %%r10,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a2 * a4 */ + "movq %%r12,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a3 * a3 */ + "movq %%r13,%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a0 * a2 (last use of %%r10) */ + "movq %%r10,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0),t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* c += a1 * a1 */ + "movq %%r11,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a3 * a4 */ + "movq %%r13,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rbx only) */ + "shrdq $52,%%rcx,%%rbx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rbx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "D"(r) +: "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h new file mode 100644 index 0000000..dd88f38 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_impl.h @@ -0,0 +1,451 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" +#include "num.h" +#include "field.h" + +#if defined(USE_ASM_X86_64) +#include "field_5x52_asm_impl.h" +#else +#include "field_5x52_int128_impl.h" +#endif + +/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, + * represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular, + * each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element + * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations + * accept any input with magnitude at most M, and have different rules for propagating magnitude to their + * output. + */ + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint64_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 2048); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + r &= (d[0] < 0xFFFFEFFFFFC2FULL); + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + if (x) { + t0 += 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint64_t z0, z1; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint64_t t0, t1, t2, t3, t4; + uint64_t z0, z1; + uint64_t x; + + t0 = r->n[0]; + t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + x = t4 >> 48; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0xFFFFFFFFFFFFFULL; + z1 = z0 ^ 0x1000003D0ULL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + + t4 &= 0x0FFFFFFFFFFFFULL; + + t1 += (t0 >> 52); + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint64_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<5; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 4; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + r->n[limb] |= (uint64_t)((a[31-i] >> (4*j)) & 0xF) << shift; + } + } + if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + c |= ((a->n[limb] >> shift) & 0xF) << (4 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 52; + r->n[1] = a->n[1] >> 12 | a->n[2] << 40; + r->n[2] = a->n[2] >> 24 | a->n[3] << 28; + r->n[3] = a->n[3] >> 36 | a->n[4] << 16; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; + r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); + r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); + r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); + r->n[4] = a->n[3] >> 16; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h new file mode 100644 index 0000000..0bf22bd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h @@ -0,0 +1,277 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +#include + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { + uint128_t c, d; + uint64_t t3, t4, tx, u0; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + VERIFY_BITS(b[0], 56); + VERIFY_BITS(b[1], 56); + VERIFY_BITS(b[2], 56); + VERIFY_BITS(b[3], 56); + VERIFY_BITS(b[4], 52); + VERIFY_CHECK(r != b); + + /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)a0 * b[3] + + (uint128_t)a1 * b[2] + + (uint128_t)a2 * b[1] + + (uint128_t)a3 * b[0]; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * b[4]; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + d += (uint128_t)a0 * b[4] + + (uint128_t)a1 * b[3] + + (uint128_t)a2 * b[2] + + (uint128_t)a3 * b[1] + + (uint128_t)a4 * b[0]; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * b[0]; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * b[4] + + (uint128_t)a2 * b[3] + + (uint128_t)a3 * b[2] + + (uint128_t)a4 * b[1]; + VERIFY_BITS(d, 115); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 63); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 115); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + c += (uint128_t)a0 * b[1] + + (uint128_t)a1 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * b[4] + + (uint128_t)a3 * b[3] + + (uint128_t)a4 * b[2]; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * b[2] + + (uint128_t)a1 * b[1] + + (uint128_t)a2 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * b[4] + + (uint128_t)a4 * b[3]; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { + uint128_t c, d; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + int64_t t3, t4, tx, u0; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + + /** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)(a0*2) * a3 + + (uint128_t)(a1*2) * a2; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * a4; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + a4 *= 2; + d += (uint128_t)a0 * a4 + + (uint128_t)(a1*2) * a3 + + (uint128_t)a2 * a2; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * a0; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * a4 + + (uint128_t)(a2*2) * a3; + VERIFY_BITS(d, 114); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 62); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 113); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + a0 *= 2; + c += (uint128_t)a0 * a1; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * a4 + + (uint128_t)a3 * a3; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * a2 + + (uint128_t)a1 * a1; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * a4; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_impl.h new file mode 100644 index 0000000..5127b27 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/field_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_IMPL_H_ +#define _SECP256K1_FIELD_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" + +#if defined(USE_FIELD_10X26) +#include "field_10x26_impl.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52_impl.h" +#else +#error "Please select field implementation" +#endif + +SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero(&na); +} + +SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero_var(&na); +} + +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { + /** Given that p is congruent to 3 mod 4, we can compute the square root of + * a mod p as the (p+1)/4'th power of a. + * + * As (p+1)/4 is an even number, it will have the same result for a and for + * (-a). Only one of these two numbers actually has a square root however, + * so we test at the end by squaring and comparing to the input. + * Also because (p+1)/4 is an even number, the computed square root is + * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). + */ + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in + * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<6; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_sqr(r, &t1); + + /* Check that a square root was actually calculated */ + + secp256k1_fe_sqr(&t1, r); + return secp256k1_fe_equal(&t1, a); +} + +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) { + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in + * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<5; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, a); + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(r, a, &t1); +} + +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) { +#if defined(USE_FIELD_INV_BUILTIN) + secp256k1_fe_inv(r, a); +#elif defined(USE_FIELD_INV_NUM) + secp256k1_num n, m; + static const secp256k1_fe negone = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL + ); + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + unsigned char b[32]; + int res; + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + res = secp256k1_fe_set_b32(r, b); + (void)res; + VERIFY_CHECK(res); + /* Verify the result is the (unique) valid inverse using non-GMP code. */ + secp256k1_fe_mul(&c, &c, r); + secp256k1_fe_add(&c, &negone); + CHECK(secp256k1_fe_normalizes_to_zero_var(&c)); +#else +#error "Please select field inverse implementation" +#endif +} + +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) { + secp256k1_fe u; + size_t i; + if (len < 1) { + return; + } + + VERIFY_CHECK((r + len <= a) || (a + len <= r)); + + r[0] = a[0]; + + i = 0; + while (++i < len) { + secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]); + } + + secp256k1_fe_inv_var(&u, &r[--i]); + + while (i > 0) { + size_t j = i--; + secp256k1_fe_mul(&r[j], &r[i], &u); + secp256k1_fe_mul(&u, &u, &a[j]); + } + + r[0] = u; +} + +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { +#ifndef USE_NUM_NONE + unsigned char b[32]; + secp256k1_num n; + secp256k1_num m; + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + return secp256k1_num_jacobi(&n, &m) >= 0; +#else + secp256k1_fe r; + return secp256k1_fe_sqrt(&r, a); +#endif +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/gen_context.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/gen_context.c new file mode 100644 index 0000000..1835fd4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/gen_context.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#define USE_BASIC_CONFIG 1 + +#include "basic-config.h" +#include "include/secp256k1.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_gen_impl.h" + +static void default_error_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + +int main(int argc, char **argv) { + secp256k1_ecmult_gen_context ctx; + int inner; + int outer; + FILE* fp; + + (void)argc; + (void)argv; + + fp = fopen("src/ecmult_static_context.h","w"); + if (fp == NULL) { + fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); + return -1; + } + + fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); + + secp256k1_ecmult_gen_context_init(&ctx); + secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); + for(outer = 0; outer != 64; outer++) { + fprintf(fp,"{\n"); + for(inner = 0; inner != 16; inner++) { + fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); + if (inner != 15) { + fprintf(fp,",\n"); + } else { + fprintf(fp,"\n"); + } + } + if (outer != 63) { + fprintf(fp,"},\n"); + } else { + fprintf(fp,"}\n"); + } + } + fprintf(fp,"};\n"); + secp256k1_ecmult_gen_context_clear(&ctx); + + fprintf(fp, "#undef SC\n"); + fprintf(fp, "#endif\n"); + fclose(fp); + + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group.h new file mode 100644 index 0000000..4957b24 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group.h @@ -0,0 +1,144 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_ +#define _SECP256K1_GROUP_ + +#include "num.h" +#include "field.h" + +/** A group element of the secp256k1 curve, in affine coordinates. */ +typedef struct { + secp256k1_fe x; + secp256k1_fe y; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_ge; + +#define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} +#define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +/** A group element of the secp256k1 curve, in jacobian coordinates. */ +typedef struct { + secp256k1_fe x; /* actual X: x/z^2 */ + secp256k1_fe y; /* actual Y: y/z^3 */ + secp256k1_fe z; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_gej; + +#define SECP256K1_GEJ_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} +#define SECP256K1_GEJ_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +typedef struct { + secp256k1_fe_storage x; + secp256k1_fe_storage y; +} secp256k1_ge_storage; + +#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} + +#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) + +/** Set a group element equal to the point with given X and Y coordinates */ +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); + +/** Set a group element (affine) equal to the point with the given X coordinate + * and a Y coordinate that is a quadratic residue modulo p. The return value + * is true iff a coordinate with the given X coordinate exists. + */ +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); + +/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness + * for Y. Return value indicates whether the result is valid. */ +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_ge_is_infinity(const secp256k1_ge *a); + +/** Check whether a group element is valid (i.e., on the curve). */ +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); + +/** Set a group element equal to another which is given in jacobian coordinates */ +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); + +/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); + +/** Set a batch of group elements equal to the inputs given in jacobian + * coordinates (with known z-ratios). zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len); + +/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to + * the same global z "denominator". zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y + * coordinates of the result are stored in r, the common z coordinate is + * stored in globalz. */ +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); + +/** Set a group element (jacobian) equal to the point at infinity. */ +static void secp256k1_gej_set_infinity(secp256k1_gej *r); + +/** Set a group element (jacobian) equal to another which is given in affine coordinates. */ +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); + +/** Compare the X coordinate of a group element (jacobian). */ +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); + +/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_gej_is_infinity(const secp256k1_gej *a); + +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). + * a may not be zero. Constant time. */ +static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); + +/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient + than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time + guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */ +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv); + +#ifdef USE_ENDOMORPHISM +/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */ +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); +#endif + +/** Clear a secp256k1_gej to prevent leaking sensitive information. */ +static void secp256k1_gej_clear(secp256k1_gej *r); + +/** Clear a secp256k1_ge to prevent leaking sensitive information. */ +static void secp256k1_ge_clear(secp256k1_ge *r); + +/** Convert a group element to the storage type. */ +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); + +/** Convert a group element back from the storage type. */ +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag); + +/** Rescale a jacobian point by b which must be non-zero. Constant-time. */ +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h new file mode 100644 index 0000000..7d72353 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/group_impl.h @@ -0,0 +1,700 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_IMPL_H_ +#define _SECP256K1_GROUP_IMPL_H_ + +#include "num.h" +#include "field.h" +#include "group.h" + +/* These points can be generated in sage as follows: + * + * 0. Setup a worksheet with the following parameters. + * b = 4 # whatever CURVE_B will be set to + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (b)]) + * + * 1. Determine all the small orders available to you. (If there are + * no satisfactory ones, go back and change b.) + * print C.order().factor(limit=1000) + * + * 2. Choose an order as one of the prime factors listed in the above step. + * (You can also multiply some to get a composite order, though the + * tests will crash trying to invert scalars during signing.) We take a + * random point and scale it to drop its order to the desired value. + * There is some probability this won't work; just try again. + * order = 199 + * P = C.random_point() + * P = (int(P.order()) / int(order)) * P + * assert(P.order() == order) + * + * 3. Print the values. You'll need to use a vim macro or something to + * split the hex output into 4-byte chunks. + * print "%x %x" % P.xy() + */ +#if defined(EXHAUSTIVE_TEST_ORDER) +# if EXHAUSTIVE_TEST_ORDER == 199 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069, + 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18, + 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868, + 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED +); + +const int CURVE_B = 4; +# elif EXHAUSTIVE_TEST_ORDER == 13 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0, + 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15, + 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e, + 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac +); +const int CURVE_B = 2; +# else +# error No known generator for the specified exhaustive test group order. +# endif +#else +/** Generator for secp256k1, value 'g' defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + */ +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL, + 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL, + 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL, + 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL +); + +const int CURVE_B = 7; +#endif + +static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { + secp256k1_fe zi2; + secp256k1_fe zi3; + secp256k1_fe_sqr(&zi2, zi); + secp256k1_fe_mul(&zi3, &zi2, zi); + secp256k1_fe_mul(&r->x, &a->x, &zi2); + secp256k1_fe_mul(&r->y, &a->y, &zi3); + r->infinity = a->infinity; +} + +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + r->infinity = 0; + r->x = *x; + r->y = *y; +} + +static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + return a->infinity; +} + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + *r = *a; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + secp256k1_fe_inv(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + if (a->infinity) { + return; + } + secp256k1_fe_inv_var(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { + secp256k1_fe *az; + secp256k1_fe *azi; + size_t i; + size_t count = 0; + az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + for (i = 0; i < len; i++) { + if (!a[i].infinity) { + az[count++] = a[i].z; + } + } + + azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); + secp256k1_fe_inv_all_var(azi, az, count); + free(az); + + count = 0; + for (i = 0; i < len; i++) { + r[i].infinity = a[i].infinity; + if (!a[i].infinity) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); + } + } + free(azi); +} + +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) { + size_t i = len - 1; + secp256k1_fe zi; + + if (len > 0) { + /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */ + secp256k1_fe_inv(&zi, &a[i].z); + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + + /* Work out way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + secp256k1_fe_mul(&zi, &zi, &zr[i]); + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + } + } +} + +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { + size_t i = len - 1; + secp256k1_fe zs; + + if (len > 0) { + /* The z of the final point gives us the "global Z" for the table. */ + r[i].x = a[i].x; + r[i].y = a[i].y; + *globalz = a[i].z; + r[i].infinity = 0; + zs = zr[i]; + + /* Work our way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + if (i != len - 1) { + secp256k1_fe_mul(&zs, &zs, &zr[i]); + } + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + } + } +} + +static void secp256k1_gej_set_infinity(secp256k1_gej *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_gej_clear(secp256k1_gej *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_ge_clear(secp256k1_ge *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); +} + +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { + secp256k1_fe x2, x3, c; + r->x = *x; + secp256k1_fe_sqr(&x2, x); + secp256k1_fe_mul(&x3, x, &x2); + r->infinity = 0; + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&c, &x3); + return secp256k1_fe_sqrt(&r->y, &c); +} + +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + if (!secp256k1_ge_set_xquad(r, x)) { + return 0; + } + secp256k1_fe_normalize_var(&r->y); + if (secp256k1_fe_is_odd(&r->y) != odd) { + secp256k1_fe_negate(&r->y, &r->y, 1); + } + return 1; + +} + +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + secp256k1_fe_set_int(&r->z, 1); +} + +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { + secp256k1_fe r, r2; + VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); + r2 = a->x; secp256k1_fe_normalize_weak(&r2); + return secp256k1_fe_equal_var(&r, &r2); +} + +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + r->z = a->z; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { + return a->infinity; +} + +static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) { + secp256k1_fe y2, x3, z2, z6; + if (a->infinity) { + return 0; + } + /** y^2 = x^3 + 7 + * (Y/Z^3)^2 = (X/Z^2)^3 + 7 + * Y^2 / Z^6 = X^3 / Z^6 + 7 + * Y^2 = X^3 + 7*Z^6 + */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2); + secp256k1_fe_mul_int(&z6, CURVE_B); + secp256k1_fe_add(&x3, &z6); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { + secp256k1_fe y2, x3, c; + if (a->infinity) { + return 0; + } + /* y^2 = x^3 + 7 */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&x3, &c); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. + * + * Note that there is an implementation described at + * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + * which trades a multiply for a square, but in practice this is actually slower, + * mainly because it requires more normalizations. + */ + secp256k1_fe t1,t2,t3,t4; + /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, + * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have + * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * the infinity flag even though the point doubles to infinity, and the result + * point will be gibberish (z = 0 but infinity = 0). + */ + r->infinity = a->infinity; + if (r->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + return; + } + + if (rzr != NULL) { + *rzr = a->y; + secp256k1_fe_normalize_weak(rzr); + secp256k1_fe_mul_int(rzr, 2); + } + + secp256k1_fe_mul(&r->z, &a->z, &a->y); + secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ + secp256k1_fe_sqr(&t1, &a->x); + secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ + secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ + secp256k1_fe_sqr(&t3, &a->y); + secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ + secp256k1_fe_sqr(&t4, &t3); + secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ + secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ + r->x = t3; + secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ + secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ + secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ + secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ + secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ + secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ + secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ + secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ + secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ +} + +static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); + secp256k1_gej_double_var(r, a, rzr); +} + +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { + /* Operations: 12 mul, 4 sqr, 2 normalize, 12 mul_int/add/negate */ + secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + *r = *b; + return; + } + + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + + r->infinity = 0; + secp256k1_fe_sqr(&z22, &b->z); + secp256k1_fe_sqr(&z12, &a->z); + secp256k1_fe_mul(&u1, &a->x, &z22); + secp256k1_fe_mul(&u2, &b->x, &z12); + secp256k1_fe_mul(&s1, &a->y, &z22); secp256k1_fe_mul(&s1, &s1, &b->z); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + secp256k1_fe_mul(&h, &h, &b->z); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { + /* 8 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + secp256k1_gej_set_ge(r, b); + return; + } + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + r->infinity = 0; + + secp256k1_fe_sqr(&z12, &a->z); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { + /* 9 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe az, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (b->infinity) { + *r = *a; + return; + } + if (a->infinity) { + secp256k1_fe bzinv2, bzinv3; + r->infinity = b->infinity; + secp256k1_fe_sqr(&bzinv2, bzinv); + secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); + secp256k1_fe_mul(&r->x, &b->x, &bzinv2); + secp256k1_fe_mul(&r->y, &b->y, &bzinv3); + secp256k1_fe_set_int(&r->z, 1); + return; + } + r->infinity = 0; + + /** We need to calculate (rx,ry,rz) = (ax,ay,az) + (bx,by,1/bzinv). Due to + * secp256k1's isomorphism we can multiply the Z coordinates on both sides + * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). + * This means that (rx,ry,rz) can be calculated as + * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. + * The variable az below holds the modified Z coordinate for a, which is used + * for the computation of rx and ry, but not for rz. + */ + secp256k1_fe_mul(&az, &a->z, bzinv); + + secp256k1_fe_sqr(&z12, &az); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &az); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, NULL); + } else { + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + r->z = a->z; secp256k1_fe_mul(&r->z, &r->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + + +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { + /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ + static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; + secp256k1_fe m_alt, rr_alt; + int infinity, degenerate; + VERIFY_CHECK(!b->infinity); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + + /** In: + * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. + * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. + * we find as solution for a unified addition/doubling formula: + * lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. + * x3 = lambda^2 - (x1 + x2) + * 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). + * + * Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: + * U1 = X1*Z2^2, U2 = X2*Z1^2 + * S1 = Y1*Z2^3, S2 = Y2*Z1^3 + * Z = Z1*Z2 + * T = U1+U2 + * M = S1+S2 + * Q = T*M^2 + * R = T^2-U1*U2 + * X3 = 4*(R^2-Q) + * Y3 = 4*(R*(3*Q-2*R^2)-M^4) + * Z3 = 2*M*Z + * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) + * + * This formula has the benefit of being the same for both addition + * of distinct points and doubling. However, it breaks down in the + * case that either point is infinity, or that y1 = -y2. We handle + * these cases in the following ways: + * + * - If b is infinity we simply bail by means of a VERIFY_CHECK. + * + * - If a is infinity, we detect this, and at the end of the + * computation replace the result (which will be meaningless, + * but we compute to be constant-time) with b.x : b.y : 1. + * + * - If a = -b, we have y1 = -y2, which is a degenerate case. + * But here the answer is infinity, so we simply set the + * infinity flag of the result, overriding the computed values + * without even needing to cmov. + * + * - If y1 = -y2 but x1 != x2, which does occur thanks to certain + * properties of our curve (specifically, 1 has nontrivial cube + * roots in our field, and the curve equation has no x coefficient) + * then the answer is not infinity but also not given by the above + * equation. In this case, we cmov in place an alternate expression + * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these + * expressions for lambda are defined, they are equal, and can be + * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) + * then substitution of x^3 + 7 for y^2 (using the curve equation). + * For all pairs of nonzero points (a, b) at least one is defined, + * so this covers everything. + */ + + secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ + u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ + secp256k1_fe_mul(&u2, &b->x, &zz); /* u2 = U2 = X2*Z1^2 (1) */ + s1 = a->y; secp256k1_fe_normalize_weak(&s1); /* s1 = S1 = Y1*Z2^3 (1) */ + secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z1^2 (1) */ + secp256k1_fe_mul(&s2, &s2, &a->z); /* s2 = S2 = Y2*Z1^3 (1) */ + t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ + m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ + secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ + secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ + secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ + secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ + /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" + * case that Z = z1z2 = 0, and this is special-cased later on). */ + degenerate = secp256k1_fe_normalizes_to_zero(&m) & + secp256k1_fe_normalizes_to_zero(&rr); + /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. + * This means either x1 == beta*x2 or beta*x1 == x2, where beta is + * a nontrivial cube root of one. In either case, an alternate + * non-indeterminate expression for lambda is (y1 - y2)/(x1 - x2), + * so we set R/M equal to this. */ + rr_alt = s1; + secp256k1_fe_mul_int(&rr_alt, 2); /* rr = Y1*Z2^3 - Y2*Z1^3 (2) */ + secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 */ + + secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); + secp256k1_fe_cmov(&m_alt, &m, !degenerate); + /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. + * From here on out Ralt and Malt represent the numerator + * and denominator of lambda; R and M represent the explicit + * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ + secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ + secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ + /* These two lines use the observation that either M == Malt or M == 0, + * so M^3 * Malt is either Malt^4 (which is computed by squaring), or + * zero (which is "computed" by cmov). So the cost is one squaring + * versus two multiplications. */ + secp256k1_fe_sqr(&n, &n); + secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ + secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ + secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ + infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity); + secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ + secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ + secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ + secp256k1_fe_normalize_weak(&t); + r->x = t; /* r->x = Ralt^2-Q (1) */ + secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ + secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ + secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ + secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ + secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ + secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ + + /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ + secp256k1_fe_cmov(&r->x, &b->x, a->infinity); + secp256k1_fe_cmov(&r->y, &b->y, a->infinity); + secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); + r->infinity = infinity; +} + +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { + /* Operations: 4 mul, 1 sqr */ + secp256k1_fe zz; + VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + secp256k1_fe_sqr(&zz, s); + secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ + secp256k1_fe_mul(&r->y, &r->y, &zz); + secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ + secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ +} + +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { + secp256k1_fe x, y; + VERIFY_CHECK(!a->infinity); + x = a->x; + secp256k1_fe_normalize(&x); + y = a->y; + secp256k1_fe_normalize(&y); + secp256k1_fe_to_storage(&r->x, &x); + secp256k1_fe_to_storage(&r->y, &y); +} + +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { + secp256k1_fe_from_storage(&r->x, &a->x); + secp256k1_fe_from_storage(&r->y, &a->y); + r->infinity = 0; +} + +static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { + secp256k1_fe_storage_cmov(&r->x, &a->x, flag); + secp256k1_fe_storage_cmov(&r->y, &a->y, flag); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { + static const secp256k1_fe beta = SECP256K1_FE_CONST( + 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, + 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul + ); + *r = *a; + secp256k1_fe_mul(&r->x, &r->x, &beta); +} +#endif + +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_quad_var(&yz); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash.h new file mode 100644 index 0000000..fca98ca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash.h @@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_ +#define _SECP256K1_HASH_ + +#include +#include + +typedef struct { + uint32_t s[8]; + uint32_t buf[16]; /* In big endian */ + size_t bytes; +} secp256k1_sha256_t; + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash); +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32); + +typedef struct { + secp256k1_sha256_t inner, outer; +} secp256k1_hmac_sha256_t; + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32); + +typedef struct { + unsigned char v[32]; + unsigned char k[32]; + int retry; +} secp256k1_rfc6979_hmac_sha256_t; + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash_impl.h new file mode 100644 index 0000000..b47e65f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/hash_impl.h @@ -0,0 +1,281 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_IMPL_H_ +#define _SECP256K1_HASH_IMPL_H_ + +#include "hash.h" + +#include +#include +#include + +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10)) +#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7)) +#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3)) +#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10)) + +#define Round(a,b,c,d,e,f,g,h,k,w) do { \ + uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \ + uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \ + (d) += t1; \ + (h) = t1 + t2; \ +} while(0) + +#ifdef WORDS_BIGENDIAN +#define BE32(x) (x) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#endif + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) { + hash->s[0] = 0x6a09e667ul; + hash->s[1] = 0xbb67ae85ul; + hash->s[2] = 0x3c6ef372ul; + hash->s[3] = 0xa54ff53aul; + hash->s[4] = 0x510e527ful; + hash->s[5] = 0x9b05688cul; + hash->s[6] = 0x1f83d9abul; + hash->s[7] = 0x5be0cd19ul; + hash->bytes = 0; +} + +/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ +static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) { + size_t bufsize = hash->bytes & 0x3F; + hash->bytes += len; + while (bufsize + len >= 64) { + /* Fill the buffer, and process it. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); + data += 64 - bufsize; + len -= 64 - bufsize; + secp256k1_sha256_transform(hash->s, hash->buf); + bufsize = 0; + } + if (len) { + /* Fill the buffer with what remains. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, len); + } +} + +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) { + static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t sizedesc[2]; + uint32_t out[8]; + int i = 0; + sizedesc[0] = BE32(hash->bytes >> 29); + sizedesc[1] = BE32(hash->bytes << 3); + secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); + secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + for (i = 0; i < 8; i++) { + out[i] = BE32(hash->s[i]); + hash->s[i] = 0; + } + memcpy(out32, (const unsigned char*)out, 32); +} + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) { + int n; + unsigned char rkey[64]; + if (keylen <= 64) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 64 - keylen); + } else { + secp256k1_sha256_t sha256; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, key, keylen); + secp256k1_sha256_finalize(&sha256, rkey); + memset(rkey + 32, 0, 32); + } + + secp256k1_sha256_initialize(&hash->outer); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c; + } + secp256k1_sha256_write(&hash->outer, rkey, 64); + + secp256k1_sha256_initialize(&hash->inner); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c ^ 0x36; + } + secp256k1_sha256_write(&hash->inner, rkey, 64); + memset(rkey, 0, 64); +} + +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) { + secp256k1_sha256_write(&hash->inner, data, size); +} + +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) { + unsigned char temp[32]; + secp256k1_sha256_finalize(&hash->inner, temp); + secp256k1_sha256_write(&hash->outer, temp, 32); + memset(temp, 0, 32); + secp256k1_sha256_finalize(&hash->outer, out32); +} + + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256_t hmac; + static const unsigned char zero[1] = {0x00}; + static const unsigned char one[1] = {0x01}; + + memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */ + memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */ + + /* RFC6979 3.2.d. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + + /* RFC6979 3.2.f. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, one, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + rng->retry = 0; +} + +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) { + /* RFC6979 3.2.h. */ + static const unsigned char zero[1] = {0x00}; + if (rng->retry) { + secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + } + + while (outlen > 0) { + secp256k1_hmac_sha256_t hmac; + int now = outlen; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + if (now > 32) { + now = 32; + } + memcpy(out, rng->v, now); + out += now; + outlen -= now; + } + + rng->retry = 1; +} + +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) { + memset(rng->k, 0, 32); + memset(rng->v, 0, 32); + rng->retry = 0; +} + +#undef BE32 +#undef Round +#undef sigma1 +#undef sigma0 +#undef Sigma1 +#undef Sigma0 +#undef Maj +#undef Ch + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java new file mode 100644 index 0000000..1c67802 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -0,0 +1,446 @@ +/* + * Copyright 2013 Google Inc. + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import java.math.BigInteger; +import com.google.common.base.Preconditions; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + *

This class holds native methods to handle ECDSA verification.

+ * + *

You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1

+ * + *

To build secp256k1 for use with bitcoinj, run + * `./configure --enable-jni --enable-experimental --enable-module-ecdh` + * and `make` then copy `.libs/libsecp256k1.so` to your system library path + * or point the JVM to the folder containing it with -Djava.library.path + *

+ */ +public class NativeSecp256k1 { + + private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private static final Lock r = rwl.readLock(); + private static final Lock w = rwl.writeLock(); + private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); + /** + * Verifies the given secp256k1 signature in native code. + * Calling when enabled == false is undefined (probably library not loaded) + * + * @param data The data which was signed, must be exactly 32 bytes + * @param signature The signature + * @param pub The public key which did the signing + */ + public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 520) { + byteBuff = ByteBuffer.allocateDirect(520); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(signature); + byteBuff.put(pub); + + byte[][] retByteArray; + + r.lock(); + try { + return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; + } finally { + r.unlock(); + } + } + + /** + * libsecp256k1 Create an ECDSA signature. + * + * @param data Message hash, 32 bytes + * @param key Secret key, 32 bytes + * + * Return values + * @param sig byte array of signature + */ + public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && sec.length <= 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + 32) { + byteBuff = ByteBuffer.allocateDirect(32 + 32); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(sec); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] sigArr = retByteArray[0]; + int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(sigArr.length, sigLen, "Got bad signature length."); + + return retVal == 0 ? new byte[0] : sigArr; + } + + /** + * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid + * + * @param seckey ECDSA Secret key, 32 bytes + */ + public static boolean secKeyVerify(byte[] seckey) { + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + r.lock(); + try { + return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; + } finally { + r.unlock(); + } + } + + + /** + * libsecp256k1 Compute Pubkey - computes public key from secret key + * + * @param seckey ECDSA Secret key, 32 bytes + * + * Return values + * @param pubkey ECDSA Public key, 33 or 65 bytes + */ + //TODO add a 'compressed' arg + public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + return retVal == 0 ? new byte[0]: pubArr; + } + + /** + * libsecp256k1 Cleanup - This destroys the secp256k1 context object + * This should be called at the end of the program for proper cleanup of the context. + */ + public static synchronized void cleanup() { + w.lock(); + try { + secp256k1_destroy_context(Secp256k1Context.getContext()); + } finally { + w.unlock(); + } + } + + public static long cloneContext() { + r.lock(); + try { + return secp256k1_ctx_clone(Secp256k1Context.getContext()); + } finally { r.unlock(); } + } + + /** + * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 create ECDH secret - constant time ECDH calculation + * + * @param seckey byte array of secret key used in exponentiaion + * @param pubkey byte array of public key used in exponentiaion + */ + public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { + byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + byteBuff.put(pubkey); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] resArr = retByteArray[0]; + int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + + assertEquals(resArr.length, 32, "Got bad result length."); + assertEquals(retVal, 1, "Failed return value check."); + + return resArr; + } + + /** + * libsecp256k1 randomize - updates the context randomization + * + * @param seed 32-byte random seed + */ + public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ + Preconditions.checkArgument(seed.length == 32 || seed == null); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seed.length) { + byteBuff = ByteBuffer.allocateDirect(seed.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seed); + + w.lock(); + try { + return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; + } finally { + w.unlock(); + } + } + + private static native long secp256k1_ctx_clone(long context); + + private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); + + private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); + + private static native void secp256k1_destroy_context(long context); + + private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); + + private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); + + private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); + + private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); + +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java new file mode 100644 index 0000000..c00d088 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,226 @@ +package org.bitcoin; + +import com.google.common.io.BaseEncoding; +import java.util.Arrays; +import java.math.BigInteger; +import javax.xml.bind.DatatypeConverter; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + * This class holds test cases defined for testing this library. + */ +public class NativeSecp256k1Test { + + //TODO improve comments/add more tests + /** + * This tests verify() for a valid signature + */ + public static void testVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + assertEquals( result, true , "testVerifyPos"); + } + + /** + * This tests verify() for a non-valid signature + */ + public static void testVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testVerifyNeg"); + } + + /** + * This tests secret key verify() for a valid secretkey + */ + public static void testSecKeyVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, true , "testSecKeyVerifyPos"); + } + + /** + * This tests secret key verify() for a invalid secretkey + */ + public static void testSecKeyVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testSecKeyVerifyNeg"); + } + + /** + * This tests public key create() for a valid secretkey + */ + public static void testPubKeyCreatePos() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); + } + + /** + * This tests public key create() for a invalid secretkey + */ + public static void testPubKeyCreateNeg() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); + } + + /** + * This tests sign() for a valid secretkey + */ + public static void testSignPos() throws AssertFailException{ + + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); + } + + /** + * This tests sign() for a invalid secretkey + */ + public static void testSignNeg() throws AssertFailException{ + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "" , "testSignNeg"); + } + + /** + * This tests private key tweak-add + */ + public static void testPrivKeyTweakAdd_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); + } + + /** + * This tests private key tweak-mul + */ + public static void testPrivKeyTweakMul_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); + } + + /** + * This tests private key tweak-add uncompressed + */ + public static void testPrivKeyTweakAdd_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); + } + + /** + * This tests private key tweak-mul uncompressed + */ + public static void testPrivKeyTweakMul_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); + } + + /** + * This tests seed randomization + */ + public static void testRandomize() throws AssertFailException { + byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random" + boolean result = NativeSecp256k1.randomize(seed); + assertEquals( result, true, "testRandomize"); + } + + public static void testCreateECDHSecret() throws AssertFailException{ + + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); + String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); + } + + public static void main(String[] args) throws AssertFailException{ + + + System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n"); + + assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" ); + + //Test verify() success/fail + testVerifyPos(); + testVerifyNeg(); + + //Test secKeyVerify() success/fail + testSecKeyVerifyPos(); + testSecKeyVerifyNeg(); + + //Test computePubkey() success/fail + testPubKeyCreatePos(); + testPubKeyCreateNeg(); + + //Test sign() success/fail + testSignPos(); + testSignNeg(); + + //Test privKeyTweakAdd() 1 + testPrivKeyTweakAdd_1(); + + //Test privKeyTweakMul() 2 + testPrivKeyTweakMul_1(); + + //Test privKeyTweakAdd() 3 + testPrivKeyTweakAdd_2(); + + //Test privKeyTweakMul() 4 + testPrivKeyTweakMul_2(); + + //Test randomize() + testRandomize(); + + //Test ECDH + testCreateECDHSecret(); + + NativeSecp256k1.cleanup(); + + System.out.println(" All tests passed." ); + + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java new file mode 100644 index 0000000..04732ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +public class NativeSecp256k1Util{ + + public static void assertEquals( int val, int val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + } + + public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static void assertEquals( String val, String val2, String message ) throws AssertFailException{ + if( !val.equals(val2) ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static class AssertFailException extends Exception { + public AssertFailException(String message) { + super( message ); + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java new file mode 100644 index 0000000..216c986 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +/** + * This class holds the context reference used in native methods + * to handle ECDSA operations. + */ +public class Secp256k1Context { + private static final boolean enabled; //true if the library is loaded + private static final long context; //ref to pointer to context obj + + static { //static initializer + boolean isEnabled = true; + long contextRef = -1; + try { + System.loadLibrary("secp256k1"); + contextRef = secp256k1_init_context(); + } catch (UnsatisfiedLinkError e) { + System.out.println("UnsatisfiedLinkError: " + e.toString()); + isEnabled = false; + } + enabled = isEnabled; + context = contextRef; + } + + public static boolean isEnabled() { + return enabled; + } + + public static long getContext() { + if(!enabled) return -1; //sanity check + return context; + } + + private static native long secp256k1_init_context(); +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c new file mode 100644 index 0000000..bcef7b3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include "org_bitcoin_NativeSecp256k1.h" +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" + + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); + + (void)classObject;(void)env; + + return ctx_clone_l; + +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_context_randomize(ctx, seed); + +} + +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + secp256k1_context_destroy(ctx); + + (void)classObject;(void)env; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* sigdata = { (unsigned char*) (data + 32) }; + const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; + + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); + + if( ret ) { + ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if( ret ) { + ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); + } + } + + (void)classObject; + + return ret; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + unsigned char* secKey = (unsigned char*) (data + 32); + + jobjectArray retArray; + jbyteArray sigArray, intsByteArray; + unsigned char intsarray[2]; + + secp256k1_ecdsa_signature sig[72]; + + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + + unsigned char outputSer[72]; + size_t outputLen = 72; + + if( ret ) { + int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + sigArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_ec_seckey_verify(ctx, secKey); +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + secp256k1_pubkey pubkey; + + jobjectArray retArray; + jbyteArray pubkeyArray, intsByteArray; + unsigned char intsarray[2]; + + int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); + + unsigned char outputSer[65]; + size_t outputLen = 65; + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubkeyArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; + +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; +/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if( ret ) { + ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if ( ret ) { + ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine + (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys) +{ + (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys; + + return 0; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* pubdata = (const unsigned char*) (secdata + 32); + + jobjectArray retArray; + jbyteArray outArray, intsByteArray; + unsigned char intsarray[1]; + secp256k1_pubkey pubkey; + unsigned char nonce_res[32]; + size_t outputLen = 32; + + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if (ret) { + ret = secp256k1_ecdh( + ctx, + nonce_res, + &pubkey, + secdata + ); + } + + intsarray[0] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + outArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); + (*env)->SetObjectArrayElement(env, retArray, 0, outArray); + + intsByteArray = (*env)->NewByteArray(env, 1); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h new file mode 100644 index 0000000..fe613c9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -0,0 +1,119 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_NativeSecp256k1 */ + +#ifndef _Included_org_bitcoin_NativeSecp256k1 +#define _Included_org_bitcoin_NativeSecp256k1 +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ctx_clone + * Signature: (J)J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_context_randomize + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_destroy_context + * Signature: (J)V + */ +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_verify + * Signature: (Ljava/nio/ByteBuffer;JII)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv *, jclass, jobject, jlong, jint, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_sign + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_seckey_verify + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_create + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_parse + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdh + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 0000000..a52939e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#include +#include +#include "org_bitcoin_Secp256k1Context.h" +#include "include/secp256k1.h" + +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv* env, jclass classObject) +{ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + (void)classObject;(void)env; + + return (uintptr_t)ctx; +} + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h new file mode 100644 index 0000000..0d2bc84 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_Secp256k1Context */ + +#ifndef _Included_org_bitcoin_Secp256k1Context +#define _Included_org_bitcoin_Secp256k1Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_Secp256k1Context + * Method: secp256k1_init_context + * Signature: ()J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include new file mode 100644 index 0000000..e3088b4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_ecdh.h +noinst_HEADERS += src/modules/ecdh/main_impl.h +noinst_HEADERS += src/modules/ecdh/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_ecdh +bench_ecdh_SOURCES = src/bench_ecdh.c +bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h new file mode 100644 index 0000000..9e30fb7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_MAIN_ +#define _SECP256K1_MODULE_ECDH_MAIN_ + +#include "include/secp256k1_ecdh.h" +#include "ecmult_const_impl.h" + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_gej res; + secp256k1_ge pt; + secp256k1_scalar s; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(result != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + secp256k1_pubkey_load(ctx, &pt, point); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + unsigned char x[32]; + unsigned char y[1]; + secp256k1_sha256_t sha; + + secp256k1_ecmult_const(&res, &pt, &s); + secp256k1_ge_set_gej(&pt, &res); + /* Compute a hash of the point in compressed form + * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not + * expect its output to be secret and has a timing sidechannel. */ + secp256k1_fe_normalize(&pt.x); + secp256k1_fe_normalize(&pt.y); + secp256k1_fe_get_b32(x, &pt.x); + y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, y, sizeof(y)); + secp256k1_sha256_write(&sha, x, sizeof(x)); + secp256k1_sha256_finalize(&sha, result); + ret = 1; + } + + secp256k1_scalar_clear(&s); + return ret; +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h new file mode 100644 index 0000000..85a5d0a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_TESTS_ +#define _SECP256K1_MODULE_ECDH_TESTS_ + +void test_ecdh_api(void) { + /* Setup context that just counts errors */ + secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_pubkey point; + unsigned char res[32]; + unsigned char s_one[32] = { 0 }; + int32_t ecount = 0; + s_one[31] = 1; + + secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); + + /* Check all NULLs are detected */ + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 3); + + /* Cleanup */ + secp256k1_context_destroy(tctx); +} + +void test_ecdh_generator_basepoint(void) { + unsigned char s_one[32] = { 0 }; + secp256k1_pubkey point[2]; + int i; + + s_one[31] = 1; + /* Check against pubkey creation when the basepoint is the generator */ + for (i = 0; i < 100; ++i) { + secp256k1_sha256_t sha; + unsigned char s_b32[32]; + unsigned char output_ecdh[32]; + unsigned char output_ser[32]; + unsigned char point_ser[33]; + size_t point_ser_len = sizeof(point_ser); + secp256k1_scalar s; + + random_scalar_order(&s); + secp256k1_scalar_get_b32(s_b32, &s); + + /* compute using ECDH function */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); + CHECK(point_ser_len == sizeof(point_ser)); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, point_ser, point_ser_len); + secp256k1_sha256_finalize(&sha, output_ser); + /* compare */ + CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + } +} + +void test_bad_scalar(void) { + unsigned char s_zero[32] = { 0 }; + unsigned char s_overflow[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + unsigned char s_rand[32] = { 0 }; + unsigned char output[32]; + secp256k1_scalar rand; + secp256k1_pubkey point; + + /* Create random point */ + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s_rand, &rand); + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); + + /* Try to multiply it by bad values */ + CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + /* ...and a good one */ + s_overflow[31] -= 1; + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); +} + +void run_ecdh_tests(void) { + test_ecdh_api(); + test_ecdh_generator_basepoint(); + test_bad_scalar(); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include new file mode 100644 index 0000000..bf23c26 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_recovery.h +noinst_HEADERS += src/modules/recovery/main_impl.h +noinst_HEADERS += src/modules/recovery/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_recover +bench_recover_SOURCES = src/bench_recover.c +bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h new file mode 100755 index 0000000..c6fbe23 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ +#define _SECP256K1_MODULE_RECOVERY_MAIN_ + +#include "include/secp256k1_recovery.h" + +static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } + *recid = sig->data[64]; +} + +static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } + sig->data[64] = recid; +} + +int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + ARG_CHECK(recid >= 0 && recid <= 3); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig) { + secp256k1_scalar r, s; + + (void)ctx; + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(recid != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const secp256k1_ecdsa_recoverable_signature* sigin) { + secp256k1_scalar r, s; + int recid; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, sigin); + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; +} + +static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { + unsigned char brx[32]; + secp256k1_fe fx; + secp256k1_ge x; + secp256k1_gej xj; + secp256k1_scalar rn, u1, u2; + secp256k1_gej qj; + int r; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_get_b32(brx, sigr); + r = secp256k1_fe_set_b32(&fx, brx); + (void)r; + VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ + if (recid & 2) { + if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + return 0; + } + secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); + } + if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { + return 0; + } + secp256k1_gej_set_ge(&xj, &x); + secp256k1_scalar_inverse_var(&rn, sigr); + secp256k1_scalar_mul(&u1, &rn, message); + secp256k1_scalar_negate(&u1, &u1); + secp256k1_scalar_mul(&u2, &rn, sigs); + secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); + secp256k1_ge_set_gej_var(pubkey, &qj); + return !secp256k1_gej_is_infinity(&qj); +} + +int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int recid; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!secp256k1_scalar_is_zero(&non) && !overflow) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + int recid; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature); + VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */ + secp256k1_scalar_set_b32(&m, msg32, NULL); + if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) { + secp256k1_pubkey_save(pubkey, &q); + return 1; + } else { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h new file mode 100644 index 0000000..765c7dd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h @@ -0,0 +1,393 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ +#define _SECP256K1_MODULE_RECOVERY_TESTS_ + +static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + + /* On the first run, return 0 to force a second run */ + if (counter == 0) { + memset(nonce32, 0, 32); + return 1; + } + /* On the second run, return an overflow to force a third run */ + if (counter == 1) { + memset(nonce32, 0xff, 32); + return 1; + } + /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */ + memset(nonce32, 1, 32); + return secp256k1_rand_bits(1); +} + +void test_ecdsa_recovery_api(void) { + /* Setup contexts that just count errors */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + secp256k1_ecdsa_signature normal_sig; + secp256k1_ecdsa_recoverable_signature recsig; + unsigned char privkey[32] = { 1 }; + unsigned char message[32] = { 2 }; + int32_t ecount = 0; + int recid = 0; + unsigned char sig[74]; + unsigned char zero_privkey[32] = { 0 }; + unsigned char over_privkey[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Check bad contexts and NULLs for signing */ + ecount = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */ + secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL); + CHECK(ecount == 5); + /* These will all fail, but not in ARG_CHECK way */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0); + /* This one will succeed. */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 5); + + /* Check signing with a goofy nonce function */ + + /* Check bad contexts and NULLs for recovery */ + ecount = 0; + CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0); + CHECK(ecount == 5); + + /* Check NULLs for conversion */ + CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1); + + /* Check NULLs for de/serialization */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1); + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0); + CHECK(ecount == 7); + /* overflow in signature will fail but not affect ecount */ + memcpy(sig, over_privkey, 32); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0); + CHECK(ecount == 7); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +void test_ecdsa_recovery_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + secp256k1_ecdsa_signature signature[5]; + secp256k1_ecdsa_recoverable_signature rsignature[5]; + unsigned char sig[74]; + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + int recid = 0; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Serialize/parse compact and verify/recover. */ + extra[0] = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(memcmp(&signature[4], &signature[0], 64) == 0); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + memset(&rsignature[4], 0, sizeof(rsignature[4])); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + /* Parse compact (with recovery id) and recover. */ + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1); + CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0); + /* Serialize/destroy/parse signature and verify again. */ + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0); + /* Recover again */ + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +} + +/* Tests several edge cases. */ +void test_ecdsa_recovery_edge_cases(void) { + const unsigned char msg32[32] = { + 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', + 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', + 's', 's', 'a', 'g', 'e', '.', '.', '.' + }; + const unsigned char sig64[64] = { + /* Generated by signing the above message with nonce 'This is the nonce we will use...' + * and secret key 0 (which is not valid), resulting in recid 0. */ + 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, + 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, + 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, + 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, + 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, + 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, + 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, + 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 + }; + secp256k1_pubkey pubkey; + /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ + const unsigned char sigb64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + secp256k1_pubkey pubkeyb; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + int recid; + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 0)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 1)); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 2)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 3)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + + for (recid = 0; recid < 4; recid++) { + int i; + int recid2; + /* (4,4) encoded in DER. */ + unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; + unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; + unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; + unsigned char sigbderalt1[39] = { + 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt2[39] = { + 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + unsigned char sigbderalt3[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt4[40] = { + 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + /* (order + r,4) encoded in DER. */ + unsigned char sigbderlong[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, + 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, + 0x8C, 0xD0, 0x36, 0x41, 0x45, 0x02, 0x01, 0x04 + }; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1); + for (recid2 = 0; recid2 < 4; recid2++) { + secp256k1_pubkey pubkey2b; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1); + /* Verifying with (order + r,4) should always fail. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + /* DER parsing tests. */ + /* Zero length r/s. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); + /* Leading zeros. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); + sigbderalt3[4] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbderalt4[7] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + /* Damage signature. */ + sigbder[7]++; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbder[7]--; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, 6) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder) - 1) == 0); + for(i = 0; i < 8; i++) { + int c; + unsigned char orig = sigbder[i]; + /*Try every single-byte change.*/ + for (c = 0; c < 256; c++) { + if (c == orig ) { + continue; + } + sigbder[i] = c; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + sigbder[i] = orig; + } + } + + /* Test r/s equal to zero */ + { + /* (1,1) encoded in DER. */ + unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; + unsigned char sigc64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + secp256k1_pubkey pubkeyc; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyc, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 1); + sigcder[4] = 0; + sigc64[31] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + sigcder[4] = 1; + sigcder[7] = 0; + sigc64[31] = 1; + sigc64[63] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + } +} + +void run_recovery_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_ecdsa_recovery_api(); + } + for (i = 0; i < 64*count; i++) { + test_ecdsa_recovery_end_to_end(); + } + test_ecdsa_recovery_edge_cases(); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num.h new file mode 100644 index 0000000..7bb9c5b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num.h @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_ +#define _SECP256K1_NUM_ + +#ifndef USE_NUM_NONE + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_NUM_GMP) +#include "num_gmp.h" +#else +#error "Please select num implementation" +#endif + +/** Copy a number. */ +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a); + +/** Convert a number's absolute value to a binary big-endian string. + * There must be enough place. */ +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a); + +/** Set a number to the value of a binary big-endian string. */ +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen); + +/** Compute a modular inverse. The input must be less than the modulus. */ +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m); + +/** Compute the jacobi symbol (a|b). b must be positive and odd. */ +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b); + +/** Compare the absolute value of two numbers. */ +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b); + +/** Test whether two number are equal (including sign). */ +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b); + +/** Add two (signed) numbers. */ +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Subtract two (signed) numbers. */ +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Multiply two (signed) numbers. */ +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1, + even if r was negative. */ +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m); + +/** Right-shift the passed number by bits bits. */ +static void secp256k1_num_shift(secp256k1_num *r, int bits); + +/** Check whether a number is zero. */ +static int secp256k1_num_is_zero(const secp256k1_num *a); + +/** Check whether a number is one. */ +static int secp256k1_num_is_one(const secp256k1_num *a); + +/** Check whether a number is strictly negative. */ +static int secp256k1_num_is_neg(const secp256k1_num *a); + +/** Change a number's sign. */ +static void secp256k1_num_negate(secp256k1_num *r); + +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp.h new file mode 100644 index 0000000..7dd8130 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp.h @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_ +#define _SECP256K1_NUM_REPR_ + +#include + +#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS) + +typedef struct { + mp_limb_t data[2*NUM_LIMBS]; + int neg; + int limbs; +} secp256k1_num; + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h new file mode 100644 index 0000000..3a46495 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_gmp_impl.h @@ -0,0 +1,288 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_IMPL_H_ +#define _SECP256K1_NUM_REPR_IMPL_H_ + +#include +#include +#include + +#include "util.h" +#include "num.h" + +#ifdef VERIFY +static void secp256k1_num_sanity(const secp256k1_num *a) { + VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0)); +} +#else +#define secp256k1_num_sanity(a) do { } while(0) +#endif + +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) { + *r = *a; +} + +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) { + unsigned char tmp[65]; + int len = 0; + int shift = 0; + if (a->limbs>1 || a->data[0] != 0) { + len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs); + } + while (shift < len && tmp[shift] == 0) shift++; + VERIFY_CHECK(len-shift <= (int)rlen); + memset(r, 0, rlen - len + shift); + if (len > shift) { + memcpy(r + rlen - len + shift, tmp + shift, len - shift); + } + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) { + int len; + VERIFY_CHECK(alen > 0); + VERIFY_CHECK(alen <= 64); + len = mpn_set_str(r->data, a, alen, 256); + if (len == 0) { + r->data[0] = 0; + len = 1; + } + VERIFY_CHECK(len <= NUM_LIMBS*2); + r->limbs = len; + r->neg = 0; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs); + r->limbs = a->limbs; + if (c != 0) { + VERIFY_CHECK(r->limbs < 2*NUM_LIMBS); + r->data[r->limbs++] = c; + } +} + +static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs); + (void)c; + VERIFY_CHECK(c == 0); + r->limbs = a->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) { + secp256k1_num_sanity(r); + secp256k1_num_sanity(m); + + if (r->limbs >= m->limbs) { + mp_limb_t t[2*NUM_LIMBS]; + mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs); + memset(t, 0, sizeof(t)); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } + + if (r->neg && (r->limbs > 1 || r->data[0] != 0)) { + secp256k1_num_sub_abs(r, m, r); + r->neg = 0; + } +} + +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) { + int i; + mp_limb_t g[NUM_LIMBS+1]; + mp_limb_t u[NUM_LIMBS+1]; + mp_limb_t v[NUM_LIMBS+1]; + mp_size_t sn; + mp_size_t gn; + secp256k1_num_sanity(a); + secp256k1_num_sanity(m); + + /** mpn_gcdext computes: (G,S) = gcdext(U,V), where + * * G = gcd(U,V) + * * G = U*S + V*T + * * U has equal or more limbs than V, and V has no padding + * If we set U to be (a padded version of) a, and V = m: + * G = a*S + m*T + * G = a*S mod m + * Assuming G=1: + * S = 1/a mod m + */ + VERIFY_CHECK(m->limbs <= NUM_LIMBS); + VERIFY_CHECK(m->data[m->limbs-1] != 0); + for (i = 0; i < m->limbs; i++) { + u[i] = (i < a->limbs) ? a->data[i] : 0; + v[i] = m->data[i]; + } + sn = NUM_LIMBS+1; + gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs); + (void)gn; + VERIFY_CHECK(gn == 1); + VERIFY_CHECK(g[0] == 1); + r->neg = a->neg ^ m->neg; + if (sn < 0) { + mpn_sub(r->data, m->data, m->limbs, r->data, -sn); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } else { + r->limbs = sn; + } + memset(g, 0, sizeof(g)); + memset(u, 0, sizeof(u)); + memset(v, 0, sizeof(v)); +} + +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) { + int ret; + mpz_t ga, gb; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1)); + + mpz_inits(ga, gb, NULL); + + mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data); + mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data); + if (a->neg) { + mpz_neg(ga, ga); + } + + ret = mpz_jacobi(ga, gb); + + mpz_clears(ga, gb, NULL); + + return ret; +} + +static int secp256k1_num_is_one(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 1); +} + +static int secp256k1_num_is_zero(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 0); +} + +static int secp256k1_num_is_neg(const secp256k1_num *a) { + return (a->limbs > 1 || a->data[0] != 0) && a->neg; +} + +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 1; + } + if (a->limbs < b->limbs) { + return -1; + } + return mpn_cmp(a->data, b->data, a->limbs); +} + +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 0; + } + if (a->limbs < b->limbs) { + return 0; + } + if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) { + return 0; + } + return mpn_cmp(a->data, b->data, a->limbs) == 0; +} + +static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) { + if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */ + r->neg = a->neg; + if (a->limbs >= b->limbs) { + secp256k1_num_add_abs(r, a, b); + } else { + secp256k1_num_add_abs(r, b, a); + } + } else { + if (secp256k1_num_cmp(a, b) > 0) { + r->neg = a->neg; + secp256k1_num_sub_abs(r, a, b); + } else { + r->neg = b->neg ^ bneg; + secp256k1_num_sub_abs(r, b, a); + } + } +} + +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 0); +} + +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 1); +} + +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t tmp[2*NUM_LIMBS+1]; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + + VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1); + if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) { + r->limbs = 1; + r->neg = 0; + r->data[0] = 0; + return; + } + if (a->limbs >= b->limbs) { + mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs); + } else { + mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs); + } + r->limbs = a->limbs + b->limbs; + if (r->limbs > 1 && tmp[r->limbs - 1]==0) { + r->limbs--; + } + VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS); + mpn_copyi(r->data, tmp, r->limbs); + r->neg = a->neg ^ b->neg; + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_shift(secp256k1_num *r, int bits) { + if (bits % GMP_NUMB_BITS) { + /* Shift within limbs. */ + mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS); + } + if (bits >= GMP_NUMB_BITS) { + int i; + /* Shift full limbs. */ + for (i = 0; i < r->limbs; i++) { + int index = i + (bits / GMP_NUMB_BITS); + if (index < r->limbs && index < 2*NUM_LIMBS) { + r->data[i] = r->data[index]; + } else { + r->data[i] = 0; + } + } + } + while (r->limbs>1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_negate(secp256k1_num *r) { + r->neg ^= 1; +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_impl.h new file mode 100644 index 0000000..0b0e3a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/num_impl.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_IMPL_H_ +#define _SECP256K1_NUM_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "num.h" + +#if defined(USE_NUM_GMP) +#include "num_gmp_impl.h" +#elif defined(USE_NUM_NONE) +/* Nothing. */ +#else +#error "Please select num implementation" +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar.h new file mode 100644 index 0000000..27e9d83 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar.h @@ -0,0 +1,106 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_ +#define _SECP256K1_SCALAR_ + +#include "num.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32.h" +#else +#error "Please select scalar implementation" +#endif + +/** Clear a scalar to prevent the leak of sensitive data. */ +static void secp256k1_scalar_clear(secp256k1_scalar *r); + +/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ +static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Access bits from a scalar. Not constant time. */ +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Set a scalar from a big endian byte array. */ +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); + +/** Set a scalar to an unsigned integer. */ +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); + +/** Convert a scalar to a byte array. */ +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); + +/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); + +/** Multiply two scalars (modulo the group order). */ +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Shift a scalar right by some amount strictly between 0 and 16, returning + * the low bits that were shifted off */ +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); + +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order). */ +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the complement of a scalar (modulo the group order). */ +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Check whether a scalar equals zero. */ +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); + +/** Check whether a scalar equals one. */ +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); + +/** Check whether a scalar is higher than the group order divided by 2. */ +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * Returns -1 if the number was negated, 1 otherwise */ +static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); + +#ifndef USE_NUM_NONE +/** Convert a scalar to a number. */ +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a); + +/** Get the order of the group as a number. */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r); +#endif + +/** Compare two scalars. */ +static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); + +#ifdef USE_ENDOMORPHISM +/** Find r1 and r2 such that r1+r2*2^128 = a. */ +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +#endif + +/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ +static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64.h new file mode 100644 index 0000000..cff4060 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint64_t d[4]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h new file mode 100644 index 0000000..56e7bd8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_4x64_impl.h @@ -0,0 +1,949 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) +#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) +#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) +#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) +#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) +#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) +#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); + return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 6) + 1 < 4); + return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ + no |= (a->d[2] < SECP256K1_N_2); + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1); + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { + uint128_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint64_t)r->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint128_t t = (uint128_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint128_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 64) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; + r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; + r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; + r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; + bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; + bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; + bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); + uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_H_3); + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint64_t mask = !flag - 1; + uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; + uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; + return 2 * (mask == 0) - 1; +} + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +#ifdef USE_ASM_X86_64 + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; + uint64_t c; + + __asm__ __volatile__( + /* Preload. */ + "movq 32(%%rsi), %%r11\n" + "movq 40(%%rsi), %%r12\n" + "movq 48(%%rsi), %%r13\n" + "movq 56(%%rsi), %%r14\n" + /* Initialize r8,r9,r10 */ + "movq 0(%%rsi), %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += n0 * c0 */ + "movq %8, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract m0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += l1 */ + "addq 8(%%rsi), %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += n1 * c0 */ + "movq %8, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n0 * c1 */ + "movq %9, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract m1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += l2 */ + "addq 16(%%rsi), %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n2 * c0 */ + "movq %8, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n1 * c1 */ + "movq %9, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n0 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract m2 */ + "movq %%r10, %q2\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += l3 */ + "addq 24(%%rsi), %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n3 * c0 */ + "movq %8, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n2 * c1 */ + "movq %9, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n1 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* extract m3 */ + "movq %%r8, %q3\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += n3 * c1 */ + "movq %9, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n2 */ + "addq %%r13, %%r9\n" + "adcq $0, %%r10\n" + "adcq $0, %%r8\n" + /* extract m4 */ + "movq %%r9, %q4\n" + /* (r10,r8) += n3 */ + "addq %%r14, %%r10\n" + "adcq $0, %%r8\n" + /* extract m5 */ + "movq %%r10, %q5\n" + /* extract m6 */ + "movq %%r8, %q6\n" + : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + + /* Reduce 385 bits into 258. */ + __asm__ __volatile__( + /* Preload */ + "movq %q9, %%r11\n" + "movq %q10, %%r12\n" + "movq %q11, %%r13\n" + /* Initialize (r8,r9,r10) */ + "movq %q5, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += m4 * c0 */ + "movq %12, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract p0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += m1 */ + "addq %q6, %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += m5 * c0 */ + "movq %12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += m4 * c1 */ + "movq %13, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract p1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += m2 */ + "addq %q7, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m6 * c0 */ + "movq %12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m5 * c1 */ + "movq %13, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m4 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract p2 */ + "movq %%r10, %q2\n" + /* (r8,r9) += m3 */ + "addq %q8, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += m6 * c1 */ + "movq %13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* (r8,r9) += m5 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + /* extract p3 */ + "movq %%r8, %q3\n" + /* (r9) += m6 */ + "addq %%r13, %%r9\n" + /* extract p4 */ + "movq %%r9, %q4\n" + : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) + : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + + /* Reduce 258 bits into 256. */ + __asm__ __volatile__( + /* Preload */ + "movq %q5, %%r10\n" + /* (rax,rdx) = p4 * c0 */ + "movq %7, %%rax\n" + "mulq %%r10\n" + /* (rax,rdx) += p0 */ + "addq %q1, %%rax\n" + "adcq $0, %%rdx\n" + /* extract r0 */ + "movq %%rax, 0(%q6)\n" + /* Move to (r8,r9) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p1 */ + "addq %q2, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += p4 * c1 */ + "movq %8, %%rax\n" + "mulq %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* Extract r1 */ + "movq %%r8, 8(%q6)\n" + "xorq %%r8, %%r8\n" + /* (r9,r8) += p4 */ + "addq %%r10, %%r9\n" + "adcq $0, %%r8\n" + /* (r9,r8) += p2 */ + "addq %q3, %%r9\n" + "adcq $0, %%r8\n" + /* Extract r2 */ + "movq %%r9, 16(%q6)\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p3 */ + "addq %q4, %%r8\n" + "adcq $0, %%r9\n" + /* Extract r3 */ + "movq %%r8, 24(%q6)\n" + /* Extract c */ + "movq %%r9, %q0\n" + : "=g"(c) + : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); +#else + uint128_t c; + uint64_t c0, c1, c2; + uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; + uint64_t m0, m1, m2, m3, m4, m5; + uint32_t m6; + uint64_t p0, p1, p2, p3; + uint32_t p4; + + /* Reduce 512 bits into 385. */ + /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + sumadd(n0); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + sumadd(n1); + extract(m3); + muladd(n3, SECP256K1_N_C_1); + sumadd(n2); + extract(m4); + sumadd_fast(n3); + extract_fast(m5); + VERIFY_CHECK(c0 <= 1); + m6 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m4, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m5, SECP256K1_N_C_0); + muladd(m4, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m6, SECP256K1_N_C_0); + muladd(m5, SECP256K1_N_C_1); + sumadd(m4); + extract(p2); + sumadd_fast(m3); + muladd_fast(m6, SECP256K1_N_C_1); + sumadd_fast(m5); + extract_fast(p3); + p4 = c0 + m6; + VERIFY_CHECK(p4 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ + c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; + r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; + r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p2 + (uint128_t)p4; + r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p3; + r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; +#endif + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { +#ifdef USE_ASM_X86_64 + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r15\n" + "movq 8(%%rdi), %%rbx\n" + "movq 16(%%rdi), %%rcx\n" + "movq 0(%%rdx), %%r11\n" + "movq 8(%%rdx), %%r12\n" + "movq 16(%%rdx), %%r13\n" + "movq 24(%%rdx), %%r14\n" + /* (rax,rdx) = a0 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a0 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a1 * b0 */ + "movq %%rbx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a0 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * b1 */ + "movq %%rbx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a2 * b0 */ + "movq %%rcx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += a0 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Preload a3 */ + "movq 24(%%rdi), %%r15\n" + /* (r10,r8,r9) += a1 * b2 */ + "movq %%rbx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a2 * b1 */ + "movq %%rcx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a3 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a1 * b3 */ + "movq %%rbx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * b2 */ + "movq %%rcx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a3 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a2 * b3 */ + "movq %%rcx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a3 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : "+d"(pb) + : "S"(l), "D"(a->d) + : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + extract(l[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + extract(l[5]); + muladd_fast(a->d[3], b->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint64_t l[8]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); + r->d[3] = (r->d[3] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = 0; + r1->d[3] = 0; + r2->d[0] = a->d[2]; + r2->d[1] = a->d[3]; + r2->d[2] = 0; + r2->d[3] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint64_t l[8]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 6; + shiftlow = shift & 0x3F; + shifthigh = 64 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32.h new file mode 100644 index 0000000..1319664 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint32_t d[8]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32_impl.h new file mode 100644 index 0000000..aae4f35 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_8x32_impl.h @@ -0,0 +1,721 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) +#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) +#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) +#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) +#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) +#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (~SECP256K1_N_2) +#define SECP256K1_N_C_3 (~SECP256K1_N_3) +#define SECP256K1_N_C_4 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) +#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) +#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) +#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) +#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); + return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 5) + 1 < 8); + return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ + no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_4); + yes |= (a->d[4] > SECP256K1_N_4) & ~no; + no |= (a->d[3] < SECP256K1_N_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_3) & ~no; + no |= (a->d[2] < SECP256K1_N_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { + uint64_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; + r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; + r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[5]; + r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[6]; + r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[7]; + r->d[7] = t & 0xFFFFFFFFUL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint64_t t = (uint64_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[4] + b->d[4]; + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[5] + b->d[5]; + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[6] + b->d[6]; + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[7] + b->d[7]; + r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint64_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); + r->d[7] = t & 0xFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 32) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; + r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; + r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; + r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; + r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; + r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; + r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; + r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; + bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; + bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; + bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; + bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; + bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; + bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; + bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); + uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; + r->d[7] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_H_7); + yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; + no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ + no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint32_t mask = !flag - 1; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { + uint64_t c; + uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; + uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; + uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; + + /* 96 bit accumulator. */ + uint32_t c0, c1, c2; + + /* Reduce 512 bits into 385. */ + /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + muladd(n0, SECP256K1_N_C_2); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + muladd(n1, SECP256K1_N_C_2); + muladd(n0, SECP256K1_N_C_3); + extract(m3); + sumadd(l[4]); + muladd(n4, SECP256K1_N_C_0); + muladd(n3, SECP256K1_N_C_1); + muladd(n2, SECP256K1_N_C_2); + muladd(n1, SECP256K1_N_C_3); + sumadd(n0); + extract(m4); + sumadd(l[5]); + muladd(n5, SECP256K1_N_C_0); + muladd(n4, SECP256K1_N_C_1); + muladd(n3, SECP256K1_N_C_2); + muladd(n2, SECP256K1_N_C_3); + sumadd(n1); + extract(m5); + sumadd(l[6]); + muladd(n6, SECP256K1_N_C_0); + muladd(n5, SECP256K1_N_C_1); + muladd(n4, SECP256K1_N_C_2); + muladd(n3, SECP256K1_N_C_3); + sumadd(n2); + extract(m6); + sumadd(l[7]); + muladd(n7, SECP256K1_N_C_0); + muladd(n6, SECP256K1_N_C_1); + muladd(n5, SECP256K1_N_C_2); + muladd(n4, SECP256K1_N_C_3); + sumadd(n3); + extract(m7); + muladd(n7, SECP256K1_N_C_1); + muladd(n6, SECP256K1_N_C_2); + muladd(n5, SECP256K1_N_C_3); + sumadd(n4); + extract(m8); + muladd(n7, SECP256K1_N_C_2); + muladd(n6, SECP256K1_N_C_3); + sumadd(n5); + extract(m9); + muladd(n7, SECP256K1_N_C_3); + sumadd(n6); + extract(m10); + sumadd_fast(n7); + extract_fast(m11); + VERIFY_CHECK(c0 <= 1); + m12 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m8, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m9, SECP256K1_N_C_0); + muladd(m8, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m10, SECP256K1_N_C_0); + muladd(m9, SECP256K1_N_C_1); + muladd(m8, SECP256K1_N_C_2); + extract(p2); + sumadd(m3); + muladd(m11, SECP256K1_N_C_0); + muladd(m10, SECP256K1_N_C_1); + muladd(m9, SECP256K1_N_C_2); + muladd(m8, SECP256K1_N_C_3); + extract(p3); + sumadd(m4); + muladd(m12, SECP256K1_N_C_0); + muladd(m11, SECP256K1_N_C_1); + muladd(m10, SECP256K1_N_C_2); + muladd(m9, SECP256K1_N_C_3); + sumadd(m8); + extract(p4); + sumadd(m5); + muladd(m12, SECP256K1_N_C_1); + muladd(m11, SECP256K1_N_C_2); + muladd(m10, SECP256K1_N_C_3); + sumadd(m9); + extract(p5); + sumadd(m6); + muladd(m12, SECP256K1_N_C_2); + muladd(m11, SECP256K1_N_C_3); + sumadd(m10); + extract(p6); + sumadd_fast(m7); + muladd_fast(m12, SECP256K1_N_C_3); + sumadd_fast(m11); + extract_fast(p7); + p8 = c0 + m12; + VERIFY_CHECK(p8 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ + c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; + r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; + c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; + r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; + c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; + r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; + c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; + r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; + c += p4 + (uint64_t)p8; + r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; + c += p5; + r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; + c += p6; + r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; + c += p7; + r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7] * b[0..7]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[0], b->d[4]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + muladd(a->d[4], b->d[0]); + extract(l[4]); + muladd(a->d[0], b->d[5]); + muladd(a->d[1], b->d[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + muladd(a->d[4], b->d[1]); + muladd(a->d[5], b->d[0]); + extract(l[5]); + muladd(a->d[0], b->d[6]); + muladd(a->d[1], b->d[5]); + muladd(a->d[2], b->d[4]); + muladd(a->d[3], b->d[3]); + muladd(a->d[4], b->d[2]); + muladd(a->d[5], b->d[1]); + muladd(a->d[6], b->d[0]); + extract(l[6]); + muladd(a->d[0], b->d[7]); + muladd(a->d[1], b->d[6]); + muladd(a->d[2], b->d[5]); + muladd(a->d[3], b->d[4]); + muladd(a->d[4], b->d[3]); + muladd(a->d[5], b->d[2]); + muladd(a->d[6], b->d[1]); + muladd(a->d[7], b->d[0]); + extract(l[7]); + muladd(a->d[1], b->d[7]); + muladd(a->d[2], b->d[6]); + muladd(a->d[3], b->d[5]); + muladd(a->d[4], b->d[4]); + muladd(a->d[5], b->d[3]); + muladd(a->d[6], b->d[2]); + muladd(a->d[7], b->d[1]); + extract(l[8]); + muladd(a->d[2], b->d[7]); + muladd(a->d[3], b->d[6]); + muladd(a->d[4], b->d[5]); + muladd(a->d[5], b->d[4]); + muladd(a->d[6], b->d[3]); + muladd(a->d[7], b->d[2]); + extract(l[9]); + muladd(a->d[3], b->d[7]); + muladd(a->d[4], b->d[6]); + muladd(a->d[5], b->d[5]); + muladd(a->d[6], b->d[4]); + muladd(a->d[7], b->d[3]); + extract(l[10]); + muladd(a->d[4], b->d[7]); + muladd(a->d[5], b->d[6]); + muladd(a->d[6], b->d[5]); + muladd(a->d[7], b->d[4]); + extract(l[11]); + muladd(a->d[5], b->d[7]); + muladd(a->d[6], b->d[6]); + muladd(a->d[7], b->d[5]); + extract(l[12]); + muladd(a->d[6], b->d[7]); + muladd(a->d[7], b->d[6]); + extract(l[13]); + muladd_fast(a->d[7], b->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint32_t l[16]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); + r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); + r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); + r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); + r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); + r->d[7] = (r->d[7] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = a->d[2]; + r1->d[3] = a->d[3]; + r1->d[4] = 0; + r1->d[5] = 0; + r1->d[6] = 0; + r1->d[7] = 0; + r2->d[0] = a->d[4]; + r2->d[1] = a->d[5]; + r2->d[2] = a->d[6]; + r2->d[3] = a->d[7]; + r2->d[4] = 0; + r2->d[5] = 0; + r2->d[6] = 0; + r2->d[7] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint32_t l[16]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 5; + shiftlow = shift & 0x1F; + shifthigh = 32 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_impl.h new file mode 100644 index 0000000..f5b2376 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_impl.h @@ -0,0 +1,370 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_IMPL_H_ +#define _SECP256K1_SCALAR_IMPL_H_ + +#include "group.h" +#include "scalar.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64_impl.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32_impl.h" +#else +#error "Please select scalar implementation" +#endif + +#ifndef USE_NUM_NONE +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) { + unsigned char c[32]; + secp256k1_scalar_get_b32(c, a); + secp256k1_num_set_bin(r, c, 32); +} + +/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r) { +#if defined(EXHAUSTIVE_TEST_ORDER) + static const unsigned char order[32] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER + }; +#else + static const unsigned char order[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; +#endif + secp256k1_num_set_bin(r, order, 32); +} +#endif + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(EXHAUSTIVE_TEST_ORDER) + int i; + *r = 0; + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) + if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) + *r = i; + /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus + * have a composite group order; fix it in exhaustive_tests.c). */ + VERIFY_CHECK(*r != 0); +} +#else + secp256k1_scalar *t; + int i; + /* First compute x ^ (2^N - 1) for some values of N. */ + secp256k1_scalar x2, x3, x4, x6, x7, x8, x15, x30, x60, x120, x127; + + secp256k1_scalar_sqr(&x2, x); + secp256k1_scalar_mul(&x2, &x2, x); + + secp256k1_scalar_sqr(&x3, &x2); + secp256k1_scalar_mul(&x3, &x3, x); + + secp256k1_scalar_sqr(&x4, &x3); + secp256k1_scalar_mul(&x4, &x4, x); + + secp256k1_scalar_sqr(&x6, &x4); + secp256k1_scalar_sqr(&x6, &x6); + secp256k1_scalar_mul(&x6, &x6, &x2); + + secp256k1_scalar_sqr(&x7, &x6); + secp256k1_scalar_mul(&x7, &x7, x); + + secp256k1_scalar_sqr(&x8, &x7); + secp256k1_scalar_mul(&x8, &x8, x); + + secp256k1_scalar_sqr(&x15, &x8); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x15, &x15); + } + secp256k1_scalar_mul(&x15, &x15, &x7); + + secp256k1_scalar_sqr(&x30, &x15); + for (i = 0; i < 14; i++) { + secp256k1_scalar_sqr(&x30, &x30); + } + secp256k1_scalar_mul(&x30, &x30, &x15); + + secp256k1_scalar_sqr(&x60, &x30); + for (i = 0; i < 29; i++) { + secp256k1_scalar_sqr(&x60, &x60); + } + secp256k1_scalar_mul(&x60, &x60, &x30); + + secp256k1_scalar_sqr(&x120, &x60); + for (i = 0; i < 59; i++) { + secp256k1_scalar_sqr(&x120, &x120); + } + secp256k1_scalar_mul(&x120, &x120, &x60); + + secp256k1_scalar_sqr(&x127, &x120); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x127, &x127); + } + secp256k1_scalar_mul(&x127, &x127, &x7); + + /* Then accumulate the final result (t starts at x127). */ + t = &x127; + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 5; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 10; i++) { /* 0000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 9; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 6; i++) { /* 00000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(r, t, &x6); /* 111111 */ +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(USE_SCALAR_INV_BUILTIN) + secp256k1_scalar_inverse(r, x); +#elif defined(USE_SCALAR_INV_NUM) + unsigned char b[32]; + secp256k1_num n, m; + secp256k1_scalar t = *x; + secp256k1_scalar_get_b32(b, &t); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_scalar_order_get_num(&m); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + secp256k1_scalar_set_b32(r, b, NULL); + /* Verify that the inverse was computed correctly, without GMP code. */ + secp256k1_scalar_mul(&t, &t, r); + CHECK(secp256k1_scalar_is_one(&t)); +#else +#error "Please select scalar inverse implementation" +#endif +} + +#ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the + * full case we don't bother making k1 and k2 be small, we just want them to be + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER; + *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; +} +#else +/** + * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where + * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, + * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72} + * + * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm + * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 + * and k2 have a small size. + * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are: + * + * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} + * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} + * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * + * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives + * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and + * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2. + * + * g1, g2 are precomputed constants used to replace division with a rounded multiplication + * when decomposing the scalar for an endomorphism-based point multiplication. + * + * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve + * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. + * + * The derivation is described in the paper "Efficient Software Implementation of Public-Key + * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), + * Section 4.3 (here we use a somewhat higher-precision estimate): + * d = a1*b2 - b1*a2 + * g1 = round((2^272)*b2/d) + * g2 = round((2^272)*b1/d) + * + * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found + * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda'). + * + * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + */ + +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + secp256k1_scalar c1, c2; + static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST( + 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL, + 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL + ); + static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL + ); + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL + ); + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, + 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + ); + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, + 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + ); + VERIFY_CHECK(r1 != a); + VERIFY_CHECK(r2 != a); + /* these _var calls are constant time since the shift amount is constant */ + secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272); + secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272); + secp256k1_scalar_mul(&c1, &c1, &minus_b1); + secp256k1_scalar_mul(&c2, &c2, &minus_b2); + secp256k1_scalar_add(r2, &c1, &c2); + secp256k1_scalar_mul(r1, r2, &minus_lambda); + secp256k1_scalar_add(r1, r1, a); +} +#endif +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low.h new file mode 100644 index 0000000..5574c44 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h new file mode 100644 index 0000000..4f94441 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/scalar_low_impl.h @@ -0,0 +1,114 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(*a & 1); +} + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + if (offset < 32) + return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); + else + return 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + return secp256k1_scalar_get_bits(a, offset, count); +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + memset(bin, 0, 32); + bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, r); + return flag ? -1 : 1; +} + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = *r & ((1 << n) - 1); + *r >>= n; + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/secp256k1.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/secp256k1.c new file mode 100755 index 0000000..fb8b882 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/secp256k1.c @@ -0,0 +1,561 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" + +#include "util.h" +#include "num_impl.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_gen_impl.h" +#include "ecdsa_impl.h" +#include "eckey_impl.h" +#include "hash_impl.h" + +#define ARG_CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return 0; \ + } \ +} while(0) + +static void default_illegal_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); + abort(); +} + +static const secp256k1_callback default_illegal_callback = { + default_illegal_callback_fn, + NULL +}; + +static void default_error_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + + +struct secp256k1_context_struct { + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; +}; + +secp256k1_context* secp256k1_context_create(unsigned int flags) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = default_illegal_callback; + ret->error_callback = default_error_callback; + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&ret->illegal_callback, + "Invalid flags"); + free(ret); + return NULL; + } + + secp256k1_ecmult_context_init(&ret->ecmult_ctx); + secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + } + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + } + + return ret; +} + +secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = ctx->illegal_callback; + ret->error_callback = ctx->error_callback; + secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); + secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); + return ret; +} + +void secp256k1_context_destroy(secp256k1_context* ctx) { + if (ctx != NULL) { + secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); + secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + + free(ctx); + } +} + +void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_illegal_callback_fn; + } + ctx->illegal_callback.fn = fun; + ctx->illegal_callback.data = data; +} + +void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_error_callback_fn; + } + ctx->error_callback.fn = fun; + ctx->error_callback.data = data; +} + +static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { + if (sizeof(secp256k1_ge_storage) == 64) { + /* When the secp256k1_ge_storage type is exactly 64 byte, use its + * representation inside secp256k1_pubkey, as conversion is very fast. + * Note that secp256k1_pubkey_save must use the same representation. */ + secp256k1_ge_storage s; + memcpy(&s, &pubkey->data[0], 64); + secp256k1_ge_from_storage(ge, &s); + } else { + /* Otherwise, fall back to 32-byte big endian for X and Y. */ + secp256k1_fe x, y; + secp256k1_fe_set_b32(&x, pubkey->data); + secp256k1_fe_set_b32(&y, pubkey->data + 32); + secp256k1_ge_set_xy(ge, &x, &y); + } + ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); + return 1; +} + +static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { + if (sizeof(secp256k1_ge_storage) == 64) { + secp256k1_ge_storage s; + secp256k1_ge_to_storage(&s, ge); + memcpy(&pubkey->data[0], &s, 64); + } else { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(pubkey->data, &ge->x); + secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); + } +} + +int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input != NULL); + if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { + return 0; + } + secp256k1_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} + +int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { + secp256k1_ge Q; + size_t len; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + len = *outputlen; + *outputlen = 0; + ARG_CHECK(output != NULL); + memset(output, 0, len); + ARG_CHECK(pubkey != NULL); + ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); + if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + if (ret) { + *outputlen = len; + } + } + return ret; +} + +static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } +} + +static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } +} + +int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; + } else { + memset(sig, 0, sizeof(*sig)); + return 0; + } +} + +int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); +} + +int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { + secp256k1_scalar r, s; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); + ret = secp256k1_scalar_is_high(&s); + if (sigout != NULL) { + if (ret) { + secp256k1_scalar_negate(&s, &s); + } + secp256k1_ecdsa_signature_save(sigout, &r, &s); + } + + return ret; +} + +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_scalar_set_b32(&m, msg32, NULL); + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return (!secp256k1_scalar_is_high(&s) && + secp256k1_pubkey_load(ctx, &q, pubkey) && + secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); +} + +static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + unsigned char keydata[112]; + int keylen = 64; + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned int i; + /* We feed a byte array to the PRNG as input, consisting of: + * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. + * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. + * - optionally 16 extra bytes with the algorithm name. + * Because the arguments have distinct fixed lengths it is not possible for + * different argument mixtures to emulate each other and result in the same + * nonces. + */ + memcpy(keydata, key32, 32); + memcpy(keydata + 32, msg32, 32); + if (data != NULL) { + memcpy(keydata + 64, data, 32); + keylen = 96; + } + if (algo16 != NULL) { + memcpy(keydata + keylen, algo16, 16); + keylen += 16; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + memset(keydata, 0, sizeof(keydata)); + for (i = 0; i <= counter; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + return 1; +} + +const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; +const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; + +int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!overflow && !secp256k1_scalar_is_zero(&non)) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_signature_save(signature, &r, &s); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { + secp256k1_scalar sec; + int ret; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = !overflow && !secp256k1_scalar_is_zero(&sec); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); + if (ret) { + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(pubkey, &p); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar term; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + + ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&term); + return ret; +} + +int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar term; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar factor; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&factor); + return ret; +} + +int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar factor; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + return 1; +} + +int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { + size_t i; + secp256k1_gej Qj; + secp256k1_ge Q; + + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(n >= 1); + ARG_CHECK(pubnonces != NULL); + + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); + return 1; +} + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/main_impl.h" +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand.h new file mode 100644 index 0000000..f8efa93 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand.h @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_H_ +#define _SECP256K1_TESTRAND_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +/* A non-cryptographic RNG used only for test infrastructure. */ + +/** Seed the pseudorandom number generator for testing. */ +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16); + +/** Generate a pseudorandom number in the range [0..2**32-1]. */ +static uint32_t secp256k1_rand32(void); + +/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or + * more. */ +static uint32_t secp256k1_rand_bits(int bits); + +/** Generate a pseudorandom number in the range [0..range-1]. */ +static uint32_t secp256k1_rand_int(uint32_t range); + +/** Generate a pseudorandom 32-byte array. */ +static void secp256k1_rand256(unsigned char *b32); + +/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ +static void secp256k1_rand256_test(unsigned char *b32); + +/** Generate pseudorandom bytes with long sequences of zero and one bits. */ +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand_impl.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand_impl.h new file mode 100644 index 0000000..15c7b9f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/testrand_impl.h @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_IMPL_H_ +#define _SECP256K1_TESTRAND_IMPL_H_ + +#include +#include + +#include "testrand.h" +#include "hash.h" + +static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng; +static uint32_t secp256k1_test_rng_precomputed[8]; +static int secp256k1_test_rng_precomputed_used = 8; +static uint64_t secp256k1_test_rng_integer; +static int secp256k1_test_rng_integer_bits_left = 0; + +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { + secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); +} + +SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { + if (secp256k1_test_rng_precomputed_used == 8) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed)); + secp256k1_test_rng_precomputed_used = 0; + } + return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; +} + +static uint32_t secp256k1_rand_bits(int bits) { + uint32_t ret; + if (secp256k1_test_rng_integer_bits_left < bits) { + secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left); + secp256k1_test_rng_integer_bits_left += 32; + } + ret = secp256k1_test_rng_integer; + secp256k1_test_rng_integer >>= bits; + secp256k1_test_rng_integer_bits_left -= bits; + ret &= ((~((uint32_t)0)) >> (32 - bits)); + return ret; +} + +static uint32_t secp256k1_rand_int(uint32_t range) { + /* We want a uniform integer between 0 and range-1, inclusive. + * B is the smallest number such that range <= 2**B. + * two mechanisms implemented here: + * - generate B bits numbers until one below range is found, and return it + * - find the largest multiple M of range that is <= 2**(B+A), generate B+A + * bits numbers until one below M is found, and return it modulo range + * The second mechanism consumes A more bits of entropy in every iteration, + * but may need fewer iterations due to M being closer to 2**(B+A) then + * range is to 2**B. The array below (indexed by B) contains a 0 when the + * first mechanism is to be used, and the number A otherwise. + */ + static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; + uint32_t trange, mult; + int bits = 0; + if (range <= 1) { + return 0; + } + trange = range - 1; + while (trange > 0) { + trange >>= 1; + bits++; + } + if (addbits[bits]) { + bits = bits + addbits[bits]; + mult = ((~((uint32_t)0)) >> (32 - bits)) / range; + trange = range * mult; + } else { + trange = range; + mult = 1; + } + while(1) { + uint32_t x = secp256k1_rand_bits(bits); + if (x < trange) { + return (mult == 1) ? x : (x % range); + } + } +} + +static void secp256k1_rand256(unsigned char *b32) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); +} + +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) { + size_t bits = 0; + memset(bytes, 0, len); + while (bits < len * 8) { + int now; + uint32_t val; + now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31; + val = secp256k1_rand_bits(1); + while (now > 0 && bits < len * 8) { + bytes[bits / 8] |= val << (bits % 8); + now--; + bits++; + } + } +} + +static void secp256k1_rand256_test(unsigned char *b32) { + secp256k1_rand_bytes_test(b32, 32); +} + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests.c new file mode 100644 index 0000000..9ae7d30 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests.c @@ -0,0 +1,4525 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#include "secp256k1.c" +#include "include/secp256k1.h" +#include "testrand_impl.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ecdsa.h" +#include "openssl/obj_mac.h" +#endif + +#include "contrib/lax_der_parsing.c" +#include "contrib/lax_der_privatekey_parsing.c" + +#if !defined(VG_CHECK) +# if defined(VALGRIND) +# include +# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) +# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) +# else +# define VG_UNDEF(x,y) +# define VG_CHECK(x,y) +# endif +#endif + +static int count = 64; +static secp256k1_context *ctx = NULL; + +static void counting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts. */ + int32_t *p; + (void)str; + p = data; + (*p)++; +} + +static void uncounting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts (backwards). */ + int32_t *p; + (void)str; + p = data; + (*p)--; +} + +void random_field_element_test(secp256k1_fe *fe) { + do { + unsigned char b32[32]; + secp256k1_rand256_test(b32); + if (secp256k1_fe_set_b32(fe, b32)) { + break; + } + } while(1); +} + +void random_field_element_magnitude(secp256k1_fe *fe) { + secp256k1_fe zero; + int n = secp256k1_rand_int(9); + secp256k1_fe_normalize(fe); + if (n == 0) { + return; + } + secp256k1_fe_clear(&zero); + secp256k1_fe_negate(&zero, &zero, 0); + secp256k1_fe_mul_int(&zero, n - 1); + secp256k1_fe_add(fe, &zero); + VERIFY_CHECK(fe->magnitude == n); +} + +void random_group_element_test(secp256k1_ge *ge) { + secp256k1_fe fe; + do { + random_field_element_test(&fe); + if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) { + secp256k1_fe_normalize(&ge->y); + break; + } + } while(1); +} + +void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { + secp256k1_fe z2, z3; + do { + random_field_element_test(&gej->z); + if (!secp256k1_fe_is_zero(&gej->z)) { + break; + } + } while(1); + secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + +void random_scalar_order_test(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256_test(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void run_context_tests(void) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + unsigned char ctmp[32]; + int32_t ecount; + int32_t ecount2; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar msg, key, nonce; + secp256k1_scalar sigr, sigs; + + ecount = 0; + ecount2 = 10; + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + + /*** clone and destroy all of them to make sure cloning was complete ***/ + { + secp256k1_context *ctx_tmp; + + ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); + } + + /* Verify that the error callback makes it across the clone. */ + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + /* And that it resets back to default. */ + secp256k1_context_set_error_callback(sign, NULL, NULL); + CHECK(vrfy->error_callback.fn == sign->error_callback.fn); + + /*** attempt to use them ***/ + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&both->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + + /* Verify context-type checking illegal-argument errors. */ + memset(ctmp, 1, 32); + CHECK(secp256k1_ec_pubkey_create(vrfy, &pubkey, ctmp) == 0); + CHECK(ecount == 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(sign, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ecdsa_sign(vrfy, &sig, ctmp, ctmp, NULL, NULL) == 0); + CHECK(ecount == 2); + VG_UNDEF(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_sign(sign, &sig, ctmp, ctmp, NULL, NULL) == 1); + VG_CHECK(&sig, sizeof(sig)); + CHECK(ecount2 == 10); + CHECK(secp256k1_ecdsa_verify(sign, &sig, ctmp, &pubkey) == 0); + CHECK(ecount2 == 11); + CHECK(secp256k1_ecdsa_verify(vrfy, &sig, ctmp, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_add(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 12); + CHECK(secp256k1_ec_pubkey_tweak_add(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_mul(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 13); + CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_context_randomize(sign, NULL) == 1); + CHECK(ecount2 == 13); + secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); + secp256k1_context_set_illegal_callback(sign, NULL, NULL); + + /* This shouldn't leak memory, due to already-set tests. */ + secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL); + secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL); + + /* obtain a working nonce */ + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try signing */ + CHECK(secp256k1_ecdsa_sig_sign(&sign->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + CHECK(secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try verifying */ + CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + /* Defined as no-op. */ + secp256k1_context_destroy(NULL); +} + +/***** HASH TESTS *****/ + +void run_sha256_tests(void) { + static const char *inputs[8] = { + "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "For this sample, this 63-byte string will be used as input data", + "This is exactly 64 bytes long, not counting the terminating byte" + }; + static const unsigned char outputs[8][32] = { + {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, + {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, + {0xf7, 0x84, 0x6f, 0x55, 0xcf, 0x23, 0xe1, 0x4e, 0xeb, 0xea, 0xb5, 0xb4, 0xe1, 0x55, 0x0c, 0xad, 0x5b, 0x50, 0x9e, 0x33, 0x48, 0xfb, 0xc4, 0xef, 0xa3, 0xa1, 0x41, 0x3d, 0x39, 0x3c, 0xb6, 0x50}, + {0xf3, 0x0c, 0xeb, 0x2b, 0xb2, 0x82, 0x9e, 0x79, 0xe4, 0xca, 0x97, 0x53, 0xd3, 0x5a, 0x8e, 0xcc, 0x00, 0x26, 0x2d, 0x16, 0x4c, 0xc0, 0x77, 0x08, 0x02, 0x95, 0x38, 0x1c, 0xbd, 0x64, 0x3f, 0x0d}, + {0x68, 0x19, 0xd9, 0x15, 0xc7, 0x3f, 0x4d, 0x1e, 0x77, 0xe4, 0xe1, 0xb5, 0x2d, 0x1f, 0xa0, 0xf9, 0xcf, 0x9b, 0xea, 0xea, 0xd3, 0x93, 0x9f, 0x15, 0x87, 0x4b, 0xd9, 0x88, 0xe2, 0xa2, 0x36, 0x30}, + {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}, + {0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42}, + {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8} + }; + int i; + for (i = 0; i < 8; i++) { + unsigned char out[32]; + secp256k1_sha256_t hasher; + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_hmac_sha256_tests(void) { + static const char *keys[6] = { + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + "\x4a\x65\x66\x65", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + }; + static const char *inputs[6] = { + "\x48\x69\x20\x54\x68\x65\x72\x65", + "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e" + }; + static const unsigned char outputs[6][32] = { + {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7}, + {0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43}, + {0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe}, + {0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b}, + {0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54}, + {0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2} + }; + int i; + for (i = 0; i < 6; i++) { + secp256k1_hmac_sha256_t hasher; + unsigned char out[32]; + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_rfc6979_hmac_sha256_tests(void) { + static const unsigned char key1[65] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x4b, 0xf5, 0x12, 0x2f, 0x34, 0x45, 0x54, 0xc5, 0x3b, 0xde, 0x2e, 0xbb, 0x8c, 0xd2, 0xb7, 0xe3, 0xd1, 0x60, 0x0a, 0xd6, 0x31, 0xc3, 0x85, 0xa5, 0xd7, 0xcc, 0xe2, 0x3c, 0x77, 0x85, 0x45, 0x9a, 0}; + static const unsigned char out1[3][32] = { + {0x4f, 0xe2, 0x95, 0x25, 0xb2, 0x08, 0x68, 0x09, 0x15, 0x9a, 0xcd, 0xf0, 0x50, 0x6e, 0xfb, 0x86, 0xb0, 0xec, 0x93, 0x2c, 0x7b, 0xa4, 0x42, 0x56, 0xab, 0x32, 0x1e, 0x42, 0x1e, 0x67, 0xe9, 0xfb}, + {0x2b, 0xf0, 0xff, 0xf1, 0xd3, 0xc3, 0x78, 0xa2, 0x2d, 0xc5, 0xde, 0x1d, 0x85, 0x65, 0x22, 0x32, 0x5c, 0x65, 0xb5, 0x04, 0x49, 0x1a, 0x0c, 0xbd, 0x01, 0xcb, 0x8f, 0x3a, 0xa6, 0x7f, 0xfd, 0x4a}, + {0xf5, 0x28, 0xb4, 0x10, 0xcb, 0x54, 0x1f, 0x77, 0x00, 0x0d, 0x7a, 0xfb, 0x6c, 0x5b, 0x53, 0xc5, 0xc4, 0x71, 0xea, 0xb4, 0x3e, 0x46, 0x6d, 0x9a, 0xc5, 0x19, 0x0c, 0x39, 0xc8, 0x2f, 0xd8, 0x2e} + }; + + static const unsigned char key2[64] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + static const unsigned char out2[3][32] = { + {0x9c, 0x23, 0x6c, 0x16, 0x5b, 0x82, 0xae, 0x0c, 0xd5, 0x90, 0x65, 0x9e, 0x10, 0x0b, 0x6b, 0xab, 0x30, 0x36, 0xe7, 0xba, 0x8b, 0x06, 0x74, 0x9b, 0xaf, 0x69, 0x81, 0xe1, 0x6f, 0x1a, 0x2b, 0x95}, + {0xdf, 0x47, 0x10, 0x61, 0x62, 0x5b, 0xc0, 0xea, 0x14, 0xb6, 0x82, 0xfe, 0xee, 0x2c, 0x9c, 0x02, 0xf2, 0x35, 0xda, 0x04, 0x20, 0x4c, 0x1d, 0x62, 0xa1, 0x53, 0x6c, 0x6e, 0x17, 0xae, 0xd7, 0xa9}, + {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} + }; + + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned char out[32]; + int i; + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 65); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) != 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out2[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); +} + +/***** RANDOM TESTS *****/ + +void test_rand_bits(int rand32, int bits) { + /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to + * get a false negative chance below once in a billion */ + static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; + /* We try multiplying the results with various odd numbers, which shouldn't + * influence the uniform distribution modulo a power of 2. */ + static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011}; + /* We only select up to 6 bits from the output to analyse */ + unsigned int usebits = bits > 6 ? 6 : bits; + unsigned int maxshift = bits - usebits; + /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit + number, track all observed outcomes, one per bit in a uint64_t. */ + uint64_t x[6][27] = {{0}}; + unsigned int i, shift, m; + /* Multiply the output of all rand calls with the odd number m, which + should not change the uniformity of its distribution. */ + for (i = 0; i < rounds[usebits]; i++) { + uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits)); + CHECK((((uint64_t)r) >> bits) == 0); + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + uint32_t rm = r * mults[m]; + for (shift = 0; shift <= maxshift; shift++) { + x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1))); + } + } + } + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + for (shift = 0; shift <= maxshift; shift++) { + /* Test that the lower usebits bits of x[shift] are 1 */ + CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0); + } + } +} + +/* Subrange must be a whole divisor of range, and at most 64 */ +void test_rand_int(uint32_t range, uint32_t subrange) { + /* (1-1/subrange)^rounds < 1/10^9 */ + int rounds = (subrange * 2073) / 100; + int i; + uint64_t x = 0; + CHECK((range % subrange) == 0); + for (i = 0; i < rounds; i++) { + uint32_t r = secp256k1_rand_int(range); + CHECK(r < range); + r = r % subrange; + x |= (((uint64_t)1) << r); + } + /* Test that the lower subrange bits of x are 1. */ + CHECK(((~x) << (64 - subrange)) == 0); +} + +void run_rand_bits(void) { + size_t b; + test_rand_bits(1, 32); + for (b = 1; b <= 32; b++) { + test_rand_bits(0, b); + } +} + +void run_rand_int(void) { + static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; + static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; + unsigned int m, s; + for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) { + for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) { + test_rand_int(ms[m] * ss[s], ss[s]); + } + } +} + +/***** NUM TESTS *****/ + +#ifndef USE_NUM_NONE +void random_num_negate(secp256k1_num *num) { + if (secp256k1_rand_bits(1)) { + secp256k1_num_negate(num); + } +} + +void random_num_order_test(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order_test(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void random_num_order(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void test_num_negate(void) { + secp256k1_num n1; + secp256k1_num n2; + random_num_order_test(&n1); /* n1 = R */ + random_num_negate(&n1); + secp256k1_num_copy(&n2, &n1); /* n2 = R */ + secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(!secp256k1_num_is_zero(&n1)); + secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2)); + secp256k1_num_negate(&n1); /* n1 = R */ + CHECK(secp256k1_num_eq(&n1, &n2)); +} + +void test_num_add_sub(void) { + int i; + secp256k1_scalar s; + secp256k1_num n1; + secp256k1_num n2; + secp256k1_num n1p2, n2p1, n1m2, n2m1; + random_num_order_test(&n1); /* n1 = R1 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n1); + } + random_num_order_test(&n2); /* n2 = R2 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n2); + } + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */ + secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */ + secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */ + secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */ + CHECK(secp256k1_num_eq(&n1p2, &n2p1)); + CHECK(!secp256k1_num_eq(&n1p2, &n1m2)); + secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1m2)); + CHECK(!secp256k1_num_eq(&n2m1, &n1)); + secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1)); + CHECK(!secp256k1_num_eq(&n2p1, &n1)); + secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */ + CHECK(secp256k1_num_eq(&n2p1, &n1)); + + /* check is_one */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&n1, &s); + CHECK(secp256k1_num_is_one(&n1)); + /* check that 2^n + 1 is never 1 */ + secp256k1_scalar_get_num(&n2, &s); + for (i = 0; i < 250; ++i) { + secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */ + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */ + CHECK(!secp256k1_num_is_one(&n1p2)); + } +} + +void test_num_mod(void) { + int i; + secp256k1_scalar s; + secp256k1_num order, n; + + /* check that 0 mod anything is 0 */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_set_int(&s, 0); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that anything mod 1 is 0 */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that increasing the number past 2^256 does not break this */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&n, &s); + /* multiply by 2^8, which'll test this case with high probability */ + for (i = 0; i < 8; ++i) { + secp256k1_num_add(&n, &n, &n); + } + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); +} + +void test_num_jacobi(void) { + secp256k1_scalar sqr; + secp256k1_scalar small; + secp256k1_scalar five; /* five is not a quadratic residue */ + secp256k1_num order, n; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* check some small values with 5 as the order */ + secp256k1_scalar_set_int(&five, 5); + secp256k1_scalar_get_num(&order, &five); + for (i = 0; i < 10; ++i) { + secp256k1_scalar_set_int(&small, i); + secp256k1_scalar_get_num(&n, &small); + CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]); + } + + /** test large values with 5 as group order */ + secp256k1_scalar_get_num(&order, &five); + /* we first need a scalar which is not a multiple of 5 */ + do { + secp256k1_num fiven; + random_scalar_order_test(&sqr); + secp256k1_scalar_get_num(&fiven, &five); + secp256k1_scalar_get_num(&n, &sqr); + secp256k1_num_mod(&n, &fiven); + } while (secp256k1_num_is_zero(&n)); + /* next force it to be a residue. 2 is a nonresidue mod 5 so we can + * just multiply by two, i.e. add the number to itself */ + if (secp256k1_num_jacobi(&n, &order) == -1) { + secp256k1_num_add(&n, &n, &n); + } + + /* test residue */ + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_num_add(&n, &n, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + + /** test with secp group order as order */ + secp256k1_scalar_order_get_num(&order); + random_scalar_order_test(&sqr); + secp256k1_scalar_sqr(&sqr, &sqr); + /* test residue */ + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_scalar_mul(&sqr, &sqr, &five); + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + /* test multiple of the order*/ + CHECK(secp256k1_num_jacobi(&order, &order) == 0); + + /* check one less than the order */ + secp256k1_scalar_set_int(&small, 1); + secp256k1_scalar_get_num(&n, &small); + secp256k1_num_sub(&n, &order, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */ +} + +void run_num_smalltests(void) { + int i; + for (i = 0; i < 100*count; i++) { + test_num_negate(); + test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); + } +} +#endif + +/***** SCALAR TESTS *****/ + +void scalar_test(void) { + secp256k1_scalar s; + secp256k1_scalar s1; + secp256k1_scalar s2; +#ifndef USE_NUM_NONE + secp256k1_num snum, s1num, s2num; + secp256k1_num order, half_order; +#endif + unsigned char c[32]; + + /* Set 's' to a random scalar, with value 'snum'. */ + random_scalar_order_test(&s); + + /* Set 's1' to a random scalar, with value 's1num'. */ + random_scalar_order_test(&s1); + + /* Set 's2' to a random scalar, with value 'snum2', and byte array representation 'c'. */ + random_scalar_order_test(&s2); + secp256k1_scalar_get_b32(c, &s2); + +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&snum, &s); + secp256k1_scalar_get_num(&s1num, &s1); + secp256k1_scalar_get_num(&s2num, &s2); + + secp256k1_scalar_order_get_num(&order); + half_order = order; + secp256k1_num_shift(&half_order, 1); +#endif + + { + int i; + /* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + secp256k1_scalar_set_int(&n, 0); + for (i = 0; i < 256; i += 4) { + secp256k1_scalar t; + int j; + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits(&s, 256 - 4 - i, 4)); + for (j = 0; j < 4; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + + { + /* Test that fetching groups of randomly-sized bits from a scalar and recursing n(i)=b*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + int i = 0; + secp256k1_scalar_set_int(&n, 0); + while (i < 256) { + secp256k1_scalar t; + int j; + int now = secp256k1_rand_int(15) + 1; + if (now + i > 256) { + now = 256 - i; + } + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits_var(&s, 256 - now - i, now)); + for (j = 0; j < now; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + i += now; + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + +#ifndef USE_NUM_NONE + { + /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */ + secp256k1_num rnum; + secp256k1_num r2num; + secp256k1_scalar r; + secp256k1_num_add(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_add(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + } + + { + /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */ + secp256k1_scalar r; + secp256k1_num r2num; + secp256k1_num rnum; + secp256k1_num_mul(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_mul(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + /* The result can only be zero if at least one of the factors was zero. */ + CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2))); + /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */ + CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2))); + CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s))); + } + + { + secp256k1_scalar neg; + secp256k1_num negnum; + secp256k1_num negnum2; + /* Check that comparison with zero matches comparison with zero on the number. */ + CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s)); + /* Check that comparison with the half order is equal to testing for high scalar. */ + CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0)); + secp256k1_scalar_negate(&neg, &s); + secp256k1_num_sub(&negnum, &order, &snum); + secp256k1_num_mod(&negnum, &order); + /* Check that comparison with the half order is equal to testing for high scalar after negation. */ + CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0)); + /* Negating should change the high property, unless the value was already zero. */ + CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s)); + secp256k1_scalar_get_num(&negnum2, &neg); + /* Negating a scalar should be equal to (order - n) mod order on the number. */ + CHECK(secp256k1_num_eq(&negnum, &negnum2)); + secp256k1_scalar_add(&neg, &neg, &s); + /* Adding a number to its negation should result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + secp256k1_scalar_negate(&neg, &neg); + /* Negating zero should still result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + } + + { + /* Test secp256k1_scalar_mul_shift_var. */ + secp256k1_scalar r; + secp256k1_num one; + secp256k1_num rnum; + secp256k1_num rnum2; + unsigned char cone[1] = {0x01}; + unsigned int shift = 256 + secp256k1_rand_int(257); + secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift); + secp256k1_num_mul(&rnum, &s1num, &s2num); + secp256k1_num_shift(&rnum, shift - 1); + secp256k1_num_set_bin(&one, cone, 1); + secp256k1_num_add(&rnum, &rnum, &one); + secp256k1_num_shift(&rnum, 1); + secp256k1_scalar_get_num(&rnum2, &r); + CHECK(secp256k1_num_eq(&rnum, &rnum2)); + } + + { + /* test secp256k1_scalar_shr_int */ + secp256k1_scalar r; + int i; + random_scalar_order_test(&r); + for (i = 0; i < 100; ++i) { + int low; + int shift = 1 + secp256k1_rand_int(15); + int expected = r.d[0] % (1 << shift); + low = secp256k1_scalar_shr_int(&r, shift); + CHECK(expected == low); + } + } +#endif + + { + /* Test that scalar inverses are equal to the inverse of their number modulo the order. */ + if (!secp256k1_scalar_is_zero(&s)) { + secp256k1_scalar inv; +#ifndef USE_NUM_NONE + secp256k1_num invnum; + secp256k1_num invnum2; +#endif + secp256k1_scalar_inverse(&inv, &s); +#ifndef USE_NUM_NONE + secp256k1_num_mod_inverse(&invnum, &snum, &order); + secp256k1_scalar_get_num(&invnum2, &inv); + CHECK(secp256k1_num_eq(&invnum, &invnum2)); +#endif + secp256k1_scalar_mul(&inv, &inv, &s); + /* Multiplying a scalar with its inverse must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); + secp256k1_scalar_inverse(&inv, &inv); + /* Inverting one must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&invnum, &inv); + CHECK(secp256k1_num_is_one(&invnum)); +#endif + } + } + + { + /* Test commutativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + secp256k1_scalar r1, r2; + secp256k1_scalar b; + int i; + /* Test add_bit. */ + int bit = secp256k1_rand_bits(8); + secp256k1_scalar_set_int(&b, 1); + CHECK(secp256k1_scalar_is_one(&b)); + for (i = 0; i < bit; i++) { + secp256k1_scalar_add(&b, &b, &b); + } + r1 = s1; + r2 = s1; + if (!secp256k1_scalar_add(&r1, &r1, &b)) { + /* No overflow happened. */ + secp256k1_scalar_cadd_bit(&r2, bit, 1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + /* cadd is a noop when flag is zero */ + secp256k1_scalar_cadd_bit(&r2, bit, 0); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + } + + { + /* Test commutativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r1, &r1, &s); + secp256k1_scalar_add(&r2, &s2, &s); + secp256k1_scalar_add(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s2, &s); + secp256k1_scalar_mul(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test distributitivity of mul over add. */ + secp256k1_scalar r1, r2, t; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s1, &s); + secp256k1_scalar_mul(&t, &s2, &s); + secp256k1_scalar_add(&r2, &r2, &t); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test multiplicative identity. */ + secp256k1_scalar r1, v1; + secp256k1_scalar_set_int(&v1,1); + secp256k1_scalar_mul(&r1, &s1, &v1); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test additive identity. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_add(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test zero product property. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_mul(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &v0)); + } + +} + +void run_scalar_tests(void) { + int i; + for (i = 0; i < 128 * count; i++) { + scalar_test(); + } + + { + /* (-1)+1 should be zero. */ + secp256k1_scalar s, o; + secp256k1_scalar_set_int(&s, 1); + CHECK(secp256k1_scalar_is_one(&s)); + secp256k1_scalar_negate(&o, &s); + secp256k1_scalar_add(&o, &o, &s); + CHECK(secp256k1_scalar_is_zero(&o)); + secp256k1_scalar_negate(&o, &o); + CHECK(secp256k1_scalar_is_zero(&o)); + } + +#ifndef USE_NUM_NONE + { + /* A scalar with value of the curve order should be 0. */ + secp256k1_num order; + secp256k1_scalar zero; + unsigned char bin[32]; + int overflow = 0; + secp256k1_scalar_order_get_num(&order); + secp256k1_num_get_bin(bin, 32, &order); + secp256k1_scalar_set_b32(&zero, bin, &overflow); + CHECK(overflow == 1); + CHECK(secp256k1_scalar_is_zero(&zero)); + } +#endif + + { + /* Does check_overflow check catch all ones? */ + static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + CHECK(secp256k1_scalar_check_overflow(&overflowed)); + } + + { + /* Static test vectors. + * These were reduced from ~10^12 random vectors based on comparison-decision + * and edge-case coverage on 32-bit and 64-bit implementations. + * The responses were generated with Sage 5.9. + */ + secp256k1_scalar x; + secp256k1_scalar y; + secp256k1_scalar z; + secp256k1_scalar zz; + secp256k1_scalar one; + secp256k1_scalar r1; + secp256k1_scalar r2; +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar zzv; +#endif + int overflow; + unsigned char chal[33][2][32] = { + {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}}, + {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, + 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}, + {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff}, + {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, + 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}}, + {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00}, + {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}}, + {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}}, + {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}}, + {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f, + 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}}, + {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, + 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} + }; + unsigned char res[33][2][32] = { + {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, + 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, + 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, + 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35}, + {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d, + 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c, + 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49, + 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}}, + {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22, + 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c, + 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f, + 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8}, + {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77, + 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4, + 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59, + 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}}, + {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef, + 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab, + 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55, + 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c}, + {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96, + 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f, + 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12, + 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}}, + {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c, + 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf, + 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9, + 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48}, + {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42, + 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5, + 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c, + 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}}, + {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb, + 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74, + 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6, + 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63}, + {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3, + 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99, + 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58, + 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}}, + {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b, + 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7, + 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f, + 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0}, + {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d, + 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d, + 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9, + 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}}, + {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7, + 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70, + 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06, + 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e}, + {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9, + 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79, + 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e, + 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}}, + {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb, + 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5, + 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a, + 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe}, + {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48, + 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e, + 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc, + 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}}, + {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b, + 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0, + 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53, + 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8}, + {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c, + 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01, + 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f, + 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}}, + {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7, + 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c, + 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92, + 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30}, + {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62, + 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e, + 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb, + 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}}, + {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25, + 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d, + 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0, + 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13}, + {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60, + 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00, + 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4, + 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}}, + {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31, + 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4, + 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88, + 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa}, + {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57, + 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38, + 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51, + 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}}, + {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c, + 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f, + 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2, + 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4}, + {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01, + 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4, + 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86, + 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}}, + {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5, + 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51, + 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3, + 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62}, + {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c, + 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91, + 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c, + 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}}, + {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e, + 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56, + 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58, + 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4}, + {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41, + 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7, + 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92, + 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}}, + {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec, + 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19, + 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3, + 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4}, + {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87, + 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a, + 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92, + 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}}, + {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64, + 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3, + 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f, + 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33}, + {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c, + 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d, + 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea, + 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}}, + {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7, + 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a, + 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae, + 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe}, + {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc, + 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39, + 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14, + 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}}, + {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23, + 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d, + 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2, + 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16}, + {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c, + 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84, + 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0, + 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}}, + {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb, + 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94, + 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b, + 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e}, + {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54, + 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00, + 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb, + 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0, + 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b, + 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94, + 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8}, + {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26, + 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d, + 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a, + 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39, + 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea, + 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf, + 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae}, + {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b, + 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb, + 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6, + 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}}, + {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a, + 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f, + 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9, + 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56}, + {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93, + 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07, + 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71, + 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}}, + {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87, + 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9, + 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55, + 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73}, + {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d, + 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86, + 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb, + 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}}, + {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2, + 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7, + 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41, + 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7}, + {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06, + 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04, + 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08, + 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}}, + {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2, + 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b, + 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40, + 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68}, + {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e, + 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a, + 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b, + 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}}, + {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67, + 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f, + 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a, + 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51}, + {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, + 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, + 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} + }; + secp256k1_scalar_set_int(&one, 1); + for (i = 0; i < 33; i++) { + secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r1, res[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r2, res[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_mul(&z, &x, &y); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&r1, &z)); + if (!secp256k1_scalar_is_zero(&y)) { + secp256k1_scalar_inverse(&zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar_inverse_var(&zzv, &y); + CHECK(secp256k1_scalar_eq(&zzv, &zz)); +#endif + secp256k1_scalar_mul(&z, &z, &zz); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&x, &z)); + secp256k1_scalar_mul(&zz, &zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&one, &zz)); + } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(!secp256k1_scalar_check_overflow(&z)); + secp256k1_scalar_sqr(&zz, &x); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&zz, &z)); + CHECK(secp256k1_scalar_eq(&r2, &zz)); + } + } +} + +/***** FIELD TESTS *****/ + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256_test(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_non_zero(secp256k1_fe *nz) { + int tries = 10; + while (--tries >= 0) { + random_fe(nz); + secp256k1_fe_normalize(nz); + if (!secp256k1_fe_is_zero(nz)) { + break; + } + } + /* Infinitesimal probability of spurious failure here */ + CHECK(tries >= 0); +} + +void random_fe_non_square(secp256k1_fe *ns) { + secp256k1_fe r; + random_fe_non_zero(ns); + if (secp256k1_fe_sqrt(&r, ns)) { + secp256k1_fe_negate(ns, ns, 1); + } +} + +int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe an = *a; + secp256k1_fe bn = *b; + secp256k1_fe_normalize_weak(&an); + secp256k1_fe_normalize_var(&bn); + return secp256k1_fe_equal_var(&an, &bn); +} + +int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) { + secp256k1_fe x; + secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe_mul(&x, a, ai); + return check_fe_equal(&x, &one); +} + +void run_field_convert(void) { + static const unsigned char b32[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40 + }; + static const secp256k1_fe_storage fes = SECP256K1_FE_STORAGE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + static const secp256k1_fe fe = SECP256K1_FE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + secp256k1_fe fe2; + unsigned char b322[32]; + secp256k1_fe_storage fes2; + /* Check conversions to fe. */ + CHECK(secp256k1_fe_set_b32(&fe2, b32)); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + secp256k1_fe_from_storage(&fe2, &fes); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + /* Check conversion from fe. */ + secp256k1_fe_get_b32(b322, &fe); + CHECK(memcmp(b322, b32, 32) == 0); + secp256k1_fe_to_storage(&fes2, &fe); + CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); +} + +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe t = *b; +#ifdef VERIFY + t.magnitude = a->magnitude; + t.normalized = a->normalized; +#endif + return memcmp(a, &t, sizeof(secp256k1_fe)); +} + +void run_field_misc(void) { + secp256k1_fe x; + secp256k1_fe y; + secp256k1_fe z; + secp256k1_fe q; + secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); + int i, j; + for (i = 0; i < 5*count; i++) { + secp256k1_fe_storage xs, ys, zs; + random_fe(&x); + random_fe_non_zero(&y); + /* Test the fe equality and comparison operations. */ + CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); + CHECK(secp256k1_fe_equal_var(&x, &x)); + z = x; + secp256k1_fe_add(&z,&y); + /* Test fe conditional move; z is not normalized here. */ + q = x; + secp256k1_fe_cmov(&x, &z, 0); + VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude); + secp256k1_fe_cmov(&x, &x, 1); + CHECK(fe_memcmp(&x, &z) != 0); + CHECK(fe_memcmp(&x, &q) == 0); + secp256k1_fe_cmov(&q, &z, 1); + VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(fe_memcmp(&q, &z) == 0); + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&z); + CHECK(!secp256k1_fe_equal_var(&x, &z)); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (i&1)); + VERIFY_CHECK(q.normalized && q.magnitude == 1); + for (j = 0; j < 6; j++) { + secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (j&1)); + VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); + } + secp256k1_fe_normalize_var(&z); + /* Test storage conversion and conditional moves. */ + secp256k1_fe_to_storage(&xs, &x); + secp256k1_fe_to_storage(&ys, &y); + secp256k1_fe_to_storage(&zs, &z); + secp256k1_fe_storage_cmov(&zs, &xs, 0); + secp256k1_fe_storage_cmov(&zs, &zs, 1); + CHECK(memcmp(&xs, &zs, sizeof(xs)) != 0); + secp256k1_fe_storage_cmov(&ys, &xs, 1); + CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0); + secp256k1_fe_from_storage(&x, &xs); + secp256k1_fe_from_storage(&y, &ys); + secp256k1_fe_from_storage(&z, &zs); + /* Test that mul_int, mul, and add agree. */ + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&y, &x); + z = x; + secp256k1_fe_mul_int(&z, 3); + CHECK(check_fe_equal(&y, &z)); + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&z, &x); + CHECK(check_fe_equal(&z, &y)); + z = x; + secp256k1_fe_mul_int(&z, 5); + secp256k1_fe_mul(&q, &x, &fe5); + CHECK(check_fe_equal(&z, &q)); + secp256k1_fe_negate(&x, &x, 1); + secp256k1_fe_add(&z, &x); + secp256k1_fe_add(&q, &x); + CHECK(check_fe_equal(&y, &z)); + CHECK(check_fe_equal(&q, &y)); + } +} + +void run_field_inv(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_var(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv_var(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv_var(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_all_var(void) { + secp256k1_fe x[16], xi[16], xii[16]; + int i; + /* Check it's safe to call for 0 elements */ + secp256k1_fe_inv_all_var(xi, x, 0); + for (i = 0; i < count; i++) { + size_t j; + size_t len = secp256k1_rand_int(15) + 1; + for (j = 0; j < len; j++) { + random_fe_non_zero(&x[j]); + } + secp256k1_fe_inv_all_var(xi, x, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_inverse(&x[j], &xi[j])); + } + secp256k1_fe_inv_all_var(xii, xi, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_equal(&x[j], &xii[j])); + } + } +} + +void run_sqr(void) { + secp256k1_fe x, s; + + { + int i; + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_negate(&x, &x, 1); + + for (i = 1; i <= 512; ++i) { + secp256k1_fe_mul_int(&x, 2); + secp256k1_fe_normalize(&x); + secp256k1_fe_sqr(&s, &x); + } + } +} + +void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { + secp256k1_fe r1, r2; + int v = secp256k1_fe_sqrt(&r1, a); + CHECK((v == 0) == (k == NULL)); + + if (k != NULL) { + /* Check that the returned root is +/- the given known answer */ + secp256k1_fe_negate(&r2, &r1, 1); + secp256k1_fe_add(&r1, k); secp256k1_fe_add(&r2, k); + secp256k1_fe_normalize(&r1); secp256k1_fe_normalize(&r2); + CHECK(secp256k1_fe_is_zero(&r1) || secp256k1_fe_is_zero(&r2)); + } +} + +void run_sqrt(void) { + secp256k1_fe ns, x, s, t; + int i; + + /* Check sqrt(0) is 0 */ + secp256k1_fe_set_int(&x, 0); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + + /* Check sqrt of small squares (and their negatives) */ + for (i = 1; i <= 100; i++) { + secp256k1_fe_set_int(&x, i); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + } + + /* Consistency checks for large random values */ + for (i = 0; i < 10; i++) { + int j; + random_fe_non_square(&ns); + for (j = 0; j < count; j++) { + random_fe(&x); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + secp256k1_fe_mul(&t, &s, &ns); + test_sqrt(&t, NULL); + } + } +} + +/***** GROUP TESTS *****/ + +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +/* This compares jacobian points including their Z, not just their geometric meaning. */ +int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej a2; + secp256k1_gej b2; + int ret = 1; + ret &= a->infinity == b->infinity; + if (ret && !a->infinity) { + a2 = *a; + b2 = *b; + secp256k1_fe_normalize(&a2.x); + secp256k1_fe_normalize(&a2.y); + secp256k1_fe_normalize(&a2.z); + secp256k1_fe_normalize(&b2.x); + secp256k1_fe_normalize(&b2.y); + secp256k1_fe_normalize(&b2.z); + ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0; + ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0; + ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0; + } + return ret; +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void test_ge(void) { + int i, i1; +#ifdef USE_ENDOMORPHISM + int runs = 6; +#else + int runs = 4; +#endif + /* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4). + * The second in each pair of identical points uses a random Z coordinate in the Jacobian form. + * All magnitudes are randomized. + * All 17*17 combinations of points are added to each other, using all applicable methods. + * + * When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well. + */ + secp256k1_ge *ge = (secp256k1_ge *)malloc(sizeof(secp256k1_ge) * (1 + 4 * runs)); + secp256k1_gej *gej = (secp256k1_gej *)malloc(sizeof(secp256k1_gej) * (1 + 4 * runs)); + secp256k1_fe *zinv = (secp256k1_fe *)malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + secp256k1_fe zf; + secp256k1_fe zfi2, zfi3; + + secp256k1_gej_set_infinity(&gej[0]); + secp256k1_ge_clear(&ge[0]); + secp256k1_ge_set_gej_var(&ge[0], &gej[0]); + for (i = 0; i < runs; i++) { + int j; + secp256k1_ge g; + random_group_element_test(&g); +#ifdef USE_ENDOMORPHISM + if (i >= runs - 2) { + secp256k1_ge_mul_lambda(&g, &ge[1]); + } + if (i >= runs - 1) { + secp256k1_ge_mul_lambda(&g, &g); + } +#endif + ge[1 + 4 * i] = g; + ge[2 + 4 * i] = g; + secp256k1_ge_neg(&ge[3 + 4 * i], &g); + secp256k1_ge_neg(&ge[4 + 4 * i], &g); + secp256k1_gej_set_ge(&gej[1 + 4 * i], &ge[1 + 4 * i]); + random_group_element_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); + secp256k1_gej_set_ge(&gej[3 + 4 * i], &ge[3 + 4 * i]); + random_group_element_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); + for (j = 0; j < 4; j++) { + random_field_element_magnitude(&ge[1 + j + 4 * i].x); + random_field_element_magnitude(&ge[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].x); + random_field_element_magnitude(&gej[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].z); + } + } + + /* Compute z inverses. */ + { + secp256k1_fe *zs = malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + for (i = 0; i < 4 * runs + 1; i++) { + if (i == 0) { + /* The point at infinity does not have a meaningful z inverse. Any should do. */ + do { + random_field_element_test(&zs[i]); + } while(secp256k1_fe_is_zero(&zs[i])); + } else { + zs[i] = gej[i].z; + } + } + secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1); + free(zs); + } + + /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ + do { + random_field_element_test(&zf); + } while(secp256k1_fe_is_zero(&zf)); + random_field_element_magnitude(&zf); + secp256k1_fe_inv_var(&zfi3, &zf); + secp256k1_fe_sqr(&zfi2, &zfi3); + secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { + int i2; + for (i2 = 0; i2 < 1 + 4 * runs; i2++) { + /* Compute reference result using gej + gej (var). */ + secp256k1_gej refj, resj; + secp256k1_ge ref; + secp256k1_fe zr; + secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + /* Check Z ratio. */ + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&refj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &refj.z)); + } + secp256k1_ge_set_gej_var(&ref, &refj); + + /* Test gej + ge with Z ratio result (var). */ + secp256k1_gej_add_ge_var(&resj, &gej[i1], &ge[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + ge_equals_gej(&ref, &resj); + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&resj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &resj.z)); + } + + /* Test gej + ge (var, with additional Z factor). */ + { + secp256k1_ge ge2_zfi = ge[i2]; /* the second term with x and y rescaled for z = 1/zf */ + secp256k1_fe_mul(&ge2_zfi.x, &ge2_zfi.x, &zfi2); + secp256k1_fe_mul(&ge2_zfi.y, &ge2_zfi.y, &zfi3); + random_field_element_magnitude(&ge2_zfi.x); + random_field_element_magnitude(&ge2_zfi.y); + secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); + ge_equals_gej(&ref, &resj); + } + + /* Test gej + ge (const). */ + if (i2 != 0) { + /* secp256k1_gej_add_ge does not support its second argument being infinity. */ + secp256k1_gej_add_ge(&resj, &gej[i1], &ge[i2]); + ge_equals_gej(&ref, &resj); + } + + /* Test doubling (var). */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 == ((i2 + 3)%4)/2)) { + secp256k1_fe zr2; + /* Normal doubling with Z ratio result. */ + secp256k1_gej_double_var(&resj, &gej[i1], &zr2); + ge_equals_gej(&ref, &resj); + /* Check Z ratio. */ + secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + /* Normal doubling. */ + secp256k1_gej_double_var(&resj, &gej[i2], NULL); + ge_equals_gej(&ref, &resj); + } + + /* Test adding opposites. */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 != ((i2 + 3)%4)/2)) { + CHECK(secp256k1_ge_is_infinity(&ref)); + } + + /* Test adding infinity. */ + if (i1 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i1])); + CHECK(secp256k1_gej_is_infinity(&gej[i1])); + ge_equals_gej(&ref, &gej[i2]); + } + if (i2 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i2])); + CHECK(secp256k1_gej_is_infinity(&gej[i2])); + ge_equals_gej(&ref, &gej[i1]); + } + } + } + + /* Test adding all points together in random order equals infinity. */ + { + secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; + secp256k1_gej *gej_shuffled = (secp256k1_gej *)malloc((4 * runs + 1) * sizeof(secp256k1_gej)); + for (i = 0; i < 4 * runs + 1; i++) { + gej_shuffled[i] = gej[i]; + } + for (i = 0; i < 4 * runs + 1; i++) { + int swap = i + secp256k1_rand_int(4 * runs + 1 - i); + if (swap != i) { + secp256k1_gej t = gej_shuffled[i]; + gej_shuffled[i] = gej_shuffled[swap]; + gej_shuffled[swap] = t; + } + } + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i], NULL); + } + CHECK(secp256k1_gej_is_infinity(&sum)); + free(gej_shuffled); + } + + /* Test batch gej -> ge conversion with and without known z ratios. */ + { + secp256k1_fe *zr = (secp256k1_fe *)malloc((4 * runs + 1) * sizeof(secp256k1_fe)); + secp256k1_ge *ge_set_table = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + secp256k1_ge *ge_set_all = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + for (i = 0; i < 4 * runs + 1; i++) { + /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ + if (i < 4 * runs) { + secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); + } + } + secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); + secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback); + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe s; + random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); + ge_equals_gej(&ge_set_table[i], &gej[i]); + ge_equals_gej(&ge_set_all[i], &gej[i]); + } + free(ge_set_table); + free(ge_set_all); + free(zr); + } + + free(ge); + free(gej); + free(zinv); +} + +void test_add_neg_y_diff_x(void) { + /* The point of this test is to check that we can add two points + * whose y-coordinates are negatives of each other but whose x + * coordinates differ. If the x-coordinates were the same, these + * points would be negatives of each other and their sum is + * infinity. This is cool because it "covers up" any degeneracy + * in the addition algorithm that would cause the xy coordinates + * of the sum to be wrong (since infinity has no xy coordinates). + * HOWEVER, if the x-coordinates are different, infinity is the + * wrong answer, and such degeneracies are exposed. This is the + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * which this test is a regression test for. + * + * These points were generated in sage as + * # secp256k1 params + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (7)]) + * G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) + * N = FiniteField(G.order()) + * + * # endomorphism values (lambda is 1^{1/3} in N, beta is 1^{1/3} in F) + * x = polygen(N) + * lam = (1 - x^3).roots()[1][0] + * + * # random "bad pair" + * P = C.random_element() + * Q = -int(lam) * P + * print " P: %x %x" % P.xy() + * print " Q: %x %x" % Q.xy() + * print "P + Q: %x %x" % (P + Q).xy() + */ + secp256k1_gej aj = SECP256K1_GEJ_CONST( + 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, + 0x0643d79f, 0x05a59614, 0x2f8ec030, 0xd58977cb, + 0x001e337a, 0x38093dcd, 0x6c0f386d, 0x0b1293a8, + 0x4d72c879, 0xd7681924, 0x44e6d2f3, 0x9190117d + ); + secp256k1_gej bj = SECP256K1_GEJ_CONST( + 0xc7b74206, 0x1f788cd9, 0xabd0937d, 0x164a0d86, + 0x95f6ff75, 0xf19a4ce9, 0xd013bd7b, 0xbf92d2a7, + 0xffe1cc85, 0xc7f6c232, 0x93f0c792, 0xf4ed6c57, + 0xb28d3786, 0x2897e6db, 0xbb192d0b, 0x6e6feab2 + ); + secp256k1_gej sumj = SECP256K1_GEJ_CONST( + 0x671a63c0, 0x3efdad4c, 0x389a7798, 0x24356027, + 0xb3d69010, 0x278625c3, 0x5c86d390, 0x184a8f7a, + 0x5f6409c2, 0x2ce01f2b, 0x511fd375, 0x25071d08, + 0xda651801, 0x70e95caf, 0x8f0d893c, 0xbed8fbbe + ); + secp256k1_ge b; + secp256k1_gej resj; + secp256k1_ge res; + secp256k1_ge_set_gej(&b, &bj); + + secp256k1_gej_add_var(&resj, &aj, &bj, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge(&resj, &aj, &b); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); +} + +void run_ge(void) { + int i; + for (i = 0; i < count * 32; i++) { + test_ge(); + } + test_add_neg_y_diff_x(); +} + +void test_ec_combine(void) { + secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey data[6]; + const secp256k1_pubkey* d[6]; + secp256k1_pubkey sd; + secp256k1_pubkey sd2; + secp256k1_gej Qj; + secp256k1_ge Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&data[i - 1], &Q); + d[i - 1] = &data[i - 1]; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&sd, &Q); + CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1); + CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + +void test_group_decompress(const secp256k1_fe* x) { + /* The input itself, normalized. */ + secp256k1_fe fex = *x; + secp256k1_fe fez; + /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; + /* Return values of the above calls. */ + int res_quad, res_even, res_odd; + + secp256k1_fe_normalize_var(&fex); + + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); + res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); + res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); + + CHECK(res_quad == res_even); + CHECK(res_quad == res_odd); + + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); + secp256k1_fe_normalize_var(&ge_odd.x); + secp256k1_fe_normalize_var(&ge_even.x); + secp256k1_fe_normalize_var(&ge_quad.y); + secp256k1_fe_normalize_var(&ge_odd.y); + secp256k1_fe_normalize_var(&ge_even.y); + + /* No infinity allowed. */ + CHECK(!ge_quad.infinity); + CHECK(!ge_even.infinity); + CHECK(!ge_odd.infinity); + + /* Check that the x coordinates check out. */ + CHECK(secp256k1_fe_equal_var(&ge_quad.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_even.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); + + /* Check that the Y coordinate result in ge_quad is a square. */ + CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); + + /* Check odd/even Y in ge_odd, ge_even. */ + CHECK(secp256k1_fe_is_odd(&ge_odd.y)); + CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + } +} + +void run_group_decompress(void) { + int i; + for (i = 0; i < count * 4; i++) { + secp256k1_fe fe; + random_fe_test(&fe); + test_group_decompress(&fe); + } +} + +/***** ECMULT TESTS *****/ + +void run_ecmult_chain(void) { + /* random starting point A (on the curve) */ + secp256k1_gej a = SECP256K1_GEJ_CONST( + 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, + 0x727fd8bc, 0x04d3362c, 0x6c7bf458, 0xe2846004, + 0xa357ae91, 0x5c4a6528, 0x1309edf2, 0x0504740f, + 0x0eb33439, 0x90216b4f, 0x81063cb6, 0x5f2f7e0f + ); + /* two random initial factors xn and gn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x84cc5452, 0xf7fde1ed, 0xb4d38a8c, 0xe9b1b84c, + 0xcef31f14, 0x6e569be9, 0x705d357a, 0x42985407 + ); + secp256k1_scalar gn = SECP256K1_SCALAR_CONST( + 0xa1e58d22, 0x553dcd42, 0xb2398062, 0x5d4c57a9, + 0x6e9323d4, 0x2b3152e5, 0xca2c3990, 0xedc7c9de + ); + /* two small multipliers to be applied to xn and gn in every iteration: */ + static const secp256k1_scalar xf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x1337); + static const secp256k1_scalar gf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x7113); + /* accumulators with the resulting coefficients to A and G */ + secp256k1_scalar ae = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar ge = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + /* actual points */ + secp256k1_gej x; + secp256k1_gej x2; + int i; + + /* the point being computed */ + x = a; + for (i = 0; i < 200*count; i++) { + /* in each iteration, compute X = xn*X + gn*G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x, &x, &xn, &gn); + /* also compute ae and ge: the actual accumulated factors for A and G */ + /* if X was (ae*A+ge*G), xn*X + gn*G results in (xn*ae*A + (xn*ge+gn)*G) */ + secp256k1_scalar_mul(&ae, &ae, &xn); + secp256k1_scalar_mul(&ge, &ge, &xn); + secp256k1_scalar_add(&ge, &ge, &gn); + /* modify xn and gn */ + secp256k1_scalar_mul(&xn, &xn, &xf); + secp256k1_scalar_mul(&gn, &gn, &gf); + + /* verify */ + if (i == 19999) { + /* expected result after 19999 iterations */ + secp256k1_gej rp = SECP256K1_GEJ_CONST( + 0xD6E96687, 0xF9B10D09, 0x2A6F3543, 0x9D86CEBE, + 0xA4535D0D, 0x409F5358, 0x6440BD74, 0xB933E830, + 0xB95CBCA2, 0xC77DA786, 0x539BE8FD, 0x53354D2D, + 0x3B4F566A, 0xE6580454, 0x07ED6015, 0xEE1B2A88 + ); + + secp256k1_gej_neg(&rp, &rp); + secp256k1_gej_add_var(&rp, &rp, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&rp)); + } + } + /* redo the computation, but directly with the resulting ae and ge coefficients: */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x2, &a, &ae, &ge); + secp256k1_gej_neg(&x2, &x2); + secp256k1_gej_add_var(&x2, &x2, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&x2)); +} + +void test_point_times_order(const secp256k1_gej *point) { + /* X * (point + G) + (order-X) * (pointer + G) = 0 */ + secp256k1_scalar x; + secp256k1_scalar nx; + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_gej res1, res2; + secp256k1_ge res3; + unsigned char pub[65]; + size_t psize = 65; + random_scalar_order_test(&x); + secp256k1_scalar_negate(&nx, &x); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &x, &x); /* calc res1 = x * point + x * G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ + secp256k1_gej_add_var(&res1, &res1, &res2, NULL); + CHECK(secp256k1_gej_is_infinity(&res1)); + CHECK(secp256k1_gej_is_valid_var(&res1) == 0); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + CHECK(secp256k1_ge_is_valid_var(&res3) == 0); + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 0) == 0); + psize = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 1) == 0); + /* check zero/one edge cases */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &zero); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_gej(&res3, point); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_ge(&res3, &secp256k1_ge_const_g); +} + +void run_point_times_order(void) { + int i; + secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); + static const secp256k1_fe xr = SECP256K1_FE_CONST( + 0x7603CB59, 0xB0EF6C63, 0xFE608479, 0x2A0C378C, + 0xDB3233A8, 0x0F8A9A09, 0xA877DEAD, 0x31B38C45 + ); + for (i = 0; i < 500; i++) { + secp256k1_ge p; + if (secp256k1_ge_set_xo_var(&p, &x, 1)) { + secp256k1_gej j; + CHECK(secp256k1_ge_is_valid_var(&p)); + secp256k1_gej_set_ge(&j, &p); + CHECK(secp256k1_gej_is_valid_var(&j)); + test_point_times_order(&j); + } + secp256k1_fe_sqr(&x, &x); + } + secp256k1_fe_normalize_var(&x); + CHECK(secp256k1_fe_equal_var(&x, &xr)); +} + +void ecmult_const_random_mult(void) { + /* random starting point A (on the curve) */ + secp256k1_ge a = SECP256K1_GE_CONST( + 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, + 0xa97f9263, 0xb1e88044, 0x93351325, 0x91bc450a, + 0x535c59f7, 0x325e5d2b, 0xc391fbe8, 0x3c12787c, + 0x337e4a98, 0xe82a9011, 0x0123ba37, 0xdd769c7d + ); + /* random initial factor xn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x649d4f77, 0xc4242df7, 0x7f2079c9, 0x14530327, + 0xa31b876a, 0xd2d8ce2a, 0x2236d5c6, 0xd7b2029b + ); + /* expected xn * A (from sage) */ + secp256k1_ge expected_b = SECP256K1_GE_CONST( + 0x23773684, 0x4d209dc7, 0x098a786f, 0x20d06fcd, + 0x070a38bf, 0xc11ac651, 0x03004319, 0x1e2a8786, + 0xed8c3b8e, 0xc06dd57b, 0xd06ea66e, 0x45492b0f, + 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 + ); + secp256k1_gej b; + secp256k1_ecmult_const(&b, &a, &xn); + + CHECK(secp256k1_ge_is_valid_var(&a)); + ge_equals_gej(&expected_b, &b); +} + +void ecmult_const_commutativity(void) { + secp256k1_scalar a; + secp256k1_scalar b; + secp256k1_gej res1; + secp256k1_gej res2; + secp256k1_ge mid1; + secp256k1_ge mid2; + random_scalar_order_test(&a); + random_scalar_order_test(&b); + + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + ge_equals_ge(&mid1, &mid2); +} + +void ecmult_const_mult_zero_one(void) { + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar negone; + secp256k1_gej res1; + secp256k1_ge res2; + secp256k1_ge point; + secp256k1_scalar_negate(&negone, &one); + + random_group_element_test(&point); + secp256k1_ecmult_const(&res1, &point, &zero); + secp256k1_ge_set_gej(&res2, &res1); + CHECK(secp256k1_ge_is_infinity(&res2)); + secp256k1_ecmult_const(&res1, &point, &one); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); + secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_gej_neg(&res1, &res1); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); +} + +void ecmult_const_chain_multiply(void) { + /* Check known result (randomly generated test problem from sage) */ + const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( + 0x4968d524, 0x2abf9b7a, 0x466abbcf, 0x34b11b6d, + 0xcd83d307, 0x827bed62, 0x05fad0ce, 0x18fae63b + ); + const secp256k1_gej expected_point = SECP256K1_GEJ_CONST( + 0x5494c15d, 0x32099706, 0xc2395f94, 0x348745fd, + 0x757ce30e, 0x4e8c90fb, 0xa2bad184, 0xf883c69f, + 0x5d195d20, 0xe191bf7f, 0x1be3e55f, 0x56a80196, + 0x6071ad01, 0xf1462f66, 0xc997fa94, 0xdb858435 + ); + secp256k1_gej point; + secp256k1_ge res; + int i; + + secp256k1_gej_set_ge(&point, &secp256k1_ge_const_g); + for (i = 0; i < 100; ++i) { + secp256k1_ge tmp; + secp256k1_ge_set_gej(&tmp, &point); + secp256k1_ecmult_const(&point, &tmp, &scalar); + } + secp256k1_ge_set_gej(&res, &point); + ge_equals_gej(&res, &expected_point); +} + +void run_ecmult_const_tests(void) { + ecmult_const_mult_zero_one(); + ecmult_const_random_mult(); + ecmult_const_commutativity(); + ecmult_const_chain_multiply(); +} + +void test_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, two, t; + int wnaf[256]; + int zeroes = -1; + int i; + int bits; + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&two, 2); + bits = secp256k1_ecmult_wnaf(wnaf, 256, number, w); + CHECK(bits <= 256); + for (i = bits-1; i >= 0; i--) { + int v = wnaf[i]; + secp256k1_scalar_mul(&x, &x, &two); + if (v) { + CHECK(zeroes == -1 || zeroes >= w-1); /* check that distance between non-zero elements is at least w-1 */ + zeroes=0; + CHECK((v & 1) == 1); /* check non-zero elements are odd */ + CHECK(v <= (1 << (w-1)) - 1); /* check range below */ + CHECK(v >= -(1 << (w-1)) - 1); /* check range above */ + } else { + CHECK(zeroes != -1); /* check that no unnecessary zero padding exists */ + zeroes++; + } + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + CHECK(secp256k1_scalar_eq(&x, number)); /* check that wnaf represents number */ +} + +void test_constant_wnaf_negate(const secp256k1_scalar *number) { + secp256k1_scalar neg1 = *number; + secp256k1_scalar neg2 = *number; + int sign1 = 1; + int sign2 = 1; + + if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { + secp256k1_scalar_negate(&neg1, &neg1); + sign1 = -1; + } + sign2 = secp256k1_scalar_cond_negate(&neg2, secp256k1_scalar_is_even(&neg2)); + CHECK(sign1 == sign2); + CHECK(secp256k1_scalar_eq(&neg1, &neg2)); +} + +void test_constant_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, shift; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&shift, 1 << w); + /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */ +#ifdef USE_ENDOMORPHISM + for (i = 0; i < 16; ++i) { + secp256k1_scalar_shr_int(&num, 8); + } +#endif + skew = secp256k1_wnaf_const(wnaf, num, w); + + for (i = WNAF_SIZE(w); i >= 0; --i) { + secp256k1_scalar t; + int v = wnaf[i]; + CHECK(v != 0); /* check nonzero */ + CHECK(v & 1); /* check parity */ + CHECK(v > -(1 << w)); /* check range above */ + CHECK(v < (1 << w)); /* check range below */ + + secp256k1_scalar_mul(&x, &x, &shift); + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + /* Skew num because when encoding numbers as odd we use an offset */ + secp256k1_scalar_cadd_bit(&num, skew == 2, 1); + CHECK(secp256k1_scalar_eq(&x, &num)); +} + +void run_wnaf(void) { + int i; + secp256k1_scalar n = {{0}}; + + /* Sanity check: 1 and 2 are the smallest odd and even numbers and should + * have easier-to-diagnose failure modes */ + n.d[0] = 1; + test_constant_wnaf(&n, 4); + n.d[0] = 2; + test_constant_wnaf(&n, 4); + /* Random tests */ + for (i = 0; i < count; i++) { + random_scalar_order(&n); + test_wnaf(&n, 4+(i%10)); + test_constant_wnaf_negate(&n); + test_constant_wnaf(&n, 4 + (i % 10)); + } + secp256k1_scalar_set_int(&n, 0); + CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); + CHECK(secp256k1_scalar_is_zero(&n)); + CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1); + CHECK(secp256k1_scalar_is_zero(&n)); +} + +void test_ecmult_constants(void) { + /* Test ecmult_gen() for [0..36) and [order-36..0). */ + secp256k1_scalar x; + secp256k1_gej r; + secp256k1_ge ng; + int i; + int j; + secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); + for (i = 0; i < 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&secp256k1_ge_const_g, &r); + } + secp256k1_gej_add_ge(&r, &r, &ng); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } + for (i = 1; i <= 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_scalar_negate(&x, &x); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&ng, &r); + } + secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } +} + +void run_ecmult_constants(void) { + test_ecmult_constants(); +} + +void test_ecmult_gen_blind(void) { + /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */ + secp256k1_scalar key; + secp256k1_scalar b; + unsigned char seed32[32]; + secp256k1_gej pgej; + secp256k1_gej pgej2; + secp256k1_gej i; + secp256k1_ge pge; + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); + secp256k1_rand256(seed32); + b = ctx->ecmult_gen_ctx.blind; + i = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key); + CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); + CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ge_set_gej(&pge, &pgej); + ge_equals_gej(&pge, &pgej2); +} + +void test_ecmult_gen_blind_reset(void) { + /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ + secp256k1_scalar b; + secp256k1_gej initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + b = ctx->ecmult_gen_ctx.blind; + initial = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); +} + +void run_ecmult_gen_blind(void) { + int i; + test_ecmult_gen_blind_reset(); + for (i = 0; i < 10; i++) { + test_ecmult_gen_blind(); + } +} + +#ifdef USE_ENDOMORPHISM +/***** ENDOMORPHISH TESTS *****/ +void test_scalar_split(void) { + secp256k1_scalar full; + secp256k1_scalar s1, slam; + const unsigned char zero[32] = {0}; + unsigned char tmp[32]; + + random_scalar_order_test(&full); + secp256k1_scalar_split_lambda(&s1, &slam, &full); + + /* check that both are <= 128 bits in size */ + if (secp256k1_scalar_is_high(&s1)) { + secp256k1_scalar_negate(&s1, &s1); + } + if (secp256k1_scalar_is_high(&slam)) { + secp256k1_scalar_negate(&slam, &slam); + } + + secp256k1_scalar_get_b32(tmp, &s1); + CHECK(memcmp(zero, tmp, 16) == 0); + secp256k1_scalar_get_b32(tmp, &slam); + CHECK(memcmp(zero, tmp, 16) == 0); +} + +void run_endomorphism_tests(void) { + test_scalar_split(); +} +#endif + +void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { + unsigned char pubkeyc[65]; + secp256k1_pubkey pubkey; + secp256k1_ge ge; + size_t pubkeyclen; + int32_t ecount; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) { + /* Smaller sizes are tested exhaustively elsewhere. */ + int32_t i; + memcpy(&pubkeyc[1], input, 64); + VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen); + for (i = 0; i < 256; i++) { + /* Try all type bytes. */ + int xpass; + int ypass; + int ysign; + pubkeyc[0] = i; + /* What sign does this point have? */ + ysign = (input[63] & 1) + 2; + /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */ + xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2); + /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */ + ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) && + ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65)); + if (xpass || ypass) { + /* These cases must parse. */ + unsigned char pubkeyo[65]; + size_t outl; + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + ecount = 0; + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 33); + CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0); + CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0])); + if (ypass) { + /* This test isn't always done because we decode with alternative signs, so the y won't match. */ + CHECK(pubkeyo[0] == ysign); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + secp256k1_pubkey_save(&pubkey, &ge); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 65); + CHECK(pubkeyo[0] == 4); + CHECK(memcmp(&pubkeyo[1], input, 64) == 0); + } + CHECK(ecount == 0); + } else { + /* These cases must fail to parse. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + } + } + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void run_ec_pubkey_parse_test(void) { +#define SECP256K1_EC_PARSE_TEST_NVALID (12) + const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { + { + /* Point with leading and trailing zeros in x and y serialization. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83, + 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00 + }, + { + /* Point with x equal to a 3rd root of unity.*/ + 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9, + 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with largest x. (1/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e, + 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d, + }, + { + /* Point with largest x. (2/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1, + 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2, + }, + { + /* Point with smallest x. (1/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with smallest x. (2/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* Point with largest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with smallest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + } + }; +#define SECP256K1_EC_PARSE_TEST_NXVALID (4) + const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = { + { + /* Valid if y overflow ignored (y = 1 mod p). (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* x on curve, y is from y^2 = x^3 + 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + } + }; +#define SECP256K1_EC_PARSE_TEST_NINVALID (7) + const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = { + { + /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */ + 0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, + 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f, + 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0, + 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d, + 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2, + 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53 + } + }; + const unsigned char pubkeyc[66] = { + /* Serialization of G. */ + 0x04, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98, 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, + 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, + 0xB8, 0x00 + }; + unsigned char sout[65]; + unsigned char shortkey[2]; + secp256k1_ge ge; + secp256k1_pubkey pubkey; + size_t len; + int32_t i; + int32_t ecount; + int32_t ecount2; + ecount = 0; + /* Nothing should be reading this far into pubkeyc. */ + VG_UNDEF(&pubkeyc[65], 1); + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + /* Zero length claimed, fail, zeroize, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(shortkey, 2); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Length one claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 256 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i; + VG_UNDEF(&shortkey[1], 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + /* Length two claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 65536 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i & 255; + shortkey[1] = i >> 8; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0); + CHECK(ecount == 2); + /* NULL input string. Illegal arg and zeroize output. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 1); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 2); + /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Valid parse. */ + memset(&pubkey, 0, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + VG_UNDEF(&ge, sizeof(ge)); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + VG_CHECK(&ge.x, sizeof(ge.x)); + VG_CHECK(&ge.y, sizeof(ge.y)); + VG_CHECK(&ge.infinity, sizeof(ge.infinity)); + ge_equals_ge(&secp256k1_ge_const_g, &ge); + CHECK(ecount == 0); + /* secp256k1_ec_pubkey_serialize illegal args. */ + ecount = 0; + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 1); + CHECK(len == 0); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 2); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); + VG_CHECK(sout, 65); + CHECK(ecount == 3); + CHECK(len == 0); + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(ecount == 4); + CHECK(len == 0); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(sout, 65); + CHECK(ecount == 4); + CHECK(len == 65); + /* Multiple illegal args. Should still set arg error only once. */ + ecount = 0; + ecount2 = 11; + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + /* Does the illegal arg callback actually change the behavior? */ + secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2); + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + CHECK(ecount2 == 10); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + /* Try a bunch of prefabbed points with all possible encodings. */ + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) { + ec_pubkey_parse_pointtest(valid[i], 1, 1); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) { + ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) { + ec_pubkey_parse_pointtest(invalid[i], 0, 0); + } +} + +void run_eckey_edge_case_test(void) { + const unsigned char orderc[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; + unsigned char ctmp[33]; + unsigned char ctmp2[33]; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey2; + secp256k1_pubkey pubkey_one; + secp256k1_pubkey pubkey_negone; + const secp256k1_pubkey *pubkeys[3]; + size_t len; + int32_t ecount; + /* Group order is too large, reject. */ + CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Maximum value is too large, reject. */ + memset(ctmp, 255, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Zero is too small, reject. */ + memset(ctmp, 0, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* One must be accepted. */ + ctmp[31] = 0x01; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_one = pubkey; + /* Group order + 1 is too large, reject. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x42; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* -1 must be accepted. */ + ctmp[31] = 0x40; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_negone = pubkey; + /* Tweak of zero leaves the value changed. */ + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); + memcpy(&pubkey2, &pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Multiply tweak of zero zeroizes the output. */ + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Overflowing key tweak zeroizes. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Private key tweaks results in a key of zero. */ + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(memcmp(zeros, ctmp2, 32) == 0); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Tweak computation wraps and results in a key of 1. */ + ctmp2[31] = 2; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1); + CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Tweak mul * 2 = 1+1. */ + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Test argument errors. */ + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(ecount == 0); + /* Zeroize pubkey on parse error. */ + memset(&pubkey, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + memset(&pubkey2, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0); + /* Plain argument errors. */ + ecount = 0; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0); + CHECK(ecount == 1); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0); + CHECK(ecount == 1); + memset(&pubkey, 1, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* secp256k1_ec_pubkey_combine tests. */ + ecount = 0; + pubkeys[0] = &pubkey_one; + VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *)); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 2); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + pubkeys[0] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Result is infinity. */ + pubkeys[0] = &pubkey_one; + pubkeys[1] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + /* Passes through infinity but comes out one. */ + pubkeys[2] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Adds to two. */ + pubkeys[1] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { + secp256k1_scalar nonce; + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); +} + +void test_ecdsa_sign_verify(void) { + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar one; + secp256k1_scalar msg, key; + secp256k1_scalar sigr, sigs; + int recid; + int getrec; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + getrec = secp256k1_rand_bits(1); + random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); + if (getrec) { + CHECK(recid >= 0 && recid < 4); + } + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); +} + +void run_ecdsa_sign_verify(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_sign_verify(); + } +} + +/** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void)msg32; + (void)key32; + (void)algo16; + memcpy(nonce32, data, 32); + return (counter == 0); +} + +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that has a fatal error on the first counter value. */ + if (counter == 0) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 1); +} + +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ + if (counter < 3) { + memset(nonce32, counter==0 ? 0 : 255, 32); + if (counter == 2) { + nonce32[31]--; + } + return 1; + } + if (counter < 5) { + static const unsigned char order[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; + memcpy(nonce32, order, 32); + if (counter == 4) { + nonce32[31]++; + } + return 1; + } + /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */ + /* If someone does fine a case where it retries for secp256k1, we'd like to know. */ + if (counter > 5) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); +} + +int is_empty_signature(const secp256k1_ecdsa_signature *sig) { + static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0}; + return memcmp(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; +} + +void test_ecdsa_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + unsigned char privkey2[32]; + secp256k1_ecdsa_signature signature[6]; + secp256k1_scalar r, s; + unsigned char sig[74]; + size_t siglen = 74; + unsigned char pubkeyc[65]; + size_t pubkeyclen = 65; + secp256k1_pubkey pubkey; + unsigned char seckey[300]; + size_t seckeylen = 300; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Verify exporting and importing public key. */ + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); + memset(&pubkey, 0, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + + /* Verify private key import and export. */ + CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1)); + CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1); + CHECK(memcmp(privkey, privkey2, 32) == 0); + + /* Optionally tweak the keys using addition. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Optionally tweak the keys using multiplication. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Sign. */ + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1); + CHECK(memcmp(&signature[0], &signature[4], sizeof(signature[0])) == 0); + CHECK(memcmp(&signature[0], &signature[1], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[2], &signature[3], sizeof(signature[0])) != 0); + /* Verify. */ + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1); + /* Test lower-S form, malleate, verify and fail, test again, malleate again */ + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0])); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(memcmp(&signature[5], &signature[0], 64) == 0); + + /* Serialize/parse DER and verify again */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + memset(&signature[0], 0, sizeof(signature[0])); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + /* Serialize/destroy/parse DER and verify again. */ + siglen = 74; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 || + secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0); +} + +void test_random_pubkeys(void) { + secp256k1_ge elem; + secp256k1_ge elem2; + unsigned char in[65]; + /* Generate some randomly sized pubkeys. */ + size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33; + if (secp256k1_rand_bits(2) == 0) { + len = secp256k1_rand_bits(6); + } + if (len == 65) { + in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); + } else { + in[0] = secp256k1_rand_bits(1) ? 2 : 3; + } + if (secp256k1_rand_bits(3) == 0) { + in[0] = secp256k1_rand_bits(8); + } + if (len > 1) { + secp256k1_rand256(&in[1]); + } + if (len > 33) { + secp256k1_rand256(&in[33]); + } + if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { + unsigned char out[65]; + unsigned char firstb; + int res; + size_t size = len; + firstb = in[0]; + /* If the pubkey can be parsed, it should round-trip... */ + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33)); + CHECK(size == len); + CHECK(memcmp(&in[1], &out[1], len-1) == 0); + /* ... except for the type of hybrid inputs. */ + if ((in[0] != 6) && (in[0] != 7)) { + CHECK(in[0] == out[0]); + } + size = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&elem, in, &size, 0)); + CHECK(size == 65); + CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); + ge_equals_ge(&elem,&elem2); + /* Check that the X9.62 hybrid type is checked. */ + in[0] = secp256k1_rand_bits(1) ? 6 : 7; + res = secp256k1_eckey_pubkey_parse(&elem2, in, size); + if (firstb == 2 || firstb == 3) { + if (in[0] == firstb + 4) { + CHECK(res); + } else { + CHECK(!res); + } + } + if (res) { + ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0)); + CHECK(memcmp(&in[1], &out[1], 64) == 0); + } + } +} + +void run_random_pubkeys(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_random_pubkeys(); + } +} + +void run_ecdsa_end_to_end(void) { + int i; + for (i = 0; i < 64*count; i++) { + test_ecdsa_end_to_end(); + } +} + +int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { + static const unsigned char zeroes[32] = {0}; +#ifdef ENABLE_OPENSSL_TESTS + static const unsigned char max_scalar[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; +#endif + + int ret = 0; + + secp256k1_ecdsa_signature sig_der; + unsigned char roundtrip_der[2048]; + unsigned char compact_der[64]; + size_t len_der = 2048; + int parsed_der = 0, valid_der = 0, roundtrips_der = 0; + + secp256k1_ecdsa_signature sig_der_lax; + unsigned char roundtrip_der_lax[2048]; + unsigned char compact_der_lax[64]; + size_t len_der_lax = 2048; + int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0; + +#ifdef ENABLE_OPENSSL_TESTS + ECDSA_SIG *sig_openssl; + const unsigned char *sigptr; + unsigned char roundtrip_openssl[2048]; + int len_openssl = 2048; + int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0; +#endif + + parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen); + if (parsed_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0; + valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0); + } + if (valid_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1; + roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0; + } + + parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen); + if (parsed_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10; + valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0); + } + if (valid_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; + roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0; + } + + if (certainly_der) { + ret |= (!parsed_der) << 2; + } + if (certainly_not_der) { + ret |= (parsed_der) << 17; + } + if (valid_der) { + ret |= (!roundtrips_der) << 3; + } + + if (valid_der) { + ret |= (!roundtrips_der_lax) << 12; + ret |= (len_der != len_der_lax) << 13; + ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14; + } + ret |= (roundtrips_der != roundtrips_der_lax) << 15; + if (parsed_der) { + ret |= (!parsed_der_lax) << 16; + } + +#ifdef ENABLE_OPENSSL_TESTS + sig_openssl = ECDSA_SIG_new(); + sigptr = sig; + parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL); + if (parsed_openssl) { + valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256; + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + } + len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL); + if (len_openssl <= 2048) { + unsigned char *ptr = roundtrip_openssl; + CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl); + roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0); + } else { + len_openssl = 0; + } + ECDSA_SIG_free(sig_openssl); + + ret |= (parsed_der && !parsed_openssl) << 4; + ret |= (valid_der && !valid_openssl) << 5; + ret |= (roundtrips_openssl && !parsed_der) << 6; + ret |= (roundtrips_der != roundtrips_openssl) << 7; + if (roundtrips_openssl) { + ret |= (len_der != (size_t)len_openssl) << 8; + ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9; + } +#endif + return ret; +} + +static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { + size_t i; + for (i = 0; i < ptrlen; i++) { + int shift = ptrlen - 1 - i; + if (shift >= 4) { + ptr[i] = 0; + } else { + ptr[i] = (val >> shift) & 0xFF; + } + } +} + +static void damage_array(unsigned char *sig, size_t *len) { + int pos; + int action = secp256k1_rand_bits(3); + if (action < 1 && *len > 3) { + /* Delete a byte. */ + pos = secp256k1_rand_int(*len); + memmove(sig + pos, sig + pos + 1, *len - pos - 1); + (*len)--; + return; + } else if (action < 2 && *len < 2048) { + /* Insert a byte. */ + pos = secp256k1_rand_int(1 + *len); + memmove(sig + pos + 1, sig + pos, *len - pos); + sig[pos] = secp256k1_rand_bits(8); + (*len)++; + return; + } else if (action < 4) { + /* Modify a byte. */ + sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + return; + } else { /* action < 8 */ + /* Modify a bit. */ + sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + return; + } +} + +static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) { + int der; + int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2]; + size_t tlen, elen, glen; + int indet; + int n; + + *len = 0; + der = secp256k1_rand_bits(2) == 0; + *certainly_der = der; + *certainly_not_der = 0; + indet = der ? 0 : secp256k1_rand_int(10) == 0; + + for (n = 0; n < 2; n++) { + /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */ + nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0); + /* The length of the number in bytes (the first byte of which will always be nonzero) */ + nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8; + CHECK(nlen[n] <= 232); + /* The top bit of the number. */ + nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1)); + /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */ + nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127)); + /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */ + nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8); + if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { + *certainly_not_der = 1; + } + CHECK(nlen[n] + nzlen[n] <= 300); + /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */ + nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2); + if (!der) { + /* nlenlen[n] max 127 bytes */ + int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + nlenlen[n] += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427); + } + + /* The total length of the data to go, so far */ + tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1]; + CHECK(tlen <= 856); + + /* The length of the garbage inside the tuple. */ + elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8; + if (elen != 0) { + *certainly_not_der = 1; + } + tlen += elen; + CHECK(tlen <= 980); + + /* The length of the garbage after the end of the tuple. */ + glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8; + if (glen != 0) { + *certainly_not_der = 1; + } + CHECK(tlen + glen <= 990); + + /* Write the tuple header. */ + sig[(*len)++] = 0x30; + if (indet) { + /* Indeterminate length */ + sig[(*len)++] = 0x80; + *certainly_not_der = 1; + } else { + int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2); + if (!der) { + int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + tlenlen += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + if (tlenlen == 0) { + /* Short length notation */ + sig[(*len)++] = tlen; + } else { + /* Long length notation */ + sig[(*len)++] = 128 + tlenlen; + assign_big_endian(sig + *len, tlenlen, tlen); + *len += tlenlen; + } + tlen += tlenlen; + } + tlen += 2; + CHECK(tlen + glen <= 1119); + + for (n = 0; n < 2; n++) { + /* Write the integer header. */ + sig[(*len)++] = 0x02; + if (nlenlen[n] == 0) { + /* Short length notation */ + sig[(*len)++] = nlen[n] + nzlen[n]; + } else { + /* Long length notation. */ + sig[(*len)++] = 128 + nlenlen[n]; + assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]); + *len += nlenlen[n]; + } + /* Write zero padding */ + while (nzlen[n] > 0) { + sig[(*len)++] = 0x00; + nzlen[n]--; + } + if (nlen[n] == 32 && !nlow[n]) { + /* Special extra 16 0xFF bytes in "high" 32-byte numbers */ + int i; + for (i = 0; i < 16; i++) { + sig[(*len)++] = 0xFF; + } + nlen[n] -= 16; + } + /* Write first byte of number */ + if (nlen[n] > 0) { + sig[(*len)++] = nhbyte[n]; + nlen[n]--; + } + /* Generate remaining random bytes of number */ + secp256k1_rand_bytes_test(sig + *len, nlen[n]); + *len += nlen[n]; + nlen[n] = 0; + } + + /* Generate random garbage inside tuple. */ + secp256k1_rand_bytes_test(sig + *len, elen); + *len += elen; + + /* Generate end-of-contents bytes. */ + if (indet) { + sig[(*len)++] = 0; + sig[(*len)++] = 0; + tlen += 2; + } + CHECK(tlen + glen <= 1121); + + /* Generate random garbage outside tuple. */ + secp256k1_rand_bytes_test(sig + *len, glen); + *len += glen; + tlen += glen; + CHECK(tlen <= 1121); + CHECK(tlen == *len); +} + +void run_ecdsa_der_parse(void) { + int i,j; + for (i = 0; i < 200 * count; i++) { + unsigned char buffer[2048]; + size_t buflen = 0; + int certainly_der = 0; + int certainly_not_der = 0; + random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); + for (j = 0; j < 16; j++) { + int ret = 0; + if (j > 0) { + damage_array(buffer, &buflen); + /* We don't know anything anymore about the DERness of the result */ + certainly_der = 0; + certainly_not_der = 0; + } + ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der); + if (ret != 0) { + size_t k; + fprintf(stderr, "Failure %x on ", ret); + for (k = 0; k < buflen; k++) { + fprintf(stderr, "%02x ", buffer[k]); + } + fprintf(stderr, "\n"); + } + CHECK(ret == 0); + } + } +} + +/* Tests several edge cases. */ +void test_ecdsa_edge_cases(void) { + int t; + secp256k1_ecdsa_signature sig; + + /* Test the case where ECDSA recomputes a point that is infinity. */ + { + secp256k1_gej keyj; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_negate(&ss, &ss); + secp256k1_scalar_inverse(&ss, &ss); + secp256k1_scalar_set_int(&sr, 1); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sr); + secp256k1_ge_set_gej(&key, &keyj); + msg = ss; + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with r of zero fails. */ + { + const unsigned char pubkey_mods_zero[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x41 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 0); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey_mods_zero, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with s of zero fails. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 0); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 1); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with message 0 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + const unsigned char pubkey2[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x43 + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 2); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message 1 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22, + 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05, + 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c, + 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76, + 0x25 + }; + const unsigned char pubkey2[33] = { + 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40, + 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae, + 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f, + 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10, + 0x62 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message -1 passes. */ + { + const unsigned char pubkey[33] = { + 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0, + 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52, + 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27, + 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20, + 0xf1 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_negate(&msg, &msg); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_set_int(&ss, 3); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Signature where s would be zero. */ + { + secp256k1_pubkey pubkey; + size_t siglen; + int32_t ecount; + unsigned char signature[72]; + static const unsigned char nonce[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + static const unsigned char nonce2[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 + }; + const unsigned char key[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + unsigned char msg[32] = { + 0x86, 0x41, 0x99, 0x81, 0x06, 0x23, 0x44, 0x53, + 0xaa, 0x5f, 0x9d, 0x6a, 0x31, 0x78, 0xf4, 0xf7, + 0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62, + 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, + }; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); + msg[31] = 0xaa; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 7); + /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */ + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0); + CHECK(ecount == 8); + siglen = 72; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1); + CHECK(ecount == 13); + siglen = 10; + /* Too little room for a signature does not fail via ARGCHECK. */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0); + CHECK(ecount == 13); + ecount = 0; + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1); + CHECK(ecount == 5); + memset(signature, 255, 64); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0); + CHECK(ecount == 5); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + } + + /* Nonce function corner cases. */ + for (t = 0; t < 2; t++) { + static const unsigned char zero[32] = {0x00}; + int i; + unsigned char key[32]; + unsigned char msg[32]; + secp256k1_ecdsa_signature sig2; + secp256k1_scalar sr[512], ss; + const unsigned char *extra; + extra = t == 0 ? NULL : zero; + memset(msg, 0, 32); + msg[31] = 1; + /* High key results in signature failure. */ + memset(key, 0xFF, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Zero key results in signature failure. */ + memset(key, 0, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Nonce function failure results in signature failure. */ + key[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_fail, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* The retry loop successfully makes its way to the first good value. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_retry, extra) == 1); + CHECK(!is_empty_signature(&sig)); + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function is deterministic. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function changes output with different messages. */ + for(i = 0; i < 256; i++) { + int j; + msg[0] = i; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + msg[0] = 0; + msg[31] = 2; + /* The default nonce function changes output with different keys. */ + for(i = 256; i < 512; i++) { + int j; + key[0] = i - 256; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + key[0] = 0; + } + + { + /* Check that optional nonce arguments do not have equivalent effect. */ + const unsigned char zeros[32] = {0}; + unsigned char nonce[32]; + unsigned char nonce2[32]; + unsigned char nonce3[32]; + unsigned char nonce4[32]; + VG_UNDEF(nonce,32); + VG_UNDEF(nonce2,32); + VG_UNDEF(nonce3,32); + VG_UNDEF(nonce4,32); + CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); + VG_CHECK(nonce,32); + CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); + VG_CHECK(nonce2,32); + CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); + VG_CHECK(nonce3,32); + CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1); + VG_CHECK(nonce4,32); + CHECK(memcmp(nonce, nonce2, 32) != 0); + CHECK(memcmp(nonce, nonce3, 32) != 0); + CHECK(memcmp(nonce, nonce4, 32) != 0); + CHECK(memcmp(nonce2, nonce3, 32) != 0); + CHECK(memcmp(nonce2, nonce4, 32) != 0); + CHECK(memcmp(nonce3, nonce4, 32) != 0); + } + + + /* Privkey export where pubkey is the point at infinity. */ + { + unsigned char privkey[300]; + unsigned char seckey[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, + }; + size_t outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + } +} + +void run_ecdsa_edge_cases(void) { + test_ecdsa_edge_cases(); +} + +#ifdef ENABLE_OPENSSL_TESTS +EC_KEY *get_openssl_key(const unsigned char *key32) { + unsigned char privkey[300]; + size_t privkeylen; + const unsigned char* pbegin = privkey; + int compr = secp256k1_rand_bits(1); + EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); + CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr)); + CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen)); + CHECK(EC_KEY_check_key(ec_key)); + return ec_key; +} + +void test_ecdsa_openssl(void) { + secp256k1_gej qj; + secp256k1_ge q; + secp256k1_scalar sigr, sigs; + secp256k1_scalar one; + secp256k1_scalar msg2; + secp256k1_scalar key, msg; + EC_KEY *ec_key; + unsigned int sigsize = 80; + size_t secp_sigsize = 80; + unsigned char message[32]; + unsigned char signature[80]; + unsigned char key32[32]; + secp256k1_rand256_test(message); + secp256k1_scalar_set_b32(&msg, message, NULL); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(key32, &key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &qj, &key); + secp256k1_ge_set_gej(&q, &qj); + ec_key = get_openssl_key(key32); + CHECK(ec_key != NULL); + CHECK(ECDSA_sign(0, message, sizeof(message), signature, &sigsize, ec_key)); + CHECK(secp256k1_ecdsa_sig_parse(&sigr, &sigs, signature, sigsize)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg2, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg2)); + + random_sign(&sigr, &sigs, &key, &msg, NULL); + CHECK(secp256k1_ecdsa_sig_serialize(signature, &secp_sigsize, &sigr, &sigs)); + CHECK(ECDSA_verify(0, message, sizeof(message), signature, secp_sigsize, ec_key) == 1); + + EC_KEY_free(ec_key); +} + +void run_ecdsa_openssl(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_openssl(); + } +} +#endif + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/tests_impl.h" +#endif + +int main(int argc, char **argv) { + unsigned char seed16[16] = {0}; + unsigned char run32[32] = {0}; + /* find iteration count */ + if (argc > 1) { + count = strtol(argv[1], NULL, 0); + } + + /* find random seed */ + if (argc > 2) { + int pos = 0; + const char* ch = argv[2]; + while (pos < 16 && ch[0] != 0 && ch[1] != 0) { + unsigned short sh; + if (sscanf(ch, "%2hx", &sh)) { + seed16[pos] = sh; + } else { + break; + } + ch += 2; + pos++; + } + } else { + FILE *frand = fopen("/dev/urandom", "r"); + if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { + uint64_t t = time(NULL) * (uint64_t)1337; + seed16[0] ^= t; + seed16[1] ^= t >> 8; + seed16[2] ^= t >> 16; + seed16[3] ^= t >> 24; + seed16[4] ^= t >> 32; + seed16[5] ^= t >> 40; + seed16[6] ^= t >> 48; + seed16[7] ^= t >> 56; + } + fclose(frand); + } + secp256k1_rand_seed(seed16); + + printf("test count = %i\n", count); + printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); + + /* initialize */ + run_context_tests(); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (secp256k1_rand_bits(1)) { + secp256k1_rand256(run32); + CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL)); + } + + run_rand_bits(); + run_rand_int(); + + run_sha256_tests(); + run_hmac_sha256_tests(); + run_rfc6979_hmac_sha256_tests(); + +#ifndef USE_NUM_NONE + /* num tests */ + run_num_smalltests(); +#endif + + /* scalar tests */ + run_scalar_tests(); + + /* field tests */ + run_field_inv(); + run_field_inv_var(); + run_field_inv_all_var(); + run_field_misc(); + run_field_convert(); + run_sqr(); + run_sqrt(); + + /* group tests */ + run_ge(); + run_group_decompress(); + + /* ecmult tests */ + run_wnaf(); + run_point_times_order(); + run_ecmult_chain(); + run_ecmult_constants(); + run_ecmult_gen_blind(); + run_ecmult_const_tests(); + run_ec_combine(); + + /* endomorphism tests */ +#ifdef USE_ENDOMORPHISM + run_endomorphism_tests(); +#endif + + /* EC point parser test */ + run_ec_pubkey_parse_test(); + + /* EC key edge cases */ + run_eckey_edge_case_test(); + +#ifdef ENABLE_MODULE_ECDH + /* ecdh tests */ + run_ecdh_tests(); +#endif + + /* ecdsa tests */ + run_random_pubkeys(); + run_ecdsa_der_parse(); + run_ecdsa_sign_verify(); + run_ecdsa_end_to_end(); + run_ecdsa_edge_cases(); +#ifdef ENABLE_OPENSSL_TESTS + run_ecdsa_openssl(); +#endif + +#ifdef ENABLE_MODULE_SCHNORR + /* Schnorr tests */ + run_schnorr_tests(); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + /* ECDSA pubkey recovery tests */ + run_recovery_tests(); +#endif + + secp256k1_rand256(run32); + printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); + + /* shutdown */ + secp256k1_context_destroy(ctx); + + printf("no problems found\n"); + return 0; +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c new file mode 100644 index 0000000..b040bb0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/tests_exhaustive.c @@ -0,0 +1,470 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#undef USE_ECMULT_STATIC_PRECOMPUTATION + +#ifndef EXHAUSTIVE_TEST_ORDER +/* see group_impl.h for allowable values */ +#define EXHAUSTIVE_TEST_ORDER 13 +#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */ +#endif + +#include "include/secp256k1.h" +#include "group.h" +#include "secp256k1.c" +#include "testrand_impl.h" + +#ifdef ENABLE_MODULE_RECOVERY +#include "src/modules/recovery/main_impl.h" +#include "include/secp256k1_recovery.h" +#endif + +/** stolen from tests.c */ +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} +/** END stolen from tests.c */ + +int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, + const unsigned char *key32, const unsigned char *algo16, + void *data, unsigned int attempt) { + secp256k1_scalar s; + int *idata = data; + (void)msg32; + (void)key32; + (void)algo16; + /* Some nonces cannot be used because they'd cause s and/or r to be zero. + * The signing function has retry logic here that just re-calls the nonce + * function with an increased `attempt`. So if attempt > 0 this means we + * need to change the nonce to avoid an infinite loop. */ + if (attempt > 0) { + *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER; + } + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +#ifdef USE_ENDOMORPHISM +void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { + int i; + for (i = 0; i < order; i++) { + secp256k1_ge res; + secp256k1_ge_mul_lambda(&res, &group[i]); + ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + } +} +#endif + +void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j; + + /* Sanity-check (and check infinity functions) */ + CHECK(secp256k1_ge_is_infinity(&group[0])); + CHECK(secp256k1_gej_is_infinity(&groupj[0])); + for (i = 1; i < order; i++) { + CHECK(!secp256k1_ge_is_infinity(&group[i])); + CHECK(!secp256k1_gej_is_infinity(&groupj[i])); + } + + /* Check all addition formulae */ + for (j = 0; j < order; j++) { + secp256k1_fe fe_inv; + secp256k1_fe_inv(&fe_inv, &groupj[j].z); + for (i = 0; i < order; i++) { + secp256k1_ge zless_gej; + secp256k1_gej tmp; + /* add_var */ + secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_ge */ + if (j > 0) { + secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + /* add_ge_var */ + secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_zinv_var */ + zless_gej.infinity = groupj[j].infinity; + zless_gej.x = groupj[j].x; + zless_gej.y = groupj[j].y; + secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + } + + /* Check doubling */ + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + if (i > 0) { + secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + secp256k1_gej_double_var(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + + /* Check negation */ + for (i = 1; i < order; i++) { + secp256k1_ge tmp; + secp256k1_gej tmpj; + secp256k1_ge_neg(&tmp, &group[i]); + ge_equals_ge(&group[order - i], &tmp); + secp256k1_gej_neg(&tmpj, &groupj[i]); + ge_equals_gej(&group[order - i], &tmpj); + } +} + +void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j, r_log; + for (r_log = 1; r_log < order; r_log++) { + for (j = 0; j < order; j++) { + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + secp256k1_scalar na, ng; + secp256k1_scalar_set_int(&na, i); + secp256k1_scalar_set_int(&ng, j); + + secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng); + ge_equals_gej(&group[(i * r_log + j) % order], &tmp); + + if (i > 0) { + secp256k1_ecmult_const(&tmp, &group[i], &ng); + ge_equals_gej(&group[(i * j) % order], &tmp); + } + } + } + } +} + +void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { + secp256k1_fe x; + unsigned char x_bin[32]; + k %= EXHAUSTIVE_TEST_ORDER; + x = group[k].x; + secp256k1_fe_normalize(&x); + secp256k1_fe_get_b32(x_bin, &x); + secp256k1_scalar_set_b32(r, x_bin, NULL); +} + +void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* Verify by calling verify */ + secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + secp256k1_scalar_get_b32(msg32, &msg_s); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } + + /* We would like to verify zero-knowledge here by counting how often every + * possible (s, r) tuple appears, but because the group order is larger + * than the field order, when coercing the x-values to scalar values, some + * appear more often than others, so we are actually not zero-knowledge. + * (This effect also appears in the real code, but the difference is on the + * order of 1/2^128th the field order, so the deviation is not useful to a + * computationally bounded attacker.) + */ +} + +#ifdef ENABLE_MODULE_RECOVERY +void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_fe r_dot_y_normalized; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + int expected_recid; + int recid; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + /* Check directly */ + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig); + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + /* In computing the recid, there is an overflow condition that is disabled in + * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value + * will exceed the group order, and our signing code always holds out for r + * values that don't overflow, so with a proper overflow check the tests would + * loop indefinitely. */ + r_dot_y_normalized = group[k].y; + secp256k1_fe_normalize(&r_dot_y_normalized); + /* Also the recovery id is flipped depending if we hit the low-s branch */ + if ((k * s) % order == (i + r * j) % order) { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0; + } else { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1; + } + CHECK(recid == expected_recid); + + /* Convert to a standard sig then check */ + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + /* This is essentially a copy of test_exhaustive_verify, with recovery added */ + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int recid = 0; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + secp256k1_scalar_get_b32(msg32, &msg_s); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* We would like to try recovering the pubkey and checking that it matches, + * but pubkey recovery is impossible in the exhaustive tests (the reason + * being that there are 12 nonzero r values, 12 nonzero points, and no + * overlap between the sets, so there are no valid signatures). */ + + /* Verify by converting to a standard signature and calling verify */ + secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid); + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} +#endif + +int main(void) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + + /* Build context */ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* TODO set z = 1, then do num_tests runs with random z values */ + + /* Generate the entire group */ + secp256k1_gej_set_infinity(&groupj[0]); + secp256k1_ge_set_gej(&group[0], &groupj[0]); + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + /* Set a different random z-value for each Jacobian point */ + secp256k1_fe z; + random_fe(&z); + + secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g); + secp256k1_ge_set_gej(&group[i], &groupj[i]); + secp256k1_gej_rescale(&groupj[i], &z); + + /* Verify against ecmult_gen */ + { + secp256k1_scalar scalar_i; + secp256k1_gej generatedj; + secp256k1_ge generated; + + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i); + secp256k1_ge_set_gej(&generated, &generatedj); + + CHECK(group[i].infinity == 0); + CHECK(generated.infinity == 0); + CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x)); + CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y)); + } + } + + /* Run the tests */ +#ifdef USE_ENDOMORPHISM + test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER); +#endif + test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); + +#ifdef ENABLE_MODULE_RECOVERY + test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); +#endif + + secp256k1_context_destroy(ctx); + return 0; +} + diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/util.h b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/util.h new file mode 100644 index 0000000..4092a86 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1/src/util.h @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_UTIL_H_ +#define _SECP256K1_UTIL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include +#include + +typedef struct { + void (*fn)(const char *text, void* data); + const void* data; +} secp256k1_callback; + +static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { + cb->fn(text, (void*)cb->data); +} + +#ifdef DETERMINISTIC +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ +} while(0); +#else +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ + abort(); \ +} while(0) +#endif + +#ifdef HAVE_BUILTIN_EXPECT +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +#ifdef DETERMINISTIC +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed"); \ + } \ +} while(0) +#else +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed: " #cond); \ + } \ +} while(0) +#endif + +/* Like assert(), but when VERIFY is defined, and side-effect safe. */ +#if defined(COVERAGE) +#define VERIFY_CHECK(check) +#define VERIFY_SETUP(stmt) +#elif defined(VERIFY) +#define VERIFY_CHECK CHECK +#define VERIFY_SETUP(stmt) do { stmt; } while(0) +#else +#define VERIFY_CHECK(cond) do { (void)(cond); } while(0) +#define VERIFY_SETUP(stmt) +#endif + +static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { + void *ret = malloc(size); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } + return ret; +} + +/* Macro for restrict, when available and not in a VERIFY build. */ +#if defined(SECP256K1_BUILD) && defined(VERIFY) +# define SECP256K1_RESTRICT +#else +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(3,0) +# define SECP256K1_RESTRICT __restrict__ +# elif (defined(_MSC_VER) && _MSC_VER >= 1400) +# define SECP256K1_RESTRICT __restrict +# else +# define SECP256K1_RESTRICT +# endif +# else +# define SECP256K1_RESTRICT restrict +# endif +#endif + +#if defined(_WIN32) +# define I64FORMAT "I64d" +# define I64uFORMAT "I64u" +#else +# define I64FORMAT "lld" +# define I64uFORMAT "llu" +#endif + +#if defined(HAVE___INT128) +# if defined(__GNUC__) +# define SECP256K1_GNUC_EXT __extension__ +# else +# define SECP256K1_GNUC_EXT +# endif +SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/panic_cb.go b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/panic_cb.go new file mode 100644 index 0000000..e0e9034 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/panic_cb.go @@ -0,0 +1,33 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package secp256k1 + +import "C" +import "unsafe" + +// Callbacks for converting libsecp256k1 internal faults into +// recoverable Go panics. + +//export secp256k1GoPanicIllegal +func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) { + panic("illegal argument: " + C.GoString(msg)) +} + +//export secp256k1GoPanicError +func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) { + panic("internal error: " + C.GoString(msg)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256.go b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256.go new file mode 100644 index 0000000..1a152a6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256.go @@ -0,0 +1,144 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package secp256k1 wraps the bitcoin secp256k1 C library. +package secp256k1 + +/* +#cgo CFLAGS: -I./libsecp256k1 +#cgo CFLAGS: -I./libsecp256k1/src/ +#define USE_NUM_NONE +#define USE_FIELD_10X26 +#define USE_FIELD_INV_BUILTIN +#define USE_SCALAR_8X32 +#define USE_SCALAR_INV_BUILTIN +#define NDEBUG +#include "./libsecp256k1/src/secp256k1.c" +#include "./libsecp256k1/src/modules/recovery/main_impl.h" +#include "ext.h" + +typedef void (*callbackFunc) (const char* msg, void* data); +extern void secp256k1GoPanicIllegal(const char* msg, void* data); +extern void secp256k1GoPanicError(const char* msg, void* data); +*/ +import "C" + +import ( + "errors" + "math/big" + "unsafe" +) + +var context *C.secp256k1_context + +func init() { + // around 20 ms on a modern CPU. + context = C.secp256k1_context_create_sign_verify() + C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) + C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil) +} + +var ( + ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes") + ErrInvalidSignatureLen = errors.New("invalid signature length") + ErrInvalidRecoveryID = errors.New("invalid signature recovery id") + ErrInvalidKey = errors.New("invalid private key") + ErrSignFailed = errors.New("signing failed") + ErrRecoverFailed = errors.New("recovery failed") +) + +// Sign creates a recoverable ECDSA signature. +// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1. +// +// The caller is responsible for ensuring that msg cannot be chosen +// directly by an attacker. It is usually preferable to use a cryptographic +// hash function on any input before handing it to this function. +func Sign(msg []byte, seckey []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if len(seckey) != 32 { + return nil, ErrInvalidKey + } + seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0])) + if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 { + return nil, ErrInvalidKey + } + + var ( + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + noncefunc = C.secp256k1_nonce_function_rfc6979 + sigstruct C.secp256k1_ecdsa_recoverable_signature + ) + if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, nil) == 0 { + return nil, ErrSignFailed + } + + var ( + sig = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + recid C.int + ) + C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct) + sig[64] = byte(recid) // add back recid to get 65 bytes sig + return sig, nil +} + +// RecoverPubkey returns the the public key of the signer. +// msg must be the 32-byte hash of the message to be signed. +// sig must be a 65-byte compact ECDSA signature containing the +// recovery id as the last element. +func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if err := checkSignature(sig); err != nil { + return nil, err + } + + var ( + pubkey = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + ) + if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 { + return nil, ErrRecoverFailed + } + return pubkey, nil +} + +func checkSignature(sig []byte) error { + if len(sig) != 65 { + return ErrInvalidSignatureLen + } + if sig[64] >= 4 { + return ErrInvalidRecoveryID + } + return nil +} + +// reads num into buf as big-endian bytes. +func readBits(buf []byte, num *big.Int) { + const wordLen = int(unsafe.Sizeof(big.Word(0))) + i := len(buf) + for _, d := range num.Bits() { + for j := 0; j < wordLen && i > 0; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256_test.go new file mode 100644 index 0000000..287ab51 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/secp256k1/secp256_test.go @@ -0,0 +1,240 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package secp256k1 + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "testing" + + "github.com/ethereum/go-ethereum/crypto/randentropy" +) + +const TestCount = 1000 + +func generateKeyPair() (pubkey, privkey []byte) { + key, err := ecdsa.GenerateKey(S256(), rand.Reader) + if err != nil { + panic(err) + } + pubkey = elliptic.Marshal(S256(), key.X, key.Y) + privkey = make([]byte, 32) + readBits(privkey, key.D) + return pubkey, privkey +} + +func randSig() []byte { + sig := randentropy.GetEntropyCSPRNG(65) + sig[32] &= 0x70 + sig[64] %= 4 + return sig +} + +// tests for malleability +// highest bit of signature ECDSA s value must be 0, in the 33th byte +func compactSigCheck(t *testing.T, sig []byte) { + var b int = int(sig[32]) + if b < 0 { + t.Errorf("highest bit is negative: %d", b) + } + if ((b >> 7) == 1) != ((b & 0x80) == 0x80) { + t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7) + } + if (b & 0x80) == 0x80 { + t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80) + } +} + +func TestSignatureValidity(t *testing.T) { + pubkey, seckey := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Errorf("signature error: %s", err) + } + compactSigCheck(t, sig) + if len(pubkey) != 65 { + t.Errorf("pubkey length mismatch: want: 65 have: %d", len(pubkey)) + } + if len(seckey) != 32 { + t.Errorf("seckey length mismatch: want: 32 have: %d", len(seckey)) + } + if len(sig) != 65 { + t.Errorf("sig length mismatch: want: 65 have: %d", len(sig)) + } + recid := int(sig[64]) + if recid > 4 || recid < 0 { + t.Errorf("sig recid mismatch: want: within 0 to 4 have: %d", int(sig[64])) + } +} + +func TestInvalidRecoveryID(t *testing.T) { + _, seckey := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + sig, _ := Sign(msg, seckey) + sig[64] = 99 + _, err := RecoverPubkey(msg, sig) + if err != ErrInvalidRecoveryID { + t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID) + } +} + +func TestSignAndRecover(t *testing.T) { + pubkey1, seckey := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Errorf("signature error: %s", err) + } + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Errorf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } +} + +func TestSignDeterministic(t *testing.T) { + _, seckey := generateKeyPair() + msg := make([]byte, 32) + copy(msg, "hi there") + + sig1, err := Sign(msg, seckey) + if err != nil { + t.Fatal(err) + } + sig2, err := Sign(msg, seckey) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(sig1, sig2) { + t.Fatal("signatures not equal") + } +} + +func TestRandomMessagesWithSameKey(t *testing.T) { + pubkey, seckey := generateKeyPair() + keys := func() ([]byte, []byte) { + return pubkey, seckey + } + signAndRecoverWithRandomMessages(t, keys) +} + +func TestRandomMessagesWithRandomKeys(t *testing.T) { + keys := func() ([]byte, []byte) { + pubkey, seckey := generateKeyPair() + return pubkey, seckey + } + signAndRecoverWithRandomMessages(t, keys) +} + +func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)) { + for i := 0; i < TestCount; i++ { + pubkey1, seckey := keys() + msg := randentropy.GetEntropyCSPRNG(32) + sig, err := Sign(msg, seckey) + if err != nil { + t.Fatalf("signature error: %s", err) + } + if sig == nil { + t.Fatal("signature is nil") + } + compactSigCheck(t, sig) + + // TODO: why do we flip around the recovery id? + sig[len(sig)-1] %= 4 + + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if pubkey2 == nil { + t.Error("pubkey is nil") + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } + } +} + +func TestRecoveryOfRandomSignature(t *testing.T) { + pubkey1, _ := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + + for i := 0; i < TestCount; i++ { + // recovery can sometimes work, but if so should always give wrong pubkey + pubkey2, _ := RecoverPubkey(msg, randSig()) + if bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2) + } + } +} + +func TestRandomMessagesAgainstValidSig(t *testing.T) { + pubkey1, seckey := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + sig, _ := Sign(msg, seckey) + + for i := 0; i < TestCount; i++ { + msg = randentropy.GetEntropyCSPRNG(32) + pubkey2, _ := RecoverPubkey(msg, sig) + // recovery can sometimes work, but if so should always give wrong pubkey + if bytes.Equal(pubkey1, pubkey2) { + t.Fatalf("iteration: %d: pubkey mismatch: do NOT want %x: ", i, pubkey2) + } + } +} + +// Useful when the underlying libsecp256k1 API changes to quickly +// check only recover function without use of signature function +func TestRecoverSanity(t *testing.T) { + msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") + sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") + pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") + pubkey2, err := RecoverPubkey(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } +} + +func BenchmarkSign(b *testing.B) { + _, seckey := generateKeyPair() + msg := randentropy.GetEntropyCSPRNG(32) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Sign(msg, seckey) + } +} + +func BenchmarkRecover(b *testing.B) { + msg := randentropy.GetEntropyCSPRNG(32) + _, seckey := generateKeyPair() + sig, _ := Sign(msg, seckey) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + RecoverPubkey(msg, sig) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/LICENSE b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/PATENTS b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/doc.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/doc.go new file mode 100644 index 0000000..3dab530 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/doc.go @@ -0,0 +1,66 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sha3 implements the SHA-3 fixed-output-length hash functions and +// the SHAKE variable-output-length hash functions defined by FIPS-202. +// +// Both types of hash function use the "sponge" construction and the Keccak +// permutation. For a detailed specification see http://keccak.noekeon.org/ +// +// +// Guidance +// +// If you aren't sure what function you need, use SHAKE256 with at least 64 +// bytes of output. The SHAKE instances are faster than the SHA3 instances; +// the latter have to allocate memory to conform to the hash.Hash interface. +// +// If you need a secret-key MAC (message authentication code), prepend the +// secret key to the input, hash with SHAKE256 and read at least 32 bytes of +// output. +// +// +// Security strengths +// +// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security +// strength against preimage attacks of x bits. Since they only produce "x" +// bits of output, their collision-resistance is only "x/2" bits. +// +// The SHAKE-256 and -128 functions have a generic security strength of 256 and +// 128 bits against all attacks, provided that at least 2x bits of their output +// is used. Requesting more than 64 or 32 bytes of output, respectively, does +// not increase the collision-resistance of the SHAKE functions. +// +// +// The sponge construction +// +// A sponge builds a pseudo-random function from a public pseudo-random +// permutation, by applying the permutation to a state of "rate + capacity" +// bytes, but hiding "capacity" of the bytes. +// +// A sponge starts out with a zero state. To hash an input using a sponge, up +// to "rate" bytes of the input are XORed into the sponge's state. The sponge +// is then "full" and the permutation is applied to "empty" it. This process is +// repeated until all the input has been "absorbed". The input is then padded. +// The digest is "squeezed" from the sponge in the same way, except that output +// output is copied out instead of input being XORed in. +// +// A sponge is parameterized by its generic security strength, which is equal +// to half its capacity; capacity + rate is equal to the permutation's width. +// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means +// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2. +// +// +// Recommendations +// +// The SHAKE functions are recommended for most new uses. They can produce +// output of arbitrary length. SHAKE256, with an output length of at least +// 64 bytes, provides 256-bit security against all attacks. The Keccak team +// recommends it for most applications upgrading from SHA2-512. (NIST chose a +// much stronger, but much slower, sponge instance for SHA3-512.) +// +// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions. +// They produce output of the same length, with the same security strengths +// against all attacks. This means, in particular, that SHA3-256 only has +// 128-bit collision resistance, because its output length is 32 bytes. +package sha3 diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/hashes.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/hashes.go new file mode 100644 index 0000000..669cd55 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/hashes.go @@ -0,0 +1,68 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// This file provides functions for creating instances of the SHA-3 +// and SHAKE hash functions, as well as utility functions for hashing +// bytes. + +import ( + "hash" +) + +// NewKeccak256 creates a new Keccak-256 hash. +func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} } + +// New224 creates a new SHA3-224 hash. +// Its generic security strength is 224 bits against preimage attacks, +// and 112 bits against collision attacks. +func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} } + +// New256 creates a new SHA3-256 hash. +// Its generic security strength is 256 bits against preimage attacks, +// and 128 bits against collision attacks. +func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} } + +// New384 creates a new SHA3-384 hash. +// Its generic security strength is 384 bits against preimage attacks, +// and 192 bits against collision attacks. +func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} } + +// New512 creates a new SHA3-512 hash. +// Its generic security strength is 512 bits against preimage attacks, +// and 256 bits against collision attacks. +func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} } + +// Sum224 returns the SHA3-224 digest of the data. +func Sum224(data []byte) (digest [28]byte) { + h := New224() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum256 returns the SHA3-256 digest of the data. +func Sum256(data []byte) (digest [32]byte) { + h := New256() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum384 returns the SHA3-384 digest of the data. +func Sum384(data []byte) (digest [48]byte) { + h := New384() + h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum512 returns the SHA3-512 digest of the data. +func Sum512(data []byte) (digest [64]byte) { + h := New512() + h.Write(data) + h.Sum(digest[:0]) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go new file mode 100644 index 0000000..46d03ed --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf.go @@ -0,0 +1,412 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine gccgo + +package sha3 + +// rc stores the round constants for use in the ι step. +var rc = [24]uint64{ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +} + +// keccakF1600 applies the Keccak permutation to a 1600b-wide +// state represented as a slice of 25 uint64s. +func keccakF1600(a *[25]uint64) { + // Implementation translated from Keccak-inplace.c + // in the keccak reference code. + var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 + + for i := 0; i < 24; i += 4 { + // Combines the 5 steps in each round into 2 steps. + // Unrolls 4 rounds per loop and spreads some steps across rounds. + + // Round 1 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[6] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[12] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[18] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[24] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i] + a[6] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[16] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[22] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[3] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[10] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[1] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[7] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[19] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[20] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[11] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[23] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[4] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[5] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[2] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[8] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[14] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[15] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + // Round 2 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[16] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[7] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[23] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[14] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1] + a[16] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[11] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[2] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[18] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[20] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[6] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[22] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[4] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[15] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[1] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[8] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[24] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[10] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[12] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[3] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[19] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[5] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + // Round 3 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[11] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[22] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[8] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[19] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2] + a[11] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[1] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[12] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[23] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[15] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[16] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[2] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[24] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[5] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[6] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[3] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[14] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[20] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[7] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[18] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[4] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[10] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + // Round 4 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[1] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[2] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[3] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[4] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3] + a[1] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[6] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[7] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[8] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[5] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[11] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[12] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[14] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[10] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[16] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[18] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[19] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[15] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[22] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[23] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[24] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[20] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go new file mode 100644 index 0000000..de035c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!appengine,!gccgo + +package sha3 + +// This function is implemented in keccakf_amd64.s. + +//go:noescape + +func keccakF1600(state *[25]uint64) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s new file mode 100644 index 0000000..a353351 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/keccakf_amd64.s @@ -0,0 +1,392 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!appengine,!gccgo + +// This code was translated into a form compatible with 6a from the public +// domain sources at https://github.com/gvanas/KeccakCodePackage + +// Offsets in state +#define _ba (0*8) +#define _be (1*8) +#define _bi (2*8) +#define _bo (3*8) +#define _bu (4*8) +#define _ga (5*8) +#define _ge (6*8) +#define _gi (7*8) +#define _go (8*8) +#define _gu (9*8) +#define _ka (10*8) +#define _ke (11*8) +#define _ki (12*8) +#define _ko (13*8) +#define _ku (14*8) +#define _ma (15*8) +#define _me (16*8) +#define _mi (17*8) +#define _mo (18*8) +#define _mu (19*8) +#define _sa (20*8) +#define _se (21*8) +#define _si (22*8) +#define _so (23*8) +#define _su (24*8) + +// Temporary registers +#define rT1 AX + +// Round vars +#define rpState DI +#define rpStack SP + +#define rDa BX +#define rDe CX +#define rDi DX +#define rDo R8 +#define rDu R9 + +#define rBa R10 +#define rBe R11 +#define rBi R12 +#define rBo R13 +#define rBu R14 + +#define rCa SI +#define rCe BP +#define rCi rBi +#define rCo rBo +#define rCu R15 + +#define MOVQ_RBI_RCE MOVQ rBi, rCe +#define XORQ_RT1_RCA XORQ rT1, rCa +#define XORQ_RT1_RCE XORQ rT1, rCe +#define XORQ_RBA_RCU XORQ rBa, rCu +#define XORQ_RBE_RCU XORQ rBe, rCu +#define XORQ_RDU_RCU XORQ rDu, rCu +#define XORQ_RDA_RCA XORQ rDa, rCa +#define XORQ_RDE_RCE XORQ rDe, rCe + +#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \ + /* Prepare round */ \ + MOVQ rCe, rDa; \ + ROLQ $1, rDa; \ + \ + MOVQ _bi(iState), rCi; \ + XORQ _gi(iState), rDi; \ + XORQ rCu, rDa; \ + XORQ _ki(iState), rCi; \ + XORQ _mi(iState), rDi; \ + XORQ rDi, rCi; \ + \ + MOVQ rCi, rDe; \ + ROLQ $1, rDe; \ + \ + MOVQ _bo(iState), rCo; \ + XORQ _go(iState), rDo; \ + XORQ rCa, rDe; \ + XORQ _ko(iState), rCo; \ + XORQ _mo(iState), rDo; \ + XORQ rDo, rCo; \ + \ + MOVQ rCo, rDi; \ + ROLQ $1, rDi; \ + \ + MOVQ rCu, rDo; \ + XORQ rCe, rDi; \ + ROLQ $1, rDo; \ + \ + MOVQ rCa, rDu; \ + XORQ rCi, rDo; \ + ROLQ $1, rDu; \ + \ + /* Result b */ \ + MOVQ _ba(iState), rBa; \ + MOVQ _ge(iState), rBe; \ + XORQ rCo, rDu; \ + MOVQ _ki(iState), rBi; \ + MOVQ _mo(iState), rBo; \ + MOVQ _su(iState), rBu; \ + XORQ rDe, rBe; \ + ROLQ $44, rBe; \ + XORQ rDi, rBi; \ + XORQ rDa, rBa; \ + ROLQ $43, rBi; \ + \ + MOVQ rBe, rCa; \ + MOVQ rc, rT1; \ + ORQ rBi, rCa; \ + XORQ rBa, rT1; \ + XORQ rT1, rCa; \ + MOVQ rCa, _ba(oState); \ + \ + XORQ rDu, rBu; \ + ROLQ $14, rBu; \ + MOVQ rBa, rCu; \ + ANDQ rBe, rCu; \ + XORQ rBu, rCu; \ + MOVQ rCu, _bu(oState); \ + \ + XORQ rDo, rBo; \ + ROLQ $21, rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _bi(oState); \ + \ + NOTQ rBi; \ + ORQ rBa, rBu; \ + ORQ rBo, rBi; \ + XORQ rBo, rBu; \ + XORQ rBe, rBi; \ + MOVQ rBu, _bo(oState); \ + MOVQ rBi, _be(oState); \ + B_RBI_RCE; \ + \ + /* Result g */ \ + MOVQ _gu(iState), rBe; \ + XORQ rDu, rBe; \ + MOVQ _ka(iState), rBi; \ + ROLQ $20, rBe; \ + XORQ rDa, rBi; \ + ROLQ $3, rBi; \ + MOVQ _bo(iState), rBa; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDo, rBa; \ + MOVQ _me(iState), rBo; \ + MOVQ _si(iState), rBu; \ + ROLQ $28, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ga(oState); \ + G_RT1_RCA; \ + \ + XORQ rDe, rBo; \ + ROLQ $45, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ge(oState); \ + G_RT1_RCE; \ + \ + XORQ rDi, rBu; \ + ROLQ $61, rBu; \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _go(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _gu(oState); \ + NOTQ rBu; \ + G_RBA_RCU; \ + \ + ORQ rBu, rBo; \ + XORQ rBi, rBo; \ + MOVQ rBo, _gi(oState); \ + \ + /* Result k */ \ + MOVQ _be(iState), rBa; \ + MOVQ _gi(iState), rBe; \ + MOVQ _ko(iState), rBi; \ + MOVQ _mu(iState), rBo; \ + MOVQ _sa(iState), rBu; \ + XORQ rDi, rBe; \ + ROLQ $6, rBe; \ + XORQ rDo, rBi; \ + ROLQ $25, rBi; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDe, rBa; \ + ROLQ $1, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ka(oState); \ + K_RT1_RCA; \ + \ + XORQ rDu, rBo; \ + ROLQ $8, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ke(oState); \ + K_RT1_RCE; \ + \ + XORQ rDa, rBu; \ + ROLQ $18, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _ki(oState); \ + \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _ko(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _ku(oState); \ + K_RBA_RCU; \ + \ + /* Result m */ \ + MOVQ _ga(iState), rBe; \ + XORQ rDa, rBe; \ + MOVQ _ke(iState), rBi; \ + ROLQ $36, rBe; \ + XORQ rDe, rBi; \ + MOVQ _bu(iState), rBa; \ + ROLQ $10, rBi; \ + MOVQ rBe, rT1; \ + MOVQ _mi(iState), rBo; \ + ANDQ rBi, rT1; \ + XORQ rDu, rBa; \ + MOVQ _so(iState), rBu; \ + ROLQ $27, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ma(oState); \ + M_RT1_RCA; \ + \ + XORQ rDi, rBo; \ + ROLQ $15, rBo; \ + MOVQ rBi, rT1; \ + ORQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _me(oState); \ + M_RT1_RCE; \ + \ + XORQ rDo, rBu; \ + ROLQ $56, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ORQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _mi(oState); \ + \ + ORQ rBa, rBe; \ + XORQ rBu, rBe; \ + MOVQ rBe, _mu(oState); \ + \ + ANDQ rBa, rBu; \ + XORQ rBo, rBu; \ + MOVQ rBu, _mo(oState); \ + M_RBE_RCU; \ + \ + /* Result s */ \ + MOVQ _bi(iState), rBa; \ + MOVQ _go(iState), rBe; \ + MOVQ _ku(iState), rBi; \ + XORQ rDi, rBa; \ + MOVQ _ma(iState), rBo; \ + ROLQ $62, rBa; \ + XORQ rDo, rBe; \ + MOVQ _se(iState), rBu; \ + ROLQ $55, rBe; \ + \ + XORQ rDu, rBi; \ + MOVQ rBa, rDu; \ + XORQ rDe, rBu; \ + ROLQ $2, rBu; \ + ANDQ rBe, rDu; \ + XORQ rBu, rDu; \ + MOVQ rDu, _su(oState); \ + \ + ROLQ $39, rBi; \ + S_RDU_RCU; \ + NOTQ rBe; \ + XORQ rDa, rBo; \ + MOVQ rBe, rDa; \ + ANDQ rBi, rDa; \ + XORQ rBa, rDa; \ + MOVQ rDa, _sa(oState); \ + S_RDA_RCA; \ + \ + ROLQ $41, rBo; \ + MOVQ rBi, rDe; \ + ORQ rBo, rDe; \ + XORQ rBe, rDe; \ + MOVQ rDe, _se(oState); \ + S_RDE_RCE; \ + \ + MOVQ rBo, rDi; \ + MOVQ rBu, rDo; \ + ANDQ rBu, rDi; \ + ORQ rBa, rDo; \ + XORQ rBi, rDi; \ + XORQ rBo, rDo; \ + MOVQ rDi, _si(oState); \ + MOVQ rDo, _so(oState) \ + +// func keccakF1600(state *[25]uint64) +TEXT ·keccakF1600(SB), 0, $200-8 + MOVQ state+0(FP), rpState + SUBQ $(8*25), SP + + // Convert the user state into an internal state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + // Execute the KeccakF permutation + MOVQ _ba(rpState), rCa + MOVQ _be(rpState), rCe + MOVQ _bu(rpState), rCu + + XORQ _ga(rpState), rCa + XORQ _ge(rpState), rCe + XORQ _gu(rpState), rCu + + XORQ _ka(rpState), rCa + XORQ _ke(rpState), rCe + XORQ _ku(rpState), rCu + + XORQ _ma(rpState), rCa + XORQ _me(rpState), rCe + XORQ _mu(rpState), rCu + + XORQ _sa(rpState), rCa + XORQ _se(rpState), rCe + MOVQ _si(rpState), rDi + MOVQ _so(rpState), rDo + XORQ _su(rpState), rCu + + mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP) + + // Revert the internal state to the user state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + ADDQ $(8*25), SP + RET diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/register.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/register.go new file mode 100644 index 0000000..3cf6a22 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/register.go @@ -0,0 +1,18 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.4 + +package sha3 + +import ( + "crypto" +) + +func init() { + crypto.RegisterHash(crypto.SHA3_224, New224) + crypto.RegisterHash(crypto.SHA3_256, New256) + crypto.RegisterHash(crypto.SHA3_384, New384) + crypto.RegisterHash(crypto.SHA3_512, New512) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go new file mode 100644 index 0000000..c86167c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3.go @@ -0,0 +1,193 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// spongeDirection indicates the direction bytes are flowing through the sponge. +type spongeDirection int + +const ( + // spongeAbsorbing indicates that the sponge is absorbing input. + spongeAbsorbing spongeDirection = iota + // spongeSqueezing indicates that the sponge is being squeezed. + spongeSqueezing +) + +const ( + // maxRate is the maximum size of the internal buffer. SHAKE-256 + // currently needs the largest buffer. + maxRate = 168 +) + +type state struct { + // Generic sponge components. + a [25]uint64 // main state of the hash + buf []byte // points into storage + rate int // the number of bytes of state to use + + // dsbyte contains the "domain separation" bits and the first bit of + // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the + // SHA-3 and SHAKE functions by appending bitstrings to the message. + // Using a little-endian bit-ordering convention, these are "01" for SHA-3 + // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the + // padding rule from section 5.1 is applied to pad the message to a multiple + // of the rate, which involves adding a "1" bit, zero or more "0" bits, and + // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, + // giving 00000110b (0x06) and 00011111b (0x1f). + // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf + // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and + // Extendable-Output Functions (May 2014)" + dsbyte byte + storage [maxRate]byte + + // Specific to SHA-3 and SHAKE. + fixedOutput bool // whether this is a fixed-output-length instance + outputLen int // the default output size in bytes + state spongeDirection // whether the sponge is absorbing or squeezing +} + +// BlockSize returns the rate of sponge underlying this hash function. +func (d *state) BlockSize() int { return d.rate } + +// Size returns the output size of the hash function in bytes. +func (d *state) Size() int { return d.outputLen } + +// Reset clears the internal state by zeroing the sponge state and +// the byte buffer, and setting Sponge.state to absorbing. +func (d *state) Reset() { + // Zero the permutation's state. + for i := range d.a { + d.a[i] = 0 + } + d.state = spongeAbsorbing + d.buf = d.storage[:0] +} + +func (d *state) clone() *state { + ret := *d + if ret.state == spongeAbsorbing { + ret.buf = ret.storage[:len(ret.buf)] + } else { + ret.buf = ret.storage[d.rate-cap(d.buf) : d.rate] + } + + return &ret +} + +// permute applies the KeccakF-1600 permutation. It handles +// any input-output buffering. +func (d *state) permute() { + switch d.state { + case spongeAbsorbing: + // If we're absorbing, we need to xor the input into the state + // before applying the permutation. + xorIn(d, d.buf) + d.buf = d.storage[:0] + keccakF1600(&d.a) + case spongeSqueezing: + // If we're squeezing, we need to apply the permutatin before + // copying more output. + keccakF1600(&d.a) + d.buf = d.storage[:d.rate] + copyOut(d, d.buf) + } +} + +// pads appends the domain separation bits in dsbyte, applies +// the multi-bitrate 10..1 padding rule, and permutes the state. +func (d *state) padAndPermute(dsbyte byte) { + if d.buf == nil { + d.buf = d.storage[:0] + } + // Pad with this instance's domain-separator bits. We know that there's + // at least one byte of space in d.buf because, if it were full, + // permute would have been called to empty it. dsbyte also contains the + // first one bit for the padding. See the comment in the state struct. + d.buf = append(d.buf, dsbyte) + zerosStart := len(d.buf) + d.buf = d.storage[:d.rate] + for i := zerosStart; i < d.rate; i++ { + d.buf[i] = 0 + } + // This adds the final one bit for the padding. Because of the way that + // bits are numbered from the LSB upwards, the final bit is the MSB of + // the last byte. + d.buf[d.rate-1] ^= 0x80 + // Apply the permutation + d.permute() + d.state = spongeSqueezing + d.buf = d.storage[:d.rate] + copyOut(d, d.buf) +} + +// Write absorbs more data into the hash's state. It produces an error +// if more data is written to the ShakeHash after writing +func (d *state) Write(p []byte) (written int, err error) { + if d.state != spongeAbsorbing { + panic("sha3: write to sponge after read") + } + if d.buf == nil { + d.buf = d.storage[:0] + } + written = len(p) + + for len(p) > 0 { + if len(d.buf) == 0 && len(p) >= d.rate { + // The fast path; absorb a full "rate" bytes of input and apply the permutation. + xorIn(d, p[:d.rate]) + p = p[d.rate:] + keccakF1600(&d.a) + } else { + // The slow path; buffer the input until we can fill the sponge, and then xor it in. + todo := d.rate - len(d.buf) + if todo > len(p) { + todo = len(p) + } + d.buf = append(d.buf, p[:todo]...) + p = p[todo:] + + // If the sponge is full, apply the permutation. + if len(d.buf) == d.rate { + d.permute() + } + } + } + + return +} + +// Read squeezes an arbitrary number of bytes from the sponge. +func (d *state) Read(out []byte) (n int, err error) { + // If we're still absorbing, pad and apply the permutation. + if d.state == spongeAbsorbing { + d.padAndPermute(d.dsbyte) + } + + n = len(out) + + // Now, do the squeezing. + for len(out) > 0 { + n := copy(out, d.buf) + d.buf = d.buf[n:] + out = out[n:] + + // Apply the permutation if we've squeezed the sponge dry. + if len(d.buf) == 0 { + d.permute() + } + } + + return +} + +// Sum applies padding to the hash state and then squeezes out the desired +// number of output bytes. +func (d *state) Sum(in []byte) []byte { + // Make a copy of the original hash so that caller can keep writing + // and summing. + dup := d.clone() + hash := make([]byte, dup.outputLen) + dup.Read(hash) + return append(in, hash...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3_test.go new file mode 100644 index 0000000..c433761 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/sha3_test.go @@ -0,0 +1,306 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// Tests include all the ShortMsgKATs provided by the Keccak team at +// https://github.com/gvanas/KeccakCodePackage +// +// They only include the zero-bit case of the bitwise testvectors +// published by NIST in the draft of FIPS-202. + +import ( + "bytes" + "compress/flate" + "encoding/hex" + "encoding/json" + "hash" + "os" + "strings" + "testing" +) + +const ( + testString = "brekeccakkeccak koax koax" + katFilename = "testdata/keccakKats.json.deflate" +) + +// Internal-use instances of SHAKE used to test against KATs. +func newHashShake128() hash.Hash { + return &state{rate: 168, dsbyte: 0x1f, outputLen: 512} +} +func newHashShake256() hash.Hash { + return &state{rate: 136, dsbyte: 0x1f, outputLen: 512} +} + +// testDigests contains functions returning hash.Hash instances +// with output-length equal to the KAT length for both SHA-3 and +// SHAKE instances. +var testDigests = map[string]func() hash.Hash{ + "SHA3-224": New224, + "SHA3-256": New256, + "SHA3-384": New384, + "SHA3-512": New512, + "SHAKE128": newHashShake128, + "SHAKE256": newHashShake256, +} + +// testShakes contains functions that return ShakeHash instances for +// testing the ShakeHash-specific interface. +var testShakes = map[string]func() ShakeHash{ + "SHAKE128": NewShake128, + "SHAKE256": NewShake256, +} + +// decodeHex converts a hex-encoded string into a raw byte string. +func decodeHex(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +// structs used to marshal JSON test-cases. +type KeccakKats struct { + Kats map[string][]struct { + Digest string `json:"digest"` + Length int64 `json:"length"` + Message string `json:"message"` + } +} + +func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) { + xorInOrig, copyOutOrig := xorIn, copyOut + xorIn, copyOut = xorInGeneric, copyOutGeneric + testf("generic") + if xorImplementationUnaligned != "generic" { + xorIn, copyOut = xorInUnaligned, copyOutUnaligned + testf("unaligned") + } + xorIn, copyOut = xorInOrig, copyOutOrig +} + +// TestKeccakKats tests the SHA-3 and Shake implementations against all the +// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage +// (The testvectors are stored in keccakKats.json.deflate due to their length.) +func TestKeccakKats(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + // Read the KATs. + deflated, err := os.Open(katFilename) + if err != nil { + t.Errorf("error opening %s: %s", katFilename, err) + } + file := flate.NewReader(deflated) + dec := json.NewDecoder(file) + var katSet KeccakKats + err = dec.Decode(&katSet) + if err != nil { + t.Errorf("error decoding KATs: %s", err) + } + + // Do the KATs. + for functionName, kats := range katSet.Kats { + d := testDigests[functionName]() + for _, kat := range kats { + d.Reset() + in, err := hex.DecodeString(kat.Message) + if err != nil { + t.Errorf("error decoding KAT: %s", err) + } + d.Write(in[:kat.Length/8]) + got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) + if got != kat.Digest { + t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s", + functionName, impl, kat.Length, kat.Message, got, kat.Digest) + t.Logf("wanted %+v", kat) + t.FailNow() + } + continue + } + } + }) +} + +// TestUnalignedWrite tests that writing data in an arbitrary pattern with +// small input buffers. +func testUnalignedWrite(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + buf := sequentialBytes(0x10000) + for alg, df := range testDigests { + d := df() + d.Reset() + d.Write(buf) + want := d.Sum(nil) + d.Reset() + for i := 0; i < len(buf); { + // Cycle through offsets which make a 137 byte sequence. + // Because 137 is prime this sequence should exercise all corner cases. + offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} + for _, j := range offsets { + if v := len(buf) - i; v < j { + j = v + } + d.Write(buf[i : i+j]) + i += j + } + } + got := d.Sum(nil) + if !bytes.Equal(got, want) { + t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want) + } + } + }) +} + +// TestAppend checks that appending works when reallocation is necessary. +func TestAppend(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + d := New224() + + for capacity := 2; capacity <= 66; capacity += 64 { + // The first time around the loop, Sum will have to reallocate. + // The second time, it will not. + buf := make([]byte, 2, capacity) + d.Reset() + d.Write([]byte{0xcc}) + buf = d.Sum(buf) + expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" + if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { + t.Errorf("got %s, want %s", got, expected) + } + } + }) +} + +// TestAppendNoRealloc tests that appending works when no reallocation is necessary. +func TestAppendNoRealloc(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + buf := make([]byte, 1, 200) + d := New224() + d.Write([]byte{0xcc}) + buf = d.Sum(buf) + expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" + if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { + t.Errorf("%s: got %s, want %s", impl, got, expected) + } + }) +} + +// TestSqueezing checks that squeezing the full output a single time produces +// the same output as repeatedly squeezing the instance. +func TestSqueezing(t *testing.T) { + testUnalignedAndGeneric(t, func(impl string) { + for functionName, newShakeHash := range testShakes { + d0 := newShakeHash() + d0.Write([]byte(testString)) + ref := make([]byte, 32) + d0.Read(ref) + + d1 := newShakeHash() + d1.Write([]byte(testString)) + var multiple []byte + for range ref { + one := make([]byte, 1) + d1.Read(one) + multiple = append(multiple, one...) + } + if !bytes.Equal(ref, multiple) { + t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref)) + } + } + }) +} + +// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. +func sequentialBytes(size int) []byte { + result := make([]byte, size) + for i := range result { + result[i] = byte(i) + } + return result +} + +// BenchmarkPermutationFunction measures the speed of the permutation function +// with no input data. +func BenchmarkPermutationFunction(b *testing.B) { + b.SetBytes(int64(200)) + var lanes [25]uint64 + for i := 0; i < b.N; i++ { + keccakF1600(&lanes) + } +} + +// benchmarkHash tests the speed to hash num buffers of buflen each. +func benchmarkHash(b *testing.B, h hash.Hash, size, num int) { + b.StopTimer() + h.Reset() + data := sequentialBytes(size) + b.SetBytes(int64(size * num)) + b.StartTimer() + + var state []byte + for i := 0; i < b.N; i++ { + for j := 0; j < num; j++ { + h.Write(data) + } + state = h.Sum(state[:0]) + } + b.StopTimer() + h.Reset() +} + +// benchmarkShake is specialized to the Shake instances, which don't +// require a copy on reading output. +func benchmarkShake(b *testing.B, h ShakeHash, size, num int) { + b.StopTimer() + h.Reset() + data := sequentialBytes(size) + d := make([]byte, 32) + + b.SetBytes(int64(size * num)) + b.StartTimer() + + for i := 0; i < b.N; i++ { + h.Reset() + for j := 0; j < num; j++ { + h.Write(data) + } + h.Read(d) + } +} + +func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) } +func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) } +func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) } +func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) } + +func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) } +func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) } +func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) } +func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) } + +func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) } + +func Example_sum() { + buf := []byte("some data to hash") + // A hash needs to be 64 bytes long to have 256-bit collision resistance. + h := make([]byte, 64) + // Compute a 64-byte hash of buf and put it in h. + ShakeSum256(h, buf) +} + +func Example_mac() { + k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long") + buf := []byte("and this is some data to authenticate") + // A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key. + h := make([]byte, 32) + d := NewShake256() + // Write the key into the hash. + d.Write(k) + // Now write the data. + d.Write(buf) + // Read 32 bytes of output from the hash into h. + d.Read(h) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/shake.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/shake.go new file mode 100644 index 0000000..841f986 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/shake.go @@ -0,0 +1,60 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// This file defines the ShakeHash interface, and provides +// functions for creating SHAKE instances, as well as utility +// functions for hashing bytes to arbitrary-length output. + +import ( + "io" +) + +// ShakeHash defines the interface to hash functions that +// support arbitrary-length output. +type ShakeHash interface { + // Write absorbs more data into the hash's state. It panics if input is + // written to it after output has been read from it. + io.Writer + + // Read reads more output from the hash; reading affects the hash's + // state. (ShakeHash.Read is thus very different from Hash.Sum) + // It never returns an error. + io.Reader + + // Clone returns a copy of the ShakeHash in its current state. + Clone() ShakeHash + + // Reset resets the ShakeHash to its initial state. + Reset() +} + +func (d *state) Clone() ShakeHash { + return d.clone() +} + +// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 128 bits against all attacks if at +// least 32 bytes of its output are used. +func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} } + +// NewShake256 creates a new SHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 256 bits against all attacks if +// at least 64 bytes of its output are used. +func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} } + +// ShakeSum128 writes an arbitrary-length digest of data into hash. +func ShakeSum128(hash, data []byte) { + h := NewShake128() + h.Write(data) + h.Read(hash) +} + +// ShakeSum256 writes an arbitrary-length digest of data into hash. +func ShakeSum256(hash, data []byte) { + h := NewShake256() + h.Write(data) + h.Read(hash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/testdata/keccakKats.json.deflate b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/testdata/keccakKats.json.deflate new file mode 100644 index 0000000000000000000000000000000000000000..62e85ae24236b46c09e5cfa84c71c69f5cc33cf6 GIT binary patch literal 521342 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%0DdSPIES;LF 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

;TQ8)#7l>4Snz*J)FMZ0Q-2>;ACWq)?04 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 z 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

nJLYnt<$PQi86UnxvK+ceSbB#mC5$ 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go new file mode 100644 index 0000000..46a0d63 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64,!386,!ppc64le appengine + +package sha3 + +var ( + xorIn = xorInGeneric + copyOut = copyOutGeneric + xorInUnaligned = xorInGeneric + copyOutUnaligned = copyOutGeneric +) + +const xorImplementationUnaligned = "generic" diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_generic.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_generic.go new file mode 100644 index 0000000..fd35f02 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_generic.go @@ -0,0 +1,28 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +import "encoding/binary" + +// xorInGeneric xors the bytes in buf into the state; it +// makes no non-portable assumptions about memory layout +// or alignment. +func xorInGeneric(d *state, buf []byte) { + n := len(buf) / 8 + + for i := 0; i < n; i++ { + a := binary.LittleEndian.Uint64(buf) + d.a[i] ^= a + buf = buf[8:] + } +} + +// copyOutGeneric copies ulint64s to a byte buffer. +func copyOutGeneric(d *state, b []byte) { + for i := 0; len(b) >= 8; i++ { + binary.LittleEndian.PutUint64(b, d.a[i]) + b = b[8:] + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go new file mode 100644 index 0000000..929a486 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/sha3/xor_unaligned.go @@ -0,0 +1,58 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 386 ppc64le +// +build !appengine + +package sha3 + +import "unsafe" + +func xorInUnaligned(d *state, buf []byte) { + bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0])) + n := len(buf) + if n >= 72 { + d.a[0] ^= bw[0] + d.a[1] ^= bw[1] + d.a[2] ^= bw[2] + d.a[3] ^= bw[3] + d.a[4] ^= bw[4] + d.a[5] ^= bw[5] + d.a[6] ^= bw[6] + d.a[7] ^= bw[7] + d.a[8] ^= bw[8] + } + if n >= 104 { + d.a[9] ^= bw[9] + d.a[10] ^= bw[10] + d.a[11] ^= bw[11] + d.a[12] ^= bw[12] + } + if n >= 136 { + d.a[13] ^= bw[13] + d.a[14] ^= bw[14] + d.a[15] ^= bw[15] + d.a[16] ^= bw[16] + } + if n >= 144 { + d.a[17] ^= bw[17] + } + if n >= 168 { + d.a[18] ^= bw[18] + d.a[19] ^= bw[19] + d.a[20] ^= bw[20] + } +} + +func copyOutUnaligned(d *state, buf []byte) { + ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) + copy(buf, ab[:]) +} + +var ( + xorIn = xorInUnaligned + copyOut = copyOutUnaligned +) + +const xorImplementationUnaligned = "unaligned" diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go b/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go new file mode 100644 index 0000000..5faa606 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go @@ -0,0 +1,64 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !nacl,!js,!nocgo + +package crypto + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +func Ecrecover(hash, sig []byte) ([]byte, error) { + return secp256k1.RecoverPubkey(hash, sig) +} + +func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { + s, err := Ecrecover(hash, sig) + if err != nil { + return nil, err + } + + x, y := elliptic.Unmarshal(S256(), s) + return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil +} + +// Sign calculates an ECDSA signature. +// +// This function is susceptible to chosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be chosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// The produced signature is in the [R || S || V] format where V is 0 or 1. +func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + if len(hash) != 32 { + return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) + } + seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8) + defer zeroBytes(seckey) + return secp256k1.Sign(hash, seckey) +} + +// S256 returns an instance of the secp256k1 curve. +func S256() elliptic.Curve { + return secp256k1.S256() +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go b/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go new file mode 100644 index 0000000..47880aa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go @@ -0,0 +1,77 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build nacl js nocgo + +package crypto + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/btcsuite/btcd/btcec" +) + +func Ecrecover(hash, sig []byte) ([]byte, error) { + pub, err := SigToPub(hash, sig) + if err != nil { + return nil, err + } + bytes := (*btcec.PublicKey)(pub).SerializeUncompressed() + return bytes, err +} + +func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { + // Convert to btcec input format with 'recovery id' v at the beginning. + btcsig := make([]byte, 65) + btcsig[0] = sig[64] + 27 + copy(btcsig[1:], sig) + + pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash) + return (*ecdsa.PublicKey)(pub), err +} + +// Sign calculates an ECDSA signature. +// +// This function is susceptible to chosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be chosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// The produced signature is in the [R || S || V] format where V is 0 or 1. +func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { + if len(hash) != 32 { + return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) + } + if prv.Curve != btcec.S256() { + return nil, fmt.Errorf("private key curve is not secp256k1") + } + sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false) + if err != nil { + return nil, err + } + // Convert to Ethereum signature format with 'recovery id' v at the end. + v := sig[0] - 27 + copy(sig, sig[1:]) + sig[64] = v + return sig, nil +} + +// S256 returns an instance of the secp256k1 curve. +func S256() elliptic.Curve { + return btcec.S256() +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/signature_test.go b/vendor/github.com/ethereum/go-ethereum/crypto/signature_test.go new file mode 100644 index 0000000..ca7eefe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/signature_test.go @@ -0,0 +1,36 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package crypto + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestRecoverSanity(t *testing.T) { + msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") + sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") + pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") + pubkey2, err := Ecrecover(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/errs/errors.go b/vendor/github.com/ethereum/go-ethereum/errs/errors.go new file mode 100644 index 0000000..daa814d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/errs/errors.go @@ -0,0 +1,88 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package errs + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +Errors implements an error handler providing standardised errors for a package. +Fields: + + Errors: + a map from error codes to description + + Package: + name of the package/component +*/ +type Errors struct { + Errors map[int]string + Package string +} + +/* +Error implements the standard go error interface. + + errors.New(code, format, params ...interface{}) + +Prints as: + + [package] description: details + +where details is fmt.Sprintf(self.format, self.params...) +*/ +type Error struct { + Code int + Name string + Package string + message string + format string + params []interface{} +} + +func (self *Errors) New(code int, format string, params ...interface{}) *Error { + name, ok := self.Errors[code] + if !ok { + panic("invalid error code") + } + return &Error{ + Code: code, + Name: name, + Package: self.Package, + format: format, + params: params, + } +} + +func (self Error) Error() (message string) { + if len(message) == 0 { + self.message = fmt.Sprintf("[%s] ERROR: %s", self.Package, self.Name) + if self.format != "" { + self.message += ": " + fmt.Sprintf(self.format, self.params...) + } + } + return self.message +} + +func (self Error) Log(v glog.Verbose) { + if v { + v.Infoln(self) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/errs/errors_test.go b/vendor/github.com/ethereum/go-ethereum/errs/errors_test.go new file mode 100644 index 0000000..5a2ffbe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/errs/errors_test.go @@ -0,0 +1,41 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package errs + +import ( + "fmt" + "testing" +) + +func testErrors() *Errors { + return &Errors{ + Package: "TEST", + Errors: map[int]string{ + 0: "zero", + 1: "one", + }, + } +} + +func TestErrorMessage(t *testing.T) { + err := testErrors().New(0, "zero detail %v", "available") + message := fmt.Sprintf("%v", err) + exp := "[TEST] ERROR: zero: zero detail available" + if message != exp { + t.Errorf("error message incorrect. expected %v, got %v", exp, message) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go new file mode 100644 index 0000000..f38c0a6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -0,0 +1,574 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "bytes" + "compress/gzip" + "errors" + "fmt" + "io" + "io/ioutil" + "math/big" + "os" + "runtime" + "strings" + "time" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +const defaultTraceTimeout = 5 * time.Second + +// PublicEthereumAPI provides an API to access Ethereum full node-related +// information. +type PublicEthereumAPI struct { + e *Ethereum +} + +// NewPublicEthereumAPI creates a new Etheruem protocol API for full nodes. +func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI { + return &PublicEthereumAPI{e} +} + +// Etherbase is the address that mining rewards will be send to +func (s *PublicEthereumAPI) Etherbase() (common.Address, error) { + return s.e.Etherbase() +} + +// Coinbase is the address that mining rewards will be send to (alias for Etherbase) +func (s *PublicEthereumAPI) Coinbase() (common.Address, error) { + return s.Etherbase() +} + +// Hashrate returns the POW hashrate +func (s *PublicEthereumAPI) Hashrate() hexutil.Uint64 { + return hexutil.Uint64(s.e.Miner().HashRate()) +} + +// PublicMinerAPI provides an API to control the miner. +// It offers only methods that operate on data that pose no security risk when it is publicly accessible. +type PublicMinerAPI struct { + e *Ethereum + agent *miner.RemoteAgent +} + +// NewPublicMinerAPI create a new PublicMinerAPI instance. +func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI { + agent := miner.NewRemoteAgent(e.Pow()) + e.Miner().Register(agent) + + return &PublicMinerAPI{e, agent} +} + +// Mining returns an indication if this node is currently mining. +func (s *PublicMinerAPI) Mining() bool { + return s.e.IsMining() +} + +// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was +// accepted. Note, this is not an indication if the provided work was valid! +func (s *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool { + return s.agent.SubmitWork(nonce, digest, solution) +} + +// GetWork returns a work package for external miner. The work package consists of 3 strings +// result[0], 32 bytes hex encoded current block header pow-hash +// result[1], 32 bytes hex encoded seed hash used for DAG +// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty +func (s *PublicMinerAPI) GetWork() (work [3]string, err error) { + if !s.e.IsMining() { + if err := s.e.StartMining(0); err != nil { + return work, err + } + } + if work, err = s.agent.GetWork(); err == nil { + return + } + glog.V(logger.Debug).Infof("%v", err) + return work, fmt.Errorf("mining not ready") +} + +// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined +// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which +// must be unique between nodes. +func (s *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool { + s.agent.SubmitHashrate(id, uint64(hashrate)) + return true +} + +// PrivateMinerAPI provides private RPC methods to control the miner. +// These methods can be abused by external users and must be considered insecure for use by untrusted users. +type PrivateMinerAPI struct { + e *Ethereum +} + +// NewPrivateMinerAPI create a new RPC service which controls the miner of this node. +func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI { + return &PrivateMinerAPI{e: e} +} + +// Start the miner with the given number of threads. If threads is nil the number of +// workers started is equal to the number of logical CPU's that are usable by this process. +func (s *PrivateMinerAPI) Start(threads *int) (bool, error) { + s.e.StartAutoDAG() + var err error + if threads == nil { + err = s.e.StartMining(runtime.NumCPU()) + } else { + err = s.e.StartMining(*threads) + } + return err == nil, err +} + +// Stop the miner +func (s *PrivateMinerAPI) Stop() bool { + s.e.StopMining() + return true +} + +// SetExtra sets the extra data string that is included when this miner mines a block. +func (s *PrivateMinerAPI) SetExtra(extra string) (bool, error) { + if err := s.e.Miner().SetExtra([]byte(extra)); err != nil { + return false, err + } + return true, nil +} + +// SetGasPrice sets the minimum accepted gas price for the miner. +func (s *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { + s.e.Miner().SetGasPrice((*big.Int)(&gasPrice)) + return true +} + +// SetEtherbase sets the etherbase of the miner +func (s *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool { + s.e.SetEtherbase(etherbase) + return true +} + +// StartAutoDAG starts auto DAG generation. This will prevent the DAG generating on epoch change +// which will cause the node to stop mining during the generation process. +func (s *PrivateMinerAPI) StartAutoDAG() bool { + s.e.StartAutoDAG() + return true +} + +// StopAutoDAG stops auto DAG generation +func (s *PrivateMinerAPI) StopAutoDAG() bool { + s.e.StopAutoDAG() + return true +} + +// MakeDAG creates the new DAG for the given block number +func (s *PrivateMinerAPI) MakeDAG(blockNr rpc.BlockNumber) (bool, error) { + if err := ethash.MakeDAG(uint64(blockNr.Int64()), ""); err != nil { + return false, err + } + return true, nil +} + +// PrivateAdminAPI is the collection of Etheruem full node-related APIs +// exposed over the private admin endpoint. +type PrivateAdminAPI struct { + eth *Ethereum +} + +// NewPrivateAdminAPI creates a new API definition for the full node private +// admin methods of the Ethereum service. +func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI { + return &PrivateAdminAPI{eth: eth} +} + +// ExportChain exports the current blockchain into a local file. +func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) { + // Make sure we can create the file to export into + out, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + return false, err + } + defer out.Close() + + var writer io.Writer = out + if strings.HasSuffix(file, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + + // Export the blockchain + if err := api.eth.BlockChain().Export(writer); err != nil { + return false, err + } + return true, nil +} + +func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { + for _, b := range bs { + if !chain.HasBlock(b.Hash()) { + return false + } + } + + return true +} + +// ImportChain imports a blockchain from a local file. +func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { + // Make sure the can access the file to import + in, err := os.Open(file) + if err != nil { + return false, err + } + defer in.Close() + + var reader io.Reader = in + if strings.HasSuffix(file, ".gz") { + if reader, err = gzip.NewReader(reader); err != nil { + return false, err + } + } + + // Run actual the import in pre-configured batches + stream := rlp.NewStream(reader, 0) + + blocks, index := make([]*types.Block, 0, 2500), 0 + for batch := 0; ; batch++ { + // Load a batch of blocks from the input file + for len(blocks) < cap(blocks) { + block := new(types.Block) + if err := stream.Decode(block); err == io.EOF { + break + } else if err != nil { + return false, fmt.Errorf("block %d: failed to parse: %v", index, err) + } + blocks = append(blocks, block) + index++ + } + if len(blocks) == 0 { + break + } + + if hasAllBlocks(api.eth.BlockChain(), blocks) { + blocks = blocks[:0] + continue + } + // Import the batch and reset the buffer + if _, err := api.eth.BlockChain().InsertChain(blocks); err != nil { + return false, fmt.Errorf("batch %d: failed to insert: %v", batch, err) + } + blocks = blocks[:0] + } + return true, nil +} + +// PublicDebugAPI is the collection of Etheruem full node APIs exposed +// over the public debugging endpoint. +type PublicDebugAPI struct { + eth *Ethereum +} + +// NewPublicDebugAPI creates a new API definition for the full node- +// related public debug methods of the Ethereum service. +func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI { + return &PublicDebugAPI{eth: eth} +} + +// DumpBlock retrieves the entire state of the database at a given block. +func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { + block := api.eth.BlockChain().GetBlockByNumber(number) + if block == nil { + return state.Dump{}, fmt.Errorf("block #%d not found", number) + } + stateDb, err := api.eth.BlockChain().StateAt(block.Root()) + if err != nil { + return state.Dump{}, err + } + return stateDb.RawDump(), nil +} + +// PrivateDebugAPI is the collection of Etheruem full node APIs exposed over +// the private debugging endpoint. +type PrivateDebugAPI struct { + config *params.ChainConfig + eth *Ethereum +} + +// NewPrivateDebugAPI creates a new API definition for the full node-related +// private debug methods of the Ethereum service. +func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI { + return &PrivateDebugAPI{config: config, eth: eth} +} + +// BlockTraceResult is the returned value when replaying a block to check for +// consensus results and full VM trace logs for all included transactions. +type BlockTraceResult struct { + Validated bool `json:"validated"` + StructLogs []ethapi.StructLogRes `json:"structLogs"` + Error string `json:"error"` +} + +// TraceArgs holds extra parameters to trace functions +type TraceArgs struct { + *vm.LogConfig + Tracer *string + Timeout *string +} + +// TraceBlock processes the given block's RLP but does not import the block in to +// the chain. +func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult { + var block types.Block + err := rlp.Decode(bytes.NewReader(blockRlp), &block) + if err != nil { + return BlockTraceResult{Error: fmt.Sprintf("could not decode block: %v", err)} + } + + validated, logs, err := api.traceBlock(&block, config) + return BlockTraceResult{ + Validated: validated, + StructLogs: ethapi.FormatLogs(logs), + Error: formatError(err), + } +} + +// TraceBlockFromFile loads the block's RLP from the given file name and attempts to +// process it but does not import the block in to the chain. +func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult { + blockRlp, err := ioutil.ReadFile(file) + if err != nil { + return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)} + } + return api.TraceBlock(blockRlp, config) +} + +// TraceBlockByNumber processes the block by canonical block number. +func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult { + // Fetch the block that we aim to reprocess + block := api.eth.BlockChain().GetBlockByNumber(number) + if block == nil { + return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", number)} + } + + validated, logs, err := api.traceBlock(block, config) + return BlockTraceResult{ + Validated: validated, + StructLogs: ethapi.FormatLogs(logs), + Error: formatError(err), + } +} + +// TraceBlockByHash processes the block by hash. +func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.LogConfig) BlockTraceResult { + // Fetch the block that we aim to reprocess + block := api.eth.BlockChain().GetBlockByHash(hash) + if block == nil { + return BlockTraceResult{Error: fmt.Sprintf("block #%x not found", hash)} + } + + validated, logs, err := api.traceBlock(block, config) + return BlockTraceResult{ + Validated: validated, + StructLogs: ethapi.FormatLogs(logs), + Error: formatError(err), + } +} + +// traceBlock processes the given block but does not save the state. +func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConfig) (bool, []vm.StructLog, error) { + // Validate and reprocess the block + var ( + blockchain = api.eth.BlockChain() + validator = blockchain.Validator() + processor = blockchain.Processor() + ) + + structLogger := vm.NewStructLogger(logConfig) + + config := vm.Config{ + Debug: true, + Tracer: structLogger, + } + + if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { + return false, structLogger.StructLogs(), err + } + statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) + if err != nil { + return false, structLogger.StructLogs(), err + } + + receipts, _, usedGas, err := processor.Process(block, statedb, config) + if err != nil { + return false, structLogger.StructLogs(), err + } + if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas); err != nil { + return false, structLogger.StructLogs(), err + } + return true, structLogger.StructLogs(), nil +} + +// callmsg is the message type used for call transitions. +type callmsg struct { + addr common.Address + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() (common.Address, error) { return m.addr, nil } +func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.to } +func (m callmsg) GasPrice() *big.Int { return m.gasPrice } +func (m callmsg) Gas() *big.Int { return m.gas } +func (m callmsg) Value() *big.Int { return m.value } +func (m callmsg) Data() []byte { return m.data } + +// formatError formats a Go error into either an empty string or the data content +// of the error itself. +func formatError(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +type timeoutError struct{} + +func (t *timeoutError) Error() string { + return "Execution time exceeded" +} + +// TraceTransaction returns the structured logs created during the execution of EVM +// and returns them as a JSON object. +func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.Hash, config *TraceArgs) (interface{}, error) { + var tracer vm.Tracer + if config != nil && config.Tracer != nil { + timeout := defaultTraceTimeout + if config.Timeout != nil { + var err error + if timeout, err = time.ParseDuration(*config.Timeout); err != nil { + return nil, err + } + } + + var err error + if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil { + return nil, err + } + + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { + <-deadlineCtx.Done() + tracer.(*ethapi.JavascriptTracer).Stop(&timeoutError{}) + }() + defer cancel() + } else if config == nil { + tracer = vm.NewStructLogger(nil) + } else { + tracer = vm.NewStructLogger(config.LogConfig) + } + + // Retrieve the tx from the chain and the containing block + tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash) + if tx == nil { + return nil, fmt.Errorf("transaction %x not found", txHash) + } + block := api.eth.BlockChain().GetBlockByHash(blockHash) + if block == nil { + return nil, fmt.Errorf("block %x not found", blockHash) + } + // Create the state database to mutate and eventually trace + parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return nil, fmt.Errorf("block parent %x not found", block.ParentHash()) + } + stateDb, err := api.eth.BlockChain().StateAt(parent.Root()) + if err != nil { + return nil, err + } + + signer := types.MakeSigner(api.config, block.Number()) + // Mutate the state and trace the selected transaction + for idx, tx := range block.Transactions() { + // Assemble the transaction call message + msg, err := tx.AsMessage(signer) + if err != nil { + return nil, fmt.Errorf("sender retrieval failed: %v", err) + } + context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain()) + + // Mutate the state if we haven't reached the tracing transaction yet + if uint64(idx) < txIndex { + vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{}) + _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { + return nil, fmt.Errorf("mutation failed: %v", err) + } + stateDb.DeleteSuicides() + continue + } + + vmenv := vm.NewEVM(context, stateDb, api.config, vm.Config{Debug: true, Tracer: tracer}) + ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { + return nil, fmt.Errorf("tracing failed: %v", err) + } + + switch tracer := tracer.(type) { + case *vm.StructLogger: + return ðapi.ExecutionResult{ + Gas: gas, + ReturnValue: fmt.Sprintf("%x", ret), + StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + }, nil + case *ethapi.JavascriptTracer: + return tracer.GetResult() + } + } + return nil, errors.New("database inconsistency") +} + +// Preimage is a debug API function that returns the preimage for a sha3 hash, if known. +func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { + db := core.PreimageTable(api.eth.ChainDb()) + return db.Get(hash.Bytes()) +} + +// GetBadBLocks returns a list of the last 'bad blocks' that the client has seen on the network +// and returns them as a JSON list of block-hashes +func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]core.BadBlockArgs, error) { + return api.eth.BlockChain().BadBlocks() +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go new file mode 100644 index 0000000..72ed76c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -0,0 +1,220 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// EthApiBackend implements ethapi.Backend for full nodes +type EthApiBackend struct { + eth *Ethereum + gpo *gasprice.GasPriceOracle +} + +func (b *EthApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *EthApiBackend) CurrentBlock() *types.Block { + return b.eth.blockchain.CurrentBlock() +} + +func (b *EthApiBackend) SetHead(number uint64) { + b.eth.blockchain.SetHead(number) +} + +func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { + // Pending block is only known by the miner + if blockNr == rpc.PendingBlockNumber { + block := b.eth.miner.PendingBlock() + return block.Header(), nil + } + // Otherwise resolve and return the block + if blockNr == rpc.LatestBlockNumber { + return b.eth.blockchain.CurrentBlock().Header(), nil + } + return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil +} + +func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { + // Pending block is only known by the miner + if blockNr == rpc.PendingBlockNumber { + block := b.eth.miner.PendingBlock() + return block, nil + } + // Otherwise resolve and return the block + if blockNr == rpc.LatestBlockNumber { + return b.eth.blockchain.CurrentBlock(), nil + } + return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil +} + +func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { + // Pending state is only known by the miner + if blockNr == rpc.PendingBlockNumber { + block, state := b.eth.miner.Pending() + return EthApiState{state}, block.Header(), nil + } + // Otherwise resolve the block number and return its state + header, err := b.HeaderByNumber(ctx, blockNr) + if header == nil || err != nil { + return nil, nil, err + } + stateDb, err := b.eth.BlockChain().StateAt(header.Root) + return EthApiState{stateDb}, header, err +} + +func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { + return b.eth.blockchain.GetBlockByHash(blockHash), nil +} + +func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { + return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil +} + +func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { + return b.eth.blockchain.GetTdByHash(blockHash) +} + +func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) { + statedb := state.(EthApiState).state + from := statedb.GetOrNewStateObject(msg.From()) + from.SetBalance(common.MaxBig) + vmError := func() error { return nil } + + context := core.NewEVMContext(msg, header, b.eth.BlockChain()) + return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil +} + +func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + b.eth.txPool.SetLocal(signedTx) + return b.eth.txPool.Add(signedTx) +} + +func (b *EthApiBackend) RemoveTx(txHash common.Hash) { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + b.eth.txPool.Remove(txHash) +} + +func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + pending, err := b.eth.txPool.Pending() + if err != nil { + return nil, err + } + + var txs types.Transactions + for _, batch := range pending { + txs = append(txs, batch...) + } + return txs, nil +} + +func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + return b.eth.txPool.Get(hash) +} + +func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + return b.eth.txPool.State().GetNonce(addr), nil +} + +func (b *EthApiBackend) Stats() (pending int, queued int) { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + return b.eth.txPool.Stats() +} + +func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + b.eth.txMu.Lock() + defer b.eth.txMu.Unlock() + + return b.eth.TxPool().Content() +} + +func (b *EthApiBackend) Downloader() *downloader.Downloader { + return b.eth.Downloader() +} + +func (b *EthApiBackend) ProtocolVersion() int { + return b.eth.EthVersion() +} + +func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { + return b.gpo.SuggestPrice(), nil +} + +func (b *EthApiBackend) ChainDb() ethdb.Database { + return b.eth.ChainDb() +} + +func (b *EthApiBackend) EventMux() *event.TypeMux { + return b.eth.EventMux() +} + +func (b *EthApiBackend) AccountManager() *accounts.Manager { + return b.eth.AccountManager() +} + +type EthApiState struct { + state *state.StateDB +} + +func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { + return s.state.GetBalance(addr), nil +} + +func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { + return s.state.GetCode(addr), nil +} + +func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { + return s.state.GetState(a, b), nil +} + +func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { + return s.state.GetNonce(addr), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go new file mode 100644 index 0000000..ef3ac93 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -0,0 +1,528 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package eth implements the Ethereum protocol. +package eth + +import ( + "errors" + "fmt" + "math/big" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rpc" +) + +const ( + epochLength = 30000 + ethashRevision = 23 + + autoDAGcheckInterval = 10 * time.Hour + autoDAGepochHeight = epochLength / 2 +) + +var ( + datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true} + portInUseErrRE = regexp.MustCompile("address already in use") +) + +type Config struct { + ChainConfig *params.ChainConfig // chain configuration + + NetworkId int // Network ID to use for selecting peers to connect to + Genesis string // Genesis JSON to seed the chain database with + FastSync bool // Enables the state download based fast synchronisation algorithm + LightMode bool // Running in light client mode + LightServ int // Maximum percentage of time allowed for serving LES requests + LightPeers int // Maximum number of LES client peers + MaxPeers int // Maximum number of global peers + + SkipBcVersionCheck bool // e.g. blockchain export + DatabaseCache int + DatabaseHandles int + + DocRoot string + AutoDAG bool + PowFake bool + PowTest bool + PowShared bool + ExtraData []byte + + Etherbase common.Address + GasPrice *big.Int + MinerThreads int + SolcPath string + + GpoMinGasPrice *big.Int + GpoMaxGasPrice *big.Int + GpoFullBlockRatio int + GpobaseStepDown int + GpobaseStepUp int + GpobaseCorrectionFactor int + + EnablePreimageRecording bool + + TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!) + TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) +} + +type LesServer interface { + Start(srvr *p2p.Server) + Stop() + Protocols() []p2p.Protocol +} + +// Ethereum implements the Ethereum full node service. +type Ethereum struct { + chainConfig *params.ChainConfig + // Channel for shutting down the service + shutdownChan chan bool // Channel for shutting down the ethereum + stopDbUpgrade func() // stop chain db sequential key upgrade + // Handlers + txPool *core.TxPool + txMu sync.Mutex + blockchain *core.BlockChain + protocolManager *ProtocolManager + lesServer LesServer + // DB interfaces + chainDb ethdb.Database // Block chain database + + eventMux *event.TypeMux + pow pow.PoW + accountManager *accounts.Manager + + ApiBackend *EthApiBackend + + miner *miner.Miner + Mining bool + MinerThreads int + AutoDAG bool + autodagquit chan bool + etherbase common.Address + solcPath string + + netVersionId int + netRPCService *ethapi.PublicNetAPI +} + +func (s *Ethereum) AddLesServer(ls LesServer) { + s.lesServer = ls + s.protocolManager.lesServer = ls +} + +// New creates a new Ethereum object (including the +// initialisation of the common Ethereum object) +func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { + chainDb, err := CreateDB(ctx, config, "chaindata") + if err != nil { + return nil, err + } + stopDbUpgrade := upgradeSequentialKeys(chainDb) + if err := SetupGenesisBlock(&chainDb, config); err != nil { + return nil, err + } + pow, err := CreatePoW(config) + if err != nil { + return nil, err + } + + eth := &Ethereum{ + chainDb: chainDb, + eventMux: ctx.EventMux, + accountManager: ctx.AccountManager, + pow: pow, + shutdownChan: make(chan bool), + stopDbUpgrade: stopDbUpgrade, + netVersionId: config.NetworkId, + etherbase: config.Etherbase, + MinerThreads: config.MinerThreads, + AutoDAG: config.AutoDAG, + solcPath: config.SolcPath, + } + + if err := upgradeChainDatabase(chainDb); err != nil { + return nil, err + } + if err := addMipmapBloomBins(chainDb); err != nil { + return nil, err + } + + glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId) + + if !config.SkipBcVersionCheck { + bcVersion := core.GetBlockChainVersion(chainDb) + if bcVersion != core.BlockChainVersion && bcVersion != 0 { + return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion) + } + core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) + } + + // load the genesis block or write a new one if no genesis + // block is prenent in the database. + genesis := core.GetBlock(chainDb, core.GetCanonicalHash(chainDb, 0), 0) + if genesis == nil { + genesis, err = core.WriteDefaultGenesisBlock(chainDb) + if err != nil { + return nil, err + } + glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") + } + + if config.ChainConfig == nil { + return nil, errors.New("missing chain config") + } + core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) + + eth.chainConfig = config.ChainConfig + + glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig) + + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux(), vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}) + if err != nil { + if err == core.ErrNoGenesis { + return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) + } + return nil, err + } + newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) + eth.txPool = newPool + + maxPeers := config.MaxPeers + if config.LightServ > 0 { + // if we are running a light server, limit the number of ETH peers so that we reserve some space for incoming LES connections + // temporary solution until the new peer connectivity API is finished + halfPeers := maxPeers / 2 + maxPeers -= config.LightPeers + if maxPeers < halfPeers { + maxPeers = halfPeers + } + } + + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { + return nil, err + } + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) + eth.miner.SetGasPrice(config.GasPrice) + eth.miner.SetExtra(config.ExtraData) + + gpoParams := &gasprice.GpoParams{ + GpoMinGasPrice: config.GpoMinGasPrice, + GpoMaxGasPrice: config.GpoMaxGasPrice, + GpoFullBlockRatio: config.GpoFullBlockRatio, + GpobaseStepDown: config.GpobaseStepDown, + GpobaseStepUp: config.GpobaseStepUp, + GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, + } + gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) + eth.ApiBackend = &EthApiBackend{eth, gpo} + + return eth, nil +} + +// CreateDB creates the chain database. +func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) { + db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles) + if db, ok := db.(*ethdb.LDBDatabase); ok { + db.Meter("eth/db/chaindata/") + } + return db, err +} + +// SetupGenesisBlock initializes the genesis block for an Ethereum service +func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error { + // Load up any custom genesis block if requested + if len(config.Genesis) > 0 { + block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis)) + if err != nil { + return err + } + glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) + } + // Load up a test setup if directly injected + if config.TestGenesisState != nil { + *chainDb = config.TestGenesisState + } + if config.TestGenesisBlock != nil { + core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) + core.WriteBlock(*chainDb, config.TestGenesisBlock) + core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) + core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash()) + } + return nil +} + +// CreatePoW creates the required type of PoW instance for an Ethereum service +func CreatePoW(config *Config) (pow.PoW, error) { + switch { + case config.PowFake: + glog.V(logger.Info).Infof("ethash used in fake mode") + return pow.PoW(core.FakePow{}), nil + case config.PowTest: + glog.V(logger.Info).Infof("ethash used in test mode") + return ethash.NewForTesting() + case config.PowShared: + glog.V(logger.Info).Infof("ethash used in shared mode") + return ethash.NewShared(), nil + default: + return ethash.New(), nil + } +} + +// APIs returns the collection of RPC services the ethereum package offers. +// NOTE, some of these services probably need to be moved to somewhere else. +func (s *Ethereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ + { + Namespace: "eth", + Version: "1.0", + Service: NewPublicEthereumAPI(s), + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: NewPublicMinerAPI(s), + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Public: true, + }, { + Namespace: "miner", + Version: "1.0", + Service: NewPrivateMinerAPI(s), + Public: false, + }, { + Namespace: "eth", + Version: "1.0", + Service: filters.NewPublicFilterAPI(s.ApiBackend, false), + Public: true, + }, { + Namespace: "admin", + Version: "1.0", + Service: NewPrivateAdminAPI(s), + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPublicDebugAPI(s), + Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(s.chainConfig, s), + }, { + Namespace: "net", + Version: "1.0", + Service: s.netRPCService, + Public: true, + }, + }...) +} + +func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { + s.blockchain.ResetWithGenesisBlock(gb) +} + +func (s *Ethereum) Etherbase() (eb common.Address, err error) { + if s.etherbase != (common.Address{}) { + return s.etherbase, nil + } + if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { + if accounts := wallets[0].Accounts(); len(accounts) > 0 { + return accounts[0].Address, nil + } + } + return common.Address{}, fmt.Errorf("etherbase address must be explicitly specified") +} + +// set in js console via admin interface or wrapper from cli flags +func (self *Ethereum) SetEtherbase(etherbase common.Address) { + self.etherbase = etherbase + self.miner.SetEtherbase(etherbase) +} + +func (s *Ethereum) StartMining(threads int) error { + eb, err := s.Etherbase() + if err != nil { + err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) + glog.V(logger.Error).Infoln(err) + return err + } + go s.miner.Start(eb, threads) + return nil +} + +func (s *Ethereum) StopMining() { s.miner.Stop() } +func (s *Ethereum) IsMining() bool { return s.miner.Mining() } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } + +func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } +func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *Ethereum) Pow() pow.PoW { return s.pow } +func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } +func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } +func (s *Ethereum) NetVersion() int { return s.netVersionId } +func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } + +// Protocols implements node.Service, returning all the currently configured +// network protocols to start. +func (s *Ethereum) Protocols() []p2p.Protocol { + if s.lesServer == nil { + return s.protocolManager.SubProtocols + } else { + return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...) + } +} + +// Start implements node.Service, starting all internal goroutines needed by the +// Ethereum protocol implementation. +func (s *Ethereum) Start(srvr *p2p.Server) error { + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) + if s.AutoDAG { + s.StartAutoDAG() + } + s.protocolManager.Start() + if s.lesServer != nil { + s.lesServer.Start(srvr) + } + return nil +} + +// Stop implements node.Service, terminating all internal goroutines used by the +// Ethereum protocol. +func (s *Ethereum) Stop() error { + if s.stopDbUpgrade != nil { + s.stopDbUpgrade() + } + s.blockchain.Stop() + s.protocolManager.Stop() + if s.lesServer != nil { + s.lesServer.Stop() + } + s.txPool.Stop() + s.miner.Stop() + s.eventMux.Stop() + + s.StopAutoDAG() + + s.chainDb.Close() + close(s.shutdownChan) + + return nil +} + +// This function will wait for a shutdown and resumes main thread execution +func (s *Ethereum) WaitForShutdown() { + <-s.shutdownChan +} + +// StartAutoDAG() spawns a go routine that checks the DAG every autoDAGcheckInterval +// by default that is 10 times per epoch +// in epoch n, if we past autoDAGepochHeight within-epoch blocks, +// it calls ethash.MakeDAG to pregenerate the DAG for the next epoch n+1 +// if it does not exist yet as well as remove the DAG for epoch n-1 +// the loop quits if autodagquit channel is closed, it can safely restart and +// stop any number of times. +// For any more sophisticated pattern of DAG generation, use CLI subcommand +// makedag +func (self *Ethereum) StartAutoDAG() { + if self.autodagquit != nil { + return // already started + } + go func() { + glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG ON (ethash dir: %s)", ethash.DefaultDir) + var nextEpoch uint64 + timer := time.After(0) + self.autodagquit = make(chan bool) + for { + select { + case <-timer: + glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir) + currentBlock := self.BlockChain().CurrentBlock().NumberU64() + thisEpoch := currentBlock / epochLength + if nextEpoch <= thisEpoch { + if currentBlock%epochLength > autoDAGepochHeight { + if thisEpoch > 0 { + previousDag, previousDagFull := dagFiles(thisEpoch - 1) + os.Remove(filepath.Join(ethash.DefaultDir, previousDag)) + os.Remove(filepath.Join(ethash.DefaultDir, previousDagFull)) + glog.V(logger.Info).Infof("removed DAG for epoch %d (%s)", thisEpoch-1, previousDag) + } + nextEpoch = thisEpoch + 1 + dag, _ := dagFiles(nextEpoch) + if _, err := os.Stat(dag); os.IsNotExist(err) { + glog.V(logger.Info).Infof("Pregenerating DAG for epoch %d (%s)", nextEpoch, dag) + err := ethash.MakeDAG(nextEpoch*epochLength, "") // "" -> ethash.DefaultDir + if err != nil { + glog.V(logger.Error).Infof("Error generating DAG for epoch %d (%s)", nextEpoch, dag) + return + } + } else { + glog.V(logger.Error).Infof("DAG for epoch %d (%s)", nextEpoch, dag) + } + } + } + timer = time.After(autoDAGcheckInterval) + case <-self.autodagquit: + return + } + } + }() +} + +// stopAutoDAG stops automatic DAG pregeneration by quitting the loop +func (self *Ethereum) StopAutoDAG() { + if self.autodagquit != nil { + close(self.autodagquit) + self.autodagquit = nil + } + glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) +} + +// dagFiles(epoch) returns the two alternative DAG filenames (not a path) +// 1) - 2) full-R- +func dagFiles(epoch uint64) (string, string) { + seedHash, _ := ethash.GetSeedHash(epoch * epochLength) + dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8]) + return dag, "full-R" + dag +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend_test.go b/vendor/github.com/ethereum/go-ethereum/eth/backend_test.go new file mode 100644 index 0000000..574731f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend_test.go @@ -0,0 +1,83 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +func TestMipmapUpgrade(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + addr := common.BytesToAddress([]byte("jeff")) + genesis := core.WriteGenesisBlockForTesting(db) + + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) { + var receipts types.Receipts + switch i { + case 1: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{{Address: addr}} + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + case 2: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{{Address: addr}} + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + } + + // store the receipts + err := core.WriteReceipts(db, receipts) + if err != nil { + t.Fatal(err) + } + }) + for i, block := range chain { + core.WriteBlock(db, block) + if err := core.WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteBlockReceipts(db, block.Hash(), block.NumberU64(), receipts[i]); err != nil { + t.Fatal("error writing block receipts:", err) + } + } + + err := addMipmapBloomBins(db) + if err != nil { + t.Fatal(err) + } + + bloom := core.GetMipmapBloom(db, 1, core.MIPMapLevels[0]) + if (bloom == types.Bloom{}) { + t.Error("got empty bloom filter") + } + + data, _ := db.Get([]byte("setting-mipmap-version")) + if len(data) == 0 { + t.Error("setting-mipmap-version not written to database") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go new file mode 100644 index 0000000..e0f05f5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/bad_block.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + // The Ethereum main network genesis block. + defaultGenesisHash = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + badBlocksURL = "https://badblocks.ethdev.com" +) + +var EnableBadBlockReporting = false + +func sendBadBlockReport(block *types.Block, err error) { + if !EnableBadBlockReporting { + return + } + + var ( + blockRLP, _ = rlp.EncodeToBytes(block) + params = map[string]interface{}{ + "block": common.Bytes2Hex(blockRLP), + "blockHash": block.Hash().Hex(), + "errortype": err.Error(), + "client": "go", + } + ) + if !block.ReceivedAt.IsZero() { + params["receivedAt"] = block.ReceivedAt.UTC().String() + } + if p, ok := block.ReceivedFrom.(*peer); ok { + params["receivedFrom"] = map[string]interface{}{ + "enode": fmt.Sprintf("enode://%x@%v", p.ID(), p.RemoteAddr()), + "name": p.Name(), + "protocolVersion": p.version, + } + } + jsonStr, _ := json.Marshal(map[string]interface{}{"method": "eth_badBlock", "id": "1", "jsonrpc": "2.0", "params": []interface{}{params}}) + client := http.Client{Timeout: 8 * time.Second} + resp, err := client.Post(badBlocksURL, "application/json", bytes.NewReader(jsonStr)) + if err != nil { + glog.V(logger.Debug).Infoln(err) + return + } + glog.V(logger.Debug).Infof("Bad Block Report posted (%d)", resp.StatusCode) + resp.Body.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/bind.go b/vendor/github.com/ethereum/go-ethereum/eth/bind.go new file mode 100644 index 0000000..2ee9f2b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/bind.go @@ -0,0 +1,140 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// ContractBackend implements bind.ContractBackend with direct calls to Ethereum +// internals to support operating on contracts within subprotocols like eth and +// swarm. +// +// Internally this backend uses the already exposed API endpoints of the Ethereum +// object. These should be rewritten to internal Go method calls when the Go API +// is refactored to support a clean library use. +type ContractBackend struct { + eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata + bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data + txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data +} + +// NewContractBackend creates a new native contract backend using an existing +// Etheruem object. +func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { + return &ContractBackend{ + eapi: ethapi.NewPublicEthereumAPI(apiBackend), + bcapi: ethapi.NewPublicBlockChainAPI(apiBackend), + txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend), + } +} + +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) + return common.FromHex(out), err +} + +// CodeAt retrieves any code associated with the contract from the local API. +func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) + return common.FromHex(out), err +} + +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) + return out, err +} + +// ContractCall implements bind.ContractCaller executing an Ethereum contract +// call with the specified data as the input. The pending flag requests execution +// against the pending block, not the stable head of the chain. +func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) + return out, err +} + +func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { + args := ethapi.CallArgs{ + To: msg.To, + From: msg.From, + Data: msg.Data, + } + if msg.Gas != nil { + args.Gas = hexutil.Big(*msg.Gas) + } + if msg.GasPrice != nil { + args.GasPrice = hexutil.Big(*msg.GasPrice) + } + if msg.Value != nil { + args.Value = hexutil.Big(*msg.Value) + } + return args +} + +func toBlockNumber(num *big.Int) rpc.BlockNumber { + if num == nil { + return rpc.LatestBlockNumber + } + return rpc.BlockNumber(num.Int64()) +} + +// PendingAccountNonce implements bind.ContractTransactor retrieving the current +// pending nonce associated with an account. +func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (nonce uint64, err error) { + out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) + if out != nil { + nonce = uint64(*out) + } + return nonce, err +} + +// SuggestGasPrice implements bind.ContractTransactor retrieving the currently +// suggested gas price to allow a timely execution of a transaction. +func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return b.eapi.GasPrice(ctx) +} + +// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas +// needed to execute a specific transaction based on the current pending state of +// the backend blockchain. There is no guarantee that this is the true gas limit +// requirement as other transactions may be added or removed by miners, but it +// should provide a basis for setting a reasonable default. +func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { + out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) + return out.ToInt(), err +} + +// SendTransaction implements bind.ContractTransactor injects the transaction +// into the pending pool for execution. +func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + raw, _ := rlp.EncodeToBytes(tx) + _, err := b.txapi.SendRawTransaction(ctx, raw) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go new file mode 100644 index 0000000..5fd73a5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/db_upgrade.go @@ -0,0 +1,356 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package eth implements the Ethereum protocol. +package eth + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" +) + +var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys") + +// upgradeSequentialKeys checks the chain database version and +// starts a background process to make upgrades if necessary. +// Returns a stop function that blocks until the process has +// been safely stopped. +func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) { + data, _ := db.Get(useSequentialKeys) + if len(data) > 0 && data[0] == 42 { + return nil // already converted + } + + if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 { + db.Put(useSequentialKeys, []byte{42}) + return nil // empty database, nothing to do + } + + glog.V(logger.Info).Infof("Upgrading chain database to use sequential keys") + + stopChn := make(chan struct{}) + stoppedChn := make(chan struct{}) + + go func() { + stopFn := func() bool { + select { + case <-time.After(time.Microsecond * 100): // make sure other processes don't get starved + case <-stopChn: + return true + } + return false + } + + err, stopped := upgradeSequentialCanonicalNumbers(db, stopFn) + if err == nil && !stopped { + err, stopped = upgradeSequentialBlocks(db, stopFn) + } + if err == nil && !stopped { + err, stopped = upgradeSequentialOrphanedReceipts(db, stopFn) + } + if err == nil && !stopped { + glog.V(logger.Info).Infof("Database conversion successful") + db.Put(useSequentialKeys, []byte{42}) + } + if err != nil { + glog.V(logger.Error).Infof("Database conversion failed: %v", err) + } + close(stoppedChn) + }() + + return func() { + close(stopChn) + <-stoppedChn + } +} + +// upgradeSequentialCanonicalNumbers reads all old format canonical numbers from +// the database, writes them in new format and deletes the old ones if successful. +func upgradeSequentialCanonicalNumbers(db ethdb.Database, stopFn func() bool) (error, bool) { + prefix := []byte("block-num-") + it := db.(*ethdb.LDBDatabase).NewIterator() + defer func() { + it.Release() + }() + it.Seek(prefix) + cnt := 0 + for bytes.HasPrefix(it.Key(), prefix) { + keyPtr := it.Key() + if len(keyPtr) < 20 { + cnt++ + if cnt%100000 == 0 { + it.Release() + it = db.(*ethdb.LDBDatabase).NewIterator() + it.Seek(keyPtr) + glog.V(logger.Info).Infof("converting %d canonical numbers...", cnt) + } + number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64() + newKey := []byte("h12345678n") + binary.BigEndian.PutUint64(newKey[1:9], number) + if err := db.Put(newKey, it.Value()); err != nil { + return err, false + } + if err := db.Delete(keyPtr); err != nil { + return err, false + } + } + + if stopFn() { + return nil, true + } + it.Next() + } + if cnt > 0 { + glog.V(logger.Info).Infof("converted %d canonical numbers...", cnt) + } + return nil, false +} + +// upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block +// receipts from the database, writes them in new format and deletes the old ones +// if successful. +func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) { + prefix := []byte("block-") + it := db.(*ethdb.LDBDatabase).NewIterator() + defer func() { + it.Release() + }() + it.Seek(prefix) + cnt := 0 + for bytes.HasPrefix(it.Key(), prefix) { + keyPtr := it.Key() + if len(keyPtr) >= 38 { + cnt++ + if cnt%10000 == 0 { + it.Release() + it = db.(*ethdb.LDBDatabase).NewIterator() + it.Seek(keyPtr) + glog.V(logger.Info).Infof("converting %d blocks...", cnt) + } + // convert header, body, td and block receipts + var keyPrefix [38]byte + copy(keyPrefix[:], keyPtr[0:38]) + hash := keyPrefix[6:38] + if err := upgradeSequentialBlockData(db, hash); err != nil { + return err, false + } + // delete old db entries belonging to this hash + for bytes.HasPrefix(it.Key(), keyPrefix[:]) { + if err := db.Delete(it.Key()); err != nil { + return err, false + } + it.Next() + } + if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil { + return err, false + } + } else { + it.Next() + } + + if stopFn() { + return nil, true + } + } + if cnt > 0 { + glog.V(logger.Info).Infof("converted %d blocks...", cnt) + } + return nil, false +} + +// upgradeSequentialOrphanedReceipts removes any old format block receipts from the +// database that did not have a corresponding block +func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) { + prefix := []byte("receipts-block-") + it := db.(*ethdb.LDBDatabase).NewIterator() + defer it.Release() + it.Seek(prefix) + cnt := 0 + for bytes.HasPrefix(it.Key(), prefix) { + // phase 2 already converted receipts belonging to existing + // blocks, just remove if there's anything left + cnt++ + if err := db.Delete(it.Key()); err != nil { + return err, false + } + + if stopFn() { + return nil, true + } + it.Next() + } + if cnt > 0 { + glog.V(logger.Info).Infof("removed %d orphaned block receipts...", cnt) + } + return nil, false +} + +// upgradeSequentialBlockData upgrades the header, body, td and block receipts +// database entries belonging to a single hash (doesn't delete old data). +func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error { + // get old chain data and block number + headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...)) + if len(headerRLP) == 0 { + return nil + } + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil { + return err + } + number := header.Number.Uint64() + bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...)) + tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...)) + receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...)) + // store new hash -> number association + encNum := make([]byte, 8) + binary.BigEndian.PutUint64(encNum, number) + if err := db.Put(append([]byte("H"), hash...), encNum); err != nil { + return err + } + // store new chain data + if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil { + return err + } + if len(tdRLP) != 0 { + if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil { + return err + } + } + if len(bodyRLP) != 0 { + if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil { + return err + } + } + if len(receiptsRLP) != 0 { + if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil { + return err + } + } + return nil +} + +// upgradeChainDatabase ensures that the chain database stores block split into +// separate header and body entries. +func upgradeChainDatabase(db ethdb.Database) error { + // Short circuit if the head block is stored already as separate header and body + data, err := db.Get([]byte("LastBlock")) + if err != nil { + return nil + } + head := common.BytesToHash(data) + + if block := core.GetBlockByHashOld(db, head); block == nil { + return nil + } + // At least some of the database is still the old format, upgrade (skip the head block!) + glog.V(logger.Info).Info("Old database detected, upgrading...") + + if db, ok := db.(*ethdb.LDBDatabase); ok { + blockPrefix := []byte("block-hash-") + for it := db.NewIterator(); it.Next(); { + // Skip anything other than a combined block + if !bytes.HasPrefix(it.Key(), blockPrefix) { + continue + } + // Skip the head block (merge last to signal upgrade completion) + if bytes.HasSuffix(it.Key(), head.Bytes()) { + continue + } + // Load the block, split and serialize (order!) + block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix))) + + if err := core.WriteTd(db, block.Hash(), block.NumberU64(), block.DeprecatedTd()); err != nil { + return err + } + if err := core.WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil { + return err + } + if err := core.WriteHeader(db, block.Header()); err != nil { + return err + } + if err := db.Delete(it.Key()); err != nil { + return err + } + } + // Lastly, upgrade the head block, disabling the upgrade mechanism + current := core.GetBlockByHashOld(db, head) + + if err := core.WriteTd(db, current.Hash(), current.NumberU64(), current.DeprecatedTd()); err != nil { + return err + } + if err := core.WriteBody(db, current.Hash(), current.NumberU64(), current.Body()); err != nil { + return err + } + if err := core.WriteHeader(db, current.Header()); err != nil { + return err + } + } + return nil +} + +func addMipmapBloomBins(db ethdb.Database) (err error) { + const mipmapVersion uint = 2 + + // check if the version is set. We ignore data for now since there's + // only one version so we can easily ignore it for now + var data []byte + data, _ = db.Get([]byte("setting-mipmap-version")) + if len(data) > 0 { + var version uint + if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion { + return nil + } + } + + defer func() { + if err == nil { + var val []byte + val, err = rlp.EncodeToBytes(mipmapVersion) + if err == nil { + err = db.Put([]byte("setting-mipmap-version"), val) + } + return + } + }() + latestHash := core.GetHeadBlockHash(db) + latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash)) + if latestBlock == nil { // clean database + return + } + + tstart := time.Now() + glog.V(logger.Info).Infoln("upgrading db log bloom bins") + for i := uint64(0); i <= latestBlock.NumberU64(); i++ { + hash := core.GetCanonicalHash(db, i) + if (hash == common.Hash{}) { + return fmt.Errorf("chain db corrupted. Could not find block %d.", i) + } + core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i)) + } + glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart)) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go new file mode 100644 index 0000000..e413768 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/api.go @@ -0,0 +1,166 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "sync" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// PublicDownloaderAPI provides an API which gives information about the current synchronisation status. +// It offers only methods that operates on data that can be available to anyone without security risks. +type PublicDownloaderAPI struct { + d *Downloader + mux *event.TypeMux + installSyncSubscription chan chan interface{} + uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest +} + +// NewPublicDownloaderAPI create a new PublicDownloaderAPI. The API has an internal event loop that +// listens for events from the downloader through the global event mux. In case it receives one of +// these events it broadcasts it to all syncing subscriptions that are installed through the +// installSyncSubscription channel. +func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAPI { + api := &PublicDownloaderAPI{ + d: d, + mux: m, + installSyncSubscription: make(chan chan interface{}), + uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest), + } + + go api.eventLoop() + + return api +} + +// eventLoop runs an loop until the event mux closes. It will install and uninstall new +// sync subscriptions and broadcasts sync status updates to the installed sync subscriptions. +func (api *PublicDownloaderAPI) eventLoop() { + var ( + sub = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{}) + syncSubscriptions = make(map[chan interface{}]struct{}) + ) + + for { + select { + case i := <-api.installSyncSubscription: + syncSubscriptions[i] = struct{}{} + case u := <-api.uninstallSyncSubscription: + delete(syncSubscriptions, u.c) + close(u.uninstalled) + case event := <-sub.Chan(): + if event == nil { + return + } + + var notification interface{} + switch event.Data.(type) { + case StartEvent: + notification = &SyncingResult{ + Syncing: true, + Status: api.d.Progress(), + } + case DoneEvent, FailedEvent: + notification = false + } + // broadcast + for c := range syncSubscriptions { + c <- notification + } + } + } +} + +// Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished. +func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + go func() { + statuses := make(chan interface{}) + sub := api.SubscribeSyncStatus(statuses) + + for { + select { + case status := <-statuses: + notifier.Notify(rpcSub.ID, status) + case <-rpcSub.Err(): + sub.Unsubscribe() + return + case <-notifier.Closed(): + sub.Unsubscribe() + return + } + } + }() + + return rpcSub, nil +} + +// SyncingResult provides information about the current synchronisation status for this node. +type SyncingResult struct { + Syncing bool `json:"syncing"` + Status ethereum.SyncProgress `json:"status"` +} + +// uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop. +type uninstallSyncSubscriptionRequest struct { + c chan interface{} + uninstalled chan interface{} +} + +// SyncStatusSubscription represents a syncing subscription. +type SyncStatusSubscription struct { + api *PublicDownloaderAPI // register subscription in event loop of this api instance + c chan interface{} // channel where events are broadcasted to + unsubOnce sync.Once // make sure unsubscribe logic is executed once +} + +// Unsubscribe uninstalls the subscription from the DownloadAPI event loop. +// The status channel that was passed to subscribeSyncStatus isn't used anymore +// after this method returns. +func (s *SyncStatusSubscription) Unsubscribe() { + s.unsubOnce.Do(func() { + req := uninstallSyncSubscriptionRequest{s.c, make(chan interface{})} + s.api.uninstallSyncSubscription <- &req + + for { + select { + case <-s.c: + // drop new status events until uninstall confirmation + continue + case <-req.uninstalled: + return + } + } + }) +} + +// SubscribeSyncStatus creates a subscription that will broadcast new synchronisation updates. +// The given channel must receive interface values, the result can either +func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription { + api.installSyncSubscription <- status + return &SyncStatusSubscription{api: api, c: status} +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go new file mode 100644 index 0000000..7e29524 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go @@ -0,0 +1,1529 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package downloader contains the manual full chain synchronisation. +package downloader + +import ( + "crypto/rand" + "errors" + "fmt" + "math" + "math/big" + "strings" + "sync" + "sync/atomic" + "time" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" + "github.com/rcrowley/go-metrics" +) + +var ( + MaxHashFetch = 512 // Amount of hashes to be fetched per retrieval request + MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request + MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request + MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly + MaxBodyFetch = 128 // Amount of block bodies to be fetched per retrieval request + MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request + MaxStateFetch = 384 // Amount of node state values to allow fetching per request + + MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation + rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests + rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests + rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value + ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion + ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts + + qosTuningPeers = 5 // Number of peers to tune based on (best peers) + qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence + qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value + + maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection) + maxHeadersProcess = 2048 // Number of header download results to import at once into the chain + maxResultsProcess = 2048 // Number of content download results to import at once into the chain + + fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync + fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected + fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it + fsPivotInterval = 256 // Number of headers out of which to randomize the pivot point + fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync + fsCriticalTrials = uint32(32) // Number of times to retry in the cricical section before bailing +) + +var ( + errBusy = errors.New("busy") + errUnknownPeer = errors.New("peer is unknown or unhealthy") + errBadPeer = errors.New("action from bad peer ignored") + errStallingPeer = errors.New("peer is stalling") + errNoPeers = errors.New("no peers to keep download active") + errTimeout = errors.New("timeout") + errEmptyHeaderSet = errors.New("empty header set by peer") + errPeersUnavailable = errors.New("no peers available or all tried for download") + errInvalidAncestor = errors.New("retrieved ancestor is invalid") + errInvalidChain = errors.New("retrieved hash chain is invalid") + errInvalidBlock = errors.New("retrieved block is invalid") + errInvalidBody = errors.New("retrieved block body is invalid") + errInvalidReceipt = errors.New("retrieved receipt is invalid") + errCancelBlockFetch = errors.New("block download canceled (requested)") + errCancelHeaderFetch = errors.New("block header download canceled (requested)") + errCancelBodyFetch = errors.New("block body download canceled (requested)") + errCancelReceiptFetch = errors.New("receipt download canceled (requested)") + errCancelStateFetch = errors.New("state data download canceled (requested)") + errCancelHeaderProcessing = errors.New("header processing canceled (requested)") + errCancelContentProcessing = errors.New("content processing canceled (requested)") + errNoSyncActive = errors.New("no sync active") + errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 62)") +) + +type Downloader struct { + mode SyncMode // Synchronisation mode defining the strategy used (per sync cycle) + mux *event.TypeMux // Event multiplexer to announce sync operation events + + queue *queue // Scheduler for selecting the hashes to download + peers *peerSet // Set of active peers from which download can proceed + + fsPivotLock *types.Header // Pivot header on critical section entry (cannot change between retries) + fsPivotFails uint32 // Number of subsequent fast sync failures in the critical section + + rttEstimate uint64 // Round trip time to target for download requests + rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops) + + // Statistics + syncStatsChainOrigin uint64 // Origin block number where syncing started at + syncStatsChainHeight uint64 // Highest block number known when syncing started + syncStatsStateDone uint64 // Number of state trie entries already pulled + syncStatsLock sync.RWMutex // Lock protecting the sync stats fields + + // Callbacks + hasHeader headerCheckFn // Checks if a header is present in the chain + hasBlockAndState blockAndStateCheckFn // Checks if a block and associated state is present in the chain + getHeader headerRetrievalFn // Retrieves a header from the chain + getBlock blockRetrievalFn // Retrieves a block from the chain + headHeader headHeaderRetrievalFn // Retrieves the head header from the chain + headBlock headBlockRetrievalFn // Retrieves the head block from the chain + headFastBlock headFastBlockRetrievalFn // Retrieves the head fast-sync block from the chain + commitHeadBlock headBlockCommitterFn // Commits a manually assembled block as the chain head + getTd tdRetrievalFn // Retrieves the TD of a block from the chain + insertHeaders headerChainInsertFn // Injects a batch of headers into the chain + insertBlocks blockChainInsertFn // Injects a batch of blocks into the chain + insertReceipts receiptChainInsertFn // Injects a batch of blocks and their receipts into the chain + rollback chainRollbackFn // Removes a batch of recently added chain links + dropPeer peerDropFn // Drops a peer for misbehaving + + // Status + synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing + synchronising int32 + notified int32 + + // Channels + newPeerCh chan *peer + headerCh chan dataPack // [eth/62] Channel receiving inbound block headers + bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies + receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts + stateCh chan dataPack // [eth/63] Channel receiving inbound node state data + bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks + receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks + stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks + headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks + + // Cancellation and termination + cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) + cancelCh chan struct{} // Channel to cancel mid-flight syncs + cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers + + quitCh chan struct{} // Quit channel to signal termination + quitLock sync.RWMutex // Lock to prevent double closes + + // Testing hooks + syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run + bodyFetchHook func([]*types.Header) // Method to call upon starting a block body fetch + receiptFetchHook func([]*types.Header) // Method to call upon starting a receipt fetch + chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations) +} + +// New creates a new downloader to fetch hashes and blocks from remote peers. +func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlockAndState blockAndStateCheckFn, + getHeader headerRetrievalFn, getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, + headFastBlock headFastBlockRetrievalFn, commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, + insertBlocks blockChainInsertFn, insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader { + + dl := &Downloader{ + mode: mode, + mux: mux, + queue: newQueue(stateDb), + peers: newPeerSet(), + rttEstimate: uint64(rttMaxEstimate), + rttConfidence: uint64(1000000), + hasHeader: hasHeader, + hasBlockAndState: hasBlockAndState, + getHeader: getHeader, + getBlock: getBlock, + headHeader: headHeader, + headBlock: headBlock, + headFastBlock: headFastBlock, + commitHeadBlock: commitHeadBlock, + getTd: getTd, + insertHeaders: insertHeaders, + insertBlocks: insertBlocks, + insertReceipts: insertReceipts, + rollback: rollback, + dropPeer: dropPeer, + newPeerCh: make(chan *peer, 1), + headerCh: make(chan dataPack, 1), + bodyCh: make(chan dataPack, 1), + receiptCh: make(chan dataPack, 1), + stateCh: make(chan dataPack, 1), + bodyWakeCh: make(chan bool, 1), + receiptWakeCh: make(chan bool, 1), + stateWakeCh: make(chan bool, 1), + headerProcCh: make(chan []*types.Header, 1), + quitCh: make(chan struct{}), + } + go dl.qosTuner() + return dl +} + +// Progress retrieves the synchronisation boundaries, specifically the origin +// block where synchronisation started at (may have failed/suspended); the block +// or header sync is currently at; and the latest known block which the sync targets. +// +// In addition, during the state download phase of fast synchronisation the number +// of processed and the total number of known states are also returned. Otherwise +// these are zero. +func (d *Downloader) Progress() ethereum.SyncProgress { + // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks + pendingStates := uint64(d.queue.PendingNodeData()) + + // Lock the current stats and return the progress + d.syncStatsLock.RLock() + defer d.syncStatsLock.RUnlock() + + current := uint64(0) + switch d.mode { + case FullSync: + current = d.headBlock().NumberU64() + case FastSync: + current = d.headFastBlock().NumberU64() + case LightSync: + current = d.headHeader().Number.Uint64() + } + return ethereum.SyncProgress{ + StartingBlock: d.syncStatsChainOrigin, + CurrentBlock: current, + HighestBlock: d.syncStatsChainHeight, + PulledStates: d.syncStatsStateDone, + KnownStates: d.syncStatsStateDone + pendingStates, + } +} + +// Synchronising returns whether the downloader is currently retrieving blocks. +func (d *Downloader) Synchronising() bool { + return atomic.LoadInt32(&d.synchronising) > 0 +} + +// RegisterPeer injects a new download peer into the set of block source to be +// used for fetching hashes and blocks from. +func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHeadRetrievalFn, + getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn, + getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error { + + glog.V(logger.Detail).Infoln("Registering peer", id) + if err := d.peers.Register(newPeer(id, version, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil { + glog.V(logger.Error).Infoln("Register failed:", err) + return err + } + d.qosReduceConfidence() + + return nil +} + +// UnregisterPeer remove a peer from the known list, preventing any action from +// the specified peer. An effort is also made to return any pending fetches into +// the queue. +func (d *Downloader) UnregisterPeer(id string) error { + // Unregister the peer from the active peer set and revoke any fetch tasks + glog.V(logger.Detail).Infoln("Unregistering peer", id) + if err := d.peers.Unregister(id); err != nil { + glog.V(logger.Error).Infoln("Unregister failed:", err) + return err + } + d.queue.Revoke(id) + + // If this peer was the master peer, abort sync immediately + d.cancelLock.RLock() + master := id == d.cancelPeer + d.cancelLock.RUnlock() + + if master { + d.cancel() + } + return nil +} + +// Synchronise tries to sync up our local block chain with a remote peer, both +// adding various sanity checks as well as wrapping it with various log entries. +func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error { + glog.V(logger.Detail).Infof("Attempting synchronisation: %v, head [%x…], TD %v", id, head[:4], td) + + err := d.synchronise(id, head, td, mode) + switch err { + case nil: + glog.V(logger.Detail).Infof("Synchronisation completed") + + case errBusy: + glog.V(logger.Detail).Infof("Synchronisation already in progress") + + case errTimeout, errBadPeer, errStallingPeer, + errEmptyHeaderSet, errPeersUnavailable, errTooOld, + errInvalidAncestor, errInvalidChain: + glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err) + d.dropPeer(id) + + default: + glog.V(logger.Warn).Infof("Synchronisation failed: %v", err) + } + return err +} + +// synchronise will select the peer and use it for synchronising. If an empty string is given +// it will use the best peer possible and synchronize if it's TD is higher than our own. If any of the +// checks fail an error will be returned. This method is synchronous +func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error { + // Mock out the synchronisation if testing + if d.synchroniseMock != nil { + return d.synchroniseMock(id, hash) + } + // Make sure only one goroutine is ever allowed past this point at once + if !atomic.CompareAndSwapInt32(&d.synchronising, 0, 1) { + return errBusy + } + defer atomic.StoreInt32(&d.synchronising, 0) + + // Post a user notification of the sync (only once per session) + if atomic.CompareAndSwapInt32(&d.notified, 0, 1) { + glog.V(logger.Info).Infoln("Block synchronisation started") + } + // Reset the queue, peer set and wake channels to clean any internal leftover state + d.queue.Reset() + d.peers.Reset() + + for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} { + select { + case <-ch: + default: + } + } + for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} { + for empty := false; !empty; { + select { + case <-ch: + default: + empty = true + } + } + } + for empty := false; !empty; { + select { + case <-d.headerProcCh: + default: + empty = true + } + } + // Create cancel channel for aborting mid-flight and mark the master peer + d.cancelLock.Lock() + d.cancelCh = make(chan struct{}) + d.cancelPeer = id + d.cancelLock.Unlock() + + defer d.cancel() // No matter what, we can't leave the cancel channel open + + // Set the requested sync mode, unless it's forbidden + d.mode = mode + if d.mode == FastSync && atomic.LoadUint32(&d.fsPivotFails) >= fsCriticalTrials { + d.mode = FullSync + } + // Retrieve the origin peer and initiate the downloading process + p := d.peers.Peer(id) + if p == nil { + return errUnknownPeer + } + return d.syncWithPeer(p, hash, td) +} + +// syncWithPeer starts a block synchronization based on the hash chain from the +// specified peer and head hash. +func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err error) { + d.mux.Post(StartEvent{}) + defer func() { + // reset on error + if err != nil { + d.mux.Post(FailedEvent{err}) + } else { + d.mux.Post(DoneEvent{}) + } + }() + if p.version < 62 { + return errTooOld + } + + glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version) + defer func(start time.Time) { + glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start)) + }(time.Now()) + + // Look up the sync boundaries: the common ancestor and the target block + latest, err := d.fetchHeight(p) + if err != nil { + return err + } + height := latest.Number.Uint64() + + origin, err := d.findAncestor(p, height) + if err != nil { + return err + } + d.syncStatsLock.Lock() + if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin { + d.syncStatsChainOrigin = origin + } + d.syncStatsChainHeight = height + d.syncStatsLock.Unlock() + + // Initiate the sync using a concurrent header and content retrieval algorithm + pivot := uint64(0) + switch d.mode { + case LightSync: + pivot = height + case FastSync: + // Calculate the new fast/slow sync pivot point + if d.fsPivotLock == nil { + pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval))) + if err != nil { + panic(fmt.Sprintf("Failed to access crypto random source: %v", err)) + } + if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() { + pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64() + } + } else { + // Pivot point locked in, use this and do not pick a new one! + pivot = d.fsPivotLock.Number.Uint64() + } + // If the point is below the origin, move origin back to ensure state download + if pivot < origin { + if pivot > 0 { + origin = pivot - 1 + } else { + origin = 0 + } + } + glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot) + } + d.queue.Prepare(origin+1, d.mode, pivot, latest) + if d.syncInitHook != nil { + d.syncInitHook(origin, height) + } + return d.spawnSync(origin+1, + func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved + func() error { return d.processHeaders(origin+1, td) }, // Headers are always retrieved + func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync + func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync + func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync + ) +} + +// spawnSync runs d.process and all given fetcher functions to completion in +// separate goroutines, returning the first error that appears. +func (d *Downloader) spawnSync(origin uint64, fetchers ...func() error) error { + var wg sync.WaitGroup + errc := make(chan error, len(fetchers)+1) + wg.Add(len(fetchers) + 1) + go func() { defer wg.Done(); errc <- d.processContent() }() + for _, fn := range fetchers { + fn := fn + go func() { defer wg.Done(); errc <- fn() }() + } + // Wait for the first error, then terminate the others. + var err error + for i := 0; i < len(fetchers)+1; i++ { + if i == len(fetchers) { + // Close the queue when all fetchers have exited. + // This will cause the block processor to end when + // it has processed the queue. + d.queue.Close() + } + if err = <-errc; err != nil { + break + } + } + d.queue.Close() + d.cancel() + wg.Wait() + + // If sync failed in the critical section, bump the fail counter + if err != nil && d.mode == FastSync && d.fsPivotLock != nil { + atomic.AddUint32(&d.fsPivotFails, 1) + } + return err +} + +// cancel cancels all of the operations and resets the queue. It returns true +// if the cancel operation was completed. +func (d *Downloader) cancel() { + // Close the current cancel channel + d.cancelLock.Lock() + if d.cancelCh != nil { + select { + case <-d.cancelCh: + // Channel was already closed + default: + close(d.cancelCh) + } + } + d.cancelLock.Unlock() +} + +// Terminate interrupts the downloader, canceling all pending operations. +// The downloader cannot be reused after calling Terminate. +func (d *Downloader) Terminate() { + // Close the termination channel (make sure double close is allowed) + d.quitLock.Lock() + select { + case <-d.quitCh: + default: + close(d.quitCh) + } + d.quitLock.Unlock() + + // Cancel any pending download requests + d.cancel() +} + +// fetchHeight retrieves the head header of the remote peer to aid in estimating +// the total time a pending synchronisation would take. +func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) { + glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p) + + // Request the advertised remote head block and wait for the response + head, _ := p.currentHead() + go p.getRelHeaders(head, 1, 0, false) + + timeout := time.After(d.requestTTL()) + for { + select { + case <-d.cancelCh: + return nil, errCancelBlockFetch + + case packet := <-d.headerCh: + // Discard anything not from the origin peer + if packet.PeerId() != p.id { + glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", packet.PeerId()) + break + } + // Make sure the peer actually gave something valid + headers := packet.(*headerPack).headers + if len(headers) != 1 { + glog.V(logger.Debug).Infof("%v: invalid number of head headers: %d != 1", p, len(headers)) + return nil, errBadPeer + } + return headers[0], nil + + case <-timeout: + glog.V(logger.Debug).Infof("%v: head header timeout", p) + return nil, errTimeout + + case <-d.bodyCh: + case <-d.stateCh: + case <-d.receiptCh: + // Out of bounds delivery, ignore + } + } +} + +// findAncestor tries to locate the common ancestor link of the local chain and +// a remote peers blockchain. In the general case when our node was in sync and +// on the correct chain, checking the top N links should already get us a match. +// In the rare scenario when we ended up on a long reorganisation (i.e. none of +// the head links match), we do a binary search to find the common ancestor. +func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) { + glog.V(logger.Debug).Infof("%v: looking for common ancestor (remote height %d)", p, height) + + // Figure out the valid ancestor range to prevent rewrite attacks + floor, ceil := int64(-1), d.headHeader().Number.Uint64() + if d.mode == FullSync { + ceil = d.headBlock().NumberU64() + } else if d.mode == FastSync { + ceil = d.headFastBlock().NumberU64() + } + if ceil >= MaxForkAncestry { + floor = int64(ceil - MaxForkAncestry) + } + // Request the topmost blocks to short circuit binary ancestor lookup + head := ceil + if head > height { + head = height + } + from := int64(head) - int64(MaxHeaderFetch) + if from < 0 { + from = 0 + } + // Span out with 15 block gaps into the future to catch bad head reports + limit := 2 * MaxHeaderFetch / 16 + count := 1 + int((int64(ceil)-from)/16) + if count > limit { + count = limit + } + go p.getAbsHeaders(uint64(from), count, 15, false) + + // Wait for the remote response to the head fetch + number, hash := uint64(0), common.Hash{} + timeout := time.After(d.requestTTL()) + + for finished := false; !finished; { + select { + case <-d.cancelCh: + return 0, errCancelHeaderFetch + + case packet := <-d.headerCh: + // Discard anything not from the origin peer + if packet.PeerId() != p.id { + glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", packet.PeerId()) + break + } + // Make sure the peer actually gave something valid + headers := packet.(*headerPack).headers + if len(headers) == 0 { + glog.V(logger.Warn).Infof("%v: empty head header set", p) + return 0, errEmptyHeaderSet + } + // Make sure the peer's reply conforms to the request + for i := 0; i < len(headers); i++ { + if number := headers[i].Number.Int64(); number != from+int64(i)*16 { + glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i)*16, number) + return 0, errInvalidChain + } + } + // Check if a common ancestor was found + finished = true + for i := len(headers) - 1; i >= 0; i-- { + // Skip any headers that underflow/overflow our requested set + if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > ceil { + continue + } + // Otherwise check if we already know the header or not + if (d.mode == FullSync && d.hasBlockAndState(headers[i].Hash())) || (d.mode != FullSync && d.hasHeader(headers[i].Hash())) { + number, hash = headers[i].Number.Uint64(), headers[i].Hash() + + // If every header is known, even future ones, the peer straight out lied about its head + if number > height && i == limit-1 { + glog.V(logger.Warn).Infof("%v: lied about chain head: reported %d, found above %d", p, height, number) + return 0, errStallingPeer + } + break + } + } + + case <-timeout: + glog.V(logger.Debug).Infof("%v: head header timeout", p) + return 0, errTimeout + + case <-d.bodyCh: + case <-d.stateCh: + case <-d.receiptCh: + // Out of bounds delivery, ignore + } + } + // If the head fetch already found an ancestor, return + if !common.EmptyHash(hash) { + if int64(number) <= floor { + glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash[:4], floor) + return 0, errInvalidAncestor + } + glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, number, hash[:4]) + return number, nil + } + // Ancestor not found, we need to binary search over our chain + start, end := uint64(0), head + if floor > 0 { + start = uint64(floor) + } + for start+1 < end { + // Split our chain interval in two, and request the hash to cross check + check := (start + end) / 2 + + timeout := time.After(d.requestTTL()) + go p.getAbsHeaders(uint64(check), 1, 0, false) + + // Wait until a reply arrives to this request + for arrived := false; !arrived; { + select { + case <-d.cancelCh: + return 0, errCancelHeaderFetch + + case packer := <-d.headerCh: + // Discard anything not from the origin peer + if packer.PeerId() != p.id { + glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", packer.PeerId()) + break + } + // Make sure the peer actually gave something valid + headers := packer.(*headerPack).headers + if len(headers) != 1 { + glog.V(logger.Debug).Infof("%v: invalid search header set (%d)", p, len(headers)) + return 0, errBadPeer + } + arrived = true + + // Modify the search interval based on the response + if (d.mode == FullSync && !d.hasBlockAndState(headers[0].Hash())) || (d.mode != FullSync && !d.hasHeader(headers[0].Hash())) { + end = check + break + } + header := d.getHeader(headers[0].Hash()) // Independent of sync mode, header surely exists + if header.Number.Uint64() != check { + glog.V(logger.Debug).Infof("%v: non requested header #%d [%x…], instead of #%d", p, header.Number, header.Hash().Bytes()[:4], check) + return 0, errBadPeer + } + start = check + + case <-timeout: + glog.V(logger.Debug).Infof("%v: search header timeout", p) + return 0, errTimeout + + case <-d.bodyCh: + case <-d.stateCh: + case <-d.receiptCh: + // Out of bounds delivery, ignore + } + } + } + // Ensure valid ancestry and return + if int64(start) <= floor { + glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor) + return 0, errInvalidAncestor + } + glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, start, hash[:4]) + return start, nil +} + +// fetchHeaders keeps retrieving headers concurrently from the number +// requested, until no more are returned, potentially throttling on the way. To +// facilitate concurrency but still protect against malicious nodes sending bad +// headers, we construct a header chain skeleton using the "origin" peer we are +// syncing with, and fill in the missing headers using anyone else. Headers from +// other peers are only accepted if they map cleanly to the skeleton. If no one +// can fill in the skeleton - not even the origin peer - it's assumed invalid and +// the origin is dropped. +func (d *Downloader) fetchHeaders(p *peer, from uint64) error { + glog.V(logger.Debug).Infof("%v: directing header downloads from #%d", p, from) + defer glog.V(logger.Debug).Infof("%v: header download terminated", p) + + // Create a timeout timer, and the associated header fetcher + skeleton := true // Skeleton assembly phase or finishing up + request := time.Now() // time of the last skeleton fetch request + timeout := time.NewTimer(0) // timer to dump a non-responsive active peer + <-timeout.C // timeout channel should be initially empty + defer timeout.Stop() + + getHeaders := func(from uint64) { + request = time.Now() + timeout.Reset(d.requestTTL()) + + if skeleton { + glog.V(logger.Detail).Infof("%v: fetching %d skeleton headers from #%d", p, MaxHeaderFetch, from) + go p.getAbsHeaders(from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false) + } else { + glog.V(logger.Detail).Infof("%v: fetching %d full headers from #%d", p, MaxHeaderFetch, from) + go p.getAbsHeaders(from, MaxHeaderFetch, 0, false) + } + } + // Start pulling the header chain skeleton until all is done + getHeaders(from) + + for { + select { + case <-d.cancelCh: + return errCancelHeaderFetch + + case packet := <-d.headerCh: + // Make sure the active peer is giving us the skeleton headers + if packet.PeerId() != p.id { + glog.V(logger.Debug).Infof("Received skeleton headers from incorrect peer (%s)", packet.PeerId()) + break + } + headerReqTimer.UpdateSince(request) + timeout.Stop() + + // If the skeleton's finished, pull any remaining head headers directly from the origin + if packet.Items() == 0 && skeleton { + skeleton = false + getHeaders(from) + continue + } + // If no more headers are inbound, notify the content fetchers and return + if packet.Items() == 0 { + glog.V(logger.Debug).Infof("%v: no available headers", p) + select { + case d.headerProcCh <- nil: + return nil + case <-d.cancelCh: + return errCancelHeaderFetch + } + } + headers := packet.(*headerPack).headers + + // If we received a skeleton batch, resolve internals concurrently + if skeleton { + filled, proced, err := d.fillHeaderSkeleton(from, headers) + if err != nil { + glog.V(logger.Debug).Infof("%v: skeleton chain invalid: %v", p, err) + return errInvalidChain + } + headers = filled[proced:] + from += uint64(proced) + } + // Insert all the new headers and fetch the next batch + if len(headers) > 0 { + glog.V(logger.Detail).Infof("%v: schedule %d headers from #%d", p, len(headers), from) + select { + case d.headerProcCh <- headers: + case <-d.cancelCh: + return errCancelHeaderFetch + } + from += uint64(len(headers)) + } + getHeaders(from) + + case <-timeout.C: + // Header retrieval timed out, consider the peer bad and drop + glog.V(logger.Debug).Infof("%v: header request timed out", p) + headerTimeoutMeter.Mark(1) + d.dropPeer(p.id) + + // Finish the sync gracefully instead of dumping the gathered data though + for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} { + select { + case ch <- false: + case <-d.cancelCh: + } + } + select { + case d.headerProcCh <- nil: + case <-d.cancelCh: + } + return errBadPeer + } + } +} + +// fillHeaderSkeleton concurrently retrieves headers from all our available peers +// and maps them to the provided skeleton header chain. +// +// Any partial results from the beginning of the skeleton is (if possible) forwarded +// immediately to the header processor to keep the rest of the pipeline full even +// in the case of header stalls. +// +// The method returs the entire filled skeleton and also the number of headers +// already forwarded for processing. +func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ([]*types.Header, int, error) { + glog.V(logger.Debug).Infof("Filling up skeleton from #%d", from) + d.queue.ScheduleSkeleton(from, skeleton) + + var ( + deliver = func(packet dataPack) (int, error) { + pack := packet.(*headerPack) + return d.queue.DeliverHeaders(pack.peerId, pack.headers, d.headerProcCh) + } + expire = func() map[string]int { return d.queue.ExpireHeaders(d.requestTTL()) } + throttle = func() bool { return false } + reserve = func(p *peer, count int) (*fetchRequest, bool, error) { + return d.queue.ReserveHeaders(p, count), false, nil + } + fetch = func(p *peer, req *fetchRequest) error { return p.FetchHeaders(req.From, MaxHeaderFetch) } + capacity = func(p *peer) int { return p.HeaderCapacity(d.requestRTT()) } + setIdle = func(p *peer, accepted int) { p.SetHeadersIdle(accepted) } + ) + err := d.fetchParts(errCancelHeaderFetch, d.headerCh, deliver, d.queue.headerContCh, expire, + d.queue.PendingHeaders, d.queue.InFlightHeaders, throttle, reserve, + nil, fetch, d.queue.CancelHeaders, capacity, d.peers.HeaderIdlePeers, setIdle, "Header") + + glog.V(logger.Debug).Infof("Skeleton fill terminated: %v", err) + + filled, proced := d.queue.RetrieveHeaders() + return filled, proced, err +} + +// fetchBodies iteratively downloads the scheduled block bodies, taking any +// available peers, reserving a chunk of blocks for each, waiting for delivery +// and also periodically checking for timeouts. +func (d *Downloader) fetchBodies(from uint64) error { + glog.V(logger.Debug).Infof("Downloading block bodies from #%d", from) + + var ( + deliver = func(packet dataPack) (int, error) { + pack := packet.(*bodyPack) + return d.queue.DeliverBodies(pack.peerId, pack.transactions, pack.uncles) + } + expire = func() map[string]int { return d.queue.ExpireBodies(d.requestTTL()) } + fetch = func(p *peer, req *fetchRequest) error { return p.FetchBodies(req) } + capacity = func(p *peer) int { return p.BlockCapacity(d.requestRTT()) } + setIdle = func(p *peer, accepted int) { p.SetBodiesIdle(accepted) } + ) + err := d.fetchParts(errCancelBodyFetch, d.bodyCh, deliver, d.bodyWakeCh, expire, + d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ShouldThrottleBlocks, d.queue.ReserveBodies, + d.bodyFetchHook, fetch, d.queue.CancelBodies, capacity, d.peers.BodyIdlePeers, setIdle, "Body") + + glog.V(logger.Debug).Infof("Block body download terminated: %v", err) + return err +} + +// fetchReceipts iteratively downloads the scheduled block receipts, taking any +// available peers, reserving a chunk of receipts for each, waiting for delivery +// and also periodically checking for timeouts. +func (d *Downloader) fetchReceipts(from uint64) error { + glog.V(logger.Debug).Infof("Downloading receipts from #%d", from) + + var ( + deliver = func(packet dataPack) (int, error) { + pack := packet.(*receiptPack) + return d.queue.DeliverReceipts(pack.peerId, pack.receipts) + } + expire = func() map[string]int { return d.queue.ExpireReceipts(d.requestTTL()) } + fetch = func(p *peer, req *fetchRequest) error { return p.FetchReceipts(req) } + capacity = func(p *peer) int { return p.ReceiptCapacity(d.requestRTT()) } + setIdle = func(p *peer, accepted int) { p.SetReceiptsIdle(accepted) } + ) + err := d.fetchParts(errCancelReceiptFetch, d.receiptCh, deliver, d.receiptWakeCh, expire, + d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ShouldThrottleReceipts, d.queue.ReserveReceipts, + d.receiptFetchHook, fetch, d.queue.CancelReceipts, capacity, d.peers.ReceiptIdlePeers, setIdle, "Receipt") + + glog.V(logger.Debug).Infof("Receipt download terminated: %v", err) + return err +} + +// fetchNodeData iteratively downloads the scheduled state trie nodes, taking any +// available peers, reserving a chunk of nodes for each, waiting for delivery and +// also periodically checking for timeouts. +func (d *Downloader) fetchNodeData() error { + glog.V(logger.Debug).Infof("Downloading node state data") + + var ( + deliver = func(packet dataPack) (int, error) { + start := time.Now() + return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(delivered int, progressed bool, err error) { + // If the peer returned old-requested data, forgive + if err == trie.ErrNotRequested { + glog.V(logger.Debug).Infof("peer %s: replied to stale state request, forgiving", packet.PeerId()) + return + } + if err != nil { + // If the node data processing failed, the root hash is very wrong, abort + glog.V(logger.Error).Infof("peer %d: state processing failed: %v", packet.PeerId(), err) + d.cancel() + return + } + // Processing succeeded, notify state fetcher of continuation + pending := d.queue.PendingNodeData() + if pending > 0 { + select { + case d.stateWakeCh <- true: + default: + } + } + d.syncStatsLock.Lock() + d.syncStatsStateDone += uint64(delivered) + syncStatsStateDone := d.syncStatsStateDone // Thread safe copy for the log below + d.syncStatsLock.Unlock() + + // If real database progress was made, reset any fast-sync pivot failure + if progressed && atomic.LoadUint32(&d.fsPivotFails) > 1 { + glog.V(logger.Debug).Infof("fast-sync progressed, resetting fail counter from %d", atomic.LoadUint32(&d.fsPivotFails)) + atomic.StoreUint32(&d.fsPivotFails, 1) // Don't ever reset to 0, as that will unlock the pivot block + } + // Log a message to the user and return + if delivered > 0 { + glog.V(logger.Info).Infof("imported %3d state entries in %9v: processed %d, pending at least %d", delivered, common.PrettyDuration(time.Since(start)), syncStatsStateDone, pending) + } + }) + } + expire = func() map[string]int { return d.queue.ExpireNodeData(d.requestTTL()) } + throttle = func() bool { return false } + reserve = func(p *peer, count int) (*fetchRequest, bool, error) { + return d.queue.ReserveNodeData(p, count), false, nil + } + fetch = func(p *peer, req *fetchRequest) error { return p.FetchNodeData(req) } + capacity = func(p *peer) int { return p.NodeDataCapacity(d.requestRTT()) } + setIdle = func(p *peer, accepted int) { p.SetNodeDataIdle(accepted) } + ) + err := d.fetchParts(errCancelStateFetch, d.stateCh, deliver, d.stateWakeCh, expire, + d.queue.PendingNodeData, d.queue.InFlightNodeData, throttle, reserve, nil, fetch, + d.queue.CancelNodeData, capacity, d.peers.NodeDataIdlePeers, setIdle, "State") + + glog.V(logger.Debug).Infof("Node state data download terminated: %v", err) + return err +} + +// fetchParts iteratively downloads scheduled block parts, taking any available +// peers, reserving a chunk of fetch requests for each, waiting for delivery and +// also periodically checking for timeouts. +// +// As the scheduling/timeout logic mostly is the same for all downloaded data +// types, this method is used by each for data gathering and is instrumented with +// various callbacks to handle the slight differences between processing them. +// +// The instrumentation parameters: +// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer) +// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers) +// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`) +// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed) +// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping) +// - pending: task callback for the number of requests still needing download (detect completion/non-completability) +// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish) +// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use) +// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions) +// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic) +// - fetch: network callback to actually send a particular download request to a physical remote peer +// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer) +// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping) +// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks +// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping) +// - kind: textual label of the type being downloaded to display in log mesages +func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool, + expire func() map[string]int, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peer, int) (*fetchRequest, bool, error), + fetchHook func([]*types.Header), fetch func(*peer, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peer) int, + idle func() ([]*peer, int), setIdle func(*peer, int), kind string) error { + + // Create a ticker to detect expired retrieval tasks + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + update := make(chan struct{}, 1) + + // Prepare the queue and fetch block parts until the block header fetcher's done + finished := false + for { + select { + case <-d.cancelCh: + return errCancel + + case packet := <-deliveryCh: + // If the peer was previously banned and failed to deliver it's pack + // in a reasonable time frame, ignore it's message. + if peer := d.peers.Peer(packet.PeerId()); peer != nil { + // Deliver the received chunk of data and check chain validity + accepted, err := deliver(packet) + if err == errInvalidChain { + return err + } + // Unless a peer delivered something completely else than requested (usually + // caused by a timed out request which came through in the end), set it to + // idle. If the delivery's stale, the peer should have already been idled. + if err != errStaleDelivery { + setIdle(peer, accepted) + } + // Issue a log to the user to see what's going on + switch { + case err == nil && packet.Items() == 0: + glog.V(logger.Detail).Infof("%s: no %s delivered", peer, strings.ToLower(kind)) + case err == nil: + glog.V(logger.Detail).Infof("%s: delivered %s %s(s)", peer, packet.Stats(), strings.ToLower(kind)) + default: + glog.V(logger.Detail).Infof("%s: %s delivery failed: %v", peer, strings.ToLower(kind), err) + } + } + // Blocks assembled, try to update the progress + select { + case update <- struct{}{}: + default: + } + + case cont := <-wakeCh: + // The header fetcher sent a continuation flag, check if it's done + if !cont { + finished = true + } + // Headers arrive, try to update the progress + select { + case update <- struct{}{}: + default: + } + + case <-ticker.C: + // Sanity check update the progress + select { + case update <- struct{}{}: + default: + } + + case <-update: + // Short circuit if we lost all our peers + if d.peers.Len() == 0 { + return errNoPeers + } + // Check for fetch request timeouts and demote the responsible peers + for pid, fails := range expire() { + if peer := d.peers.Peer(pid); peer != nil { + // If a lot of retrieval elements expired, we might have overestimated the remote peer or perhaps + // ourselves. Only reset to minimal throughput but don't drop just yet. If even the minimal times + // out that sync wise we need to get rid of the peer. + // + // The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth + // and latency of a peer separately, which requires pushing the measures capacity a bit and seeing + // how response times reacts, to it always requests one more than the minimum (i.e. min 2). + if fails > 2 { + glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind)) + setIdle(peer, 0) + } else { + glog.V(logger.Debug).Infof("%s: stalling %s delivery, dropping", peer, strings.ToLower(kind)) + d.dropPeer(pid) + } + } + } + // If there's nothing more to fetch, wait or terminate + if pending() == 0 { + if !inFlight() && finished { + glog.V(logger.Debug).Infof("%s fetching completed", kind) + return nil + } + break + } + // Send a download request to all idle peers, until throttled + progressed, throttled, running := false, false, inFlight() + idles, total := idle() + + for _, peer := range idles { + // Short circuit if throttling activated + if throttle() { + throttled = true + break + } + // Reserve a chunk of fetches for a peer. A nil can mean either that + // no more headers are available, or that the peer is known not to + // have them. + request, progress, err := reserve(peer, capacity(peer)) + if err != nil { + return err + } + if progress { + progressed = true + } + if request == nil { + continue + } + if glog.V(logger.Detail) { + if request.From > 0 { + glog.Infof("%s: requesting %s(s) from #%d", peer, strings.ToLower(kind), request.From) + } else if len(request.Headers) > 0 { + glog.Infof("%s: requesting %d %s(s), first at #%d", peer, len(request.Headers), strings.ToLower(kind), request.Headers[0].Number) + } else { + glog.Infof("%s: requesting %d %s(s)", peer, len(request.Hashes), strings.ToLower(kind)) + } + } + // Fetch the chunk and make sure any errors return the hashes to the queue + if fetchHook != nil { + fetchHook(request.Headers) + } + if err := fetch(peer, request); err != nil { + // Although we could try and make an attempt to fix this, this error really + // means that we've double allocated a fetch task to a peer. If that is the + // case, the internal state of the downloader and the queue is very wrong so + // better hard crash and note the error instead of silently accumulating into + // a much bigger issue. + panic(fmt.Sprintf("%v: %s fetch assignment failed", peer, strings.ToLower(kind))) + } + running = true + } + // Make sure that we have peers available for fetching. If all peers have been tried + // and all failed throw an error + if !progressed && !throttled && !running && len(idles) == total && pending() > 0 { + return errPeersUnavailable + } + } + } +} + +// processHeaders takes batches of retrieved headers from an input channel and +// keeps processing and scheduling them into the header chain and downloader's +// queue until the stream ends or a failure occurs. +func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { + // Calculate the pivoting point for switching from fast to slow sync + pivot := d.queue.FastSyncPivot() + + // Keep a count of uncertain headers to roll back + rollback := []*types.Header{} + defer func() { + if len(rollback) > 0 { + // Flatten the headers and roll them back + hashes := make([]common.Hash, len(rollback)) + for i, header := range rollback { + hashes[i] = header.Hash() + } + lastHeader, lastFastBlock, lastBlock := d.headHeader().Number, common.Big0, common.Big0 + if d.headFastBlock != nil { + lastFastBlock = d.headFastBlock().Number() + } + if d.headBlock != nil { + lastBlock = d.headBlock().Number() + } + d.rollback(hashes) + curFastBlock, curBlock := common.Big0, common.Big0 + if d.headFastBlock != nil { + curFastBlock = d.headFastBlock().Number() + } + if d.headBlock != nil { + curBlock = d.headBlock().Number() + } + glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", + len(hashes), lastHeader, d.headHeader().Number, lastFastBlock, curFastBlock, lastBlock, curBlock) + + // If we're already past the pivot point, this could be an attack, thread carefully + if rollback[len(rollback)-1].Number.Uint64() > pivot { + // If we didn't ever fail, lock in te pivot header (must! not! change!) + if atomic.LoadUint32(&d.fsPivotFails) == 0 { + for _, header := range rollback { + if header.Number.Uint64() == pivot { + glog.V(logger.Warn).Infof("Fast-sync critical section failure, locked pivot to header #%d [%x…]", pivot, header.Hash().Bytes()[:4]) + d.fsPivotLock = header + } + } + } + } + } + }() + + // Wait for batches of headers to process + gotHeaders := false + + for { + select { + case <-d.cancelCh: + return errCancelHeaderProcessing + + case headers := <-d.headerProcCh: + // Terminate header processing if we synced up + if len(headers) == 0 { + // Notify everyone that headers are fully processed + for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} { + select { + case ch <- false: + case <-d.cancelCh: + } + } + // If no headers were retrieved at all, the peer violated it's TD promise that it had a + // better chain compared to ours. The only exception is if it's promised blocks were + // already imported by other means (e.g. fecher): + // + // R , L : Both at block 10 + // R: Mine block 11, and propagate it to L + // L: Queue block 11 for import + // L: Notice that R's head and TD increased compared to ours, start sync + // L: Import of block 11 finishes + // L: Sync begins, and finds common ancestor at 11 + // L: Request new headers up from 11 (R's TD was higher, it must have something) + // R: Nothing to give + if d.mode != LightSync { + if !gotHeaders && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 { + return errStallingPeer + } + } + // If fast or light syncing, ensure promised headers are indeed delivered. This is + // needed to detect scenarios where an attacker feeds a bad pivot and then bails out + // of delivering the post-pivot blocks that would flag the invalid content. + // + // This check cannot be executed "as is" for full imports, since blocks may still be + // queued for processing when the header download completes. However, as long as the + // peer gave us something useful, we're already happy/progressed (above check). + if d.mode == FastSync || d.mode == LightSync { + if td.Cmp(d.getTd(d.headHeader().Hash())) > 0 { + return errStallingPeer + } + } + // Disable any rollback and return + rollback = nil + return nil + } + // Otherwise split the chunk of headers into batches and process them + gotHeaders = true + + for len(headers) > 0 { + // Terminate if something failed in between processing chunks + select { + case <-d.cancelCh: + return errCancelHeaderProcessing + default: + } + // Select the next chunk of headers to import + limit := maxHeadersProcess + if limit > len(headers) { + limit = len(headers) + } + chunk := headers[:limit] + + // In case of header only syncing, validate the chunk immediately + if d.mode == FastSync || d.mode == LightSync { + // Collect the yet unknown headers to mark them as uncertain + unknown := make([]*types.Header, 0, len(headers)) + for _, header := range chunk { + if !d.hasHeader(header.Hash()) { + unknown = append(unknown, header) + } + } + // If we're importing pure headers, verify based on their recentness + frequency := fsHeaderCheckFrequency + if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot { + frequency = 1 + } + if n, err := d.insertHeaders(chunk, frequency); err != nil { + // If some headers were inserted, add them too to the rollback list + if n > 0 { + rollback = append(rollback, chunk[:n]...) + } + glog.V(logger.Debug).Infof("invalid header #%d [%x…]: %v", chunk[n].Number, chunk[n].Hash().Bytes()[:4], err) + return errInvalidChain + } + // All verifications passed, store newly found uncertain headers + rollback = append(rollback, unknown...) + if len(rollback) > fsHeaderSafetyNet { + rollback = append(rollback[:0], rollback[len(rollback)-fsHeaderSafetyNet:]...) + } + } + // If we're fast syncing and just pulled in the pivot, make sure it's the one locked in + if d.mode == FastSync && d.fsPivotLock != nil && chunk[0].Number.Uint64() <= pivot && chunk[len(chunk)-1].Number.Uint64() >= pivot { + if pivot := chunk[int(pivot-chunk[0].Number.Uint64())]; pivot.Hash() != d.fsPivotLock.Hash() { + glog.V(logger.Warn).Infof("Pivot doesn't match locked in version: have #%v [%x…], want #%v [%x…]", pivot.Number, pivot.Hash().Bytes()[:4], d.fsPivotLock.Number, d.fsPivotLock.Hash().Bytes()[:4]) + return errInvalidChain + } + } + // Unless we're doing light chains, schedule the headers for associated content retrieval + if d.mode == FullSync || d.mode == FastSync { + // If we've reached the allowed number of pending headers, stall a bit + for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { + select { + case <-d.cancelCh: + return errCancelHeaderProcessing + case <-time.After(time.Second): + } + } + // Otherwise insert the headers for content retrieval + inserts := d.queue.Schedule(chunk, origin) + if len(inserts) != len(chunk) { + glog.V(logger.Debug).Infof("stale headers") + return errBadPeer + } + } + headers = headers[limit:] + origin += uint64(limit) + } + // Signal the content downloaders of the availablility of new tasks + for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} { + select { + case ch <- true: + default: + } + } + } + } +} + +// processContent takes fetch results from the queue and tries to import them +// into the chain. The type of import operation will depend on the result contents. +func (d *Downloader) processContent() error { + pivot := d.queue.FastSyncPivot() + for { + results := d.queue.WaitResults() + if len(results) == 0 { + return nil // queue empty + } + if d.chainInsertHook != nil { + d.chainInsertHook(results) + } + // Actually import the blocks + if glog.V(logger.Debug) { + first, last := results[0].Header, results[len(results)-1].Header + glog.Infof("Inserting chain with %d items (#%d [%x…] - #%d [%x…])", len(results), first.Number, first.Hash().Bytes()[:4], last.Number, last.Hash().Bytes()[:4]) + } + for len(results) != 0 { + // Check for any termination requests + select { + case <-d.quitCh: + return errCancelContentProcessing + default: + } + // Retrieve the a batch of results to import + var ( + blocks = make([]*types.Block, 0, maxResultsProcess) + receipts = make([]types.Receipts, 0, maxResultsProcess) + ) + items := int(math.Min(float64(len(results)), float64(maxResultsProcess))) + for _, result := range results[:items] { + switch { + case d.mode == FullSync: + blocks = append(blocks, types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)) + case d.mode == FastSync: + blocks = append(blocks, types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)) + if result.Header.Number.Uint64() <= pivot { + receipts = append(receipts, result.Receipts) + } + } + } + // Try to process the results, aborting if there's an error + var ( + err error + index int + ) + switch { + case len(receipts) > 0: + index, err = d.insertReceipts(blocks, receipts) + if err == nil && blocks[len(blocks)-1].NumberU64() == pivot { + glog.V(logger.Debug).Infof("Committing block #%d [%x…] as the new head", blocks[len(blocks)-1].Number(), blocks[len(blocks)-1].Hash().Bytes()[:4]) + index, err = len(blocks)-1, d.commitHeadBlock(blocks[len(blocks)-1].Hash()) + } + default: + index, err = d.insertBlocks(blocks) + } + if err != nil { + glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err) + return errInvalidChain + } + // Shift the results to the next batch + results = results[items:] + } + } +} + +// DeliverHeaders injects a new batch of block headers received from a remote +// node into the download schedule. +func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) { + return d.deliver(id, d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) +} + +// DeliverBodies injects a new batch of block bodies received from a remote node. +func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) (err error) { + return d.deliver(id, d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) +} + +// DeliverReceipts injects a new batch of receipts received from a remote node. +func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) (err error) { + return d.deliver(id, d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter) +} + +// DeliverNodeData injects a new batch of node state data received from a remote node. +func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) { + return d.deliver(id, d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter) +} + +// deliver injects a new batch of data received from a remote node. +func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { + // Update the delivery metrics for both good and failed deliveries + inMeter.Mark(int64(packet.Items())) + defer func() { + if err != nil { + dropMeter.Mark(int64(packet.Items())) + } + }() + // Deliver or abort if the sync is canceled while queuing + d.cancelLock.RLock() + cancel := d.cancelCh + d.cancelLock.RUnlock() + if cancel == nil { + return errNoSyncActive + } + select { + case destCh <- packet: + return nil + case <-cancel: + return errNoSyncActive + } +} + +// qosTuner is the quality of service tuning loop that occasionally gathers the +// peer latency statistics and updates the estimated request round trip time. +func (d *Downloader) qosTuner() { + for { + // Retrieve the current median RTT and integrate into the previoust target RTT + rtt := time.Duration(float64(1-qosTuningImpact)*float64(atomic.LoadUint64(&d.rttEstimate)) + qosTuningImpact*float64(d.peers.medianRTT())) + atomic.StoreUint64(&d.rttEstimate, uint64(rtt)) + + // A new RTT cycle passed, increase our confidence in the estimated RTT + conf := atomic.LoadUint64(&d.rttConfidence) + conf = conf + (1000000-conf)/2 + atomic.StoreUint64(&d.rttConfidence, conf) + + // Log the new QoS values and sleep until the next RTT + glog.V(logger.Debug).Infof("Quality of service: rtt %v, conf %.3f, ttl %v", rtt, float64(conf)/1000000.0, d.requestTTL()) + select { + case <-d.quitCh: + return + case <-time.After(rtt): + } + } +} + +// qosReduceConfidence is meant to be called when a new peer joins the downloader's +// peer set, needing to reduce the confidence we have in out QoS estimates. +func (d *Downloader) qosReduceConfidence() { + // If we have a single peer, confidence is always 1 + peers := uint64(d.peers.Len()) + if peers == 1 { + atomic.StoreUint64(&d.rttConfidence, 1000000) + return + } + // If we have a ton of peers, don't drop confidence) + if peers >= uint64(qosConfidenceCap) { + return + } + // Otherwise drop the confidence factor + conf := atomic.LoadUint64(&d.rttConfidence) * (peers - 1) / peers + if float64(conf)/1000000 < rttMinConfidence { + conf = uint64(rttMinConfidence * 1000000) + } + atomic.StoreUint64(&d.rttConfidence, conf) + + rtt := time.Duration(atomic.LoadUint64(&d.rttEstimate)) + glog.V(logger.Debug).Infof("Quality of service: rtt %v, conf %.3f, ttl %v", rtt, float64(conf)/1000000.0, d.requestTTL()) +} + +// requestRTT returns the current target round trip time for a download request +// to complete in. +// +// Note, the returned RTT is .9 of the actually estimated RTT. The reason is that +// the downloader tries to adapt queries to the RTT, so multiple RTT values can +// be adapted to, but smaller ones are preffered (stabler download stream). +func (d *Downloader) requestRTT() time.Duration { + return time.Duration(atomic.LoadUint64(&d.rttEstimate)) * 9 / 10 +} + +// requestTTL returns the current timeout allowance for a single download request +// to finish under. +func (d *Downloader) requestTTL() time.Duration { + var ( + rtt = time.Duration(atomic.LoadUint64(&d.rttEstimate)) + conf = float64(atomic.LoadUint64(&d.rttConfidence)) / 1000000.0 + ) + ttl := time.Duration(ttlScaling) * time.Duration(float64(rtt)/conf) + if ttl > ttlLimit { + ttl = ttlLimit + } + return ttl +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader_test.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader_test.go new file mode 100644 index 0000000..a9ea797 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader_test.go @@ -0,0 +1,1797 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "errors" + "fmt" + "math/big" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddress = crypto.PubkeyToAddress(testKey.PublicKey) +) + +// Reduce some of the parameters to make the tester faster. +func init() { + MaxForkAncestry = uint64(10000) + blockCacheLimit = 1024 + fsCriticalTrials = 10 +} + +// downloadTester is a test simulator for mocking out local block chain. +type downloadTester struct { + downloader *Downloader + + genesis *types.Block // Genesis blocks used by the tester and peers + stateDb ethdb.Database // Database used by the tester for syncing from peers + peerDb ethdb.Database // Database of the peers containing all data + + ownHashes []common.Hash // Hash chain belonging to the tester + ownHeaders map[common.Hash]*types.Header // Headers belonging to the tester + ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester + ownReceipts map[common.Hash]types.Receipts // Receipts belonging to the tester + ownChainTd map[common.Hash]*big.Int // Total difficulties of the blocks in the local chain + + peerHashes map[string][]common.Hash // Hash chain belonging to different test peers + peerHeaders map[string]map[common.Hash]*types.Header // Headers belonging to different test peers + peerBlocks map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers + peerReceipts map[string]map[common.Hash]types.Receipts // Receipts belonging to different test peers + peerChainTds map[string]map[common.Hash]*big.Int // Total difficulties of the blocks in the peer chains + + peerMissingStates map[string]map[common.Hash]bool // State entries that fast sync should not return + + lock sync.RWMutex +} + +// newTester creates a new downloader test mocker. +func newTester() *downloadTester { + testdb, _ := ethdb.NewMemDatabase() + genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) + + tester := &downloadTester{ + genesis: genesis, + peerDb: testdb, + ownHashes: []common.Hash{genesis.Hash()}, + ownHeaders: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()}, + ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, + ownReceipts: map[common.Hash]types.Receipts{genesis.Hash(): nil}, + ownChainTd: map[common.Hash]*big.Int{genesis.Hash(): genesis.Difficulty()}, + peerHashes: make(map[string][]common.Hash), + peerHeaders: make(map[string]map[common.Hash]*types.Header), + peerBlocks: make(map[string]map[common.Hash]*types.Block), + peerReceipts: make(map[string]map[common.Hash]types.Receipts), + peerChainTds: make(map[string]map[common.Hash]*big.Int), + peerMissingStates: make(map[string]map[common.Hash]bool), + } + tester.stateDb, _ = ethdb.NewMemDatabase() + tester.stateDb.Put(genesis.Root().Bytes(), []byte{0x00}) + + tester.downloader = New(FullSync, tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader, + tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd, + tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer) + + return tester +} + +// makeChain creates a chain of n blocks starting at and including parent. +// the returned hash chain is ordered head->parent. In addition, every 3rd block +// contains a transaction and every 5th an uncle to allow testing correct block +// reassembly. +func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) { + // Generate the block chain + blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, dl.peerDb, n, func(i int, block *core.BlockGen) { + block.SetCoinbase(common.Address{seed}) + + // If a heavy chain is requested, delay blocks to raise difficulty + if heavy { + block.OffsetTime(-1) + } + // If the block number is multiple of 3, send a bonus transaction to the miner + if parent == dl.genesis && i%3 == 0 { + signer := types.MakeSigner(params.TestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey) + if err != nil { + panic(err) + } + block.AddTx(tx) + } + // If the block number is a multiple of 5, add a bonus uncle to the block + if i > 0 && i%5 == 0 { + block.AddUncle(&types.Header{ + ParentHash: block.PrevBlock(i - 1).Hash(), + Number: big.NewInt(block.Number().Int64() - 1), + }) + } + }) + // Convert the block-chain into a hash-chain and header/block maps + hashes := make([]common.Hash, n+1) + hashes[len(hashes)-1] = parent.Hash() + + headerm := make(map[common.Hash]*types.Header, n+1) + headerm[parent.Hash()] = parent.Header() + + blockm := make(map[common.Hash]*types.Block, n+1) + blockm[parent.Hash()] = parent + + receiptm := make(map[common.Hash]types.Receipts, n+1) + receiptm[parent.Hash()] = parentReceipts + + for i, b := range blocks { + hashes[len(hashes)-i-2] = b.Hash() + headerm[b.Hash()] = b.Header() + blockm[b.Hash()] = b + receiptm[b.Hash()] = receipts[i] + } + return hashes, headerm, blockm, receiptm +} + +// makeChainFork creates two chains of length n, such that h1[:f] and +// h2[:f] are different but have a common suffix of length n-f. +func (dl *downloadTester) makeChainFork(n, f int, parent *types.Block, parentReceipts types.Receipts, balanced bool) ([]common.Hash, []common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]*types.Block, map[common.Hash]types.Receipts, map[common.Hash]types.Receipts) { + // Create the common suffix + hashes, headers, blocks, receipts := dl.makeChain(n-f, 0, parent, parentReceipts, false) + + // Create the forks, making the second heavyer if non balanced forks were requested + hashes1, headers1, blocks1, receipts1 := dl.makeChain(f, 1, blocks[hashes[0]], receipts[hashes[0]], false) + hashes1 = append(hashes1, hashes[1:]...) + + heavy := false + if !balanced { + heavy = true + } + hashes2, headers2, blocks2, receipts2 := dl.makeChain(f, 2, blocks[hashes[0]], receipts[hashes[0]], heavy) + hashes2 = append(hashes2, hashes[1:]...) + + for hash, header := range headers { + headers1[hash] = header + headers2[hash] = header + } + for hash, block := range blocks { + blocks1[hash] = block + blocks2[hash] = block + } + for hash, receipt := range receipts { + receipts1[hash] = receipt + receipts2[hash] = receipt + } + return hashes1, hashes2, headers1, headers2, blocks1, blocks2, receipts1, receipts2 +} + +// terminate aborts any operations on the embedded downloader and releases all +// held resources. +func (dl *downloadTester) terminate() { + dl.downloader.Terminate() +} + +// sync starts synchronizing with a remote peer, blocking until it completes. +func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error { + dl.lock.RLock() + hash := dl.peerHashes[id][0] + // If no particular TD was requested, load from the peer's blockchain + if td == nil { + td = big.NewInt(1) + if diff, ok := dl.peerChainTds[id][hash]; ok { + td = diff + } + } + dl.lock.RUnlock() + + // Synchronise with the chosen peer and ensure proper cleanup afterwards + err := dl.downloader.synchronise(id, hash, td, mode) + select { + case <-dl.downloader.cancelCh: + // Ok, downloader fully cancelled after sync cycle + default: + // Downloader is still accepting packets, can block a peer up + panic("downloader active post sync cycle") // panic will be caught by tester + } + return err +} + +// hasHeader checks if a header is present in the testers canonical chain. +func (dl *downloadTester) hasHeader(hash common.Hash) bool { + return dl.getHeader(hash) != nil +} + +// hasBlock checks if a block and associated state is present in the testers canonical chain. +func (dl *downloadTester) hasBlock(hash common.Hash) bool { + block := dl.getBlock(hash) + if block == nil { + return false + } + _, err := dl.stateDb.Get(block.Root().Bytes()) + return err == nil +} + +// getHeader retrieves a header from the testers canonical chain. +func (dl *downloadTester) getHeader(hash common.Hash) *types.Header { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.ownHeaders[hash] +} + +// getBlock retrieves a block from the testers canonical chain. +func (dl *downloadTester) getBlock(hash common.Hash) *types.Block { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.ownBlocks[hash] +} + +// headHeader retrieves the current head header from the canonical chain. +func (dl *downloadTester) headHeader() *types.Header { + dl.lock.RLock() + defer dl.lock.RUnlock() + + for i := len(dl.ownHashes) - 1; i >= 0; i-- { + if header := dl.ownHeaders[dl.ownHashes[i]]; header != nil { + return header + } + } + return dl.genesis.Header() +} + +// headBlock retrieves the current head block from the canonical chain. +func (dl *downloadTester) headBlock() *types.Block { + dl.lock.RLock() + defer dl.lock.RUnlock() + + for i := len(dl.ownHashes) - 1; i >= 0; i-- { + if block := dl.ownBlocks[dl.ownHashes[i]]; block != nil { + if _, err := dl.stateDb.Get(block.Root().Bytes()); err == nil { + return block + } + } + } + return dl.genesis +} + +// headFastBlock retrieves the current head fast-sync block from the canonical chain. +func (dl *downloadTester) headFastBlock() *types.Block { + dl.lock.RLock() + defer dl.lock.RUnlock() + + for i := len(dl.ownHashes) - 1; i >= 0; i-- { + if block := dl.ownBlocks[dl.ownHashes[i]]; block != nil { + return block + } + } + return dl.genesis +} + +// commitHeadBlock manually sets the head block to a given hash. +func (dl *downloadTester) commitHeadBlock(hash common.Hash) error { + // For now only check that the state trie is correct + if block := dl.getBlock(hash); block != nil { + _, err := trie.NewSecure(block.Root(), dl.stateDb, 0) + return err + } + return fmt.Errorf("non existent block: %x", hash[:4]) +} + +// getTd retrieves the block's total difficulty from the canonical chain. +func (dl *downloadTester) getTd(hash common.Hash) *big.Int { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.ownChainTd[hash] +} + +// insertHeaders injects a new batch of headers into the simulated chain. +func (dl *downloadTester) insertHeaders(headers []*types.Header, checkFreq int) (int, error) { + dl.lock.Lock() + defer dl.lock.Unlock() + + // Do a quick check, as the blockchain.InsertHeaderChain doesn't insert anything in case of errors + if _, ok := dl.ownHeaders[headers[0].ParentHash]; !ok { + return 0, errors.New("unknown parent") + } + for i := 1; i < len(headers); i++ { + if headers[i].ParentHash != headers[i-1].Hash() { + return i, errors.New("unknown parent") + } + } + // Do a full insert if pre-checks passed + for i, header := range headers { + if _, ok := dl.ownHeaders[header.Hash()]; ok { + continue + } + if _, ok := dl.ownHeaders[header.ParentHash]; !ok { + return i, errors.New("unknown parent") + } + dl.ownHashes = append(dl.ownHashes, header.Hash()) + dl.ownHeaders[header.Hash()] = header + dl.ownChainTd[header.Hash()] = new(big.Int).Add(dl.ownChainTd[header.ParentHash], header.Difficulty) + } + return len(headers), nil +} + +// insertBlocks injects a new batch of blocks into the simulated chain. +func (dl *downloadTester) insertBlocks(blocks types.Blocks) (int, error) { + dl.lock.Lock() + defer dl.lock.Unlock() + + for i, block := range blocks { + if parent, ok := dl.ownBlocks[block.ParentHash()]; !ok { + return i, errors.New("unknown parent") + } else if _, err := dl.stateDb.Get(parent.Root().Bytes()); err != nil { + return i, fmt.Errorf("unknown parent state %x: %v", parent.Root(), err) + } + if _, ok := dl.ownHeaders[block.Hash()]; !ok { + dl.ownHashes = append(dl.ownHashes, block.Hash()) + dl.ownHeaders[block.Hash()] = block.Header() + } + dl.ownBlocks[block.Hash()] = block + dl.stateDb.Put(block.Root().Bytes(), []byte{0x00}) + dl.ownChainTd[block.Hash()] = new(big.Int).Add(dl.ownChainTd[block.ParentHash()], block.Difficulty()) + } + return len(blocks), nil +} + +// insertReceipts injects a new batch of receipts into the simulated chain. +func (dl *downloadTester) insertReceipts(blocks types.Blocks, receipts []types.Receipts) (int, error) { + dl.lock.Lock() + defer dl.lock.Unlock() + + for i := 0; i < len(blocks) && i < len(receipts); i++ { + if _, ok := dl.ownHeaders[blocks[i].Hash()]; !ok { + return i, errors.New("unknown owner") + } + if _, ok := dl.ownBlocks[blocks[i].ParentHash()]; !ok { + return i, errors.New("unknown parent") + } + dl.ownBlocks[blocks[i].Hash()] = blocks[i] + dl.ownReceipts[blocks[i].Hash()] = receipts[i] + } + return len(blocks), nil +} + +// rollback removes some recently added elements from the chain. +func (dl *downloadTester) rollback(hashes []common.Hash) { + dl.lock.Lock() + defer dl.lock.Unlock() + + for i := len(hashes) - 1; i >= 0; i-- { + if dl.ownHashes[len(dl.ownHashes)-1] == hashes[i] { + dl.ownHashes = dl.ownHashes[:len(dl.ownHashes)-1] + } + delete(dl.ownChainTd, hashes[i]) + delete(dl.ownHeaders, hashes[i]) + delete(dl.ownReceipts, hashes[i]) + delete(dl.ownBlocks, hashes[i]) + } +} + +// newPeer registers a new block download source into the downloader. +func (dl *downloadTester) newPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts) error { + return dl.newSlowPeer(id, version, hashes, headers, blocks, receipts, 0) +} + +// newSlowPeer registers a new block download source into the downloader, with a +// specific delay time on processing the network packets sent to it, simulating +// potentially slow network IO. +func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Hash, headers map[common.Hash]*types.Header, blocks map[common.Hash]*types.Block, receipts map[common.Hash]types.Receipts, delay time.Duration) error { + dl.lock.Lock() + defer dl.lock.Unlock() + + var err error + switch version { + case 62: + err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil) + case 63: + err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) + case 64: + err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay)) + } + if err == nil { + // Assign the owned hashes, headers and blocks to the peer (deep copy) + dl.peerHashes[id] = make([]common.Hash, len(hashes)) + copy(dl.peerHashes[id], hashes) + + dl.peerHeaders[id] = make(map[common.Hash]*types.Header) + dl.peerBlocks[id] = make(map[common.Hash]*types.Block) + dl.peerReceipts[id] = make(map[common.Hash]types.Receipts) + dl.peerChainTds[id] = make(map[common.Hash]*big.Int) + dl.peerMissingStates[id] = make(map[common.Hash]bool) + + genesis := hashes[len(hashes)-1] + if header := headers[genesis]; header != nil { + dl.peerHeaders[id][genesis] = header + dl.peerChainTds[id][genesis] = header.Difficulty + } + if block := blocks[genesis]; block != nil { + dl.peerBlocks[id][genesis] = block + dl.peerChainTds[id][genesis] = block.Difficulty() + } + + for i := len(hashes) - 2; i >= 0; i-- { + hash := hashes[i] + + if header, ok := headers[hash]; ok { + dl.peerHeaders[id][hash] = header + if _, ok := dl.peerHeaders[id][header.ParentHash]; ok { + dl.peerChainTds[id][hash] = new(big.Int).Add(header.Difficulty, dl.peerChainTds[id][header.ParentHash]) + } + } + if block, ok := blocks[hash]; ok { + dl.peerBlocks[id][hash] = block + if _, ok := dl.peerBlocks[id][block.ParentHash()]; ok { + dl.peerChainTds[id][hash] = new(big.Int).Add(block.Difficulty(), dl.peerChainTds[id][block.ParentHash()]) + } + } + if receipt, ok := receipts[hash]; ok { + dl.peerReceipts[id][hash] = receipt + } + } + } + return err +} + +// dropPeer simulates a hard peer removal from the connection pool. +func (dl *downloadTester) dropPeer(id string) { + dl.lock.Lock() + defer dl.lock.Unlock() + + delete(dl.peerHashes, id) + delete(dl.peerHeaders, id) + delete(dl.peerBlocks, id) + delete(dl.peerChainTds, id) + + dl.downloader.UnregisterPeer(id) +} + +// peerCurrentHeadFn constructs a function to retrieve a peer's current head hash +// and total difficulty. +func (dl *downloadTester) peerCurrentHeadFn(id string) func() (common.Hash, *big.Int) { + return func() (common.Hash, *big.Int) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + return dl.peerHashes[id][0], nil + } +} + +// peerGetRelHeadersFn constructs a GetBlockHeaders function based on a hashed +// origin; associated with a particular peer in the download tester. The returned +// function can be used to retrieve batches of headers from the particular peer. +func (dl *downloadTester) peerGetRelHeadersFn(id string, delay time.Duration) func(common.Hash, int, int, bool) error { + return func(origin common.Hash, amount int, skip int, reverse bool) error { + // Find the canonical number of the hash + dl.lock.RLock() + number := uint64(0) + for num, hash := range dl.peerHashes[id] { + if hash == origin { + number = uint64(len(dl.peerHashes[id]) - num - 1) + break + } + } + dl.lock.RUnlock() + + // Use the absolute header fetcher to satisfy the query + return dl.peerGetAbsHeadersFn(id, delay)(number, amount, skip, reverse) + } +} + +// peerGetAbsHeadersFn constructs a GetBlockHeaders function based on a numbered +// origin; associated with a particular peer in the download tester. The returned +// function can be used to retrieve batches of headers from the particular peer. +func (dl *downloadTester) peerGetAbsHeadersFn(id string, delay time.Duration) func(uint64, int, int, bool) error { + return func(origin uint64, amount int, skip int, reverse bool) error { + time.Sleep(delay) + + dl.lock.RLock() + defer dl.lock.RUnlock() + + // Gather the next batch of headers + hashes := dl.peerHashes[id] + headers := dl.peerHeaders[id] + result := make([]*types.Header, 0, amount) + for i := 0; i < amount && len(hashes)-int(origin)-1-i*(skip+1) >= 0; i++ { + if header, ok := headers[hashes[len(hashes)-int(origin)-1-i*(skip+1)]]; ok { + result = append(result, header) + } + } + // Delay delivery a bit to allow attacks to unfold + go func() { + time.Sleep(time.Millisecond) + dl.downloader.DeliverHeaders(id, result) + }() + return nil + } +} + +// peerGetBodiesFn constructs a getBlockBodies method associated with a particular +// peer in the download tester. The returned function can be used to retrieve +// batches of block bodies from the particularly requested peer. +func (dl *downloadTester) peerGetBodiesFn(id string, delay time.Duration) func([]common.Hash) error { + return func(hashes []common.Hash) error { + time.Sleep(delay) + + dl.lock.RLock() + defer dl.lock.RUnlock() + + blocks := dl.peerBlocks[id] + + transactions := make([][]*types.Transaction, 0, len(hashes)) + uncles := make([][]*types.Header, 0, len(hashes)) + + for _, hash := range hashes { + if block, ok := blocks[hash]; ok { + transactions = append(transactions, block.Transactions()) + uncles = append(uncles, block.Uncles()) + } + } + go dl.downloader.DeliverBodies(id, transactions, uncles) + + return nil + } +} + +// peerGetReceiptsFn constructs a getReceipts method associated with a particular +// peer in the download tester. The returned function can be used to retrieve +// batches of block receipts from the particularly requested peer. +func (dl *downloadTester) peerGetReceiptsFn(id string, delay time.Duration) func([]common.Hash) error { + return func(hashes []common.Hash) error { + time.Sleep(delay) + + dl.lock.RLock() + defer dl.lock.RUnlock() + + receipts := dl.peerReceipts[id] + + results := make([][]*types.Receipt, 0, len(hashes)) + for _, hash := range hashes { + if receipt, ok := receipts[hash]; ok { + results = append(results, receipt) + } + } + go dl.downloader.DeliverReceipts(id, results) + + return nil + } +} + +// peerGetNodeDataFn constructs a getNodeData method associated with a particular +// peer in the download tester. The returned function can be used to retrieve +// batches of node state data from the particularly requested peer. +func (dl *downloadTester) peerGetNodeDataFn(id string, delay time.Duration) func([]common.Hash) error { + return func(hashes []common.Hash) error { + time.Sleep(delay) + + dl.lock.RLock() + defer dl.lock.RUnlock() + + results := make([][]byte, 0, len(hashes)) + for _, hash := range hashes { + if data, err := dl.peerDb.Get(hash.Bytes()); err == nil { + if !dl.peerMissingStates[id][hash] { + results = append(results, data) + } + } + } + go dl.downloader.DeliverNodeData(id, results) + + return nil + } +} + +// assertOwnChain checks if the local chain contains the correct number of items +// of the various chain components. +func assertOwnChain(t *testing.T, tester *downloadTester, length int) { + assertOwnForkedChain(t, tester, 1, []int{length}) +} + +// assertOwnForkedChain checks if the local forked chain contains the correct +// number of items of the various chain components. +func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, lengths []int) { + // Initialize the counters for the first fork + headers, blocks := lengths[0], lengths[0] + + minReceipts, maxReceipts := lengths[0]-fsMinFullBlocks-fsPivotInterval, lengths[0]-fsMinFullBlocks + if minReceipts < 0 { + minReceipts = 1 + } + if maxReceipts < 0 { + maxReceipts = 1 + } + // Update the counters for each subsequent fork + for _, length := range lengths[1:] { + headers += length - common + blocks += length - common + + minReceipts += length - common - fsMinFullBlocks - fsPivotInterval + maxReceipts += length - common - fsMinFullBlocks + } + switch tester.downloader.mode { + case FullSync: + minReceipts, maxReceipts = 1, 1 + case LightSync: + blocks, minReceipts, maxReceipts = 1, 1, 1 + } + if hs := len(tester.ownHeaders); hs != headers { + t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers) + } + if bs := len(tester.ownBlocks); bs != blocks { + t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks) + } + if rs := len(tester.ownReceipts); rs < minReceipts || rs > maxReceipts { + t.Fatalf("synchronised receipts mismatch: have %v, want between [%v, %v]", rs, minReceipts, maxReceipts) + } + // Verify the state trie too for fast syncs + if tester.downloader.mode == FastSync { + var index int + if pivot := int(tester.downloader.queue.fastSyncPivot); pivot < common { + index = pivot + } else { + index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot) + } + if index > 0 { + if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, tester.stateDb); statedb == nil || err != nil { + t.Fatalf("state reconstruction failed: %v", err) + } + } + } +} + +// Tests that simple synchronization against a canonical chain works correctly. +// In this test common ancestor lookup should be short circuited and not require +// binary searching. +func TestCanonicalSynchronisation62(t *testing.T) { testCanonicalSynchronisation(t, 62, FullSync) } +func TestCanonicalSynchronisation63Full(t *testing.T) { testCanonicalSynchronisation(t, 63, FullSync) } +func TestCanonicalSynchronisation63Fast(t *testing.T) { testCanonicalSynchronisation(t, 63, FastSync) } +func TestCanonicalSynchronisation64Full(t *testing.T) { testCanonicalSynchronisation(t, 64, FullSync) } +func TestCanonicalSynchronisation64Fast(t *testing.T) { testCanonicalSynchronisation(t, 64, FastSync) } +func TestCanonicalSynchronisation64Light(t *testing.T) { testCanonicalSynchronisation(t, 64, LightSync) } + +func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + + // Synchronise with the peer and make sure all relevant data was retrieved + if err := tester.sync("peer", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) +} + +// Tests that if a large batch of blocks are being downloaded, it is throttled +// until the cached blocks are retrieved. +func TestThrottling62(t *testing.T) { testThrottling(t, 62, FullSync) } +func TestThrottling63Full(t *testing.T) { testThrottling(t, 63, FullSync) } +func TestThrottling63Fast(t *testing.T) { testThrottling(t, 63, FastSync) } +func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) } +func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) } + +func testThrottling(t *testing.T, protocol int, mode SyncMode) { + tester := newTester() + defer tester.terminate() + + // Create a long block chain to download and the tester + targetBlocks := 8 * blockCacheLimit + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + + // Wrap the importer to allow stepping + blocked, proceed := uint32(0), make(chan struct{}) + tester.downloader.chainInsertHook = func(results []*fetchResult) { + atomic.StoreUint32(&blocked, uint32(len(results))) + <-proceed + } + // Start a synchronisation concurrently + errc := make(chan error) + go func() { + errc <- tester.sync("peer", nil, mode) + }() + // Iteratively take some blocks, always checking the retrieval count + for { + // Check the retrieval count synchronously (! reason for this ugly block) + tester.lock.RLock() + retrieved := len(tester.ownBlocks) + tester.lock.RUnlock() + if retrieved >= targetBlocks+1 { + break + } + // Wait a bit for sync to throttle itself + var cached, frozen int + for start := time.Now(); time.Since(start) < 3*time.Second; { + time.Sleep(25 * time.Millisecond) + + tester.lock.Lock() + tester.downloader.queue.lock.Lock() + cached = len(tester.downloader.queue.blockDonePool) + if mode == FastSync { + if receipts := len(tester.downloader.queue.receiptDonePool); receipts < cached { + if tester.downloader.queue.resultCache[receipts].Header.Number.Uint64() < tester.downloader.queue.fastSyncPivot { + cached = receipts + } + } + } + frozen = int(atomic.LoadUint32(&blocked)) + retrieved = len(tester.ownBlocks) + tester.downloader.queue.lock.Unlock() + tester.lock.Unlock() + + if cached == blockCacheLimit || retrieved+cached+frozen == targetBlocks+1 { + break + } + } + // Make sure we filled up the cache, then exhaust it + time.Sleep(25 * time.Millisecond) // give it a chance to screw up + + tester.lock.RLock() + retrieved = len(tester.ownBlocks) + tester.lock.RUnlock() + if cached != blockCacheLimit && retrieved+cached+frozen != targetBlocks+1 { + t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheLimit, retrieved, frozen, targetBlocks+1) + } + // Permit the blocked blocks to import + if atomic.LoadUint32(&blocked) > 0 { + atomic.StoreUint32(&blocked, uint32(0)) + proceed <- struct{}{} + } + } + // Check that we haven't pulled more blocks than available + assertOwnChain(t, tester, targetBlocks+1) + if err := <-errc; err != nil { + t.Fatalf("block synchronization failed: %v", err) + } +} + +// Tests that simple synchronization against a forked chain works correctly. In +// this test common ancestor lookup should *not* be short circuited, and a full +// binary search should be executed. +func TestForkedSync62(t *testing.T) { testForkedSync(t, 62, FullSync) } +func TestForkedSync63Full(t *testing.T) { testForkedSync(t, 63, FullSync) } +func TestForkedSync63Fast(t *testing.T) { testForkedSync(t, 63, FastSync) } +func TestForkedSync64Full(t *testing.T) { testForkedSync(t, 64, FullSync) } +func TestForkedSync64Fast(t *testing.T) { testForkedSync(t, 64, FastSync) } +func TestForkedSync64Light(t *testing.T) { testForkedSync(t, 64, LightSync) } + +func testForkedSync(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a long enough forked chain + common, fork := MaxHashFetch, 2*MaxHashFetch + hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := tester.makeChainFork(common+fork, fork, tester.genesis, nil, true) + + tester.newPeer("fork A", protocol, hashesA, headersA, blocksA, receiptsA) + tester.newPeer("fork B", protocol, hashesB, headersB, blocksB, receiptsB) + + // Synchronise with the peer and make sure all blocks were retrieved + if err := tester.sync("fork A", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, common+fork+1) + + // Synchronise with the second peer and make sure that fork is pulled too + if err := tester.sync("fork B", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnForkedChain(t, tester, common+1, []int{common + fork + 1, common + fork + 1}) +} + +// Tests that synchronising against a much shorter but much heavyer fork works +// corrently and is not dropped. +func TestHeavyForkedSync62(t *testing.T) { testHeavyForkedSync(t, 62, FullSync) } +func TestHeavyForkedSync63Full(t *testing.T) { testHeavyForkedSync(t, 63, FullSync) } +func TestHeavyForkedSync63Fast(t *testing.T) { testHeavyForkedSync(t, 63, FastSync) } +func TestHeavyForkedSync64Full(t *testing.T) { testHeavyForkedSync(t, 64, FullSync) } +func TestHeavyForkedSync64Fast(t *testing.T) { testHeavyForkedSync(t, 64, FastSync) } +func TestHeavyForkedSync64Light(t *testing.T) { testHeavyForkedSync(t, 64, LightSync) } + +func testHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a long enough forked chain + common, fork := MaxHashFetch, 4*MaxHashFetch + hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := tester.makeChainFork(common+fork, fork, tester.genesis, nil, false) + + tester.newPeer("light", protocol, hashesA, headersA, blocksA, receiptsA) + tester.newPeer("heavy", protocol, hashesB[fork/2:], headersB, blocksB, receiptsB) + + // Synchronise with the peer and make sure all blocks were retrieved + if err := tester.sync("light", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, common+fork+1) + + // Synchronise with the second peer and make sure that fork is pulled too + if err := tester.sync("heavy", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnForkedChain(t, tester, common+1, []int{common + fork + 1, common + fork/2 + 1}) +} + +// Tests that chain forks are contained within a certain interval of the current +// chain head, ensuring that malicious peers cannot waste resources by feeding +// long dead chains. +func TestBoundedForkedSync62(t *testing.T) { testBoundedForkedSync(t, 62, FullSync) } +func TestBoundedForkedSync63Full(t *testing.T) { testBoundedForkedSync(t, 63, FullSync) } +func TestBoundedForkedSync63Fast(t *testing.T) { testBoundedForkedSync(t, 63, FastSync) } +func TestBoundedForkedSync64Full(t *testing.T) { testBoundedForkedSync(t, 64, FullSync) } +func TestBoundedForkedSync64Fast(t *testing.T) { testBoundedForkedSync(t, 64, FastSync) } +func TestBoundedForkedSync64Light(t *testing.T) { testBoundedForkedSync(t, 64, LightSync) } + +func testBoundedForkedSync(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a long enough forked chain + common, fork := 13, int(MaxForkAncestry+17) + hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := tester.makeChainFork(common+fork, fork, tester.genesis, nil, true) + + tester.newPeer("original", protocol, hashesA, headersA, blocksA, receiptsA) + tester.newPeer("rewriter", protocol, hashesB, headersB, blocksB, receiptsB) + + // Synchronise with the peer and make sure all blocks were retrieved + if err := tester.sync("original", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, common+fork+1) + + // Synchronise with the second peer and ensure that the fork is rejected to being too old + if err := tester.sync("rewriter", nil, mode); err != errInvalidAncestor { + t.Fatalf("sync failure mismatch: have %v, want %v", err, errInvalidAncestor) + } +} + +// Tests that chain forks are contained within a certain interval of the current +// chain head for short but heavy forks too. These are a bit special because they +// take different ancestor lookup paths. +func TestBoundedHeavyForkedSync62(t *testing.T) { testBoundedHeavyForkedSync(t, 62, FullSync) } +func TestBoundedHeavyForkedSync63Full(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FullSync) } +func TestBoundedHeavyForkedSync63Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FastSync) } +func TestBoundedHeavyForkedSync64Full(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FullSync) } +func TestBoundedHeavyForkedSync64Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 64, FastSync) } +func TestBoundedHeavyForkedSync64Light(t *testing.T) { testBoundedHeavyForkedSync(t, 64, LightSync) } + +func testBoundedHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a long enough forked chain + common, fork := 13, int(MaxForkAncestry+17) + hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := tester.makeChainFork(common+fork, fork, tester.genesis, nil, false) + + tester.newPeer("original", protocol, hashesA, headersA, blocksA, receiptsA) + tester.newPeer("heavy-rewriter", protocol, hashesB[MaxForkAncestry-17:], headersB, blocksB, receiptsB) // Root the fork below the ancestor limit + + // Synchronise with the peer and make sure all blocks were retrieved + if err := tester.sync("original", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, common+fork+1) + + // Synchronise with the second peer and ensure that the fork is rejected to being too old + if err := tester.sync("heavy-rewriter", nil, mode); err != errInvalidAncestor { + t.Fatalf("sync failure mismatch: have %v, want %v", err, errInvalidAncestor) + } +} + +// Tests that an inactive downloader will not accept incoming block headers and +// bodies. +func TestInactiveDownloader62(t *testing.T) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Check that neither block headers nor bodies are accepted + if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive { + t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) + } + if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive { + t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) + } +} + +// Tests that an inactive downloader will not accept incoming block headers, +// bodies and receipts. +func TestInactiveDownloader63(t *testing.T) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Check that neither block headers nor bodies are accepted + if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive { + t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) + } + if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive { + t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) + } + if err := tester.downloader.DeliverReceipts("bad peer", [][]*types.Receipt{}); err != errNoSyncActive { + t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) + } +} + +// Tests that a canceled download wipes all previously accumulated state. +func TestCancel62(t *testing.T) { testCancel(t, 62, FullSync) } +func TestCancel63Full(t *testing.T) { testCancel(t, 63, FullSync) } +func TestCancel63Fast(t *testing.T) { testCancel(t, 63, FastSync) } +func TestCancel64Full(t *testing.T) { testCancel(t, 64, FullSync) } +func TestCancel64Fast(t *testing.T) { testCancel(t, 64, FastSync) } +func TestCancel64Light(t *testing.T) { testCancel(t, 64, LightSync) } + +func testCancel(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download and the tester + targetBlocks := blockCacheLimit - 15 + if targetBlocks >= MaxHashFetch { + targetBlocks = MaxHashFetch - 15 + } + if targetBlocks >= MaxHeaderFetch { + targetBlocks = MaxHeaderFetch - 15 + } + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + + // Make sure canceling works with a pristine downloader + tester.downloader.cancel() + if !tester.downloader.queue.Idle() { + t.Errorf("download queue not idle") + } + // Synchronise with the peer, but cancel afterwards + if err := tester.sync("peer", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + tester.downloader.cancel() + if !tester.downloader.queue.Idle() { + t.Errorf("download queue not idle") + } +} + +// Tests that synchronisation from multiple peers works as intended (multi thread sanity test). +func TestMultiSynchronisation62(t *testing.T) { testMultiSynchronisation(t, 62, FullSync) } +func TestMultiSynchronisation63Full(t *testing.T) { testMultiSynchronisation(t, 63, FullSync) } +func TestMultiSynchronisation63Fast(t *testing.T) { testMultiSynchronisation(t, 63, FastSync) } +func TestMultiSynchronisation64Full(t *testing.T) { testMultiSynchronisation(t, 64, FullSync) } +func TestMultiSynchronisation64Fast(t *testing.T) { testMultiSynchronisation(t, 64, FastSync) } +func TestMultiSynchronisation64Light(t *testing.T) { testMultiSynchronisation(t, 64, LightSync) } + +func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create various peers with various parts of the chain + targetPeers := 8 + targetBlocks := targetPeers*blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + for i := 0; i < targetPeers; i++ { + id := fmt.Sprintf("peer #%d", i) + tester.newPeer(id, protocol, hashes[i*blockCacheLimit:], headers, blocks, receipts) + } + if err := tester.sync("peer #0", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) +} + +// Tests that synchronisations behave well in multi-version protocol environments +// and not wreak havoc on other nodes in the network. +func TestMultiProtoSynchronisation62(t *testing.T) { testMultiProtoSync(t, 62, FullSync) } +func TestMultiProtoSynchronisation63Full(t *testing.T) { testMultiProtoSync(t, 63, FullSync) } +func TestMultiProtoSynchronisation63Fast(t *testing.T) { testMultiProtoSync(t, 63, FastSync) } +func TestMultiProtoSynchronisation64Full(t *testing.T) { testMultiProtoSync(t, 64, FullSync) } +func TestMultiProtoSynchronisation64Fast(t *testing.T) { testMultiProtoSync(t, 64, FastSync) } +func TestMultiProtoSynchronisation64Light(t *testing.T) { testMultiProtoSync(t, 64, LightSync) } + +func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Create peers of every type + tester.newPeer("peer 62", 62, hashes, headers, blocks, nil) + tester.newPeer("peer 63", 63, hashes, headers, blocks, receipts) + tester.newPeer("peer 64", 64, hashes, headers, blocks, receipts) + + // Synchronise with the requested peer and make sure all blocks were retrieved + if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) + + // Check that no peers have been dropped off + for _, version := range []int{62, 63, 64} { + peer := fmt.Sprintf("peer %d", version) + if _, ok := tester.peerHashes[peer]; !ok { + t.Errorf("%s dropped", peer) + } + } +} + +// Tests that if a block is empty (e.g. header only), no body request should be +// made, and instead the header should be assembled into a whole block in itself. +func TestEmptyShortCircuit62(t *testing.T) { testEmptyShortCircuit(t, 62, FullSync) } +func TestEmptyShortCircuit63Full(t *testing.T) { testEmptyShortCircuit(t, 63, FullSync) } +func TestEmptyShortCircuit63Fast(t *testing.T) { testEmptyShortCircuit(t, 63, FastSync) } +func TestEmptyShortCircuit64Full(t *testing.T) { testEmptyShortCircuit(t, 64, FullSync) } +func TestEmptyShortCircuit64Fast(t *testing.T) { testEmptyShortCircuit(t, 64, FastSync) } +func TestEmptyShortCircuit64Light(t *testing.T) { testEmptyShortCircuit(t, 64, LightSync) } + +func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a block chain to download + targetBlocks := 2*blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + + // Instrument the downloader to signal body requests + bodiesHave, receiptsHave := int32(0), int32(0) + tester.downloader.bodyFetchHook = func(headers []*types.Header) { + atomic.AddInt32(&bodiesHave, int32(len(headers))) + } + tester.downloader.receiptFetchHook = func(headers []*types.Header) { + atomic.AddInt32(&receiptsHave, int32(len(headers))) + } + // Synchronise with the peer and make sure all blocks were retrieved + if err := tester.sync("peer", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) + + // Validate the number of block bodies that should have been requested + bodiesNeeded, receiptsNeeded := 0, 0 + for _, block := range blocks { + if mode != LightSync && block != tester.genesis && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) { + bodiesNeeded++ + } + } + for hash, receipt := range receipts { + if mode == FastSync && len(receipt) > 0 && headers[hash].Number.Uint64() <= tester.downloader.queue.fastSyncPivot { + receiptsNeeded++ + } + } + if int(bodiesHave) != bodiesNeeded { + t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave, bodiesNeeded) + } + if int(receiptsHave) != receiptsNeeded { + t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave, receiptsNeeded) + } +} + +// Tests that headers are enqueued continuously, preventing malicious nodes from +// stalling the downloader by feeding gapped header chains. +func TestMissingHeaderAttack62(t *testing.T) { testMissingHeaderAttack(t, 62, FullSync) } +func TestMissingHeaderAttack63Full(t *testing.T) { testMissingHeaderAttack(t, 63, FullSync) } +func TestMissingHeaderAttack63Fast(t *testing.T) { testMissingHeaderAttack(t, 63, FastSync) } +func TestMissingHeaderAttack64Full(t *testing.T) { testMissingHeaderAttack(t, 64, FullSync) } +func TestMissingHeaderAttack64Fast(t *testing.T) { testMissingHeaderAttack(t, 64, FastSync) } +func TestMissingHeaderAttack64Light(t *testing.T) { testMissingHeaderAttack(t, 64, LightSync) } + +func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Attempt a full sync with an attacker feeding gapped headers + tester.newPeer("attack", protocol, hashes, headers, blocks, receipts) + missing := targetBlocks / 2 + delete(tester.peerHeaders["attack"], hashes[missing]) + + if err := tester.sync("attack", nil, mode); err == nil { + t.Fatalf("succeeded attacker synchronisation") + } + // Synchronise with the valid peer and make sure sync succeeds + tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) +} + +// Tests that if requested headers are shifted (i.e. first is missing), the queue +// detects the invalid numbering. +func TestShiftedHeaderAttack62(t *testing.T) { testShiftedHeaderAttack(t, 62, FullSync) } +func TestShiftedHeaderAttack63Full(t *testing.T) { testShiftedHeaderAttack(t, 63, FullSync) } +func TestShiftedHeaderAttack63Fast(t *testing.T) { testShiftedHeaderAttack(t, 63, FastSync) } +func TestShiftedHeaderAttack64Full(t *testing.T) { testShiftedHeaderAttack(t, 64, FullSync) } +func TestShiftedHeaderAttack64Fast(t *testing.T) { testShiftedHeaderAttack(t, 64, FastSync) } +func TestShiftedHeaderAttack64Light(t *testing.T) { testShiftedHeaderAttack(t, 64, LightSync) } + +func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) { + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Attempt a full sync with an attacker feeding shifted headers + tester.newPeer("attack", protocol, hashes, headers, blocks, receipts) + delete(tester.peerHeaders["attack"], hashes[len(hashes)-2]) + delete(tester.peerBlocks["attack"], hashes[len(hashes)-2]) + delete(tester.peerReceipts["attack"], hashes[len(hashes)-2]) + + if err := tester.sync("attack", nil, mode); err == nil { + t.Fatalf("succeeded attacker synchronisation") + } + // Synchronise with the valid peer and make sure sync succeeds + tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + assertOwnChain(t, tester, targetBlocks+1) +} + +// Tests that upon detecting an invalid header, the recent ones are rolled back +// for various failure scenarios. Afterwards a full sync is attempted to make +// sure no state was corrupted. +func TestInvalidHeaderRollback63Fast(t *testing.T) { testInvalidHeaderRollback(t, 63, FastSync) } +func TestInvalidHeaderRollback64Fast(t *testing.T) { testInvalidHeaderRollback(t, 64, FastSync) } +func TestInvalidHeaderRollback64Light(t *testing.T) { testInvalidHeaderRollback(t, 64, LightSync) } + +func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) { + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := 3*fsHeaderSafetyNet + fsPivotInterval + fsMinFullBlocks + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Attempt to sync with an attacker that feeds junk during the fast sync phase. + // This should result in the last fsHeaderSafetyNet headers being rolled back. + tester.newPeer("fast-attack", protocol, hashes, headers, blocks, receipts) + missing := fsHeaderSafetyNet + MaxHeaderFetch + 1 + delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) + + if err := tester.sync("fast-attack", nil, mode); err == nil { + t.Fatalf("succeeded fast attacker synchronisation") + } + if head := tester.headHeader().Number.Int64(); int(head) > MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch) + } + // Attempt to sync with an attacker that feeds junk during the block import phase. + // This should result in both the last fsHeaderSafetyNet number of headers being + // rolled back, and also the pivot point being reverted to a non-block status. + tester.newPeer("block-attack", protocol, hashes, headers, blocks, receipts) + missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 + delete(tester.peerHeaders["fast-attack"], hashes[len(hashes)-missing]) // Make sure the fast-attacker doesn't fill in + delete(tester.peerHeaders["block-attack"], hashes[len(hashes)-missing]) + + if err := tester.sync("block-attack", nil, mode); err == nil { + t.Fatalf("succeeded block attacker synchronisation") + } + if head := tester.headHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) + } + if mode == FastSync { + if head := tester.headBlock().NumberU64(); head != 0 { + t.Errorf("fast sync pivot block #%d not rolled back", head) + } + } + // Attempt to sync with an attacker that withholds promised blocks after the + // fast sync pivot point. This could be a trial to leave the node with a bad + // but already imported pivot block. + tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts) + missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1 + + tester.downloader.fsPivotFails = 0 + tester.downloader.syncInitHook = func(uint64, uint64) { + for i := missing; i <= len(hashes); i++ { + delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i]) + } + tester.downloader.syncInitHook = nil + } + + if err := tester.sync("withhold-attack", nil, mode); err == nil { + t.Fatalf("succeeded withholding attacker synchronisation") + } + if head := tester.headHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { + t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) + } + if mode == FastSync { + if head := tester.headBlock().NumberU64(); head != 0 { + t.Errorf("fast sync pivot block #%d not rolled back", head) + } + } + tester.downloader.fsPivotFails = fsCriticalTrials + + // Synchronise with the valid peer and make sure sync succeeds. Since the last + // rollback should also disable fast syncing for this process, verify that we + // did a fresh full sync. Note, we can't assert anything about the receipts + // since we won't purge the database of them, hence we can't use assertOwnChain. + tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + if hs := len(tester.ownHeaders); hs != len(headers) { + t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, len(headers)) + } + if mode != LightSync { + if bs := len(tester.ownBlocks); bs != len(blocks) { + t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(blocks)) + } + } +} + +// Tests that a peer advertising an high TD doesn't get to stall the downloader +// afterwards by not sending any useful hashes. +func TestHighTDStarvationAttack62(t *testing.T) { testHighTDStarvationAttack(t, 62, FullSync) } +func TestHighTDStarvationAttack63Full(t *testing.T) { testHighTDStarvationAttack(t, 63, FullSync) } +func TestHighTDStarvationAttack63Fast(t *testing.T) { testHighTDStarvationAttack(t, 63, FastSync) } +func TestHighTDStarvationAttack64Full(t *testing.T) { testHighTDStarvationAttack(t, 64, FullSync) } +func TestHighTDStarvationAttack64Fast(t *testing.T) { testHighTDStarvationAttack(t, 64, FastSync) } +func TestHighTDStarvationAttack64Light(t *testing.T) { testHighTDStarvationAttack(t, 64, LightSync) } + +func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + hashes, headers, blocks, receipts := tester.makeChain(0, 0, tester.genesis, nil, false) + tester.newPeer("attack", protocol, []common.Hash{hashes[0]}, headers, blocks, receipts) + + if err := tester.sync("attack", big.NewInt(1000000), mode); err != errStallingPeer { + t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errStallingPeer) + } +} + +// Tests that misbehaving peers are disconnected, whilst behaving ones are not. +func TestBlockHeaderAttackerDropping62(t *testing.T) { testBlockHeaderAttackerDropping(t, 62) } +func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDropping(t, 63) } +func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) } + +func testBlockHeaderAttackerDropping(t *testing.T, protocol int) { + // Define the disconnection requirement for individual hash fetch errors + tests := []struct { + result error + drop bool + }{ + {nil, false}, // Sync succeeded, all is well + {errBusy, false}, // Sync is already in progress, no problem + {errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop + {errBadPeer, true}, // Peer was deemed bad for some reason, drop it + {errStallingPeer, true}, // Peer was detected to be stalling, drop it + {errNoPeers, false}, // No peers to download from, soft race, no issue + {errTimeout, true}, // No hashes received in due time, drop the peer + {errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end + {errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser + {errInvalidAncestor, true}, // Agreed upon ancestor is not acceptable, drop the chain rewriter + {errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop + {errInvalidBlock, false}, // A bad peer was detected, but not the sync origin + {errInvalidBody, false}, // A bad peer was detected, but not the sync origin + {errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin + {errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop + {errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop + {errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop + {errCancelReceiptFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop + {errCancelHeaderProcessing, false}, // Synchronisation was canceled, origin may be innocent, don't drop + {errCancelContentProcessing, false}, // Synchronisation was canceled, origin may be innocent, don't drop + } + // Run the tests and check disconnection status + tester := newTester() + defer tester.terminate() + + for i, tt := range tests { + // Register a new peer and ensure it's presence + id := fmt.Sprintf("test %d", i) + if err := tester.newPeer(id, protocol, []common.Hash{tester.genesis.Hash()}, nil, nil, nil); err != nil { + t.Fatalf("test %d: failed to register new peer: %v", i, err) + } + if _, ok := tester.peerHashes[id]; !ok { + t.Fatalf("test %d: registered peer not found", i) + } + // Simulate a synchronisation and check the required result + tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result } + + tester.downloader.Synchronise(id, tester.genesis.Hash(), big.NewInt(1000), FullSync) + if _, ok := tester.peerHashes[id]; !ok != tt.drop { + t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop) + } + } +} + +// Tests that synchronisation progress (origin block number, current block number +// and highest block number) is tracked and updated correctly. +func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) } +func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) } +func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) } +func TestSyncProgress64Full(t *testing.T) { testSyncProgress(t, 64, FullSync) } +func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) } +func TestSyncProgress64Light(t *testing.T) { testSyncProgress(t, 64, LightSync) } + +func testSyncProgress(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Set a sync init hook to catch progress changes + starting := make(chan struct{}) + progress := make(chan struct{}) + + tester.downloader.syncInitHook = func(origin, latest uint64) { + starting <- struct{}{} + <-progress + } + // Retrieve the sync progress and ensure they are zero (pristine sync) + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { + t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) + } + // Synchronise half the blocks and check initial progress + tester.newPeer("peer-half", protocol, hashes[targetBlocks/2:], headers, blocks, receipts) + pending := new(sync.WaitGroup) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("peer-half", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(targetBlocks/2+1) { + t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, targetBlocks/2+1) + } + progress <- struct{}{} + pending.Wait() + + // Synchronise all the blocks and check continuation progress + tester.newPeer("peer-full", protocol, hashes, headers, blocks, receipts) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("peer-full", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks/2+1) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Completing progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks/2+1, targetBlocks) + } + progress <- struct{}{} + pending.Wait() + + // Check final progress after successful sync + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(targetBlocks/2+1) || progress.CurrentBlock != uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Final progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2+1, targetBlocks, targetBlocks) + } +} + +// Tests that synchronisation progress (origin block number and highest block +// number) is tracked and updated correctly in case of a fork (or manual head +// revertal). +func TestForkedSyncProgress62(t *testing.T) { testForkedSyncProgress(t, 62, FullSync) } +func TestForkedSyncProgress63Full(t *testing.T) { testForkedSyncProgress(t, 63, FullSync) } +func TestForkedSyncProgress63Fast(t *testing.T) { testForkedSyncProgress(t, 63, FastSync) } +func TestForkedSyncProgress64Full(t *testing.T) { testForkedSyncProgress(t, 64, FullSync) } +func TestForkedSyncProgress64Fast(t *testing.T) { testForkedSyncProgress(t, 64, FastSync) } +func TestForkedSyncProgress64Light(t *testing.T) { testForkedSyncProgress(t, 64, LightSync) } + +func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a forked chain to simulate origin revertal + common, fork := MaxHashFetch, 2*MaxHashFetch + hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := tester.makeChainFork(common+fork, fork, tester.genesis, nil, true) + + // Set a sync init hook to catch progress changes + starting := make(chan struct{}) + progress := make(chan struct{}) + + tester.downloader.syncInitHook = func(origin, latest uint64) { + starting <- struct{}{} + <-progress + } + // Retrieve the sync progress and ensure they are zero (pristine sync) + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { + t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) + } + // Synchronise with one of the forks and check progress + tester.newPeer("fork A", protocol, hashesA, headersA, blocksA, receiptsA) + pending := new(sync.WaitGroup) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("fork A", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(len(hashesA)-1) { + t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, len(hashesA)-1) + } + progress <- struct{}{} + pending.Wait() + + // Simulate a successful sync above the fork + tester.downloader.syncStatsChainOrigin = tester.downloader.syncStatsChainHeight + + // Synchronise with the second fork and check progress resets + tester.newPeer("fork B", protocol, hashesB, headersB, blocksB, receiptsB) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("fork B", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(common) || progress.CurrentBlock != uint64(len(hashesA)-1) || progress.HighestBlock != uint64(len(hashesB)-1) { + t.Fatalf("Forking progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, common, len(hashesA)-1, len(hashesB)-1) + } + progress <- struct{}{} + pending.Wait() + + // Check final progress after successful sync + if progress := tester.downloader.Progress(); progress.StartingBlock != uint64(common) || progress.CurrentBlock != uint64(len(hashesB)-1) || progress.HighestBlock != uint64(len(hashesB)-1) { + t.Fatalf("Final progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, common, len(hashesB)-1, len(hashesB)-1) + } +} + +// Tests that if synchronisation is aborted due to some failure, then the progress +// origin is not updated in the next sync cycle, as it should be considered the +// continuation of the previous sync and not a new instance. +func TestFailedSyncProgress62(t *testing.T) { testFailedSyncProgress(t, 62, FullSync) } +func TestFailedSyncProgress63Full(t *testing.T) { testFailedSyncProgress(t, 63, FullSync) } +func TestFailedSyncProgress63Fast(t *testing.T) { testFailedSyncProgress(t, 63, FastSync) } +func TestFailedSyncProgress64Full(t *testing.T) { testFailedSyncProgress(t, 64, FullSync) } +func TestFailedSyncProgress64Fast(t *testing.T) { testFailedSyncProgress(t, 64, FastSync) } +func TestFailedSyncProgress64Light(t *testing.T) { testFailedSyncProgress(t, 64, LightSync) } + +func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small enough block chain to download + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Set a sync init hook to catch progress changes + starting := make(chan struct{}) + progress := make(chan struct{}) + + tester.downloader.syncInitHook = func(origin, latest uint64) { + starting <- struct{}{} + <-progress + } + // Retrieve the sync progress and ensure they are zero (pristine sync) + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { + t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) + } + // Attempt a full sync with a faulty peer + tester.newPeer("faulty", protocol, hashes, headers, blocks, receipts) + missing := targetBlocks / 2 + delete(tester.peerHeaders["faulty"], hashes[missing]) + delete(tester.peerBlocks["faulty"], hashes[missing]) + delete(tester.peerReceipts["faulty"], hashes[missing]) + + pending := new(sync.WaitGroup) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("faulty", nil, mode); err == nil { + t.Fatalf("succeeded faulty synchronisation") + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, targetBlocks) + } + progress <- struct{}{} + pending.Wait() + + // Synchronise with a good peer and check that the progress origin remind the same after a failure + tester.newPeer("valid", protocol, hashes, headers, blocks, receipts) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock > uint64(targetBlocks/2) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Completing progress mismatch: have %v/%v/%v, want %v/0-%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, targetBlocks/2, targetBlocks) + } + progress <- struct{}{} + pending.Wait() + + // Check final progress after successful sync + if progress := tester.downloader.Progress(); progress.StartingBlock > uint64(targetBlocks/2) || progress.CurrentBlock != uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Final progress mismatch: have %v/%v/%v, want 0-%v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks/2, targetBlocks, targetBlocks) + } +} + +// Tests that if an attacker fakes a chain height, after the attack is detected, +// the progress height is successfully reduced at the next sync invocation. +func TestFakedSyncProgress62(t *testing.T) { testFakedSyncProgress(t, 62, FullSync) } +func TestFakedSyncProgress63Full(t *testing.T) { testFakedSyncProgress(t, 63, FullSync) } +func TestFakedSyncProgress63Fast(t *testing.T) { testFakedSyncProgress(t, 63, FastSync) } +func TestFakedSyncProgress64Full(t *testing.T) { testFakedSyncProgress(t, 64, FullSync) } +func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, FastSync) } +func TestFakedSyncProgress64Light(t *testing.T) { testFakedSyncProgress(t, 64, LightSync) } + +func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + tester := newTester() + defer tester.terminate() + + // Create a small block chain + targetBlocks := blockCacheLimit - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks+3, 0, tester.genesis, nil, false) + + // Set a sync init hook to catch progress changes + starting := make(chan struct{}) + progress := make(chan struct{}) + + tester.downloader.syncInitHook = func(origin, latest uint64) { + starting <- struct{}{} + <-progress + } + // Retrieve the sync progress and ensure they are zero (pristine sync) + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != 0 { + t.Fatalf("Pristine progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, 0) + } + // Create and sync with an attacker that promises a higher chain than available + tester.newPeer("attack", protocol, hashes, headers, blocks, receipts) + for i := 1; i < 3; i++ { + delete(tester.peerHeaders["attack"], hashes[i]) + delete(tester.peerBlocks["attack"], hashes[i]) + delete(tester.peerReceipts["attack"], hashes[i]) + } + + pending := new(sync.WaitGroup) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("attack", nil, mode); err == nil { + t.Fatalf("succeeded attacker synchronisation") + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock != 0 || progress.HighestBlock != uint64(targetBlocks+3) { + t.Fatalf("Initial progress mismatch: have %v/%v/%v, want %v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, 0, targetBlocks+3) + } + progress <- struct{}{} + pending.Wait() + + // Synchronise with a good peer and check that the progress height has been reduced to the true value + tester.newPeer("valid", protocol, hashes[3:], headers, blocks, receipts) + pending.Add(1) + + go func() { + defer pending.Done() + if err := tester.sync("valid", nil, mode); err != nil { + t.Fatalf("failed to synchronise blocks: %v", err) + } + }() + <-starting + if progress := tester.downloader.Progress(); progress.StartingBlock != 0 || progress.CurrentBlock > uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Completing progress mismatch: have %v/%v/%v, want %v/0-%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, 0, targetBlocks, targetBlocks) + } + progress <- struct{}{} + pending.Wait() + + // Check final progress after successful sync + if progress := tester.downloader.Progress(); progress.StartingBlock > uint64(targetBlocks) || progress.CurrentBlock != uint64(targetBlocks) || progress.HighestBlock != uint64(targetBlocks) { + t.Fatalf("Final progress mismatch: have %v/%v/%v, want 0-%v/%v/%v", progress.StartingBlock, progress.CurrentBlock, progress.HighestBlock, targetBlocks, targetBlocks, targetBlocks) + } +} + +// This test reproduces an issue where unexpected deliveries would +// block indefinitely if they arrived at the right time. +func TestDeliverHeadersHang62(t *testing.T) { testDeliverHeadersHang(t, 62, FullSync) } +func TestDeliverHeadersHang63Full(t *testing.T) { testDeliverHeadersHang(t, 63, FullSync) } +func TestDeliverHeadersHang63Fast(t *testing.T) { testDeliverHeadersHang(t, 63, FastSync) } +func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) } +func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) } +func TestDeliverHeadersHang64Light(t *testing.T) { testDeliverHeadersHang(t, 64, LightSync) } + +func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) { + t.Parallel() + + master := newTester() + defer master.terminate() + + hashes, headers, blocks, receipts := master.makeChain(5, 0, master.genesis, nil, false) + fakeHeads := []*types.Header{{}, {}, {}, {}} + for i := 0; i < 200; i++ { + tester := newTester() + tester.peerDb = master.peerDb + + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + // Whenever the downloader requests headers, flood it with + // a lot of unrequested header deliveries. + tester.downloader.peers.peers["peer"].getAbsHeaders = func(from uint64, count, skip int, reverse bool) error { + deliveriesDone := make(chan struct{}, 500) + for i := 0; i < cap(deliveriesDone); i++ { + peer := fmt.Sprintf("fake-peer%d", i) + go func() { + tester.downloader.DeliverHeaders(peer, fakeHeads) + deliveriesDone <- struct{}{} + }() + } + // Deliver the actual requested headers. + impl := tester.peerGetAbsHeadersFn("peer", 0) + go impl(from, count, skip, reverse) + // None of the extra deliveries should block. + timeout := time.After(15 * time.Second) + for i := 0; i < cap(deliveriesDone); i++ { + select { + case <-deliveriesDone: + case <-timeout: + panic("blocked") + } + } + return nil + } + if err := tester.sync("peer", nil, mode); err != nil { + t.Errorf("sync failed: %v", err) + } + tester.terminate() + } +} + +// Tests that if fast sync aborts in the critical section, it can restart a few +// times before giving up. +func TestFastCriticalRestartsFail63(t *testing.T) { testFastCriticalRestarts(t, 63, false) } +func TestFastCriticalRestartsFail64(t *testing.T) { testFastCriticalRestarts(t, 64, false) } +func TestFastCriticalRestartsCont63(t *testing.T) { testFastCriticalRestarts(t, 63, true) } +func TestFastCriticalRestartsCont64(t *testing.T) { testFastCriticalRestarts(t, 64, true) } + +func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) { + tester := newTester() + defer tester.terminate() + + // Create a large enough blockchin to actually fast sync on + targetBlocks := fsMinFullBlocks + 2*fsPivotInterval - 15 + hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false) + + // Create a tester peer with a critical section header missing (force failures) + tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) + delete(tester.peerHeaders["peer"], hashes[fsMinFullBlocks-1]) + tester.downloader.dropPeer = func(id string) {} // We reuse the same "faulty" peer throughout the test + + // Remove all possible pivot state roots and slow down replies (test failure resets later) + for i := 0; i < fsPivotInterval; i++ { + tester.peerMissingStates["peer"][headers[hashes[fsMinFullBlocks+i]].Root] = true + } + tester.downloader.peers.peers["peer"].getNodeData = tester.peerGetNodeDataFn("peer", 500*time.Millisecond) // Enough to reach the critical section + + // Synchronise with the peer a few times and make sure they fail until the retry limit + for i := 0; i < int(fsCriticalTrials)-1; i++ { + // Attempt a sync and ensure it fails properly + if err := tester.sync("peer", nil, FastSync); err == nil { + t.Fatalf("failing fast sync succeeded: %v", err) + } + time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain + + // If it's the first failure, pivot should be locked => reenable all others to detect pivot changes + if i == 0 { + if tester.downloader.fsPivotLock == nil { + time.Sleep(400 * time.Millisecond) // Make sure the first huge timeout expires too + t.Fatalf("pivot block not locked in after critical section failure") + } + tester.lock.Lock() + tester.peerHeaders["peer"][hashes[fsMinFullBlocks-1]] = headers[hashes[fsMinFullBlocks-1]] + tester.peerMissingStates["peer"] = map[common.Hash]bool{tester.downloader.fsPivotLock.Root: true} + tester.downloader.peers.peers["peer"].getNodeData = tester.peerGetNodeDataFn("peer", 0) + tester.lock.Unlock() + } + } + // Return all nodes if we're testing fast sync progression + if progress { + tester.lock.Lock() + tester.peerMissingStates["peer"] = map[common.Hash]bool{} + tester.lock.Unlock() + + if err := tester.sync("peer", nil, FastSync); err != nil { + t.Fatalf("failed to synchronise blocks in progressed fast sync: %v", err) + } + time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain + + if fails := atomic.LoadUint32(&tester.downloader.fsPivotFails); fails != 1 { + t.Fatalf("progressed pivot trial count mismatch: have %v, want %v", fails, 1) + } + assertOwnChain(t, tester, targetBlocks+1) + } else { + if err := tester.sync("peer", nil, FastSync); err == nil { + t.Fatalf("succeeded to synchronise blocks in failed fast sync") + } + time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain + + if fails := atomic.LoadUint32(&tester.downloader.fsPivotFails); fails != fsCriticalTrials { + t.Fatalf("failed pivot trial count mismatch: have %v, want %v", fails, fsCriticalTrials) + } + } + // Retry limit exhausted, downloader will switch to full sync, should succeed + if err := tester.sync("peer", nil, FastSync); err != nil { + t.Fatalf("failed to synchronise blocks in slow sync: %v", err) + } + // Note, we can't assert the chain here because the test asserter assumes sync + // completed using a single mode of operation, whereas fast-then-slow can result + // in arbitrary intermediate state that's not cleanly verifiable. +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/events.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/events.go new file mode 100644 index 0000000..64905b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/events.go @@ -0,0 +1,21 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +type DoneEvent struct{} +type StartEvent struct{} +type FailedEvent struct{ Err error } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/metrics.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/metrics.go new file mode 100644 index 0000000..0d76c7d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/metrics.go @@ -0,0 +1,45 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the metrics collected by the downloader. + +package downloader + +import ( + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + headerInMeter = metrics.NewMeter("eth/downloader/headers/in") + headerReqTimer = metrics.NewTimer("eth/downloader/headers/req") + headerDropMeter = metrics.NewMeter("eth/downloader/headers/drop") + headerTimeoutMeter = metrics.NewMeter("eth/downloader/headers/timeout") + + bodyInMeter = metrics.NewMeter("eth/downloader/bodies/in") + bodyReqTimer = metrics.NewTimer("eth/downloader/bodies/req") + bodyDropMeter = metrics.NewMeter("eth/downloader/bodies/drop") + bodyTimeoutMeter = metrics.NewMeter("eth/downloader/bodies/timeout") + + receiptInMeter = metrics.NewMeter("eth/downloader/receipts/in") + receiptReqTimer = metrics.NewTimer("eth/downloader/receipts/req") + receiptDropMeter = metrics.NewMeter("eth/downloader/receipts/drop") + receiptTimeoutMeter = metrics.NewMeter("eth/downloader/receipts/timeout") + + stateInMeter = metrics.NewMeter("eth/downloader/states/in") + stateReqTimer = metrics.NewTimer("eth/downloader/states/req") + stateDropMeter = metrics.NewMeter("eth/downloader/states/drop") + stateTimeoutMeter = metrics.NewMeter("eth/downloader/states/timeout") +) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/modes.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/modes.go new file mode 100644 index 0000000..ec339c0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/modes.go @@ -0,0 +1,26 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +// SyncMode represents the synchronisation mode of the downloader. +type SyncMode int + +const ( + FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks + FastSync // Quickly download the headers, full sync only at the chain head + LightSync // Download only the headers and terminate afterwards +) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/peer.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/peer.go new file mode 100644 index 0000000..ea4b6a6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/peer.go @@ -0,0 +1,564 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the active peer-set of the downloader, maintaining both failures +// as well as reputation metrics to prioritize the block retrievals. + +package downloader + +import ( + "errors" + "fmt" + "math" + "math/big" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items + measurementImpact = 0.1 // The impact a single measurement has on a peer's final throughput value. +) + +// Head hash and total difficulty retriever for +type currentHeadRetrievalFn func() (common.Hash, *big.Int) + +// Block header and body fetchers belonging to eth/62 and above +type relativeHeaderFetcherFn func(common.Hash, int, int, bool) error +type absoluteHeaderFetcherFn func(uint64, int, int, bool) error +type blockBodyFetcherFn func([]common.Hash) error +type receiptFetcherFn func([]common.Hash) error +type stateFetcherFn func([]common.Hash) error + +var ( + errAlreadyFetching = errors.New("already fetching blocks from peer") + errAlreadyRegistered = errors.New("peer is already registered") + errNotRegistered = errors.New("peer is not registered") +) + +// peer represents an active peer from which hashes and blocks are retrieved. +type peer struct { + id string // Unique identifier of the peer + + headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1) + blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1) + receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1) + stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1) + + headerThroughput float64 // Number of headers measured to be retrievable per second + blockThroughput float64 // Number of blocks (bodies) measured to be retrievable per second + receiptThroughput float64 // Number of receipts measured to be retrievable per second + stateThroughput float64 // Number of node data pieces measured to be retrievable per second + + rtt time.Duration // Request round trip time to track responsiveness (QoS) + + headerStarted time.Time // Time instance when the last header fetch was started + blockStarted time.Time // Time instance when the last block (body) fetch was started + receiptStarted time.Time // Time instance when the last receipt fetch was started + stateStarted time.Time // Time instance when the last node data fetch was started + + lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) + + currentHead currentHeadRetrievalFn // Method to fetch the currently known head of the peer + + getRelHeaders relativeHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an origin hash + getAbsHeaders absoluteHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an absolute position + getBlockBodies blockBodyFetcherFn // [eth/62] Method to retrieve a batch of block bodies + + getReceipts receiptFetcherFn // [eth/63] Method to retrieve a batch of block transaction receipts + getNodeData stateFetcherFn // [eth/63] Method to retrieve a batch of state trie data + + version int // Eth protocol version number to switch strategies + lock sync.RWMutex +} + +// newPeer create a new downloader peer, with specific hash and block retrieval +// mechanisms. +func newPeer(id string, version int, currentHead currentHeadRetrievalFn, + getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn, + getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer { + return &peer{ + id: id, + lacking: make(map[common.Hash]struct{}), + + currentHead: currentHead, + getRelHeaders: getRelHeaders, + getAbsHeaders: getAbsHeaders, + getBlockBodies: getBlockBodies, + + getReceipts: getReceipts, + getNodeData: getNodeData, + + version: version, + } +} + +// Reset clears the internal state of a peer entity. +func (p *peer) Reset() { + p.lock.Lock() + defer p.lock.Unlock() + + atomic.StoreInt32(&p.headerIdle, 0) + atomic.StoreInt32(&p.blockIdle, 0) + atomic.StoreInt32(&p.receiptIdle, 0) + atomic.StoreInt32(&p.stateIdle, 0) + + p.headerThroughput = 0 + p.blockThroughput = 0 + p.receiptThroughput = 0 + p.stateThroughput = 0 + + p.lacking = make(map[common.Hash]struct{}) +} + +// FetchHeaders sends a header retrieval request to the remote peer. +func (p *peer) FetchHeaders(from uint64, count int) error { + // Sanity check the protocol version + if p.version < 62 { + panic(fmt.Sprintf("header fetch [eth/62+] requested on eth/%d", p.version)) + } + // Short circuit if the peer is already fetching + if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) { + return errAlreadyFetching + } + p.headerStarted = time.Now() + + // Issue the header retrieval request (absolut upwards without gaps) + go p.getAbsHeaders(from, count, 0, false) + + return nil +} + +// FetchBodies sends a block body retrieval request to the remote peer. +func (p *peer) FetchBodies(request *fetchRequest) error { + // Sanity check the protocol version + if p.version < 62 { + panic(fmt.Sprintf("body fetch [eth/62+] requested on eth/%d", p.version)) + } + // Short circuit if the peer is already fetching + if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) { + return errAlreadyFetching + } + p.blockStarted = time.Now() + + // Convert the header set to a retrievable slice + hashes := make([]common.Hash, 0, len(request.Headers)) + for _, header := range request.Headers { + hashes = append(hashes, header.Hash()) + } + go p.getBlockBodies(hashes) + + return nil +} + +// FetchReceipts sends a receipt retrieval request to the remote peer. +func (p *peer) FetchReceipts(request *fetchRequest) error { + // Sanity check the protocol version + if p.version < 63 { + panic(fmt.Sprintf("body fetch [eth/63+] requested on eth/%d", p.version)) + } + // Short circuit if the peer is already fetching + if !atomic.CompareAndSwapInt32(&p.receiptIdle, 0, 1) { + return errAlreadyFetching + } + p.receiptStarted = time.Now() + + // Convert the header set to a retrievable slice + hashes := make([]common.Hash, 0, len(request.Headers)) + for _, header := range request.Headers { + hashes = append(hashes, header.Hash()) + } + go p.getReceipts(hashes) + + return nil +} + +// FetchNodeData sends a node state data retrieval request to the remote peer. +func (p *peer) FetchNodeData(request *fetchRequest) error { + // Sanity check the protocol version + if p.version < 63 { + panic(fmt.Sprintf("node data fetch [eth/63+] requested on eth/%d", p.version)) + } + // Short circuit if the peer is already fetching + if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) { + return errAlreadyFetching + } + p.stateStarted = time.Now() + + // Convert the hash set to a retrievable slice + hashes := make([]common.Hash, 0, len(request.Hashes)) + for hash := range request.Hashes { + hashes = append(hashes, hash) + } + go p.getNodeData(hashes) + + return nil +} + +// SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval +// requests. Its estimated header retrieval throughput is updated with that measured +// just now. +func (p *peer) SetHeadersIdle(delivered int) { + p.setIdle(p.headerStarted, delivered, &p.headerThroughput, &p.headerIdle) +} + +// SetBlocksIdle sets the peer to idle, allowing it to execute new block retrieval +// requests. Its estimated block retrieval throughput is updated with that measured +// just now. +func (p *peer) SetBlocksIdle(delivered int) { + p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle) +} + +// SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval +// requests. Its estimated body retrieval throughput is updated with that measured +// just now. +func (p *peer) SetBodiesIdle(delivered int) { + p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle) +} + +// SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt +// retrieval requests. Its estimated receipt retrieval throughput is updated +// with that measured just now. +func (p *peer) SetReceiptsIdle(delivered int) { + p.setIdle(p.receiptStarted, delivered, &p.receiptThroughput, &p.receiptIdle) +} + +// SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie +// data retrieval requests. Its estimated state retrieval throughput is updated +// with that measured just now. +func (p *peer) SetNodeDataIdle(delivered int) { + p.setIdle(p.stateStarted, delivered, &p.stateThroughput, &p.stateIdle) +} + +// setIdle sets the peer to idle, allowing it to execute new retrieval requests. +// Its estimated retrieval throughput is updated with that measured just now. +func (p *peer) setIdle(started time.Time, delivered int, throughput *float64, idle *int32) { + // Irrelevant of the scaling, make sure the peer ends up idle + defer atomic.StoreInt32(idle, 0) + + p.lock.Lock() + defer p.lock.Unlock() + + // If nothing was delivered (hard timeout / unavailable data), reduce throughput to minimum + if delivered == 0 { + *throughput = 0 + return + } + // Otherwise update the throughput with a new measurement + elapsed := time.Since(started) + 1 // +1 (ns) to ensure non-zero divisor + measured := float64(delivered) / (float64(elapsed) / float64(time.Second)) + + *throughput = (1-measurementImpact)*(*throughput) + measurementImpact*measured + p.rtt = time.Duration((1-measurementImpact)*float64(p.rtt) + measurementImpact*float64(elapsed)) +} + +// HeaderCapacity retrieves the peers header download allowance based on its +// previously discovered throughput. +func (p *peer) HeaderCapacity(targetRTT time.Duration) int { + p.lock.RLock() + defer p.lock.RUnlock() + + return int(math.Min(1+math.Max(1, p.headerThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxHeaderFetch))) +} + +// BlockCapacity retrieves the peers block download allowance based on its +// previously discovered throughput. +func (p *peer) BlockCapacity(targetRTT time.Duration) int { + p.lock.RLock() + defer p.lock.RUnlock() + + return int(math.Min(1+math.Max(1, p.blockThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxBlockFetch))) +} + +// ReceiptCapacity retrieves the peers receipt download allowance based on its +// previously discovered throughput. +func (p *peer) ReceiptCapacity(targetRTT time.Duration) int { + p.lock.RLock() + defer p.lock.RUnlock() + + return int(math.Min(1+math.Max(1, p.receiptThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxReceiptFetch))) +} + +// NodeDataCapacity retrieves the peers state download allowance based on its +// previously discovered throughput. +func (p *peer) NodeDataCapacity(targetRTT time.Duration) int { + p.lock.RLock() + defer p.lock.RUnlock() + + return int(math.Min(1+math.Max(1, p.stateThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxStateFetch))) +} + +// MarkLacking appends a new entity to the set of items (blocks, receipts, states) +// that a peer is known not to have (i.e. have been requested before). If the +// set reaches its maximum allowed capacity, items are randomly dropped off. +func (p *peer) MarkLacking(hash common.Hash) { + p.lock.Lock() + defer p.lock.Unlock() + + for len(p.lacking) >= maxLackingHashes { + for drop := range p.lacking { + delete(p.lacking, drop) + break + } + } + p.lacking[hash] = struct{}{} +} + +// Lacks retrieves whether the hash of a blockchain item is on the peers lacking +// list (i.e. whether we know that the peer does not have it). +func (p *peer) Lacks(hash common.Hash) bool { + p.lock.RLock() + defer p.lock.RUnlock() + + _, ok := p.lacking[hash] + return ok +} + +// String implements fmt.Stringer. +func (p *peer) String() string { + p.lock.RLock() + defer p.lock.RUnlock() + + return fmt.Sprintf("Peer %s [%s]", p.id, strings.Join([]string{ + fmt.Sprintf("hs %3.2f/s", p.headerThroughput), + fmt.Sprintf("bs %3.2f/s", p.blockThroughput), + fmt.Sprintf("rs %3.2f/s", p.receiptThroughput), + fmt.Sprintf("ss %3.2f/s", p.stateThroughput), + fmt.Sprintf("miss %4d", len(p.lacking)), + fmt.Sprintf("rtt %v", p.rtt), + }, ", ")) +} + +// peerSet represents the collection of active peer participating in the chain +// download procedure. +type peerSet struct { + peers map[string]*peer + lock sync.RWMutex +} + +// newPeerSet creates a new peer set top track the active download sources. +func newPeerSet() *peerSet { + return &peerSet{ + peers: make(map[string]*peer), + } +} + +// Reset iterates over the current peer set, and resets each of the known peers +// to prepare for a next batch of block retrieval. +func (ps *peerSet) Reset() { + ps.lock.RLock() + defer ps.lock.RUnlock() + + for _, peer := range ps.peers { + peer.Reset() + } +} + +// Register injects a new peer into the working set, or returns an error if the +// peer is already known. +// +// The method also sets the starting throughput values of the new peer to the +// average of all existing peers, to give it a realistic chance of being used +// for data retrievals. +func (ps *peerSet) Register(p *peer) error { + // Retrieve the current median RTT as a sane default + p.rtt = ps.medianRTT() + + // Register the new peer with some meaningful defaults + ps.lock.Lock() + defer ps.lock.Unlock() + + if _, ok := ps.peers[p.id]; ok { + return errAlreadyRegistered + } + if len(ps.peers) > 0 { + p.headerThroughput, p.blockThroughput, p.receiptThroughput, p.stateThroughput = 0, 0, 0, 0 + + for _, peer := range ps.peers { + peer.lock.RLock() + p.headerThroughput += peer.headerThroughput + p.blockThroughput += peer.blockThroughput + p.receiptThroughput += peer.receiptThroughput + p.stateThroughput += peer.stateThroughput + peer.lock.RUnlock() + } + p.headerThroughput /= float64(len(ps.peers)) + p.blockThroughput /= float64(len(ps.peers)) + p.receiptThroughput /= float64(len(ps.peers)) + p.stateThroughput /= float64(len(ps.peers)) + } + ps.peers[p.id] = p + return nil +} + +// Unregister removes a remote peer from the active set, disabling any further +// actions to/from that particular entity. +func (ps *peerSet) Unregister(id string) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if _, ok := ps.peers[id]; !ok { + return errNotRegistered + } + delete(ps.peers, id) + return nil +} + +// Peer retrieves the registered peer with the given id. +func (ps *peerSet) Peer(id string) *peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.peers[id] +} + +// Len returns if the current number of peers in the set. +func (ps *peerSet) Len() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return len(ps.peers) +} + +// AllPeers retrieves a flat list of all the peers within the set. +func (ps *peerSet) AllPeers() []*peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*peer, 0, len(ps.peers)) + for _, p := range ps.peers { + list = append(list, p) + } + return list +} + +// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers +// within the active peer set, ordered by their reputation. +func (ps *peerSet) HeaderIdlePeers() ([]*peer, int) { + idle := func(p *peer) bool { + return atomic.LoadInt32(&p.headerIdle) == 0 + } + throughput := func(p *peer) float64 { + p.lock.RLock() + defer p.lock.RUnlock() + return p.headerThroughput + } + return ps.idlePeers(62, 64, idle, throughput) +} + +// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within +// the active peer set, ordered by their reputation. +func (ps *peerSet) BodyIdlePeers() ([]*peer, int) { + idle := func(p *peer) bool { + return atomic.LoadInt32(&p.blockIdle) == 0 + } + throughput := func(p *peer) float64 { + p.lock.RLock() + defer p.lock.RUnlock() + return p.blockThroughput + } + return ps.idlePeers(62, 64, idle, throughput) +} + +// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers +// within the active peer set, ordered by their reputation. +func (ps *peerSet) ReceiptIdlePeers() ([]*peer, int) { + idle := func(p *peer) bool { + return atomic.LoadInt32(&p.receiptIdle) == 0 + } + throughput := func(p *peer) float64 { + p.lock.RLock() + defer p.lock.RUnlock() + return p.receiptThroughput + } + return ps.idlePeers(63, 64, idle, throughput) +} + +// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle +// peers within the active peer set, ordered by their reputation. +func (ps *peerSet) NodeDataIdlePeers() ([]*peer, int) { + idle := func(p *peer) bool { + return atomic.LoadInt32(&p.stateIdle) == 0 + } + throughput := func(p *peer) float64 { + p.lock.RLock() + defer p.lock.RUnlock() + return p.stateThroughput + } + return ps.idlePeers(63, 64, idle, throughput) +} + +// idlePeers retrieves a flat list of all currently idle peers satisfying the +// protocol version constraints, using the provided function to check idleness. +// The resulting set of peers are sorted by their measure throughput. +func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peer) bool, throughput func(*peer) float64) ([]*peer, int) { + ps.lock.RLock() + defer ps.lock.RUnlock() + + idle, total := make([]*peer, 0, len(ps.peers)), 0 + for _, p := range ps.peers { + if p.version >= minProtocol && p.version <= maxProtocol { + if idleCheck(p) { + idle = append(idle, p) + } + total++ + } + } + for i := 0; i < len(idle); i++ { + for j := i + 1; j < len(idle); j++ { + if throughput(idle[i]) < throughput(idle[j]) { + idle[i], idle[j] = idle[j], idle[i] + } + } + } + return idle, total +} + +// medianRTT returns the median RTT of te peerset, considering only the tuning +// peers if there are more peers available. +func (ps *peerSet) medianRTT() time.Duration { + // Gather all the currnetly measured round trip times + ps.lock.RLock() + defer ps.lock.RUnlock() + + rtts := make([]float64, 0, len(ps.peers)) + for _, p := range ps.peers { + p.lock.RLock() + rtts = append(rtts, float64(p.rtt)) + p.lock.RUnlock() + } + sort.Float64s(rtts) + + median := rttMaxEstimate + if qosTuningPeers <= len(rtts) { + median = time.Duration(rtts[qosTuningPeers/2]) // Median of our tuning peers + } else if len(rtts) > 0 { + median = time.Duration(rtts[len(rtts)/2]) // Median of our connected peers (maintain even like this some baseline qos) + } + // Restrict the RTT into some QoS defaults, irrelevant of true RTT + if median < rttMinEstimate { + median = rttMinEstimate + } + if median > rttMaxEstimate { + median = rttMaxEstimate + } + return median +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go new file mode 100644 index 0000000..5be09f3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/queue.go @@ -0,0 +1,1143 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the block download scheduler to collect download tasks and schedule +// them in an ordered, and throttled way. + +package downloader + +import ( + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/trie" + "github.com/rcrowley/go-metrics" + "gopkg.in/karalabe/cookiejar.v2/collections/prque" +) + +var ( + blockCacheLimit = 8192 // Maximum number of blocks to cache before throttling the download + maxInFlightStates = 8192 // Maximum number of state downloads to allow concurrently +) + +var ( + errNoFetchesPending = errors.New("no fetches pending") + errStaleDelivery = errors.New("stale delivery") +) + +// fetchRequest is a currently running data retrieval operation. +type fetchRequest struct { + Peer *peer // Peer to which the request was sent + From uint64 // [eth/62] Requested chain element index (used for skeleton fills only) + Hashes map[common.Hash]int // [eth/61] Requested hashes with their insertion index (priority) + Headers []*types.Header // [eth/62] Requested headers, sorted by request order + Time time.Time // Time when the request was made +} + +// fetchResult is a struct collecting partial results from data fetchers until +// all outstanding pieces complete and the result as a whole can be processed. +type fetchResult struct { + Pending int // Number of data fetches still pending + + Header *types.Header + Uncles []*types.Header + Transactions types.Transactions + Receipts types.Receipts +} + +// queue represents hashes that are either need fetching or are being fetched +type queue struct { + mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching + fastSyncPivot uint64 // Block number where the fast sync pivots into archive synchronisation mode + + headerHead common.Hash // [eth/62] Hash of the last queued header to verify order + + // Headers are "special", they download in batches, supported by a skeleton chain + headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers + headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for + headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable + headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations + headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers + headerProced int // [eth/62] Number of headers already processed from the results + headerOffset uint64 // [eth/62] Number of the first header in the result cache + headerContCh chan bool // [eth/62] Channel to notify when header download finishes + + // All data retrievals below are based on an already assembles header chain + blockTaskPool map[common.Hash]*types.Header // [eth/62] Pending block (body) retrieval tasks, mapping hashes to headers + blockTaskQueue *prque.Prque // [eth/62] Priority queue of the headers to fetch the blocks (bodies) for + blockPendPool map[string]*fetchRequest // [eth/62] Currently pending block (body) retrieval operations + blockDonePool map[common.Hash]struct{} // [eth/62] Set of the completed block (body) fetches + + receiptTaskPool map[common.Hash]*types.Header // [eth/63] Pending receipt retrieval tasks, mapping hashes to headers + receiptTaskQueue *prque.Prque // [eth/63] Priority queue of the headers to fetch the receipts for + receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations + receiptDonePool map[common.Hash]struct{} // [eth/63] Set of the completed receipt fetches + + stateTaskIndex int // [eth/63] Counter indexing the added hashes to ensure prioritised retrieval order + stateTaskPool map[common.Hash]int // [eth/63] Pending node data retrieval tasks, mapping to their priority + stateTaskQueue *prque.Prque // [eth/63] Priority queue of the hashes to fetch the node data for + statePendPool map[string]*fetchRequest // [eth/63] Currently pending node data retrieval operations + + stateDatabase ethdb.Database // [eth/63] Trie database to populate during state reassembly + stateScheduler *state.StateSync // [eth/63] State trie synchronisation scheduler and integrator + stateWriters int // [eth/63] Number of running state DB writer goroutines + + resultCache []*fetchResult // Downloaded but not yet delivered fetch results + resultOffset uint64 // Offset of the first cached fetch result in the block chain + + lock *sync.Mutex + active *sync.Cond + closed bool +} + +// newQueue creates a new download queue for scheduling block retrieval. +func newQueue(stateDb ethdb.Database) *queue { + lock := new(sync.Mutex) + return &queue{ + headerPendPool: make(map[string]*fetchRequest), + headerContCh: make(chan bool), + blockTaskPool: make(map[common.Hash]*types.Header), + blockTaskQueue: prque.New(), + blockPendPool: make(map[string]*fetchRequest), + blockDonePool: make(map[common.Hash]struct{}), + receiptTaskPool: make(map[common.Hash]*types.Header), + receiptTaskQueue: prque.New(), + receiptPendPool: make(map[string]*fetchRequest), + receiptDonePool: make(map[common.Hash]struct{}), + stateTaskPool: make(map[common.Hash]int), + stateTaskQueue: prque.New(), + statePendPool: make(map[string]*fetchRequest), + stateDatabase: stateDb, + resultCache: make([]*fetchResult, blockCacheLimit), + active: sync.NewCond(lock), + lock: lock, + } +} + +// Reset clears out the queue contents. +func (q *queue) Reset() { + q.lock.Lock() + defer q.lock.Unlock() + + q.closed = false + q.mode = FullSync + q.fastSyncPivot = 0 + + q.headerHead = common.Hash{} + + q.headerPendPool = make(map[string]*fetchRequest) + + q.blockTaskPool = make(map[common.Hash]*types.Header) + q.blockTaskQueue.Reset() + q.blockPendPool = make(map[string]*fetchRequest) + q.blockDonePool = make(map[common.Hash]struct{}) + + q.receiptTaskPool = make(map[common.Hash]*types.Header) + q.receiptTaskQueue.Reset() + q.receiptPendPool = make(map[string]*fetchRequest) + q.receiptDonePool = make(map[common.Hash]struct{}) + + q.stateTaskIndex = 0 + q.stateTaskPool = make(map[common.Hash]int) + q.stateTaskQueue.Reset() + q.statePendPool = make(map[string]*fetchRequest) + q.stateScheduler = nil + + q.resultCache = make([]*fetchResult, blockCacheLimit) + q.resultOffset = 0 +} + +// Close marks the end of the sync, unblocking WaitResults. +// It may be called even if the queue is already closed. +func (q *queue) Close() { + q.lock.Lock() + q.closed = true + q.lock.Unlock() + q.active.Broadcast() +} + +// PendingHeaders retrieves the number of header requests pending for retrieval. +func (q *queue) PendingHeaders() int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.headerTaskQueue.Size() +} + +// PendingBlocks retrieves the number of block (body) requests pending for retrieval. +func (q *queue) PendingBlocks() int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.blockTaskQueue.Size() +} + +// PendingReceipts retrieves the number of block receipts pending for retrieval. +func (q *queue) PendingReceipts() int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.receiptTaskQueue.Size() +} + +// PendingNodeData retrieves the number of node data entries pending for retrieval. +func (q *queue) PendingNodeData() int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.pendingNodeDataLocked() +} + +// pendingNodeDataLocked retrieves the number of node data entries pending for retrieval. +// The caller must hold q.lock. +func (q *queue) pendingNodeDataLocked() int { + var n int + if q.stateScheduler != nil { + n = q.stateScheduler.Pending() + } + // Ensure that PendingNodeData doesn't return 0 until all state is written. + if q.stateWriters > 0 { + n++ + } + return n +} + +// InFlightHeaders retrieves whether there are header fetch requests currently +// in flight. +func (q *queue) InFlightHeaders() bool { + q.lock.Lock() + defer q.lock.Unlock() + + return len(q.headerPendPool) > 0 +} + +// InFlightBlocks retrieves whether there are block fetch requests currently in +// flight. +func (q *queue) InFlightBlocks() bool { + q.lock.Lock() + defer q.lock.Unlock() + + return len(q.blockPendPool) > 0 +} + +// InFlightReceipts retrieves whether there are receipt fetch requests currently +// in flight. +func (q *queue) InFlightReceipts() bool { + q.lock.Lock() + defer q.lock.Unlock() + + return len(q.receiptPendPool) > 0 +} + +// InFlightNodeData retrieves whether there are node data entry fetch requests +// currently in flight. +func (q *queue) InFlightNodeData() bool { + q.lock.Lock() + defer q.lock.Unlock() + + return len(q.statePendPool)+q.stateWriters > 0 +} + +// Idle returns if the queue is fully idle or has some data still inside. This +// method is used by the tester to detect termination events. +func (q *queue) Idle() bool { + q.lock.Lock() + defer q.lock.Unlock() + + queued := q.blockTaskQueue.Size() + q.receiptTaskQueue.Size() + q.stateTaskQueue.Size() + pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool) + cached := len(q.blockDonePool) + len(q.receiptDonePool) + + if q.stateScheduler != nil { + queued += q.stateScheduler.Pending() + } + return (queued + pending + cached) == 0 +} + +// FastSyncPivot retrieves the currently used fast sync pivot point. +func (q *queue) FastSyncPivot() uint64 { + q.lock.Lock() + defer q.lock.Unlock() + + return q.fastSyncPivot +} + +// ShouldThrottleBlocks checks if the download should be throttled (active block (body) +// fetches exceed block cache). +func (q *queue) ShouldThrottleBlocks() bool { + q.lock.Lock() + defer q.lock.Unlock() + + // Calculate the currently in-flight block (body) requests + pending := 0 + for _, request := range q.blockPendPool { + pending += len(request.Hashes) + len(request.Headers) + } + // Throttle if more blocks (bodies) are in-flight than free space in the cache + return pending >= len(q.resultCache)-len(q.blockDonePool) +} + +// ShouldThrottleReceipts checks if the download should be throttled (active receipt +// fetches exceed block cache). +func (q *queue) ShouldThrottleReceipts() bool { + q.lock.Lock() + defer q.lock.Unlock() + + // Calculate the currently in-flight receipt requests + pending := 0 + for _, request := range q.receiptPendPool { + pending += len(request.Headers) + } + // Throttle if more receipts are in-flight than free space in the cache + return pending >= len(q.resultCache)-len(q.receiptDonePool) +} + +// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill +// up an already retrieved header skeleton. +func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) { + q.lock.Lock() + defer q.lock.Unlock() + + // No skeleton retrieval can be in progress, fail hard if so (huge implementation bug) + if q.headerResults != nil { + panic("skeleton assembly already in progress") + } + // Shedule all the header retrieval tasks for the skeleton assembly + q.headerTaskPool = make(map[uint64]*types.Header) + q.headerTaskQueue = prque.New() + q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains + q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch) + q.headerProced = 0 + q.headerOffset = from + q.headerContCh = make(chan bool, 1) + + for i, header := range skeleton { + index := from + uint64(i*MaxHeaderFetch) + + q.headerTaskPool[index] = header + q.headerTaskQueue.Push(index, -float32(index)) + } +} + +// RetrieveHeaders retrieves the header chain assemble based on the scheduled +// skeleton. +func (q *queue) RetrieveHeaders() ([]*types.Header, int) { + q.lock.Lock() + defer q.lock.Unlock() + + headers, proced := q.headerResults, q.headerProced + q.headerResults, q.headerProced = nil, 0 + + return headers, proced +} + +// Schedule adds a set of headers for the download queue for scheduling, returning +// the new headers encountered. +func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { + q.lock.Lock() + defer q.lock.Unlock() + + // Insert all the headers prioritised by the contained block number + inserts := make([]*types.Header, 0, len(headers)) + for _, header := range headers { + // Make sure chain order is honoured and preserved throughout + hash := header.Hash() + if header.Number == nil || header.Number.Uint64() != from { + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ordering, expected %d", header.Number, hash[:4], from) + break + } + if q.headerHead != (common.Hash{}) && q.headerHead != header.ParentHash { + glog.V(logger.Warn).Infof("Header #%v [%x…] broke chain ancestry", header.Number, hash[:4]) + break + } + // Make sure no duplicate requests are executed + if _, ok := q.blockTaskPool[hash]; ok { + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for block fetch", header.Number.Uint64(), hash[:4]) + continue + } + if _, ok := q.receiptTaskPool[hash]; ok { + glog.V(logger.Warn).Infof("Header #%d [%x…] already scheduled for receipt fetch", header.Number.Uint64(), hash[:4]) + continue + } + // Queue the header for content retrieval + q.blockTaskPool[hash] = header + q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) + + if q.mode == FastSync && header.Number.Uint64() <= q.fastSyncPivot { + // Fast phase of the fast sync, retrieve receipts too + q.receiptTaskPool[hash] = header + q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) + } + if q.mode == FastSync && header.Number.Uint64() == q.fastSyncPivot { + // Pivoting point of the fast sync, switch the state retrieval to this + glog.V(logger.Debug).Infof("Switching state downloads to %d [%x…]", header.Number.Uint64(), header.Hash().Bytes()[:4]) + + q.stateTaskIndex = 0 + q.stateTaskPool = make(map[common.Hash]int) + q.stateTaskQueue.Reset() + for _, req := range q.statePendPool { + req.Hashes = make(map[common.Hash]int) // Make sure executing requests fail, but don't disappear + } + + q.stateScheduler = state.NewStateSync(header.Root, q.stateDatabase) + } + inserts = append(inserts, header) + q.headerHead = hash + from++ + } + return inserts +} + +// WaitResults retrieves and permanently removes a batch of fetch +// results from the cache. the result slice will be empty if the queue +// has been closed. +func (q *queue) WaitResults() []*fetchResult { + q.lock.Lock() + defer q.lock.Unlock() + + nproc := q.countProcessableItems() + for nproc == 0 && !q.closed { + q.active.Wait() + nproc = q.countProcessableItems() + } + results := make([]*fetchResult, nproc) + copy(results, q.resultCache[:nproc]) + if len(results) > 0 { + // Mark results as done before dropping them from the cache. + for _, result := range results { + hash := result.Header.Hash() + delete(q.blockDonePool, hash) + delete(q.receiptDonePool, hash) + } + // Delete the results from the cache and clear the tail. + copy(q.resultCache, q.resultCache[nproc:]) + for i := len(q.resultCache) - nproc; i < len(q.resultCache); i++ { + q.resultCache[i] = nil + } + // Advance the expected block number of the first cache entry. + q.resultOffset += uint64(nproc) + } + return results +} + +// countProcessableItems counts the processable items. +func (q *queue) countProcessableItems() int { + for i, result := range q.resultCache { + // Don't process incomplete or unavailable items. + if result == nil || result.Pending > 0 { + return i + } + // Special handling for the fast-sync pivot block: + if q.mode == FastSync { + bnum := result.Header.Number.Uint64() + if bnum == q.fastSyncPivot { + // If the state of the pivot block is not + // available yet, we cannot proceed and return 0. + // + // Stop before processing the pivot block to ensure that + // resultCache has space for fsHeaderForceVerify items. Not + // doing this could leave us unable to download the required + // amount of headers. + if i > 0 || len(q.stateTaskPool) > 0 || q.pendingNodeDataLocked() > 0 { + return i + } + for j := 0; j < fsHeaderForceVerify; j++ { + if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil { + return i + } + } + } + // If we're just the fast sync pivot, stop as well + // because the following batch needs different insertion. + // This simplifies handling the switchover in d.process. + if bnum == q.fastSyncPivot+1 && i > 0 { + return i + } + } + } + return len(q.resultCache) +} + +// ReserveHeaders reserves a set of headers for the given peer, skipping any +// previously failed batches. +func (q *queue) ReserveHeaders(p *peer, count int) *fetchRequest { + q.lock.Lock() + defer q.lock.Unlock() + + // Short circuit if the peer's already downloading something (sanity check to + // not corrupt state) + if _, ok := q.headerPendPool[p.id]; ok { + return nil + } + // Retrieve a batch of hashes, skipping previously failed ones + send, skip := uint64(0), []uint64{} + for send == 0 && !q.headerTaskQueue.Empty() { + from, _ := q.headerTaskQueue.Pop() + if q.headerPeerMiss[p.id] != nil { + if _, ok := q.headerPeerMiss[p.id][from.(uint64)]; ok { + skip = append(skip, from.(uint64)) + continue + } + } + send = from.(uint64) + } + // Merge all the skipped batches back + for _, from := range skip { + q.headerTaskQueue.Push(from, -float32(from)) + } + // Assemble and return the block download request + if send == 0 { + return nil + } + request := &fetchRequest{ + Peer: p, + From: send, + Time: time.Now(), + } + q.headerPendPool[p.id] = request + return request +} + +// ReserveNodeData reserves a set of node data hashes for the given peer, skipping +// any previously failed download. +func (q *queue) ReserveNodeData(p *peer, count int) *fetchRequest { + // Create a task generator to fetch status-fetch tasks if all schedules ones are done + generator := func(max int) { + if q.stateScheduler != nil { + for _, hash := range q.stateScheduler.Missing(max) { + q.stateTaskPool[hash] = q.stateTaskIndex + q.stateTaskQueue.Push(hash, -float32(q.stateTaskIndex)) + q.stateTaskIndex++ + } + } + } + q.lock.Lock() + defer q.lock.Unlock() + + return q.reserveHashes(p, count, q.stateTaskQueue, generator, q.statePendPool, maxInFlightStates) +} + +// reserveHashes reserves a set of hashes for the given peer, skipping previously +// failed ones. +// +// Note, this method expects the queue lock to be already held for writing. The +// reason the lock is not obtained in here is because the parameters already need +// to access the queue, so they already need a lock anyway. +func (q *queue) reserveHashes(p *peer, count int, taskQueue *prque.Prque, taskGen func(int), pendPool map[string]*fetchRequest, maxPending int) *fetchRequest { + // Short circuit if the peer's already downloading something (sanity check to + // not corrupt state) + if _, ok := pendPool[p.id]; ok { + return nil + } + // Calculate an upper limit on the hashes we might fetch (i.e. throttling) + allowance := maxPending + if allowance > 0 { + for _, request := range pendPool { + allowance -= len(request.Hashes) + } + } + // If there's a task generator, ask it to fill our task queue + if taskGen != nil && taskQueue.Size() < allowance { + taskGen(allowance - taskQueue.Size()) + } + if taskQueue.Empty() { + return nil + } + // Retrieve a batch of hashes, skipping previously failed ones + send := make(map[common.Hash]int) + skip := make(map[common.Hash]int) + + for proc := 0; (allowance == 0 || proc < allowance) && len(send) < count && !taskQueue.Empty(); proc++ { + hash, priority := taskQueue.Pop() + if p.Lacks(hash.(common.Hash)) { + skip[hash.(common.Hash)] = int(priority) + } else { + send[hash.(common.Hash)] = int(priority) + } + } + // Merge all the skipped hashes back + for hash, index := range skip { + taskQueue.Push(hash, float32(index)) + } + // Assemble and return the block download request + if len(send) == 0 { + return nil + } + request := &fetchRequest{ + Peer: p, + Hashes: send, + Time: time.Now(), + } + pendPool[p.id] = request + + return request +} + +// ReserveBodies reserves a set of body fetches for the given peer, skipping any +// previously failed downloads. Beside the next batch of needed fetches, it also +// returns a flag whether empty blocks were queued requiring processing. +func (q *queue) ReserveBodies(p *peer, count int) (*fetchRequest, bool, error) { + isNoop := func(header *types.Header) bool { + return header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash + } + q.lock.Lock() + defer q.lock.Unlock() + + return q.reserveHeaders(p, count, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool, q.blockDonePool, isNoop) +} + +// ReserveReceipts reserves a set of receipt fetches for the given peer, skipping +// any previously failed downloads. Beside the next batch of needed fetches, it +// also returns a flag whether empty receipts were queued requiring importing. +func (q *queue) ReserveReceipts(p *peer, count int) (*fetchRequest, bool, error) { + isNoop := func(header *types.Header) bool { + return header.ReceiptHash == types.EmptyRootHash + } + q.lock.Lock() + defer q.lock.Unlock() + + return q.reserveHeaders(p, count, q.receiptTaskPool, q.receiptTaskQueue, q.receiptPendPool, q.receiptDonePool, isNoop) +} + +// reserveHeaders reserves a set of data download operations for a given peer, +// skipping any previously failed ones. This method is a generic version used +// by the individual special reservation functions. +// +// Note, this method expects the queue lock to be already held for writing. The +// reason the lock is not obtained in here is because the parameters already need +// to access the queue, so they already need a lock anyway. +func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, + pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, isNoop func(*types.Header) bool) (*fetchRequest, bool, error) { + // Short circuit if the pool has been depleted, or if the peer's already + // downloading something (sanity check not to corrupt state) + if taskQueue.Empty() { + return nil, false, nil + } + if _, ok := pendPool[p.id]; ok { + return nil, false, nil + } + // Calculate an upper limit on the items we might fetch (i.e. throttling) + space := len(q.resultCache) - len(donePool) + for _, request := range pendPool { + space -= len(request.Headers) + } + // Retrieve a batch of tasks, skipping previously failed ones + send := make([]*types.Header, 0, count) + skip := make([]*types.Header, 0) + + progress := false + for proc := 0; proc < space && len(send) < count && !taskQueue.Empty(); proc++ { + header := taskQueue.PopItem().(*types.Header) + + // If we're the first to request this task, initialise the result container + index := int(header.Number.Int64() - int64(q.resultOffset)) + if index >= len(q.resultCache) || index < 0 { + common.Report("index allocation went beyond available resultCache space") + return nil, false, errInvalidChain + } + if q.resultCache[index] == nil { + components := 1 + if q.mode == FastSync && header.Number.Uint64() <= q.fastSyncPivot { + components = 2 + } + q.resultCache[index] = &fetchResult{ + Pending: components, + Header: header, + } + } + // If this fetch task is a noop, skip this fetch operation + if isNoop(header) { + donePool[header.Hash()] = struct{}{} + delete(taskPool, header.Hash()) + + space, proc = space-1, proc-1 + q.resultCache[index].Pending-- + progress = true + continue + } + // Otherwise unless the peer is known not to have the data, add to the retrieve list + if p.Lacks(header.Hash()) { + skip = append(skip, header) + } else { + send = append(send, header) + } + } + // Merge all the skipped headers back + for _, header := range skip { + taskQueue.Push(header, -float32(header.Number.Uint64())) + } + if progress { + // Wake WaitResults, resultCache was modified + q.active.Signal() + } + // Assemble and return the block download request + if len(send) == 0 { + return nil, progress, nil + } + request := &fetchRequest{ + Peer: p, + Headers: send, + Time: time.Now(), + } + pendPool[p.id] = request + + return request, progress, nil +} + +// CancelHeaders aborts a fetch request, returning all pending skeleton indexes to the queue. +func (q *queue) CancelHeaders(request *fetchRequest) { + q.cancel(request, q.headerTaskQueue, q.headerPendPool) +} + +// CancelBodies aborts a body fetch request, returning all pending headers to the +// task queue. +func (q *queue) CancelBodies(request *fetchRequest) { + q.cancel(request, q.blockTaskQueue, q.blockPendPool) +} + +// CancelReceipts aborts a body fetch request, returning all pending headers to +// the task queue. +func (q *queue) CancelReceipts(request *fetchRequest) { + q.cancel(request, q.receiptTaskQueue, q.receiptPendPool) +} + +// CancelNodeData aborts a node state data fetch request, returning all pending +// hashes to the task queue. +func (q *queue) CancelNodeData(request *fetchRequest) { + q.cancel(request, q.stateTaskQueue, q.statePendPool) +} + +// Cancel aborts a fetch request, returning all pending hashes to the task queue. +func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool map[string]*fetchRequest) { + q.lock.Lock() + defer q.lock.Unlock() + + if request.From > 0 { + taskQueue.Push(request.From, -float32(request.From)) + } + for hash, index := range request.Hashes { + taskQueue.Push(hash, float32(index)) + } + for _, header := range request.Headers { + taskQueue.Push(header, -float32(header.Number.Uint64())) + } + delete(pendPool, request.Peer.id) +} + +// Revoke cancels all pending requests belonging to a given peer. This method is +// meant to be called during a peer drop to quickly reassign owned data fetches +// to remaining nodes. +func (q *queue) Revoke(peerId string) { + q.lock.Lock() + defer q.lock.Unlock() + + if request, ok := q.blockPendPool[peerId]; ok { + for _, header := range request.Headers { + q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) + } + delete(q.blockPendPool, peerId) + } + if request, ok := q.receiptPendPool[peerId]; ok { + for _, header := range request.Headers { + q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) + } + delete(q.receiptPendPool, peerId) + } + if request, ok := q.statePendPool[peerId]; ok { + for hash, index := range request.Hashes { + q.stateTaskQueue.Push(hash, float32(index)) + } + delete(q.statePendPool, peerId) + } +} + +// ExpireHeaders checks for in flight requests that exceeded a timeout allowance, +// canceling them and returning the responsible peers for penalisation. +func (q *queue) ExpireHeaders(timeout time.Duration) map[string]int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.expire(timeout, q.headerPendPool, q.headerTaskQueue, headerTimeoutMeter) +} + +// ExpireBodies checks for in flight block body requests that exceeded a timeout +// allowance, canceling them and returning the responsible peers for penalisation. +func (q *queue) ExpireBodies(timeout time.Duration) map[string]int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.expire(timeout, q.blockPendPool, q.blockTaskQueue, bodyTimeoutMeter) +} + +// ExpireReceipts checks for in flight receipt requests that exceeded a timeout +// allowance, canceling them and returning the responsible peers for penalisation. +func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.expire(timeout, q.receiptPendPool, q.receiptTaskQueue, receiptTimeoutMeter) +} + +// ExpireNodeData checks for in flight node data requests that exceeded a timeout +// allowance, canceling them and returning the responsible peers for penalisation. +func (q *queue) ExpireNodeData(timeout time.Duration) map[string]int { + q.lock.Lock() + defer q.lock.Unlock() + + return q.expire(timeout, q.statePendPool, q.stateTaskQueue, stateTimeoutMeter) +} + +// expire is the generic check that move expired tasks from a pending pool back +// into a task pool, returning all entities caught with expired tasks. +// +// Note, this method expects the queue lock to be already held. The +// reason the lock is not obtained in here is because the parameters already need +// to access the queue, so they already need a lock anyway. +func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int { + // Iterate over the expired requests and return each to the queue + expiries := make(map[string]int) + for id, request := range pendPool { + if time.Since(request.Time) > timeout { + // Update the metrics with the timeout + timeoutMeter.Mark(1) + + // Return any non satisfied requests to the pool + if request.From > 0 { + taskQueue.Push(request.From, -float32(request.From)) + } + for hash, index := range request.Hashes { + taskQueue.Push(hash, float32(index)) + } + for _, header := range request.Headers { + taskQueue.Push(header, -float32(header.Number.Uint64())) + } + // Add the peer to the expiry report along the the number of failed requests + expirations := len(request.Hashes) + if expirations < len(request.Headers) { + expirations = len(request.Headers) + } + expiries[id] = expirations + } + } + // Remove the expired requests from the pending pool + for id := range expiries { + delete(pendPool, id) + } + return expiries +} + +// DeliverHeaders injects a header retrieval response into the header results +// cache. This method either accepts all headers it received, or none of them +// if they do not map correctly to the skeleton. +// +// If the headers are accepted, the method makes an attempt to deliver the set +// of ready headers to the processor to keep the pipeline full. However it will +// not block to prevent stalling other pending deliveries. +func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh chan []*types.Header) (int, error) { + q.lock.Lock() + defer q.lock.Unlock() + + // Short circuit if the data was never requested + request := q.headerPendPool[id] + if request == nil { + return 0, errNoFetchesPending + } + headerReqTimer.UpdateSince(request.Time) + delete(q.headerPendPool, id) + + // Ensure headers can be mapped onto the skeleton chain + target := q.headerTaskPool[request.From].Hash() + + accepted := len(headers) == MaxHeaderFetch + if accepted { + if headers[0].Number.Uint64() != request.From { + glog.V(logger.Detail).Infof("Peer %s: first header #%v [%x…] broke chain ordering, expected %d", id, headers[0].Number, headers[0].Hash().Bytes()[:4], request.From) + accepted = false + } else if headers[len(headers)-1].Hash() != target { + glog.V(logger.Detail).Infof("Peer %s: last header #%v [%x…] broke skeleton structure, expected %x", id, headers[len(headers)-1].Number, headers[len(headers)-1].Hash().Bytes()[:4], target[:4]) + accepted = false + } + } + if accepted { + for i, header := range headers[1:] { + hash := header.Hash() + if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ordering, expected %d", id, header.Number, hash[:4], want) + accepted = false + break + } + if headers[i].Hash() != header.ParentHash { + glog.V(logger.Warn).Infof("Peer %s: header #%v [%x…] broke chain ancestry", id, header.Number, hash[:4]) + accepted = false + break + } + } + } + // If the batch of headers wasn't accepted, mark as unavailable + if !accepted { + glog.V(logger.Detail).Infof("Peer %s: skeleton filling from header #%d not accepted", id, request.From) + + miss := q.headerPeerMiss[id] + if miss == nil { + q.headerPeerMiss[id] = make(map[uint64]struct{}) + miss = q.headerPeerMiss[id] + } + miss[request.From] = struct{}{} + + q.headerTaskQueue.Push(request.From, -float32(request.From)) + return 0, errors.New("delivery not accepted") + } + // Clean up a successful fetch and try to deliver any sub-results + copy(q.headerResults[request.From-q.headerOffset:], headers) + delete(q.headerTaskPool, request.From) + + ready := 0 + for q.headerProced+ready < len(q.headerResults) && q.headerResults[q.headerProced+ready] != nil { + ready += MaxHeaderFetch + } + if ready > 0 { + // Headers are ready for delivery, gather them and push forward (non blocking) + process := make([]*types.Header, ready) + copy(process, q.headerResults[q.headerProced:q.headerProced+ready]) + + select { + case headerProcCh <- process: + glog.V(logger.Detail).Infof("%s: pre-scheduled %d headers from #%v", id, len(process), process[0].Number) + q.headerProced += len(process) + default: + } + } + // Check for termination and return + if len(q.headerTaskPool) == 0 { + q.headerContCh <- false + } + return len(headers), nil +} + +// DeliverBodies injects a block body retrieval response into the results queue. +// The method returns the number of blocks bodies accepted from the delivery and +// also wakes any threads waiting for data delivery. +func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) { + q.lock.Lock() + defer q.lock.Unlock() + + reconstruct := func(header *types.Header, index int, result *fetchResult) error { + if types.DeriveSha(types.Transactions(txLists[index])) != header.TxHash || types.CalcUncleHash(uncleLists[index]) != header.UncleHash { + return errInvalidBody + } + result.Transactions = txLists[index] + result.Uncles = uncleLists[index] + return nil + } + return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool, q.blockDonePool, bodyReqTimer, len(txLists), reconstruct) +} + +// DeliverReceipts injects a receipt retrieval response into the results queue. +// The method returns the number of transaction receipts accepted from the delivery +// and also wakes any threads waiting for data delivery. +func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, error) { + q.lock.Lock() + defer q.lock.Unlock() + + reconstruct := func(header *types.Header, index int, result *fetchResult) error { + if types.DeriveSha(types.Receipts(receiptList[index])) != header.ReceiptHash { + return errInvalidReceipt + } + result.Receipts = receiptList[index] + return nil + } + return q.deliver(id, q.receiptTaskPool, q.receiptTaskQueue, q.receiptPendPool, q.receiptDonePool, receiptReqTimer, len(receiptList), reconstruct) +} + +// deliver injects a data retrieval response into the results queue. +// +// Note, this method expects the queue lock to be already held for writing. The +// reason the lock is not obtained in here is because the parameters already need +// to access the queue, so they already need a lock anyway. +func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, + pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer metrics.Timer, + results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) { + + // Short circuit if the data was never requested + request := pendPool[id] + if request == nil { + return 0, errNoFetchesPending + } + reqTimer.UpdateSince(request.Time) + delete(pendPool, id) + + // If no data items were retrieved, mark them as unavailable for the origin peer + if results == 0 { + for _, header := range request.Headers { + request.Peer.MarkLacking(header.Hash()) + } + } + // Assemble each of the results with their headers and retrieved data parts + var ( + accepted int + failure error + useful bool + ) + for i, header := range request.Headers { + // Short circuit assembly if no more fetch results are found + if i >= results { + break + } + // Reconstruct the next result if contents match up + index := int(header.Number.Int64() - int64(q.resultOffset)) + if index >= len(q.resultCache) || index < 0 || q.resultCache[index] == nil { + failure = errInvalidChain + break + } + if err := reconstruct(header, i, q.resultCache[index]); err != nil { + failure = err + break + } + donePool[header.Hash()] = struct{}{} + q.resultCache[index].Pending-- + useful = true + accepted++ + + // Clean up a successful fetch + request.Headers[i] = nil + delete(taskPool, header.Hash()) + } + // Return all failed or missing fetches to the queue + for _, header := range request.Headers { + if header != nil { + taskQueue.Push(header, -float32(header.Number.Uint64())) + } + } + // Wake up WaitResults + if accepted > 0 { + q.active.Signal() + } + // If none of the data was good, it's a stale delivery + switch { + case failure == nil || failure == errInvalidChain: + return accepted, failure + case useful: + return accepted, fmt.Errorf("partial failure: %v", failure) + default: + return accepted, errStaleDelivery + } +} + +// DeliverNodeData injects a node state data retrieval response into the queue. +// The method returns the number of node state accepted from the delivery. +func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(int, bool, error)) (int, error) { + q.lock.Lock() + defer q.lock.Unlock() + + // Short circuit if the data was never requested + request := q.statePendPool[id] + if request == nil { + return 0, errNoFetchesPending + } + stateReqTimer.UpdateSince(request.Time) + delete(q.statePendPool, id) + + // If no data was retrieved, mark their hashes as unavailable for the origin peer + if len(data) == 0 { + for hash := range request.Hashes { + request.Peer.MarkLacking(hash) + } + } + // Iterate over the downloaded data and verify each of them + errs := make([]error, 0) + process := []trie.SyncResult{} + for _, blob := range data { + // Skip any state trie entries that were not requested + hash := common.BytesToHash(crypto.Keccak256(blob)) + if _, ok := request.Hashes[hash]; !ok { + errs = append(errs, fmt.Errorf("non-requested state data %x", hash)) + continue + } + // Inject the next state trie item into the processing queue + process = append(process, trie.SyncResult{Hash: hash, Data: blob}) + delete(request.Hashes, hash) + delete(q.stateTaskPool, hash) + } + // Return all failed or missing fetches to the queue + for hash, index := range request.Hashes { + q.stateTaskQueue.Push(hash, float32(index)) + } + if q.stateScheduler == nil { + return 0, errNoFetchesPending + } + + // Run valid nodes through the trie download scheduler. It writes completed nodes to a + // batch, which is committed asynchronously. This may lead to over-fetches because the + // scheduler treats everything as written after Process has returned, but it's + // unlikely to be an issue in practice. + batch := q.stateDatabase.NewBatch() + progressed, nproc, procerr := q.stateScheduler.Process(process, batch) + q.stateWriters += 1 + go func() { + if procerr == nil { + nproc = len(process) + procerr = batch.Write() + } + // Return processing errors through the callback so the sync gets canceled. The + // number of writers is decremented prior to the call so PendingNodeData will + // return zero when the callback runs. + q.lock.Lock() + q.stateWriters -= 1 + q.lock.Unlock() + callback(nproc, progressed, procerr) + // Wake up WaitResults after the state has been written because it might be + // waiting for completion of the pivot block's state download. + q.active.Signal() + }() + + // If none of the data items were good, it's a stale delivery + switch { + case len(errs) == 0: + return len(process), nil + case len(errs) == len(request.Hashes): + return len(process), errStaleDelivery + default: + return len(process), fmt.Errorf("multiple failures: %v", errs) + } +} + +// Prepare configures the result cache to allow accepting and caching inbound +// fetch results. +func (q *queue) Prepare(offset uint64, mode SyncMode, pivot uint64, head *types.Header) { + q.lock.Lock() + defer q.lock.Unlock() + + // Prepare the queue for sync results + if q.resultOffset < offset { + q.resultOffset = offset + } + q.fastSyncPivot = pivot + q.mode = mode + + // If long running fast sync, also start up a head stateretrieval immediately + if mode == FastSync && pivot > 0 { + q.stateScheduler = state.NewStateSync(head.Root, q.stateDatabase) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/types.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/types.go new file mode 100644 index 0000000..e105104 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/types.go @@ -0,0 +1,120 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// headerCheckFn is a callback type for verifying a header's presence in the local chain. +type headerCheckFn func(common.Hash) bool + +// blockAndStateCheckFn is a callback type for verifying block and associated states' presence in the local chain. +type blockAndStateCheckFn func(common.Hash) bool + +// headerRetrievalFn is a callback type for retrieving a header from the local chain. +type headerRetrievalFn func(common.Hash) *types.Header + +// blockRetrievalFn is a callback type for retrieving a block from the local chain. +type blockRetrievalFn func(common.Hash) *types.Block + +// headHeaderRetrievalFn is a callback type for retrieving the head header from the local chain. +type headHeaderRetrievalFn func() *types.Header + +// headBlockRetrievalFn is a callback type for retrieving the head block from the local chain. +type headBlockRetrievalFn func() *types.Block + +// headFastBlockRetrievalFn is a callback type for retrieving the head fast block from the local chain. +type headFastBlockRetrievalFn func() *types.Block + +// headBlockCommitterFn is a callback for directly committing the head block to a certain entity. +type headBlockCommitterFn func(common.Hash) error + +// tdRetrievalFn is a callback type for retrieving the total difficulty of a local block. +type tdRetrievalFn func(common.Hash) *big.Int + +// headerChainInsertFn is a callback type to insert a batch of headers into the local chain. +type headerChainInsertFn func([]*types.Header, int) (int, error) + +// blockChainInsertFn is a callback type to insert a batch of blocks into the local chain. +type blockChainInsertFn func(types.Blocks) (int, error) + +// receiptChainInsertFn is a callback type to insert a batch of receipts into the local chain. +type receiptChainInsertFn func(types.Blocks, []types.Receipts) (int, error) + +// chainRollbackFn is a callback type to remove a few recently added elements from the local chain. +type chainRollbackFn func([]common.Hash) + +// peerDropFn is a callback type for dropping a peer detected as malicious. +type peerDropFn func(id string) + +// dataPack is a data message returned by a peer for some query. +type dataPack interface { + PeerId() string + Items() int + Stats() string +} + +// headerPack is a batch of block headers returned by a peer. +type headerPack struct { + peerId string + headers []*types.Header +} + +func (p *headerPack) PeerId() string { return p.peerId } +func (p *headerPack) Items() int { return len(p.headers) } +func (p *headerPack) Stats() string { return fmt.Sprintf("%d", len(p.headers)) } + +// bodyPack is a batch of block bodies returned by a peer. +type bodyPack struct { + peerId string + transactions [][]*types.Transaction + uncles [][]*types.Header +} + +func (p *bodyPack) PeerId() string { return p.peerId } +func (p *bodyPack) Items() int { + if len(p.transactions) <= len(p.uncles) { + return len(p.transactions) + } + return len(p.uncles) +} +func (p *bodyPack) Stats() string { return fmt.Sprintf("%d:%d", len(p.transactions), len(p.uncles)) } + +// receiptPack is a batch of receipts returned by a peer. +type receiptPack struct { + peerId string + receipts [][]*types.Receipt +} + +func (p *receiptPack) PeerId() string { return p.peerId } +func (p *receiptPack) Items() int { return len(p.receipts) } +func (p *receiptPack) Stats() string { return fmt.Sprintf("%d", len(p.receipts)) } + +// statePack is a batch of states returned by a peer. +type statePack struct { + peerId string + states [][]byte +} + +func (p *statePack) PeerId() string { return p.peerId } +func (p *statePack) Items() int { return len(p.states) } +func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go new file mode 100644 index 0000000..e225b49 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher.go @@ -0,0 +1,751 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package fetcher contains the block announcement based synchronisation. +package fetcher + +import ( + "errors" + "fmt" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "gopkg.in/karalabe/cookiejar.v2/collections/prque" +) + +const ( + arriveTimeout = 500 * time.Millisecond // Time allowance before an announced block is explicitly requested + gatherSlack = 100 * time.Millisecond // Interval used to collate almost-expired announces with fetches + fetchTimeout = 5 * time.Second // Maximum allotted time to return an explicitly requested block + maxUncleDist = 7 // Maximum allowed backward distance from the chain head + maxQueueDist = 32 // Maximum allowed distance from the chain head to queue + hashLimit = 256 // Maximum number of unique blocks a peer may have announced + blockLimit = 64 // Maximum number of unique blocks a peer may have delivered +) + +var ( + errTerminated = errors.New("terminated") +) + +// blockRetrievalFn is a callback type for retrieving a block from the local chain. +type blockRetrievalFn func(common.Hash) *types.Block + +// headerRequesterFn is a callback type for sending a header retrieval request. +type headerRequesterFn func(common.Hash) error + +// bodyRequesterFn is a callback type for sending a body retrieval request. +type bodyRequesterFn func([]common.Hash) error + +// blockValidatorFn is a callback type to verify a block's header for fast propagation. +type blockValidatorFn func(block *types.Block, parent *types.Block) error + +// blockBroadcasterFn is a callback type for broadcasting a block to connected peers. +type blockBroadcasterFn func(block *types.Block, propagate bool) + +// chainHeightFn is a callback type to retrieve the current chain height. +type chainHeightFn func() uint64 + +// chainInsertFn is a callback type to insert a batch of blocks into the local chain. +type chainInsertFn func(types.Blocks) (int, error) + +// peerDropFn is a callback type for dropping a peer detected as malicious. +type peerDropFn func(id string) + +// announce is the hash notification of the availability of a new block in the +// network. +type announce struct { + hash common.Hash // Hash of the block being announced + number uint64 // Number of the block being announced (0 = unknown | old protocol) + header *types.Header // Header of the block partially reassembled (new protocol) + time time.Time // Timestamp of the announcement + + origin string // Identifier of the peer originating the notification + + fetchHeader headerRequesterFn // [eth/62] Fetcher function to retrieve the header of an announced block + fetchBodies bodyRequesterFn // [eth/62] Fetcher function to retrieve the body of an announced block +} + +// headerFilterTask represents a batch of headers needing fetcher filtering. +type headerFilterTask struct { + headers []*types.Header // Collection of headers to filter + time time.Time // Arrival time of the headers +} + +// headerFilterTask represents a batch of block bodies (transactions and uncles) +// needing fetcher filtering. +type bodyFilterTask struct { + transactions [][]*types.Transaction // Collection of transactions per block bodies + uncles [][]*types.Header // Collection of uncles per block bodies + time time.Time // Arrival time of the blocks' contents +} + +// inject represents a schedules import operation. +type inject struct { + origin string + block *types.Block +} + +// Fetcher is responsible for accumulating block announcements from various peers +// and scheduling them for retrieval. +type Fetcher struct { + // Various event channels + notify chan *announce + inject chan *inject + + blockFilter chan chan []*types.Block + headerFilter chan chan *headerFilterTask + bodyFilter chan chan *bodyFilterTask + + done chan common.Hash + quit chan struct{} + + // Announce states + announces map[string]int // Per peer announce counts to prevent memory exhaustion + announced map[common.Hash][]*announce // Announced blocks, scheduled for fetching + fetching map[common.Hash]*announce // Announced blocks, currently fetching + fetched map[common.Hash][]*announce // Blocks with headers fetched, scheduled for body retrieval + completing map[common.Hash]*announce // Blocks with headers, currently body-completing + + // Block cache + queue *prque.Prque // Queue containing the import operations (block number sorted) + queues map[string]int // Per peer block counts to prevent memory exhaustion + queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports) + + // Callbacks + getBlock blockRetrievalFn // Retrieves a block from the local chain + validateBlock blockValidatorFn // Checks if a block's headers have a valid proof of work + broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers + chainHeight chainHeightFn // Retrieves the current chain's height + insertChain chainInsertFn // Injects a batch of blocks into the chain + dropPeer peerDropFn // Drops a peer for misbehaving + + // Testing hooks + announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the announce list + queueChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a block from the import queue + fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch + completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62) + importedHook func(*types.Block) // Method to call upon successful block import (both eth/61 and eth/62) +} + +// New creates a block fetcher to retrieve blocks based on hash announcements. +func New(getBlock blockRetrievalFn, validateBlock blockValidatorFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher { + return &Fetcher{ + notify: make(chan *announce), + inject: make(chan *inject), + blockFilter: make(chan chan []*types.Block), + headerFilter: make(chan chan *headerFilterTask), + bodyFilter: make(chan chan *bodyFilterTask), + done: make(chan common.Hash), + quit: make(chan struct{}), + announces: make(map[string]int), + announced: make(map[common.Hash][]*announce), + fetching: make(map[common.Hash]*announce), + fetched: make(map[common.Hash][]*announce), + completing: make(map[common.Hash]*announce), + queue: prque.New(), + queues: make(map[string]int), + queued: make(map[common.Hash]*inject), + getBlock: getBlock, + validateBlock: validateBlock, + broadcastBlock: broadcastBlock, + chainHeight: chainHeight, + insertChain: insertChain, + dropPeer: dropPeer, + } +} + +// Start boots up the announcement based synchroniser, accepting and processing +// hash notifications and block fetches until termination requested. +func (f *Fetcher) Start() { + go f.loop() +} + +// Stop terminates the announcement based synchroniser, canceling all pending +// operations. +func (f *Fetcher) Stop() { + close(f.quit) +} + +// Notify announces the fetcher of the potential availability of a new block in +// the network. +func (f *Fetcher) Notify(peer string, hash common.Hash, number uint64, time time.Time, + headerFetcher headerRequesterFn, bodyFetcher bodyRequesterFn) error { + block := &announce{ + hash: hash, + number: number, + time: time, + origin: peer, + fetchHeader: headerFetcher, + fetchBodies: bodyFetcher, + } + select { + case f.notify <- block: + return nil + case <-f.quit: + return errTerminated + } +} + +// Enqueue tries to fill gaps the the fetcher's future import queue. +func (f *Fetcher) Enqueue(peer string, block *types.Block) error { + op := &inject{ + origin: peer, + block: block, + } + select { + case f.inject <- op: + return nil + case <-f.quit: + return errTerminated + } +} + +// FilterHeaders extracts all the headers that were explicitly requested by the fetcher, +// returning those that should be handled differently. +func (f *Fetcher) FilterHeaders(headers []*types.Header, time time.Time) []*types.Header { + glog.V(logger.Detail).Infof("[eth/62] filtering %d headers", len(headers)) + + // Send the filter channel to the fetcher + filter := make(chan *headerFilterTask) + + select { + case f.headerFilter <- filter: + case <-f.quit: + return nil + } + // Request the filtering of the header list + select { + case filter <- &headerFilterTask{headers: headers, time: time}: + case <-f.quit: + return nil + } + // Retrieve the headers remaining after filtering + select { + case task := <-filter: + return task.headers + case <-f.quit: + return nil + } +} + +// FilterBodies extracts all the block bodies that were explicitly requested by +// the fetcher, returning those that should be handled differently. +func (f *Fetcher) FilterBodies(transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) { + glog.V(logger.Detail).Infof("[eth/62] filtering %d:%d bodies", len(transactions), len(uncles)) + + // Send the filter channel to the fetcher + filter := make(chan *bodyFilterTask) + + select { + case f.bodyFilter <- filter: + case <-f.quit: + return nil, nil + } + // Request the filtering of the body list + select { + case filter <- &bodyFilterTask{transactions: transactions, uncles: uncles, time: time}: + case <-f.quit: + return nil, nil + } + // Retrieve the bodies remaining after filtering + select { + case task := <-filter: + return task.transactions, task.uncles + case <-f.quit: + return nil, nil + } +} + +// Loop is the main fetcher loop, checking and processing various notification +// events. +func (f *Fetcher) loop() { + // Iterate the block fetching until a quit is requested + fetchTimer := time.NewTimer(0) + completeTimer := time.NewTimer(0) + + for { + // Clean up any expired block fetches + for hash, announce := range f.fetching { + if time.Since(announce.time) > fetchTimeout { + f.forgetHash(hash) + } + } + // Import any queued blocks that could potentially fit + height := f.chainHeight() + for !f.queue.Empty() { + op := f.queue.PopItem().(*inject) + if f.queueChangeHook != nil { + f.queueChangeHook(op.block.Hash(), false) + } + // If too high up the chain or phase, continue later + number := op.block.NumberU64() + if number > height+1 { + f.queue.Push(op, -float32(op.block.NumberU64())) + if f.queueChangeHook != nil { + f.queueChangeHook(op.block.Hash(), true) + } + break + } + // Otherwise if fresh and still unknown, try and import + hash := op.block.Hash() + if number+maxUncleDist < height || f.getBlock(hash) != nil { + f.forgetBlock(hash) + continue + } + f.insert(op.origin, op.block) + } + // Wait for an outside event to occur + select { + case <-f.quit: + // Fetcher terminating, abort all operations + return + + case notification := <-f.notify: + // A block was announced, make sure the peer isn't DOSing us + propAnnounceInMeter.Mark(1) + + count := f.announces[notification.origin] + 1 + if count > hashLimit { + glog.V(logger.Debug).Infof("Peer %s: exceeded outstanding announces (%d)", notification.origin, hashLimit) + propAnnounceDOSMeter.Mark(1) + break + } + // If we have a valid block number, check that it's potentially useful + if notification.number > 0 { + if dist := int64(notification.number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist { + glog.V(logger.Debug).Infof("[eth/62] Peer %s: discarded announcement #%d [%x…], distance %d", notification.origin, notification.number, notification.hash[:4], dist) + propAnnounceDropMeter.Mark(1) + break + } + } + // All is well, schedule the announce if block's not yet downloading + if _, ok := f.fetching[notification.hash]; ok { + break + } + if _, ok := f.completing[notification.hash]; ok { + break + } + f.announces[notification.origin] = count + f.announced[notification.hash] = append(f.announced[notification.hash], notification) + if f.announceChangeHook != nil && len(f.announced[notification.hash]) == 1 { + f.announceChangeHook(notification.hash, true) + } + if len(f.announced) == 1 { + f.rescheduleFetch(fetchTimer) + } + + case op := <-f.inject: + // A direct block insertion was requested, try and fill any pending gaps + propBroadcastInMeter.Mark(1) + f.enqueue(op.origin, op.block) + + case hash := <-f.done: + // A pending import finished, remove all traces of the notification + f.forgetHash(hash) + f.forgetBlock(hash) + + case <-fetchTimer.C: + // At least one block's timer ran out, check for needing retrieval + request := make(map[string][]common.Hash) + + for hash, announces := range f.announced { + if time.Since(announces[0].time) > arriveTimeout-gatherSlack { + // Pick a random peer to retrieve from, reset all others + announce := announces[rand.Intn(len(announces))] + f.forgetHash(hash) + + // If the block still didn't arrive, queue for fetching + if f.getBlock(hash) == nil { + request[announce.origin] = append(request[announce.origin], hash) + f.fetching[hash] = announce + } + } + } + // Send out all block header requests + for peer, hashes := range request { + if glog.V(logger.Detail) && len(hashes) > 0 { + list := "[" + for _, hash := range hashes { + list += fmt.Sprintf("%x…, ", hash[:4]) + } + list = list[:len(list)-2] + "]" + glog.V(logger.Detail).Infof("[eth/62] Peer %s: fetching headers %s", peer, list) + } + // Create a closure of the fetch and schedule in on a new thread + fetchHeader, hashes := f.fetching[hashes[0]].fetchHeader, hashes + go func() { + if f.fetchingHook != nil { + f.fetchingHook(hashes) + } + for _, hash := range hashes { + headerFetchMeter.Mark(1) + fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals + } + }() + } + // Schedule the next fetch if blocks are still pending + f.rescheduleFetch(fetchTimer) + + case <-completeTimer.C: + // At least one header's timer ran out, retrieve everything + request := make(map[string][]common.Hash) + + for hash, announces := range f.fetched { + // Pick a random peer to retrieve from, reset all others + announce := announces[rand.Intn(len(announces))] + f.forgetHash(hash) + + // If the block still didn't arrive, queue for completion + if f.getBlock(hash) == nil { + request[announce.origin] = append(request[announce.origin], hash) + f.completing[hash] = announce + } + } + // Send out all block body requests + for peer, hashes := range request { + if glog.V(logger.Detail) && len(hashes) > 0 { + list := "[" + for _, hash := range hashes { + list += fmt.Sprintf("%x…, ", hash[:4]) + } + list = list[:len(list)-2] + "]" + + glog.V(logger.Detail).Infof("[eth/62] Peer %s: fetching bodies %s", peer, list) + } + // Create a closure of the fetch and schedule in on a new thread + if f.completingHook != nil { + f.completingHook(hashes) + } + bodyFetchMeter.Mark(int64(len(hashes))) + go f.completing[hashes[0]].fetchBodies(hashes) + } + // Schedule the next fetch if blocks are still pending + f.rescheduleComplete(completeTimer) + + case filter := <-f.headerFilter: + // Headers arrived from a remote peer. Extract those that were explicitly + // requested by the fetcher, and return everything else so it's delivered + // to other parts of the system. + var task *headerFilterTask + select { + case task = <-filter: + case <-f.quit: + return + } + headerFilterInMeter.Mark(int64(len(task.headers))) + + // Split the batch of headers into unknown ones (to return to the caller), + // known incomplete ones (requiring body retrievals) and completed blocks. + unknown, incomplete, complete := []*types.Header{}, []*announce{}, []*types.Block{} + for _, header := range task.headers { + hash := header.Hash() + + // Filter fetcher-requested headers from other synchronisation algorithms + if announce := f.fetching[hash]; announce != nil && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil { + // If the delivered header does not match the promised number, drop the announcer + if header.Number.Uint64() != announce.number { + glog.V(logger.Detail).Infof("[eth/62] Peer %s: invalid block number for [%x…]: announced %d, provided %d", announce.origin, header.Hash().Bytes()[:4], announce.number, header.Number.Uint64()) + f.dropPeer(announce.origin) + f.forgetHash(hash) + continue + } + // Only keep if not imported by other means + if f.getBlock(hash) == nil { + announce.header = header + announce.time = task.time + + // If the block is empty (header only), short circuit into the final import queue + if header.TxHash == types.DeriveSha(types.Transactions{}) && header.UncleHash == types.CalcUncleHash([]*types.Header{}) { + glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] empty, skipping body retrieval", announce.origin, header.Number.Uint64(), header.Hash().Bytes()[:4]) + + block := types.NewBlockWithHeader(header) + block.ReceivedAt = task.time + + complete = append(complete, block) + f.completing[hash] = announce + continue + } + // Otherwise add to the list of blocks needing completion + incomplete = append(incomplete, announce) + } else { + glog.V(logger.Detail).Infof("[eth/62] Peer %s: block #%d [%x…] already imported, discarding header", announce.origin, header.Number.Uint64(), header.Hash().Bytes()[:4]) + f.forgetHash(hash) + } + } else { + // Fetcher doesn't know about it, add to the return list + unknown = append(unknown, header) + } + } + headerFilterOutMeter.Mark(int64(len(unknown))) + select { + case filter <- &headerFilterTask{headers: unknown, time: task.time}: + case <-f.quit: + return + } + // Schedule the retrieved headers for body completion + for _, announce := range incomplete { + hash := announce.header.Hash() + if _, ok := f.completing[hash]; ok { + continue + } + f.fetched[hash] = append(f.fetched[hash], announce) + if len(f.fetched) == 1 { + f.rescheduleComplete(completeTimer) + } + } + // Schedule the header-only blocks for import + for _, block := range complete { + if announce := f.completing[block.Hash()]; announce != nil { + f.enqueue(announce.origin, block) + } + } + + case filter := <-f.bodyFilter: + // Block bodies arrived, extract any explicitly requested blocks, return the rest + var task *bodyFilterTask + select { + case task = <-filter: + case <-f.quit: + return + } + bodyFilterInMeter.Mark(int64(len(task.transactions))) + + blocks := []*types.Block{} + for i := 0; i < len(task.transactions) && i < len(task.uncles); i++ { + // Match up a body to any possible completion request + matched := false + + for hash, announce := range f.completing { + if f.queued[hash] == nil { + txnHash := types.DeriveSha(types.Transactions(task.transactions[i])) + uncleHash := types.CalcUncleHash(task.uncles[i]) + + if txnHash == announce.header.TxHash && uncleHash == announce.header.UncleHash { + // Mark the body matched, reassemble if still unknown + matched = true + + if f.getBlock(hash) == nil { + block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i]) + block.ReceivedAt = task.time + + blocks = append(blocks, block) + } else { + f.forgetHash(hash) + } + } + } + } + if matched { + task.transactions = append(task.transactions[:i], task.transactions[i+1:]...) + task.uncles = append(task.uncles[:i], task.uncles[i+1:]...) + i-- + continue + } + } + + bodyFilterOutMeter.Mark(int64(len(task.transactions))) + select { + case filter <- task: + case <-f.quit: + return + } + // Schedule the retrieved blocks for ordered import + for _, block := range blocks { + if announce := f.completing[block.Hash()]; announce != nil { + f.enqueue(announce.origin, block) + } + } + } + } +} + +// rescheduleFetch resets the specified fetch timer to the next announce timeout. +func (f *Fetcher) rescheduleFetch(fetch *time.Timer) { + // Short circuit if no blocks are announced + if len(f.announced) == 0 { + return + } + // Otherwise find the earliest expiring announcement + earliest := time.Now() + for _, announces := range f.announced { + if earliest.After(announces[0].time) { + earliest = announces[0].time + } + } + fetch.Reset(arriveTimeout - time.Since(earliest)) +} + +// rescheduleComplete resets the specified completion timer to the next fetch timeout. +func (f *Fetcher) rescheduleComplete(complete *time.Timer) { + // Short circuit if no headers are fetched + if len(f.fetched) == 0 { + return + } + // Otherwise find the earliest expiring announcement + earliest := time.Now() + for _, announces := range f.fetched { + if earliest.After(announces[0].time) { + earliest = announces[0].time + } + } + complete.Reset(gatherSlack - time.Since(earliest)) +} + +// enqueue schedules a new future import operation, if the block to be imported +// has not yet been seen. +func (f *Fetcher) enqueue(peer string, block *types.Block) { + hash := block.Hash() + + // Ensure the peer isn't DOSing us + count := f.queues[peer] + 1 + if count > blockLimit { + glog.V(logger.Debug).Infof("Peer %s: discarded block #%d [%x…], exceeded allowance (%d)", peer, block.NumberU64(), hash.Bytes()[:4], blockLimit) + propBroadcastDOSMeter.Mark(1) + f.forgetHash(hash) + return + } + // Discard any past or too distant blocks + if dist := int64(block.NumberU64()) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist { + glog.V(logger.Debug).Infof("Peer %s: discarded block #%d [%x…], distance %d", peer, block.NumberU64(), hash.Bytes()[:4], dist) + propBroadcastDropMeter.Mark(1) + f.forgetHash(hash) + return + } + // Schedule the block for future importing + if _, ok := f.queued[hash]; !ok { + op := &inject{ + origin: peer, + block: block, + } + f.queues[peer] = count + f.queued[hash] = op + f.queue.Push(op, -float32(block.NumberU64())) + if f.queueChangeHook != nil { + f.queueChangeHook(op.block.Hash(), true) + } + if glog.V(logger.Debug) { + glog.Infof("Peer %s: queued block #%d [%x…], total %v", peer, block.NumberU64(), hash.Bytes()[:4], f.queue.Size()) + } + } +} + +// insert spawns a new goroutine to run a block insertion into the chain. If the +// block's number is at the same height as the current import phase, if updates +// the phase states accordingly. +func (f *Fetcher) insert(peer string, block *types.Block) { + hash := block.Hash() + + // Run the import on a new thread + glog.V(logger.Debug).Infof("Peer %s: importing block #%d [%x…]", peer, block.NumberU64(), hash[:4]) + go func() { + defer func() { f.done <- hash }() + + // If the parent's unknown, abort insertion + parent := f.getBlock(block.ParentHash()) + if parent == nil { + glog.V(logger.Debug).Infof("Peer %s: parent [%x…] of block #%d [%x…] unknown", peer, block.ParentHash().Bytes()[:4], block.NumberU64(), hash[:4]) + return + } + // Quickly validate the header and propagate the block if it passes + switch err := f.validateBlock(block, parent); err { + case nil: + // All ok, quickly propagate to our peers + propBroadcastOutTimer.UpdateSince(block.ReceivedAt) + go f.broadcastBlock(block, true) + + case core.BlockFutureErr: + // Weird future block, don't fail, but neither propagate + + default: + // Something went very wrong, drop the peer + glog.V(logger.Debug).Infof("Peer %s: block #%d [%x…] verification failed: %v", peer, block.NumberU64(), hash[:4], err) + f.dropPeer(peer) + return + } + // Run the actual import and log any issues + if _, err := f.insertChain(types.Blocks{block}); err != nil { + glog.V(logger.Warn).Infof("Peer %s: block #%d [%x…] import failed: %v", peer, block.NumberU64(), hash[:4], err) + return + } + // If import succeeded, broadcast the block + propAnnounceOutTimer.UpdateSince(block.ReceivedAt) + go f.broadcastBlock(block, false) + + // Invoke the testing hook if needed + if f.importedHook != nil { + f.importedHook(block) + } + }() +} + +// forgetHash removes all traces of a block announcement from the fetcher's +// internal state. +func (f *Fetcher) forgetHash(hash common.Hash) { + // Remove all pending announces and decrement DOS counters + for _, announce := range f.announced[hash] { + f.announces[announce.origin]-- + if f.announces[announce.origin] == 0 { + delete(f.announces, announce.origin) + } + } + delete(f.announced, hash) + if f.announceChangeHook != nil { + f.announceChangeHook(hash, false) + } + // Remove any pending fetches and decrement the DOS counters + if announce := f.fetching[hash]; announce != nil { + f.announces[announce.origin]-- + if f.announces[announce.origin] == 0 { + delete(f.announces, announce.origin) + } + delete(f.fetching, hash) + } + + // Remove any pending completion requests and decrement the DOS counters + for _, announce := range f.fetched[hash] { + f.announces[announce.origin]-- + if f.announces[announce.origin] == 0 { + delete(f.announces, announce.origin) + } + } + delete(f.fetched, hash) + + // Remove any pending completions and decrement the DOS counters + if announce := f.completing[hash]; announce != nil { + f.announces[announce.origin]-- + if f.announces[announce.origin] == 0 { + delete(f.announces, announce.origin) + } + delete(f.completing, hash) + } +} + +// forgetBlock removes all traces of a queued block from the fetcher's internal +// state. +func (f *Fetcher) forgetBlock(hash common.Hash) { + if insert := f.queued[hash]; insert != nil { + f.queues[insert.origin]-- + if f.queues[insert.origin] == 0 { + delete(f.queues, insert.origin) + } + delete(f.queued, hash) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher_test.go b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher_test.go new file mode 100644 index 0000000..7a94241 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/fetcher_test.go @@ -0,0 +1,780 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package fetcher + +import ( + "errors" + "math/big" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +var ( + testdb, _ = ethdb.NewMemDatabase() + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddress = crypto.PubkeyToAddress(testKey.PublicKey) + genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) + unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil) +) + +// makeChain creates a chain of n blocks starting at and including parent. +// the returned hash chain is ordered head->parent. In addition, every 3rd block +// contains a transaction and every 5th an uncle to allow testing correct block +// reassembly. +func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { + blocks, _ := core.GenerateChain(params.TestChainConfig, parent, testdb, n, func(i int, block *core.BlockGen) { + block.SetCoinbase(common.Address{seed}) + + // If the block number is multiple of 3, send a bonus transaction to the miner + if parent == genesis && i%3 == 0 { + signer := types.MakeSigner(params.TestChainConfig, block.Number()) + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey) + if err != nil { + panic(err) + } + block.AddTx(tx) + } + // If the block number is a multiple of 5, add a bonus uncle to the block + if i%5 == 0 { + block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))}) + } + }) + hashes := make([]common.Hash, n+1) + hashes[len(hashes)-1] = parent.Hash() + blockm := make(map[common.Hash]*types.Block, n+1) + blockm[parent.Hash()] = parent + for i, b := range blocks { + hashes[len(hashes)-i-2] = b.Hash() + blockm[b.Hash()] = b + } + return hashes, blockm +} + +// fetcherTester is a test simulator for mocking out local block chain. +type fetcherTester struct { + fetcher *Fetcher + + hashes []common.Hash // Hash chain belonging to the tester + blocks map[common.Hash]*types.Block // Blocks belonging to the tester + drops map[string]bool // Map of peers dropped by the fetcher + + lock sync.RWMutex +} + +// newTester creates a new fetcher test mocker. +func newTester() *fetcherTester { + tester := &fetcherTester{ + hashes: []common.Hash{genesis.Hash()}, + blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, + drops: make(map[string]bool), + } + tester.fetcher = New(tester.getBlock, tester.verifyBlock, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) + tester.fetcher.Start() + + return tester +} + +// getBlock retrieves a block from the tester's block chain. +func (f *fetcherTester) getBlock(hash common.Hash) *types.Block { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.blocks[hash] +} + +// verifyBlock is a nop placeholder for the block header verification. +func (f *fetcherTester) verifyBlock(block *types.Block, parent *types.Block) error { + return nil +} + +// broadcastBlock is a nop placeholder for the block broadcasting. +func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) { +} + +// chainHeight retrieves the current height (block number) of the chain. +func (f *fetcherTester) chainHeight() uint64 { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() +} + +// insertChain injects a new blocks into the simulated chain. +func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) { + f.lock.Lock() + defer f.lock.Unlock() + + for i, block := range blocks { + // Make sure the parent in known + if _, ok := f.blocks[block.ParentHash()]; !ok { + return i, errors.New("unknown parent") + } + // Discard any new blocks if the same height already exists + if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() { + return i, nil + } + // Otherwise build our current chain + f.hashes = append(f.hashes, block.Hash()) + f.blocks[block.Hash()] = block + } + return 0, nil +} + +// dropPeer is an emulator for the peer removal, simply accumulating the various +// peers dropped by the fetcher. +func (f *fetcherTester) dropPeer(peer string) { + f.lock.Lock() + defer f.lock.Unlock() + + f.drops[peer] = true +} + +// makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer. +func (f *fetcherTester) makeHeaderFetcher(blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn { + closure := make(map[common.Hash]*types.Block) + for hash, block := range blocks { + closure[hash] = block + } + // Create a function that return a header from the closure + return func(hash common.Hash) error { + // Gather the blocks to return + headers := make([]*types.Header, 0, 1) + if block, ok := closure[hash]; ok { + headers = append(headers, block.Header()) + } + // Return on a new thread + go f.fetcher.FilterHeaders(headers, time.Now().Add(drift)) + + return nil + } +} + +// makeBodyFetcher retrieves a block body fetcher associated with a simulated peer. +func (f *fetcherTester) makeBodyFetcher(blocks map[common.Hash]*types.Block, drift time.Duration) bodyRequesterFn { + closure := make(map[common.Hash]*types.Block) + for hash, block := range blocks { + closure[hash] = block + } + // Create a function that returns blocks from the closure + return func(hashes []common.Hash) error { + // Gather the block bodies to return + transactions := make([][]*types.Transaction, 0, len(hashes)) + uncles := make([][]*types.Header, 0, len(hashes)) + + for _, hash := range hashes { + if block, ok := closure[hash]; ok { + transactions = append(transactions, block.Transactions()) + uncles = append(uncles, block.Uncles()) + } + } + // Return on a new thread + go f.fetcher.FilterBodies(transactions, uncles, time.Now().Add(drift)) + + return nil + } +} + +// verifyFetchingEvent verifies that one single event arrive on an fetching channel. +func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) { + if arrive { + select { + case <-fetching: + case <-time.After(time.Second): + t.Fatalf("fetching timeout") + } + } else { + select { + case <-fetching: + t.Fatalf("fetching invoked") + case <-time.After(10 * time.Millisecond): + } + } +} + +// verifyCompletingEvent verifies that one single event arrive on an completing channel. +func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) { + if arrive { + select { + case <-completing: + case <-time.After(time.Second): + t.Fatalf("completing timeout") + } + } else { + select { + case <-completing: + t.Fatalf("completing invoked") + case <-time.After(10 * time.Millisecond): + } + } +} + +// verifyImportEvent verifies that one single event arrive on an import channel. +func verifyImportEvent(t *testing.T, imported chan *types.Block, arrive bool) { + if arrive { + select { + case <-imported: + case <-time.After(time.Second): + t.Fatalf("import timeout") + } + } else { + select { + case <-imported: + t.Fatalf("import invoked") + case <-time.After(10 * time.Millisecond): + } + } +} + +// verifyImportCount verifies that exactly count number of events arrive on an +// import hook channel. +func verifyImportCount(t *testing.T, imported chan *types.Block, count int) { + for i := 0; i < count; i++ { + select { + case <-imported: + case <-time.After(time.Second): + t.Fatalf("block %d: import timeout", i+1) + } + } + verifyImportDone(t, imported) +} + +// verifyImportDone verifies that no more events are arriving on an import channel. +func verifyImportDone(t *testing.T, imported chan *types.Block) { + select { + case <-imported: + t.Fatalf("extra block imported") + case <-time.After(50 * time.Millisecond): + } +} + +// Tests that a fetcher accepts block announcements and initiates retrievals for +// them, successfully importing into the local chain. +func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) } +func TestSequentialAnnouncements63(t *testing.T) { testSequentialAnnouncements(t, 63) } +func TestSequentialAnnouncements64(t *testing.T) { testSequentialAnnouncements(t, 64) } + +func testSequentialAnnouncements(t *testing.T, protocol int) { + // Create a chain of blocks to import + targetBlocks := 4 * hashLimit + hashes, blocks := makeChain(targetBlocks, 0, genesis) + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + // Iteratively announce blocks until all are imported + imported := make(chan *types.Block) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + for i := len(hashes) - 2; i >= 0; i-- { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + verifyImportEvent(t, imported, true) + } + verifyImportDone(t, imported) +} + +// Tests that if blocks are announced by multiple peers (or even the same buggy +// peer), they will only get downloaded at most once. +func TestConcurrentAnnouncements62(t *testing.T) { testConcurrentAnnouncements(t, 62) } +func TestConcurrentAnnouncements63(t *testing.T) { testConcurrentAnnouncements(t, 63) } +func TestConcurrentAnnouncements64(t *testing.T) { testConcurrentAnnouncements(t, 64) } + +func testConcurrentAnnouncements(t *testing.T, protocol int) { + // Create a chain of blocks to import + targetBlocks := 4 * hashLimit + hashes, blocks := makeChain(targetBlocks, 0, genesis) + + // Assemble a tester with a built in counter for the requests + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + counter := uint32(0) + headerWrapper := func(hash common.Hash) error { + atomic.AddUint32(&counter, 1) + return headerFetcher(hash) + } + // Iteratively announce blocks until all are imported + imported := make(chan *types.Block) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + for i := len(hashes) - 2; i >= 0; i-- { + tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher) + tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), headerWrapper, bodyFetcher) + tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), headerWrapper, bodyFetcher) + verifyImportEvent(t, imported, true) + } + verifyImportDone(t, imported) + + // Make sure no blocks were retrieved twice + if int(counter) != targetBlocks { + t.Fatalf("retrieval count mismatch: have %v, want %v", counter, targetBlocks) + } +} + +// Tests that announcements arriving while a previous is being fetched still +// results in a valid import. +func TestOverlappingAnnouncements62(t *testing.T) { testOverlappingAnnouncements(t, 62) } +func TestOverlappingAnnouncements63(t *testing.T) { testOverlappingAnnouncements(t, 63) } +func TestOverlappingAnnouncements64(t *testing.T) { testOverlappingAnnouncements(t, 64) } + +func testOverlappingAnnouncements(t *testing.T, protocol int) { + // Create a chain of blocks to import + targetBlocks := 4 * hashLimit + hashes, blocks := makeChain(targetBlocks, 0, genesis) + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + // Iteratively announce blocks, but overlap them continuously + overlap := 16 + imported := make(chan *types.Block, len(hashes)-1) + for i := 0; i < overlap; i++ { + imported <- nil + } + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + for i := len(hashes) - 2; i >= 0; i-- { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + select { + case <-imported: + case <-time.After(time.Second): + t.Fatalf("block %d: import timeout", len(hashes)-i) + } + } + // Wait for all the imports to complete and check count + verifyImportCount(t, imported, overlap) +} + +// Tests that announces already being retrieved will not be duplicated. +func TestPendingDeduplication62(t *testing.T) { testPendingDeduplication(t, 62) } +func TestPendingDeduplication63(t *testing.T) { testPendingDeduplication(t, 63) } +func TestPendingDeduplication64(t *testing.T) { testPendingDeduplication(t, 64) } + +func testPendingDeduplication(t *testing.T, protocol int) { + // Create a hash and corresponding block + hashes, blocks := makeChain(1, 0, genesis) + + // Assemble a tester with a built in counter and delayed fetcher + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + delay := 50 * time.Millisecond + counter := uint32(0) + headerWrapper := func(hash common.Hash) error { + atomic.AddUint32(&counter, 1) + + // Simulate a long running fetch + go func() { + time.Sleep(delay) + headerFetcher(hash) + }() + return nil + } + // Announce the same block many times until it's fetched (wait for any pending ops) + for tester.getBlock(hashes[0]) == nil { + tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher) + time.Sleep(time.Millisecond) + } + time.Sleep(delay) + + // Check that all blocks were imported and none fetched twice + if imported := len(tester.blocks); imported != 2 { + t.Fatalf("synchronised block mismatch: have %v, want %v", imported, 2) + } + if int(counter) != 1 { + t.Fatalf("retrieval count mismatch: have %v, want %v", counter, 1) + } +} + +// Tests that announcements retrieved in a random order are cached and eventually +// imported when all the gaps are filled in. +func TestRandomArrivalImport62(t *testing.T) { testRandomArrivalImport(t, 62) } +func TestRandomArrivalImport63(t *testing.T) { testRandomArrivalImport(t, 63) } +func TestRandomArrivalImport64(t *testing.T) { testRandomArrivalImport(t, 64) } + +func testRandomArrivalImport(t *testing.T, protocol int) { + // Create a chain of blocks to import, and choose one to delay + targetBlocks := maxQueueDist + hashes, blocks := makeChain(targetBlocks, 0, genesis) + skip := targetBlocks / 2 + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + // Iteratively announce blocks, skipping one entry + imported := make(chan *types.Block, len(hashes)-1) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + for i := len(hashes) - 1; i >= 0; i-- { + if i != skip { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + time.Sleep(time.Millisecond) + } + } + // Finally announce the skipped entry and check full import + tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + verifyImportCount(t, imported, len(hashes)-1) +} + +// Tests that direct block enqueues (due to block propagation vs. hash announce) +// are correctly schedule, filling and import queue gaps. +func TestQueueGapFill62(t *testing.T) { testQueueGapFill(t, 62) } +func TestQueueGapFill63(t *testing.T) { testQueueGapFill(t, 63) } +func TestQueueGapFill64(t *testing.T) { testQueueGapFill(t, 64) } + +func testQueueGapFill(t *testing.T, protocol int) { + // Create a chain of blocks to import, and choose one to not announce at all + targetBlocks := maxQueueDist + hashes, blocks := makeChain(targetBlocks, 0, genesis) + skip := targetBlocks / 2 + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + // Iteratively announce blocks, skipping one entry + imported := make(chan *types.Block, len(hashes)-1) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + for i := len(hashes) - 1; i >= 0; i-- { + if i != skip { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + time.Sleep(time.Millisecond) + } + } + // Fill the missing block directly as if propagated + tester.fetcher.Enqueue("valid", blocks[hashes[skip]]) + verifyImportCount(t, imported, len(hashes)-1) +} + +// Tests that blocks arriving from various sources (multiple propagations, hash +// announces, etc) do not get scheduled for import multiple times. +func TestImportDeduplication62(t *testing.T) { testImportDeduplication(t, 62) } +func TestImportDeduplication63(t *testing.T) { testImportDeduplication(t, 63) } +func TestImportDeduplication64(t *testing.T) { testImportDeduplication(t, 64) } + +func testImportDeduplication(t *testing.T, protocol int) { + // Create two blocks to import (one for duplication, the other for stalling) + hashes, blocks := makeChain(2, 0, genesis) + + // Create the tester and wrap the importer with a counter + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + counter := uint32(0) + tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) { + atomic.AddUint32(&counter, uint32(len(blocks))) + return tester.insertChain(blocks) + } + // Instrument the fetching and imported events + fetching := make(chan []common.Hash) + imported := make(chan *types.Block, len(hashes)-1) + tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + // Announce the duplicating block, wait for retrieval, and also propagate directly + tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + <-fetching + + tester.fetcher.Enqueue("valid", blocks[hashes[0]]) + tester.fetcher.Enqueue("valid", blocks[hashes[0]]) + tester.fetcher.Enqueue("valid", blocks[hashes[0]]) + + // Fill the missing block directly as if propagated, and check import uniqueness + tester.fetcher.Enqueue("valid", blocks[hashes[1]]) + verifyImportCount(t, imported, 2) + + if counter != 2 { + t.Fatalf("import invocation count mismatch: have %v, want %v", counter, 2) + } +} + +// Tests that blocks with numbers much lower or higher than out current head get +// discarded to prevent wasting resources on useless blocks from faulty peers. +func TestDistantPropagationDiscarding(t *testing.T) { + // Create a long chain to import and define the discard boundaries + hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) + head := hashes[len(hashes)/2] + + low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 + + // Create a tester and simulate a head block being the middle of the above chain + tester := newTester() + + tester.lock.Lock() + tester.hashes = []common.Hash{head} + tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} + tester.lock.Unlock() + + // Ensure that a block with a lower number than the threshold is discarded + tester.fetcher.Enqueue("lower", blocks[hashes[low]]) + time.Sleep(10 * time.Millisecond) + if !tester.fetcher.queue.Empty() { + t.Fatalf("fetcher queued stale block") + } + // Ensure that a block with a higher number than the threshold is discarded + tester.fetcher.Enqueue("higher", blocks[hashes[high]]) + time.Sleep(10 * time.Millisecond) + if !tester.fetcher.queue.Empty() { + t.Fatalf("fetcher queued future block") + } +} + +// Tests that announcements with numbers much lower or higher than out current +// head get discarded to prevent wasting resources on useless blocks from faulty +// peers. +func TestDistantAnnouncementDiscarding62(t *testing.T) { testDistantAnnouncementDiscarding(t, 62) } +func TestDistantAnnouncementDiscarding63(t *testing.T) { testDistantAnnouncementDiscarding(t, 63) } +func TestDistantAnnouncementDiscarding64(t *testing.T) { testDistantAnnouncementDiscarding(t, 64) } + +func testDistantAnnouncementDiscarding(t *testing.T, protocol int) { + // Create a long chain to import and define the discard boundaries + hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) + head := hashes[len(hashes)/2] + + low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 + + // Create a tester and simulate a head block being the middle of the above chain + tester := newTester() + + tester.lock.Lock() + tester.hashes = []common.Hash{head} + tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} + tester.lock.Unlock() + + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + fetching := make(chan struct{}, 2) + tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} } + + // Ensure that a block with a lower number than the threshold is discarded + tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + select { + case <-time.After(50 * time.Millisecond): + case <-fetching: + t.Fatalf("fetcher requested stale header") + } + // Ensure that a block with a higher number than the threshold is discarded + tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + select { + case <-time.After(50 * time.Millisecond): + case <-fetching: + t.Fatalf("fetcher requested future header") + } +} + +// Tests that peers announcing blocks with invalid numbers (i.e. not matching +// the headers provided afterwards) get dropped as malicious. +func TestInvalidNumberAnnouncement62(t *testing.T) { testInvalidNumberAnnouncement(t, 62) } +func TestInvalidNumberAnnouncement63(t *testing.T) { testInvalidNumberAnnouncement(t, 63) } +func TestInvalidNumberAnnouncement64(t *testing.T) { testInvalidNumberAnnouncement(t, 64) } + +func testInvalidNumberAnnouncement(t *testing.T, protocol int) { + // Create a single block to import and check numbers against + hashes, blocks := makeChain(1, 0, genesis) + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + imported := make(chan *types.Block) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + // Announce a block with a bad number, check for immediate drop + tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + verifyImportEvent(t, imported, false) + + tester.lock.RLock() + dropped := tester.drops["bad"] + tester.lock.RUnlock() + + if !dropped { + t.Fatalf("peer with invalid numbered announcement not dropped") + } + // Make sure a good announcement passes without a drop + tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + verifyImportEvent(t, imported, true) + + tester.lock.RLock() + dropped = tester.drops["good"] + tester.lock.RUnlock() + + if dropped { + t.Fatalf("peer with valid numbered announcement dropped") + } + verifyImportDone(t, imported) +} + +// Tests that if a block is empty (i.e. header only), no body request should be +// made, and instead the header should be assembled into a whole block in itself. +func TestEmptyBlockShortCircuit62(t *testing.T) { testEmptyBlockShortCircuit(t, 62) } +func TestEmptyBlockShortCircuit63(t *testing.T) { testEmptyBlockShortCircuit(t, 63) } +func TestEmptyBlockShortCircuit64(t *testing.T) { testEmptyBlockShortCircuit(t, 64) } + +func testEmptyBlockShortCircuit(t *testing.T, protocol int) { + // Create a chain of blocks to import + hashes, blocks := makeChain(32, 0, genesis) + + tester := newTester() + headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + bodyFetcher := tester.makeBodyFetcher(blocks, 0) + + // Add a monitoring hook for all internal events + fetching := make(chan []common.Hash) + tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } + + completing := make(chan []common.Hash) + tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes } + + imported := make(chan *types.Block) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + + // Iteratively announce blocks until all are imported + for i := len(hashes) - 2; i >= 0; i-- { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) + + // All announces should fetch the header + verifyFetchingEvent(t, fetching, true) + + // Only blocks with data contents should request bodies + verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0) + + // Irrelevant of the construct, import should succeed + verifyImportEvent(t, imported, true) + } + verifyImportDone(t, imported) +} + +// Tests that a peer is unable to use unbounded memory with sending infinite +// block announcements to a node, but that even in the face of such an attack, +// the fetcher remains operational. +func TestHashMemoryExhaustionAttack62(t *testing.T) { testHashMemoryExhaustionAttack(t, 62) } +func TestHashMemoryExhaustionAttack63(t *testing.T) { testHashMemoryExhaustionAttack(t, 63) } +func TestHashMemoryExhaustionAttack64(t *testing.T) { testHashMemoryExhaustionAttack(t, 64) } + +func testHashMemoryExhaustionAttack(t *testing.T, protocol int) { + // Create a tester with instrumented import hooks + tester := newTester() + + imported, announces := make(chan *types.Block), int32(0) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) { + if added { + atomic.AddInt32(&announces, 1) + } else { + atomic.AddInt32(&announces, -1) + } + } + // Create a valid chain and an infinite junk chain + targetBlocks := hashLimit + 2*maxQueueDist + hashes, blocks := makeChain(targetBlocks, 0, genesis) + validHeaderFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack) + validBodyFetcher := tester.makeBodyFetcher(blocks, 0) + + attack, _ := makeChain(targetBlocks, 0, unknownBlock) + attackerHeaderFetcher := tester.makeHeaderFetcher(nil, -gatherSlack) + attackerBodyFetcher := tester.makeBodyFetcher(nil, 0) + + // Feed the tester a huge hashset from the attacker, and a limited from the valid peer + for i := 0; i < len(attack); i++ { + if i < maxQueueDist { + tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher) + } + tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher) + } + if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist { + t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist) + } + // Wait for fetches to complete + verifyImportCount(t, imported, maxQueueDist) + + // Feed the remaining valid hashes to ensure DOS protection state remains clean + for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- { + tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher) + verifyImportEvent(t, imported, true) + } + verifyImportDone(t, imported) +} + +// Tests that blocks sent to the fetcher (either through propagation or via hash +// announces and retrievals) don't pile up indefinitely, exhausting available +// system memory. +func TestBlockMemoryExhaustionAttack(t *testing.T) { + // Create a tester with instrumented import hooks + tester := newTester() + + imported, enqueued := make(chan *types.Block), int32(0) + tester.fetcher.importedHook = func(block *types.Block) { imported <- block } + tester.fetcher.queueChangeHook = func(hash common.Hash, added bool) { + if added { + atomic.AddInt32(&enqueued, 1) + } else { + atomic.AddInt32(&enqueued, -1) + } + } + // Create a valid chain and a batch of dangling (but in range) blocks + targetBlocks := hashLimit + 2*maxQueueDist + hashes, blocks := makeChain(targetBlocks, 0, genesis) + attack := make(map[common.Hash]*types.Block) + for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ { + hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock) + for _, hash := range hashes[:maxQueueDist-2] { + attack[hash] = blocks[hash] + } + } + // Try to feed all the attacker blocks make sure only a limited batch is accepted + for _, block := range attack { + tester.fetcher.Enqueue("attacker", block) + } + time.Sleep(200 * time.Millisecond) + if queued := atomic.LoadInt32(&enqueued); queued != blockLimit { + t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit) + } + // Queue up a batch of valid blocks, and check that a new peer is allowed to do so + for i := 0; i < maxQueueDist-1; i++ { + tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-3-i]]) + } + time.Sleep(100 * time.Millisecond) + if queued := atomic.LoadInt32(&enqueued); queued != blockLimit+maxQueueDist-1 { + t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit+maxQueueDist-1) + } + // Insert the missing piece (and sanity check the import) + tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2]]) + verifyImportCount(t, imported, maxQueueDist) + + // Insert the remaining blocks in chunks to ensure clean DOS protection + for i := maxQueueDist; i < len(hashes)-1; i++ { + tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2-i]]) + verifyImportEvent(t, imported, true) + } + verifyImportDone(t, imported) +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/fetcher/metrics.go b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/metrics.go new file mode 100644 index 0000000..1ed8075 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/fetcher/metrics.go @@ -0,0 +1,43 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the metrics collected by the fetcher. + +package fetcher + +import ( + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + propAnnounceInMeter = metrics.NewMeter("eth/fetcher/prop/announces/in") + propAnnounceOutTimer = metrics.NewTimer("eth/fetcher/prop/announces/out") + propAnnounceDropMeter = metrics.NewMeter("eth/fetcher/prop/announces/drop") + propAnnounceDOSMeter = metrics.NewMeter("eth/fetcher/prop/announces/dos") + + propBroadcastInMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/in") + propBroadcastOutTimer = metrics.NewTimer("eth/fetcher/prop/broadcasts/out") + propBroadcastDropMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/drop") + propBroadcastDOSMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/dos") + + headerFetchMeter = metrics.NewMeter("eth/fetcher/fetch/headers") + bodyFetchMeter = metrics.NewMeter("eth/fetcher/fetch/bodies") + + headerFilterInMeter = metrics.NewMeter("eth/fetcher/filter/headers/in") + headerFilterOutMeter = metrics.NewMeter("eth/fetcher/filter/headers/out") + bodyFilterInMeter = metrics.NewMeter("eth/fetcher/filter/bodies/in") + bodyFilterOutMeter = metrics.NewMeter("eth/fetcher/filter/bodies/out") +) diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go new file mode 100644 index 0000000..02a544c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/api.go @@ -0,0 +1,555 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filters + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "sync" + "time" + + "golang.org/x/net/context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline +) + +// filter is a helper struct that holds meta information over the filter type +// and associated subscription in the event system. +type filter struct { + typ Type + deadline *time.Timer // filter is inactiv when deadline triggers + hashes []common.Hash + crit FilterCriteria + logs []*types.Log + s *Subscription // associated subscription in event system +} + +// PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various +// information related to the Ethereum protocol such als blocks, transactions and logs. +type PublicFilterAPI struct { + backend Backend + useMipMap bool + mux *event.TypeMux + quit chan struct{} + chainDb ethdb.Database + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter +} + +// NewPublicFilterAPI returns a new PublicFilterAPI instance. +func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { + api := &PublicFilterAPI{ + backend: backend, + useMipMap: !lightMode, + mux: backend.EventMux(), + chainDb: backend.ChainDb(), + events: NewEventSystem(backend.EventMux(), backend, lightMode), + filters: make(map[rpc.ID]*filter), + } + + go api.timeoutLoop() + + return api +} + +// timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. +// Tt is started when the api is created. +func (api *PublicFilterAPI) timeoutLoop() { + ticker := time.NewTicker(5 * time.Minute) + for { + <-ticker.C + api.filtersMu.Lock() + for id, f := range api.filters { + select { + case <-f.deadline.C: + f.s.Unsubscribe() + delete(api.filters, id) + default: + continue + } + } + api.filtersMu.Unlock() + } +} + +// NewPendingTransactionFilter creates a filter that fetches pending transaction hashes +// as transactions enter the pending state. +// +// It is part of the filter package because this filter can be used throug the +// `eth_getFilterChanges` polling method that is also used for log filters. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter +func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { + var ( + pendingTxs = make(chan common.Hash) + pendingTxSub = api.events.SubscribePendingTxEvents(pendingTxs) + ) + + api.filtersMu.Lock() + api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} + api.filtersMu.Unlock() + + go func() { + for { + select { + case ph := <-pendingTxs: + api.filtersMu.Lock() + if f, found := api.filters[pendingTxSub.ID]; found { + f.hashes = append(f.hashes, ph) + } + api.filtersMu.Unlock() + case <-pendingTxSub.Err(): + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID) + api.filtersMu.Unlock() + return + } + } + }() + + return pendingTxSub.ID +} + +// NewPendingTransactions creates a subscription that is triggered each time a transaction +// enters the transaction pool and was signed from one of the transactions this nodes manages. +func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + go func() { + txHashes := make(chan common.Hash) + pendingTxSub := api.events.SubscribePendingTxEvents(txHashes) + + for { + select { + case h := <-txHashes: + notifier.Notify(rpcSub.ID, h) + case <-rpcSub.Err(): + pendingTxSub.Unsubscribe() + return + case <-notifier.Closed(): + pendingTxSub.Unsubscribe() + return + } + } + }() + + return rpcSub, nil +} + +// NewBlockFilter creates a filter that fetches blocks that are imported into the chain. +// It is part of the filter package since polling goes with eth_getFilterChanges. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter +func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { + var ( + headers = make(chan *types.Header) + headerSub = api.events.SubscribeNewHeads(headers) + ) + + api.filtersMu.Lock() + api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: headerSub} + api.filtersMu.Unlock() + + go func() { + for { + select { + case h := <-headers: + api.filtersMu.Lock() + if f, found := api.filters[headerSub.ID]; found { + f.hashes = append(f.hashes, h.Hash()) + } + api.filtersMu.Unlock() + case <-headerSub.Err(): + api.filtersMu.Lock() + delete(api.filters, headerSub.ID) + api.filtersMu.Unlock() + return + } + } + }() + + return headerSub.ID +} + +// NewHeads send a notification each time a new (header) block is appended to the chain. +func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + go func() { + headers := make(chan *types.Header) + headersSub := api.events.SubscribeNewHeads(headers) + + for { + select { + case h := <-headers: + notifier.Notify(rpcSub.ID, h) + case <-rpcSub.Err(): + headersSub.Unsubscribe() + return + case <-notifier.Closed(): + headersSub.Unsubscribe() + return + } + } + }() + + return rpcSub, nil +} + +// Logs creates a subscription that fires for all new log that match the given filter criteria. +func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + var ( + rpcSub = notifier.CreateSubscription() + matchedLogs = make(chan []*types.Log) + ) + + logsSub, err := api.events.SubscribeLogs(crit, matchedLogs) + if err != nil { + return nil, err + } + + go func() { + + for { + select { + case logs := <-matchedLogs: + for _, log := range logs { + notifier.Notify(rpcSub.ID, &log) + } + case <-rpcSub.Err(): // client send an unsubscribe request + logsSub.Unsubscribe() + return + case <-notifier.Closed(): // connection dropped + logsSub.Unsubscribe() + return + } + } + }() + + return rpcSub, nil +} + +// FilterCriteria represents a request to create a new filter. +type FilterCriteria struct { + FromBlock *big.Int + ToBlock *big.Int + Addresses []common.Address + Topics [][]common.Hash +} + +// NewFilter creates a new filter and returns the filter id. It can be +// used to retrieve logs when the state changes. This method cannot be +// used to fetch logs that are already stored in the state. +// +// Default criteria for the from and to block are "latest". +// Using "latest" as block number will return logs for mined blocks. +// Using "pending" as block number returns logs for not yet mined (pending) blocks. +// In case logs are removed (chain reorg) previously returned logs are returned +// again but with the removed property set to true. +// +// In case "fromBlock" > "toBlock" an error is returned. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter +func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { + logs := make(chan []*types.Log) + logsSub, err := api.events.SubscribeLogs(crit, logs) + if err != nil { + return rpc.ID(""), err + } + + api.filtersMu.Lock() + api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub} + api.filtersMu.Unlock() + + go func() { + for { + select { + case l := <-logs: + api.filtersMu.Lock() + if f, found := api.filters[logsSub.ID]; found { + f.logs = append(f.logs, l...) + } + api.filtersMu.Unlock() + case <-logsSub.Err(): + api.filtersMu.Lock() + delete(api.filters, logsSub.ID) + api.filtersMu.Unlock() + return + } + } + }() + + return logsSub.ID, nil +} + +// GetLogs returns logs matching the given argument that are stored within the state. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { + if crit.FromBlock == nil { + crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) + } + if crit.ToBlock == nil { + crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) + } + + filter := New(api.backend, api.useMipMap) + filter.SetBeginBlock(crit.FromBlock.Int64()) + filter.SetEndBlock(crit.ToBlock.Int64()) + filter.SetAddresses(crit.Addresses) + filter.SetTopics(crit.Topics) + + logs, err := filter.Find(ctx) + return returnLogs(logs), err +} + +// UninstallFilter removes the filter with the given filter id. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { + api.filtersMu.Lock() + f, found := api.filters[id] + if found { + delete(api.filters, id) + } + api.filtersMu.Unlock() + if found { + f.s.Unsubscribe() + } + + return found +} + +// GetFilterLogs returns the logs for the filter with the given id. +// If the filter could not be found an empty array of logs is returned. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) { + api.filtersMu.Lock() + f, found := api.filters[id] + api.filtersMu.Unlock() + + if !found || f.typ != LogsSubscription { + return nil, fmt.Errorf("filter not found") + } + + filter := New(api.backend, api.useMipMap) + if f.crit.FromBlock != nil { + filter.SetBeginBlock(f.crit.FromBlock.Int64()) + } else { + filter.SetBeginBlock(rpc.LatestBlockNumber.Int64()) + } + if f.crit.ToBlock != nil { + filter.SetEndBlock(f.crit.ToBlock.Int64()) + } else { + filter.SetEndBlock(rpc.LatestBlockNumber.Int64()) + } + filter.SetAddresses(f.crit.Addresses) + filter.SetTopics(f.crit.Topics) + + logs, err := filter.Find(ctx) + if err != nil { + return nil, err + } + return returnLogs(logs), nil +} + +// GetFilterChanges returns the logs for the filter with the given id since +// last time is was called. This can be used for polling. +// +// For pending transaction and block filters the result is []common.Hash. +// (pending)Log filters return []Log. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges +func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + if f, found := api.filters[id]; found { + if !f.deadline.Stop() { + // timer expired but filter is not yet removed in timeout loop + // receive timer value and reset timer + <-f.deadline.C + } + f.deadline.Reset(deadline) + + switch f.typ { + case PendingTransactionsSubscription, BlocksSubscription: + hashes := f.hashes + f.hashes = nil + return returnHashes(hashes), nil + case LogsSubscription: + logs := f.logs + f.logs = nil + return returnLogs(logs), nil + } + } + + return []interface{}{}, fmt.Errorf("filter not found") +} + +// returnHashes is a helper that will return an empty hash array case the given hash array is nil, +// otherwise the given hashes array is returned. +func returnHashes(hashes []common.Hash) []common.Hash { + if hashes == nil { + return []common.Hash{} + } + return hashes +} + +// returnLogs is a helper that will return an empty log array in case the given logs array is nil, +// otherwise the given logs array is returned. +func returnLogs(logs []*types.Log) []*types.Log { + if logs == nil { + return []*types.Log{} + } + return logs +} + +// UnmarshalJSON sets *args fields with given data. +func (args *FilterCriteria) UnmarshalJSON(data []byte) error { + type input struct { + From *rpc.BlockNumber `json:"fromBlock"` + ToBlock *rpc.BlockNumber `json:"toBlock"` + Addresses interface{} `json:"address"` + Topics []interface{} `json:"topics"` + } + + var raw input + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + if raw.From != nil { + args.FromBlock = big.NewInt(raw.From.Int64()) + } + + if raw.ToBlock != nil { + args.ToBlock = big.NewInt(raw.ToBlock.Int64()) + } + + args.Addresses = []common.Address{} + + if raw.Addresses != nil { + // raw.Address can contain a single address or an array of addresses + switch rawAddr := raw.Addresses.(type) { + case []interface{}: + for i, addr := range rawAddr { + if strAddr, ok := addr.(string); ok { + addr, err := decodeAddress(strAddr) + if err != nil { + return fmt.Errorf("invalid address at index %d: %v", i, err) + } + args.Addresses = append(args.Addresses, addr) + } else { + return fmt.Errorf("non-string address at index %d", i) + } + } + case string: + addr, err := decodeAddress(rawAddr) + if err != nil { + return fmt.Errorf("invalid address: %v", err) + } + args.Addresses = []common.Address{addr} + default: + return errors.New("invalid addresses in query") + } + } + + // topics is an array consisting of strings and/or arrays of strings. + // JSON null values are converted to common.Hash{} and ignored by the filter manager. + if len(raw.Topics) > 0 { + args.Topics = make([][]common.Hash, len(raw.Topics)) + for i, t := range raw.Topics { + switch topic := t.(type) { + case nil: + // ignore topic when matching logs + args.Topics[i] = []common.Hash{{}} + + case string: + // match specific topic + top, err := decodeTopic(topic) + if err != nil { + return err + } + args.Topics[i] = []common.Hash{top} + case []interface{}: + // or case e.g. [null, "topic0", "topic1"] + for _, rawTopic := range topic { + if rawTopic == nil { + args.Topics[i] = append(args.Topics[i], common.Hash{}) + } else if topic, ok := rawTopic.(string); ok { + parsed, err := decodeTopic(topic) + if err != nil { + return err + } + args.Topics[i] = append(args.Topics[i], parsed) + } else { + return fmt.Errorf("invalid topic(s)") + } + } + default: + return fmt.Errorf("invalid topic(s)") + } + } + } + + return nil +} + +func decodeAddress(s string) (common.Address, error) { + b, err := hexutil.Decode(s) + if err == nil && len(b) != common.AddressLength { + err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) + } + return common.BytesToAddress(b), err +} + +func decodeTopic(s string) (common.Hash, error) { + b, err := hexutil.Decode(s) + if err == nil && len(b) != common.HashLength { + err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) + } + return common.BytesToHash(b), err +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/api_test.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/api_test.go new file mode 100644 index 0000000..068a5ea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/api_test.go @@ -0,0 +1,197 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filters + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" +) + +func TestUnmarshalJSONNewFilterArgs(t *testing.T) { + var ( + fromBlock rpc.BlockNumber = 0x123435 + toBlock rpc.BlockNumber = 0xabcdef + address0 = common.StringToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d") + address1 = common.StringToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83") + topic0 = common.HexToHash("3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca") + topic1 = common.HexToHash("9084a792d2f8b16a62b882fd56f7860c07bf5fa91dd8a2ae7e809e5180fef0b3") + topic2 = common.HexToHash("6ccae1c4af4152f460ff510e573399795dfab5dcf1fa60d1f33ac8fdc1e480ce") + nullTopic = common.Hash{} + ) + + // default values + var test0 FilterCriteria + if err := json.Unmarshal([]byte("{}"), &test0); err != nil { + t.Fatal(err) + } + if test0.FromBlock != nil { + t.Fatalf("expected nil, got %d", test0.FromBlock) + } + if test0.ToBlock != nil { + t.Fatalf("expected nil, got %d", test0.ToBlock) + } + if len(test0.Addresses) != 0 { + t.Fatalf("expected 0 addresses, got %d", len(test0.Addresses)) + } + if len(test0.Topics) != 0 { + t.Fatalf("expected 0 topics, got %d topics", len(test0.Topics)) + } + + // from, to block number + var test1 FilterCriteria + vector := fmt.Sprintf(`{"fromBlock":"0x%x","toBlock":"0x%x"}`, fromBlock, toBlock) + if err := json.Unmarshal([]byte(vector), &test1); err != nil { + t.Fatal(err) + } + if test1.FromBlock.Int64() != fromBlock.Int64() { + t.Fatalf("expected FromBlock %d, got %d", fromBlock, test1.FromBlock) + } + if test1.ToBlock.Int64() != toBlock.Int64() { + t.Fatalf("expected ToBlock %d, got %d", toBlock, test1.ToBlock) + } + + // single address + var test2 FilterCriteria + vector = fmt.Sprintf(`{"address": "%s"}`, address0.Hex()) + if err := json.Unmarshal([]byte(vector), &test2); err != nil { + t.Fatal(err) + } + if len(test2.Addresses) != 1 { + t.Fatalf("expected 1 address, got %d address(es)", len(test2.Addresses)) + } + if test2.Addresses[0] != address0 { + t.Fatalf("expected address %x, got %x", address0, test2.Addresses[0]) + } + + // multiple address + var test3 FilterCriteria + vector = fmt.Sprintf(`{"address": ["%s", "%s"]}`, address0.Hex(), address1.Hex()) + if err := json.Unmarshal([]byte(vector), &test3); err != nil { + t.Fatal(err) + } + if len(test3.Addresses) != 2 { + t.Fatalf("expected 2 addresses, got %d address(es)", len(test3.Addresses)) + } + if test3.Addresses[0] != address0 { + t.Fatalf("expected address %x, got %x", address0, test3.Addresses[0]) + } + if test3.Addresses[1] != address1 { + t.Fatalf("expected address %x, got %x", address1, test3.Addresses[1]) + } + + // single topic + var test4 FilterCriteria + vector = fmt.Sprintf(`{"topics": ["%s"]}`, topic0.Hex()) + if err := json.Unmarshal([]byte(vector), &test4); err != nil { + t.Fatal(err) + } + if len(test4.Topics) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test4.Topics)) + } + if len(test4.Topics[0]) != 1 { + t.Fatalf("expected len(topics[0]) to be 1, got %d", len(test4.Topics[0])) + } + if test4.Topics[0][0] != topic0 { + t.Fatalf("got %x, expected %x", test4.Topics[0][0], topic0) + } + + // test multiple "AND" topics + var test5 FilterCriteria + vector = fmt.Sprintf(`{"topics": ["%s", "%s"]}`, topic0.Hex(), topic1.Hex()) + if err := json.Unmarshal([]byte(vector), &test5); err != nil { + t.Fatal(err) + } + if len(test5.Topics) != 2 { + t.Fatalf("expected 2 topics, got %d", len(test5.Topics)) + } + if len(test5.Topics[0]) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test5.Topics[0])) + } + if test5.Topics[0][0] != topic0 { + t.Fatalf("got %x, expected %x", test5.Topics[0][0], topic0) + } + if len(test5.Topics[1]) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test5.Topics[1])) + } + if test5.Topics[1][0] != topic1 { + t.Fatalf("got %x, expected %x", test5.Topics[1][0], topic1) + } + + // test optional topic + var test6 FilterCriteria + vector = fmt.Sprintf(`{"topics": ["%s", null, "%s"]}`, topic0.Hex(), topic2.Hex()) + if err := json.Unmarshal([]byte(vector), &test6); err != nil { + t.Fatal(err) + } + if len(test6.Topics) != 3 { + t.Fatalf("expected 3 topics, got %d", len(test6.Topics)) + } + if len(test6.Topics[0]) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test6.Topics[0])) + } + if test6.Topics[0][0] != topic0 { + t.Fatalf("got %x, expected %x", test6.Topics[0][0], topic0) + } + if len(test6.Topics[1]) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test6.Topics[1])) + } + if test6.Topics[1][0] != nullTopic { + t.Fatalf("got %x, expected empty hash", test6.Topics[1][0]) + } + if len(test6.Topics[2]) != 1 { + t.Fatalf("expected 1 topic, got %d", len(test6.Topics[2])) + } + if test6.Topics[2][0] != topic2 { + t.Fatalf("got %x, expected %x", test6.Topics[2][0], topic2) + } + + // test OR topics + var test7 FilterCriteria + vector = fmt.Sprintf(`{"topics": [["%s", "%s"], null, ["%s", null]]}`, topic0.Hex(), topic1.Hex(), topic2.Hex()) + if err := json.Unmarshal([]byte(vector), &test7); err != nil { + t.Fatal(err) + } + if len(test7.Topics) != 3 { + t.Fatalf("expected 3 topics, got %d topics", len(test7.Topics)) + } + if len(test7.Topics[0]) != 2 { + t.Fatalf("expected 2 topics, got %d topics", len(test7.Topics[0])) + } + if test7.Topics[0][0] != topic0 || test7.Topics[0][1] != topic1 { + t.Fatalf("invalid topics expected [%x,%x], got [%x,%x]", + topic0, topic1, test7.Topics[0][0], test7.Topics[0][1], + ) + } + if len(test7.Topics[1]) != 1 { + t.Fatalf("expected 1 topic, got %d topics", len(test7.Topics[1])) + } + if test7.Topics[1][0] != nullTopic { + t.Fatalf("expected empty hash, got %x", test7.Topics[1][0]) + } + if len(test7.Topics[2]) != 2 { + t.Fatalf("expected 2 topics, got %d topics", len(test7.Topics[2])) + } + if test7.Topics[2][0] != topic2 || test7.Topics[2][1] != nullTopic { + t.Fatalf("invalid topics expected [%x,%x], got [%x,%x]", + topic2, nullTopic, test7.Topics[2][0], test7.Topics[2][1], + ) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go new file mode 100644 index 0000000..9a8e2fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter.go @@ -0,0 +1,287 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filters + +import ( + "math" + "time" + + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +type Backend interface { + ChainDb() ethdb.Database + EventMux() *event.TypeMux + HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) + GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) +} + +// Filter can be used to retrieve and filter logs. +type Filter struct { + backend Backend + useMipMap bool + + created time.Time + + db ethdb.Database + begin, end int64 + addresses []common.Address + topics [][]common.Hash +} + +// New creates a new filter which uses a bloom filter on blocks to figure out whether +// a particular block is interesting or not. +// MipMaps allow past blocks to be searched much more efficiently, but are not available +// to light clients. +func New(backend Backend, useMipMap bool) *Filter { + return &Filter{ + backend: backend, + useMipMap: useMipMap, + db: backend.ChainDb(), + } +} + +// SetBeginBlock sets the earliest block for filtering. +// -1 = latest block (i.e., the current block) +// hash = particular hash from-to +func (f *Filter) SetBeginBlock(begin int64) { + f.begin = begin +} + +// SetEndBlock sets the latest block for filtering. +func (f *Filter) SetEndBlock(end int64) { + f.end = end +} + +// SetAddresses matches only logs that are generated from addresses that are included +// in the given addresses. +func (f *Filter) SetAddresses(addr []common.Address) { + f.addresses = addr +} + +// SetTopics matches only logs that have topics matching the given topics. +func (f *Filter) SetTopics(topics [][]common.Hash) { + f.topics = topics +} + +// FindOnce searches the blockchain for matching log entries, returning +// all matching entries from the first block that contains matches, +// updating the start point of the filter accordingly. If no results are +// found, a nil slice is returned. +func (f *Filter) FindOnce(ctx context.Context) ([]*types.Log, error) { + head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + if head == nil { + return nil, nil + } + headBlockNumber := head.Number.Uint64() + + var beginBlockNo uint64 = uint64(f.begin) + if f.begin == -1 { + beginBlockNo = headBlockNumber + } + var endBlockNo uint64 = uint64(f.end) + if f.end == -1 { + endBlockNo = headBlockNumber + } + + // if no addresses are present we can't make use of fast search which + // uses the mipmap bloom filters to check for fast inclusion and uses + // higher range probability in order to ensure at least a false positive + if !f.useMipMap || len(f.addresses) == 0 { + logs, blockNumber, err := f.getLogs(ctx, beginBlockNo, endBlockNo) + f.begin = int64(blockNumber + 1) + return logs, err + } + + logs, blockNumber := f.mipFind(beginBlockNo, endBlockNo, 0) + f.begin = int64(blockNumber + 1) + return logs, nil +} + +// Run filters logs with the current parameters set +func (f *Filter) Find(ctx context.Context) (logs []*types.Log, err error) { + for { + newLogs, err := f.FindOnce(ctx) + if len(newLogs) == 0 || err != nil { + return logs, err + } + logs = append(logs, newLogs...) + } +} + +func (f *Filter) mipFind(start, end uint64, depth int) (logs []*types.Log, blockNumber uint64) { + level := core.MIPMapLevels[depth] + // normalise numerator so we can work in level specific batches and + // work with the proper range checks + for num := start / level * level; num <= end; num += level { + // find addresses in bloom filters + bloom := core.GetMipmapBloom(f.db, num, level) + // Don't bother checking the first time through the loop - we're probably picking + // up where a previous run left off. + first := true + for _, addr := range f.addresses { + if first || bloom.TestBytes(addr[:]) { + first = false + // range check normalised values and make sure that + // we're resolving the correct range instead of the + // normalised values. + start := uint64(math.Max(float64(num), float64(start))) + end := uint64(math.Min(float64(num+level-1), float64(end))) + if depth+1 == len(core.MIPMapLevels) { + l, blockNumber, _ := f.getLogs(context.Background(), start, end) + if len(l) > 0 { + return l, blockNumber + } + } else { + l, blockNumber := f.mipFind(start, end, depth+1) + if len(l) > 0 { + return l, blockNumber + } + } + } + } + } + + return nil, end +} + +func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) { + for i := start; i <= end; i++ { + blockNumber := rpc.BlockNumber(i) + header, err := f.backend.HeaderByNumber(ctx, blockNumber) + if header == nil || err != nil { + return logs, end, err + } + + // Use bloom filtering to see if this block is interesting given the + // current parameters + if f.bloomFilter(header.Bloom) { + // Get the logs of the block + receipts, err := f.backend.GetReceipts(ctx, header.Hash()) + if err != nil { + return nil, end, err + } + var unfiltered []*types.Log + for _, receipt := range receipts { + unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...) + } + logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) + if len(logs) > 0 { + return logs, uint64(blockNumber), nil + } + } + } + + return logs, end, nil +} + +func includes(addresses []common.Address, a common.Address) bool { + for _, addr := range addresses { + if addr == a { + return true + } + } + + return false +} + +// filterLogs creates a slice of logs matching the given criteria. +func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { + var ret []*types.Log +Logs: + for _, log := range logs { + if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { + continue + } + if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { + continue + } + + if len(addresses) > 0 && !includes(addresses, log.Address) { + continue + } + + logTopics := make([]common.Hash, len(topics)) + copy(logTopics, log.Topics) + + // If the to filtered topics is greater than the amount of topics in logs, skip. + if len(topics) > len(log.Topics) { + continue Logs + } + + for i, topics := range topics { + var match bool + for _, topic := range topics { + // common.Hash{} is a match all (wildcard) + if (topic == common.Hash{}) || log.Topics[i] == topic { + match = true + break + } + } + + if !match { + continue Logs + } + } + ret = append(ret, log) + } + + return ret +} + +func (f *Filter) bloomFilter(bloom types.Bloom) bool { + return bloomFilter(bloom, f.addresses, f.topics) +} + +func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { + var included bool + for _, addr := range addresses { + if types.BloomLookup(bloom, addr) { + included = true + break + } + } + + if !included { + return false + } + } + + for _, sub := range topics { + var included bool + for _, topic := range sub { + if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { + included = true + break + } + } + if !included { + return false + } + } + + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go new file mode 100644 index 0000000..3adf811 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system.go @@ -0,0 +1,432 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package filters implements an ethereum filtering system for block, +// transactions and log events. +package filters + +import ( + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Type determines the kind of filter and is used to put the filter in to +// the correct bucket when added. +type Type byte + +const ( + // UnknownSubscription indicates an unknown subscription type + UnknownSubscription Type = iota + // LogsSubscription queries for new or removed (chain reorg) logs + LogsSubscription + // PendingLogsSubscription queries for logs in pending blocks + PendingLogsSubscription + // MinedAndPendingLogsSubscription queries for logs in mined and pending blocks. + MinedAndPendingLogsSubscription + // PendingTransactionsSubscription queries tx hashes for pending + // transactions entering the pending state + PendingTransactionsSubscription + // BlocksSubscription queries hashes for blocks that are imported + BlocksSubscription + // LastSubscription keeps track of the last index + LastIndexSubscription +) + +var ( + ErrInvalidSubscriptionID = errors.New("invalid id") +) + +type subscription struct { + id rpc.ID + typ Type + created time.Time + logsCrit FilterCriteria + logs chan []*types.Log + hashes chan common.Hash + headers chan *types.Header + installed chan struct{} // closed when the filter is installed + err chan error // closed when the filter is uninstalled +} + +// EventSystem creates subscriptions, processes events and broadcasts them to the +// subscription which match the subscription criteria. +type EventSystem struct { + mux *event.TypeMux + sub *event.TypeMuxSubscription + backend Backend + lightMode bool + lastHead *types.Header + install chan *subscription // install filter for event notification + uninstall chan *subscription // remove filter for event notification +} + +// NewEventSystem creates a new manager that listens for event on the given mux, +// parses and filters them. It uses the all map to retrieve filter changes. The +// work loop holds its own index that is used to forward events to filters. +// +// The returned manager has a loop that needs to be stopped with the Stop function +// or by stopping the given mux. +func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { + m := &EventSystem{ + mux: mux, + backend: backend, + lightMode: lightMode, + install: make(chan *subscription), + uninstall: make(chan *subscription), + } + + go m.eventLoop() + + return m +} + +// Subscription is created when the client registers itself for a particular event. +type Subscription struct { + ID rpc.ID + f *subscription + es *EventSystem + unsubOnce sync.Once +} + +// Err returns a channel that is closed when unsubscribed. +func (sub *Subscription) Err() <-chan error { + return sub.f.err +} + +// Unsubscribe uninstalls the subscription from the event broadcast loop. +func (sub *Subscription) Unsubscribe() { + sub.unsubOnce.Do(func() { + uninstallLoop: + for { + // write uninstall request and consume logs/hashes. This prevents + // the eventLoop broadcast method to deadlock when writing to the + // filter event channel while the subscription loop is waiting for + // this method to return (and thus not reading these events). + select { + case sub.es.uninstall <- sub.f: + break uninstallLoop + case <-sub.f.logs: + case <-sub.f.hashes: + case <-sub.f.headers: + } + } + + // wait for filter to be uninstalled in work loop before returning + // this ensures that the manager won't use the event channel which + // will probably be closed by the client asap after this method returns. + <-sub.Err() + }) +} + +// subscribe installs the subscription in the event broadcast loop. +func (es *EventSystem) subscribe(sub *subscription) *Subscription { + es.install <- sub + <-sub.installed + return &Subscription{ID: sub.id, f: sub, es: es} +} + +// SubscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. Default value for the from and to +// block is "latest". If the fromBlock > toBlock an error is returned. +func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*types.Log) (*Subscription, error) { + var from, to rpc.BlockNumber + if crit.FromBlock == nil { + from = rpc.LatestBlockNumber + } else { + from = rpc.BlockNumber(crit.FromBlock.Int64()) + } + if crit.ToBlock == nil { + to = rpc.LatestBlockNumber + } else { + to = rpc.BlockNumber(crit.ToBlock.Int64()) + } + + // only interested in pending logs + if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { + return es.subscribePendingLogs(crit, logs), nil + } + // only interested in new mined logs + if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { + return es.subscribeLogs(crit, logs), nil + } + // only interested in mined logs within a specific block range + if from >= 0 && to >= 0 && to >= from { + return es.subscribeLogs(crit, logs), nil + } + // interested in mined logs from a specific block number, new logs and pending logs + if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { + return es.subscribeMinedPendingLogs(crit, logs), nil + } + // interested in logs from a specific block number to new mined blocks + if from >= 0 && to == rpc.LatestBlockNumber { + return es.subscribeLogs(crit, logs), nil + } + return nil, fmt.Errorf("invalid from and to block combination: from > to") +} + +// subscribeMinedPendingLogs creates a subscription that returned mined and +// pending logs that match the given criteria. +func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: MinedAndPendingLogsSubscription, + logsCrit: crit, + created: time.Now(), + logs: logs, + hashes: make(chan common.Hash), + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +// subscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. +func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: LogsSubscription, + logsCrit: crit, + created: time.Now(), + logs: logs, + hashes: make(chan common.Hash), + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +// subscribePendingLogs creates a subscription that writes transaction hashes for +// transactions that enter the transaction pool. +func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: PendingLogsSubscription, + logsCrit: crit, + created: time.Now(), + logs: logs, + hashes: make(chan common.Hash), + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +// SubscribeNewHeads creates a subscription that writes the header of a block that is +// imported in the chain. +func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: BlocksSubscription, + created: time.Now(), + logs: make(chan []*types.Log), + hashes: make(chan common.Hash), + headers: headers, + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +// SubscribePendingTxEvents creates a subscription that writes transaction hashes for +// transactions that enter the transaction pool. +func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: PendingTransactionsSubscription, + created: time.Now(), + logs: make(chan []*types.Log), + hashes: hashes, + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + + return es.subscribe(sub) +} + +type filterIndex map[Type]map[rpc.ID]*subscription + +// broadcast event to filters that match criteria. +func (es *EventSystem) broadcast(filters filterIndex, ev *event.TypeMuxEvent) { + if ev == nil { + return + } + + switch e := ev.Data.(type) { + case []*types.Log: + if len(e) > 0 { + for _, f := range filters[LogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + } + case core.RemovedLogsEvent: + for _, f := range filters[LogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + case core.PendingLogsEvent: + for _, f := range filters[PendingLogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := filterLogs(e.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + case core.TxPreEvent: + for _, f := range filters[PendingTransactionsSubscription] { + if ev.Time.After(f.created) { + f.hashes <- e.Tx.Hash() + } + } + case core.ChainEvent: + for _, f := range filters[BlocksSubscription] { + if ev.Time.After(f.created) { + f.headers <- e.Block.Header() + } + } + if es.lightMode && len(filters[LogsSubscription]) > 0 { + es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { + for _, f := range filters[LogsSubscription] { + if ev.Time.After(f.created) { + if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } + } + }) + } + } +} + +func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { + oldh := es.lastHead + es.lastHead = newHeader + if oldh == nil { + return + } + newh := newHeader + // find common ancestor, create list of rolled back and new block hashes + var oldHeaders, newHeaders []*types.Header + for oldh.Hash() != newh.Hash() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { + oldHeaders = append(oldHeaders, oldh) + oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) + } + if oldh.Number.Uint64() < newh.Number.Uint64() { + newHeaders = append(newHeaders, newh) + newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) + if newh == nil { + // happens when CHT syncing, nothing to do + newh = oldh + } + } + } + // roll back old blocks + for _, h := range oldHeaders { + callBack(h, true) + } + // check new blocks (array is in reverse order) + for i := len(newHeaders) - 1; i >= 0; i-- { + callBack(newHeaders[i], false) + } +} + +// filter logs of a single header in light client mode +func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { + if bloomFilter(header.Bloom, addresses, topics) { + // Get the logs of the block + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + receipts, err := es.backend.GetReceipts(ctx, header.Hash()) + if err != nil { + return nil + } + var unfiltered []*types.Log + for _, receipt := range receipts { + for _, log := range receipt.Logs { + logcopy := *log + logcopy.Removed = remove + unfiltered = append(unfiltered, &logcopy) + } + } + logs := filterLogs(unfiltered, nil, nil, addresses, topics) + return logs + } + return nil +} + +// eventLoop (un)installs filters and processes mux events. +func (es *EventSystem) eventLoop() { + var ( + index = make(filterIndex) + sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, []*types.Log{}, core.TxPreEvent{}, core.ChainEvent{}) + ) + + for i := UnknownSubscription; i < LastIndexSubscription; i++ { + index[i] = make(map[rpc.ID]*subscription) + } + + for { + select { + case ev, active := <-sub.Chan(): + if !active { // system stopped + return + } + es.broadcast(index, ev) + case f := <-es.install: + if f.typ == MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + index[LogsSubscription][f.id] = f + index[PendingLogsSubscription][f.id] = f + } else { + index[f.typ][f.id] = f + } + close(f.installed) + case f := <-es.uninstall: + if f.typ == MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + delete(index[LogsSubscription], f.id) + delete(index[PendingLogsSubscription], f.id) + } else { + delete(index[f.typ], f.id) + } + close(f.err) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system_test.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system_test.go new file mode 100644 index 0000000..1cfced7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_system_test.go @@ -0,0 +1,465 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filters + +import ( + "math/big" + "reflect" + "testing" + "time" + + "golang.org/x/net/context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +type testBackend struct { + mux *event.TypeMux + db ethdb.Database +} + +func (b *testBackend) ChainDb() ethdb.Database { + return b.db +} + +func (b *testBackend) EventMux() *event.TypeMux { + return b.mux +} + +func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { + var hash common.Hash + var num uint64 + if blockNr == rpc.LatestBlockNumber { + hash = core.GetHeadBlockHash(b.db) + num = core.GetBlockNumber(b.db, hash) + } else { + num = uint64(blockNr) + hash = core.GetCanonicalHash(b.db, num) + } + return core.GetHeader(b.db, hash, num), nil +} + +func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { + num := core.GetBlockNumber(b.db, blockHash) + return core.GetBlockReceipts(b.db, blockHash, num), nil +} + +// TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. +// It creates multiple subscriptions: +// - one at the start and should receive all posted chain events and a second (blockHashes) +// - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2]) +// - one that is created after the second cutoff moment (blockHashes[cutoff2:]) +func TestBlockSubscription(t *testing.T) { + t.Parallel() + + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + + genesis = core.WriteGenesisBlockForTesting(db) + chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {}) + chainEvents = []core.ChainEvent{} + ) + + for _, blk := range chain { + chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk}) + } + + chan0 := make(chan *types.Header) + sub0 := api.events.SubscribeNewHeads(chan0) + chan1 := make(chan *types.Header) + sub1 := api.events.SubscribeNewHeads(chan1) + + go func() { // simulate client + i1, i2 := 0, 0 + for i1 != len(chainEvents) || i2 != len(chainEvents) { + select { + case header := <-chan0: + if chainEvents[i1].Hash != header.Hash() { + t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash()) + } + i1++ + case header := <-chan1: + if chainEvents[i2].Hash != header.Hash() { + t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash()) + } + i2++ + } + } + + sub0.Unsubscribe() + sub1.Unsubscribe() + }() + + time.Sleep(1 * time.Second) + for _, e := range chainEvents { + mux.Post(e) + } + + <-sub0.Err() + <-sub1.Err() +} + +// TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux. +func TestPendingTxFilter(t *testing.T) { + t.Parallel() + + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + + transactions = []*types.Transaction{ + types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), + types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), + types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), + types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), + types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), + } + + hashes []common.Hash + ) + + fid0 := api.NewPendingTransactionFilter() + + time.Sleep(1 * time.Second) + for _, tx := range transactions { + ev := core.TxPreEvent{Tx: tx} + mux.Post(ev) + } + + for { + results, err := api.GetFilterChanges(fid0) + if err != nil { + t.Fatalf("Unable to retrieve logs: %v", err) + } + + h := results.([]common.Hash) + hashes = append(hashes, h...) + if len(hashes) >= len(transactions) { + break + } + + time.Sleep(100 * time.Millisecond) + } + + for i := range hashes { + if hashes[i] != transactions[i].Hash() { + t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i]) + } + } +} + +// TestLogFilterCreation test whether a given filter criteria makes sense. +// If not it must return an error. +func TestLogFilterCreation(t *testing.T) { + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + + testCases = []struct { + crit FilterCriteria + success bool + }{ + // defaults + {FilterCriteria{}, true}, + // valid block number range + {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true}, + // "mined" block range to pending + {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true}, + // new mined and pending blocks + {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true}, + // from block "higher" than to block + {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false}, + // from block "higher" than to block + {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, + // from block "higher" than to block + {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, + // from block "higher" than to block + {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false}, + } + ) + + for i, test := range testCases { + _, err := api.NewFilter(test.crit) + if test.success && err != nil { + t.Errorf("expected filter creation for case %d to success, got %v", i, err) + } + if !test.success && err == nil { + t.Errorf("expected testcase %d to fail with an error", i) + } + } +} + +// TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error +// when the filter is created. +func TestInvalidLogFilterCreation(t *testing.T) { + t.Parallel() + + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + ) + + // different situations where log filter creation should fail. + // Reason: fromBlock > toBlock + testCases := []FilterCriteria{ + 0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, + 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, + 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, + } + + for i, test := range testCases { + if _, err := api.NewFilter(test); err == nil { + t.Errorf("Expected NewFilter for case #%d to fail", i) + } + } +} + +// TestLogFilter tests whether log filters match the correct logs that are posted to the event mux. +func TestLogFilter(t *testing.T) { + t.Parallel() + + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + + firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") + secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") + thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") + notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") + firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") + secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") + notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") + + // posted twice, once as vm.Logs and once as core.PendingLogsEvent + allLogs = []*types.Log{ + {Address: firstAddr}, + {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, + {Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, + {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2}, + {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}, + } + + expectedCase7 = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]} + expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]} + + testCases = []struct { + crit FilterCriteria + expected []*types.Log + id rpc.ID + }{ + // match all + 0: {FilterCriteria{}, allLogs, ""}, + // match none due to no matching addresses + 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, []*types.Log{}, ""}, + // match logs based on addresses, ignore topics + 2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""}, + // match none due to no matching topics (match with address) + 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""}, + // match logs based on addresses and topics + 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""}, + // match logs based on multiple addresses and "or" topics + 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""}, + // logs in the pending block + 6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""}, + // mined logs with block num >= 2 or pending logs + 7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""}, + // all "mined" logs with block num >= 2 + 8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""}, + // all "mined" logs + 9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""}, + // all "mined" logs with 1>= block num <=2 and topic secondTopic + 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""}, + // all "mined" and pending logs with topic firstTopic + 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""}, + } + ) + + // create all filters + for i := range testCases { + testCases[i].id, _ = api.NewFilter(testCases[i].crit) + } + + // raise events + time.Sleep(1 * time.Second) + if err := mux.Post(allLogs); err != nil { + t.Fatal(err) + } + if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil { + t.Fatal(err) + } + + for i, tt := range testCases { + var fetched []*types.Log + for { // fetch all expected logs + results, err := api.GetFilterChanges(tt.id) + if err != nil { + t.Fatalf("Unable to fetch logs: %v", err) + } + + fetched = append(fetched, results.([]*types.Log)...) + if len(fetched) >= len(tt.expected) { + break + } + + time.Sleep(100 * time.Millisecond) + } + + if len(fetched) != len(tt.expected) { + t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) + return + } + + for l := range fetched { + if fetched[l].Removed { + t.Errorf("expected log not to be removed for log %d in case %d", l, i) + } + if !reflect.DeepEqual(fetched[l], tt.expected[l]) { + t.Errorf("invalid log on index %d for case %d", l, i) + } + } + } +} + +// TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event mux. +func TestPendingLogsSubscription(t *testing.T) { + t.Parallel() + + var ( + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + + firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") + secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") + thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") + notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") + firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") + secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") + thirdTopic = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333") + forthTopic = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444") + notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") + + allLogs = []core.PendingLogsEvent{ + {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}}, + {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}}, + {Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}}, + {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}}, + {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}}, + {Logs: []*types.Log{ + {Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, + {Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5}, + {Address: thirdAddress, Topics: []common.Hash{forthTopic}, BlockNumber: 5}, + {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, + }}, + } + + convertLogs = func(pl []core.PendingLogsEvent) []*types.Log { + var logs []*types.Log + for _, l := range pl { + logs = append(logs, l.Logs...) + } + return logs + } + + testCases = []struct { + crit FilterCriteria + expected []*types.Log + c chan []*types.Log + sub *Subscription + }{ + // match all + {FilterCriteria{}, convertLogs(allLogs), nil, nil}, + // match none due to no matching addresses + {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{{}}}, []*types.Log{}, nil, nil}, + // match logs based on addresses, ignore topics + {FilterCriteria{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, + // match none due to no matching topics (match with address) + {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil}, + // match logs based on addresses and topics + {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil}, + // match logs based on multiple addresses and "or" topics + {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil}, + // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes + {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, + // multiple pending logs, should match only 2 topics from the logs in block 5 + {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, forthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil}, + } + ) + + // create all subscriptions, this ensures all subscriptions are created before the events are posted. + // on slow machines this could otherwise lead to missing events when the subscription is created after + // (some) events are posted. + for i := range testCases { + testCases[i].c = make(chan []*types.Log) + testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c) + } + + for n, test := range testCases { + i := n + tt := test + go func() { + var fetched []*types.Log + fetchLoop: + for { + logs := <-tt.c + fetched = append(fetched, logs...) + if len(fetched) >= len(tt.expected) { + break fetchLoop + } + } + + if len(fetched) != len(tt.expected) { + t.Fatalf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) + } + + for l := range fetched { + if fetched[l].Removed { + t.Errorf("expected log not to be removed for log %d in case %d", l, i) + } + if !reflect.DeepEqual(fetched[l], tt.expected[l]) { + t.Errorf("invalid log on index %d for case %d", l, i) + } + } + }() + } + + // raise events + time.Sleep(1 * time.Second) + for _, l := range allLogs { + if err := mux.Post(l); err != nil { + t.Fatal(err) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_test.go b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_test.go new file mode 100644 index 0000000..83ff3e9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/filters/filter_test.go @@ -0,0 +1,289 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filters + +import ( + "io/ioutil" + "math/big" + "os" + "testing" + + "golang.org/x/net/context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +func makeReceipt(addr common.Address) *types.Receipt { + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{ + {Address: addr}, + } + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + return receipt +} + +func BenchmarkMipmaps(b *testing.B) { + dir, err := ioutil.TempDir("", "mipmap") + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(dir) + + var ( + db, _ = ethdb.NewLDBDatabase(dir, 0, 0) + mux = new(event.TypeMux) + backend = &testBackend{mux, db} + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = common.BytesToAddress([]byte("jeff")) + addr3 = common.BytesToAddress([]byte("ethereum")) + addr4 = common.BytesToAddress([]byte("random addresses please")) + ) + defer db.Close() + + genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)}) + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 100010, func(i int, gen *core.BlockGen) { + var receipts types.Receipts + switch i { + case 2403: + receipt := makeReceipt(addr1) + receipts = types.Receipts{receipt} + gen.AddUncheckedReceipt(receipt) + case 1034: + receipt := makeReceipt(addr2) + receipts = types.Receipts{receipt} + gen.AddUncheckedReceipt(receipt) + case 34: + receipt := makeReceipt(addr3) + receipts = types.Receipts{receipt} + gen.AddUncheckedReceipt(receipt) + case 99999: + receipt := makeReceipt(addr4) + receipts = types.Receipts{receipt} + gen.AddUncheckedReceipt(receipt) + + } + + // store the receipts + err := core.WriteReceipts(db, receipts) + if err != nil { + b.Fatal(err) + } + core.WriteMipmapBloom(db, uint64(i+1), receipts) + }) + for i, block := range chain { + core.WriteBlock(db, block) + if err := core.WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil { + b.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { + b.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteBlockReceipts(db, block.Hash(), block.NumberU64(), receipts[i]); err != nil { + b.Fatal("error writing block receipts:", err) + } + } + b.ResetTimer() + + filter := New(backend, true) + filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) + filter.SetBeginBlock(0) + filter.SetEndBlock(-1) + + for i := 0; i < b.N; i++ { + logs, _ := filter.Find(context.Background()) + if len(logs) != 4 { + b.Fatal("expected 4 log, got", len(logs)) + } + } +} + +func TestFilters(t *testing.T) { + dir, err := ioutil.TempDir("", "mipmap") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + var ( + db, _ = ethdb.NewLDBDatabase(dir, 0, 0) + mux = new(event.TypeMux) + backend = &testBackend{mux, db} + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key1.PublicKey) + + hash1 = common.BytesToHash([]byte("topic1")) + hash2 = common.BytesToHash([]byte("topic2")) + hash3 = common.BytesToHash([]byte("topic3")) + hash4 = common.BytesToHash([]byte("topic4")) + ) + defer db.Close() + + genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)}) + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 1000, func(i int, gen *core.BlockGen) { + var receipts types.Receipts + switch i { + case 1: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{ + { + Address: addr, + Topics: []common.Hash{hash1}, + }, + } + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + case 2: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{ + { + Address: addr, + Topics: []common.Hash{hash2}, + }, + } + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + case 998: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{ + { + Address: addr, + Topics: []common.Hash{hash3}, + }, + } + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + case 999: + receipt := types.NewReceipt(nil, new(big.Int)) + receipt.Logs = []*types.Log{ + { + Address: addr, + Topics: []common.Hash{hash4}, + }, + } + gen.AddUncheckedReceipt(receipt) + receipts = types.Receipts{receipt} + } + + // store the receipts + err := core.WriteReceipts(db, receipts) + if err != nil { + t.Fatal(err) + } + // i is used as block number for the writes but since the i + // starts at 0 and block 0 (genesis) is already present increment + // by one + core.WriteMipmapBloom(db, uint64(i+1), receipts) + }) + for i, block := range chain { + core.WriteBlock(db, block) + if err := core.WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { + t.Fatalf("failed to insert block number: %v", err) + } + if err := core.WriteBlockReceipts(db, block.Hash(), block.NumberU64(), receipts[i]); err != nil { + t.Fatal("error writing block receipts:", err) + } + } + + filter := New(backend, true) + filter.SetAddresses([]common.Address{addr}) + filter.SetTopics([][]common.Hash{{hash1, hash2, hash3, hash4}}) + filter.SetBeginBlock(0) + filter.SetEndBlock(-1) + + logs, _ := filter.Find(context.Background()) + if len(logs) != 4 { + t.Error("expected 4 log, got", len(logs)) + } + + filter = New(backend, true) + filter.SetAddresses([]common.Address{addr}) + filter.SetTopics([][]common.Hash{{hash3}}) + filter.SetBeginBlock(900) + filter.SetEndBlock(999) + logs, _ = filter.Find(context.Background()) + if len(logs) != 1 { + t.Error("expected 1 log, got", len(logs)) + } + if len(logs) > 0 && logs[0].Topics[0] != hash3 { + t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) + } + + filter = New(backend, true) + filter.SetAddresses([]common.Address{addr}) + filter.SetTopics([][]common.Hash{{hash3}}) + filter.SetBeginBlock(990) + filter.SetEndBlock(-1) + logs, _ = filter.Find(context.Background()) + if len(logs) != 1 { + t.Error("expected 1 log, got", len(logs)) + } + if len(logs) > 0 && logs[0].Topics[0] != hash3 { + t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) + } + + filter = New(backend, true) + filter.SetTopics([][]common.Hash{{hash1, hash2}}) + filter.SetBeginBlock(1) + filter.SetEndBlock(10) + + logs, _ = filter.Find(context.Background()) + if len(logs) != 2 { + t.Error("expected 2 log, got", len(logs)) + } + + failHash := common.BytesToHash([]byte("fail")) + filter = New(backend, true) + filter.SetTopics([][]common.Hash{{failHash}}) + filter.SetBeginBlock(0) + filter.SetEndBlock(-1) + + logs, _ = filter.Find(context.Background()) + if len(logs) != 0 { + t.Error("expected 0 log, got", len(logs)) + } + + failAddr := common.BytesToAddress([]byte("failmenow")) + filter = New(backend, true) + filter.SetAddresses([]common.Address{failAddr}) + filter.SetBeginBlock(0) + filter.SetEndBlock(-1) + + logs, _ = filter.Find(context.Background()) + if len(logs) != 0 { + t.Error("expected 0 log, got", len(logs)) + } + + filter = New(backend, true) + filter.SetTopics([][]common.Hash{{failHash}, {hash1}}) + filter.SetBeginBlock(0) + filter.SetEndBlock(-1) + + logs, _ = filter.Find(context.Background()) + if len(logs) != 0 { + t.Error("expected 0 log, got", len(logs)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/gasprice.go b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/gasprice.go new file mode 100644 index 0000000..eb2df4a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/gasprice.go @@ -0,0 +1,229 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasprice + +import ( + "math/big" + "math/rand" + "sync" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + gpoProcessPastBlocks = 100 + + // for testing + gpoDefaultBaseCorrectionFactor = 110 + gpoDefaultMinGasPrice = 10000000000000 +) + +type blockPriceInfo struct { + baseGasPrice *big.Int +} + +type GpoParams struct { + GpoMinGasPrice *big.Int + GpoMaxGasPrice *big.Int + GpoFullBlockRatio int + GpobaseStepDown int + GpobaseStepUp int + GpobaseCorrectionFactor int +} + +// GasPriceOracle recommends gas prices based on the content of recent +// blocks. +type GasPriceOracle struct { + chain *core.BlockChain + db ethdb.Database + evmux *event.TypeMux + params *GpoParams + initOnce sync.Once + minPrice *big.Int + lastBaseMutex sync.Mutex + lastBase *big.Int + + // state of listenLoop + blocks map[uint64]*blockPriceInfo + firstProcessed, lastProcessed uint64 + minBase *big.Int +} + +// NewGasPriceOracle returns a new oracle. +func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle { + minprice := params.GpoMinGasPrice + if minprice == nil { + minprice = big.NewInt(gpoDefaultMinGasPrice) + } + minbase := new(big.Int).Mul(minprice, big.NewInt(100)) + if params.GpobaseCorrectionFactor > 0 { + minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor))) + } + return &GasPriceOracle{ + chain: chain, + db: db, + evmux: evmux, + params: params, + blocks: make(map[uint64]*blockPriceInfo), + minBase: minbase, + minPrice: minprice, + lastBase: minprice, + } +} + +func (gpo *GasPriceOracle) init() { + gpo.initOnce.Do(func() { + gpo.processPastBlocks() + go gpo.listenLoop() + }) +} + +func (self *GasPriceOracle) processPastBlocks() { + last := int64(-1) + cblock := self.chain.CurrentBlock() + if cblock != nil { + last = int64(cblock.NumberU64()) + } + first := int64(0) + if last > gpoProcessPastBlocks { + first = last - gpoProcessPastBlocks + } + self.firstProcessed = uint64(first) + for i := first; i <= last; i++ { + block := self.chain.GetBlockByNumber(uint64(i)) + if block != nil { + self.processBlock(block) + } + } + +} + +func (self *GasPriceOracle) listenLoop() { + events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) + defer events.Unsubscribe() + + for event := range events.Chan() { + switch event := event.Data.(type) { + case core.ChainEvent: + self.processBlock(event.Block) + case core.ChainSplitEvent: + self.processBlock(event.Block) + } + } +} + +func (self *GasPriceOracle) processBlock(block *types.Block) { + i := block.NumberU64() + if i > self.lastProcessed { + self.lastProcessed = i + } + + lastBase := self.minPrice + bpl := self.blocks[i-1] + if bpl != nil { + lastBase = bpl.baseGasPrice + } + if lastBase == nil { + return + } + + var corr int + lp := self.lowestPrice(block) + if lp == nil { + return + } + + if lastBase.Cmp(lp) < 0 { + corr = self.params.GpobaseStepUp + } else { + corr = -self.params.GpobaseStepDown + } + + crand := int64(corr * (900 + rand.Intn(201))) + newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand)) + newBase.Div(newBase, big.NewInt(1000000)) + + if newBase.Cmp(self.minBase) < 0 { + newBase = self.minBase + } + + bpi := self.blocks[i] + if bpi == nil { + bpi = &blockPriceInfo{} + self.blocks[i] = bpi + } + bpi.baseGasPrice = newBase + self.lastBaseMutex.Lock() + self.lastBase = newBase + self.lastBaseMutex.Unlock() + + glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", i, newBase.Int64()) +} + +// returns the lowers possible price with which a tx was or could have been included +func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { + gasUsed := big.NewInt(0) + + receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64()) + if len(receipts) > 0 { + if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { + gasUsed = receipts[len(receipts)-1].CumulativeGasUsed + } + } + + if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(), + big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 { + // block is not full, could have posted a tx with MinGasPrice + return big.NewInt(0) + } + + txs := block.Transactions() + if len(txs) == 0 { + return big.NewInt(0) + } + // block is full, find smallest gasPrice + minPrice := txs[0].GasPrice() + for i := 1; i < len(txs); i++ { + price := txs[i].GasPrice() + if price.Cmp(minPrice) < 0 { + minPrice = price + } + } + return minPrice +} + +// SuggestPrice returns the recommended gas price. +func (self *GasPriceOracle) SuggestPrice() *big.Int { + self.init() + self.lastBaseMutex.Lock() + price := new(big.Int).Set(self.lastBase) + self.lastBaseMutex.Unlock() + + price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor))) + price.Div(price, big.NewInt(100)) + if price.Cmp(self.minPrice) < 0 { + price.Set(self.minPrice) + } else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 { + price.Set(self.params.GpoMaxGasPrice) + } + return price +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go new file mode 100644 index 0000000..8886d32 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/gasprice/lightprice.go @@ -0,0 +1,160 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package gasprice + +import ( + "math/big" + "sort" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +const ( + LpoAvgCount = 5 + LpoMinCount = 3 + LpoMaxBlocks = 20 + LpoSelect = 50 + LpoDefaultPrice = 20000000000 +) + +// LightPriceOracle recommends gas prices based on the content of recent +// blocks. Suitable for both light and full clients. +type LightPriceOracle struct { + backend ethapi.Backend + lastHead common.Hash + lastPrice *big.Int + cacheLock sync.RWMutex + fetchLock sync.Mutex +} + +// NewLightPriceOracle returns a new oracle. +func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { + return &LightPriceOracle{ + backend: backend, + lastPrice: big.NewInt(LpoDefaultPrice), + } +} + +// SuggestPrice returns the recommended gas price. +func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error) { + self.cacheLock.RLock() + lastHead := self.lastHead + lastPrice := self.lastPrice + self.cacheLock.RUnlock() + + head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + headHash := head.Hash() + if headHash == lastHead { + return lastPrice, nil + } + + self.fetchLock.Lock() + defer self.fetchLock.Unlock() + + // try checking the cache again, maybe the last fetch fetched what we need + self.cacheLock.RLock() + lastHead = self.lastHead + lastPrice = self.lastPrice + self.cacheLock.RUnlock() + if headHash == lastHead { + return lastPrice, nil + } + + blockNum := head.Number.Uint64() + chn := make(chan lpResult, LpoMaxBlocks) + sent := 0 + exp := 0 + var lps bigIntArray + for sent < LpoAvgCount && blockNum > 0 { + go self.getLowestPrice(ctx, blockNum, chn) + sent++ + exp++ + blockNum-- + } + maxEmpty := LpoAvgCount - LpoMinCount + for exp > 0 { + res := <-chn + if res.err != nil { + return nil, res.err + } + exp-- + if res.price != nil { + lps = append(lps, res.price) + } else { + if maxEmpty > 0 { + maxEmpty-- + } else { + if blockNum > 0 && sent < LpoMaxBlocks { + go self.getLowestPrice(ctx, blockNum, chn) + sent++ + exp++ + blockNum-- + } + } + } + } + price := lastPrice + if len(lps) > 0 { + sort.Sort(lps) + price = lps[(len(lps)-1)*LpoSelect/100] + } + + self.cacheLock.Lock() + self.lastHead = headHash + self.lastPrice = price + self.cacheLock.Unlock() + return price, nil +} + +type lpResult struct { + price *big.Int + err error +} + +// getLowestPrice calculates the lowest transaction gas price in a given block +// and sends it to the result channel. If the block is empty, price is nil. +func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) { + block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + if block == nil { + chn <- lpResult{nil, err} + return + } + txs := block.Transactions() + if len(txs) == 0 { + chn <- lpResult{nil, nil} + return + } + // find smallest gasPrice + minPrice := txs[0].GasPrice() + for i := 1; i < len(txs); i++ { + price := txs[i].GasPrice() + if price.Cmp(minPrice) < 0 { + minPrice = price + } + } + chn <- lpResult{minPrice, nil} +} + +type bigIntArray []*big.Int + +func (s bigIntArray) Len() int { return len(s) } +func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } +func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler.go b/vendor/github.com/ethereum/go-ethereum/eth/handler.go new file mode 100644 index 0000000..0e7eed3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler.go @@ -0,0 +1,768 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/fetcher" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. + estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header +) + +var ( + daoChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the DAO handshake challenge +) + +// errIncompatibleConfig is returned if the requested protocols and configs are +// not compatible (low protocol version restrictions and high requirements). +var errIncompatibleConfig = errors.New("incompatible configuration") + +func errResp(code errCode, format string, v ...interface{}) error { + return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) +} + +type ProtocolManager struct { + networkId int + + fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) + synced uint32 // Flag whether we're considered synchronised (enables transaction processing) + + txpool txPool + blockchain *core.BlockChain + chaindb ethdb.Database + chainconfig *params.ChainConfig + maxPeers int + + downloader *downloader.Downloader + fetcher *fetcher.Fetcher + peers *peerSet + + SubProtocols []p2p.Protocol + + eventMux *event.TypeMux + txSub *event.TypeMuxSubscription + minedBlockSub *event.TypeMuxSubscription + + // channels for fetcher, syncer, txsyncLoop + newPeerCh chan *peer + txsyncCh chan *txsync + quitSync chan struct{} + noMorePeers chan struct{} + + lesServer LesServer + + // wait group is used for graceful shutdowns during downloading + // and processing + wg sync.WaitGroup + + badBlockReportingEnabled bool +} + +// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable +// with the ethereum network. +func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { + // Create the protocol manager with the base fields + manager := &ProtocolManager{ + networkId: networkId, + eventMux: mux, + txpool: txpool, + blockchain: blockchain, + chaindb: chaindb, + chainconfig: config, + maxPeers: maxPeers, + peers: newPeerSet(), + newPeerCh: make(chan *peer), + noMorePeers: make(chan struct{}), + txsyncCh: make(chan *txsync), + quitSync: make(chan struct{}), + } + // Figure out whether to allow fast sync or not + if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { + glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled") + fastSync = false + } + if fastSync { + manager.fastSync = uint32(1) + } + // Initiate a sub-protocol for every implemented version we can handle + manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) + for i, version := range ProtocolVersions { + // Skip protocol version if incompatible with the mode of operation + if fastSync && version < eth63 { + continue + } + // Compatible; initialise the sub-protocol + version := version // Closure for the run + manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{ + Name: ProtocolName, + Version: version, + Length: ProtocolLengths[i], + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + peer := manager.newPeer(int(version), p, rw) + select { + case manager.newPeerCh <- peer: + manager.wg.Add(1) + defer manager.wg.Done() + return manager.handle(peer) + case <-manager.quitSync: + return p2p.DiscQuitting + } + }, + NodeInfo: func() interface{} { + return manager.NodeInfo() + }, + PeerInfo: func(id discover.NodeID) interface{} { + if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { + return p.Info() + } + return nil + }, + }) + } + if len(manager.SubProtocols) == 0 { + return nil, errIncompatibleConfig + } + // Construct the different synchronisation mechanisms + manager.downloader = downloader.New(downloader.FullSync, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash, + blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, + blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.insertChain, blockchain.InsertReceiptChain, blockchain.Rollback, + manager.removePeer) + + validator := func(block *types.Block, parent *types.Block) error { + return core.ValidateHeader(config, pow, block.Header(), parent.Header(), true, false) + } + heighter := func() uint64 { + return blockchain.CurrentBlock().NumberU64() + } + inserter := func(blocks types.Blocks) (int, error) { + atomic.StoreUint32(&manager.synced, 1) // Mark initial sync done on any fetcher import + return manager.insertChain(blocks) + } + manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) + + if blockchain.Genesis().Hash().Hex() == defaultGenesisHash && networkId == 1 { + glog.V(logger.Debug).Infoln("Bad Block Reporting is enabled") + manager.badBlockReportingEnabled = true + } + + return manager, nil +} + +func (pm *ProtocolManager) insertChain(blocks types.Blocks) (i int, err error) { + i, err = pm.blockchain.InsertChain(blocks) + if pm.badBlockReportingEnabled && core.IsValidationErr(err) && i < len(blocks) { + go sendBadBlockReport(blocks[i], err) + } + return i, err +} + +func (pm *ProtocolManager) removePeer(id string) { + // Short circuit if the peer was already removed + peer := pm.peers.Peer(id) + if peer == nil { + return + } + glog.V(logger.Debug).Infoln("Removing peer", id) + + // Unregister the peer from the downloader and Ethereum peer set + pm.downloader.UnregisterPeer(id) + if err := pm.peers.Unregister(id); err != nil { + glog.V(logger.Error).Infoln("Removal failed:", err) + } + // Hard disconnect at the networking layer + if peer != nil { + peer.Peer.Disconnect(p2p.DiscUselessPeer) + } +} + +func (pm *ProtocolManager) Start() { + // broadcast transactions + pm.txSub = pm.eventMux.Subscribe(core.TxPreEvent{}) + go pm.txBroadcastLoop() + // broadcast mined blocks + pm.minedBlockSub = pm.eventMux.Subscribe(core.NewMinedBlockEvent{}) + go pm.minedBroadcastLoop() + + // start sync handlers + go pm.syncer() + go pm.txsyncLoop() +} + +func (pm *ProtocolManager) Stop() { + glog.V(logger.Info).Infoln("Stopping ethereum protocol handler...") + + pm.txSub.Unsubscribe() // quits txBroadcastLoop + pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop + + // Quit the sync loop. + // After this send has completed, no new peers will be accepted. + pm.noMorePeers <- struct{}{} + + // Quit fetcher, txsyncLoop. + close(pm.quitSync) + + // Disconnect existing sessions. + // This also closes the gate for any new registrations on the peer set. + // sessions which are already established but not added to pm.peers yet + // will exit when they try to register. + pm.peers.Close() + + // Wait for all peer handler goroutines and the loops to come down. + pm.wg.Wait() + + glog.V(logger.Info).Infoln("Ethereum protocol handler stopped") +} + +func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { + return newPeer(pv, p, newMeteredMsgWriter(rw)) +} + +// handle is the callback invoked to manage the life cycle of an eth peer. When +// this function terminates, the peer is disconnected. +func (pm *ProtocolManager) handle(p *peer) error { + if pm.peers.Len() >= pm.maxPeers { + return p2p.DiscTooManyPeers + } + + glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name()) + + // Execute the Ethereum handshake + td, head, genesis := pm.blockchain.Status() + if err := p.Handshake(pm.networkId, td, head, genesis); err != nil { + glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err) + return err + } + if rw, ok := p.rw.(*meteredMsgReadWriter); ok { + rw.Init(p.version) + } + // Register the peer locally + glog.V(logger.Detail).Infof("%v: adding peer", p) + if err := pm.peers.Register(p); err != nil { + glog.V(logger.Error).Infof("%v: addition failed: %v", p, err) + return err + } + defer pm.removePeer(p.id) + + // Register the peer in the downloader. If the downloader considers it banned, we disconnect + if err := pm.downloader.RegisterPeer(p.id, p.version, p.Head, p.RequestHeadersByHash, p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil { + return err + } + // Propagate existing transactions. new transactions appearing + // after this will be sent via broadcasts. + pm.syncTransactions(p) + + // If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork + if daoBlock := pm.chainconfig.DAOForkBlock; daoBlock != nil { + // Request the peer's DAO fork header for extra-data validation + if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil { + return err + } + // Start a timer to disconnect if the peer doesn't reply in time + p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() { + glog.V(logger.Debug).Infof("%v: timed out DAO fork-check, dropping", p) + pm.removePeer(p.id) + }) + // Make sure it's cleaned up if the peer dies off + defer func() { + if p.forkDrop != nil { + p.forkDrop.Stop() + p.forkDrop = nil + } + }() + } + // main loop. handle incoming messages. + for { + if err := pm.handleMsg(p); err != nil { + glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err) + return err + } + } +} + +// handleMsg is invoked whenever an inbound message is received from a remote +// peer. The remote connection is torn down upon returning any error. +func (pm *ProtocolManager) handleMsg(p *peer) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + if msg.Size > ProtocolMaxMsgSize { + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + defer msg.Discard() + + // Handle the message depending on its contents + switch { + case msg.Code == StatusMsg: + // Status messages should never arrive after the handshake + return errResp(ErrExtraStatusMsg, "uncontrolled status message") + + // Block header query, collect the requested headers and reply + case msg.Code == GetBlockHeadersMsg: + // Decode the complex header query + var query getBlockHeadersData + if err := msg.Decode(&query); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + hashMode := query.Origin.Hash != (common.Hash{}) + + // Gather headers until the fetch or network limits is reached + var ( + bytes common.StorageSize + headers []*types.Header + unknown bool + ) + for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch { + // Retrieve the next header satisfying the query + var origin *types.Header + if hashMode { + origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash) + } else { + origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) + } + if origin == nil { + break + } + number := origin.Number.Uint64() + headers = append(headers, origin) + bytes += estHeaderRlpSize + + // Advance to the next header of the query + switch { + case query.Origin.Hash != (common.Hash{}) && query.Reverse: + // Hash based traversal towards the genesis block + for i := 0; i < int(query.Skip)+1; i++ { + if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil { + query.Origin.Hash = header.ParentHash + number-- + } else { + unknown = true + break + } + } + case query.Origin.Hash != (common.Hash{}) && !query.Reverse: + // Hash based traversal towards the leaf block + var ( + current = origin.Number.Uint64() + next = current + query.Skip + 1 + ) + if next <= current { + infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") + glog.V(logger.Warn).Infof("%v: GetBlockHeaders skip overflow attack (current %v, skip %v, next %v)\nMalicious peer infos: %s", p, current, query.Skip, next, infos) + unknown = true + } else { + if header := pm.blockchain.GetHeaderByNumber(next); header != nil { + if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { + query.Origin.Hash = header.Hash() + } else { + unknown = true + } + } else { + unknown = true + } + } + case query.Reverse: + // Number based traversal towards the genesis block + if query.Origin.Number >= query.Skip+1 { + query.Origin.Number -= (query.Skip + 1) + } else { + unknown = true + } + + case !query.Reverse: + // Number based traversal towards the leaf block + query.Origin.Number += (query.Skip + 1) + } + } + return p.SendBlockHeaders(headers) + + case msg.Code == BlockHeadersMsg: + // A batch of headers arrived to one of our previous requests + var headers []*types.Header + if err := msg.Decode(&headers); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // If no headers were received, but we're expending a DAO fork check, maybe it's that + if len(headers) == 0 && p.forkDrop != nil { + // Possibly an empty reply to the fork header checks, sanity check TDs + verifyDAO := true + + // If we already have a DAO header, we can check the peer's TD against it. If + // the peer's ahead of this, it too must have a reply to the DAO check + if daoHeader := pm.blockchain.GetHeaderByNumber(pm.chainconfig.DAOForkBlock.Uint64()); daoHeader != nil { + if _, td := p.Head(); td.Cmp(pm.blockchain.GetTd(daoHeader.Hash(), daoHeader.Number.Uint64())) >= 0 { + verifyDAO = false + } + } + // If we're seemingly on the same chain, disable the drop timer + if verifyDAO { + glog.V(logger.Debug).Infof("%v: seems to be on the same side of the DAO fork", p) + p.forkDrop.Stop() + p.forkDrop = nil + return nil + } + } + // Filter out any explicitly requested headers, deliver the rest to the downloader + filter := len(headers) == 1 + if filter { + // If it's a potential DAO fork check, validate against the rules + if p.forkDrop != nil && pm.chainconfig.DAOForkBlock.Cmp(headers[0].Number) == 0 { + // Disable the fork drop timer + p.forkDrop.Stop() + p.forkDrop = nil + + // Validate the header and either drop the peer or continue + if err := core.ValidateDAOHeaderExtraData(pm.chainconfig, headers[0]); err != nil { + glog.V(logger.Debug).Infof("%v: verified to be on the other side of the DAO fork, dropping", p) + return err + } + glog.V(logger.Debug).Infof("%v: verified to be on the same side of the DAO fork", p) + return nil + } + // Irrelevant of the fork checks, send the header to the fetcher just in case + headers = pm.fetcher.FilterHeaders(headers, time.Now()) + } + if len(headers) > 0 || !filter { + err := pm.downloader.DeliverHeaders(p.id, headers) + if err != nil { + glog.V(logger.Debug).Infoln(err) + } + } + + case msg.Code == GetBlockBodiesMsg: + // Decode the retrieval message + msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) + if _, err := msgStream.List(); err != nil { + return err + } + // Gather blocks until the fetch or network limits is reached + var ( + hash common.Hash + bytes int + bodies []rlp.RawValue + ) + for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch { + // Retrieve the hash of the next block + if err := msgStream.Decode(&hash); err == rlp.EOL { + break + } else if err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Retrieve the requested block body, stopping if enough was found + if data := pm.blockchain.GetBodyRLP(hash); len(data) != 0 { + bodies = append(bodies, data) + bytes += len(data) + } + } + return p.SendBlockBodiesRLP(bodies) + + case msg.Code == BlockBodiesMsg: + // A batch of block bodies arrived to one of our previous requests + var request blockBodiesData + if err := msg.Decode(&request); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Deliver them all to the downloader for queuing + trasactions := make([][]*types.Transaction, len(request)) + uncles := make([][]*types.Header, len(request)) + + for i, body := range request { + trasactions[i] = body.Transactions + uncles[i] = body.Uncles + } + // Filter out any explicitly requested bodies, deliver the rest to the downloader + filter := len(trasactions) > 0 || len(uncles) > 0 + if filter { + trasactions, uncles = pm.fetcher.FilterBodies(trasactions, uncles, time.Now()) + } + if len(trasactions) > 0 || len(uncles) > 0 || !filter { + err := pm.downloader.DeliverBodies(p.id, trasactions, uncles) + if err != nil { + glog.V(logger.Debug).Infoln(err) + } + } + + case p.version >= eth63 && msg.Code == GetNodeDataMsg: + // Decode the retrieval message + msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) + if _, err := msgStream.List(); err != nil { + return err + } + // Gather state data until the fetch or network limits is reached + var ( + hash common.Hash + bytes int + data [][]byte + ) + for bytes < softResponseLimit && len(data) < downloader.MaxStateFetch { + // Retrieve the hash of the next state entry + if err := msgStream.Decode(&hash); err == rlp.EOL { + break + } else if err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Retrieve the requested state entry, stopping if enough was found + if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil { + data = append(data, entry) + bytes += len(entry) + } + } + return p.SendNodeData(data) + + case p.version >= eth63 && msg.Code == NodeDataMsg: + // A batch of node state data arrived to one of our previous requests + var data [][]byte + if err := msg.Decode(&data); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Deliver all to the downloader + if err := pm.downloader.DeliverNodeData(p.id, data); err != nil { + glog.V(logger.Debug).Infof("failed to deliver node state data: %v", err) + } + + case p.version >= eth63 && msg.Code == GetReceiptsMsg: + // Decode the retrieval message + msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size)) + if _, err := msgStream.List(); err != nil { + return err + } + // Gather state data until the fetch or network limits is reached + var ( + hash common.Hash + bytes int + receipts []rlp.RawValue + ) + for bytes < softResponseLimit && len(receipts) < downloader.MaxReceiptFetch { + // Retrieve the hash of the next block + if err := msgStream.Decode(&hash); err == rlp.EOL { + break + } else if err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Retrieve the requested block's receipts, skipping if unknown to us + results := core.GetBlockReceipts(pm.chaindb, hash, core.GetBlockNumber(pm.chaindb, hash)) + if results == nil { + if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + continue + } + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(results); err != nil { + glog.V(logger.Error).Infof("failed to encode receipt: %v", err) + } else { + receipts = append(receipts, encoded) + bytes += len(encoded) + } + } + return p.SendReceiptsRLP(receipts) + + case p.version >= eth63 && msg.Code == ReceiptsMsg: + // A batch of receipts arrived to one of our previous requests + var receipts [][]*types.Receipt + if err := msg.Decode(&receipts); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Deliver all to the downloader + if err := pm.downloader.DeliverReceipts(p.id, receipts); err != nil { + glog.V(logger.Debug).Infof("failed to deliver receipts: %v", err) + } + + case msg.Code == NewBlockHashesMsg: + var announces newBlockHashesData + if err := msg.Decode(&announces); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + // Mark the hashes as present at the remote node + for _, block := range announces { + p.MarkBlock(block.Hash) + } + // Schedule all the unknown hashes for retrieval + unknown := make(newBlockHashesData, 0, len(announces)) + for _, block := range announces { + if !pm.blockchain.HasBlock(block.Hash) { + unknown = append(unknown, block) + } + } + for _, block := range unknown { + pm.fetcher.Notify(p.id, block.Hash, block.Number, time.Now(), p.RequestOneHeader, p.RequestBodies) + } + + case msg.Code == NewBlockMsg: + // Retrieve and decode the propagated block + var request newBlockData + if err := msg.Decode(&request); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + request.Block.ReceivedAt = msg.ReceivedAt + request.Block.ReceivedFrom = p + + // Mark the peer as owning the block and schedule it for import + p.MarkBlock(request.Block.Hash()) + pm.fetcher.Enqueue(p.id, request.Block) + + // Assuming the block is importable by the peer, but possibly not yet done so, + // calculate the head hash and TD that the peer truly must have. + var ( + trueHead = request.Block.ParentHash() + trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty()) + ) + // Update the peers total difficulty if better than the previous + if _, td := p.Head(); trueTD.Cmp(td) > 0 { + p.SetHead(trueHead, trueTD) + + // Schedule a sync if above ours. Note, this will not fire a sync for a gap of + // a singe block (as the true TD is below the propagated block), however this + // scenario should easily be covered by the fetcher. + currentBlock := pm.blockchain.CurrentBlock() + if trueTD.Cmp(pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())) > 0 { + go pm.synchronise(p) + } + } + + case msg.Code == TxMsg: + // Transactions arrived, make sure we have a valid and fresh chain to handle them + if atomic.LoadUint32(&pm.synced) == 0 { + break + } + // Transactions can be processed, parse all of them and deliver to the pool + var txs []*types.Transaction + if err := msg.Decode(&txs); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + for i, tx := range txs { + // Validate and mark the remote transaction + if tx == nil { + return errResp(ErrDecode, "transaction %d is nil", i) + } + p.MarkTransaction(tx.Hash()) + } + pm.txpool.AddBatch(txs) + + default: + return errResp(ErrInvalidMsgCode, "%v", msg.Code) + } + return nil +} + +// BroadcastBlock will either propagate a block to a subset of it's peers, or +// will only announce it's availability (depending what's requested). +func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) { + hash := block.Hash() + peers := pm.peers.PeersWithoutBlock(hash) + + // If propagation is requested, send to a subset of the peer + if propagate { + // Calculate the TD of the block (it's not imported yet, so block.Td is not valid) + var td *big.Int + if parent := pm.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { + td = new(big.Int).Add(block.Difficulty(), pm.blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)) + } else { + glog.V(logger.Error).Infof("propagating dangling block #%d [%x]", block.NumberU64(), hash[:4]) + return + } + // Send the block to a subset of our peers + transfer := peers[:int(math.Sqrt(float64(len(peers))))] + for _, peer := range transfer { + peer.SendNewBlock(block, td) + } + glog.V(logger.Detail).Infof("propagated block %x to %d peers in %v", hash[:4], len(transfer), time.Since(block.ReceivedAt)) + } + // Otherwise if the block is indeed in out own chain, announce it + if pm.blockchain.HasBlock(hash) { + for _, peer := range peers { + peer.SendNewBlockHashes([]common.Hash{hash}, []uint64{block.NumberU64()}) + } + glog.V(logger.Detail).Infof("announced block %x to %d peers in %v", hash[:4], len(peers), time.Since(block.ReceivedAt)) + } +} + +// BroadcastTx will propagate a transaction to all peers which are not known to +// already have the given transaction. +func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction) { + // Broadcast transaction to a batch of peers not knowing about it + peers := pm.peers.PeersWithoutTx(hash) + //FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))] + for _, peer := range peers { + peer.SendTransactions(types.Transactions{tx}) + } + glog.V(logger.Detail).Infoln("broadcast tx to", len(peers), "peers") +} + +// Mined broadcast loop +func (self *ProtocolManager) minedBroadcastLoop() { + // automatically stops if unsubscribe + for obj := range self.minedBlockSub.Chan() { + switch ev := obj.Data.(type) { + case core.NewMinedBlockEvent: + self.BroadcastBlock(ev.Block, true) // First propagate block to peers + self.BroadcastBlock(ev.Block, false) // Only then announce to the rest + } + } +} + +func (self *ProtocolManager) txBroadcastLoop() { + // automatically stops if unsubscribe + for obj := range self.txSub.Chan() { + event := obj.Data.(core.TxPreEvent) + self.BroadcastTx(event.Tx.Hash(), event.Tx) + } +} + +// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known +// about the host peer. +type EthNodeInfo struct { + Network int `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3) + Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain + Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block + Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block +} + +// NodeInfo retrieves some protocol metadata about the running host node. +func (self *ProtocolManager) NodeInfo() *EthNodeInfo { + currentBlock := self.blockchain.CurrentBlock() + return &EthNodeInfo{ + Network: self.networkId, + Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), + Genesis: self.blockchain.Genesis().Hash(), + Head: currentBlock.Hash(), + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler_test.go b/vendor/github.com/ethereum/go-ethereum/eth/handler_test.go new file mode 100644 index 0000000..03d5404 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler_test.go @@ -0,0 +1,522 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" +) + +var bigTxGas = new(big.Int).SetUint64(params.TxGas) + +// Tests that protocol versions and modes of operations are matched up properly. +func TestProtocolCompatibility(t *testing.T) { + // Define the compatibility chart + tests := []struct { + version uint + fastSync bool + compatible bool + }{ + {61, false, true}, {62, false, true}, {63, false, true}, + {61, true, false}, {62, true, false}, {63, true, true}, + } + // Make sure anything we screw up is restored + backup := ProtocolVersions + defer func() { ProtocolVersions = backup }() + + // Try all available compatibility configs and check for errors + for i, tt := range tests { + ProtocolVersions = []uint{tt.version} + + pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil) + if pm != nil { + defer pm.Stop() + } + if (err == nil && !tt.compatible) || (err != nil && tt.compatible) { + t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible) + } + } +} + +// Tests that block headers can be retrieved from a remote chain based on user queries. +func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } +func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } + +func testGetBlockHeaders(t *testing.T, protocol int) { + pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil) + peer, _ := newTestPeer("peer", protocol, pm, true) + defer peer.close() + + // Create a "random" unknown hash for testing + var unknown common.Hash + for i := range unknown { + unknown[i] = byte(i) + } + // Create a batch of tests for various scenarios + limit := uint64(downloader.MaxHeaderFetch) + tests := []struct { + query *getBlockHeadersData // The query to execute for header retrieval + expect []common.Hash // The hashes of the block whose headers are expected + }{ + // A single random block should be retrievable by hash and number too + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, + []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, + []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, + }, + // Multiple headers should be retrievable in both directions + { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(limit / 2).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(limit / 2).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), + }, + }, + // Multiple headers with skip lists should be retrievable + { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(limit / 2).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(limit / 2).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), + pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), + }, + }, + // The chain endpoints should be retrievable + { + &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, + []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, + []common.Hash{pm.blockchain.CurrentBlock().Hash()}, + }, + // Ensure protocol limits are honored + { + &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, + pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), + }, + // Check that requesting more than available is handled gracefully + { + &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), + pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(4).Hash(), + pm.blockchain.GetBlockByNumber(0).Hash(), + }, + }, + // Check that requesting more than available is handled gracefully, even if mid skip + { + &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), + pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(4).Hash(), + pm.blockchain.GetBlockByNumber(1).Hash(), + }, + }, + // Check a corner case where requesting more can iterate past the endpoints + { + &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(2).Hash(), + pm.blockchain.GetBlockByNumber(1).Hash(), + pm.blockchain.GetBlockByNumber(0).Hash(), + }, + }, + // Check a corner case where skipping overflow loops back into the chain start + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(3).Hash(), + }, + }, + // Check a corner case where skipping overflow loops back to the same header + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, + []common.Hash{ + pm.blockchain.GetBlockByNumber(1).Hash(), + }, + }, + // Check that non existing headers aren't returned + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, + []common.Hash{}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, + []common.Hash{}, + }, + } + // Run each of the tests and verify the results against the chain + for i, tt := range tests { + // Collect the headers to expect in the response + headers := []*types.Header{} + for _, hash := range tt.expect { + headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) + } + // Send the hash request and verify the response + p2p.Send(peer.app, 0x03, tt.query) + if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { + t.Errorf("test %d: headers mismatch: %v", i, err) + } + // If the test used number origins, repeat with hashes as the too + if tt.query.Origin.Hash == (common.Hash{}) { + if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { + tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 + + p2p.Send(peer.app, 0x03, tt.query) + if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { + t.Errorf("test %d: headers mismatch: %v", i, err) + } + } + } + } +} + +// Tests that block contents can be retrieved from a remote chain based on their hashes. +func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } +func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } + +func testGetBlockBodies(t *testing.T, protocol int) { + pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil) + peer, _ := newTestPeer("peer", protocol, pm, true) + defer peer.close() + + // Create a batch of tests for various scenarios + limit := downloader.MaxBlockFetch + tests := []struct { + random int // Number of blocks to fetch randomly from the chain + explicit []common.Hash // Explicitly requested blocks + available []bool // Availability of explicitly requested blocks + expected int // Total number of existing blocks to expect + }{ + {1, nil, nil, 1}, // A single random block should be retrievable + {10, nil, nil, 10}, // Multiple random blocks should be retrievable + {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable + {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned + {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable + {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable + {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned + + // Existing and non-existing blocks interleaved should not cause problems + {0, []common.Hash{ + {}, + pm.blockchain.GetBlockByNumber(1).Hash(), + {}, + pm.blockchain.GetBlockByNumber(10).Hash(), + {}, + pm.blockchain.GetBlockByNumber(100).Hash(), + {}, + }, []bool{false, true, false, true, false, true, false}, 3}, + } + // Run each of the tests and verify the results against the chain + for i, tt := range tests { + // Collect the hashes to request, and the response to expect + hashes, seen := []common.Hash{}, make(map[int64]bool) + bodies := []*blockBody{} + + for j := 0; j < tt.random; j++ { + for { + num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) + if !seen[num] { + seen[num] = true + + block := pm.blockchain.GetBlockByNumber(uint64(num)) + hashes = append(hashes, block.Hash()) + if len(bodies) < tt.expected { + bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + break + } + } + } + for j, hash := range tt.explicit { + hashes = append(hashes, hash) + if tt.available[j] && len(bodies) < tt.expected { + block := pm.blockchain.GetBlockByHash(hash) + bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + } + // Send the hash request and verify the response + p2p.Send(peer.app, 0x05, hashes) + if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { + t.Errorf("test %d: bodies mismatch: %v", i, err) + } + } +} + +// Tests that the node state database can be retrieved based on hashes. +func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } + +func testGetNodeData(t *testing.T, protocol int) { + // Define three accounts to simulate transactions with + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + signer := types.HomesteadSigner{} + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + generator := func(i int, block *core.BlockGen) { + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // acc1Addr passes it on to account #2. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) + block.AddTx(tx1) + block.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + } + } + // Assemble the test environment + pm := newTestProtocolManagerMust(t, false, 4, generator, nil) + peer, _ := newTestPeer("peer", protocol, pm, true) + defer peer.close() + + // Fetch for now the entire chain db + hashes := []common.Hash{} + for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() { + if len(key) == len(common.Hash{}) { + hashes = append(hashes, common.BytesToHash(key)) + } + } + p2p.Send(peer.app, 0x0d, hashes) + msg, err := peer.app.ReadMsg() + if err != nil { + t.Fatalf("failed to read node data response: %v", err) + } + if msg.Code != 0x0e { + t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) + } + var data [][]byte + if err := msg.Decode(&data); err != nil { + t.Fatalf("failed to decode response node data: %v", err) + } + // Verify that all hashes correspond to the requested data, and reconstruct a state tree + for i, want := range hashes { + if hash := crypto.Keccak256Hash(data[i]); hash != want { + t.Errorf("data hash mismatch: have %x, want %x", hash, want) + } + } + statedb, _ := ethdb.NewMemDatabase() + for i := 0; i < len(data); i++ { + statedb.Put(hashes[i].Bytes(), data[i]) + } + accounts := []common.Address{testBank.Address, acc1Addr, acc2Addr} + for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { + trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), statedb) + + for j, acc := range accounts { + state, _ := pm.blockchain.State() + bw := state.GetBalance(acc) + bh := trie.GetBalance(acc) + + if (bw != nil && bh == nil) || (bw == nil && bh != nil) { + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + } + if bw != nil && bh != nil && bw.Cmp(bw) != 0 { + t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) + } + } + } +} + +// Tests that the transaction receipts can be retrieved based on hashes. +func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } + +func testGetReceipt(t *testing.T, protocol int) { + // Define three accounts to simulate transactions with + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + + signer := types.HomesteadSigner{} + // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) + generator := func(i int, block *core.BlockGen) { + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // acc1Addr passes it on to account #2. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) + block.AddTx(tx1) + block.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + } + } + // Assemble the test environment + pm := newTestProtocolManagerMust(t, false, 4, generator, nil) + peer, _ := newTestPeer("peer", protocol, pm, true) + defer peer.close() + + // Collect the hashes to request, and the response to expect + hashes, receipts := []common.Hash{}, []types.Receipts{} + for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { + block := pm.blockchain.GetBlockByNumber(i) + + hashes = append(hashes, block.Hash()) + receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash(), block.NumberU64())) + } + // Send the hash request and verify the response + p2p.Send(peer.app, 0x0f, hashes) + if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { + t.Errorf("receipts mismatch: %v", err) + } +} + +// Tests that post eth protocol handshake, DAO fork-enabled clients also execute +// a DAO "challenge" verifying each others' DAO fork headers to ensure they're on +// compatible chains. +func TestDAOChallengeNoVsNo(t *testing.T) { testDAOChallenge(t, false, false, false) } +func TestDAOChallengeNoVsPro(t *testing.T) { testDAOChallenge(t, false, true, false) } +func TestDAOChallengeProVsNo(t *testing.T) { testDAOChallenge(t, true, false, false) } +func TestDAOChallengeProVsPro(t *testing.T) { testDAOChallenge(t, true, true, false) } +func TestDAOChallengeNoVsTimeout(t *testing.T) { testDAOChallenge(t, false, false, true) } +func TestDAOChallengeProVsTimeout(t *testing.T) { testDAOChallenge(t, true, true, true) } + +func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool) { + // Reduce the DAO handshake challenge timeout + if timeout { + defer func(old time.Duration) { daoChallengeTimeout = old }(daoChallengeTimeout) + daoChallengeTimeout = 500 * time.Millisecond + } + // Create a DAO aware protocol manager + var ( + evmux = new(event.TypeMux) + pow = new(core.FakePow) + db, _ = ethdb.NewMemDatabase() + genesis = core.WriteGenesisBlockForTesting(db) + config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} + blockchain, _ = core.NewBlockChain(db, config, pow, evmux, vm.Config{}) + ) + pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db) + if err != nil { + t.Fatalf("failed to start test protocol manager: %v", err) + } + pm.Start() + defer pm.Stop() + + // Connect a new peer and check that we receive the DAO challenge + peer, _ := newTestPeer("peer", eth63, pm, true) + defer peer.close() + + challenge := &getBlockHeadersData{ + Origin: hashOrNumber{Number: config.DAOForkBlock.Uint64()}, + Amount: 1, + Skip: 0, + Reverse: false, + } + if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil { + t.Fatalf("challenge mismatch: %v", err) + } + // Create a block to reply to the challenge if no timeout is simulated + if !timeout { + blocks, _ := core.GenerateChain(¶ms.ChainConfig{}, genesis, db, 1, func(i int, block *core.BlockGen) { + if remoteForked { + block.SetExtra(params.DAOForkBlockExtra) + } + }) + if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{blocks[0].Header()}); err != nil { + t.Fatalf("failed to answer challenge: %v", err) + } + time.Sleep(100 * time.Millisecond) // Sleep to avoid the verification racing with the drops + } else { + // Otherwise wait until the test timeout passes + time.Sleep(daoChallengeTimeout + 500*time.Millisecond) + } + // Verify that depending on fork side, the remote peer is maintained or dropped + if localForked == remoteForked && !timeout { + if peers := pm.peers.Len(); peers != 1 { + t.Fatalf("peer count mismatch: have %d, want %d", peers, 1) + } + } else { + if peers := pm.peers.Len(); peers != 0 { + t.Fatalf("peer count mismatch: have %d, want %d", peers, 0) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/helper_test.go b/vendor/github.com/ethereum/go-ethereum/eth/helper_test.go new file mode 100644 index 0000000..0861c88 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/helper_test.go @@ -0,0 +1,191 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains some shares testing functionality, common to multiple +// different files and modules being tested. + +package eth + +import ( + "crypto/ecdsa" + "crypto/rand" + "math/big" + "sort" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" +) + +var ( + testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testBank = core.GenesisAccount{ + Address: crypto.PubkeyToAddress(testBankKey.PublicKey), + Balance: big.NewInt(1000000), + } +) + +// newTestProtocolManager creates a new protocol manager for testing purposes, +// with the given number of blocks already known, and potential notification +// channels for different events. +func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) { + var ( + evmux = new(event.TypeMux) + pow = new(core.FakePow) + db, _ = ethdb.NewMemDatabase() + genesis = core.WriteGenesisBlockForTesting(db, testBank) + chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker + blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux, vm.Config{}) + ) + chain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) + if _, err := blockchain.InsertChain(chain); err != nil { + panic(err) + } + + pm, err := NewProtocolManager(chainConfig, fastSync, NetworkId, 1000, evmux, &testTxPool{added: newtx}, pow, blockchain, db) + if err != nil { + return nil, err + } + pm.Start() + return pm, nil +} + +// newTestProtocolManagerMust creates a new protocol manager for testing purposes, +// with the given number of blocks already known, and potential notification +// channels for different events. In case of an error, the constructor force- +// fails the test. +func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { + pm, err := newTestProtocolManager(fastSync, blocks, generator, newtx) + if err != nil { + t.Fatalf("Failed to create protocol manager: %v", err) + } + return pm +} + +// testTxPool is a fake, helper transaction pool for testing purposes +type testTxPool struct { + pool []*types.Transaction // Collection of all transactions + added chan<- []*types.Transaction // Notification channel for new transactions + + lock sync.RWMutex // Protects the transaction pool +} + +// AddBatch appends a batch of transactions to the pool, and notifies any +// listeners if the addition channel is non nil +func (p *testTxPool) AddBatch(txs []*types.Transaction) error { + p.lock.Lock() + defer p.lock.Unlock() + + p.pool = append(p.pool, txs...) + if p.added != nil { + p.added <- txs + } + + return nil +} + +// Pending returns all the transactions known to the pool +func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) { + p.lock.RLock() + defer p.lock.RUnlock() + + batches := make(map[common.Address]types.Transactions) + for _, tx := range p.pool { + from, _ := types.Sender(types.HomesteadSigner{}, tx) + batches[from] = append(batches[from], tx) + } + for _, batch := range batches { + sort.Sort(types.TxByNonce(batch)) + } + return batches, nil +} + +// newTestTransaction create a new dummy transaction. +func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction { + tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from) + return tx +} + +// testPeer is a simulated peer to allow testing direct network calls. +type testPeer struct { + net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging + app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side + *peer +} + +// newTestPeer creates a new peer registered at the given protocol manager. +func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id discover.NodeID + rand.Read(id[:]) + + peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net) + + // Start the peer on a new thread + errc := make(chan error, 1) + go func() { + select { + case pm.newPeerCh <- peer: + errc <- pm.handle(peer) + case <-pm.quitSync: + errc <- p2p.DiscQuitting + } + }() + tp := &testPeer{app: app, net: net, peer: peer} + // Execute any implicitly requested handshakes and return + if shake { + td, head, genesis := pm.blockchain.Status() + tp.handshake(nil, td, head, genesis) + } + return tp, errc +} + +// handshake simulates a trivial handshake that expects the same state from the +// remote side as we are simulating locally. +func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) { + msg := &statusData{ + ProtocolVersion: uint32(p.version), + NetworkId: uint32(NetworkId), + TD: td, + CurrentBlock: head, + GenesisBlock: genesis, + } + if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil { + t.Fatalf("status recv: %v", err) + } + if err := p2p.Send(p.app, StatusMsg, msg); err != nil { + t.Fatalf("status send: %v", err) + } +} + +// close terminates the local side of the peer, notifying the remote protocol +// manager of termination. +func (p *testPeer) close() { + p.app.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/metrics.go b/vendor/github.com/ethereum/go-ethereum/eth/metrics.go new file mode 100644 index 0000000..5fa2597 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/metrics.go @@ -0,0 +1,139 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" +) + +var ( + propTxnInPacketsMeter = metrics.NewMeter("eth/prop/txns/in/packets") + propTxnInTrafficMeter = metrics.NewMeter("eth/prop/txns/in/traffic") + propTxnOutPacketsMeter = metrics.NewMeter("eth/prop/txns/out/packets") + propTxnOutTrafficMeter = metrics.NewMeter("eth/prop/txns/out/traffic") + propHashInPacketsMeter = metrics.NewMeter("eth/prop/hashes/in/packets") + propHashInTrafficMeter = metrics.NewMeter("eth/prop/hashes/in/traffic") + propHashOutPacketsMeter = metrics.NewMeter("eth/prop/hashes/out/packets") + propHashOutTrafficMeter = metrics.NewMeter("eth/prop/hashes/out/traffic") + propBlockInPacketsMeter = metrics.NewMeter("eth/prop/blocks/in/packets") + propBlockInTrafficMeter = metrics.NewMeter("eth/prop/blocks/in/traffic") + propBlockOutPacketsMeter = metrics.NewMeter("eth/prop/blocks/out/packets") + propBlockOutTrafficMeter = metrics.NewMeter("eth/prop/blocks/out/traffic") + reqHeaderInPacketsMeter = metrics.NewMeter("eth/req/headers/in/packets") + reqHeaderInTrafficMeter = metrics.NewMeter("eth/req/headers/in/traffic") + reqHeaderOutPacketsMeter = metrics.NewMeter("eth/req/headers/out/packets") + reqHeaderOutTrafficMeter = metrics.NewMeter("eth/req/headers/out/traffic") + reqBodyInPacketsMeter = metrics.NewMeter("eth/req/bodies/in/packets") + reqBodyInTrafficMeter = metrics.NewMeter("eth/req/bodies/in/traffic") + reqBodyOutPacketsMeter = metrics.NewMeter("eth/req/bodies/out/packets") + reqBodyOutTrafficMeter = metrics.NewMeter("eth/req/bodies/out/traffic") + reqStateInPacketsMeter = metrics.NewMeter("eth/req/states/in/packets") + reqStateInTrafficMeter = metrics.NewMeter("eth/req/states/in/traffic") + reqStateOutPacketsMeter = metrics.NewMeter("eth/req/states/out/packets") + reqStateOutTrafficMeter = metrics.NewMeter("eth/req/states/out/traffic") + reqReceiptInPacketsMeter = metrics.NewMeter("eth/req/receipts/in/packets") + reqReceiptInTrafficMeter = metrics.NewMeter("eth/req/receipts/in/traffic") + reqReceiptOutPacketsMeter = metrics.NewMeter("eth/req/receipts/out/packets") + reqReceiptOutTrafficMeter = metrics.NewMeter("eth/req/receipts/out/traffic") + miscInPacketsMeter = metrics.NewMeter("eth/misc/in/packets") + miscInTrafficMeter = metrics.NewMeter("eth/misc/in/traffic") + miscOutPacketsMeter = metrics.NewMeter("eth/misc/out/packets") + miscOutTrafficMeter = metrics.NewMeter("eth/misc/out/traffic") +) + +// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of +// accumulating the above defined metrics based on the data stream contents. +type meteredMsgReadWriter struct { + p2p.MsgReadWriter // Wrapped message stream to meter + version int // Protocol version to select correct meters +} + +// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the +// metrics system is disabled, this function returns the original object. +func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { + if !metrics.Enabled { + return rw + } + return &meteredMsgReadWriter{MsgReadWriter: rw} +} + +// Init sets the protocol version used by the stream to know which meters to +// increment in case of overlapping message ids between protocol versions. +func (rw *meteredMsgReadWriter) Init(version int) { + rw.version = version +} + +func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) { + // Read the message and short circuit in case of an error + msg, err := rw.MsgReadWriter.ReadMsg() + if err != nil { + return msg, err + } + // Account for the data traffic + packets, traffic := miscInPacketsMeter, miscInTrafficMeter + switch { + case msg.Code == BlockHeadersMsg: + packets, traffic = reqHeaderInPacketsMeter, reqHeaderInTrafficMeter + case msg.Code == BlockBodiesMsg: + packets, traffic = reqBodyInPacketsMeter, reqBodyInTrafficMeter + + case rw.version >= eth63 && msg.Code == NodeDataMsg: + packets, traffic = reqStateInPacketsMeter, reqStateInTrafficMeter + case rw.version >= eth63 && msg.Code == ReceiptsMsg: + packets, traffic = reqReceiptInPacketsMeter, reqReceiptInTrafficMeter + + case msg.Code == NewBlockHashesMsg: + packets, traffic = propHashInPacketsMeter, propHashInTrafficMeter + case msg.Code == NewBlockMsg: + packets, traffic = propBlockInPacketsMeter, propBlockInTrafficMeter + case msg.Code == TxMsg: + packets, traffic = propTxnInPacketsMeter, propTxnInTrafficMeter + } + packets.Mark(1) + traffic.Mark(int64(msg.Size)) + + return msg, err +} + +func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error { + // Account for the data traffic + packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter + switch { + case msg.Code == BlockHeadersMsg: + packets, traffic = reqHeaderOutPacketsMeter, reqHeaderOutTrafficMeter + case msg.Code == BlockBodiesMsg: + packets, traffic = reqBodyOutPacketsMeter, reqBodyOutTrafficMeter + + case rw.version >= eth63 && msg.Code == NodeDataMsg: + packets, traffic = reqStateOutPacketsMeter, reqStateOutTrafficMeter + case rw.version >= eth63 && msg.Code == ReceiptsMsg: + packets, traffic = reqReceiptOutPacketsMeter, reqReceiptOutTrafficMeter + + case msg.Code == NewBlockHashesMsg: + packets, traffic = propHashOutPacketsMeter, propHashOutTrafficMeter + case msg.Code == NewBlockMsg: + packets, traffic = propBlockOutPacketsMeter, propBlockOutTrafficMeter + case msg.Code == TxMsg: + packets, traffic = propTxnOutPacketsMeter, propTxnOutTrafficMeter + } + packets.Mark(1) + traffic.Mark(int64(msg.Size)) + + // Send the packet to the p2p layer + return rw.MsgReadWriter.WriteMsg(msg) +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/peer.go b/vendor/github.com/ethereum/go-ethereum/eth/peer.go new file mode 100644 index 0000000..aa85631 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/peer.go @@ -0,0 +1,419 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "errors" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "gopkg.in/fatih/set.v0" +) + +var ( + errClosed = errors.New("peer set is closed") + errAlreadyRegistered = errors.New("peer is already registered") + errNotRegistered = errors.New("peer is not registered") +) + +const ( + maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS) + maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS) + handshakeTimeout = 5 * time.Second +) + +// PeerInfo represents a short summary of the Ethereum sub-protocol metadata known +// about a connected peer. +type PeerInfo struct { + Version int `json:"version"` // Ethereum protocol version negotiated + Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain + Head string `json:"head"` // SHA3 hash of the peer's best owned block +} + +type peer struct { + id string + + *p2p.Peer + rw p2p.MsgReadWriter + + version int // Protocol version negotiated + forkDrop *time.Timer // Timed connection dropper if forks aren't validated in time + + head common.Hash + td *big.Int + lock sync.RWMutex + + knownTxs *set.Set // Set of transaction hashes known to be known by this peer + knownBlocks *set.Set // Set of block hashes known to be known by this peer +} + +func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { + id := p.ID() + + return &peer{ + Peer: p, + rw: rw, + version: version, + id: fmt.Sprintf("%x", id[:8]), + knownTxs: set.New(), + knownBlocks: set.New(), + } +} + +// Info gathers and returns a collection of metadata known about a peer. +func (p *peer) Info() *PeerInfo { + hash, td := p.Head() + + return &PeerInfo{ + Version: p.version, + Difficulty: td, + Head: hash.Hex(), + } +} + +// Head retrieves a copy of the current head hash and total difficulty of the +// peer. +func (p *peer) Head() (hash common.Hash, td *big.Int) { + p.lock.RLock() + defer p.lock.RUnlock() + + copy(hash[:], p.head[:]) + return hash, new(big.Int).Set(p.td) +} + +// SetHead updates the head hash and total difficulty of the peer. +func (p *peer) SetHead(hash common.Hash, td *big.Int) { + p.lock.Lock() + defer p.lock.Unlock() + + copy(p.head[:], hash[:]) + p.td.Set(td) +} + +// MarkBlock marks a block as known for the peer, ensuring that the block will +// never be propagated to this particular peer. +func (p *peer) MarkBlock(hash common.Hash) { + // If we reached the memory allowance, drop a previously known block hash + for p.knownBlocks.Size() >= maxKnownBlocks { + p.knownBlocks.Pop() + } + p.knownBlocks.Add(hash) +} + +// MarkTransaction marks a transaction as known for the peer, ensuring that it +// will never be propagated to this particular peer. +func (p *peer) MarkTransaction(hash common.Hash) { + // If we reached the memory allowance, drop a previously known transaction hash + for p.knownTxs.Size() >= maxKnownTxs { + p.knownTxs.Pop() + } + p.knownTxs.Add(hash) +} + +// SendTransactions sends transactions to the peer and includes the hashes +// in its transaction hash set for future reference. +func (p *peer) SendTransactions(txs types.Transactions) error { + for _, tx := range txs { + p.knownTxs.Add(tx.Hash()) + } + return p2p.Send(p.rw, TxMsg, txs) +} + +// SendNewBlockHashes announces the availability of a number of blocks through +// a hash notification. +func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { + for _, hash := range hashes { + p.knownBlocks.Add(hash) + } + request := make(newBlockHashesData, len(hashes)) + for i := 0; i < len(hashes); i++ { + request[i].Hash = hashes[i] + request[i].Number = numbers[i] + } + return p2p.Send(p.rw, NewBlockHashesMsg, request) +} + +// SendNewBlock propagates an entire block to a remote peer. +func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error { + p.knownBlocks.Add(block.Hash()) + return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td}) +} + +// SendBlockHeaders sends a batch of block headers to the remote peer. +func (p *peer) SendBlockHeaders(headers []*types.Header) error { + return p2p.Send(p.rw, BlockHeadersMsg, headers) +} + +// SendBlockBodies sends a batch of block contents to the remote peer. +func (p *peer) SendBlockBodies(bodies []*blockBody) error { + return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) +} + +// SendBlockBodiesRLP sends a batch of block contents to the remote peer from +// an already RLP encoded format. +func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error { + return p2p.Send(p.rw, BlockBodiesMsg, bodies) +} + +// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the +// hashes requested. +func (p *peer) SendNodeData(data [][]byte) error { + return p2p.Send(p.rw, NodeDataMsg, data) +} + +// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the +// ones requested from an already RLP encoded format. +func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error { + return p2p.Send(p.rw, ReceiptsMsg, receipts) +} + +// RequestHeaders is a wrapper around the header query functions to fetch a +// single header. It is used solely by the fetcher. +func (p *peer) RequestOneHeader(hash common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching a single header: %x", p, hash) + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false}) +} + +// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the +// specified header query, based on the hash of an origin block. +func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { + glog.V(logger.Debug).Infof("%v fetching %d headers from %x, skipping %d (reverse = %v)", p, amount, origin[:4], skip, reverse) + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +} + +// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the +// specified header query, based on the number of an origin block. +func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { + glog.V(logger.Debug).Infof("%v fetching %d headers from #%d, skipping %d (reverse = %v)", p, amount, origin, skip, reverse) + return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +} + +// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes +// specified. +func (p *peer) RequestBodies(hashes []common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching %d block bodies", p, len(hashes)) + return p2p.Send(p.rw, GetBlockBodiesMsg, hashes) +} + +// RequestNodeData fetches a batch of arbitrary data from a node's known state +// data, corresponding to the specified hashes. +func (p *peer) RequestNodeData(hashes []common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(hashes)) + return p2p.Send(p.rw, GetNodeDataMsg, hashes) +} + +// RequestReceipts fetches a batch of transaction receipts from a remote node. +func (p *peer) RequestReceipts(hashes []common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching %v receipts", p, len(hashes)) + return p2p.Send(p.rw, GetReceiptsMsg, hashes) +} + +// Handshake executes the eth protocol handshake, negotiating version number, +// network IDs, difficulties, head and genesis blocks. +func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error { + // Send out own handshake in a new thread + errc := make(chan error, 2) + var status statusData // safe to read after two values have been received from errc + + go func() { + errc <- p2p.Send(p.rw, StatusMsg, &statusData{ + ProtocolVersion: uint32(p.version), + NetworkId: uint32(network), + TD: td, + CurrentBlock: head, + GenesisBlock: genesis, + }) + }() + go func() { + errc <- p.readStatus(network, &status, genesis) + }() + timeout := time.NewTimer(handshakeTimeout) + defer timeout.Stop() + for i := 0; i < 2; i++ { + select { + case err := <-errc: + if err != nil { + return err + } + case <-timeout.C: + return p2p.DiscReadTimeout + } + } + p.td, p.head = status.TD, status.CurrentBlock + return nil +} + +func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) { + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + if msg.Code != StatusMsg { + return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) + } + if msg.Size > ProtocolMaxMsgSize { + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + // Decode the handshake and make sure everything matches + if err := msg.Decode(&status); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + if status.GenesisBlock != genesis { + return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis) + } + if int(status.NetworkId) != network { + return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network) + } + if int(status.ProtocolVersion) != p.version { + return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) + } + return nil +} + +// String implements fmt.Stringer. +func (p *peer) String() string { + return fmt.Sprintf("Peer %s [%s]", p.id, + fmt.Sprintf("eth/%2d", p.version), + ) +} + +// peerSet represents the collection of active peers currently participating in +// the Ethereum sub-protocol. +type peerSet struct { + peers map[string]*peer + lock sync.RWMutex + closed bool +} + +// newPeerSet creates a new peer set to track the active participants. +func newPeerSet() *peerSet { + return &peerSet{ + peers: make(map[string]*peer), + } +} + +// Register injects a new peer into the working set, or returns an error if the +// peer is already known. +func (ps *peerSet) Register(p *peer) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if ps.closed { + return errClosed + } + if _, ok := ps.peers[p.id]; ok { + return errAlreadyRegistered + } + ps.peers[p.id] = p + return nil +} + +// Unregister removes a remote peer from the active set, disabling any further +// actions to/from that particular entity. +func (ps *peerSet) Unregister(id string) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if _, ok := ps.peers[id]; !ok { + return errNotRegistered + } + delete(ps.peers, id) + return nil +} + +// Peer retrieves the registered peer with the given id. +func (ps *peerSet) Peer(id string) *peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.peers[id] +} + +// Len returns if the current number of peers in the set. +func (ps *peerSet) Len() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return len(ps.peers) +} + +// PeersWithoutBlock retrieves a list of peers that do not have a given block in +// their set of known hashes. +func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*peer, 0, len(ps.peers)) + for _, p := range ps.peers { + if !p.knownBlocks.Has(hash) { + list = append(list, p) + } + } + return list +} + +// PeersWithoutTx retrieves a list of peers that do not have a given transaction +// in their set of known hashes. +func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*peer, 0, len(ps.peers)) + for _, p := range ps.peers { + if !p.knownTxs.Has(hash) { + list = append(list, p) + } + } + return list +} + +// BestPeer retrieves the known peer with the currently highest total difficulty. +func (ps *peerSet) BestPeer() *peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + var ( + bestPeer *peer + bestTd *big.Int + ) + for _, p := range ps.peers { + if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { + bestPeer, bestTd = p, td + } + } + return bestPeer +} + +// Close disconnects all peers. +// No new peers can be registered after Close has returned. +func (ps *peerSet) Close() { + ps.lock.Lock() + defer ps.lock.Unlock() + + for _, p := range ps.peers { + p.Disconnect(p2p.DiscQuitting) + } + ps.closed = true +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go new file mode 100644 index 0000000..7d22b33 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go @@ -0,0 +1,180 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// Constants to match up protocol versions and messages +const ( + eth62 = 62 + eth63 = 63 +) + +// Official short name of the protocol used during capability negotiation. +var ProtocolName = "eth" + +// Supported versions of the eth protocol (first is primary). +var ProtocolVersions = []uint{eth63, eth62} + +// Number of implemented message corresponding to different protocol versions. +var ProtocolLengths = []uint64{17, 8} + +const ( + NetworkId = 1 + ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message +) + +// eth protocol message codes +const ( + // Protocol messages belonging to eth/62 + StatusMsg = 0x00 + NewBlockHashesMsg = 0x01 + TxMsg = 0x02 + GetBlockHeadersMsg = 0x03 + BlockHeadersMsg = 0x04 + GetBlockBodiesMsg = 0x05 + BlockBodiesMsg = 0x06 + NewBlockMsg = 0x07 + + // Protocol messages belonging to eth/63 + GetNodeDataMsg = 0x0d + NodeDataMsg = 0x0e + GetReceiptsMsg = 0x0f + ReceiptsMsg = 0x10 +) + +type errCode int + +const ( + ErrMsgTooLarge = iota + ErrDecode + ErrInvalidMsgCode + ErrProtocolVersionMismatch + ErrNetworkIdMismatch + ErrGenesisBlockMismatch + ErrNoStatusMsg + ErrExtraStatusMsg + ErrSuspendedPeer +) + +func (e errCode) String() string { + return errorToString[int(e)] +} + +// XXX change once legacy code is out +var errorToString = map[int]string{ + ErrMsgTooLarge: "Message too long", + ErrDecode: "Invalid message", + ErrInvalidMsgCode: "Invalid message code", + ErrProtocolVersionMismatch: "Protocol version mismatch", + ErrNetworkIdMismatch: "NetworkId mismatch", + ErrGenesisBlockMismatch: "Genesis block mismatch", + ErrNoStatusMsg: "No status message", + ErrExtraStatusMsg: "Extra status message", + ErrSuspendedPeer: "Suspended peer", +} + +type txPool interface { + // AddBatch should add the given transactions to the pool. + AddBatch([]*types.Transaction) error + + // Pending should return pending transactions. + // The slice should be modifiable by the caller. + Pending() (map[common.Address]types.Transactions, error) +} + +// statusData is the network packet for the status message. +type statusData struct { + ProtocolVersion uint32 + NetworkId uint32 + TD *big.Int + CurrentBlock common.Hash + GenesisBlock common.Hash +} + +// newBlockHashesData is the network packet for the block announcements. +type newBlockHashesData []struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced +} + +// getBlockHeadersData represents a block header query. +type getBlockHeadersData struct { + Origin hashOrNumber // Block from which to retrieve headers + Amount uint64 // Maximum number of headers to retrieve + Skip uint64 // Blocks to skip between consecutive headers + Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) +} + +// hashOrNumber is a combined field for specifying an origin block. +type hashOrNumber struct { + Hash common.Hash // Block hash from which to retrieve headers (excludes Number) + Number uint64 // Block hash from which to retrieve headers (excludes Hash) +} + +// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the +// two contained union fields. +func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { + if hn.Hash == (common.Hash{}) { + return rlp.Encode(w, hn.Number) + } + if hn.Number != 0 { + return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) + } + return rlp.Encode(w, hn.Hash) +} + +// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents +// into either a block hash or a block number. +func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + origin, err := s.Raw() + if err == nil { + switch { + case size == 32: + err = rlp.DecodeBytes(origin, &hn.Hash) + case size <= 8: + err = rlp.DecodeBytes(origin, &hn.Number) + default: + err = fmt.Errorf("invalid input size %d for origin", size) + } + } + return err +} + +// newBlockData is the network packet for the block propagation message. +type newBlockData struct { + Block *types.Block + TD *big.Int +} + +// blockBody represents the data content of a single block. +type blockBody struct { + Transactions []*types.Transaction // Transactions contained within a block + Uncles []*types.Header // Uncles contained within a block +} + +// blockBodiesData is the network packet for block content distribution. +type blockBodiesData []*blockBody diff --git a/vendor/github.com/ethereum/go-ethereum/eth/protocol_test.go b/vendor/github.com/ethereum/go-ethereum/eth/protocol_test.go new file mode 100644 index 0000000..3b80564 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/protocol_test.go @@ -0,0 +1,219 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "fmt" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + // glog.SetToStderr(true) + // glog.SetV(6) +} + +var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +// Tests that handshake failures are detected and reported correctly. +func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) } +func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) } + +func testStatusMsgErrors(t *testing.T, protocol int) { + pm := newTestProtocolManagerMust(t, false, 0, nil, nil) + td, currentBlock, genesis := pm.blockchain.Status() + defer pm.Stop() + + tests := []struct { + code uint64 + data interface{} + wantError error + }{ + { + code: TxMsg, data: []interface{}{}, + wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), + }, + { + code: StatusMsg, data: statusData{10, NetworkId, td, currentBlock, genesis}, + wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol), + }, + { + code: StatusMsg, data: statusData{uint32(protocol), 999, td, currentBlock, genesis}, + wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"), + }, + { + code: StatusMsg, data: statusData{uint32(protocol), NetworkId, td, currentBlock, common.Hash{3}}, + wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000000000000000000000000000000000000000000000000000 (!= %x)", genesis), + }, + } + + for i, test := range tests { + p, errc := newTestPeer("peer", protocol, pm, false) + // The send call might hang until reset because + // the protocol might not read the payload. + go p2p.Send(p.app, test.code, test.data) + + select { + case err := <-errc: + if err == nil { + t.Errorf("test %d: protocol returned nil error, want %q", i, test.wantError) + } else if err.Error() != test.wantError.Error() { + t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.wantError) + } + case <-time.After(2 * time.Second): + t.Errorf("protocol did not shut down within 2 seconds") + } + p.close() + } +} + +// This test checks that received transactions are added to the local pool. +func TestRecvTransactions62(t *testing.T) { testRecvTransactions(t, 62) } +func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) } + +func testRecvTransactions(t *testing.T, protocol int) { + txAdded := make(chan []*types.Transaction) + pm := newTestProtocolManagerMust(t, false, 0, nil, txAdded) + pm.synced = 1 // mark synced to accept transactions + p, _ := newTestPeer("peer", protocol, pm, true) + defer pm.Stop() + defer p.close() + + tx := newTestTransaction(testAccount, 0, 0) + if err := p2p.Send(p.app, TxMsg, []interface{}{tx}); err != nil { + t.Fatalf("send error: %v", err) + } + select { + case added := <-txAdded: + if len(added) != 1 { + t.Errorf("wrong number of added transactions: got %d, want 1", len(added)) + } else if added[0].Hash() != tx.Hash() { + t.Errorf("added wrong tx hash: got %v, want %v", added[0].Hash(), tx.Hash()) + } + case <-time.After(2 * time.Second): + t.Errorf("no TxPreEvent received within 2 seconds") + } +} + +// This test checks that pending transactions are sent. +func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) } +func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) } + +func testSendTransactions(t *testing.T, protocol int) { + pm := newTestProtocolManagerMust(t, false, 0, nil, nil) + defer pm.Stop() + + // Fill the pool with big transactions. + const txsize = txsyncPackSize / 10 + alltxs := make([]*types.Transaction, 100) + for nonce := range alltxs { + alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), txsize) + } + pm.txpool.AddBatch(alltxs) + + // Connect several peers. They should all receive the pending transactions. + var wg sync.WaitGroup + checktxs := func(p *testPeer) { + defer wg.Done() + defer p.close() + seen := make(map[common.Hash]bool) + for _, tx := range alltxs { + seen[tx.Hash()] = false + } + for n := 0; n < len(alltxs) && !t.Failed(); { + var txs []*types.Transaction + msg, err := p.app.ReadMsg() + if err != nil { + t.Errorf("%v: read error: %v", p.Peer, err) + } else if msg.Code != TxMsg { + t.Errorf("%v: got code %d, want TxMsg", p.Peer, msg.Code) + } + if err := msg.Decode(&txs); err != nil { + t.Errorf("%v: %v", p.Peer, err) + } + for _, tx := range txs { + hash := tx.Hash() + seentx, want := seen[hash] + if seentx { + t.Errorf("%v: got tx more than once: %x", p.Peer, hash) + } + if !want { + t.Errorf("%v: got unexpected tx: %x", p.Peer, hash) + } + seen[hash] = true + n++ + } + } + } + for i := 0; i < 3; i++ { + p, _ := newTestPeer(fmt.Sprintf("peer #%d", i), protocol, pm, true) + wg.Add(1) + go checktxs(p) + } + wg.Wait() +} + +// Tests that the custom union field encoder and decoder works correctly. +func TestGetBlockHeadersDataEncodeDecode(t *testing.T) { + // Create a "random" hash for testing + var hash common.Hash + for i := range hash { + hash[i] = byte(i) + } + // Assemble some table driven tests + tests := []struct { + packet *getBlockHeadersData + fail bool + }{ + // Providing the origin as either a hash or a number should both work + {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Number: 314}}}, + {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}}}, + + // Providing arbitrary query field should also work + {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Number: 314}, Amount: 314, Skip: 1, Reverse: true}}, + {fail: false, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: 314, Skip: 1, Reverse: true}}, + + // Providing both the origin hash and origin number must fail + {fail: true, packet: &getBlockHeadersData{Origin: hashOrNumber{Hash: hash, Number: 314}}}, + } + // Iterate over each of the tests and try to encode and then decode + for i, tt := range tests { + bytes, err := rlp.EncodeToBytes(tt.packet) + if err != nil && !tt.fail { + t.Fatalf("test %d: failed to encode packet: %v", i, err) + } else if err == nil && tt.fail { + t.Fatalf("test %d: encode should have failed", i) + } + if !tt.fail { + packet := new(getBlockHeadersData) + if err := rlp.DecodeBytes(bytes, packet); err != nil { + t.Fatalf("test %d: failed to decode packet: %v", i, err) + } + if packet.Origin.Hash != tt.packet.Origin.Hash || packet.Origin.Number != tt.packet.Origin.Number || packet.Amount != tt.packet.Amount || + packet.Skip != tt.packet.Skip || packet.Reverse != tt.packet.Reverse { + t.Fatalf("test %d: encode decode mismatch: have %+v, want %+v", i, packet, tt.packet) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/sync.go b/vendor/github.com/ethereum/go-ethereum/eth/sync.go new file mode 100644 index 0000000..373cc20 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/sync.go @@ -0,0 +1,194 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/rand" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" +) + +const ( + forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available + minDesiredPeerCount = 5 // Amount of peers desired to start syncing + + // This is the target size for the packs of transactions sent by txsyncLoop. + // A pack can get larger than this if a single transactions exceeds this size. + txsyncPackSize = 100 * 1024 +) + +type txsync struct { + p *peer + txs []*types.Transaction +} + +// syncTransactions starts sending all currently pending transactions to the given peer. +func (pm *ProtocolManager) syncTransactions(p *peer) { + var txs types.Transactions + pending, _ := pm.txpool.Pending() + for _, batch := range pending { + txs = append(txs, batch...) + } + if len(txs) == 0 { + return + } + select { + case pm.txsyncCh <- &txsync{p, txs}: + case <-pm.quitSync: + } +} + +// txsyncLoop takes care of the initial transaction sync for each new +// connection. When a new peer appears, we relay all currently pending +// transactions. In order to minimise egress bandwidth usage, we send +// the transactions in small packs to one peer at a time. +func (pm *ProtocolManager) txsyncLoop() { + var ( + pending = make(map[discover.NodeID]*txsync) + sending = false // whether a send is active + pack = new(txsync) // the pack that is being sent + done = make(chan error, 1) // result of the send + ) + + // send starts a sending a pack of transactions from the sync. + send := func(s *txsync) { + // Fill pack with transactions up to the target size. + size := common.StorageSize(0) + pack.p = s.p + pack.txs = pack.txs[:0] + for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { + pack.txs = append(pack.txs, s.txs[i]) + size += s.txs[i].Size() + } + // Remove the transactions that will be sent. + s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])] + if len(s.txs) == 0 { + delete(pending, s.p.ID()) + } + // Send the pack in the background. + glog.V(logger.Detail).Infof("%v: sending %d transactions (%v)", s.p.Peer, len(pack.txs), size) + sending = true + go func() { done <- pack.p.SendTransactions(pack.txs) }() + } + + // pick chooses the next pending sync. + pick := func() *txsync { + if len(pending) == 0 { + return nil + } + n := rand.Intn(len(pending)) + 1 + for _, s := range pending { + if n--; n == 0 { + return s + } + } + return nil + } + + for { + select { + case s := <-pm.txsyncCh: + pending[s.p.ID()] = s + if !sending { + send(s) + } + case err := <-done: + sending = false + // Stop tracking peers that cause send failures. + if err != nil { + glog.V(logger.Debug).Infof("%v: tx send failed: %v", pack.p.Peer, err) + delete(pending, pack.p.ID()) + } + // Schedule the next send. + if s := pick(); s != nil { + send(s) + } + case <-pm.quitSync: + return + } + } +} + +// syncer is responsible for periodically synchronising with the network, both +// downloading hashes and blocks as well as handling the announcement handler. +func (pm *ProtocolManager) syncer() { + // Start and ensure cleanup of sync mechanisms + pm.fetcher.Start() + defer pm.fetcher.Stop() + defer pm.downloader.Terminate() + + // Wait for different events to fire synchronisation operations + forceSync := time.Tick(forceSyncCycle) + for { + select { + case <-pm.newPeerCh: + // Make sure we have peers to select from, then sync + if pm.peers.Len() < minDesiredPeerCount { + break + } + go pm.synchronise(pm.peers.BestPeer()) + + case <-forceSync: + // Force a sync even if not enough peers are present + go pm.synchronise(pm.peers.BestPeer()) + + case <-pm.noMorePeers: + return + } + } +} + +// synchronise tries to sync up our local block chain with a remote peer. +func (pm *ProtocolManager) synchronise(peer *peer) { + // Short circuit if no peers are available + if peer == nil { + return + } + // Make sure the peer's TD is higher than our own + currentBlock := pm.blockchain.CurrentBlock() + td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) + + pHead, pTd := peer.Head() + if pTd.Cmp(td) <= 0 { + return + } + // Otherwise try to sync with the downloader + mode := downloader.FullSync + if atomic.LoadUint32(&pm.fastSync) == 1 { + mode = downloader.FastSync + } + if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { + return + } + atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done + + // If fast sync was enabled, and we synced up, disable it + if atomic.LoadUint32(&pm.fastSync) == 1 { + // Disable fast sync if we indeed have something in our chain + if pm.blockchain.CurrentBlock().NumberU64() > 0 { + glog.V(logger.Info).Infof("fast sync complete, auto disabling") + atomic.StoreUint32(&pm.fastSync, 0) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/eth/sync_test.go b/vendor/github.com/ethereum/go-ethereum/eth/sync_test.go new file mode 100644 index 0000000..198ffaf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/eth/sync_test.go @@ -0,0 +1,54 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" +) + +// Tests that fast sync gets disabled as soon as a real block is successfully +// imported into the blockchain. +func TestFastSyncDisabling(t *testing.T) { + // Create a pristine protocol manager, check that fast sync is left enabled + pmEmpty := newTestProtocolManagerMust(t, true, 0, nil, nil) + if atomic.LoadUint32(&pmEmpty.fastSync) == 0 { + t.Fatalf("fast sync disabled on pristine blockchain") + } + // Create a full protocol manager, check that fast sync gets disabled + pmFull := newTestProtocolManagerMust(t, true, 1024, nil, nil) + if atomic.LoadUint32(&pmFull.fastSync) == 1 { + t.Fatalf("fast sync not disabled on non-empty blockchain") + } + // Sync up the two peers + io1, io2 := p2p.MsgPipe() + + go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2)) + go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1)) + + time.Sleep(250 * time.Millisecond) + pmEmpty.synchronise(pmEmpty.peers.BestPeer()) + + // Check that fast sync was disabled + if atomic.LoadUint32(&pmEmpty.fastSync) == 1 { + t.Fatalf("fast sync not disabled after successful synchronisation") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go new file mode 100644 index 0000000..1d04d9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient.go @@ -0,0 +1,441 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethclient provides a client for the Ethereum RPC API. +package ethclient + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Client defines typed wrappers for the Ethereum RPC API. +type Client struct { + c *rpc.Client +} + +// Dial connects a client to the given URL. +func Dial(rawurl string) (*Client, error) { + c, err := rpc.Dial(rawurl) + if err != nil { + return nil, err + } + return NewClient(c), nil +} + +// NewClient creates a client that uses the given RPC client. +func NewClient(c *rpc.Client) *Client { + return &Client{c} +} + +// Blockchain Access + +// BlockByHash returns the given full block. +// +// Note that loading full blocks requires two requests. Use HeaderByHash +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) +} + +// BlockByNumber returns a block from the current canonical chain. If number is nil, the +// latest known block is returned. +// +// Note that loading full blocks requires two requests. Use HeaderByNumber +// if you don't need all transactions or uncle headers. +func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) +} + +type rpcBlock struct { + Hash common.Hash `json:"hash"` + Transactions []*types.Transaction `json:"transactions"` + UncleHashes []common.Hash `json:"uncles"` +} + +func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { + var raw json.RawMessage + err := ec.c.CallContext(ctx, &raw, method, args...) + if err != nil { + return nil, err + } else if len(raw) == 0 { + return nil, ethereum.NotFound + } + // Decode header and transactions. + var head *types.Header + var body rpcBlock + if err := json.Unmarshal(raw, &head); err != nil { + return nil, err + } + if err := json.Unmarshal(raw, &body); err != nil { + return nil, err + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. + if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { + return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles") + } + if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { + return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles") + } + if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 { + return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") + } + if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 { + return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") + } + // Load uncles because they are not included in the block response. + var uncles []*types.Header + if len(body.UncleHashes) > 0 { + uncles = make([]*types.Header, len(body.UncleHashes)) + reqs := make([]rpc.BatchElem, len(body.UncleHashes)) + for i := range reqs { + reqs[i] = rpc.BatchElem{ + Method: "eth_getUncleByBlockHashAndIndex", + Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, + Result: &uncles[i], + } + } + if err := ec.c.BatchCallContext(ctx, reqs); err != nil { + return nil, err + } + for i := range reqs { + if reqs[i].Error != nil { + return nil, reqs[i].Error + } + if uncles[i] == nil { + return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:]) + } + } + } + return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil +} + +// HeaderByHash returns the block header with the given hash. +func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + var head *types.Header + err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) + if err == nil && head == nil { + err = ethereum.NotFound + } + return head, err +} + +// HeaderByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + var head *types.Header + err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) + if err == nil && head == nil { + err = ethereum.NotFound + } + return head, err +} + +// TransactionByHash returns the transaction with the given hash. +func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { + var raw json.RawMessage + err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash) + if err != nil { + return nil, false, err + } else if len(raw) == 0 { + return nil, false, ethereum.NotFound + } + if err := json.Unmarshal(raw, &tx); err != nil { + return nil, false, err + } else if _, r, _ := tx.RawSignatureValues(); r == nil { + return nil, false, fmt.Errorf("server returned transaction without signature") + } + var block struct{ BlockHash *common.Hash } + if err := json.Unmarshal(raw, &block); err != nil { + return nil, false, err + } + return tx, block.BlockHash == nil, nil +} + +// TransactionCount returns the total number of transactions in the given block. +func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { + var num hexutil.Uint + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) + return uint(num), err +} + +// TransactionInBlock returns a single transaction at index in the given block. +func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { + var tx *types.Transaction + err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) + if err == nil { + if tx == nil { + return nil, ethereum.NotFound + } else if _, r, _ := tx.RawSignatureValues(); r == nil { + return nil, fmt.Errorf("server returned transaction without signature") + } + } + return tx, err +} + +// TransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + var r *types.Receipt + err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) + if err == nil { + if r == nil { + return nil, ethereum.NotFound + } else if len(r.PostState) == 0 { + return nil, fmt.Errorf("server returned receipt without post state") + } + } + return r, err +} + +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + return hexutil.EncodeBig(number) +} + +type rpcProgress struct { + StartingBlock hexutil.Uint64 + CurrentBlock hexutil.Uint64 + HighestBlock hexutil.Uint64 + PulledStates hexutil.Uint64 + KnownStates hexutil.Uint64 +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + var raw json.RawMessage + if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil { + return nil, err + } + // Handle the possible response types + var syncing bool + if err := json.Unmarshal(raw, &syncing); err == nil { + return nil, nil // Not syncing (always false) + } + var progress *rpcProgress + if err := json.Unmarshal(raw, &progress); err != nil { + return nil, err + } + return ðereum.SyncProgress{ + StartingBlock: uint64(progress.StartingBlock), + CurrentBlock: uint64(progress.CurrentBlock), + HighestBlock: uint64(progress.HighestBlock), + PulledStates: uint64(progress.PulledStates), + KnownStates: uint64(progress.KnownStates), + }, nil +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "newHeads", map[string]struct{}{}) +} + +// State Access + +// BalanceAt returns the wei balance of the given account. +// The block number can be nil, in which case the balance is taken from the latest known block. +func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + var result hexutil.Big + err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) + return (*big.Int)(&result), err +} + +// StorageAt returns the value of key in the contract storage of the given account. +// The block number can be nil, in which case the value is taken from the latest known block. +func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + var result hexutil.Bytes + err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) + return result, err +} + +// CodeAt returns the contract code of the given account. +// The block number can be nil, in which case the code is taken from the latest known block. +func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + var result hexutil.Bytes + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) + return result, err +} + +// NonceAt returns the account nonce of the given account. +// The block number can be nil, in which case the nonce is taken from the latest known block. +func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + var result hexutil.Uint64 + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) + return uint64(result), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + var result []types.Log + err := ec.c.CallContext(ctx, &result, "eth_getLogs", toFilterArg(q)) + return result, err +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q)) +} + +func toFilterArg(q ethereum.FilterQuery) interface{} { + arg := map[string]interface{}{ + "fromBlock": toBlockNumArg(q.FromBlock), + "toBlock": toBlockNumArg(q.ToBlock), + "address": q.Addresses, + "topics": q.Topics, + } + if q.FromBlock == nil { + arg["fromBlock"] = "0x0" + } + return arg +} + +// Pending State + +// PendingBalanceAt returns the wei balance of the given account in the pending state. +func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { + var result hexutil.Big + err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending") + return (*big.Int)(&result), err +} + +// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. +func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { + var result hexutil.Bytes + err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending") + return result, err +} + +// PendingCodeAt returns the contract code of the given account in the pending state. +func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + var result hexutil.Bytes + err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") + return result, err +} + +// PendingNonceAt returns the account nonce of the given account in the pending state. +// This is the nonce that should be used for the next transaction. +func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + var result hexutil.Uint64 + err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") + return uint64(result), err +} + +// PendingTransactionCount returns the total number of transactions in the pending state. +func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { + var num hexutil.Uint + err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") + return uint(num), err +} + +// TODO: SubscribePendingTransactions (needs server side) + +// Contract Calling + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be nil, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + var hex hexutil.Bytes + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) + if err != nil { + return nil, err + } + return hex, nil +} + +// PendingCallContract executes a message call transaction using the EVM. +// The state seen by the contract call is the pending state. +func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + var hex hexutil.Bytes + err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") + if err != nil { + return nil, err + } + return hex, nil +} + +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + var hex hexutil.Big + if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// EstimateGas tries to estimate the gas needed to execute a specific transaction based on +// the current pending state of the backend blockchain. There is no guarantee that this is +// the true gas limit requirement as other transactions may be added or removed by miners, +// but it should provide a basis for setting a reasonable default. +func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { + var hex hexutil.Big + err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) + if err != nil { + return nil, err + } + return (*big.Int)(&hex), nil +} + +// SendTransaction injects a signed transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data)) +} + +func toCallArg(msg ethereum.CallMsg) interface{} { + arg := map[string]interface{}{ + "from": msg.From, + "to": msg.To, + } + if len(msg.Data) > 0 { + arg["data"] = hexutil.Bytes(msg.Data) + } + if msg.Value != nil { + arg["value"] = (*hexutil.Big)(msg.Value) + } + if msg.Gas != nil { + arg["gas"] = (*hexutil.Big)(msg.Gas) + } + if msg.GasPrice != nil { + arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) + } + return arg +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient_test.go b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient_test.go new file mode 100644 index 0000000..178eb2b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethclient/ethclient_test.go @@ -0,0 +1,34 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethclient + +import "github.com/ethereum/go-ethereum" + +// Verify that Client implements the ethereum interfaces. +var ( + _ = ethereum.ChainReader(&Client{}) + _ = ethereum.TransactionReader(&Client{}) + _ = ethereum.ChainStateReader(&Client{}) + _ = ethereum.ChainSyncReader(&Client{}) + _ = ethereum.ContractCaller(&Client{}) + _ = ethereum.GasEstimator(&Client{}) + _ = ethereum.GasPricer(&Client{}) + _ = ethereum.LogFilterer(&Client{}) + _ = ethereum.PendingStateReader(&Client{}) + // _ = ethereum.PendingStateEventer(&Client{}) + _ = ethereum.PendingContractCaller(&Client{}) +) diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/.gitignore b/vendor/github.com/ethereum/go-ethereum/ethdb/.gitignore new file mode 100644 index 0000000..f725d58 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/.gitignore @@ -0,0 +1,12 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store + diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/database.go b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go new file mode 100644 index 0000000..c0e92a8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/database.go @@ -0,0 +1,359 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethdb + +import ( + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/metrics" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/filter" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + + gometrics "github.com/rcrowley/go-metrics" +) + +var OpenFileLimit = 64 + +// cacheRatio specifies how the total allotted cache is distributed between the +// various system databases. +var cacheRatio = map[string]float64{ + "chaindata": 1.0, + "lightchaindata": 1.0, +} + +// handleRatio specifies how the total allotted file descriptors is distributed +// between the various system databases. +var handleRatio = map[string]float64{ + "chaindata": 1.0, + "lightchaindata": 1.0, +} + +type LDBDatabase struct { + fn string // filename for reporting + db *leveldb.DB // LevelDB instance + + getTimer gometrics.Timer // Timer for measuring the database get request counts and latencies + putTimer gometrics.Timer // Timer for measuring the database put request counts and latencies + delTimer gometrics.Timer // Timer for measuring the database delete request counts and latencies + missMeter gometrics.Meter // Meter for measuring the missed database get requests + readMeter gometrics.Meter // Meter for measuring the database get request data usage + writeMeter gometrics.Meter // Meter for measuring the database put request data usage + compTimeMeter gometrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter gometrics.Meter // Meter for measuring the data read during compaction + compWriteMeter gometrics.Meter // Meter for measuring the data written during compaction + + quitLock sync.Mutex // Mutex protecting the quit channel access + quitChan chan chan error // Quit channel to stop the metrics collection before closing the database +} + +// NewLDBDatabase returns a LevelDB wrapped object. +func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) { + // Calculate the cache and file descriptor allowance for this particular database + cache = int(float64(cache) * cacheRatio[filepath.Base(file)]) + if cache < 16 { + cache = 16 + } + handles = int(float64(handles) * handleRatio[filepath.Base(file)]) + if handles < 16 { + handles = 16 + } + glog.V(logger.Info).Infof("Allotted %dMB cache and %d file handles to %s", cache, handles, file) + + // Open the db and recover any potential corruptions + db, err := leveldb.OpenFile(file, &opt.Options{ + OpenFilesCacheCapacity: handles, + BlockCacheCapacity: cache / 2 * opt.MiB, + WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally + Filter: filter.NewBloomFilter(10), + }) + if _, corrupted := err.(*errors.ErrCorrupted); corrupted { + db, err = leveldb.RecoverFile(file, nil) + } + // (Re)check for errors and abort if opening of the db failed + if err != nil { + return nil, err + } + return &LDBDatabase{ + fn: file, + db: db, + }, nil +} + +// Path returns the path to the database directory. +func (db *LDBDatabase) Path() string { + return db.fn +} + +// Put puts the given key / value to the queue +func (self *LDBDatabase) Put(key []byte, value []byte) error { + // Measure the database put latency, if requested + if self.putTimer != nil { + defer self.putTimer.UpdateSince(time.Now()) + } + // Generate the data to write to disk, update the meter and write + //value = rle.Compress(value) + + if self.writeMeter != nil { + self.writeMeter.Mark(int64(len(value))) + } + return self.db.Put(key, value, nil) +} + +// Get returns the given key if it's present. +func (self *LDBDatabase) Get(key []byte) ([]byte, error) { + // Measure the database get latency, if requested + if self.getTimer != nil { + defer self.getTimer.UpdateSince(time.Now()) + } + // Retrieve the key and increment the miss counter if not found + dat, err := self.db.Get(key, nil) + if err != nil { + if self.missMeter != nil { + self.missMeter.Mark(1) + } + return nil, err + } + // Otherwise update the actually retrieved amount of data + if self.readMeter != nil { + self.readMeter.Mark(int64(len(dat))) + } + return dat, nil + //return rle.Decompress(dat) +} + +// Delete deletes the key from the queue and database +func (self *LDBDatabase) Delete(key []byte) error { + // Measure the database delete latency, if requested + if self.delTimer != nil { + defer self.delTimer.UpdateSince(time.Now()) + } + // Execute the actual operation + return self.db.Delete(key, nil) +} + +func (self *LDBDatabase) NewIterator() iterator.Iterator { + return self.db.NewIterator(nil, nil) +} + +func (self *LDBDatabase) Close() { + // Stop the metrics collection to avoid internal database races + self.quitLock.Lock() + defer self.quitLock.Unlock() + + if self.quitChan != nil { + errc := make(chan error) + self.quitChan <- errc + if err := <-errc; err != nil { + glog.V(logger.Error).Infof("metrics failure in '%s': %v\n", self.fn, err) + } + } + err := self.db.Close() + if glog.V(logger.Error) { + if err == nil { + glog.Infoln("closed db:", self.fn) + } else { + glog.Errorf("error closing db %s: %v", self.fn, err) + } + } +} + +func (self *LDBDatabase) LDB() *leveldb.DB { + return self.db +} + +// Meter configures the database metrics collectors and +func (self *LDBDatabase) Meter(prefix string) { + // Short circuit metering if the metrics system is disabled + if !metrics.Enabled { + return + } + // Initialize all the metrics collector at the requested prefix + self.getTimer = metrics.NewTimer(prefix + "user/gets") + self.putTimer = metrics.NewTimer(prefix + "user/puts") + self.delTimer = metrics.NewTimer(prefix + "user/dels") + self.missMeter = metrics.NewMeter(prefix + "user/misses") + self.readMeter = metrics.NewMeter(prefix + "user/reads") + self.writeMeter = metrics.NewMeter(prefix + "user/writes") + self.compTimeMeter = metrics.NewMeter(prefix + "compact/time") + self.compReadMeter = metrics.NewMeter(prefix + "compact/input") + self.compWriteMeter = metrics.NewMeter(prefix + "compact/output") + + // Create a quit channel for the periodic collector and run it + self.quitLock.Lock() + self.quitChan = make(chan chan error) + self.quitLock.Unlock() + + go self.meter(3 * time.Second) +} + +// meter periodically retrieves internal leveldb counters and reports them to +// the metrics subsystem. +// +// This is how a stats table look like (currently): +// Compactions +// Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) +// -------+------------+---------------+---------------+---------------+--------------- +// 0 | 0 | 0.00000 | 1.27969 | 0.00000 | 12.31098 +// 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294 +// 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884 +// 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000 +func (self *LDBDatabase) meter(refresh time.Duration) { + // Create the counters to store current and previous values + counters := make([][]float64, 2) + for i := 0; i < 2; i++ { + counters[i] = make([]float64, 3) + } + // Iterate ad infinitum and collect the stats + for i := 1; ; i++ { + // Retrieve the database stats + stats, err := self.db.GetProperty("leveldb.stats") + if err != nil { + glog.V(logger.Error).Infof("failed to read database stats: %v", err) + return + } + // Find the compaction table, skip the header + lines := strings.Split(stats, "\n") + for len(lines) > 0 && strings.TrimSpace(lines[0]) != "Compactions" { + lines = lines[1:] + } + if len(lines) <= 3 { + glog.V(logger.Error).Infof("compaction table not found") + return + } + lines = lines[3:] + + // Iterate over all the table rows, and accumulate the entries + for j := 0; j < len(counters[i%2]); j++ { + counters[i%2][j] = 0 + } + for _, line := range lines { + parts := strings.Split(line, "|") + if len(parts) != 6 { + break + } + for idx, counter := range parts[3:] { + if value, err := strconv.ParseFloat(strings.TrimSpace(counter), 64); err != nil { + glog.V(logger.Error).Infof("compaction entry parsing failed: %v", err) + return + } else { + counters[i%2][idx] += value + } + } + } + // Update all the requested meters + if self.compTimeMeter != nil { + self.compTimeMeter.Mark(int64((counters[i%2][0] - counters[(i-1)%2][0]) * 1000 * 1000 * 1000)) + } + if self.compReadMeter != nil { + self.compReadMeter.Mark(int64((counters[i%2][1] - counters[(i-1)%2][1]) * 1024 * 1024)) + } + if self.compWriteMeter != nil { + self.compWriteMeter.Mark(int64((counters[i%2][2] - counters[(i-1)%2][2]) * 1024 * 1024)) + } + // Sleep a bit, then repeat the stats collection + select { + case errc := <-self.quitChan: + // Quit requesting, stop hammering the database + errc <- nil + return + + case <-time.After(refresh): + // Timeout, gather a new set of stats + } + } +} + +// TODO: remove this stuff and expose leveldb directly + +func (db *LDBDatabase) NewBatch() Batch { + return &ldbBatch{db: db.db, b: new(leveldb.Batch)} +} + +type ldbBatch struct { + db *leveldb.DB + b *leveldb.Batch +} + +func (b *ldbBatch) Put(key, value []byte) error { + b.b.Put(key, value) + return nil +} + +func (b *ldbBatch) Write() error { + return b.db.Write(b.b, nil) +} + +type table struct { + db Database + prefix string +} + +// NewTable returns a Database object that prefixes all keys with a given +// string. +func NewTable(db Database, prefix string) Database { + return &table{ + db: db, + prefix: prefix, + } +} + +func (dt *table) Put(key []byte, value []byte) error { + return dt.db.Put(append([]byte(dt.prefix), key...), value) +} + +func (dt *table) Get(key []byte) ([]byte, error) { + return dt.db.Get(append([]byte(dt.prefix), key...)) +} + +func (dt *table) Delete(key []byte) error { + return dt.db.Delete(append([]byte(dt.prefix), key...)) +} + +func (dt *table) Close() { + // Do nothing; don't close the underlying DB. +} + +type tableBatch struct { + batch Batch + prefix string +} + +// NewTableBatch returns a Batch object which prefixes all keys with a given string. +func NewTableBatch(db Database, prefix string) Batch { + return &tableBatch{db.NewBatch(), prefix} +} + +func (dt *table) NewBatch() Batch { + return &tableBatch{dt.db.NewBatch(), dt.prefix} +} + +func (tb *tableBatch) Put(key, value []byte) error { + return tb.batch.Put(append([]byte(tb.prefix), key...), value) +} + +func (tb *tableBatch) Write() error { + return tb.batch.Write() +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/database_test.go b/vendor/github.com/ethereum/go-ethereum/ethdb/database_test.go new file mode 100644 index 0000000..0e69a12 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/database_test.go @@ -0,0 +1,34 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethdb + +import ( + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" +) + +func newDb() *LDBDatabase { + file := filepath.Join("/", "tmp", "ldbtesttmpfile") + if common.FileExist(file) { + os.RemoveAll(file) + } + db, _ := NewLDBDatabase(file, 0, 0) + + return db +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/interface.go b/vendor/github.com/ethereum/go-ethereum/ethdb/interface.go new file mode 100644 index 0000000..f4b787a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/interface.go @@ -0,0 +1,30 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethdb + +type Database interface { + Put(key []byte, value []byte) error + Get(key []byte) ([]byte, error) + Delete(key []byte) error + Close() + NewBatch() Batch +} + +type Batch interface { + Put(key, value []byte) error + Write() error +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/memory_database.go b/vendor/github.com/ethereum/go-ethereum/ethdb/memory_database.go new file mode 100644 index 0000000..65c4879 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/memory_database.go @@ -0,0 +1,125 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethdb + +import ( + "errors" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +/* + * This is a test memory database. Do not use for any production it does not get persisted + */ +type MemDatabase struct { + db map[string][]byte + lock sync.RWMutex +} + +func NewMemDatabase() (*MemDatabase, error) { + return &MemDatabase{ + db: make(map[string][]byte), + }, nil +} + +func (db *MemDatabase) Put(key []byte, value []byte) error { + db.lock.Lock() + defer db.lock.Unlock() + + db.db[string(key)] = common.CopyBytes(value) + return nil +} + +func (db *MemDatabase) Set(key []byte, value []byte) { + db.lock.Lock() + defer db.lock.Unlock() + + db.Put(key, value) +} + +func (db *MemDatabase) Get(key []byte) ([]byte, error) { + db.lock.RLock() + defer db.lock.RUnlock() + + if entry, ok := db.db[string(key)]; ok { + return entry, nil + } + return nil, errors.New("not found") +} + +func (db *MemDatabase) Keys() [][]byte { + db.lock.RLock() + defer db.lock.RUnlock() + + keys := [][]byte{} + for key := range db.db { + keys = append(keys, []byte(key)) + } + return keys +} + +/* +func (db *MemDatabase) GetKeys() []*common.Key { + data, _ := db.Get([]byte("KeyRing")) + + return []*common.Key{common.NewKeyFromBytes(data)} +} +*/ + +func (db *MemDatabase) Delete(key []byte) error { + db.lock.Lock() + defer db.lock.Unlock() + + delete(db.db, string(key)) + return nil +} + +func (db *MemDatabase) Close() {} + +func (db *MemDatabase) NewBatch() Batch { + return &memBatch{db: db} +} + +type kv struct{ k, v []byte } + +type memBatch struct { + db *MemDatabase + writes []kv + lock sync.RWMutex +} + +func (b *memBatch) Put(key, value []byte) error { + b.lock.Lock() + defer b.lock.Unlock() + + b.writes = append(b.writes, kv{common.CopyBytes(key), common.CopyBytes(value)}) + return nil +} + +func (b *memBatch) Write() error { + b.lock.RLock() + defer b.lock.RUnlock() + + b.db.lock.Lock() + defer b.db.lock.Unlock() + + for _, kv := range b.writes { + b.db.db[string(kv.k)] = kv.v + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go b/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go new file mode 100644 index 0000000..8692a43 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/ethstats/ethstats.go @@ -0,0 +1,608 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethstats implements the network stats reporting service. +package ethstats + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "regexp" + "runtime" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/websocket" +) + +// historyUpdateRange is the number of blocks a node should report upon login or +// history request. +const historyUpdateRange = 50 + +// Service implements an Ethereum netstats reporting daemon that pushes local +// chain statistics up to a monitoring server. +type Service struct { + stack *node.Node // Temporary workaround, remove when API finalized + + server *p2p.Server // Peer-to-peer server to retrieve networking infos + eth *eth.Ethereum // Full Ethereum service if monitoring a full node + les *les.LightEthereum // Light Ethereum service if monitoring a light node + + node string // Name of the node to display on the monitoring page + pass string // Password to authorize access to the monitoring page + host string // Remote address of the monitoring service + + pongCh chan struct{} // Pong notifications are fed into this channel + histCh chan []uint64 // History request block numbers are fed into this channel +} + +// New returns a monitoring service ready for stats reporting. +func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Service, error) { + // Parse the netstats connection url + re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)") + parts := re.FindStringSubmatch(url) + if len(parts) != 5 { + return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) + } + // Assemble and return the stats service + return &Service{ + eth: ethServ, + les: lesServ, + node: parts[1], + pass: parts[3], + host: parts[4], + pongCh: make(chan struct{}), + histCh: make(chan []uint64, 1), + }, nil +} + +// Protocols implements node.Service, returning the P2P network protocols used +// by the stats service (nil as it doesn't use the devp2p overlay network). +func (s *Service) Protocols() []p2p.Protocol { return nil } + +// APIs implements node.Service, returning the RPC API endpoints provided by the +// stats service (nil as it doesn't provide any user callable APIs). +func (s *Service) APIs() []rpc.API { return nil } + +// Start implements node.Service, starting up the monitoring and reporting daemon. +func (s *Service) Start(server *p2p.Server) error { + s.server = server + go s.loop() + + glog.V(logger.Info).Infoln("Stats daemon started") + return nil +} + +// Stop implements node.Service, terminating the monitoring and reporting daemon. +func (s *Service) Stop() error { + glog.V(logger.Info).Infoln("Stats daemon stopped") + return nil +} + +// loop keeps trying to connect to the netstats server, reporting chain events +// until termination. +func (s *Service) loop() { + // Subscribe tso chain events to execute updates on + var emux *event.TypeMux + if s.eth != nil { + emux = s.eth.EventMux() + } else { + emux = s.les.EventMux() + } + headSub := emux.Subscribe(core.ChainHeadEvent{}) + defer headSub.Unsubscribe() + + txSub := emux.Subscribe(core.TxPreEvent{}) + defer txSub.Unsubscribe() + + // Loop reporting until termination + for { + // Establish a websocket connection to the server and authenticate the node + url := fmt.Sprintf("%s/api", s.host) + if !strings.Contains(url, "://") { + url = "wss://" + url + } + conn, err := websocket.Dial(url, "", "http://localhost/") + if err != nil { + glog.V(logger.Warn).Infof("Stats server unreachable: %v", err) + time.Sleep(10 * time.Second) + continue + } + in := json.NewDecoder(conn) + out := json.NewEncoder(conn) + + if err = s.login(in, out); err != nil { + glog.V(logger.Warn).Infof("Stats login failed: %v", err) + conn.Close() + time.Sleep(10 * time.Second) + continue + } + go s.readLoop(conn, in) + + // Send the initial stats so our node looks decent from the get go + if err = s.report(out); err != nil { + glog.V(logger.Warn).Infof("Initial stats report failed: %v", err) + conn.Close() + continue + } + if err = s.reportHistory(out, nil); err != nil { + glog.V(logger.Warn).Infof("History report failed: %v", err) + conn.Close() + continue + } + // Keep sending status updates until the connection breaks + fullReport := time.NewTicker(15 * time.Second) + + for err == nil { + select { + case <-fullReport.C: + if err = s.report(out); err != nil { + glog.V(logger.Warn).Infof("Full stats report failed: %v", err) + } + case list := <-s.histCh: + if err = s.reportHistory(out, list); err != nil { + glog.V(logger.Warn).Infof("Block history report failed: %v", err) + } + case head, ok := <-headSub.Chan(): + if !ok { // node stopped + conn.Close() + return + } + if err = s.reportBlock(out, head.Data.(core.ChainHeadEvent).Block); err != nil { + glog.V(logger.Warn).Infof("Block stats report failed: %v", err) + } + if err = s.reportPending(out); err != nil { + glog.V(logger.Warn).Infof("Post-block transaction stats report failed: %v", err) + } + case _, ok := <-txSub.Chan(): + if !ok { // node stopped + conn.Close() + return + } + // Exhaust events to avoid reporting too frequently + for exhausted := false; !exhausted; { + select { + case <-headSub.Chan(): + default: + exhausted = true + } + } + if err = s.reportPending(out); err != nil { + glog.V(logger.Warn).Infof("Transaction stats report failed: %v", err) + } + } + } + // Make sure the connection is closed + conn.Close() + } +} + +// readLoop loops as long as the connection is alive and retrieves data packets +// from the network socket. If any of them match an active request, it forwards +// it, if they themselves are requests it initiates a reply, and lastly it drops +// unknown packets. +func (s *Service) readLoop(conn *websocket.Conn, in *json.Decoder) { + // If the read loop exists, close the connection + defer conn.Close() + + for { + // Retrieve the next generic network packet and bail out on error + var msg map[string][]interface{} + if err := in.Decode(&msg); err != nil { + glog.V(logger.Warn).Infof("Failed to decode stats server message: %v", err) + return + } + if len(msg["emit"]) == 0 { + glog.V(logger.Warn).Infof("Stats server sent non-broadcast: %v", msg) + return + } + command, ok := msg["emit"][0].(string) + if !ok { + glog.V(logger.Warn).Infof("Invalid stats server message type: %v", msg["emit"][0]) + return + } + // If the message is a ping reply, deliver (someone must be listening!) + if len(msg["emit"]) == 2 && command == "node-pong" { + select { + case s.pongCh <- struct{}{}: + // Pong delivered, continue listening + continue + default: + // Ping routine dead, abort + glog.V(logger.Warn).Infof("Stats server pinger seems to have died") + return + } + } + // If the message is a history request, forward to the event processor + if len(msg["emit"]) == 2 && command == "history" { + // Make sure the request is valid and doesn't crash us + request, ok := msg["emit"][1].(map[string]interface{}) + if !ok { + glog.V(logger.Warn).Infof("Invalid history request: %v", msg["emit"][1]) + return + } + list, ok := request["list"].([]interface{}) + if !ok { + glog.V(logger.Warn).Infof("Invalid history block list: %v", request["list"]) + return + } + // Convert the block number list to an integer list + numbers := make([]uint64, len(list)) + for i, num := range list { + n, ok := num.(float64) + if !ok { + glog.V(logger.Warn).Infof("Invalid history block number: %v", num) + return + } + numbers[i] = uint64(n) + } + select { + case s.histCh <- numbers: + continue + default: + } + } + // Report anything else and continue + glog.V(logger.Info).Infof("Unknown stats message: %v", msg) + } +} + +// nodeInfo is the collection of metainformation about a node that is displayed +// on the monitoring page. +type nodeInfo struct { + Name string `json:"name"` + Node string `json:"node"` + Port int `json:"port"` + Network string `json:"net"` + Protocol string `json:"protocol"` + API string `json:"api"` + Os string `json:"os"` + OsVer string `json:"os_v"` + Client string `json:"client"` + History bool `json:"canUpdateHistory"` +} + +// authMsg is the authentication infos needed to login to a monitoring server. +type authMsg struct { + Id string `json:"id"` + Info nodeInfo `json:"info"` + Secret string `json:"secret"` +} + +// login tries to authorize the client at the remote server. +func (s *Service) login(in *json.Decoder, out *json.Encoder) error { + // Construct and send the login authentication + infos := s.server.NodeInfo() + + var network, protocol string + if info := infos.Protocols["eth"]; info != nil { + network = strconv.Itoa(info.(*eth.EthNodeInfo).Network) + protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0]) + } else { + network = strconv.Itoa(infos.Protocols["les"].(*eth.EthNodeInfo).Network) + protocol = fmt.Sprintf("les/%d", les.ProtocolVersions[0]) + } + auth := &authMsg{ + Id: s.node, + Info: nodeInfo{ + Name: s.node, + Node: infos.Name, + Port: infos.Ports.Listener, + Network: network, + Protocol: protocol, + API: "No", + Os: runtime.GOOS, + OsVer: runtime.GOARCH, + Client: "0.1.1", + History: true, + }, + Secret: s.pass, + } + login := map[string][]interface{}{ + "emit": {"hello", auth}, + } + if err := out.Encode(login); err != nil { + return err + } + // Retrieve the remote ack or connection termination + var ack map[string][]string + if err := in.Decode(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { + return errors.New("unauthorized") + } + return nil +} + +// report collects all possible data to report and send it to the stats server. +// This should only be used on reconnects or rarely to avoid overloading the +// server. Use the individual methods for reporting subscribed events. +func (s *Service) report(out *json.Encoder) error { + if err := s.reportLatency(out); err != nil { + return err + } + if err := s.reportBlock(out, nil); err != nil { + return err + } + if err := s.reportPending(out); err != nil { + return err + } + if err := s.reportStats(out); err != nil { + return err + } + return nil +} + +// reportLatency sends a ping request to the server, measures the RTT time and +// finally sends a latency update. +func (s *Service) reportLatency(out *json.Encoder) error { + // Send the current time to the ethstats server + start := time.Now() + + ping := map[string][]interface{}{ + "emit": {"node-ping", map[string]string{ + "id": s.node, + "clientTime": start.String(), + }}, + } + if err := out.Encode(ping); err != nil { + return err + } + // Wait for the pong request to arrive back + select { + case <-s.pongCh: + // Pong delivered, report the latency + case <-time.After(3 * time.Second): + // Ping timeout, abort + return errors.New("ping timed out") + } + // Send back the measured latency + latency := map[string][]interface{}{ + "emit": {"latency", map[string]string{ + "id": s.node, + "latency": strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)), + }}, + } + return out.Encode(latency) +} + +// blockStats is the information to report about individual blocks. +type blockStats struct { + Number *big.Int `json:"number"` + Hash common.Hash `json:"hash"` + Timestamp *big.Int `json:"timestamp"` + Miner common.Address `json:"miner"` + GasUsed *big.Int `json:"gasUsed"` + GasLimit *big.Int `json:"gasLimit"` + Diff string `json:"difficulty"` + TotalDiff string `json:"totalDifficulty"` + Txs txStats `json:"transactions"` + Uncles uncleStats `json:"uncles"` +} + +// txStats is a custom wrapper around a transaction array to force serializing +// empty arrays instead of returning null for them. +type txStats []*types.Transaction + +func (s txStats) MarshalJSON() ([]byte, error) { + if txs := ([]*types.Transaction)(s); len(txs) > 0 { + return json.Marshal(txs) + } + return []byte("[]"), nil +} + +// uncleStats is a custom wrapper around an uncle array to force serializing +// empty arrays instead of returning null for them. +type uncleStats []*types.Header + +func (s uncleStats) MarshalJSON() ([]byte, error) { + if uncles := ([]*types.Header)(s); len(uncles) > 0 { + return json.Marshal(uncles) + } + return []byte("[]"), nil +} + +// reportBlock retrieves the current chain head and repors it to the stats server. +func (s *Service) reportBlock(out *json.Encoder, block *types.Block) error { + // Assemble the block stats report and send it to the server + stats := map[string]interface{}{ + "id": s.node, + "block": s.assembleBlockStats(block), + } + report := map[string][]interface{}{ + "emit": {"block", stats}, + } + return out.Encode(report) +} + +// assembleBlockStats retrieves any required metadata to report a single block +// and assembles the block stats. If block is nil, the current head is processed. +func (s *Service) assembleBlockStats(block *types.Block) *blockStats { + // Gather the block infos from the local blockchain + var ( + header *types.Header + td *big.Int + txs []*types.Transaction + uncles []*types.Header + ) + if s.eth != nil { + // Full nodes have all needed information available + if block == nil { + block = s.eth.BlockChain().CurrentBlock() + } + header = block.Header() + td = s.eth.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) + + txs = block.Transactions() + uncles = block.Uncles() + } else { + // Light nodes would need on-demand lookups for transactions/uncles, skip + if block != nil { + header = block.Header() + } else { + header = s.les.BlockChain().CurrentHeader() + } + td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) + } + // Assemble and return the block stats + return &blockStats{ + Number: header.Number, + Hash: header.Hash(), + Timestamp: header.Time, + Miner: header.Coinbase, + GasUsed: new(big.Int).Set(header.GasUsed), + GasLimit: new(big.Int).Set(header.GasLimit), + Diff: header.Difficulty.String(), + TotalDiff: td.String(), + Txs: txs, + Uncles: uncles, + } +} + +// reportHistory retrieves the most recent batch of blocks and reports it to the +// stats server. +func (s *Service) reportHistory(out *json.Encoder, list []uint64) error { + // Figure out the indexes that need reporting + indexes := make([]uint64, 0, historyUpdateRange) + if len(list) > 0 { + // Specific indexes requested, send them back in particular + indexes = append(indexes, list...) + } else { + // No indexes requested, send back the top ones + var head *types.Header + if s.eth != nil { + head = s.eth.BlockChain().CurrentHeader() + } else { + head = s.les.BlockChain().CurrentHeader() + } + start := head.Number.Int64() - historyUpdateRange + if start < 0 { + start = 0 + } + for i := uint64(start); i <= head.Number.Uint64(); i++ { + indexes = append(indexes, i) + } + } + // Gather the batch of blocks to report + history := make([]*blockStats, len(indexes)) + for i, number := range indexes { + if s.eth != nil { + history[i] = s.assembleBlockStats(s.eth.BlockChain().GetBlockByNumber(number)) + } else { + history[i] = s.assembleBlockStats(types.NewBlockWithHeader(s.les.BlockChain().GetHeaderByNumber(number))) + } + } + // Assemble the history report and send it to the server + stats := map[string]interface{}{ + "id": s.node, + "history": history, + } + report := map[string][]interface{}{ + "emit": {"history", stats}, + } + return out.Encode(report) +} + +// pendStats is the information to report about pending transactions. +type pendStats struct { + Pending int `json:"pending"` +} + +// reportPending retrieves the current number of pending transactions and reports +// it to the stats server. +func (s *Service) reportPending(out *json.Encoder) error { + // Retrieve the pending count from the local blockchain + var pending int + if s.eth != nil { + pending, _ = s.eth.TxPool().Stats() + } else { + pending = s.les.TxPool().Stats() + } + // Assemble the transaction stats and send it to the server + stats := map[string]interface{}{ + "id": s.node, + "stats": &pendStats{ + Pending: pending, + }, + } + report := map[string][]interface{}{ + "emit": {"pending", stats}, + } + return out.Encode(report) +} + +// blockStats is the information to report about the local node. +type nodeStats struct { + Active bool `json:"active"` + Syncing bool `json:"syncing"` + Mining bool `json:"mining"` + Hashrate int `json:"hashrate"` + Peers int `json:"peers"` + GasPrice int `json:"gasPrice"` + Uptime int `json:"uptime"` +} + +// reportPending retrieves various stats about the node at the networking and +// mining layer and reports it to the stats server. +func (s *Service) reportStats(out *json.Encoder) error { + // Gather the syncing and mining infos from the local miner instance + var ( + mining bool + hashrate int + syncing bool + gasprice int + ) + if s.eth != nil { + mining = s.eth.Miner().Mining() + hashrate = int(s.eth.Miner().HashRate()) + + sync := s.eth.Downloader().Progress() + syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock + + gasprice = int(s.eth.Miner().GasPrice().Uint64()) + } else { + sync := s.les.Downloader().Progress() + syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock + } + stats := map[string]interface{}{ + "id": s.node, + "stats": &nodeStats{ + Active: true, + Mining: mining, + Hashrate: hashrate, + Peers: s.server.PeerCount(), + GasPrice: gasprice, + Syncing: syncing, + Uptime: 100, + }, + } + report := map[string][]interface{}{ + "emit": {"stats", stats}, + } + return out.Encode(report) +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/event.go b/vendor/github.com/ethereum/go-ethereum/event/event.go new file mode 100644 index 0000000..d3e84f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/event.go @@ -0,0 +1,211 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package event deals with subscriptions to real-time events. +package event + +import ( + "errors" + "fmt" + "reflect" + "sync" + "time" +) + +// TypeMuxEvent is a time-tagged notification pushed to subscribers. +type TypeMuxEvent struct { + Time time.Time + Data interface{} +} + +// A TypeMux dispatches events to registered receivers. Receivers can be +// registered to handle events of certain type. Any operation +// called after mux is stopped will return ErrMuxClosed. +// +// The zero value is ready to use. +// +// Deprecated: use Feed +type TypeMux struct { + mutex sync.RWMutex + subm map[reflect.Type][]*TypeMuxSubscription + stopped bool +} + +// ErrMuxClosed is returned when Posting on a closed TypeMux. +var ErrMuxClosed = errors.New("event: mux closed") + +// Subscribe creates a subscription for events of the given types. The +// subscription's channel is closed when it is unsubscribed +// or the mux is closed. +func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription { + sub := newsub(mux) + mux.mutex.Lock() + defer mux.mutex.Unlock() + if mux.stopped { + // set the status to closed so that calling Unsubscribe after this + // call will short curuit + sub.closed = true + close(sub.postC) + } else { + if mux.subm == nil { + mux.subm = make(map[reflect.Type][]*TypeMuxSubscription) + } + for _, t := range types { + rtyp := reflect.TypeOf(t) + oldsubs := mux.subm[rtyp] + if find(oldsubs, sub) != -1 { + panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp)) + } + subs := make([]*TypeMuxSubscription, len(oldsubs)+1) + copy(subs, oldsubs) + subs[len(oldsubs)] = sub + mux.subm[rtyp] = subs + } + } + return sub +} + +// Post sends an event to all receivers registered for the given type. +// It returns ErrMuxClosed if the mux has been stopped. +func (mux *TypeMux) Post(ev interface{}) error { + event := &TypeMuxEvent{ + Time: time.Now(), + Data: ev, + } + rtyp := reflect.TypeOf(ev) + mux.mutex.RLock() + if mux.stopped { + mux.mutex.RUnlock() + return ErrMuxClosed + } + subs := mux.subm[rtyp] + mux.mutex.RUnlock() + for _, sub := range subs { + sub.deliver(event) + } + return nil +} + +// Stop closes a mux. The mux can no longer be used. +// Future Post calls will fail with ErrMuxClosed. +// Stop blocks until all current deliveries have finished. +func (mux *TypeMux) Stop() { + mux.mutex.Lock() + for _, subs := range mux.subm { + for _, sub := range subs { + sub.closewait() + } + } + mux.subm = nil + mux.stopped = true + mux.mutex.Unlock() +} + +func (mux *TypeMux) del(s *TypeMuxSubscription) { + mux.mutex.Lock() + for typ, subs := range mux.subm { + if pos := find(subs, s); pos >= 0 { + if len(subs) == 1 { + delete(mux.subm, typ) + } else { + mux.subm[typ] = posdelete(subs, pos) + } + } + } + s.mux.mutex.Unlock() +} + +func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int { + for i, v := range slice { + if v == item { + return i + } + } + return -1 +} + +func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription { + news := make([]*TypeMuxSubscription, len(slice)-1) + copy(news[:pos], slice[:pos]) + copy(news[pos:], slice[pos+1:]) + return news +} + +// TypeMuxSubscription is a subscription established through TypeMux. +type TypeMuxSubscription struct { + mux *TypeMux + created time.Time + closeMu sync.Mutex + closing chan struct{} + closed bool + + // these two are the same channel. they are stored separately so + // postC can be set to nil without affecting the return value of + // Chan. + postMu sync.RWMutex + readC <-chan *TypeMuxEvent + postC chan<- *TypeMuxEvent +} + +func newsub(mux *TypeMux) *TypeMuxSubscription { + c := make(chan *TypeMuxEvent) + return &TypeMuxSubscription{ + mux: mux, + created: time.Now(), + readC: c, + postC: c, + closing: make(chan struct{}), + } +} + +func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { + return s.readC +} + +func (s *TypeMuxSubscription) Unsubscribe() { + s.mux.del(s) + s.closewait() +} + +func (s *TypeMuxSubscription) closewait() { + s.closeMu.Lock() + defer s.closeMu.Unlock() + if s.closed { + return + } + close(s.closing) + s.closed = true + + s.postMu.Lock() + close(s.postC) + s.postC = nil + s.postMu.Unlock() +} + +func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) { + // Short circuit delivery if stale event + if s.created.After(event.Time) { + return + } + // Otherwise deliver the event + s.postMu.RLock() + defer s.postMu.RUnlock() + + select { + case s.postC <- event: + case <-s.closing: + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/event_test.go b/vendor/github.com/ethereum/go-ethereum/event/event_test.go new file mode 100644 index 0000000..a12945a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/event_test.go @@ -0,0 +1,218 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import ( + "math/rand" + "sync" + "testing" + "time" +) + +type testEvent int + +func TestSubCloseUnsub(t *testing.T) { + // the point of this test is **not** to panic + var mux TypeMux + mux.Stop() + sub := mux.Subscribe(int(0)) + sub.Unsubscribe() +} + +func TestSub(t *testing.T) { + mux := new(TypeMux) + defer mux.Stop() + + sub := mux.Subscribe(testEvent(0)) + go func() { + if err := mux.Post(testEvent(5)); err != nil { + t.Errorf("Post returned unexpected error: %v", err) + } + }() + ev := <-sub.Chan() + + if ev.Data.(testEvent) != testEvent(5) { + t.Errorf("Got %v (%T), expected event %v (%T)", + ev, ev, testEvent(5), testEvent(5)) + } +} + +func TestMuxErrorAfterStop(t *testing.T) { + mux := new(TypeMux) + mux.Stop() + + sub := mux.Subscribe(testEvent(0)) + if _, isopen := <-sub.Chan(); isopen { + t.Errorf("subscription channel was not closed") + } + if err := mux.Post(testEvent(0)); err != ErrMuxClosed { + t.Errorf("Post error mismatch, got: %s, expected: %s", err, ErrMuxClosed) + } +} + +func TestUnsubscribeUnblockPost(t *testing.T) { + mux := new(TypeMux) + defer mux.Stop() + + sub := mux.Subscribe(testEvent(0)) + unblocked := make(chan bool) + go func() { + mux.Post(testEvent(5)) + unblocked <- true + }() + + select { + case <-unblocked: + t.Errorf("Post returned before Unsubscribe") + default: + sub.Unsubscribe() + <-unblocked + } +} + +func TestSubscribeDuplicateType(t *testing.T) { + mux := new(TypeMux) + expected := "event: duplicate type event.testEvent in Subscribe" + + defer func() { + err := recover() + if err == nil { + t.Errorf("Subscribe didn't panic for duplicate type") + } else if err != expected { + t.Errorf("panic mismatch: got %#v, expected %#v", err, expected) + } + }() + mux.Subscribe(testEvent(1), testEvent(2)) +} + +func TestMuxConcurrent(t *testing.T) { + rand.Seed(time.Now().Unix()) + mux := new(TypeMux) + defer mux.Stop() + + recv := make(chan int) + poster := func() { + for { + err := mux.Post(testEvent(0)) + if err != nil { + return + } + } + } + sub := func(i int) { + time.Sleep(time.Duration(rand.Intn(99)) * time.Millisecond) + sub := mux.Subscribe(testEvent(0)) + <-sub.Chan() + sub.Unsubscribe() + recv <- i + } + + go poster() + go poster() + go poster() + nsubs := 1000 + for i := 0; i < nsubs; i++ { + go sub(i) + } + + // wait until everyone has been served + counts := make(map[int]int, nsubs) + for i := 0; i < nsubs; i++ { + counts[<-recv]++ + } + for i, count := range counts { + if count != 1 { + t.Errorf("receiver %d called %d times, expected only 1 call", i, count) + } + } +} + +func emptySubscriber(mux *TypeMux, types ...interface{}) { + s := mux.Subscribe(testEvent(0)) + go func() { + for range s.Chan() { + } + }() +} + +func BenchmarkPost1000(b *testing.B) { + var ( + mux = new(TypeMux) + subscribed, done sync.WaitGroup + nsubs = 1000 + ) + subscribed.Add(nsubs) + done.Add(nsubs) + for i := 0; i < nsubs; i++ { + go func() { + s := mux.Subscribe(testEvent(0)) + subscribed.Done() + for range s.Chan() { + } + done.Done() + }() + } + subscribed.Wait() + + // The actual benchmark. + b.ResetTimer() + for i := 0; i < b.N; i++ { + mux.Post(testEvent(0)) + } + + b.StopTimer() + mux.Stop() + done.Wait() +} + +func BenchmarkPostConcurrent(b *testing.B) { + var mux = new(TypeMux) + defer mux.Stop() + emptySubscriber(mux, testEvent(0)) + emptySubscriber(mux, testEvent(0)) + emptySubscriber(mux, testEvent(0)) + + var wg sync.WaitGroup + poster := func() { + for i := 0; i < b.N; i++ { + mux.Post(testEvent(0)) + } + wg.Done() + } + wg.Add(5) + for i := 0; i < 5; i++ { + go poster() + } + wg.Wait() +} + +// for comparison +func BenchmarkChanSend(b *testing.B) { + c := make(chan interface{}) + closed := make(chan struct{}) + go func() { + for range c { + } + }() + + for i := 0; i < b.N; i++ { + select { + case c <- i: + case <-closed: + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/example_feed_test.go b/vendor/github.com/ethereum/go-ethereum/event/example_feed_test.go new file mode 100644 index 0000000..63436b2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/example_feed_test.go @@ -0,0 +1,73 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event_test + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/event" +) + +func ExampleFeed_acknowledgedEvents() { + // This example shows how the return value of Send can be used for request/reply + // interaction between event consumers and producers. + var feed event.Feed + type ackedEvent struct { + i int + ack chan<- struct{} + } + + // Consumers wait for events on the feed and acknowledge processing. + done := make(chan struct{}) + defer close(done) + for i := 0; i < 3; i++ { + ch := make(chan ackedEvent, 100) + sub := feed.Subscribe(ch) + go func() { + defer sub.Unsubscribe() + for { + select { + case ev := <-ch: + fmt.Println(ev.i) // "process" the event + ev.ack <- struct{}{} + case <-done: + return + } + } + }() + } + + // The producer sends values of type ackedEvent with increasing values of i. + // It waits for all consumers to acknowledge before sending the next event. + for i := 0; i < 3; i++ { + acksignal := make(chan struct{}) + n := feed.Send(ackedEvent{i, acksignal}) + for ack := 0; ack < n; ack++ { + <-acksignal + } + } + // Output: + // 0 + // 0 + // 0 + // 1 + // 1 + // 1 + // 2 + // 2 + // 2 +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/example_scope_test.go b/vendor/github.com/ethereum/go-ethereum/event/example_scope_test.go new file mode 100644 index 0000000..c517a83 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/example_scope_test.go @@ -0,0 +1,128 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event_test + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/event" +) + +// This example demonstrates how SubscriptionScope can be used to control the lifetime of +// subscriptions. +// +// Our example program consists of two servers, each of which performs a calculation when +// requested. The servers also allow subscribing to results of all computations. +type divServer struct{ results event.Feed } +type mulServer struct{ results event.Feed } + +func (s *divServer) do(a, b int) int { + r := a / b + s.results.Send(r) + return r +} + +func (s *mulServer) do(a, b int) int { + r := a * b + s.results.Send(r) + return r +} + +// The servers are contained in an App. The app controls the servers and exposes them +// through its API. +type App struct { + divServer + mulServer + scope event.SubscriptionScope +} + +func (s *App) Calc(op byte, a, b int) int { + switch op { + case '/': + return s.divServer.do(a, b) + case '*': + return s.mulServer.do(a, b) + default: + panic("invalid op") + } +} + +// The app's SubscribeResults method starts sending calculation results to the given +// channel. Subscriptions created through this method are tied to the lifetime of the App +// because they are registered in the scope. +func (s *App) SubscribeResults(op byte, ch chan<- int) event.Subscription { + switch op { + case '/': + return s.scope.Track(s.divServer.results.Subscribe(ch)) + case '*': + return s.scope.Track(s.mulServer.results.Subscribe(ch)) + default: + panic("invalid op") + } +} + +// Stop stops the App, closing all subscriptions created through SubscribeResults. +func (s *App) Stop() { + s.scope.Close() +} + +func ExampleSubscriptionScope() { + // Create the app. + var ( + app App + wg sync.WaitGroup + divs = make(chan int) + muls = make(chan int) + ) + + // Run a subscriber in the background. + divsub := app.SubscribeResults('/', divs) + mulsub := app.SubscribeResults('*', muls) + wg.Add(1) + go func() { + defer wg.Done() + defer fmt.Println("subscriber exited") + defer divsub.Unsubscribe() + defer mulsub.Unsubscribe() + for { + select { + case result := <-divs: + fmt.Println("division happened:", result) + case result := <-muls: + fmt.Println("multiplication happened:", result) + case <-divsub.Err(): + return + case <-mulsub.Err(): + return + } + } + }() + + // Interact with the app. + app.Calc('/', 22, 11) + app.Calc('*', 3, 4) + + // Stop the app. This shuts down the subscriptions, causing the subscriber to exit. + app.Stop() + wg.Wait() + + // Output: + // division happened: 2 + // multiplication happened: 12 + // subscriber exited +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/example_subscription_test.go b/vendor/github.com/ethereum/go-ethereum/event/example_subscription_test.go new file mode 100644 index 0000000..de11266 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/example_subscription_test.go @@ -0,0 +1,56 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event_test + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/event" +) + +func ExampleNewSubscription() { + // Create a subscription that sends 10 integers on ch. + ch := make(chan int) + sub := event.NewSubscription(func(quit <-chan struct{}) error { + for i := 0; i < 10; i++ { + select { + case ch <- i: + case <-quit: + fmt.Println("unsubscribed") + return nil + } + } + return nil + }) + + // This is the consumer. It reads 5 integers, then aborts the subscription. + // Note that Unsubscribe waits until the producer has shut down. + for i := range ch { + fmt.Println(i) + if i == 4 { + sub.Unsubscribe() + break + } + } + // Output: + // 0 + // 1 + // 2 + // 3 + // 4 + // unsubscribed +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/example_test.go b/vendor/github.com/ethereum/go-ethereum/event/example_test.go new file mode 100644 index 0000000..29938e8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/example_test.go @@ -0,0 +1,58 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import "fmt" + +func ExampleTypeMux() { + type someEvent struct{ I int } + type otherEvent struct{ S string } + type yetAnotherEvent struct{ X, Y int } + + var mux TypeMux + + // Start a subscriber. + done := make(chan struct{}) + sub := mux.Subscribe(someEvent{}, otherEvent{}) + go func() { + for event := range sub.Chan() { + fmt.Printf("Received: %#v\n", event.Data) + } + fmt.Println("done") + close(done) + }() + + // Post some events. + mux.Post(someEvent{5}) + mux.Post(yetAnotherEvent{X: 3, Y: 4}) + mux.Post(someEvent{6}) + mux.Post(otherEvent{"whoa"}) + + // Stop closes all subscription channels. + // The subscriber goroutine will print "done" + // and exit. + mux.Stop() + + // Wait for subscriber to return. + <-done + + // Output: + // Received: event.someEvent{I:5} + // Received: event.someEvent{I:6} + // Received: event.otherEvent{S:"whoa"} + // done +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/feed.go b/vendor/github.com/ethereum/go-ethereum/event/feed.go new file mode 100644 index 0000000..b1b597f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/feed.go @@ -0,0 +1,243 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import ( + "errors" + "reflect" + "sync" +) + +var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type") + +// Feed implements one-to-many subscriptions where the carrier of events is a channel. +// Values sent to a Feed are delivered to all subscribed channels simultaneously. +// +// Feeds can only be used with a single type. The type is determined by the first Send or +// Subscribe operation. Subsequent calls to these methods panic if the type does not +// match. +// +// The zero value is ready to use. +type Feed struct { + once sync.Once // ensures that init only runs once + sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases. + removeSub chan interface{} // interrupts Send + sendCases caseList // the active set of select cases used by Send + + // The inbox holds newly subscribed channels until they are added to sendCases. + mu sync.Mutex + inbox caseList + etype reflect.Type + closed bool +} + +// This is the index of the first actual subscription channel in sendCases. +// sendCases[0] is a SelectRecv case for the removeSub channel. +const firstSubSendCase = 1 + +type feedTypeError struct { + got, want reflect.Type + op string +} + +func (e feedTypeError) Error() string { + return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String() +} + +func (f *Feed) init() { + f.removeSub = make(chan interface{}) + f.sendLock = make(chan struct{}, 1) + f.sendLock <- struct{}{} + f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}} +} + +// Subscribe adds a channel to the feed. Future sends will be delivered on the channel +// until the subscription is canceled. All channels added must have the same element type. +// +// The channel should have ample buffer space to avoid blocking other subscribers. +// Slow subscribers are not dropped. +func (f *Feed) Subscribe(channel interface{}) Subscription { + f.once.Do(f.init) + + chanval := reflect.ValueOf(channel) + chantyp := chanval.Type() + if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 { + panic(errBadChannel) + } + sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)} + + f.mu.Lock() + defer f.mu.Unlock() + if !f.typecheck(chantyp.Elem()) { + panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)}) + } + // Add the select case to the inbox. + // The next Send will add it to f.sendCases. + cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval} + f.inbox = append(f.inbox, cas) + return sub +} + +// note: callers must hold f.mu +func (f *Feed) typecheck(typ reflect.Type) bool { + if f.etype == nil { + f.etype = typ + return true + } + return f.etype == typ +} + +func (f *Feed) remove(sub *feedSub) { + // Delete from inbox first, which covers channels + // that have not been added to f.sendCases yet. + ch := sub.channel.Interface() + f.mu.Lock() + index := f.inbox.find(ch) + if index != -1 { + f.inbox = f.inbox.delete(index) + f.mu.Unlock() + return + } + f.mu.Unlock() + + select { + case f.removeSub <- ch: + // Send will remove the channel from f.sendCases. + case <-f.sendLock: + // No Send is in progress, delete the channel now that we have the send lock. + f.sendCases = f.sendCases.delete(f.sendCases.find(ch)) + f.sendLock <- struct{}{} + } +} + +// Send delivers to all subscribed channels simultaneously. +// It returns the number of subscribers that the value was sent to. +func (f *Feed) Send(value interface{}) (nsent int) { + f.once.Do(f.init) + <-f.sendLock + + // Add new cases from the inbox after taking the send lock. + f.mu.Lock() + f.sendCases = append(f.sendCases, f.inbox...) + f.inbox = nil + f.mu.Unlock() + + // Set the sent value on all channels. + rvalue := reflect.ValueOf(value) + if !f.typecheck(rvalue.Type()) { + f.sendLock <- struct{}{} + panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype}) + } + for i := firstSubSendCase; i < len(f.sendCases); i++ { + f.sendCases[i].Send = rvalue + } + + // Send until all channels except removeSub have been chosen. + cases := f.sendCases + for { + // Fast path: try sending without blocking before adding to the select set. + // This should usually succeed if subscribers are fast enough and have free + // buffer space. + for i := firstSubSendCase; i < len(cases); i++ { + if cases[i].Chan.TrySend(rvalue) { + nsent++ + cases = cases.deactivate(i) + i-- + } + } + if len(cases) == firstSubSendCase { + break + } + // Select on all the receivers, waiting for them to unblock. + chosen, recv, _ := reflect.Select(cases) + if chosen == 0 /* <-f.removeSub */ { + index := f.sendCases.find(recv.Interface()) + f.sendCases = f.sendCases.delete(index) + if index >= 0 && index < len(cases) { + cases = f.sendCases[:len(cases)-1] + } + } else { + cases = cases.deactivate(chosen) + nsent++ + } + } + + // Forget about the sent value and hand off the send lock. + for i := firstSubSendCase; i < len(f.sendCases); i++ { + f.sendCases[i].Send = reflect.Value{} + } + f.sendLock <- struct{}{} + return nsent +} + +type feedSub struct { + feed *Feed + channel reflect.Value + errOnce sync.Once + err chan error +} + +func (sub *feedSub) Unsubscribe() { + sub.errOnce.Do(func() { + sub.feed.remove(sub) + close(sub.err) + }) +} + +func (sub *feedSub) Err() <-chan error { + return sub.err +} + +type caseList []reflect.SelectCase + +// find returns the index of a case containing the given channel. +func (cs caseList) find(channel interface{}) int { + for i, cas := range cs { + if cas.Chan.Interface() == channel { + return i + } + } + return -1 +} + +// delete removes the given case from cs. +func (cs caseList) delete(index int) caseList { + return append(cs[:index], cs[index+1:]...) +} + +// deactivate moves the case at index into the non-accessible portion of the cs slice. +func (cs caseList) deactivate(index int) caseList { + last := len(cs) - 1 + cs[index], cs[last] = cs[last], cs[index] + return cs[:last] +} + +// func (cs caseList) String() string { +// s := "[" +// for i, cas := range cs { +// if i != 0 { +// s += ", " +// } +// switch cas.Dir { +// case reflect.SelectSend: +// s += fmt.Sprintf("%v<-", cas.Chan.Interface()) +// case reflect.SelectRecv: +// s += fmt.Sprintf("<-%v", cas.Chan.Interface()) +// } +// } +// return s + "]" +// } diff --git a/vendor/github.com/ethereum/go-ethereum/event/feed_test.go b/vendor/github.com/ethereum/go-ethereum/event/feed_test.go new file mode 100644 index 0000000..a82c103 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/feed_test.go @@ -0,0 +1,294 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import ( + "fmt" + "reflect" + "sync" + "testing" + "time" +) + +func TestFeedPanics(t *testing.T) { + { + var f Feed + f.Send(int(2)) + want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} + if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { + t.Error(err) + } + } + { + var f Feed + ch := make(chan int) + f.Subscribe(ch) + want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} + if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { + t.Error(err) + } + } + { + var f Feed + f.Send(int(2)) + want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))} + if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil { + t.Error(err) + } + } + { + var f Feed + if err := checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) }); err != nil { + t.Error(err) + } + } + { + var f Feed + if err := checkPanic(errBadChannel, func() { f.Subscribe(int(0)) }); err != nil { + t.Error(err) + } + } +} + +func checkPanic(want error, fn func()) (err error) { + defer func() { + panic := recover() + if panic == nil { + err = fmt.Errorf("didn't panic") + } else if !reflect.DeepEqual(panic, want) { + err = fmt.Errorf("panicked with wrong error: got %q, want %q", panic, want) + } + }() + fn() + return nil +} + +func TestFeed(t *testing.T) { + var feed Feed + var done, subscribed sync.WaitGroup + subscriber := func(i int) { + defer done.Done() + + subchan := make(chan int) + sub := feed.Subscribe(subchan) + timeout := time.NewTimer(2 * time.Second) + subscribed.Done() + + select { + case v := <-subchan: + if v != 1 { + t.Errorf("%d: received value %d, want 1", i, v) + } + case <-timeout.C: + t.Errorf("%d: receive timeout", i) + } + + sub.Unsubscribe() + select { + case _, ok := <-sub.Err(): + if ok { + t.Errorf("%d: error channel not closed after unsubscribe", i) + } + case <-timeout.C: + t.Errorf("%d: unsubscribe timeout", i) + } + } + + const n = 1000 + done.Add(n) + subscribed.Add(n) + for i := 0; i < n; i++ { + go subscriber(i) + } + subscribed.Wait() + if nsent := feed.Send(1); nsent != n { + t.Errorf("first send delivered %d times, want %d", nsent, n) + } + if nsent := feed.Send(2); nsent != 0 { + t.Errorf("second send delivered %d times, want 0", nsent) + } + done.Wait() +} + +func TestFeedSubscribeSameChannel(t *testing.T) { + var ( + feed Feed + done sync.WaitGroup + ch = make(chan int) + sub1 = feed.Subscribe(ch) + sub2 = feed.Subscribe(ch) + _ = feed.Subscribe(ch) + ) + expectSends := func(value, n int) { + if nsent := feed.Send(value); nsent != n { + t.Errorf("send delivered %d times, want %d", nsent, n) + } + done.Done() + } + expectRecv := func(wantValue, n int) { + for i := 0; i < n; i++ { + if v := <-ch; v != wantValue { + t.Errorf("received %d, want %d", v, wantValue) + } + } + } + + done.Add(1) + go expectSends(1, 3) + expectRecv(1, 3) + done.Wait() + + sub1.Unsubscribe() + + done.Add(1) + go expectSends(2, 2) + expectRecv(2, 2) + done.Wait() + + sub2.Unsubscribe() + + done.Add(1) + go expectSends(3, 1) + expectRecv(3, 1) + done.Wait() +} + +func TestFeedSubscribeBlockedPost(t *testing.T) { + var ( + feed Feed + nsends = 2000 + ch1 = make(chan int) + ch2 = make(chan int) + wg sync.WaitGroup + ) + defer wg.Wait() + + feed.Subscribe(ch1) + wg.Add(nsends) + for i := 0; i < nsends; i++ { + go func() { + feed.Send(99) + wg.Done() + }() + } + + sub2 := feed.Subscribe(ch2) + defer sub2.Unsubscribe() + + // We're done when ch1 has received N times. + // The number of receives on ch2 depends on scheduling. + for i := 0; i < nsends; { + select { + case <-ch1: + i++ + case <-ch2: + } + } +} + +func TestFeedUnsubscribeBlockedPost(t *testing.T) { + var ( + feed Feed + nsends = 200 + chans = make([]chan int, 2000) + subs = make([]Subscription, len(chans)) + bchan = make(chan int) + bsub = feed.Subscribe(bchan) + wg sync.WaitGroup + ) + for i := range chans { + chans[i] = make(chan int, nsends) + } + + // Queue up some Sends. None of these can make progress while bchan isn't read. + wg.Add(nsends) + for i := 0; i < nsends; i++ { + go func() { + feed.Send(99) + wg.Done() + }() + } + // Subscribe the other channels. + for i, ch := range chans { + subs[i] = feed.Subscribe(ch) + } + // Unsubscribe them again. + for _, sub := range subs { + sub.Unsubscribe() + } + // Unblock the Sends. + bsub.Unsubscribe() + wg.Wait() +} + +func TestFeedUnsubscribeFromInbox(t *testing.T) { + var ( + feed Feed + ch1 = make(chan int) + ch2 = make(chan int) + sub1 = feed.Subscribe(ch1) + sub2 = feed.Subscribe(ch1) + sub3 = feed.Subscribe(ch2) + ) + if len(feed.inbox) != 3 { + t.Errorf("inbox length != 3 after subscribe") + } + if len(feed.sendCases) != 1 { + t.Errorf("sendCases is non-empty after unsubscribe") + } + + sub1.Unsubscribe() + sub2.Unsubscribe() + sub3.Unsubscribe() + if len(feed.inbox) != 0 { + t.Errorf("inbox is non-empty after unsubscribe") + } + if len(feed.sendCases) != 1 { + t.Errorf("sendCases is non-empty after unsubscribe") + } +} + +func BenchmarkFeedSend1000(b *testing.B) { + var ( + done sync.WaitGroup + feed Feed + nsubs = 1000 + ) + subscriber := func(ch <-chan int) { + for i := 0; i < b.N; i++ { + <-ch + } + done.Done() + } + done.Add(nsubs) + for i := 0; i < nsubs; i++ { + ch := make(chan int, 200) + feed.Subscribe(ch) + go subscriber(ch) + } + + // The actual benchmark. + b.ResetTimer() + for i := 0; i < b.N; i++ { + if feed.Send(i) != nsubs { + panic("wrong number of sends") + } + } + + b.StopTimer() + done.Wait() +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/filter/filter.go b/vendor/github.com/ethereum/go-ethereum/event/filter/filter.go new file mode 100644 index 0000000..b1fbf30 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/filter/filter.go @@ -0,0 +1,95 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package filter implements event filters. +package filter + +import "reflect" + +type Filter interface { + Compare(Filter) bool + Trigger(data interface{}) +} + +type FilterEvent struct { + filter Filter + data interface{} +} + +type Filters struct { + id int + watchers map[int]Filter + ch chan FilterEvent + + quit chan struct{} +} + +func New() *Filters { + return &Filters{ + ch: make(chan FilterEvent), + watchers: make(map[int]Filter), + quit: make(chan struct{}), + } +} + +func (self *Filters) Start() { + go self.loop() +} + +func (self *Filters) Stop() { + close(self.quit) +} + +func (self *Filters) Notify(filter Filter, data interface{}) { + self.ch <- FilterEvent{filter, data} +} + +func (self *Filters) Install(watcher Filter) int { + self.watchers[self.id] = watcher + self.id++ + + return self.id - 1 +} + +func (self *Filters) Uninstall(id int) { + delete(self.watchers, id) +} + +func (self *Filters) loop() { +out: + for { + select { + case <-self.quit: + break out + case event := <-self.ch: + for _, watcher := range self.watchers { + if reflect.TypeOf(watcher) == reflect.TypeOf(event.filter) { + if watcher.Compare(event.filter) { + watcher.Trigger(event.data) + } + } + } + } + } +} + +func (self *Filters) Match(a, b Filter) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b) +} + +func (self *Filters) Get(i int) Filter { + return self.watchers[i] +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/filter/filter_test.go b/vendor/github.com/ethereum/go-ethereum/event/filter/filter_test.go new file mode 100644 index 0000000..dcc9112 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/filter/filter_test.go @@ -0,0 +1,60 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filter + +import ( + "testing" + "time" +) + +// Simple test to check if baseline matching/mismatching filtering works. +func TestFilters(t *testing.T) { + fm := New() + fm.Start() + + // Register two filters to catch posted data + first := make(chan struct{}) + fm.Install(Generic{ + Str1: "hello", + Fn: func(data interface{}) { + first <- struct{}{} + }, + }) + second := make(chan struct{}) + fm.Install(Generic{ + Str1: "hello1", + Str2: "hello", + Fn: func(data interface{}) { + second <- struct{}{} + }, + }) + // Post an event that should only match the first filter + fm.Notify(Generic{Str1: "hello"}, true) + fm.Stop() + + // Ensure only the mathcing filters fire + select { + case <-first: + case <-time.After(100 * time.Millisecond): + t.Error("matching filter timed out") + } + select { + case <-second: + t.Error("mismatching filter fired") + case <-time.After(100 * time.Millisecond): + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/filter/generic_filter.go b/vendor/github.com/ethereum/go-ethereum/event/filter/generic_filter.go new file mode 100644 index 0000000..d679b8b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/filter/generic_filter.go @@ -0,0 +1,48 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package filter + +type Generic struct { + Str1, Str2, Str3 string + Data map[string]struct{} + + Fn func(data interface{}) +} + +// self = registered, f = incoming +func (self Generic) Compare(f Filter) bool { + var strMatch, dataMatch = true, true + + filter := f.(Generic) + if (len(self.Str1) > 0 && filter.Str1 != self.Str1) || + (len(self.Str2) > 0 && filter.Str2 != self.Str2) || + (len(self.Str3) > 0 && filter.Str3 != self.Str3) { + strMatch = false + } + + for k := range self.Data { + if _, ok := filter.Data[k]; !ok { + return false + } + } + + return strMatch && dataMatch +} + +func (self Generic) Trigger(data interface{}) { + self.Fn(data) +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/subscription.go b/vendor/github.com/ethereum/go-ethereum/event/subscription.go new file mode 100644 index 0000000..83bd212 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/subscription.go @@ -0,0 +1,275 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "golang.org/x/net/context" +) + +// Subscription represents a stream of events. The carrier of the events is typically a +// channel, but isn't part of the interface. +// +// Subscriptions can fail while established. Failures are reported through an error +// channel. It receives a value if there is an issue with the subscription (e.g. the +// network connection delivering the events has been closed). Only one value will ever be +// sent. +// +// The error channel is closed when the subscription ends successfully (i.e. when the +// source of events is closed). It is also closed when Unsubscribe is called. +// +// The Unsubscribe method cancels the sending of events. You must call Unsubscribe in all +// cases to ensure that resources related to the subscription are released. It can be +// called any number of times. +type Subscription interface { + Err() <-chan error // returns the error channel + Unsubscribe() // cancels sending of events, closing the error channel +} + +// NewSubscription runs a producer function as a subscription in a new goroutine. The +// channel given to the producer is closed when Unsubscribe is called. If fn returns an +// error, it is sent on the subscription's error channel. +func NewSubscription(producer func(<-chan struct{}) error) Subscription { + s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)} + go func() { + defer close(s.err) + err := producer(s.unsub) + s.mu.Lock() + defer s.mu.Unlock() + if !s.unsubscribed { + if err != nil { + s.err <- err + } + s.unsubscribed = true + } + }() + return s +} + +type funcSub struct { + unsub chan struct{} + err chan error + mu sync.Mutex + unsubscribed bool +} + +func (s *funcSub) Unsubscribe() { + s.mu.Lock() + if s.unsubscribed { + s.mu.Unlock() + return + } + s.unsubscribed = true + close(s.unsub) + s.mu.Unlock() + // Wait for producer shutdown. + <-s.err +} + +func (s *funcSub) Err() <-chan error { + return s.err +} + +// Resubscribe calls fn repeatedly to keep a subscription established. When the +// subscription is established, Resubscribe waits for it to fail and calls fn again. This +// process repeats until Unsubscribe is called or the active subscription ends +// successfully. +// +// Resubscribe applies backoff between calls to fn. The time between calls is adapted +// based on the error rate, but will never exceed backoffMax. +func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { + s := &resubscribeSub{ + waitTime: backoffMax / 10, + backoffMax: backoffMax, + fn: fn, + err: make(chan error), + unsub: make(chan struct{}), + } + go s.loop() + return s +} + +// A ResubscribeFunc attempts to establish a subscription. +type ResubscribeFunc func(context.Context) (Subscription, error) + +type resubscribeSub struct { + fn ResubscribeFunc + err chan error + unsub chan struct{} + unsubOnce sync.Once + lastTry mclock.AbsTime + waitTime, backoffMax time.Duration +} + +func (s *resubscribeSub) Unsubscribe() { + s.unsubOnce.Do(func() { + s.unsub <- struct{}{} + <-s.err + }) +} + +func (s *resubscribeSub) Err() <-chan error { + return s.err +} + +func (s *resubscribeSub) loop() { + defer close(s.err) + var done bool + for !done { + sub := s.subscribe() + if sub == nil { + break + } + done = s.waitForError(sub) + sub.Unsubscribe() + } +} + +func (s *resubscribeSub) subscribe() Subscription { + subscribed := make(chan error) + var sub Subscription +retry: + for { + s.lastTry = mclock.Now() + ctx, cancel := context.WithCancel(context.Background()) + go func() { + rsub, err := s.fn(ctx) + sub = rsub + subscribed <- err + }() + select { + case err := <-subscribed: + cancel() + if err != nil { + // Subscribing failed, wait before launching the next try. + if s.backoffWait() { + return nil + } + continue retry + } + if sub == nil { + panic("event: ResubscribeFunc returned nil subscription and no error") + } + return sub + case <-s.unsub: + cancel() + return nil + } + } +} + +func (s *resubscribeSub) waitForError(sub Subscription) bool { + defer sub.Unsubscribe() + select { + case err := <-sub.Err(): + return err == nil + case <-s.unsub: + return true + } +} + +func (s *resubscribeSub) backoffWait() bool { + if time.Duration(mclock.Now()-s.lastTry) > s.backoffMax { + s.waitTime = s.backoffMax / 10 + } else { + s.waitTime *= 2 + if s.waitTime > s.backoffMax { + s.waitTime = s.backoffMax + } + } + + t := time.NewTimer(s.waitTime) + defer t.Stop() + select { + case <-t.C: + return false + case <-s.unsub: + return true + } +} + +// SubscriptionScope provides a facility to unsubscribe multiple subscriptions at once. +// +// For code that handle more than one subscription, a scope can be used to conveniently +// unsubscribe all of them with a single call. The example demonstrates a typical use in a +// larger program. +// +// The zero value is ready to use. +type SubscriptionScope struct { + mu sync.Mutex + subs map[*scopeSub]struct{} + closed bool +} + +type scopeSub struct { + sc *SubscriptionScope + s Subscription +} + +// Track starts tracking a subscription. If the scope is closed, Track returns nil. The +// returned subscription is a wrapper. Unsubscribing the wrapper removes it from the +// scope. +func (sc *SubscriptionScope) Track(s Subscription) Subscription { + sc.mu.Lock() + defer sc.mu.Unlock() + if sc.closed { + return nil + } + if sc.subs == nil { + sc.subs = make(map[*scopeSub]struct{}) + } + ss := &scopeSub{sc, s} + sc.subs[ss] = struct{}{} + return ss +} + +// Close calls Unsubscribe on all tracked subscriptions and prevents further additions to +// the tracked set. Calls to Track after Close return nil. +func (sc *SubscriptionScope) Close() { + sc.mu.Lock() + defer sc.mu.Unlock() + if sc.closed { + return + } + sc.closed = true + for s := range sc.subs { + s.s.Unsubscribe() + } + sc.subs = nil +} + +// Count returns the number of tracked subscriptions. +// It is meant to be used for debugging. +func (sc *SubscriptionScope) Count() int { + sc.mu.Lock() + defer sc.mu.Unlock() + return len(sc.subs) +} + +func (s *scopeSub) Unsubscribe() { + s.s.Unsubscribe() + s.sc.mu.Lock() + defer s.sc.mu.Unlock() + delete(s.sc.subs, s) +} + +func (s *scopeSub) Err() <-chan error { + return s.s.Err() +} diff --git a/vendor/github.com/ethereum/go-ethereum/event/subscription_test.go b/vendor/github.com/ethereum/go-ethereum/event/subscription_test.go new file mode 100644 index 0000000..a4fe302 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/event/subscription_test.go @@ -0,0 +1,121 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package event + +import ( + "errors" + "testing" + "time" + + "golang.org/x/net/context" +) + +var errInts = errors.New("error in subscribeInts") + +func subscribeInts(max, fail int, c chan<- int) Subscription { + return NewSubscription(func(quit <-chan struct{}) error { + for i := 0; i < max; i++ { + if i >= fail { + return errInts + } + select { + case c <- i: + case <-quit: + return nil + } + } + return nil + }) +} + +func TestNewSubscriptionError(t *testing.T) { + t.Parallel() + + channel := make(chan int) + sub := subscribeInts(10, 2, channel) +loop: + for want := 0; want < 10; want++ { + select { + case got := <-channel: + if got != want { + t.Fatalf("wrong int %d, want %d", got, want) + } + case err := <-sub.Err(): + if err != errInts { + t.Fatalf("wrong error: got %q, want %q", err, errInts) + } + if want != 2 { + t.Fatalf("got errInts at int %d, should be received at 2", want) + } + break loop + } + } + sub.Unsubscribe() + + err, ok := <-sub.Err() + if err != nil { + t.Fatal("got non-nil error after Unsubscribe") + } + if ok { + t.Fatal("channel still open after Unsubscribe") + } +} + +func TestResubscribe(t *testing.T) { + t.Parallel() + + var i int + nfails := 6 + sub := Resubscribe(100*time.Millisecond, func(ctx context.Context) (Subscription, error) { + // fmt.Printf("call #%d @ %v\n", i, time.Now()) + i++ + if i == 2 { + // Delay the second failure a bit to reset the resubscribe interval. + time.Sleep(200 * time.Millisecond) + } + if i < nfails { + return nil, errors.New("oops") + } + sub := NewSubscription(func(unsubscribed <-chan struct{}) error { return nil }) + return sub, nil + }) + + <-sub.Err() + if i != nfails { + t.Fatalf("resubscribe function called %d times, want %d times", i, nfails) + } +} + +func TestResubscribeAbort(t *testing.T) { + t.Parallel() + + done := make(chan error) + sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) { + select { + case <-ctx.Done(): + done <- nil + case <-time.After(2 * time.Second): + done <- errors.New("context given to resubscribe function not canceled within 2s") + } + return nil, nil + }) + + sub.Unsubscribe() + if err := <-done; err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/interfaces.go b/vendor/github.com/ethereum/go-ethereum/interfaces.go new file mode 100644 index 0000000..f7e71a3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/interfaces.go @@ -0,0 +1,210 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethereum defines interfaces for interacting with Ethereum. +package ethereum + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "golang.org/x/net/context" +) + +// NotFound is returned by API methods if the requested item does not exist. +var NotFound = errors.New("not found") + +// TODO: move subscription to package event + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription interface { + // Unsubscribe cancels the sending of events to the data channel + // and closes the error channel. + Unsubscribe() + // Err returns the subscription error channel. The error channel receives + // a value if there is an issue with the subscription (e.g. the network connection + // delivering the events has been closed). Only one value will ever be sent. + // The error channel is closed by Unsubscribe. + Err() <-chan error +} + +// ChainReader provides access to the blockchain. The methods in this interface access raw +// data from either the canonical chain (when requesting by block number) or any +// blockchain fork that was previously downloaded and processed by the node. The block +// number argument can be nil to select the latest canonical block. Reading block headers +// should be preferred over full blocks whenever possible. +// +// The returned error is NotFound if the requested item does not exist. +type ChainReader interface { + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) + TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) + TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) + + // This method subscribes to notifications about changes of the head block of + // the canonical chain. + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error) +} + +// TransactionReader provides access to past transactions and their receipts. +// Implementations may impose arbitrary restrictions on the transactions and receipts that +// can be retrieved. Historic transactions may not be available. +// +// Avoid relying on this interface if possible. Contract logs (through the LogFilterer +// interface) are more reliable and usually safer in the presence of chain +// reorganisations. +// +// The returned error is NotFound if the requested item does not exist. +type TransactionReader interface { + // TransactionByHash checks the pool of pending transactions in addition to the + // blockchain. The isPending return value indicates whether the transaction has been + // mined yet. Note that the transaction may not be part of the canonical chain even if + // it's not pending. + TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) + // TransactionReceipt returns the receipt of a mined transaction. Note that the + // transaction may not be included in the current canonical chain even if a receipt + // exists. + TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) +} + +// ChainStateReader wraps access to the state trie of the canonical blockchain. Note that +// implementations of the interface may be unable to return state values for old blocks. +// In many cases, using CallContract can be preferable to reading raw contract storage. +type ChainStateReader interface { + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) + CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + StartingBlock uint64 // Block number where sync began + CurrentBlock uint64 // Current block number where sync is at + HighestBlock uint64 // Highest alleged block number in the chain + PulledStates uint64 // Number of state trie entries already downloaded + KnownStates uint64 // Total number os state trie entries known about +} + +// ChainSyncReader wraps access to the node's current sync status. If there's no +// sync currently running, it returns nil. +type ChainSyncReader interface { + SyncProgress(ctx context.Context) (*SyncProgress, error) +} + +// CallMsg contains parameters for contract calls. +type CallMsg struct { + From common.Address // the sender of the 'transaction' + To *common.Address // the destination contract (nil for contract creation) + Gas *big.Int // if nil, the call executes with near-infinite gas + GasPrice *big.Int // wei <-> gas exchange ratio + Value *big.Int // amount of wei sent along with the call + Data []byte // input data, usually an ABI-encoded contract method invocation +} + +// A ContractCaller provides contract calls, essentially transactions that are executed by +// the EVM but not mined into the blockchain. ContractCall is a low-level method to +// execute such calls. For applications which are structured around specific contracts, +// the abigen tool provides a nicer, properly typed way to perform calls. +type ContractCaller interface { + CallContract(ctx context.Context, call CallMsg, blockNumber *big.Int) ([]byte, error) +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + FromBlock *big.Int // beginning of the queried range, nil means genesis block + ToBlock *big.Int // end of the range, nil means latest block + Addresses []common.Address // restricts matches to events created by specific contracts + + // The Topic list restricts matches to particular event topics. Each event has a list + // of topics. Topics matches a prefix of that list. An empty element slice matches any + // topic. Non-empty elements represent an alternative that matches any of the + // contained topics. + // + // Examples: + // {} or nil matches any topic list + // {{A}} matches topic A in first position + // {{}, {B}} matches any topic in first position, B in second position + // {{A}}, {B}} matches topic A in first position, B in second position + // {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position + Topics [][]common.Hash +} + +// LogFilterer provides access to contract log events using a one-off query or continuous +// event subscription. +// +// Logs received through a streaming query subscription may have Removed set to true, +// indicating that the log was reverted due to a chain reorganisation. +type LogFilterer interface { + FilterLogs(ctx context.Context, q FilterQuery) ([]types.Log, error) + SubscribeFilterLogs(ctx context.Context, q FilterQuery, ch chan<- types.Log) (Subscription, error) +} + +// TransactionSender wraps transaction sending. The SendTransaction method injects a +// signed transaction into the pending transaction pool for execution. If the transaction +// was a contract creation, the TransactionReceipt method can be used to retrieve the +// contract address after the transaction has been mined. +// +// The transaction must be signed and have a valid nonce to be included. Consumers of the +// API can use package accounts to maintain local private keys and need can retrieve the +// next available nonce using PendingNonceAt. +type TransactionSender interface { + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +// GasPricer wraps the gas price oracle, which monitors the blockchain to determine the +// optimal gas price given current fee market conditions. +type GasPricer interface { + SuggestGasPrice(ctx context.Context) (*big.Int, error) +} + +// A PendingStateReader provides access to the pending state, which is the result of all +// known executable transactions which have not yet been included in the blockchain. It is +// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value +// transfers) initiated by the user. The PendingNonceAt operation is a good way to +// retrieve the next available transaction nonce for a specific account. +type PendingStateReader interface { + PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) + PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) + PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + PendingTransactionCount(ctx context.Context) (uint, error) +} + +// PendingContractCaller can be used to perform calls against the pending state. +type PendingContractCaller interface { + PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) +} + +// GasEstimator wraps EstimateGas, which tries to estimate the gas needed to execute a +// specific transaction based on the pending state. There is no guarantee that this is the +// true gas limit requirement as other transactions may be added or removed by miners, but +// it should provide a basis for setting a reasonable default. +type GasEstimator interface { + EstimateGas(ctx context.Context, call CallMsg) (usedGas *big.Int, err error) +} + +// A PendingStateEventer provides access to real time notifications about changes to the +// pending state. +type PendingStateEventer interface { + SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/build/archive.go b/vendor/github.com/ethereum/go-ethereum/internal/build/archive.go new file mode 100644 index 0000000..ac680ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/build/archive.go @@ -0,0 +1,185 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +type Archive interface { + // Directory adds a new directory entry to the archive and sets the + // directory for subsequent calls to Header. + Directory(name string) error + + // Header adds a new file to the archive. The file is added to the directory + // set by Directory. The content of the file must be written to the returned + // writer. + Header(os.FileInfo) (io.Writer, error) + + // Close flushes the archive and closes the underlying file. + Close() error +} + +func NewArchive(file *os.File) (Archive, string) { + switch { + case strings.HasSuffix(file.Name(), ".zip"): + return NewZipArchive(file), strings.TrimSuffix(file.Name(), ".zip") + case strings.HasSuffix(file.Name(), ".tar.gz"): + return NewTarballArchive(file), strings.TrimSuffix(file.Name(), ".tar.gz") + default: + return nil, "" + } +} + +// AddFile appends an existing file to an archive. +func AddFile(a Archive, file string) error { + fd, err := os.Open(file) + if err != nil { + return err + } + defer fd.Close() + fi, err := fd.Stat() + if err != nil { + return err + } + w, err := a.Header(fi) + if err != nil { + return err + } + if _, err := io.Copy(w, fd); err != nil { + return err + } + return nil +} + +// WriteArchive creates an archive containing the given files. +func WriteArchive(name string, files []string) (err error) { + archfd, err := os.Create(name) + if err != nil { + return err + } + + defer func() { + archfd.Close() + // Remove the half-written archive on failure. + if err != nil { + os.Remove(name) + } + }() + archive, basename := NewArchive(archfd) + if archive == nil { + return fmt.Errorf("unknown archive extension") + } + fmt.Println(name) + if err := archive.Directory(basename); err != nil { + return err + } + for _, file := range files { + fmt.Println(" +", filepath.Base(file)) + if err := AddFile(archive, file); err != nil { + return err + } + } + return archive.Close() +} + +type ZipArchive struct { + dir string + zipw *zip.Writer + file io.Closer +} + +func NewZipArchive(w io.WriteCloser) Archive { + return &ZipArchive{"", zip.NewWriter(w), w} +} + +func (a *ZipArchive) Directory(name string) error { + a.dir = name + "/" + return nil +} + +func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) { + head, err := zip.FileInfoHeader(fi) + if err != nil { + return nil, fmt.Errorf("can't make zip header: %v", err) + } + head.Name = a.dir + head.Name + head.Method = zip.Deflate + w, err := a.zipw.CreateHeader(head) + if err != nil { + return nil, fmt.Errorf("can't add zip header: %v", err) + } + return w, nil +} + +func (a *ZipArchive) Close() error { + if err := a.zipw.Close(); err != nil { + return err + } + return a.file.Close() +} + +type TarballArchive struct { + dir string + tarw *tar.Writer + gzw *gzip.Writer + file io.Closer +} + +func NewTarballArchive(w io.WriteCloser) Archive { + gzw := gzip.NewWriter(w) + tarw := tar.NewWriter(gzw) + return &TarballArchive{"", tarw, gzw, w} +} + +func (a *TarballArchive) Directory(name string) error { + a.dir = name + "/" + return a.tarw.WriteHeader(&tar.Header{ + Name: a.dir, + Mode: 0755, + Typeflag: tar.TypeDir, + }) +} + +func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) { + head, err := tar.FileInfoHeader(fi, "") + if err != nil { + return nil, fmt.Errorf("can't make tar header: %v", err) + } + head.Name = a.dir + head.Name + if err := a.tarw.WriteHeader(head); err != nil { + return nil, fmt.Errorf("can't add tar header: %v", err) + } + return a.tarw, nil +} + +func (a *TarballArchive) Close() error { + if err := a.tarw.Close(); err != nil { + return err + } + if err := a.gzw.Close(); err != nil { + return err + } + return a.file.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/build/azure.go b/vendor/github.com/ethereum/go-ethereum/internal/build/azure.go new file mode 100644 index 0000000..32f5355 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/build/azure.go @@ -0,0 +1,65 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "fmt" + "os" + + "github.com/Azure/azure-sdk-for-go/storage" +) + +// AzureBlobstoreConfig is an authentication and configuration struct containing +// the data needed by the Azure SDK to interact with a speicifc container in the +// blobstore. +type AzureBlobstoreConfig struct { + Account string // Account name to authorize API requests with + Token string // Access token for the above account + Container string // Blob container to upload files into +} + +// AzureBlobstoreUpload uploads a local file to the Azure Blob Storage. Note, this +// method assumes a max file size of 64MB (Azure limitation). Larger files will +// need a multi API call approach implemented. +// +// See: https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx#Anchor_3 +func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig) error { + if *DryRunFlag { + fmt.Printf("would upload %q to %s/%s/%s\n", path, config.Account, config.Container, name) + return nil + } + + // Create an authenticated client against the Azure cloud + rawClient, err := storage.NewBasicClient(config.Account, config.Token) + if err != nil { + return err + } + client := rawClient.GetBlobService() + + // Stream the file to upload into the designated blobstore container + in, err := os.Open(path) + if err != nil { + return err + } + defer in.Close() + + info, err := in.Stat() + if err != nil { + return err + } + return client.CreateBlockBlobFromReader(config.Container, name, uint64(info.Size()), in, nil) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/build/env.go b/vendor/github.com/ethereum/go-ethereum/internal/build/env.go new file mode 100644 index 0000000..15b2dfe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/build/env.go @@ -0,0 +1,122 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "flag" + "fmt" + "os" + "strings" +) + +var ( + // These flags override values in build env. + GitCommitFlag = flag.String("git-commit", "", `Overrides git commit hash embedded into executables`) + GitBranchFlag = flag.String("git-branch", "", `Overrides git branch being built`) + GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`) + BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`) + PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`) +) + +// Environment contains metadata provided by the build environment. +type Environment struct { + Name string // name of the environment + Repo string // name of GitHub repo + Commit, Branch, Tag string // Git info + Buildnum string + IsPullRequest bool +} + +func (env Environment) String() string { + return fmt.Sprintf("%s env (commit:%s branch:%s tag:%s buildnum:%s pr:%t)", + env.Name, env.Commit, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest) +} + +// Env returns metadata about the current CI environment, falling back to LocalEnv +// if not running on CI. +func Env() Environment { + switch { + case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true": + return Environment{ + Name: "travis", + Repo: os.Getenv("TRAVIS_REPO_SLUG"), + Commit: os.Getenv("TRAVIS_COMMIT"), + Branch: os.Getenv("TRAVIS_BRANCH"), + Tag: os.Getenv("TRAVIS_TAG"), + Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"), + IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false", + } + case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True": + return Environment{ + Name: "appveyor", + Repo: os.Getenv("APPVEYOR_REPO_NAME"), + Commit: os.Getenv("APPVEYOR_REPO_COMMIT"), + Branch: os.Getenv("APPVEYOR_REPO_BRANCH"), + Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"), + Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"), + IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", + } + default: + return LocalEnv() + } +} + +// LocalEnv returns build environment metadata gathered from git. +func LocalEnv() Environment { + env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"}) + if _, err := os.Stat(".git"); err != nil { + return env + } + if env.Commit == "" { + env.Commit = RunGit("rev-parse", "HEAD") + } + if env.Branch == "" { + if b := RunGit("rev-parse", "--abbrev-ref", "HEAD"); b != "HEAD" { + env.Branch = b + } + } + if env.Tag == "" { + env.Tag = firstLine(RunGit("tag", "-l", "--points-at", "HEAD")) + } + return env +} + +func firstLine(s string) string { + return strings.Split(s, "\n")[0] +} + +func applyEnvFlags(env Environment) Environment { + if !flag.Parsed() { + panic("you need to call flag.Parse before Env or LocalEnv") + } + if *GitCommitFlag != "" { + env.Commit = *GitCommitFlag + } + if *GitBranchFlag != "" { + env.Branch = *GitBranchFlag + } + if *GitTagFlag != "" { + env.Tag = *GitTagFlag + } + if *BuildnumFlag != "" { + env.Buildnum = *BuildnumFlag + } + if *PullRequestFlag { + env.IsPullRequest = true + } + return env +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/build/pgp.go b/vendor/github.com/ethereum/go-ethereum/internal/build/pgp.go new file mode 100644 index 0000000..79ab9c0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/build/pgp.go @@ -0,0 +1,59 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// signFile reads the contents of an input file and signs it (in armored format) +// with the key provided, placing the signature into the output file. + +package build + +import ( + "bytes" + "fmt" + "os" + + "golang.org/x/crypto/openpgp" +) + +// PGPSignFile parses a PGP private key from the specified string and creates a +// signature file into the output parameter of the input file. +// +// Note, this method assumes a single key will be container in the pgpkey arg, +// furthermore that it is in armored format. +func PGPSignFile(input string, output string, pgpkey string) error { + // Parse the keyring and make sure we only have a single private key in it + keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pgpkey)) + if err != nil { + return err + } + if len(keys) != 1 { + return fmt.Errorf("key count mismatch: have %d, want %d", len(keys), 1) + } + // Create the input and output streams for signing + in, err := os.Open(input) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(output) + if err != nil { + return err + } + defer out.Close() + + // Generate the signature and return + return openpgp.ArmoredDetachSign(out, keys[0], in, nil) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/build/util.go b/vendor/github.com/ethereum/go-ethereum/internal/build/util.go new file mode 100644 index 0000000..1523a06 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/build/util.go @@ -0,0 +1,147 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" +) + +var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands") + +// MustRun executes the given command and exits the host process for +// any error. +func MustRun(cmd *exec.Cmd) { + fmt.Println(">>>", strings.Join(cmd.Args, " ")) + if !*DryRunFlag { + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + log.Fatal(err) + } + } +} + +func MustRunCommand(cmd string, args ...string) { + MustRun(exec.Command(cmd, args...)) +} + +// GOPATH returns the value that the GOPATH environment +// variable should be set to. +func GOPATH() string { + path := filepath.SplitList(os.Getenv("GOPATH")) + if len(path) == 0 { + log.Fatal("GOPATH is not set") + } + // Ensure that our internal vendor folder is on GOPATH + vendor, _ := filepath.Abs(filepath.Join("build", "_vendor")) + for _, dir := range path { + if dir == vendor { + return strings.Join(path, string(filepath.ListSeparator)) + } + } + newpath := append(path[:1], append([]string{vendor}, path[1:]...)...) + return strings.Join(newpath, string(filepath.ListSeparator)) +} + +// VERSION returns the content of the VERSION file. +func VERSION() string { + version, err := ioutil.ReadFile("VERSION") + if err != nil { + log.Fatal(err) + } + return string(bytes.TrimSpace(version)) +} + +var warnedAboutGit bool + +// RunGit runs a git subcommand and returns its output. +// The command must complete successfully. +func RunGit(args ...string) string { + cmd := exec.Command("git", args...) + var stdout, stderr bytes.Buffer + cmd.Stdout, cmd.Stderr = &stdout, &stderr + if err := cmd.Run(); err == exec.ErrNotFound { + if !warnedAboutGit { + log.Println("Warning: can't find 'git' in PATH") + warnedAboutGit = true + } + return "" + } else if err != nil { + log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String()) + } + return strings.TrimSpace(stdout.String()) +} + +// Render renders the given template file into outputFile. +func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) { + tpl := template.Must(template.ParseFiles(templateFile)) + render(tpl, outputFile, outputPerm, x) +} + +// RenderString renders the given template string into outputFile. +func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) { + tpl := template.Must(template.New("").Parse(templateContent)) + render(tpl, outputFile, outputPerm, x) +} + +func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) { + if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil { + log.Fatal(err) + } + out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm) + if err != nil { + log.Fatal(err) + } + if err := tpl.Execute(out, x); err != nil { + log.Fatal(err) + } + if err := out.Close(); err != nil { + log.Fatal(err) + } +} + +// CopyFile copies a file. +func CopyFile(dst, src string, mode os.FileMode) { + if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { + log.Fatal(err) + } + destFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + log.Fatal(err) + } + defer destFile.Close() + + srcFile, err := os.Open(src) + if err != nil { + log.Fatal(err) + } + defer srcFile.Close() + + if _, err := io.Copy(destFile, srcFile); err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/api.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/api.go new file mode 100644 index 0000000..9609154 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/api.go @@ -0,0 +1,207 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package debug interfaces Go runtime debugging facilities. +// This package is mostly glue code making these facilities available +// through the CLI and RPC subsystem. If you want to use them from Go code, +// use package runtime instead. +package debug + +import ( + "errors" + "io" + "os" + "os/user" + "path/filepath" + "runtime" + "runtime/debug" + "runtime/pprof" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// Handler is the global debugging handler. +var Handler = new(HandlerT) + +// HandlerT implements the debugging API. +// Do not create values of this type, use the one +// in the Handler variable instead. +type HandlerT struct { + mu sync.Mutex + cpuW io.WriteCloser + cpuFile string + traceW io.WriteCloser + traceFile string +} + +// Verbosity sets the glog verbosity ceiling. +// The verbosity of individual packages and source files +// can be raised using Vmodule. +func (*HandlerT) Verbosity(level int) { + glog.SetV(level) +} + +// Vmodule sets the glog verbosity pattern. See package +// glog for details on pattern syntax. +func (*HandlerT) Vmodule(pattern string) error { + return glog.GetVModule().Set(pattern) +} + +// BacktraceAt sets the glog backtrace location. +// See package glog for details on pattern syntax. +func (*HandlerT) BacktraceAt(location string) error { + return glog.GetTraceLocation().Set(location) +} + +// MemStats returns detailed runtime memory statistics. +func (*HandlerT) MemStats() *runtime.MemStats { + s := new(runtime.MemStats) + runtime.ReadMemStats(s) + return s +} + +// GcStats returns GC statistics. +func (*HandlerT) GcStats() *debug.GCStats { + s := new(debug.GCStats) + debug.ReadGCStats(s) + return s +} + +// CpuProfile turns on CPU profiling for nsec seconds and writes +// profile data to file. +func (h *HandlerT) CpuProfile(file string, nsec uint) error { + if err := h.StartCPUProfile(file); err != nil { + return err + } + time.Sleep(time.Duration(nsec) * time.Second) + h.StopCPUProfile() + return nil +} + +// StartCPUProfile turns on CPU profiling, writing to the given file. +func (h *HandlerT) StartCPUProfile(file string) error { + h.mu.Lock() + defer h.mu.Unlock() + if h.cpuW != nil { + return errors.New("CPU profiling already in progress") + } + f, err := os.Create(expandHome(file)) + if err != nil { + return err + } + if err := pprof.StartCPUProfile(f); err != nil { + f.Close() + return err + } + h.cpuW = f + h.cpuFile = file + glog.V(logger.Info).Infoln("CPU profiling started, writing to", h.cpuFile) + return nil +} + +// StopCPUProfile stops an ongoing CPU profile. +func (h *HandlerT) StopCPUProfile() error { + h.mu.Lock() + defer h.mu.Unlock() + pprof.StopCPUProfile() + if h.cpuW == nil { + return errors.New("CPU profiling not in progress") + } + glog.V(logger.Info).Infoln("done writing CPU profile to", h.cpuFile) + h.cpuW.Close() + h.cpuW = nil + h.cpuFile = "" + return nil +} + +// GoTrace turns on tracing for nsec seconds and writes +// trace data to file. +func (h *HandlerT) GoTrace(file string, nsec uint) error { + if err := h.StartGoTrace(file); err != nil { + return err + } + time.Sleep(time.Duration(nsec) * time.Second) + h.StopGoTrace() + return nil +} + +// BlockProfile turns on CPU profiling for nsec seconds and writes +// profile data to file. It uses a profile rate of 1 for most accurate +// information. If a different rate is desired, set the rate +// and write the profile manually. +func (*HandlerT) BlockProfile(file string, nsec uint) error { + runtime.SetBlockProfileRate(1) + time.Sleep(time.Duration(nsec) * time.Second) + defer runtime.SetBlockProfileRate(0) + return writeProfile("block", file) +} + +// SetBlockProfileRate sets the rate of goroutine block profile data collection. +// rate 0 disables block profiling. +func (*HandlerT) SetBlockProfileRate(rate int) { + runtime.SetBlockProfileRate(rate) +} + +// WriteBlockProfile writes a goroutine blocking profile to the given file. +func (*HandlerT) WriteBlockProfile(file string) error { + return writeProfile("block", file) +} + +// WriteMemProfile writes an allocation profile to the given file. +// Note that the profiling rate cannot be set through the API, +// it must be set on the command line. +func (*HandlerT) WriteMemProfile(file string) error { + return writeProfile("heap", file) +} + +// Stacks returns a printed representation of the stacks of all goroutines. +func (*HandlerT) Stacks() string { + buf := make([]byte, 1024*1024) + buf = buf[:runtime.Stack(buf, true)] + return string(buf) +} + +func writeProfile(name, file string) error { + p := pprof.Lookup(name) + glog.V(logger.Info).Infof("writing %d %s profile records to %s", p.Count(), name, file) + f, err := os.Create(expandHome(file)) + if err != nil { + return err + } + defer f.Close() + return p.WriteTo(f, 0) +} + +// expands home directory in file paths. +// ~someuser/tmp will not be expanded. +func expandHome(p string) string { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + home := os.Getenv("HOME") + if home == "" { + if usr, err := user.Current(); err == nil { + home = usr.HomeDir + } + } + if home != "" { + p = home + p[1:] + } + } + return filepath.Clean(p) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go new file mode 100644 index 0000000..d7bbfae --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/flags.go @@ -0,0 +1,123 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package debug + +import ( + "fmt" + "net/http" + _ "net/http/pprof" + "runtime" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "gopkg.in/urfave/cli.v1" +) + +var ( + verbosityFlag = cli.GenericFlag{ + Name: "verbosity", + Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail", + Value: glog.GetVerbosity(), + } + vmoduleFlag = cli.GenericFlag{ + Name: "vmodule", + Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=6,p2p=5)", + Value: glog.GetVModule(), + } + backtraceAtFlag = cli.GenericFlag{ + Name: "backtrace", + Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", + Value: glog.GetTraceLocation(), + } + pprofFlag = cli.BoolFlag{ + Name: "pprof", + Usage: "Enable the pprof HTTP server", + } + pprofPortFlag = cli.IntFlag{ + Name: "pprofport", + Usage: "pprof HTTP server listening port", + Value: 6060, + } + pprofAddrFlag = cli.StringFlag{ + Name: "pprofaddr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + } + memprofilerateFlag = cli.IntFlag{ + Name: "memprofilerate", + Usage: "Turn on memory profiling with the given rate", + Value: runtime.MemProfileRate, + } + blockprofilerateFlag = cli.IntFlag{ + Name: "blockprofilerate", + Usage: "Turn on block profiling with the given rate", + } + cpuprofileFlag = cli.StringFlag{ + Name: "cpuprofile", + Usage: "Write CPU profile to the given file", + } + traceFlag = cli.StringFlag{ + Name: "trace", + Usage: "Write execution trace to the given file", + } +) + +// Flags holds all command-line flags required for debugging. +var Flags = []cli.Flag{ + verbosityFlag, vmoduleFlag, backtraceAtFlag, + pprofFlag, pprofAddrFlag, pprofPortFlag, + memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, +} + +// Setup initializes profiling and logging based on the CLI flags. +// It should be called as early as possible in the program. +func Setup(ctx *cli.Context) error { + // logging + glog.CopyStandardLogTo("INFO") + glog.SetToStderr(true) + + // profiling, tracing + runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) + Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name)) + if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" { + if err := Handler.StartGoTrace(traceFile); err != nil { + return err + } + } + if cpuFile := ctx.GlobalString(cpuprofileFlag.Name); cpuFile != "" { + if err := Handler.StartCPUProfile(cpuFile); err != nil { + return err + } + } + + // pprof server + if ctx.GlobalBool(pprofFlag.Name) { + address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) + go func() { + glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address) + glog.Errorln(http.ListenAndServe(address, nil)) + }() + } + return nil +} + +// Exit stops all running profiles, flushing their output to the +// respective file. +func Exit() { + Handler.StopCPUProfile() + Handler.StopGoTrace() +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic.go new file mode 100644 index 0000000..572ebce --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic.go @@ -0,0 +1,27 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build go1.6 + +package debug + +import "runtime/debug" + +// LoudPanic panics in a way that gets all goroutine stacks printed on stderr. +func LoudPanic(x interface{}) { + debug.SetTraceback("all") + panic(x) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic_fallback.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic_fallback.go new file mode 100644 index 0000000..4ce4985 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/loudpanic_fallback.go @@ -0,0 +1,24 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !go1.6 + +package debug + +// LoudPanic panics in a way that gets all goroutine stacks printed on stderr. +func LoudPanic(x interface{}) { + panic(x) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/trace.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/trace.go new file mode 100644 index 0000000..c0cf921 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/trace.go @@ -0,0 +1,64 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build go1.5 + +package debug + +import ( + "errors" + "os" + "runtime/trace" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// StartGoTrace turns on tracing, writing to the given file. +func (h *HandlerT) StartGoTrace(file string) error { + h.mu.Lock() + defer h.mu.Unlock() + if h.traceW != nil { + return errors.New("trace already in progress") + } + f, err := os.Create(expandHome(file)) + if err != nil { + return err + } + if err := trace.Start(f); err != nil { + f.Close() + return err + } + h.traceW = f + h.traceFile = file + glog.V(logger.Info).Infoln("trace started, writing to", h.traceFile) + return nil +} + +// StopTrace stops an ongoing trace. +func (h *HandlerT) StopGoTrace() error { + h.mu.Lock() + defer h.mu.Unlock() + trace.Stop() + if h.traceW == nil { + return errors.New("trace not in progress") + } + glog.V(logger.Info).Infoln("done writing trace to", h.traceFile) + h.traceW.Close() + h.traceW = nil + h.traceFile = "" + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/debug/trace_fallback.go b/vendor/github.com/ethereum/go-ethereum/internal/debug/trace_fallback.go new file mode 100644 index 0000000..4118ff4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/debug/trace_fallback.go @@ -0,0 +1,31 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build !go1.5 + +// no-op implementation of tracing methods for Go < 1.5. + +package debug + +import "errors" + +func (*HandlerT) StartGoTrace(string) error { + return errors.New("tracing is not supported on Go < 1.5") +} + +func (*HandlerT) StopGoTrace() error { + return errors.New("tracing is not supported on Go < 1.5") +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go new file mode 100644 index 0000000..6b11cbc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -0,0 +1,1463 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "math" + "math/big" + "strings" + "time" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" + "golang.org/x/net/context" +) + +const defaultGas = 90000 + +var emptyHex = "0x" + +// PublicEthereumAPI provides an API to access Ethereum related information. +// It offers only methods that operate on public data that is freely available to anyone. +type PublicEthereumAPI struct { + b Backend +} + +// NewPublicEthereumAPI creates a new Etheruem protocol API. +func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI { + return &PublicEthereumAPI{b} +} + +// GasPrice returns a suggestion for a gas price. +func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { + return s.b.SuggestPrice(ctx) +} + +// ProtocolVersion returns the current Ethereum protocol version this node supports +func (s *PublicEthereumAPI) ProtocolVersion() hexutil.Uint { + return hexutil.Uint(s.b.ProtocolVersion()) +} + +// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not +// yet received the latest block headers from its pears. In case it is synchronizing: +// - startingBlock: block number this node started to synchronise from +// - currentBlock: block number this node is currently importing +// - highestBlock: block number of the highest block header this node has received from peers +// - pulledStates: number of state entries processed until now +// - knownStates: number of known state entries that still need to be pulled +func (s *PublicEthereumAPI) Syncing() (interface{}, error) { + progress := s.b.Downloader().Progress() + + // Return not syncing if the synchronisation already completed + if progress.CurrentBlock >= progress.HighestBlock { + return false, nil + } + // Otherwise gather the block sync stats + return map[string]interface{}{ + "startingBlock": hexutil.Uint64(progress.StartingBlock), + "currentBlock": hexutil.Uint64(progress.CurrentBlock), + "highestBlock": hexutil.Uint64(progress.HighestBlock), + "pulledStates": hexutil.Uint64(progress.PulledStates), + "knownStates": hexutil.Uint64(progress.KnownStates), + }, nil +} + +// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. +type PublicTxPoolAPI struct { + b Backend +} + +// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool. +func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { + return &PublicTxPoolAPI{b} +} + +// Content returns the transactions contained within the transaction pool. +func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { + content := map[string]map[string]map[string]*RPCTransaction{ + "pending": make(map[string]map[string]*RPCTransaction), + "queued": make(map[string]map[string]*RPCTransaction), + } + pending, queue := s.b.TxPoolContent() + + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} + +// Status returns the number of pending and queued transaction in the pool. +func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint { + pending, queue := s.b.Stats() + return map[string]hexutil.Uint{ + "pending": hexutil.Uint(pending), + "queued": hexutil.Uint(queue), + } +} + +// Inspect retrieves the content of the transaction pool and flattens it into an +// easily inspectable list. +func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), + } + pending, queue := s.b.TxPoolContent() + + // Define a formatter to flatten a transaction into a string + var format = func(tx *types.Transaction) string { + if to := tx.To(); to != nil { + return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) + } + return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) + } + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} + +// PublicAccountAPI provides an API to access accounts managed by this node. +// It offers only methods that can retrieve accounts. +type PublicAccountAPI struct { + am *accounts.Manager +} + +// NewPublicAccountAPI creates a new PublicAccountAPI. +func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI { + return &PublicAccountAPI{am: am} +} + +// Accounts returns the collection of accounts this node manages +func (s *PublicAccountAPI) Accounts() []common.Address { + var addresses []common.Address + for _, wallet := range s.am.Wallets() { + for _, account := range wallet.Accounts() { + addresses = append(addresses, account.Address) + } + } + return addresses +} + +// PrivateAccountAPI provides an API to access accounts managed by this node. +// It offers methods to create, (un)lock en list accounts. Some methods accept +// passwords and are therefore considered private by default. +type PrivateAccountAPI struct { + am *accounts.Manager + b Backend +} + +// NewPrivateAccountAPI create a new PrivateAccountAPI. +func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI { + return &PrivateAccountAPI{ + am: b.AccountManager(), + b: b, + } +} + +// ListAccounts will return a list of addresses for accounts this node manages. +func (s *PrivateAccountAPI) ListAccounts() []common.Address { + var addresses []common.Address + for _, wallet := range s.am.Wallets() { + for _, account := range wallet.Accounts() { + addresses = append(addresses, account.Address) + } + } + return addresses +} + +// rawWallet is a JSON representation of an accounts.Wallet interface, with its +// data contents extracted into plain fields. +type rawWallet struct { + URL string `json:"url"` + Status string `json:"status"` + Accounts []accounts.Account `json:"accounts"` +} + +// ListWallets will return a list of wallets this node manages. +func (s *PrivateAccountAPI) ListWallets() []rawWallet { + var wallets []rawWallet + for _, wallet := range s.am.Wallets() { + wallets = append(wallets, rawWallet{ + URL: wallet.URL().String(), + Status: wallet.Status(), + Accounts: wallet.Accounts(), + }) + } + return wallets +} + +// DeriveAccount requests a HD wallet to derive a new account, optionally pinning +// it for later reuse. +func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { + wallet, err := s.am.Wallet(url) + if err != nil { + return accounts.Account{}, err + } + derivPath, err := accounts.ParseDerivationPath(path) + if err != nil { + return accounts.Account{}, err + } + if pin == nil { + pin = new(bool) + } + return wallet.Derive(derivPath, *pin) +} + +// NewAccount will create a new account and returns the address for the new account. +func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { + acc, err := fetchKeystore(s.am).NewAccount(password) + if err == nil { + return acc.Address, nil + } + return common.Address{}, err +} + +// fetchKeystore retrives the encrypted keystore from the account manager. +func fetchKeystore(am *accounts.Manager) *keystore.KeyStore { + return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) +} + +// ImportRawKey stores the given hex encoded ECDSA key into the key directory, +// encrypting it with the passphrase. +func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { + hexkey, err := hex.DecodeString(privkey) + if err != nil { + return common.Address{}, err + } + + acc, err := fetchKeystore(s.am).ImportECDSA(crypto.ToECDSA(hexkey), password) + return acc.Address, err +} + +// UnlockAccount will unlock the account associated with the given address with +// the given password for duration seconds. If duration is nil it will use a +// default of 300 seconds. It returns an indication if the account was unlocked. +func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) { + const max = uint64(time.Duration(math.MaxInt64) / time.Second) + var d time.Duration + if duration == nil { + d = 300 * time.Second + } else if *duration > max { + return false, errors.New("unlock duration too large") + } else { + d = time.Duration(*duration) * time.Second + } + err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d) + return err == nil, err +} + +// LockAccount will lock the account associated with the given address when it's unlocked. +func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { + return fetchKeystore(s.am).Lock(addr) == nil +} + +// SendTransaction will create a transaction from the given arguments and +// tries to sign it with the key associated with args.To. If the given passwd isn't +// able to decrypt the key it fails. +func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { + // Set some sanity defaults and terminate on failure + if err := args.setDefaults(ctx, s.b); err != nil { + return common.Hash{}, err + } + // Look up the wallet containing the requested signer + account := accounts.Account{Address: args.From} + + wallet, err := s.am.Find(account) + if err != nil { + return common.Hash{}, err + } + // Assemble the transaction and sign with the wallet + tx := args.toTransaction() + + var chainID *big.Int + if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) { + chainID = config.ChainId + } + signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, chainID) + if err != nil { + return common.Hash{}, err + } + return submitTransaction(ctx, s.b, signed) +} + +// signHash is a helper function that calculates a hash for the given message that can be +// safely used to calculate a signature from. +// +// The hash is calulcated as +// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). +// +// This gives context to the signed message and prevents signing of transactions. +func signHash(data []byte) []byte { + msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) + return crypto.Keccak256([]byte(msg)) +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// Note, the produced signature conforms to the secp256k1 curve R, S and V values, +// where the V value will be 27 or 28 for legacy reasons. +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { + // Look up the wallet containing the requested signer + account := accounts.Account{Address: addr} + + wallet, err := s.b.AccountManager().Find(account) + if err != nil { + return nil, err + } + // Assemble sign the data with the wallet + signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data)) + if err != nil { + return nil, err + } + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + return signature, nil +} + +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// Note, the signature must conform to the secp256k1 curve R, S and V values, where +// the V value must be be 27 or 28 for legacy reasons. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover +func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { + if len(sig) != 65 { + return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + } + if sig[64] != 27 && sig[64] != 28 { + return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") + } + sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 + + rpk, err := crypto.Ecrecover(signHash(data), sig) + if err != nil { + return common.Address{}, err + } + pubKey := crypto.ToECDSAPub(rpk) + recoveredAddr := crypto.PubkeyToAddress(*pubKey) + return recoveredAddr, nil +} + +// SignAndSendTransaction was renamed to SendTransaction. This method is deprecated +// and will be removed in the future. It primary goal is to give clients time to update. +func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { + return s.SendTransaction(ctx, args, passwd) +} + +// PublicBlockChainAPI provides an API to access the Ethereum blockchain. +// It offers only methods that operate on public data that is freely available to anyone. +type PublicBlockChainAPI struct { + b Backend +} + +// NewPublicBlockChainAPI creates a new Etheruem blockchain API. +func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { + return &PublicBlockChainAPI{b} +} + +// BlockNumber returns the block number of the chain head. +func (s *PublicBlockChainAPI) BlockNumber() *big.Int { + header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + return header.Number +} + +// GetBalance returns the amount of wei for the given address in the state of the +// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta +// block numbers are also allowed. +func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, err + } + + return state.GetBalance(ctx, address) +} + +// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all +// transactions in the block are returned in full detail, otherwise only the transaction hash is returned. +func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { + block, err := s.b.BlockByNumber(ctx, blockNr) + if block != nil { + response, err := s.rpcOutputBlock(block, true, fullTx) + if err == nil && blockNr == rpc.PendingBlockNumber { + // Pending blocks need to nil out a few fields + for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} { + response[field] = nil + } + } + return response, err + } + return nil, err +} + +// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full +// detail, otherwise only the transaction hash is returned. +func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { + block, err := s.b.GetBlock(ctx, blockHash) + if block != nil { + return s.rpcOutputBlock(block, true, fullTx) + } + return nil, err +} + +// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true +// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. +func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { + block, err := s.b.BlockByNumber(ctx, blockNr) + if block != nil { + uncles := block.Uncles() + if index >= hexutil.Uint(len(uncles)) { + glog.V(logger.Debug).Infof("uncle block on index %d not found for block #%d", index, blockNr) + return nil, nil + } + block = types.NewBlockWithHeader(uncles[index]) + return s.rpcOutputBlock(block, false, false) + } + return nil, err +} + +// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true +// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. +func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) { + block, err := s.b.GetBlock(ctx, blockHash) + if block != nil { + uncles := block.Uncles() + if index >= hexutil.Uint(len(uncles)) { + glog.V(logger.Debug).Infof("uncle block on index %d not found for block %s", index, blockHash.Hex()) + return nil, nil + } + block = types.NewBlockWithHeader(uncles[index]) + return s.rpcOutputBlock(block, false, false) + } + return nil, err +} + +// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number +func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + n := hexutil.Uint(len(block.Uncles())) + return &n + } + return nil +} + +// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash +func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + n := hexutil.Uint(len(block.Uncles())) + return &n + } + return nil +} + +// GetCode returns the code stored at the given address in the state for the given block number. +func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return "", err + } + res, err := state.GetCode(ctx, address) + if len(res) == 0 || err != nil { // backwards compatibility + return "0x", err + } + return common.ToHex(res), nil +} + +// GetStorageAt returns the storage from the state at the given address, key and +// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block +// numbers are also allowed. +func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return "0x", err + } + res, err := state.GetState(ctx, address, common.HexToHash(key)) + if err != nil { + return "0x", err + } + return res.Hex(), nil +} + +// callmsg is the message type used for call transitions. +type callmsg struct { + addr common.Address + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() (common.Address, error) { return m.addr, nil } +func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.to } +func (m callmsg) GasPrice() *big.Int { return m.gasPrice } +func (m callmsg) Gas() *big.Int { return m.gas } +func (m callmsg) Value() *big.Int { return m.value } +func (m callmsg) Data() []byte { return m.data } + +// CallArgs represents the arguments for a call. +type CallArgs struct { + From common.Address `json:"from"` + To *common.Address `json:"to"` + Gas hexutil.Big `json:"gas"` + GasPrice hexutil.Big `json:"gasPrice"` + Value hexutil.Big `json:"value"` + Data hexutil.Bytes `json:"data"` +} + +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) { + defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) + + state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, common.Big0, err + } + // Set sender address or use a default if none specified + addr := args.From + if addr == (common.Address{}) { + if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 { + if accounts := wallets[0].Accounts(); len(accounts) > 0 { + addr = accounts[0].Address + } + } + } + // Set default gas & gas price if none were set + gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt() + if gas.BitLen() == 0 { + gas = big.NewInt(50000000) + } + if gasPrice.BitLen() == 0 { + gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + } + + // Create new call message + msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false) + + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if vmCfg.DisableGasMetering { + ctx, cancel = context.WithTimeout(ctx, time.Second*5) + } else { + ctx, cancel = context.WithCancel(ctx) + } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer func() { cancel() }() + + // Get a new instance of the EVM. + evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg) + if err != nil { + return nil, common.Big0, err + } + // Wait for the context to be done and cancel the evm. Even if the + // EVM has finished, cancelling may be done (repeatedly) + go func() { + select { + case <-ctx.Done(): + evm.Cancel() + } + }() + + // Setup the gas pool (also for unmetered requests) + // and apply the message. + gp := new(core.GasPool).AddGas(common.MaxBig) + res, gas, err := core.ApplyMessage(evm, msg, gp) + if err := vmError(); err != nil { + return nil, common.Big0, err + } + return res, gas, err +} + +// Call executes the given transaction on the state for the given block number. +// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { + result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true}) + return (hexutil.Bytes)(result), err +} + +// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. +func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*hexutil.Big, error) { + // Binary search the gas requirement, as it may be higher than the amount used + var lo, hi uint64 + if (*big.Int)(&args.Gas).BitLen() > 0 { + hi = (*big.Int)(&args.Gas).Uint64() + } else { + // Retrieve the current pending block to act as the gas ceiling + block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber) + if err != nil { + return nil, err + } + hi = block.GasLimit().Uint64() + } + for lo+1 < hi { + // Take a guess at the gas, and check transaction validity + mid := (hi + lo) / 2 + (*big.Int)(&args.Gas).SetUint64(mid) + + _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) + + // If the transaction became invalid or used all the gas (failed), raise the gas limit + if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 { + lo = mid + continue + } + // Otherwise assume the transaction succeeded, lower the gas limit + hi = mid + } + return (*hexutil.Big)(new(big.Int).SetUint64(hi)), nil +} + +// ExecutionResult groups all structured logs emitted by the EVM +// while replaying a transaction in debug mode as well as the amount of +// gas used and the return value +type ExecutionResult struct { + Gas *big.Int `json:"gas"` + ReturnValue string `json:"returnValue"` + StructLogs []StructLogRes `json:"structLogs"` +} + +// StructLogRes stores a structured log emitted by the EVM while replaying a +// transaction in debug mode +type StructLogRes struct { + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas *big.Int `json:"gas"` + GasCost *big.Int `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error"` + Stack []string `json:"stack"` + Memory []string `json:"memory"` + Storage map[string]string `json:"storage"` +} + +// formatLogs formats EVM returned structured logs for json output +func FormatLogs(structLogs []vm.StructLog) []StructLogRes { + formattedStructLogs := make([]StructLogRes, len(structLogs)) + for index, trace := range structLogs { + formattedStructLogs[index] = StructLogRes{ + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.Err, + Stack: make([]string, len(trace.Stack)), + Storage: make(map[string]string), + } + + for i, stackValue := range trace.Stack { + formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(stackValue.Bytes(), 32)) + } + + for i := 0; i+32 <= len(trace.Memory); i += 32 { + formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + } + + for i, storageValue := range trace.Storage { + formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + } + return formattedStructLogs +} + +// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are +// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain +// transaction hashes. +func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { + head := b.Header() // copies the header once + fields := map[string]interface{}{ + "number": (*hexutil.Big)(head.Number), + "hash": b.Hash(), + "parentHash": head.ParentHash, + "nonce": head.Nonce, + "mixHash": head.MixDigest, + "sha3Uncles": head.UncleHash, + "logsBloom": head.Bloom, + "stateRoot": head.Root, + "miner": head.Coinbase, + "difficulty": (*hexutil.Big)(head.Difficulty), + "totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())), + "extraData": hexutil.Bytes(head.Extra), + "size": hexutil.Uint64(uint64(b.Size().Int64())), + "gasLimit": (*hexutil.Big)(head.GasLimit), + "gasUsed": (*hexutil.Big)(head.GasUsed), + "timestamp": (*hexutil.Big)(head.Time), + "transactionsRoot": head.TxHash, + "receiptsRoot": head.ReceiptHash, + } + + if inclTx { + formatTx := func(tx *types.Transaction) (interface{}, error) { + return tx.Hash(), nil + } + + if fullTx { + formatTx = func(tx *types.Transaction) (interface{}, error) { + return newRPCTransaction(b, tx.Hash()) + } + } + + txs := b.Transactions() + transactions := make([]interface{}, len(txs)) + var err error + for i, tx := range b.Transactions() { + if transactions[i], err = formatTx(tx); err != nil { + return nil, err + } + } + fields["transactions"] = transactions + } + + uncles := b.Uncles() + uncleHashes := make([]common.Hash, len(uncles)) + for i, uncle := range uncles { + uncleHashes[i] = uncle.Hash() + } + fields["uncles"] = uncleHashes + + return fields, nil +} + +// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction +type RPCTransaction struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas *hexutil.Big `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex hexutil.Uint `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` +} + +// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation +func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + v, r, s := tx.RawSignatureValues() + return &RPCTransaction{ + From: from, + Gas: (*hexutil.Big)(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + Hash: tx.Hash(), + Input: hexutil.Bytes(tx.Data()), + Nonce: hexutil.Uint64(tx.Nonce()), + To: tx.To(), + Value: (*hexutil.Big)(tx.Value()), + V: (*hexutil.Big)(v), + R: (*hexutil.Big)(r), + S: (*hexutil.Big)(s), + } +} + +// newRPCTransaction returns a transaction that will serialize to the RPC representation. +func newRPCTransactionFromBlockIndex(b *types.Block, txIndex uint) (*RPCTransaction, error) { + if txIndex < uint(len(b.Transactions())) { + tx := b.Transactions()[txIndex] + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + v, r, s := tx.RawSignatureValues() + return &RPCTransaction{ + BlockHash: b.Hash(), + BlockNumber: (*hexutil.Big)(b.Number()), + From: from, + Gas: (*hexutil.Big)(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + Hash: tx.Hash(), + Input: hexutil.Bytes(tx.Data()), + Nonce: hexutil.Uint64(tx.Nonce()), + To: tx.To(), + TransactionIndex: hexutil.Uint(txIndex), + Value: (*hexutil.Big)(tx.Value()), + V: (*hexutil.Big)(v), + R: (*hexutil.Big)(r), + S: (*hexutil.Big)(s), + }, nil + } + + return nil, nil +} + +// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. +func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex uint) (hexutil.Bytes, error) { + if txIndex < uint(len(b.Transactions())) { + tx := b.Transactions()[txIndex] + return rlp.EncodeToBytes(tx) + } + + return nil, nil +} + +// newRPCTransaction returns a transaction that will serialize to the RPC representation. +func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { + for idx, tx := range b.Transactions() { + if tx.Hash() == txHash { + return newRPCTransactionFromBlockIndex(b, uint(idx)) + } + } + + return nil, nil +} + +// PublicTransactionPoolAPI exposes methods for the RPC interface +type PublicTransactionPoolAPI struct { + b Backend +} + +// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. +func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI { + return &PublicTransactionPoolAPI{b} +} + +func getTransaction(chainDb ethdb.Database, b Backend, txHash common.Hash) (*types.Transaction, bool, error) { + txData, err := chainDb.Get(txHash.Bytes()) + isPending := false + tx := new(types.Transaction) + + if err == nil && len(txData) > 0 { + if err := rlp.DecodeBytes(txData, tx); err != nil { + return nil, isPending, err + } + } else { + // pending transaction? + tx = b.GetPoolTransaction(txHash) + isPending = true + } + + return tx, isPending, nil +} + +// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. +func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + n := hexutil.Uint(len(block.Transactions())) + return &n + } + return nil +} + +// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. +func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + n := hexutil.Uint(len(block.Transactions())) + return &n + } + return nil +} + +// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (*RPCTransaction, error) { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCTransactionFromBlockIndex(block, uint(index)) + } + return nil, nil +} + +// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. +func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (*RPCTransaction, error) { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCTransactionFromBlockIndex(block, uint(index)) + } + return nil, nil +} + +// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (hexutil.Bytes, error) { + if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCRawTransactionFromBlockIndex(block, uint(index)) + } + return nil, nil +} + +// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (hexutil.Bytes, error) { + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCRawTransactionFromBlockIndex(block, uint(index)) + } + return nil, nil +} + +// GetTransactionCount returns the number of transactions the given address has sent for the given block number +func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, err + } + nonce, err := state.GetNonce(ctx, address) + if err != nil { + return nil, err + } + return (*hexutil.Uint64)(&nonce), nil +} + +// getTransactionBlockData fetches the meta data for the given transaction from the chain database. This is useful to +// retrieve block information for a hash. It returns the block hash, block index and transaction index. +func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common.Hash, uint64, uint64, error) { + var txBlock struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + } + + blockData, err := chainDb.Get(append(txHash.Bytes(), 0x0001)) + if err != nil { + return common.Hash{}, uint64(0), uint64(0), err + } + + reader := bytes.NewReader(blockData) + if err = rlp.Decode(reader, &txBlock); err != nil { + return common.Hash{}, uint64(0), uint64(0), err + } + + return txBlock.BlockHash, txBlock.BlockIndex, txBlock.Index, nil +} + +// GetTransactionByHash returns the transaction for the given hash +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*RPCTransaction, error) { + var tx *types.Transaction + var isPending bool + var err error + + if tx, isPending, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } else if tx == nil { + return nil, nil + } + + if isPending { + return newRPCPendingTransaction(tx), nil + } + + blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), txHash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCTransaction(block, txHash) + } + + return nil, nil +} + +// GetRawTransactionByHash returns the bytes of the transaction for the given hash. +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (hexutil.Bytes, error) { + var tx *types.Transaction + var err error + + if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } else if tx == nil { + return nil, nil + } + + return rlp.EncodeToBytes(tx) +} + +// GetTransactionReceipt returns the transaction receipt for the given transaction hash. +func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { + receipt := core.GetReceipt(s.b.ChainDb(), txHash) + if receipt == nil { + glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex()) + return nil, nil + } + + tx, _, err := getTransaction(s.b.ChainDb(), s.b, txHash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + var signer types.Signer = types.FrontierSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + + fields := map[string]interface{}{ + "root": hexutil.Bytes(receipt.PostState), + "blockHash": txBlock, + "blockNumber": hexutil.Uint64(blockIndex), + "transactionHash": txHash, + "transactionIndex": hexutil.Uint64(index), + "from": from, + "to": tx.To(), + "gasUsed": (*hexutil.Big)(receipt.GasUsed), + "cumulativeGasUsed": (*hexutil.Big)(receipt.CumulativeGasUsed), + "contractAddress": nil, + "logs": receipt.Logs, + "logsBloom": receipt.Bloom, + } + if receipt.Logs == nil { + fields["logs"] = [][]*types.Log{} + } + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if receipt.ContractAddress != (common.Address{}) { + fields["contractAddress"] = receipt.ContractAddress + } + return fields, nil +} + +// sign is a helper function that signs a transaction with the private key of the given address. +func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { + // Look up the wallet containing the requested signer + account := accounts.Account{Address: addr} + + wallet, err := s.b.AccountManager().Find(account) + if err != nil { + return nil, err + } + // Request the wallet to sign the transaction + var chainID *big.Int + if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) { + chainID = config.ChainId + } + return wallet.SignTx(account, tx, chainID) +} + +// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. +type SendTxArgs struct { + From common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Big `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Data hexutil.Bytes `json:"data"` + Nonce *hexutil.Uint64 `json:"nonce"` +} + +// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields. +func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { + if args.Gas == nil { + args.Gas = (*hexutil.Big)(big.NewInt(defaultGas)) + } + if args.GasPrice == nil { + price, err := b.SuggestPrice(ctx) + if err != nil { + return err + } + args.GasPrice = (*hexutil.Big)(price) + } + if args.Value == nil { + args.Value = new(hexutil.Big) + } + if args.Nonce == nil { + nonce, err := b.GetPoolNonce(ctx, args.From) + if err != nil { + return err + } + args.Nonce = (*hexutil.Uint64)(&nonce) + } + return nil +} + +func (args *SendTxArgs) toTransaction() *types.Transaction { + if args.To == nil { + return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data) + } + return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), (*big.Int)(args.Gas), (*big.Int)(args.GasPrice), args.Data) +} + +// submitTransaction is a helper function that submits tx to txPool and logs a message. +func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) { + if err := b.SendTx(ctx, tx); err != nil { + return common.Hash{}, err + } + if tx.To() == nil { + signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) + from, _ := types.Sender(signer, tx) + addr := crypto.CreateAddress(from, tx.Nonce()) + glog.V(logger.Info).Infof("Tx(%s) created: %s\n", tx.Hash().Hex(), addr.Hex()) + } else { + glog.V(logger.Info).Infof("Tx(%s) to: %s\n", tx.Hash().Hex(), tx.To().Hex()) + } + return tx.Hash(), nil +} + +// SendTransaction creates a transaction for the given argument, sign it and submit it to the +// transaction pool. +func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) { + // Set some sanity defaults and terminate on failure + if err := args.setDefaults(ctx, s.b); err != nil { + return common.Hash{}, err + } + // Look up the wallet containing the requested signer + account := accounts.Account{Address: args.From} + + wallet, err := s.b.AccountManager().Find(account) + if err != nil { + return common.Hash{}, err + } + // Assemble the transaction and sign with the wallet + tx := args.toTransaction() + + var chainID *big.Int + if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) { + chainID = config.ChainId + } + signed, err := wallet.SignTx(account, tx, chainID) + if err != nil { + return common.Hash{}, err + } + return submitTransaction(ctx, s.b, signed) +} + +// SendRawTransaction will add the signed transaction to the transaction pool. +// The sender is responsible for signing the transaction and using the correct nonce. +func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (string, error) { + tx := new(types.Transaction) + if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + return "", err + } + + if err := s.b.SendTx(ctx, tx); err != nil { + return "", err + } + + signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + if tx.To() == nil { + from, err := types.Sender(signer, tx) + if err != nil { + return "", err + } + addr := crypto.CreateAddress(from, tx.Nonce()) + glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr) + } else { + glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To()) + } + + return tx.Hash().Hex(), nil +} + +// Sign calculates an ECDSA signature for: +// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). +// +// Note, the produced signature conforms to the secp256k1 curve R, S and V values, +// where the V value will be 27 or 28 for legacy reasons. +// +// The account associated with addr must be unlocked. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign +func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { + // Look up the wallet containing the requested signer + account := accounts.Account{Address: addr} + + wallet, err := s.b.AccountManager().Find(account) + if err != nil { + return nil, err + } + // Sign the requested hash with the wallet + signature, err := wallet.SignHash(account, signHash(data)) + if err == nil { + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + } + return signature, err +} + +// SignTransactionResult represents a RLP encoded signed transaction. +type SignTransactionResult struct { + Raw hexutil.Bytes `json:"raw"` + Tx *types.Transaction `json:"tx"` +} + +// SignTransaction will sign the given transaction with the from account. +// The node needs to have the private key of the account corresponding with +// the given from address and it needs to be unlocked. +func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) { + if err := args.setDefaults(ctx, s.b); err != nil { + return nil, err + } + tx, err := s.sign(args.From, args.toTransaction()) + if err != nil { + return nil, err + } + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return nil, err + } + return &SignTransactionResult{data, tx}, nil +} + +// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of +// the accounts this node manages. +func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) { + pending, err := s.b.GetPoolTransactions() + if err != nil { + return nil, err + } + + transactions := make([]*RPCTransaction, 0, len(pending)) + for _, tx := range pending { + var signer types.Signer = types.HomesteadSigner{} + if tx.Protected() { + signer = types.NewEIP155Signer(tx.ChainId()) + } + from, _ := types.Sender(signer, tx) + if _, err := s.b.AccountManager().Find(accounts.Account{Address: from}); err == nil { + transactions = append(transactions, newRPCPendingTransaction(tx)) + } + } + return transactions, nil +} + +// Resend accepts an existing transaction and a new gas price and limit. It will remove +// the given transaction from the pool and reinsert it with the new gas price and limit. +func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxArgs, gasPrice, gasLimit *hexutil.Big) (common.Hash, error) { + if sendArgs.Nonce == nil { + return common.Hash{}, fmt.Errorf("missing transaction nonce in transaction spec") + } + if err := sendArgs.setDefaults(ctx, s.b); err != nil { + return common.Hash{}, err + } + matchTx := sendArgs.toTransaction() + pending, err := s.b.GetPoolTransactions() + if err != nil { + return common.Hash{}, err + } + + for _, p := range pending { + var signer types.Signer = types.HomesteadSigner{} + if p.Protected() { + signer = types.NewEIP155Signer(p.ChainId()) + } + wantSigHash := signer.Hash(matchTx) + + if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash { + // Match. Re-sign and send the transaction. + if gasPrice != nil { + sendArgs.GasPrice = gasPrice + } + if gasLimit != nil { + sendArgs.Gas = gasLimit + } + signedTx, err := s.sign(sendArgs.From, sendArgs.toTransaction()) + if err != nil { + return common.Hash{}, err + } + s.b.RemoveTx(p.Hash()) + if err = s.b.SendTx(ctx, signedTx); err != nil { + return common.Hash{}, err + } + return signedTx.Hash(), nil + } + } + + return common.Hash{}, fmt.Errorf("Transaction %#x not found", matchTx.Hash()) +} + +// PublicDebugAPI is the collection of Etheruem APIs exposed over the public +// debugging endpoint. +type PublicDebugAPI struct { + b Backend +} + +// NewPublicDebugAPI creates a new API definition for the public debug methods +// of the Ethereum service. +func NewPublicDebugAPI(b Backend) *PublicDebugAPI { + return &PublicDebugAPI{b: b} +} + +// GetBlockRlp retrieves the RLP encoded for of a single block. +func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { + block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) + if block == nil { + return "", fmt.Errorf("block #%d not found", number) + } + encoded, err := rlp.EncodeToBytes(block) + if err != nil { + return "", err + } + return fmt.Sprintf("%x", encoded), nil +} + +// PrintBlock retrieves a block and returns its pretty printed form. +func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { + block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) + if block == nil { + return "", fmt.Errorf("block #%d not found", number) + } + return fmt.Sprintf("%s", block), nil +} + +// SeedHash retrieves the seed hash of a block. +func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { + block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) + if block == nil { + return "", fmt.Errorf("block #%d not found", number) + } + hash, err := ethash.GetSeedHash(number) + if err != nil { + return "", err + } + return fmt.Sprintf("0x%x", hash), nil +} + +// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private +// debugging endpoint. +type PrivateDebugAPI struct { + b Backend +} + +// NewPrivateDebugAPI creates a new API definition for the private debug methods +// of the Ethereum service. +func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { + return &PrivateDebugAPI{b: b} +} + +// ChaindbProperty returns leveldb properties of the chain database. +func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return "", fmt.Errorf("chaindbProperty does not work for memory databases") + } + if property == "" { + property = "leveldb.stats" + } else if !strings.HasPrefix(property, "leveldb.") { + property = "leveldb." + property + } + return ldb.LDB().GetProperty(property) +} + +func (api *PrivateDebugAPI) ChaindbCompact() error { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return fmt.Errorf("chaindbCompact does not work for memory databases") + } + for b := byte(0); b < 255; b++ { + glog.V(logger.Info).Infof("compacting chain DB range 0x%0.2X-0x%0.2X", b, b+1) + err := ldb.LDB().CompactRange(util.Range{Start: []byte{b}, Limit: []byte{b + 1}}) + if err != nil { + glog.Errorf("compaction error: %v", err) + return err + } + } + return nil +} + +// SetHead rewinds the head of the blockchain to a previous block. +func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) { + api.b.SetHead(uint64(number)) +} + +// PublicNetAPI offers network related RPC methods +type PublicNetAPI struct { + net *p2p.Server + networkVersion int +} + +// NewPublicNetAPI creates a new net API instance. +func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { + return &PublicNetAPI{net, networkVersion} +} + +// Listening returns an indication if the node is listening for network connections. +func (s *PublicNetAPI) Listening() bool { + return true // always listening +} + +// PeerCount returns the number of connected peers +func (s *PublicNetAPI) PeerCount() hexutil.Uint { + return hexutil.Uint(s.net.PeerCount()) +} + +// Version returns the current ethereum protocol version. +func (s *PublicNetAPI) Version() string { + return fmt.Sprintf("%d", s.networkVersion) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go new file mode 100644 index 0000000..214214f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -0,0 +1,120 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package ethapi implements the general Ethereum API functions. +package ethapi + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// Backend interface provides the common API services (that are provided by +// both full and light clients) with access to necessary functions. +type Backend interface { + // general Ethereum API + Downloader() *downloader.Downloader + ProtocolVersion() int + SuggestPrice(ctx context.Context) (*big.Int, error) + ChainDb() ethdb.Database + EventMux() *event.TypeMux + AccountManager() *accounts.Manager + // BlockChain API + SetHead(number uint64) + HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) + BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) + StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *types.Header, error) + GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) + GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) + GetTd(blockHash common.Hash) *big.Int + GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) + // TxPool API + SendTx(ctx context.Context, signedTx *types.Transaction) error + RemoveTx(txHash common.Hash) + GetPoolTransactions() (types.Transactions, error) + GetPoolTransaction(txHash common.Hash) *types.Transaction + GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) + Stats() (pending int, queued int) + TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + + ChainConfig() *params.ChainConfig + CurrentBlock() *types.Block +} + +type State interface { + GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) + GetCode(ctx context.Context, addr common.Address) ([]byte, error) + GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) + GetNonce(ctx context.Context, addr common.Address) (uint64, error) +} + +func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { + compiler := makeCompilerAPIs(solcPath) + all := []rpc.API{ + { + Namespace: "eth", + Version: "1.0", + Service: NewPublicEthereumAPI(apiBackend), + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: NewPublicBlockChainAPI(apiBackend), + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: NewPublicTransactionPoolAPI(apiBackend), + Public: true, + }, { + Namespace: "txpool", + Version: "1.0", + Service: NewPublicTxPoolAPI(apiBackend), + Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPublicDebugAPI(apiBackend), + Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(apiBackend), + }, { + Namespace: "eth", + Version: "1.0", + Service: NewPublicAccountAPI(apiBackend.AccountManager()), + Public: true, + }, { + Namespace: "personal", + Version: "1.0", + Service: NewPrivateAccountAPI(apiBackend), + Public: false, + }, + } + return append(compiler, all...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/solc.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/solc.go new file mode 100644 index 0000000..b9acc51 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/solc.go @@ -0,0 +1,82 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/rpc" +) + +func makeCompilerAPIs(solcPath string) []rpc.API { + c := &compilerAPI{solc: solcPath} + return []rpc.API{ + { + Namespace: "eth", + Version: "1.0", + Service: (*PublicCompilerAPI)(c), + Public: true, + }, + { + Namespace: "admin", + Version: "1.0", + Service: (*CompilerAdminAPI)(c), + Public: true, + }, + } +} + +type compilerAPI struct { + // This lock guards the solc path set through the API. + // It also ensures that only one solc process is used at + // any time. + mu sync.Mutex + solc string +} + +type CompilerAdminAPI compilerAPI + +// SetSolc sets the Solidity compiler path to be used by the node. +func (api *CompilerAdminAPI) SetSolc(path string) (string, error) { + api.mu.Lock() + defer api.mu.Unlock() + info, err := compiler.SolidityVersion(path) + if err != nil { + return "", err + } + api.solc = path + return info.FullVersion, nil +} + +type PublicCompilerAPI compilerAPI + +// CompileSolidity compiles the given solidity source. +func (api *PublicCompilerAPI) CompileSolidity(source string) (map[string]*compiler.Contract, error) { + api.mu.Lock() + defer api.mu.Unlock() + return compiler.CompileSolidityString(api.solc, source) +} + +func (api *PublicCompilerAPI) GetCompilers() ([]string, error) { + api.mu.Lock() + defer api.mu.Unlock() + if _, err := compiler.SolidityVersion(api.solc); err == nil { + return []string{"Solidity"}, nil + } + return []string{}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go new file mode 100644 index 0000000..ef107fc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer.go @@ -0,0 +1,318 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/robertkrimen/otto" +) + +// fakeBig is used to provide an interface to Javascript for 'big.NewInt' +type fakeBig struct{} + +// NewInt creates a new big.Int with the specified int64 value. +func (fb *fakeBig) NewInt(x int64) *big.Int { + return big.NewInt(x) +} + +// OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it +// as an object, instead of a number. +type opCodeWrapper struct { + op vm.OpCode +} + +// toNumber returns the ID of this opcode as an integer +func (ocw *opCodeWrapper) toNumber() int { + return int(ocw.op) +} + +// toString returns the string representation of the opcode +func (ocw *opCodeWrapper) toString() string { + return ocw.op.String() +} + +// isPush returns true if the op is a Push +func (ocw *opCodeWrapper) isPush() bool { + return ocw.op.IsPush() +} + +// MarshalJSON serializes the opcode as JSON +func (ocw *opCodeWrapper) MarshalJSON() ([]byte, error) { + return json.Marshal(ocw.op.String()) +} + +// toValue returns an otto.Value for the opCodeWrapper +func (ocw *opCodeWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(ocw) + obj := value.Object() + obj.Set("toNumber", ocw.toNumber) + obj.Set("toString", ocw.toString) + obj.Set("isPush", ocw.isPush) + return value +} + +// memoryWrapper provides a JS wrapper around vm.Memory +type memoryWrapper struct { + memory *vm.Memory +} + +// slice returns the requested range of memory as a byte slice +func (mw *memoryWrapper) slice(begin, end int64) []byte { + return mw.memory.Get(begin, end-begin) +} + +// getUint returns the 32 bytes at the specified address interpreted +// as an unsigned integer +func (mw *memoryWrapper) getUint(addr int64) *big.Int { + ret := big.NewInt(0) + ret.SetBytes(mw.memory.GetPtr(addr, 32)) + return ret +} + +// toValue returns an otto.Value for the memoryWrapper +func (mw *memoryWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(mw) + obj := value.Object() + obj.Set("slice", mw.slice) + obj.Set("getUint", mw.getUint) + return value +} + +// stackWrapper provides a JS wrapper around vm.Stack +type stackWrapper struct { + stack *vm.Stack +} + +// peek returns the nth-from-the-top element of the stack. +func (sw *stackWrapper) peek(idx int) *big.Int { + return sw.stack.Data()[len(sw.stack.Data())-idx-1] +} + +// length returns the length of the stack +func (sw *stackWrapper) length() int { + return len(sw.stack.Data()) +} + +// toValue returns an otto.Value for the stackWrapper +func (sw *stackWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(sw) + obj := value.Object() + obj.Set("peek", sw.peek) + obj.Set("length", sw.length) + return value +} + +// dbWrapper provides a JS wrapper around vm.Database +type dbWrapper struct { + db vm.StateDB +} + +// getBalance retrieves an account's balance +func (dw *dbWrapper) getBalance(addr common.Address) *big.Int { + return dw.db.GetBalance(addr) +} + +// getNonce retrieves an account's nonce +func (dw *dbWrapper) getNonce(addr common.Address) uint64 { + return dw.db.GetNonce(addr) +} + +// getCode retrieves an account's code +func (dw *dbWrapper) getCode(addr common.Address) []byte { + return dw.db.GetCode(addr) +} + +// getState retrieves an account's state data for the given hash +func (dw *dbWrapper) getState(addr common.Address, hash common.Hash) common.Hash { + return dw.db.GetState(addr, hash) +} + +// exists returns true iff the account exists +func (dw *dbWrapper) exists(addr common.Address) bool { + return dw.db.Exist(addr) +} + +// toValue returns an otto.Value for the dbWrapper +func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(dw) + obj := value.Object() + obj.Set("getBalance", dw.getBalance) + obj.Set("getNonce", dw.getNonce) + obj.Set("getCode", dw.getCode) + obj.Set("getState", dw.getState) + obj.Set("exists", dw.exists) + return value +} + +// JavascriptTracer provides an implementation of Tracer that evaluates a +// Javascript function for each VM execution step. +type JavascriptTracer struct { + vm *otto.Otto // Javascript VM instance + traceobj *otto.Object // User-supplied object to call + log map[string]interface{} // (Reusable) map for the `log` arg to `step` + logvalue otto.Value // JS view of `log` + memory *memoryWrapper // Wrapper around the VM memory + memvalue otto.Value // JS view of `memory` + stack *stackWrapper // Wrapper around the VM stack + stackvalue otto.Value // JS view of `stack` + db *dbWrapper // Wrapper around the VM environment + dbvalue otto.Value // JS view of `db` + err error // Error, if one has occurred +} + +// NewJavascriptTracer instantiates a new JavascriptTracer instance. +// code specifies a Javascript snippet, which must evaluate to an expression +// returning an object with 'step' and 'result' functions. +func NewJavascriptTracer(code string) (*JavascriptTracer, error) { + vm := otto.New() + vm.Interrupt = make(chan func(), 1) + + // Set up builtins for this environment + vm.Set("big", &fakeBig{}) + + jstracer, err := vm.Object("(" + code + ")") + if err != nil { + return nil, err + } + + // Check the required functions exist + step, err := jstracer.Get("step") + if err != nil { + return nil, err + } + if !step.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function step()") + } + + result, err := jstracer.Get("result") + if err != nil { + return nil, err + } + if !result.IsFunction() { + return nil, fmt.Errorf("Trace object must expose a function result()") + } + + // Create the persistent log object + log := make(map[string]interface{}) + logvalue, _ := vm.ToValue(log) + + // Create persistent wrappers for memory and stack + mem := &memoryWrapper{} + stack := &stackWrapper{} + db := &dbWrapper{} + + return &JavascriptTracer{ + vm: vm, + traceobj: jstracer, + log: log, + logvalue: logvalue, + memory: mem, + memvalue: mem.toValue(vm), + stack: stack, + stackvalue: stack.toValue(vm), + db: db, + dbvalue: db.toValue(vm), + err: nil, + }, nil +} + +// Stop terminates execution of any JavaScript +func (jst *JavascriptTracer) Stop(err error) { + jst.vm.Interrupt <- func() { + panic(err) + } +} + +// callSafely executes a method on a JS object, catching any panics and +// returning them as error objects. +func (jst *JavascriptTracer) callSafely(method string, argumentList ...interface{}) (ret interface{}, err error) { + defer func() { + if caught := recover(); caught != nil { + switch caught := caught.(type) { + case error: + err = caught + case string: + err = errors.New(caught) + case fmt.Stringer: + err = errors.New(caught.String()) + default: + panic(caught) + } + } + }() + + value, err := jst.traceobj.Call(method, argumentList...) + ret, _ = value.Export() + return ret, err +} + +func wrapError(context string, err error) error { + var message string + switch err := err.(type) { + case *otto.Error: + message = err.String() + default: + message = err.Error() + } + return fmt.Errorf("%v in server-side tracer function '%v'", message, context) +} + +// CaptureState implements the Tracer interface to trace a single step of VM execution +func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { + if jst.err == nil { + jst.memory.memory = memory + jst.stack.stack = stack + jst.db.db = env.StateDB + + ocw := &opCodeWrapper{op} + + jst.log["pc"] = pc + jst.log["op"] = ocw.toValue(jst.vm) + jst.log["gas"] = gas.Int64() + jst.log["gasPrice"] = cost.Int64() + jst.log["memory"] = jst.memvalue + jst.log["stack"] = jst.stackvalue + jst.log["depth"] = depth + jst.log["account"] = contract.Address() + jst.log["err"] = err + + _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) + if err != nil { + jst.err = wrapError("step", err) + } + } + return nil +} + +// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error +func (jst *JavascriptTracer) GetResult() (result interface{}, err error) { + if jst.err != nil { + return nil, jst.err + } + + result, err = jst.callSafely("result") + if err != nil { + err = wrapError("result", err) + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer_test.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer_test.go new file mode 100644 index 0000000..693afe8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/tracer_test.go @@ -0,0 +1,147 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "errors" + "math/big" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" +) + +type account struct{} + +func (account) SubBalance(amount *big.Int) {} +func (account) AddBalance(amount *big.Int) {} +func (account) SetAddress(common.Address) {} +func (account) Value() *big.Int { return nil } +func (account) SetBalance(*big.Int) {} +func (account) SetNonce(uint64) {} +func (account) Balance() *big.Int { return nil } +func (account) Address() common.Address { return common.Address{} } +func (account) ReturnGas(*big.Int) {} +func (account) SetCode(common.Hash, []byte) {} +func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} + +func runTrace(tracer *JavascriptTracer) (interface{}, error) { + env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + + contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) + contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} + + _, err := env.Interpreter().Run(contract, []byte{}) + if err != nil { + return nil, err + } + + return tracer.GetResult() +} + +func TestTracing(t *testing.T) { + tracer, err := NewJavascriptTracer("{count: 0, step: function() { this.count += 1; }, result: function() { return this.count; }}") + if err != nil { + t.Fatal(err) + } + + ret, err := runTrace(tracer) + if err != nil { + t.Fatal(err) + } + + value, ok := ret.(float64) + if !ok { + t.Errorf("Expected return value to be float64, was %T", ret) + } + if value != 3 { + t.Errorf("Expected return value to be 3, got %v", value) + } +} + +func TestStack(t *testing.T) { + tracer, err := NewJavascriptTracer("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, result: function() { return this.depths; }}") + if err != nil { + t.Fatal(err) + } + + ret, err := runTrace(tracer) + if err != nil { + t.Fatal(err) + } + + expected := []int{0, 1, 2} + if !reflect.DeepEqual(ret, expected) { + t.Errorf("Expected return value to be %#v, got %#v", expected, ret) + } +} + +func TestOpcodes(t *testing.T) { + tracer, err := NewJavascriptTracer("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, result: function() { return this.opcodes; }}") + if err != nil { + t.Fatal(err) + } + + ret, err := runTrace(tracer) + if err != nil { + t.Fatal(err) + } + + expected := []string{"PUSH1", "PUSH1", "STOP"} + if !reflect.DeepEqual(ret, expected) { + t.Errorf("Expected return value to be %#v, got %#v", expected, ret) + } +} + +func TestHalt(t *testing.T) { + timeout := errors.New("stahp") + tracer, err := NewJavascriptTracer("{step: function() { while(1); }, result: function() { return null; }}") + if err != nil { + t.Fatal(err) + } + + go func() { + time.Sleep(1 * time.Second) + tracer.Stop(timeout) + }() + + if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" { + t.Errorf("Expected timeout error, got %v", err) + } +} + +func TestHaltBetweenSteps(t *testing.T) { + tracer, err := NewJavascriptTracer("{step: function() {}, result: function() { return null; }}") + if err != nil { + t.Fatal(err) + } + + env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) + + tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) + timeout := errors.New("stahp") + tracer.Stop(timeout) + tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) + + if _, err := tracer.GetResult(); err.Error() != "stahp in server-side tracer function 'step'" { + t.Errorf("Expected timeout error, got %v", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/guide/guide.go b/vendor/github.com/ethereum/go-ethereum/internal/guide/guide.go new file mode 100644 index 0000000..44a0bc4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/guide/guide.go @@ -0,0 +1,18 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package guide is a small test suite to ensure snippets in the dev guide work. +package guide diff --git a/vendor/github.com/ethereum/go-ethereum/internal/guide/guide_test.go b/vendor/github.com/ethereum/go-ethereum/internal/guide/guide_test.go new file mode 100644 index 0000000..9c7ad16 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/guide/guide_test.go @@ -0,0 +1,101 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains the code snippets from the developer's guide embedded into +// Go tests. This ensures that any code published in out guides will not break +// accidentally via some code update. If some API changes nonetheless that needs +// modifying this file, please port any modification over into the developer's +// guide wiki pages too! + +package guide + +import ( + "io/ioutil" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/core/types" +) + +// Tests that the account management snippets work correctly. +func TestAccountManagement(t *testing.T) { + // Create a temporary folder to work with + workdir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("Failed to create temporary work dir: %v", err) + } + defer os.RemoveAll(workdir) + + // Create an encrypted keystore with standard crypto parameters + ks := keystore.NewKeyStore(filepath.Join(workdir, "keystore"), keystore.StandardScryptN, keystore.StandardScryptP) + + // Create a new account with the specified encryption passphrase + newAcc, err := ks.NewAccount("Creation password") + if err != nil { + t.Fatalf("Failed to create new account: %v", err) + } + // Export the newly created account with a different passphrase. The returned + // data from this method invocation is a JSON encoded, encrypted key-file + jsonAcc, err := ks.Export(newAcc, "Creation password", "Export password") + if err != nil { + t.Fatalf("Failed to export account: %v", err) + } + // Update the passphrase on the account created above inside the local keystore + if err := ks.Update(newAcc, "Creation password", "Update password"); err != nil { + t.Fatalf("Failed to update account: %v", err) + } + // Delete the account updated above from the local keystore + if err := ks.Delete(newAcc, "Update password"); err != nil { + t.Fatalf("Failed to delete account: %v", err) + } + // Import back the account we've exported (and then deleted) above with yet + // again a fresh passphrase + if _, err := ks.Import(jsonAcc, "Export password", "Import password"); err != nil { + t.Fatalf("Failed to import account: %v", err) + } + // Create a new account to sign transactions with + signer, err := ks.NewAccount("Signer password") + if err != nil { + t.Fatalf("Failed to create signer account: %v", err) + } + tx, chain := new(types.Transaction), big.NewInt(1) + + // Sign a transaction with a single authorization + if _, err := ks.SignTxWithPassphrase(signer, "Signer password", tx, chain); err != nil { + t.Fatalf("Failed to sign with passphrase: %v", err) + } + // Sign a transaction with multiple manually cancelled authorizations + if err := ks.Unlock(signer, "Signer password"); err != nil { + t.Fatalf("Failed to unlock account: %v", err) + } + if _, err := ks.SignTx(signer, tx, chain); err != nil { + t.Fatalf("Failed to sign with unlocked account: %v", err) + } + if err := ks.Lock(signer.Address); err != nil { + t.Fatalf("Failed to lock account: %v", err) + } + // Sign a transaction with multiple automatically cancelled authorizations + if err := ks.TimedUnlock(signer, "Signer password", time.Second); err != nil { + t.Fatalf("Failed to time unlock account: %v", err) + } + if _, err := ks.SignTx(signer, tx, chain); err != nil { + t.Fatalf("Failed to sign with time unlocked account: %v", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion.go new file mode 100644 index 0000000..7f484bb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package jsre + +import ( + "sort" + "strings" + + "github.com/robertkrimen/otto" +) + +// CompleteKeywords returns potential continuations for the given line. Since line is +// evaluated, callers need to make sure that evaluating line does not have side effects. +func (jsre *JSRE) CompleteKeywords(line string) []string { + var results []string + jsre.Do(func(vm *otto.Otto) { + results = getCompletions(vm, line) + }) + return results +} + +func getCompletions(vm *otto.Otto, line string) (results []string) { + parts := strings.Split(line, ".") + objRef := "this" + prefix := line + if len(parts) > 1 { + objRef = strings.Join(parts[0:len(parts)-1], ".") + prefix = parts[len(parts)-1] + } + + obj, _ := vm.Object(objRef) + if obj == nil { + return nil + } + iterOwnAndConstructorKeys(vm, obj, func(k string) { + if strings.HasPrefix(k, prefix) { + if objRef == "this" { + results = append(results, k) + } else { + results = append(results, strings.Join(parts[:len(parts)-1], ".")+"."+k) + } + } + }) + + // Append opening parenthesis (for functions) or dot (for objects) + // if the line itself is the only completion. + if len(results) == 1 && results[0] == line { + obj, _ := vm.Object(line) + if obj != nil { + if obj.Class() == "Function" { + results[0] += "(" + } else { + results[0] += "." + } + } + } + + sort.Strings(results) + return results +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion_test.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion_test.go new file mode 100644 index 0000000..ccbd73d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/completion_test.go @@ -0,0 +1,88 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package jsre + +import ( + "os" + "reflect" + "testing" +) + +func TestCompleteKeywords(t *testing.T) { + re := New("", os.Stdout) + re.Run(` + function theClass() { + this.foo = 3; + this.gazonk = {xyz: 4}; + } + theClass.prototype.someMethod = function () {}; + var x = new theClass(); + var y = new theClass(); + y.someMethod = function override() {}; + `) + + var tests = []struct { + input string + want []string + }{ + { + input: "x", + want: []string{"x."}, + }, + { + input: "x.someMethod", + want: []string{"x.someMethod("}, + }, + { + input: "x.", + want: []string{ + "x.constructor", + "x.foo", + "x.gazonk", + "x.someMethod", + }, + }, + { + input: "y.", + want: []string{ + "y.constructor", + "y.foo", + "y.gazonk", + "y.someMethod", + }, + }, + { + input: "x.gazonk.", + want: []string{ + "x.gazonk.constructor", + "x.gazonk.hasOwnProperty", + "x.gazonk.isPrototypeOf", + "x.gazonk.propertyIsEnumerable", + "x.gazonk.toLocaleString", + "x.gazonk.toString", + "x.gazonk.valueOf", + "x.gazonk.xyz", + }, + }, + } + for _, test := range tests { + cs := re.CompleteKeywords(test.input) + if !reflect.DeepEqual(cs, test.want) { + t.Errorf("wrong completions for %q\ngot %v\nwant %v", test.input, cs, test.want) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bignumber.js b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bignumber.js new file mode 100644 index 0000000..17c8851 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bignumber.js @@ -0,0 +1,4 @@ +/* bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */ +/* modified by zelig to fix https://github.com/robertkrimen/otto#regular-expression-incompatibility */ +!function(e){"use strict";function n(e){function a(e,n){var t,r,i,o,u,s,f=this;if(!(f instanceof a))return j&&L(26,"constructor call without new",e),new a(e,n);if(null!=n&&H(n,2,64,M,"base")){if(n=0|n,s=e+"",10==n)return f=new a(e instanceof a?e:s),U(f,P+f.e+1,k);if((o="number"==typeof e)&&0*e!=0||!new RegExp("^-?"+(t="["+O.slice(0,n)+"]+")+"(?:\\."+t+")?$",37>n?"i":"").test(s))return g(f,s,o,n);o?(f.s=0>1/e?(s=s.slice(1),-1):1,j&&s.replace(/^0\.0*|\./,"").length>15&&L(M,b,e),o=!1):f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1,s=D(s,10,n,f.s)}else{if(e instanceof a)return f.s=e.s,f.e=e.e,f.c=(e=e.c)?e.slice():e,void(M=0);if((o="number"==typeof e)&&0*e==0){if(f.s=0>1/e?(e=-e,-1):1,e===~~e){for(r=0,i=e;i>=10;i/=10,r++);return f.e=r,f.c=[e],void(M=0)}s=e+""}else{if(!p.test(s=e+""))return g(f,s,o);f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1}}for((r=s.indexOf("."))>-1&&(s=s.replace(".","")),(i=s.search(/e/i))>0?(0>r&&(r=i),r+=+s.slice(i+1),s=s.substring(0,i)):0>r&&(r=s.length),i=0;48===s.charCodeAt(i);i++);for(u=s.length;48===s.charCodeAt(--u););if(s=s.slice(i,u+1))if(u=s.length,o&&j&&u>15&&L(M,b,f.s*e),r=r-i-1,r>z)f.c=f.e=null;else if(G>r)f.c=[f.e=0];else{if(f.e=r,f.c=[],i=(r+1)%y,0>r&&(i+=y),u>i){for(i&&f.c.push(+s.slice(0,i)),u-=y;u>i;)f.c.push(+s.slice(i,i+=y));s=s.slice(i),i=y-s.length}else i-=u;for(;i--;s+="0");f.c.push(+s)}else f.c=[f.e=0];M=0}function D(e,n,t,i){var o,u,f,c,h,g,p,d=e.indexOf("."),m=P,w=k;for(37>t&&(e=e.toLowerCase()),d>=0&&(f=J,J=0,e=e.replace(".",""),p=new a(t),h=p.pow(e.length-d),J=f,p.c=s(l(r(h.c),h.e),10,n),p.e=p.c.length),g=s(e,t,n),u=f=g.length;0==g[--f];g.pop());if(!g[0])return"0";if(0>d?--u:(h.c=g,h.e=u,h.s=i,h=C(h,p,m,w,n),g=h.c,c=h.r,u=h.e),o=u+m+1,d=g[o],f=n/2,c=c||0>o||null!=g[o+1],c=4>w?(null!=d||c)&&(0==w||w==(h.s<0?3:2)):d>f||d==f&&(4==w||c||6==w&&1&g[o-1]||w==(h.s<0?8:7)),1>o||!g[0])e=c?l("1",-m):"0";else{if(g.length=o,c)for(--n;++g[--o]>n;)g[o]=0,o||(++u,g.unshift(1));for(f=g.length;!g[--f];);for(d=0,e="";f>=d;e+=O.charAt(g[d++]));e=l(e,u)}return e}function _(e,n,t,i){var o,u,s,c,h;if(t=null!=t&&H(t,0,8,i,v)?0|t:k,!e.c)return e.toString();if(o=e.c[0],s=e.e,null==n)h=r(e.c),h=19==i||24==i&&B>=s?f(h,s):l(h,s);else if(e=U(new a(e),n,t),u=e.e,h=r(e.c),c=h.length,19==i||24==i&&(u>=n||B>=u)){for(;n>c;h+="0",c++);h=f(h,u)}else if(n-=s,h=l(h,u),u+1>c){if(--n>0)for(h+=".";n--;h+="0");}else if(n+=u-c,n>0)for(u+1==c&&(h+=".");n--;h+="0");return e.s<0&&o?"-"+h:h}function x(e,n){var t,r,i=0;for(u(e[0])&&(e=e[0]),t=new a(e[0]);++ie||e>t||e!=c(e))&&L(r,(i||"decimal places")+(n>e||e>t?" out of range":" not an integer"),e),!0}function I(e,n,t){for(var r=1,i=n.length;!n[--i];n.pop());for(i=n[0];i>=10;i/=10,r++);return(t=r+t*y-1)>z?e.c=e.e=null:G>t?e.c=[e.e=0]:(e.e=t,e.c=n),e}function L(e,n,t){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][e]+"() "+n+": "+t);throw r.name="BigNumber Error",M=0,r}function U(e,n,t,r){var i,o,u,s,f,l,c,a=e.c,h=R;if(a){e:{for(i=1,s=a[0];s>=10;s/=10,i++);if(o=n-i,0>o)o+=y,u=n,f=a[l=0],c=f/h[i-u-1]%10|0;else if(l=d((o+1)/y),l>=a.length){if(!r)break e;for(;a.length<=l;a.push(0));f=c=0,i=1,o%=y,u=o-y+1}else{for(f=s=a[l],i=1;s>=10;s/=10,i++);o%=y,u=o-y+i,c=0>u?0:f/h[i-u-1]%10|0}if(r=r||0>n||null!=a[l+1]||(0>u?f:f%h[i-u-1]),r=4>t?(c||r)&&(0==t||t==(e.s<0?3:2)):c>5||5==c&&(4==t||r||6==t&&(o>0?u>0?f/h[i-u]:0:a[l-1])%10&1||t==(e.s<0?8:7)),1>n||!a[0])return a.length=0,r?(n-=e.e+1,a[0]=h[n%y],e.e=-n||0):a[0]=e.e=0,e;if(0==o?(a.length=l,s=1,l--):(a.length=l+1,s=h[y-o],a[l]=u>0?m(f/h[i-u]%h[u])*s:0),r)for(;;){if(0==l){for(o=1,u=a[0];u>=10;u/=10,o++);for(u=a[0]+=s,s=1;u>=10;u/=10,s++);o!=s&&(e.e++,a[0]==N&&(a[0]=1));break}if(a[l]+=s,a[l]!=N)break;a[l--]=0,s=1}for(o=a.length;0===a[--o];a.pop());}e.e>z?e.c=e.e=null:e.et?null!=(e=i[t++]):void 0};return f(n="DECIMAL_PLACES")&&H(e,0,E,2,n)&&(P=0|e),r[n]=P,f(n="ROUNDING_MODE")&&H(e,0,8,2,n)&&(k=0|e),r[n]=k,f(n="EXPONENTIAL_AT")&&(u(e)?H(e[0],-E,0,2,n)&&H(e[1],0,E,2,n)&&(B=0|e[0],$=0|e[1]):H(e,-E,E,2,n)&&(B=-($=0|(0>e?-e:e)))),r[n]=[B,$],f(n="RANGE")&&(u(e)?H(e[0],-E,-1,2,n)&&H(e[1],1,E,2,n)&&(G=0|e[0],z=0|e[1]):H(e,-E,E,2,n)&&(0|e?G=-(z=0|(0>e?-e:e)):j&&L(2,n+" cannot be zero",e))),r[n]=[G,z],f(n="ERRORS")&&(e===!!e||1===e||0===e?(M=0,H=(j=!!e)?F:o):j&&L(2,n+w,e)),r[n]=j,f(n="CRYPTO")&&(e===!!e||1===e||0===e?(V=!(!e||!h||"object"!=typeof h),e&&!V&&j&&L(2,"crypto unavailable",h)):j&&L(2,n+w,e)),r[n]=V,f(n="MODULO_MODE")&&H(e,0,9,2,n)&&(W=0|e),r[n]=W,f(n="POW_PRECISION")&&H(e,0,E,2,n)&&(J=0|e),r[n]=J,f(n="FORMAT")&&("object"==typeof e?X=e:j&&L(2,n+" not an object",e)),r[n]=X,r},a.max=function(){return x(arguments,T.lt)},a.min=function(){return x(arguments,T.gt)},a.random=function(){var e=9007199254740992,n=Math.random()*e&2097151?function(){return m(Math.random()*e)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(e){var t,r,i,o,u,s=0,f=[],l=new a(q);if(e=null!=e&&H(e,0,E,14)?0|e:P,o=d(e/y),V)if(h&&h.getRandomValues){for(t=h.getRandomValues(new Uint32Array(o*=2));o>s;)u=131072*t[s]+(t[s+1]>>>11),u>=9e15?(r=h.getRandomValues(new Uint32Array(2)),t[s]=r[0],t[s+1]=r[1]):(f.push(u%1e14),s+=2);s=o/2}else if(h&&h.randomBytes){for(t=h.randomBytes(o*=7);o>s;)u=281474976710656*(31&t[s])+1099511627776*t[s+1]+4294967296*t[s+2]+16777216*t[s+3]+(t[s+4]<<16)+(t[s+5]<<8)+t[s+6],u>=9e15?h.randomBytes(7).copy(t,s):(f.push(u%1e14),s+=7);s=o/7}else j&&L(14,"crypto unavailable",h);if(!s)for(;o>s;)u=n(),9e15>u&&(f[s++]=u%1e14);for(o=f[--s],e%=y,o&&e&&(u=R[y-e],f[s]=m(o/u)*u);0===f[s];f.pop(),s--);if(0>s)f=[i=0];else{for(i=-1;0===f[0];f.shift(),i-=y);for(s=1,u=f[0];u>=10;u/=10,s++);y>s&&(i-=y-s)}return l.e=i,l.c=f,l}}(),C=function(){function e(e,n,t){var r,i,o,u,s=0,f=e.length,l=n%A,c=n/A|0;for(e=e.slice();f--;)o=e[f]%A,u=e[f]/A|0,r=c*o+u*l,i=l*o+r%A*A+s,s=(i/t|0)+(r/A|0)+c*u,e[f]=i%t;return s&&e.unshift(s),e}function n(e,n,t,r){var i,o;if(t!=r)o=t>r?1:-1;else for(i=o=0;t>i;i++)if(e[i]!=n[i]){o=e[i]>n[i]?1:-1;break}return o}function r(e,n,t,r){for(var i=0;t--;)e[t]-=i,i=e[t]1;e.shift());}return function(i,o,u,s,f){var l,c,h,g,p,d,w,v,b,O,S,R,A,E,D,_,x,F=i.s==o.s?1:-1,I=i.c,L=o.c;if(!(I&&I[0]&&L&&L[0]))return new a(i.s&&o.s&&(I?!L||I[0]!=L[0]:L)?I&&0==I[0]||!L?0*F:F/0:0/0);for(v=new a(F),b=v.c=[],c=i.e-o.e,F=u+c+1,f||(f=N,c=t(i.e/y)-t(o.e/y),F=F/y|0),h=0;L[h]==(I[h]||0);h++);if(L[h]>(I[h]||0)&&c--,0>F)b.push(1),g=!0;else{for(E=I.length,_=L.length,h=0,F+=2,p=m(f/(L[0]+1)),p>1&&(L=e(L,p,f),I=e(I,p,f),_=L.length,E=I.length),A=_,O=I.slice(0,_),S=O.length;_>S;O[S++]=0);x=L.slice(),x.unshift(0),D=L[0],L[1]>=f/2&&D++;do p=0,l=n(L,O,_,S),0>l?(R=O[0],_!=S&&(R=R*f+(O[1]||0)),p=m(R/D),p>1?(p>=f&&(p=f-1),d=e(L,p,f),w=d.length,S=O.length,l=n(d,O,w,S),1==l&&(p--,r(d,w>_?x:L,w,f))):(0==p&&(l=p=1),d=L.slice()),w=d.length,S>w&&d.unshift(0),r(O,d,S,f),-1==l&&(S=O.length,l=n(L,O,_,S),1>l&&(p++,r(O,S>_?x:L,S,f))),S=O.length):0===l&&(p++,O=[0]),b[h++]=p,l&&O[0]?O[S++]=I[A]||0:(O=[I[A]],S=1);while((A++=10;F/=10,h++);U(v,u+(v.e=h+c*y-1)+1,s,g)}else v.e=c,v.r=+g;return v}}(),g=function(){var e=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,t=/^\.([^.]+)$/,r=/^-?(Infinity|NaN)$/,i=/^\s*\+([\w.])|^\s+|\s+$/g;return function(o,u,s,f){var l,c=s?u:u.replace(i,"$1");if(r.test(c))o.s=isNaN(c)?null:0>c?-1:1;else{if(!s&&(c=c.replace(e,function(e,n,t){return l="x"==(t=t.toLowerCase())?16:"b"==t?2:8,f&&f!=l?e:n}),f&&(l=f,c=c.replace(n,"$1").replace(t,"0.$1")),u!=c))return new a(c,l);j&&L(M,"not a"+(f?" base "+f:"")+" number",u),o.s=null}o.c=o.e=null,M=0}}(),T.absoluteValue=T.abs=function(){var e=new a(this);return e.s<0&&(e.s=1),e},T.ceil=function(){return U(new a(this),this.e+1,2)},T.comparedTo=T.cmp=function(e,n){return M=1,i(this,new a(e,n))},T.decimalPlaces=T.dp=function(){var e,n,r=this.c;if(!r)return null;if(e=((n=r.length-1)-t(this.e/y))*y,n=r[n])for(;n%10==0;n/=10,e--);return 0>e&&(e=0),e},T.dividedBy=T.div=function(e,n){return M=3,C(this,new a(e,n),P,k)},T.dividedToIntegerBy=T.divToInt=function(e,n){return M=4,C(this,new a(e,n),0,1)},T.equals=T.eq=function(e,n){return M=5,0===i(this,new a(e,n))},T.floor=function(){return U(new a(this),this.e+1,3)},T.greaterThan=T.gt=function(e,n){return M=6,i(this,new a(e,n))>0},T.greaterThanOrEqualTo=T.gte=function(e,n){return M=7,1===(n=i(this,new a(e,n)))||0===n},T.isFinite=function(){return!!this.c},T.isInteger=T.isInt=function(){return!!this.c&&t(this.e/y)>this.c.length-2},T.isNaN=function(){return!this.s},T.isNegative=T.isNeg=function(){return this.s<0},T.isZero=function(){return!!this.c&&0==this.c[0]},T.lessThan=T.lt=function(e,n){return M=8,i(this,new a(e,n))<0},T.lessThanOrEqualTo=T.lte=function(e,n){return M=9,-1===(n=i(this,new a(e,n)))||0===n},T.minus=T.sub=function(e,n){var r,i,o,u,s=this,f=s.s;if(M=10,e=new a(e,n),n=e.s,!f||!n)return new a(0/0);if(f!=n)return e.s=-n,s.plus(e);var l=s.e/y,c=e.e/y,h=s.c,g=e.c;if(!l||!c){if(!h||!g)return h?(e.s=-n,e):new a(g?s:0/0);if(!h[0]||!g[0])return g[0]?(e.s=-n,e):new a(h[0]?s:3==k?-0:0)}if(l=t(l),c=t(c),h=h.slice(),f=l-c){for((u=0>f)?(f=-f,o=h):(c=l,o=g),o.reverse(),n=f;n--;o.push(0));o.reverse()}else for(i=(u=(f=h.length)<(n=g.length))?f:n,f=n=0;i>n;n++)if(h[n]!=g[n]){u=h[n]0)for(;n--;h[r++]=0);for(n=N-1;i>f;){if(h[--i]0?(s=u,r=l):(o=-o,r=f),r.reverse();o--;r.push(0));r.reverse()}for(o=f.length,n=l.length,0>o-n&&(r=l,l=f,f=r,n=o),o=0;n;)o=(f[--n]=f[n]+l[n]+o)/N|0,f[n]%=N;return o&&(f.unshift(o),++s),I(e,f,s)},T.precision=T.sd=function(e){var n,t,r=this,i=r.c;if(null!=e&&e!==!!e&&1!==e&&0!==e&&(j&&L(13,"argument"+w,e),e!=!!e&&(e=null)),!i)return null;if(t=i.length-1,n=t*y+1,t=i[t]){for(;t%10==0;t/=10,n--);for(t=i[0];t>=10;t/=10,n++);}return e&&r.e+1>n&&(n=r.e+1),n},T.round=function(e,n){var t=new a(this);return(null==e||H(e,0,E,15))&&U(t,~~e+this.e+1,null!=n&&H(n,0,8,15,v)?0|n:k),t},T.shift=function(e){var n=this;return H(e,-S,S,16,"argument")?n.times("1e"+c(e)):new a(n.c&&n.c[0]&&(-S>e||e>S)?n.s*(0>e?0:1/0):n)},T.squareRoot=T.sqrt=function(){var e,n,i,o,u,s=this,f=s.c,l=s.s,c=s.e,h=P+4,g=new a("0.5");if(1!==l||!f||!f[0])return new a(!l||0>l&&(!f||f[0])?0/0:f?s:1/0);if(l=Math.sqrt(+s),0==l||l==1/0?(n=r(f),(n.length+c)%2==0&&(n+="0"),l=Math.sqrt(n),c=t((c+1)/2)-(0>c||c%2),l==1/0?n="1e"+c:(n=l.toExponential(),n=n.slice(0,n.indexOf("e")+1)+c),i=new a(n)):i=new a(l+""),i.c[0])for(c=i.e,l=c+h,3>l&&(l=0);;)if(u=i,i=g.times(u.plus(C(s,u,h,1))),r(u.c).slice(0,l)===(n=r(i.c)).slice(0,l)){if(i.el&&(m=O,O=S,S=m,o=l,l=g,g=o),o=l+g,m=[];o--;m.push(0));for(w=N,v=A,o=g;--o>=0;){for(r=0,p=S[o]%v,d=S[o]/v|0,s=l,u=o+s;u>o;)c=O[--s]%v,h=O[s]/v|0,f=d*c+h*p,c=p*c+f%v*v+m[u]+r,r=(c/w|0)+(f/v|0)+d*h,m[u--]=c%w;m[u]=r}return r?++i:m.shift(),I(e,m,i)},T.toDigits=function(e,n){var t=new a(this);return e=null!=e&&H(e,1,E,18,"precision")?0|e:null,n=null!=n&&H(n,0,8,18,v)?0|n:k,e?U(t,e,n):t},T.toExponential=function(e,n){return _(this,null!=e&&H(e,0,E,19)?~~e+1:null,n,19)},T.toFixed=function(e,n){return _(this,null!=e&&H(e,0,E,20)?~~e+this.e+1:null,n,20)},T.toFormat=function(e,n){var t=_(this,null!=e&&H(e,0,E,21)?~~e+this.e+1:null,n,21);if(this.c){var r,i=t.split("."),o=+X.groupSize,u=+X.secondaryGroupSize,s=X.groupSeparator,f=i[0],l=i[1],c=this.s<0,a=c?f.slice(1):f,h=a.length;if(u&&(r=o,o=u,u=r,h-=r),o>0&&h>0){for(r=h%o||o,f=a.substr(0,r);h>r;r+=o)f+=s+a.substr(r,o);u>0&&(f+=s+a.slice(r)),c&&(f="-"+f)}t=l?f+X.decimalSeparator+((u=+X.fractionGroupSize)?l.replace(new RegExp("\\d{"+u+"}\\B","g"),"$&"+X.fractionGroupSeparator):l):f}return t},T.toFraction=function(e){var n,t,i,o,u,s,f,l,c,h=j,g=this,p=g.c,d=new a(q),m=t=new a(q),w=f=new a(q);if(null!=e&&(j=!1,s=new a(e),j=h,(!(h=s.isInt())||s.lt(q))&&(j&&L(22,"max denominator "+(h?"out of range":"not an integer"),e),e=!h&&s.c&&U(s,s.e+1,1).gte(q)?s:null)),!p)return g.toString();for(c=r(p),o=d.e=c.length-g.e-1,d.c[0]=R[(u=o%y)<0?y+u:u],e=!e||s.cmp(d)>0?o>0?d:m:s,u=z,z=1/0,s=new a(c),f.c[0]=0;l=C(s,d,0,1),i=t.plus(l.times(w)),1!=i.cmp(e);)t=w,w=i,m=f.plus(l.times(i=m)),f=i,d=s.minus(l.times(i=d)),s=i;return i=C(e.minus(t),w,0,1),f=f.plus(i.times(m)),t=t.plus(i.times(w)),f.s=m.s=g.s,o*=2,n=C(m,w,o,k).minus(g).abs().cmp(C(f,t,o,k).minus(g).abs())<1?[m.toString(),w.toString()]:[f.toString(),t.toString()],z=u,n},T.toNumber=function(){var e=this;return+e||(e.s?0*e.s:0/0)},T.toPower=T.pow=function(e){var n,t,r=m(0>e?-e:+e),i=this;if(!H(e,-S,S,23,"exponent")&&(!isFinite(e)||r>S&&(e/=0)||parseFloat(e)!=e&&!(e=0/0)))return new a(Math.pow(+i,e));for(n=J?d(J/y+2):0,t=new a(q);;){if(r%2){if(t=t.times(i),!t.c)break;n&&t.c.length>n&&(t.c.length=n)}if(r=m(r/2),!r)break;i=i.times(i),n&&i.c&&i.c.length>n&&(i.c.length=n)}return 0>e&&(t=q.div(t)),n?U(t,J,k):t},T.toPrecision=function(e,n){return _(this,null!=e&&H(e,1,E,24,"precision")?0|e:null,n,24)},T.toString=function(e){var n,t=this,i=t.s,o=t.e;return null===o?i?(n="Infinity",0>i&&(n="-"+n)):n="NaN":(n=r(t.c),n=null!=e&&H(e,2,64,25,"base")?D(l(n,o),0|e,10,i):B>=o||o>=$?f(n,o):l(n,o),0>i&&t.c[0]&&(n="-"+n)),n},T.truncated=T.trunc=function(){return U(new a(this),this.e+1,1)},T.valueOf=T.toJSON=function(){return this.toString()},null!=e&&a.config(e),a}function t(e){var n=0|e;return e>0||e===n?n:n-1}function r(e){for(var n,t,r=1,i=e.length,o=e[0]+"";i>r;){for(n=e[r++]+"",t=y-n.length;t--;n="0"+n);o+=n}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function i(e,n){var t,r,i=e.c,o=n.c,u=e.s,s=n.s,f=e.e,l=n.e;if(!u||!s)return null;if(t=i&&!i[0],r=o&&!o[0],t||r)return t?r?0:-s:u;if(u!=s)return u;if(t=0>u,r=f==l,!i||!o)return r?0:!i^t?1:-1;if(!r)return f>l^t?1:-1;for(s=(f=i.length)<(l=o.length)?f:l,u=0;s>u;u++)if(i[u]!=o[u])return i[u]>o[u]^t?1:-1;return f==l?0:f>l^t?1:-1}function o(e,n,t){return(e=c(e))>=n&&t>=e}function u(e){return"[object Array]"==Object.prototype.toString.call(e)}function s(e,n,t){for(var r,i,o=[0],u=0,s=e.length;s>u;){for(i=o.length;i--;o[i]*=n);for(o[r=0]+=O.indexOf(e.charAt(u++));rt-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/t|0,o[r]%=t)}return o.reverse()}function f(e,n){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(0>n?"e":"e+")+n}function l(e,n){var t,r;if(0>n){for(r="0.";++n;r+="0");e=r+e}else if(t=e.length,++n>t){for(r="0",n-=t;--n;r+="0");e+=r}else t>n&&(e=e.slice(0,n)+"."+e.slice(n));return e}function c(e){return e=parseFloat(e),0>e?d(e):m(e)}var a,h,g,p=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,d=Math.ceil,m=Math.floor,w=" not a boolean or binary digit",v="rounding mode",b="number type has more than 15 significant digits",O="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",N=1e14,y=14,S=9007199254740991,R=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],A=1e7,E=1e9;if(a=n(),"function"==typeof define&&define.amd)define(function(){return a});else if("undefined"!=typeof module&&module.exports){if(module.exports=a,!h)try{h=require("crypto")}catch(D){}}else e.BigNumber=a}(this); +//# sourceMappingURL=doc/bignumber.js.map diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go new file mode 100644 index 0000000..5f6a2b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/bindata.go @@ -0,0 +1,258 @@ +// Code generated by go-bindata. +// sources: +// bignumber.js +// web3.js +// DO NOT EDIT! + +package deps + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _bignumberJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xbc\x6b\x77\x9b\xc8\x93\x38\xfc\x7e\x3f\x85\xc4\xc6\x9c\x6e\x53\x20\x90\x9d\x38\x86\x14\x9c\x4c\x62\xe7\xe7\x79\x1c\x3b\x4f\x9c\xcc\xcc\xae\xa2\xc9\x91\x51\x23\x75\x82\x40\xe1\x62\xc7\x09\xfe\x7d\xf6\xff\xa9\x6e\x40\xf2\x25\xbb\xb3\x6f\x2c\xe8\x4b\x75\x75\x75\xdd\xbb\xf0\x68\x77\x70\x29\x17\x59\xbd\xba\x14\x85\xf3\xa5\x1c\x5c\x8d\x1d\xd7\xd9\x1b\x2c\xab\x6a\x5d\xfa\xa3\xd1\x42\x56\xcb\xfa\xd2\x89\xf3\xd5\xe8\xad\xfc\x2a\xde\xc6\xe9\x68\x7b\xf8\xe8\xf4\xe4\xd5\xd1\xd9\xab\xa3\xc1\xee\xe8\x3f\x46\xbb\x83\x55\x3e\x97\x89\x14\xf3\xc1\xe5\xcd\xe0\x87\x48\xe5\x62\x50\xe5\x83\x44\x7e\x7f\x0c\x5c\x91\x5f\x8a\xa2\xfa\x5a\xc8\x95\xc8\x46\x79\x55\xe5\xff\x59\x88\x45\x9d\xce\x0a\x5b\x7c\x5f\x17\xa2\x2c\x65\x9e\xd9\x32\x8b\xf3\xd5\x7a\x56\xc9\x4b\x99\xca\xea\x86\x96\x19\x26\x75\x16\x57\x32\xcf\x98\xe0\x3f\x8d\xba\x14\x83\xb2\x2a\x64\x5c\x19\x41\xd7\x31\x50\x5d\xfd\xdb\x8c\x09\xc8\xf8\xcf\xab\x59\x31\xa8\xa0\x00\x09\x39\xd4\x50\x42\x82\xd5\x52\x96\x81\x4c\xd8\x90\x25\x03\x99\x95\xd5\x2c\x8b\x45\x9e\x0c\x66\x9c\x17\xa2\xaa\x8b\x6c\xf0\xc5\x34\x4f\xd9\xf8\x19\x18\x71\x9e\x95\x55\x51\xc7\x55\x5e\x0c\xe2\x59\x9a\x0e\xae\x65\xb5\xcc\xeb\x6a\x90\x89\x6b\x03\x04\x87\x4c\x5c\xb7\xeb\x10\xc0\xac\x4e\xd3\x21\x66\xa6\xf9\x2f\x96\xc1\x18\x9e\xed\xc3\x5b\x30\x2e\x67\xa5\x30\x38\xff\x49\xfd\xe8\x36\x19\x94\x28\x2c\xc3\x00\xcf\x45\xcc\xba\x15\x13\x6c\x21\xdd\x41\x28\x12\x7e\xc9\xe1\x23\x4b\xe0\x9d\x95\x38\xc2\xf2\xe0\xab\x5a\x87\xe5\x68\xe8\xa3\x30\x10\xab\x9b\x35\x0d\x16\xdc\x34\xdd\x5d\x31\x44\xb7\x69\x86\x04\xec\xbd\x58\x1c\x7d\x5f\x33\xe3\x6f\x3b\x32\x2c\x56\xa1\x31\x31\xac\x73\xa7\x4c\x65\x2c\x98\x0b\x19\xb7\x8c\xa9\x65\x70\xcb\x60\x91\xff\xe9\x93\x63\x58\x95\x65\xf0\xe8\x89\x01\x7b\x07\x61\x16\x19\xd2\xf0\x0d\x83\x3b\x95\x28\x2b\x56\xf6\x84\x59\xb0\x04\x4a\xc8\x69\xbb\x79\xc4\x12\xa7\x44\x37\xf4\x46\x22\x62\x25\x96\x2d\x68\x8f\x83\xed\x71\xdf\x83\x2f\xa6\x59\x3a\x85\x58\xa7\xb3\x58\xb0\xd1\xdf\xee\x27\xc7\xdd\x6d\x3e\x39\x23\x20\xb8\xa9\xc8\x16\xd5\x32\xf4\x9e\x12\xa5\xdf\xc2\x25\xd1\x32\xc7\xa1\xc7\x7d\x02\xba\xff\x14\x11\x4b\x27\x5e\xce\x8a\x57\xf9\x5c\xbc\xac\x98\xcb\x1f\x5d\xa3\xc4\xd7\xac\x04\xcf\x85\x0c\x12\xa7\xe4\xb7\x22\x2d\x05\x11\xfa\x2e\x19\x7b\x22\x3b\x25\x0a\xa7\x84\xc4\x11\x28\x1c\x01\x89\x13\x23\xa3\xc7\x98\x47\xa2\x05\xcd\x7d\x01\x57\xb9\x9c\xb3\xb7\xe8\xfe\x6f\xb4\x46\x74\xd5\xb1\x6e\xd1\x41\xa0\x2d\x5a\xdc\x04\x22\xfe\xfb\xdf\xc4\x90\x79\xc1\x0a\x74\x41\xa2\x08\x64\x88\x9e\x1b\xc8\x11\x7a\x2e\x14\x96\xc5\x83\x1e\x35\x81\x85\x42\x68\x22\xa6\x1b\x04\x6e\x35\xaf\xf4\xfb\x1a\xae\xdb\x13\x51\xcd\xf7\x8f\x85\x07\xff\x17\xe2\xdd\xde\x12\x62\xac\xc0\xd2\x91\xd9\x5c\x7c\x3f\x4f\x98\xe1\x18\x9c\x87\xb6\x67\x9a\x6a\x7c\x77\x78\x86\x63\xd0\xa1\x71\x60\x92\xa0\x88\x59\x11\x2f\xd9\x48\x8c\x24\xe7\xa1\x1b\x31\x37\x2c\x4c\x93\x15\x28\x39\x14\x16\x5a\xdd\x3a\xd2\xf2\x38\xa8\x65\xeb\x4b\x92\xd4\x6c\xc1\x5c\x90\x9c\xfb\xdd\xf8\xb2\xe5\x02\x0e\x12\xdd\x60\xff\xf9\x7d\xb4\x25\x0f\x24\x91\x88\xd0\xac\xfb\xd1\x8f\x0c\xb4\xed\x9a\x07\xea\xb0\x36\xbb\x94\x50\x5b\x1e\xe7\x32\xd9\x9a\x0a\xb9\x69\x7e\x31\xcd\x7a\x8b\xed\x12\xa7\xdc\x15\x1c\x0a\x2c\x6c\x69\x7b\x50\x84\x3f\x38\x1d\x02\x1d\x07\x09\x73\x40\x84\x1f\xc8\x84\xbd\x09\x0b\xd5\x31\xa1\x1e\x77\x1a\x74\x07\xb2\x75\x6e\x53\x90\xc8\x0a\xcb\xe3\x3b\x37\xa0\xb7\x28\x2d\xbc\xe1\x50\x87\x52\xf3\x80\x34\xcd\xc4\x89\x9d\x75\x5d\x2e\x59\x4f\x25\x45\x12\xa8\x6d\xbc\x09\xea\x50\x06\xfc\xe1\x08\x09\x0a\x0e\x0f\xb6\x36\x47\x24\xbb\xb1\xbb\x7d\xdd\x6a\x2c\x6d\xac\x15\xad\x02\x69\xdb\x41\x69\xa1\xe1\x1a\xc4\x11\x3d\x3c\x2d\x1e\x83\xed\x6d\xbc\x45\xf7\xb6\xd7\x97\xaf\x49\x8f\x41\x05\x52\xeb\x4c\xd2\x96\x09\xc4\xb0\x84\x05\xac\x61\x8e\xe2\x0e\x9b\xc0\x0a\xdf\xc1\x35\x7e\x55\x2b\xee\x1d\x84\x95\x69\x2a\x51\xaa\xf2\xd3\xfc\x5a\x14\xaf\x66\xa5\x60\x9c\xc3\x3c\x44\xd7\x34\x59\x82\xbf\xc3\xef\xe8\x02\x8d\xb8\xc7\x55\xb0\x6e\x55\x5f\xc5\x61\x89\x6b\x67\x9d\x5f\x33\xd1\x6e\xcc\x9e\x73\xf8\x1d\x13\x58\x3b\x31\x96\x2c\x65\x05\x5b\x3a\x31\x87\xa5\x23\xb8\x12\x7a\x0e\x6b\x47\xe0\xda\x89\x7b\x4e\x5a\x60\xc9\x04\x54\xd4\x55\x63\x82\x8b\x8e\x69\x5c\xc4\xc5\xc4\xb6\x93\x69\xb0\x70\xd6\xf9\x9a\x71\xc5\x2e\xc3\xc5\xc4\x9d\xb6\x42\x64\xb8\x06\x35\xb9\xe1\x3c\xb2\xed\xda\xa7\x95\x70\x41\x4b\x61\x0d\x4b\xa7\x44\x09\x4b\x7c\xc5\x96\xb0\x86\x15\x5c\x13\xfc\x05\x2e\x9d\x18\x62\x5c\x3a\x05\xd4\xa8\x70\xca\xb1\xb6\x56\x96\x07\x73\x5c\x4c\xf2\x29\x24\x98\x8d\xc6\x10\x63\xdc\x34\x6e\x98\x37\x8d\x36\x0f\x8b\x49\x6e\x79\x53\x88\x71\x3f\xbc\x8e\x5a\x93\x31\x6f\x9a\x98\x9b\x26\x73\x11\xaf\x9b\xe6\x1a\x91\x2d\x9d\xf2\x85\x1b\xed\xf9\x63\xce\xfd\x79\x98\x34\xcd\x1c\x31\x31\x4d\xb6\xaf\x46\xc4\x4d\xf3\x0c\xf1\xda\x34\x3d\x73\x31\xc9\x6d\x6f\xba\x3d\xe9\xb9\x7f\xc0\x39\x78\xb4\xa2\xde\xa0\xc0\x38\x4a\x99\xe1\x19\x60\xaf\xb8\x4f\x1b\xed\xd8\xb7\xa3\x0f\xe6\x10\x73\x3a\x49\xdb\xce\x02\xcb\x22\x52\xe5\xd3\x30\x0b\x38\xed\x03\x5d\xc8\x9b\x86\x59\x56\x0d\x0b\xa7\xce\xca\xa5\x4c\x2a\xe6\x71\x2d\x98\x5b\x34\x1e\xb6\x14\xd6\x1d\x73\x75\xdc\x86\x11\x24\x21\xce\x03\x61\xe1\xb9\x12\xd9\x97\x15\x5b\x4c\xe6\x96\x35\xe5\x3c\x10\x98\x32\x01\x35\xbf\x6d\xd5\x98\xd8\xf0\xe2\xe7\x87\xbc\x58\x12\x2f\xd2\x11\x55\xa8\x89\x56\x91\x9d\xad\xc0\x85\xe7\x20\xe1\x8a\x47\x6e\x53\xf9\x5f\x61\x48\xea\xbc\x03\xe8\x54\xf9\x85\x56\x3d\xea\xbc\x73\xd2\xf5\x13\x77\x4a\x26\xd8\x11\x40\x60\xc8\x06\x2f\xb1\x60\x42\x31\x16\x7a\x87\x88\xb2\x69\xc6\xfb\x88\xd2\x34\x7f\x0b\xb1\x8c\x12\xb6\x84\x92\xfb\xa9\xfa\xe9\x15\x82\xc0\x8f\xac\x35\xd9\x9c\x30\x25\x7e\x23\x98\x3d\x2c\x62\x8c\x56\xed\xdc\x05\xca\xea\x10\xb3\xa6\xf9\x2d\xc4\x9a\x6b\xc5\x10\x64\x61\x1c\x2c\x95\xc0\x42\x4c\x1a\x6f\x89\xb4\x68\xdd\x0a\x2c\x39\x0e\x36\x96\xb0\xc4\x54\xb5\x92\x66\x0b\x63\x65\x79\x6c\x3b\x0b\x5d\x75\x70\x34\xdd\x31\x82\xcc\xb6\x5b\x48\x3c\xd8\xcc\xb6\xb0\xb6\x63\xe8\x86\xd6\x96\x87\x18\x9b\x66\x3b\x87\xdf\x99\xd4\x53\xae\x7c\xe1\x9a\x66\x1e\x19\xb6\x61\x2d\xfd\xe5\xe6\x64\xbe\xdf\xf3\xaa\xd0\xd5\x0a\x9a\x09\x62\x35\xad\x05\xe8\x09\xaa\xce\xa5\xa1\xb7\xc0\xb2\xe4\x8b\x4e\xac\x03\x85\x7b\xd1\xf7\xcb\x29\x87\x61\xe1\x94\xfc\x67\x85\x45\x70\x59\x88\xd9\xd7\xdb\xcc\x21\x7f\x8b\x55\x50\x10\xcc\x0a\x8b\x9e\x4b\xaa\x0d\x2e\xc7\x2d\x97\x14\xc4\x27\xba\x9b\x65\xa1\x68\x1a\x11\x56\x4d\x23\x86\x18\x33\xc1\x39\xe9\xfa\x02\x98\x6c\x1a\x63\x2e\x62\xb9\x9a\xa5\x03\xa5\x81\x4a\x83\x5b\xfd\xf0\xc8\x18\x90\x5f\x97\x27\x83\x62\x96\x2d\x84\xe1\x1b\x83\x2c\xaf\x06\xb3\x6c\x20\xb3\x4a\x2c\x44\x61\x70\xf2\x51\x86\x5b\xfa\xf2\x44\xaf\xae\xcf\x90\xe8\x51\xa0\x07\x12\xb3\x5e\x1e\xb2\x89\x6d\xcb\x69\x90\x75\x1a\x47\x19\x01\xcc\x26\xee\xf4\x57\x7e\x00\x6d\xd4\xaa\x76\x6f\x6c\x8f\x87\x3f\x22\xe1\xc4\xc4\x53\x8a\xdd\xfd\x37\x61\xa5\x1a\x26\x42\xa9\x6e\x9f\xd1\x6f\x05\xd4\x94\x71\xd8\x12\x9d\xd3\x0e\x2d\x8d\x12\x11\xf9\xa8\x28\xf2\x82\x4d\x0c\x7a\xfe\x4d\x2e\xce\xb4\x3b\x03\x46\xbc\x5a\x1b\xca\xc9\x4d\xe4\xc2\x00\x63\x2e\xaf\xf4\xdf\x0f\xf9\x49\x56\x19\x60\x88\x6f\x06\x18\x8b\x4a\xfd\x11\x06\x18\x69\xa5\xfe\xd0\xe3\x4a\x66\x75\x49\xbf\xf9\xdc\x00\x63\x9d\xaa\x97\x75\x21\x62\x49\xfe\xbb\x01\x46\x31\xcb\xe6\xf9\x8a\x1e\xf2\x3a\xa3\x31\x4a\x6f\x18\x60\x54\x72\x25\x68\x70\x95\xbf\x96\x0b\x59\xe9\xc7\xa3\xef\xeb\x3c\x13\x59\x25\x67\xa9\x7a\x3f\x96\xdf\xc5\x5c\x3f\xe5\xc5\x6a\x56\xe9\xc7\x62\xa6\xb6\x48\x2b\xe5\xd7\xaa\xe9\xdd\xd6\x8a\x9d\xac\x1b\x60\x6c\x36\x39\x9d\x88\xa9\x65\x30\x3e\x30\xac\xcc\x32\xfc\x81\x61\x55\x3c\xa8\x96\x45\x7e\x3d\x28\x9c\x6c\xb6\x12\xb8\x19\xac\xe9\x64\xc0\x5b\x74\xa1\xd8\x10\xf4\x63\xc7\x65\x9a\xa4\x7d\x1c\x01\x29\xc4\x30\x23\x95\x02\x4b\x7c\x4f\xfa\x65\xc6\x7f\x0a\x5f\xdb\x7a\x24\xe7\x74\x46\x47\x5d\xaa\xa3\x2e\xd5\x51\x2b\x7f\x46\x29\xa2\xcc\x96\xe0\x86\x39\xcf\x2d\xbc\x81\x1a\x33\x48\x70\x36\x49\xd1\x25\xc3\x90\x8c\x96\x13\x69\xd7\xb6\x37\xdd\xf1\xdc\xc6\xed\x75\x4e\x8a\x73\xc6\x72\xcb\xe3\xa3\x1b\x0e\x69\x88\xb3\xce\xec\x29\xd7\xb0\xe0\x4a\x72\x06\x42\x3b\x01\x5d\xe7\x0b\x4c\x83\x99\x76\x01\x5c\xe2\x41\x8c\x95\x2b\xea\x41\xbe\xa3\x56\xce\xed\x1b\xcb\xd3\x0e\xa6\xd6\xe7\x84\x76\x4a\xce\x8c\xf7\x10\xf5\xad\x39\x12\x62\x74\xc3\x3a\x72\xfd\x7b\xe8\xde\x2a\xd9\x2e\xc8\xe6\x65\x9d\xcd\x9b\x4d\x52\x8b\x8c\x14\xa3\x19\x89\x9f\xec\x74\x33\xc8\xf5\xda\x0f\xab\x88\xc5\x4d\x53\xb4\x16\xb0\x6a\x9a\x0a\x91\x89\x2d\x0b\x18\x87\x4f\x9b\xe6\xa9\xd6\x5a\xfb\x6a\x44\xa1\x2c\x20\x79\x1d\x79\xe8\x46\x75\xe8\x46\x2d\x1a\x53\xdf\xf5\x67\x93\x94\x60\xef\x78\xae\xe9\x6d\x03\xeb\x2c\x63\xd6\x34\xc3\xd9\xc6\xf4\x0f\x3a\x5a\xd1\xb9\x47\xa4\x6c\x85\x0a\xb6\x68\x08\x2e\x27\xd9\xce\xcd\x14\x48\xda\xec\xac\x69\x5c\xee\xab\x66\x25\x85\x20\x94\xcb\x80\x98\x47\xac\x87\x91\x42\x89\x1e\xa4\xb6\xcd\xfd\xad\x46\x8b\xf8\x61\x39\xb9\xb1\xf3\x29\x10\x7d\x91\x50\x5e\xb1\x0e\xe9\x9d\xe5\xa4\x9e\xf2\xdd\xd2\x77\x39\x14\x4a\x4b\x07\x5a\x4b\xba\x88\xa9\xd6\x30\x39\x7a\x50\x6b\x96\xaa\xd5\xb9\xd4\xea\x5c\xf2\x8d\x8b\x4c\x7d\x16\x96\xb4\xfe\x9d\x21\xa5\x3a\xba\x21\x96\xa4\x9d\x1d\x61\x59\x7a\x67\x78\x66\x9a\x4c\x3d\x91\x31\xd7\x6a\x97\x98\x78\x92\x2a\x28\xf4\x3b\xc4\x33\xcd\x55\x01\x91\xd4\x26\x57\xa0\x44\xef\x56\xa3\x33\xdb\x72\xae\x70\xa6\x5c\x06\xe2\x34\xad\xeb\x6e\x85\x23\xee\xab\x30\xe1\x88\x17\x6f\x14\x0e\xbd\x1a\xdb\xb2\xfd\x24\x5b\xaf\x94\xec\x7d\xc0\x99\xb3\x2e\xf2\x2a\xa7\x70\x0b\xbe\xb5\x76\xc2\xe3\xf0\x0e\xc7\x2e\x7c\xc5\x7d\xf8\x0d\xed\x03\x78\x82\x63\x0f\xde\xa0\xed\x89\x03\xf8\x81\xf4\xf7\x0b\x0e\x5d\xf8\x17\x1e\xc3\x1f\x38\xf4\xe0\x4f\xf4\xe0\x77\xf4\x5c\x17\xfe\xc2\x9f\xad\xe6\xbf\x10\xeb\x59\x31\xab\xf2\xc2\x27\xf7\x73\x51\xe4\xf5\x7a\xab\x09\xba\x26\xf9\x43\xf8\x7b\x50\x8a\x38\xcf\xe6\xb3\xe2\xe6\x4d\xdf\xe8\x42\xd2\x2a\xa1\x37\xf7\xe6\x0e\x8c\x7b\x5d\x6a\xf8\x6d\xd0\xb3\xd8\x2c\xcb\xab\xa5\x28\x30\x83\x99\xf3\xfe\xfc\xe3\xd9\xeb\xcf\x1f\xdf\xa1\xdb\xbf\xbc\x3e\xff\xf3\x0c\xbd\xfe\xf5\xd5\xd1\xc9\x29\x8e\xfb\xd7\xe3\xd3\xf3\xf3\xf7\xb8\xd7\xbf\xff\xeb\xe5\xe9\x31\xcd\xdf\xbf\xdb\xa2\x80\x3c\xbd\xdb\x76\xf4\xc7\xd1\x19\x3e\xbb\xdb\xa6\xa0\x1f\xdc\x6d\xd3\x4b\x3c\x87\x99\x73\xf4\xf1\xd5\xe9\xc9\x6b\x3c\x84\x99\xa3\x6d\x03\xf6\xa9\x17\xad\x02\x95\x3e\x24\x61\xc1\x9f\xb7\x20\x71\x56\x2c\xea\x95\xc8\x2a\xe2\x3c\x49\xee\x55\x42\xac\x66\xe4\x97\x5f\x44\x5c\x6d\xa2\xe6\x32\xda\x02\xd3\x92\xa5\x74\x96\xb3\xf2\xfc\x3a\x7b\x57\xe4\x6b\x51\x54\x37\x2c\xe3\x91\x56\x19\x4c\x60\x39\xc9\xa6\xdc\xa7\x60\x78\xe0\xde\xfa\x0f\x27\xcb\x2e\x8d\x50\x6d\xe6\xc8\x49\x45\xce\x65\x37\xab\x8f\xaf\x59\x86\xc6\xeb\xa3\x57\x27\x6f\x5f\x9e\x7e\x7e\x77\xfa\xf2\xd5\xd1\x85\xc1\xc9\x7f\x14\xe0\xc2\x11\x8c\x21\x23\xe5\xf3\x0e\xdd\x86\xa2\xc1\x49\x36\xc5\x77\xa0\xe6\x28\x02\x9d\x9c\xbd\xf9\xfc\xf6\xfc\xf5\xd1\x66\xca\xf3\x6e\xca\xd7\xad\x29\x5f\xf5\x94\xa3\xbf\xde\x9d\x9f\x1d\x9d\x7d\x38\x79\x79\xfa\xf9\xe5\x07\x9a\x43\xde\x11\x8f\xfe\xa5\x5c\x21\xb0\x8f\xc0\x6d\x67\x53\x8b\x37\xdd\xc6\xe0\x37\x02\x47\xa3\x9e\xa8\x07\x6f\xca\x7d\x5a\xd0\x3e\xda\x1e\x62\x33\xea\x65\x6e\x28\x22\x5b\xf8\x82\x73\xde\x22\x30\xf9\x0d\x9e\x4c\x5b\xbc\x5f\x9e\xbd\x39\x7a\x6c\x6d\xdb\xbb\xbb\xb8\xb7\x81\xfc\xa6\x5b\xfc\xc7\x2f\x17\x77\x1b\x11\xbd\x41\x9b\xfd\xb8\x8b\x80\xaf\x33\x66\x90\x59\xc6\x20\x9e\x65\xe4\x39\x5d\x8a\xc1\x0f\x51\xe4\x06\x88\x0d\x7a\x6f\xe0\x47\x8b\xde\xd1\xfb\xf7\xe7\xef\xd5\x11\x30\x81\x88\xc3\xa1\x68\x1a\x0f\x11\x45\xd3\x90\x36\x11\x11\x23\x45\xf0\x2f\x64\x5f\xa8\x8f\x47\xc7\x7e\xbe\xb5\xc8\x35\x01\xd5\x30\xbf\x68\x78\xaf\xde\xff\xd7\xbb\x0f\xe7\xff\x13\xbc\x3f\x70\xc8\xa8\x75\xb8\x6c\x9a\x8e\x35\x87\x1d\x6b\x2e\x39\x08\xd3\x1c\xfe\xa1\xf2\x03\xb4\x86\x11\x17\x37\xeb\x2a\x1f\xd4\xd9\xec\x6a\x26\xd3\xd9\x65\x2a\x0c\x58\xf2\xc7\x71\xf8\x43\xe3\xf0\xf6\xfc\xf5\xc7\xd3\xf3\x7b\x8c\x72\xd8\x51\xee\xcf\x2d\x46\xf9\x53\x4f\x78\x77\xfe\xe7\xe7\x77\xef\x8f\x5e\x9d\x5c\x9c\x9c\x9f\x3d\xc2\x8e\xbf\x6f\x4d\xf9\x5d\x4f\x39\x3e\x7f\xff\xb6\xe5\xa9\x07\xf2\x25\xa2\xbf\x50\x6c\x9f\x44\xeb\xc0\xb6\xe3\x36\xf8\xfe\x05\xc5\x2d\xcc\x9c\xd5\xec\x3b\x3e\x14\xaa\xef\x6c\x23\xce\x1f\x9c\xb4\xe2\x6a\xa8\xcc\xfe\xd7\xa1\x0b\x3d\x54\xfb\x7d\x0f\x34\x06\x1e\xba\xee\x81\x77\x78\x38\x7e\xba\x7f\xb0\xef\x1e\x1e\x8e\x21\xc3\xb7\xb3\x6a\xd9\x8e\x67\x7c\x57\x98\x63\xf7\xf0\xc0\x7b\xea\x3d\xa2\x26\x56\xec\xde\x58\xfe\x98\x3e\x78\xbe\xf7\xfc\xf9\x33\xf7\xf9\x2e\xf3\xdc\x83\xbd\x83\x7d\xef\xf9\x78\x7f\xf7\xce\xbc\xc6\xe5\x16\xeb\x46\xdd\xef\xd9\xe8\x8a\xad\x3c\xf3\xbd\xe4\x31\xba\x90\xe0\x64\x0a\x69\x6b\x93\xbe\x29\x6f\x4e\xb4\x01\xa9\xd8\x9c\xa0\xb7\x4f\xf1\xa8\xf0\xdf\x41\x8e\x73\x26\xc8\x61\xfb\x83\xcb\x84\x2d\x4d\x73\xe9\x2c\x44\xf5\x5e\xad\xfb\xc7\x2c\xad\x45\xa9\xcd\x7b\x85\x0f\x3a\x54\x80\xf9\x51\x66\xd5\xde\xf8\x65\x51\xcc\x6e\x58\xbe\x8b\x63\xce\x83\x3c\x2c\x03\x5e\xa3\xb7\xe7\xb9\x07\xe3\xdd\x6a\x52\x4e\x2d\x56\x4d\x4a\xcb\x9b\x86\x61\xe8\x79\x1c\xea\x10\x0f\x85\xf7\x34\x62\xc5\x3f\x00\x3a\xe6\x1c\x08\x06\x16\x24\xfa\x1a\x0e\x16\x4a\xfa\x59\xa2\x1d\xc7\x7a\xc7\x13\xde\x3e\x87\xd2\xc2\x31\x0f\x4a\xcc\x47\xe3\x3e\xb8\x54\x3b\xd2\x64\xfc\xed\xa6\xda\xde\xcd\x56\x23\x61\x7e\xd0\x23\x3e\x7e\xee\xed\x1f\xec\x1f\x1e\x3c\x3b\xf0\xdc\x67\x4f\x9f\xed\xb2\x3d\xcf\x24\x0c\xb8\xe5\xb9\x87\x87\x4f\x3d\xef\xd9\xf8\xe0\xe0\xe0\xd9\xae\xc6\xc5\xda\x1f\x1f\xee\x1f\x3e\x3b\x18\x1f\xea\x96\xf1\xd4\xf2\x9e\x1d\x1c\x1c\x8c\x3d\xfd\xbe\xd7\xee\x7e\x7f\xfa\xe2\x85\xf7\x8c\xeb\x97\xa7\xd3\x17\x2f\x9e\x73\x8b\x1e\x9f\x4d\x7b\x7a\xdc\xc5\xe9\x80\x3b\x71\xbe\xbe\x61\x15\x85\xf7\x8f\x6c\xf5\x40\x6f\xf5\x40\x6f\x55\xc9\x95\xb7\xff\x2b\xcd\xa0\xd2\x49\xa5\xf6\xdc\xda\x6d\x66\x8c\x03\x2d\x1b\xd6\xa6\xc9\x92\x49\x69\x59\x53\x6c\xc1\x07\xda\x83\x4a\x26\xb6\x5d\x4e\x41\x90\x57\x9d\x9b\xa6\x20\x6d\x8d\xef\x27\x37\xb6\x98\x42\x42\x47\xb2\x62\xf9\xa8\xe6\xbb\x35\x57\x3e\x16\x35\x05\x89\xf6\xb0\xa0\xb4\x6d\xae\x13\x56\x25\x4f\x70\x22\xfb\xac\xa4\x0e\x3f\x6c\xaf\x9d\xe2\xd2\x14\x9d\xb3\xe1\x20\x6d\xbc\xd1\x8b\x97\xca\x9b\x4c\xee\x7b\x93\xca\x55\xbc\x09\xc9\x53\xa4\xb1\x76\xd9\x3b\x68\xa9\x23\x50\x42\xea\xc4\x98\x40\x7a\x7b\xcb\x38\xbc\xda\x16\xf2\x3e\x5a\x12\x77\xc2\xcf\x3b\x82\xd3\xc5\xff\x24\x3e\x3b\x2f\x21\xc6\x6c\xf4\xb2\xd1\xe9\x03\x81\x7d\x02\x3e\x48\x6c\x3b\xe0\x39\x8a\x49\x32\xdd\x79\x09\xb5\x7a\xa0\x81\x50\x60\xbc\x9b\x5b\xf5\x6e\x0a\x12\xd3\xdd\xdc\x2a\x76\x5e\xee\xbe\xb4\xc8\xeb\x60\x72\x54\x29\xe1\x2e\x68\x20\xb7\xe2\xdd\x1a\x68\x1a\xca\x9d\xaa\x13\xeb\xd2\x34\x45\x9f\xbe\x2a\xef\x84\xcc\xd9\x83\x08\x4f\xe5\x99\x86\x58\xf0\x1c\xab\xb0\x88\x3c\xdf\xf6\x74\x18\xa6\xa9\x9b\xa3\x1b\x54\xa1\x54\xf9\x69\x52\x00\x13\x39\x1d\x62\x36\x91\x53\xfe\x93\x10\x97\xd3\x90\x5e\xf4\x34\xed\x58\xb7\x48\xe4\x9b\x45\x8b\xcd\xa2\x5d\x02\x41\x12\x58\xda\xbd\x98\x54\x53\x1b\x25\x48\xa4\xa7\x17\xd9\xa4\x22\x60\x2e\xd0\x1b\xca\xdd\xc2\x52\x03\xa8\x59\x07\x7b\x43\x32\xdb\xb4\xbf\xee\x5e\x25\x10\xdd\x99\xf3\xe0\xf6\xbe\x5e\xeb\x23\x58\xbd\xdd\x74\x93\xe4\x85\x6b\xb8\x82\x4b\x38\x87\x0b\x78\x0f\x2f\xe1\x08\x5e\xc3\x67\xf8\x0e\xc7\x28\x9d\x12\x31\x77\x4a\xb5\x25\x38\x41\xe9\xc4\x70\x8a\xb9\x13\xeb\x7b\xb4\x13\xd3\x3c\x51\x18\x9c\x9a\xe6\x29\x05\x56\x5d\x64\xa5\xd5\xa4\x74\x4a\xd3\xcc\xe9\x0f\x3b\x89\x86\xa7\x4d\x43\x83\x87\x48\x23\xfd\x53\x1e\x9d\x98\xa6\x8b\x48\x6d\x4d\x33\x3c\x8d\xdc\xdd\x63\xff\x78\xe4\xfa\xee\xc8\xd5\xbc\x7a\xd5\x6a\xdb\x63\x0e\x97\x78\xa5\x73\xed\x31\x4a\x47\xd8\xb9\x23\xe0\x18\x6b\x2b\xb6\x3c\x48\x9a\x86\x25\x78\x06\x31\x56\x4c\x3a\xa4\x72\xed\x8a\xe5\xea\x01\x8e\xf1\x78\x74\xd3\xb8\x1c\x96\xe8\x06\xa7\x93\xe5\x14\x91\x9d\x4c\x96\x53\x8a\xe7\x82\x65\x1b\x94\x53\x7b\xd8\x37\x9b\x66\x6c\xdb\xe0\x86\xc7\xfc\x52\x6b\x06\x8f\xc3\x02\x87\xee\x46\xc8\x8e\xf0\xa4\x63\xe8\xcf\x78\xda\x3d\x52\x10\x79\x6c\xe1\x18\xd6\x48\xe1\x1d\xa3\x4d\x5a\x1e\xe7\xb0\x0e\x3d\xd3\x64\xa7\x28\xd8\x29\xac\x21\xe1\x70\x82\x82\x9d\xe8\xc7\xad\xf9\x1b\xa8\x1c\x5e\xe2\x67\x38\xc7\x93\xfe\xaa\xe0\x33\x87\x0b\x3c\xef\xc2\xae\xcf\xe1\x45\x70\x3e\xb9\x20\xb5\xe2\xf2\xe0\x3b\x9e\x76\x12\x04\xdf\x7b\x3e\x77\x39\xbc\x56\x74\x86\xd3\x89\x37\x0d\x31\x19\x8d\x4d\xf3\xb5\x65\x05\xf3\x7c\xb0\x46\x97\x24\x91\x9d\xc2\x39\x7c\x86\x0b\x0e\x6e\x98\x46\xec\x3d\x9e\xd3\xf0\xcf\x43\xbc\x30\x4d\xf6\x1e\xdf\xef\x26\x16\x3b\x9f\x78\x8a\x28\x5c\xed\xea\xfd\xe8\xb5\xda\x4e\xc4\xd6\xa1\x4a\x4a\xaf\x31\xb1\x3d\x0e\xf3\xcd\xde\xae\x71\xde\x6d\x68\x83\xb1\x5a\x6d\x0e\xe7\x70\x4d\xab\x79\x88\x29\xcd\xb5\x6d\x28\xd8\x1c\xae\xc3\xcf\xd1\x77\xff\x14\xae\x21\xe1\x9c\xfb\x14\xf8\xae\x4d\x93\xa5\xb8\x46\x05\xba\xdf\xdd\x5d\xe0\xe1\xb5\x69\xce\xb7\xb7\x5b\xb0\x73\x98\xc3\x05\x21\x61\xb7\x4b\xdc\xc3\xa0\xdf\xaf\x17\x2a\x04\x2c\x4b\x4d\xba\x68\x11\xb8\x50\x08\x6c\xa1\xcd\x7d\xd2\xa4\xdd\xd0\x73\x54\xd9\xcd\xcb\xc9\x92\x08\xbf\x86\xd4\x34\x89\x60\x51\x7b\x12\x27\x93\x97\x44\x29\x9f\x9d\xe3\x84\x9e\xa7\x70\x81\x1e\x0f\xae\x97\x32\x15\x8c\xbd\xb4\xac\x17\x47\x5d\x52\xe4\x5c\x27\x4c\x8f\x49\x91\x2f\x70\xd3\x06\x97\x4a\x12\x2e\x3b\x09\xa6\xa0\x3c\x41\x3c\xd3\x7a\x62\x89\x1e\x1c\x23\x0d\x09\x8e\x95\xe2\x3e\x56\x8a\x5b\x31\xf1\x47\x76\x05\xb5\xc5\xae\x1c\x81\x4b\x2b\x56\x69\x44\xcb\x83\x12\x16\x6d\x26\x99\x3a\x62\xb8\x72\x0a\xb4\x16\x9d\x5a\xbc\x52\xba\xfc\x61\x88\x87\xa3\xbf\x99\x1d\x71\x97\x4d\xbe\x5f\xe6\x53\xce\x3e\x5d\x4f\x3e\x5d\x3b\xd3\xdd\x27\x7c\x24\x21\xa3\xde\xc9\xdf\xce\xd4\xe2\x9f\x9c\x27\x23\xa8\x70\xf4\xf7\x27\xa7\x6d\x79\x32\x82\x02\x47\x7f\xdb\x11\x3b\xc9\x12\x99\xc9\xea\xa6\x39\x9b\x9d\x51\xb3\xa4\x61\xe5\xee\x27\x8b\x29\x58\xbc\xf9\xfb\x53\x69\x35\x9f\x4a\xeb\xc9\x68\xf1\xc0\xfb\xba\xaf\xa3\xb0\x8c\x6a\xbf\xee\xaf\x8f\x24\x18\x4f\x3c\x43\x09\x6e\xa1\x2f\x45\x63\xce\x73\xa7\x44\x59\x9e\xcd\xce\x58\xac\xe3\x48\xdf\x0d\xe3\xc8\xf6\x7c\xaf\xbf\xf2\x18\x92\x16\x8a\x31\xee\x01\x09\xd8\x38\x7c\xda\x72\x75\x16\x0f\x8d\xef\x06\x22\xab\xb0\xba\x77\xad\x15\x79\xcf\x7c\xe3\x92\x3c\xef\x68\xec\x3f\x87\xc4\x34\x93\x21\xa6\x91\xf0\xb3\x5b\x4e\x6f\x2c\xc5\x04\xb6\xd7\xc8\x34\xb2\xfd\x7b\x05\x86\xeb\x50\x0b\x87\x7a\x88\xf1\x3d\x75\x19\x43\xca\x83\x2f\xfa\x8a\xd2\x50\x4e\xbc\x61\xb1\x24\x32\x06\x97\xb3\x52\x0c\x0c\x2b\xf1\x0d\x83\x93\x7f\xdf\xe6\x71\x6b\x0e\xb4\x71\xda\xef\x6d\xee\xc4\x98\xb7\x09\x17\x78\x8b\xae\x3a\xdd\x0f\xce\xec\xb2\xcc\xd3\xba\x12\xca\x07\x44\xf5\xfe\xf0\xc4\xdb\x7b\xb8\xa5\x2c\xef\xdf\x03\x30\xe1\x94\x24\x86\xe2\x16\x3e\x38\xb1\x90\xe9\x23\xd1\x40\x77\x1f\xa2\xe6\x03\xfd\x55\x49\xb4\x31\x57\x73\xf2\xd5\x7a\x56\x88\xf9\x87\x1c\x3f\x38\xf1\x6a\x8d\xdb\x34\xef\x41\xbc\x45\x0f\xa4\x02\xb0\x55\x58\xa1\xe6\xb7\xe9\x9b\x77\x2a\x6f\x8f\x1f\x9c\xf9\xfa\xb1\x9c\x44\xa1\x4a\x3b\x5a\xa3\x54\xf4\x44\xad\xd3\x54\xbb\xe9\x8c\x65\x58\x74\x77\x8b\x1e\xd9\x07\x8d\xe6\xe8\x86\xf3\xdd\x1b\xc8\x90\xc2\x23\xed\xc3\x65\x3b\x9e\x8b\xe8\x06\x99\x92\x2e\x41\x32\xda\x82\x73\x43\xa1\xa2\x4c\xb7\x25\xc7\x5c\x5e\xc9\xb9\x98\xff\x76\x83\xea\xf9\x57\x3b\xdb\x83\x57\xf7\x77\x06\xef\xe0\x2b\xdf\x02\xa1\xd2\xee\x62\x21\x8a\x0e\x96\x6a\xf8\x15\xc0\xfd\x47\x00\xba\xe0\x29\x80\xe2\x5b\x3d\x4b\x89\x4e\xe2\xdb\xaf\xa6\x3f\x05\xd2\x6a\x8f\x53\x3b\x49\xf3\xbc\xf8\xe7\x47\xbc\xa7\x26\x2d\x0a\x31\xab\x44\xf1\x61\x39\xcb\x90\xa2\xc1\x5f\x2d\xfc\xec\x91\x23\x0e\xdd\x7b\x10\xce\x8b\x23\xda\x82\x62\x97\x45\x25\x7e\x05\xeb\x80\xac\x08\xb2\xec\x91\x7d\x70\x1d\xf9\x67\x04\x58\x96\xc7\xa4\x87\xc4\xc3\x2d\x0d\x87\x9a\x63\xf4\xa8\x96\xfc\xd8\x3e\xff\x7a\xb8\x69\x6e\xb1\x4e\xa8\xdb\x3a\xbe\x1a\x6b\x58\x67\xb3\xb3\x47\xe6\xab\xa1\x65\x3b\x42\x2c\x66\x95\xbc\x12\xd8\xbe\x3c\x42\x70\x3d\xfc\x85\xab\x27\xfc\xb7\x28\xf2\xff\x09\x27\x17\x5b\xfe\x9f\xb8\x53\x9a\x91\x8a\xb2\x6c\x8f\x23\xfd\xe5\x71\x3c\x7f\xe4\x38\xf4\x82\xdd\xf4\xed\xb3\x48\x7f\x7d\x16\x87\xca\xde\xfe\xef\x87\xa1\x6e\x8e\xf0\x83\x53\xd6\x97\xf7\x40\xdd\x8d\x18\x14\x8c\x04\x4b\x47\xd5\x6a\xbd\x55\x62\x88\x5b\xbc\x9e\xa9\x5a\x9e\x61\xd2\x34\xc3\xec\xae\xfe\x54\x8e\x23\x19\xcd\xe1\xa6\xc0\x8a\x14\x98\x9d\x41\xe9\xac\xd3\xba\x64\x82\x07\xca\xaa\xa0\x3a\x41\x50\x39\xea\xd1\x0d\x2c\xb1\x74\x62\x58\xa0\x68\x55\x48\xda\x34\x43\x7d\xd1\x3a\x5c\x36\xcd\x70\xd1\x01\x5b\x46\xac\x85\x27\xb8\xaf\xd7\x5c\x44\xa5\xdf\xad\x3b\x5c\x6a\x57\x76\xab\xba\x60\x40\xcf\x0f\x67\xd1\xc0\xa8\xf4\xf7\x10\xbf\x46\xb6\xeb\xbb\xca\xd6\xa7\x58\xb1\x94\x2b\x3f\x56\xdd\x49\x2f\x7b\xbf\x2e\xc1\xd4\x8e\xb5\x1b\xc0\x6a\x74\xc3\x84\x47\x2c\x41\x3b\x81\x1c\x97\xdc\x67\x31\xa6\x90\xe3\x82\xac\x41\x21\xae\x44\x41\xb6\x0a\x32\x4c\xd4\x05\x6f\xbe\xb9\x03\xda\xea\xbe\xdd\x0a\x6a\x58\x8d\x2c\xe9\x6f\xad\xf9\x0b\x96\xf5\x77\xfb\x9c\x47\x89\x9f\x41\x82\x19\xba\x81\x0c\xb3\x20\xd3\x81\xcf\x72\x92\x4d\x87\xb8\x20\xad\xf9\xb3\x46\x7a\x7b\x41\x2f\x9b\xcb\x04\x0a\x7d\x73\x24\xaf\x78\x01\x0b\xcc\x41\x11\x40\x38\x25\xe1\xc5\xe4\x06\xbe\xad\x52\x15\x9d\xdf\xdb\xdd\x54\xeb\x9b\xe9\x49\xd1\xba\xb8\xd4\x94\xe1\x99\xed\x05\x32\x4c\xf4\xf5\xc8\x52\x5d\xb1\xbe\x58\xa8\xd0\x4b\x17\x5a\xc9\xa0\x30\xcd\x21\x75\x14\x53\x9a\x3c\xc5\x8c\x07\xb6\x4d\x4f\xb0\x9c\xc8\xa9\x85\x67\xb7\xf4\x6b\x23\xcd\x52\x77\x19\x14\x2a\xd3\x51\x04\xcb\x3e\x52\xb6\xed\xb8\xd7\xf8\xea\x94\x4e\x98\x80\x25\xc4\xdc\x57\x87\xa8\x4f\xcc\xf3\x3d\xd8\xba\xcc\x00\xa1\x14\xe1\x2a\x9f\xd7\x29\x09\xcb\x2a\x9f\x3f\xc2\xe1\xfa\xd6\x5c\xd5\x20\x6e\xcc\x9e\x77\x97\xb7\x87\xd2\x89\x9b\x66\x28\x9c\xb2\x69\x04\x89\xf6\x50\x17\x2e\x44\x1b\x06\xf7\xa9\xa9\x69\xa4\xea\x95\xdb\xbd\x92\xfb\xec\x10\xf1\xcf\x88\x15\x4a\x44\x94\xed\x86\x0a\x5f\x31\x09\x02\x5c\xd8\xe3\xaa\xa9\x80\xca\x29\x77\xb1\xe0\xfe\xa6\xeb\x4f\x0e\x52\x0b\x28\xab\x1c\x75\x51\xcb\x04\xd7\x36\x21\x23\x6d\x25\xe6\xa8\x9e\xfe\xa9\xef\xa0\xce\x5a\xfb\xbb\xda\x58\x92\xf4\x91\xfb\x31\x7f\x8c\x32\x1d\x5d\x20\xa7\x78\xb3\x95\xfa\xf1\xa3\x52\x9f\xff\x5a\xea\xf3\x87\x52\xdf\xed\xa9\x15\xfb\x1a\x55\x7c\xa8\xab\x40\x46\x37\x90\xa8\x70\x36\xed\xc5\xbe\x6e\x9a\x61\xa9\xc5\x9e\xb4\x4b\x7a\x77\x9d\xbc\x93\xf2\x44\x4b\x79\xba\x25\xe5\xf4\x4c\x6e\xa0\x1a\x48\xfd\x91\xf4\xdd\xdd\x5c\x89\x75\x8d\x15\xab\x39\x29\x36\x56\x92\x28\x27\xbd\x58\xe7\x58\xdb\x6d\xde\x2c\x0f\xdd\x88\x95\x58\x43\x81\x29\xf7\x59\x8e\x76\x0e\x05\x26\x1c\x8a\x8d\xcc\x06\xb9\x6d\x07\xc5\x46\x9c\xb7\xba\xda\x9b\xb9\xa4\x0b\x77\x32\x4c\xbb\x47\x37\xcc\xed\x4c\xd5\xdd\xa5\x40\xee\x69\x82\x05\x64\x98\xd3\xea\x6e\x90\x05\x3c\x47\x96\x4c\x6c\x3b\x9b\x62\x32\xc9\xa6\x56\x4a\x7f\x72\x3e\x3a\x6b\x5c\xa0\x86\x1d\x3c\xeb\xce\x35\x37\x4d\x96\xf4\x21\x57\xce\xc1\xb2\x4a\x0e\x24\x1f\x09\x94\x8a\x57\xfa\x3a\x00\x52\xf3\xdb\x27\xad\xcf\x59\x65\x3d\xf4\x49\x4b\x2c\x34\xd1\xfb\x0c\xaa\x18\xaa\xf4\xbd\x69\x7a\x43\xa4\x77\x57\xff\x30\x9d\x7f\xdb\x03\xa3\xcb\x39\x1b\x2a\x05\x0f\x62\xa8\x87\xb7\x59\x58\x4e\xc2\x73\xdf\xf3\xab\x50\xf6\x5e\x1f\x64\x58\xed\xde\x58\x24\x10\x72\x52\xb5\x5a\x23\xa8\x5a\x77\xaf\x52\xee\x5e\x46\xee\x9e\x4e\x63\x4a\x52\x0b\x95\x0a\xb4\xda\x3e\x0a\xb4\xfa\x5b\x4b\xd3\x2c\xc8\x05\x0a\x89\xb2\xe4\x5b\x0a\xcb\xe3\xa0\xcc\x9c\x2a\x7b\x78\x4c\xfc\x1f\x11\x15\xa6\x2b\x91\x44\xd3\xf4\xf9\xe3\xa7\x9c\x9b\xe6\x47\x56\xc1\xbf\xff\x2d\xac\xde\xd3\xba\x53\x60\xec\xc2\x73\xf0\x9e\xea\xca\xa7\xcc\xff\xca\xa1\xa2\x75\xd5\xa9\x3c\x24\xf9\x1d\x85\xa3\x6e\x75\x2e\xe0\x02\xbc\x67\x5b\xf4\xe4\x51\xd6\xca\xbc\xe1\x09\xc3\x52\xb5\x33\x2d\x2b\x67\xa4\x65\x32\xa5\x64\x4c\x93\xd9\x17\xba\x68\xe6\x82\x66\x94\xbb\xea\x1e\xc8\xf5\x3d\x52\x4a\x99\x3a\xff\xf2\x5b\x3d\x2b\xc4\xfb\x3c\xaf\x88\x01\xbe\x15\xd5\x63\xce\xfa\x03\x3b\x4f\x22\x58\x3a\x25\x45\x7a\xaa\x90\xea\x9d\xb5\x0f\x8b\x96\x5a\x86\xeb\x3c\xd5\xc1\x1e\xb1\x05\xd9\x65\x92\xcc\x64\x4b\xf4\xf4\x38\x32\xd9\xae\x0a\xeb\x69\x80\xea\x8f\xdc\x91\xeb\x27\x51\xa9\x10\x0c\x94\x7d\x55\xa9\x7f\xc2\x8b\x11\xe7\xba\x0a\x60\x8a\xe8\x8d\xdc\x88\x4e\x91\x25\x1c\x58\x57\xc6\x63\xc5\x7c\x67\x8c\xaa\x8a\x31\xd3\x35\x52\xb0\x0d\x20\xd3\x86\x9a\xc5\x96\xc7\x47\x63\x6e\x33\x37\x8c\x9b\x26\xde\x19\xd3\x30\x05\x31\x43\x4d\x4e\x9f\x91\x34\xde\x29\x75\x51\xe6\x39\xdb\xd4\x64\x6f\x2a\x2c\x85\xc1\x2d\x8f\x5b\x31\x07\xd9\x52\x20\xe3\xdc\xef\x9e\x53\xcb\x30\x48\x53\xd3\x79\x28\x43\xa9\xb2\x61\x90\x62\x6c\x2d\x61\x4f\x6d\x3f\x25\x83\x19\xe8\xfa\x57\x09\x64\x69\xf5\xd1\xd6\xda\x01\x7a\xc5\x4a\xa8\x61\x09\x9e\xba\x9c\x63\xb5\x13\xf3\x1e\x8d\x94\x6b\x37\xae\x60\xd2\x89\xf9\x76\xbb\xd2\x89\xd2\x11\x2f\x62\xd3\xb4\xed\x74\x0b\xf9\xd4\xde\x83\x94\x78\xdf\x38\x3c\x3c\x3c\x34\x14\x8f\xb2\xbc\x69\x8c\xfd\xf6\x95\xf3\x9f\x6c\x68\x65\x4d\x33\xb4\xb2\xbe\x10\xd9\x34\x8d\xa7\x06\x62\xd6\x55\x06\xba\xc4\xf4\xec\x23\x93\x20\x1d\x61\xbd\xb3\xc6\x40\x31\x27\x0e\x65\x8b\xbc\xe4\x8e\xf8\xc6\xca\xed\x6a\x85\x61\xae\x66\xd4\x50\xb7\x33\x5c\x0e\x75\xb7\xd7\x6e\x38\xff\x29\xb1\x6e\xe7\x2c\x2d\xdc\x87\x94\xfe\xe4\xe8\xdd\xf6\x81\x4d\xb7\xa4\x07\x5f\x5b\x33\xae\x60\x90\x15\xaf\xd3\xff\xc9\x4f\x6d\xeb\x80\xba\x04\xea\x4a\xa7\x50\x35\x57\x9f\xe3\xa5\x13\xc3\x05\x92\x1d\x3b\xb8\x63\xc7\x78\x97\x39\x3d\x37\xcd\x0b\x9d\x41\x32\xcd\x8b\xad\xcc\xe9\xf0\x92\x0c\xa7\xf6\x00\xce\x4d\x73\xa8\x47\x0c\x2f\x9a\xe6\x82\x7e\xf4\xdb\x79\x5f\x5f\x21\xda\xf8\x5f\x79\x27\xbb\x78\xe9\x94\x40\x90\x23\x5d\x6b\xe1\xea\xfa\x15\x97\xfb\xdb\xf5\x18\x1c\x44\x5b\x92\x56\xb1\x4b\x15\xc9\x58\x15\x13\x3a\x61\xda\x43\x49\x37\xb9\xb3\x05\x5e\xf4\x8f\x8a\xc7\x56\x78\x0e\xe7\x78\x01\x17\xb8\x82\x5c\x99\x15\xe5\xe4\x91\x49\x49\xad\x05\xac\x70\x32\x55\xb6\x6a\xb5\x55\x7e\x94\x17\xec\x1a\xcf\xe0\x0a\x5f\x92\xab\x1a\xd8\x76\x1e\xa2\x1b\x6c\x8a\xe4\xd7\x78\x31\xc9\xa7\x3b\x57\x30\x57\x0f\xa3\xab\xc6\x85\x12\x53\xa8\x31\xb7\xca\xa0\x0e\xf3\x80\xc7\x78\xae\xee\x4d\x76\xae\x60\x89\xe7\x93\x52\x0f\x4a\x70\xbe\x1b\x5b\xcb\xdd\x35\xc4\xb8\xde\x8d\xad\x64\xe7\x6a\xf7\xca\x5a\x4d\xea\xa9\x55\x40\x81\x2c\x1e\x5d\xab\x1b\x82\x84\x46\x73\x6b\xbe\xbb\x84\xd5\xa4\xb6\xed\x29\xc6\x3b\xd7\x01\x8d\xc3\xa2\x63\x87\x22\xb2\x2c\xe9\xaf\x7a\x67\x90\x6c\xdb\x0a\xa4\x66\x8b\xb6\x6c\xed\x1f\xaa\xf6\xc1\xbd\xcb\x41\x8f\x94\xfb\xf3\xed\x52\x39\x7d\x51\xa8\x5c\xa4\x0c\x1f\x2a\xf8\xe7\xbd\x82\x07\x11\x91\x41\xa0\xe5\xfc\x4a\xa3\xb2\xa5\x4b\x1e\x0f\xcb\x3e\xb7\xa1\xd8\x83\xfb\xc9\x43\x1e\x91\x65\xf1\xda\x85\xa9\x41\x83\x54\x95\x77\xff\x37\x60\x63\x57\x03\xeb\xcc\x54\x07\x73\xec\x76\x30\x55\x0d\xdf\xa3\x14\xfb\x25\x4c\xef\x17\x30\x3d\xa5\xc3\x75\x9c\xbb\xe5\x36\x3a\xe5\x3a\x95\x95\x2e\x4d\xcf\xd1\xfa\xcb\xe9\x0b\x79\xa0\xa6\xd7\x87\xb5\x3c\x50\x62\x37\xaa\xab\xe2\x21\x4f\x90\x84\x25\x45\x39\x51\x25\xda\x5d\xfc\x0d\x33\x8c\xa3\xa4\xd7\x5b\x7e\x02\xcb\x4d\xf9\x53\x1b\xe6\x14\x98\x93\x27\x07\x35\x16\xb0\xb4\xb1\xe0\x90\x87\xae\x69\x2e\x43\xb7\xe3\xee\xe5\x4e\xde\x34\x39\x24\x38\x6b\xbf\x89\x60\x2e\x14\x3c\x58\x86\x45\x50\x58\x98\xf3\xc4\xc2\xd2\xea\xfb\x0a\xc8\x79\x50\x87\xaa\x7c\xbe\xed\x50\xcb\x17\x9c\x43\xac\x6a\xea\x0d\xdb\xb0\x12\x7e\x5b\x61\x1a\x25\xd6\x5f\xce\xfd\x12\x27\x8b\x82\x44\xeb\x2f\xe7\x41\x59\x12\x8f\xd2\x4d\x66\x72\xeb\x4b\xa1\x4f\x9f\xe6\x3f\x0d\xab\xb6\x8c\xdb\x4f\x9f\x7e\x33\xc0\x58\x18\x1c\x8c\x27\xa6\xf1\x00\x46\xb7\x02\xf7\x53\xee\x27\x9b\xc2\x5c\x7d\xd8\xed\xd0\x47\xdd\xbe\x7b\x4a\x13\xbf\xc0\x42\xab\xca\x35\x2e\x9c\x18\xe6\xfd\xbd\x3a\xac\xb0\xda\xbc\x5c\x63\x72\xe7\xc6\xbd\x67\x17\xf6\x05\x87\x1e\x94\xd8\x97\x62\x7f\xc1\x25\xb0\x21\xa3\x48\x5e\xe5\x70\x18\xe7\x4d\x53\x3a\x69\xc5\xbe\x29\xe3\xa2\xcb\x23\xc6\x60\xac\x66\xdf\x07\x73\x91\xe5\x2b\x99\xd1\x56\x06\x86\xc5\x96\x91\x71\xaf\x06\xf8\xb1\x12\x60\x81\xc3\xa5\x69\xaa\x84\xcb\x47\x56\x82\x76\xcc\x3c\xee\x2c\x2a\xc1\xbe\xf1\xa8\xf4\x3b\x37\x74\xdd\xc7\xfe\xdb\x65\xe8\xda\x5c\x17\x6c\x4d\x7c\x3a\x77\x04\xf6\x89\xa3\x85\x23\x6c\x0f\xe6\xca\xaa\xe3\xfb\x09\xab\x31\xdf\xb9\xe1\x2f\xdc\xe8\xc6\xaa\xfd\x7a\x4a\x0b\x0b\xda\x4b\xbc\x5a\xb3\x39\x0f\xdd\x88\x82\x85\xb9\xbf\xf2\x4b\xa8\xf1\x07\xfc\x20\x6f\xa3\x27\x45\xcc\x21\xd1\x90\xdc\x20\x45\x32\xf7\x73\x95\x1d\x54\xb2\xa2\x5c\x80\xb4\xb5\x92\xd7\x9c\x83\x37\xa4\x10\x68\xb5\xa6\x08\x89\x57\x78\x0d\xd7\x28\x61\x85\xc9\xdd\x91\x12\x57\x9c\x22\x17\x09\x73\x2c\xdb\x90\x6a\xd3\x37\xe7\x14\xdc\xc8\x4e\xef\x49\x7c\xc5\x44\x17\x4b\x72\xb8\xd6\xab\x27\x1d\xcc\xce\xa4\x13\xc4\xaa\x43\x49\x6e\xa1\x94\x38\x25\xae\x9c\x12\x17\x4e\x09\xf9\x2e\x8e\x21\xc3\x57\x8c\xac\x6b\x0e\x5f\x79\x0b\x77\xc1\x9d\xd9\x65\xc9\xb8\x42\xfd\x15\x4b\xa0\x7a\xac\x97\xbf\xf0\xa2\xc9\x6a\xeb\x0c\xe0\x7a\xeb\x65\xea\x4f\x92\xed\xbe\x6a\xbb\x0f\x7e\x60\xad\xdd\xf9\x2a\xd7\x35\xc2\x0f\x23\xdf\x2d\xc7\xda\x12\x4d\x43\x06\x38\x72\x77\x85\xa3\xf3\x41\x7a\xee\xbb\xfc\x5a\xa5\x15\xd7\xf9\xf5\x2f\xa2\xa1\x55\x57\x4d\x65\x09\xde\xa5\x07\xc8\x41\xe8\x5d\xf5\xf1\x1e\x18\xa2\x55\xf7\xaa\xfe\x67\xd8\x65\x35\x99\xe0\x4d\x53\x84\x17\x14\x03\x8d\xd0\xe5\x4d\xb3\x9e\x15\xa5\x38\x4e\xf3\x59\xc5\x04\x57\x72\x32\x64\x02\x09\x9d\x7b\x37\x0d\xca\x8f\x5d\xe7\xd7\xcc\x92\x20\x78\x97\x61\xf9\x3d\x9a\xb3\xdf\x47\x37\xd6\x98\xfb\x2e\x6c\xa4\xb0\xad\x48\x2d\x76\xc6\xea\x57\x5d\x8b\xb4\x6e\x19\x0c\x2b\x27\x6e\x2b\x45\x33\xd3\xac\xfa\x6c\xa8\x0a\x8c\x36\xaf\x98\x71\x5d\x1e\xbc\x62\xc5\x68\xcc\xa1\x2b\x5a\x0e\x24\x6e\x7c\x3c\xc8\x4c\x53\xa5\x35\xe4\x5d\x30\xf2\x0e\x98\x3b\xd9\xf8\x0a\xbf\x39\x73\x79\xc5\x2a\xce\x21\x53\x56\xf2\x77\xf8\xda\x5b\xc9\xbe\x48\xfc\x9f\x9b\x35\x55\x15\xb7\xff\x2b\x33\x0d\xe3\xfd\xf6\x60\x35\xa7\x3c\x76\xa6\x5d\x7c\x5b\x11\xff\x62\xe5\x88\x60\x2b\x28\x45\xc4\x3c\x92\x14\x6c\x18\xdd\x1d\x99\x01\x6e\x28\x55\x14\x49\x6a\x9d\xbc\xfd\x0c\x8d\xb3\xd9\x99\xe1\x2b\x57\x9c\xe8\xdb\xfb\x07\x2d\x92\xea\x0b\xd3\xf1\xd3\xee\x13\xd3\xe8\x35\x4b\x59\x06\x39\x07\xb7\x11\xe0\xb9\x20\xb9\xff\x5b\x88\x64\x73\x42\x7c\x12\x25\xaa\xcf\xef\x86\xd0\x62\x55\x17\xd1\xf5\x8b\xb6\xcc\x5e\xd4\x59\xdc\x66\x7b\xd4\xf3\x3f\xbf\x0b\xd0\xf7\x0f\x57\xb3\xb4\x16\xe7\x09\x4d\xcf\x7f\xbf\x38\x7f\x24\x13\xae\x53\xdb\x1b\x51\xbb\xdd\xd0\xbf\xab\x3a\x25\x75\x3e\xdb\xd4\x4b\x54\x9b\x58\xd6\x6d\x7a\x6a\x8a\xd0\x6d\x1a\x81\x88\x59\x94\xf9\x99\xed\xdd\xa9\xaf\xd8\x54\x56\x68\x21\xf3\x40\x6e\x8a\x50\x72\xf5\x9d\x8a\x65\x18\x81\x0c\x8b\xd6\x03\xcd\x50\xa8\x6c\xa3\x65\x18\x50\xe1\x8d\xdd\x7f\xcb\x51\xd9\x76\x90\x51\xf4\x67\x65\x3c\xc8\x2d\xcc\x6e\xdb\x42\x90\x3b\x5f\x25\xe6\x77\xbf\x4a\x94\x3c\xe8\xdd\xc0\x7c\xf3\xbd\x9f\xe5\x35\x8d\xc7\x37\x88\xca\xfb\xb9\x41\xe1\xc4\x90\x53\x54\xa4\xbe\x29\x2a\x49\xa7\x3b\xa5\xaa\x9f\xa1\x18\x2f\x73\xc4\x56\x96\xea\x61\xa6\xc3\x34\x87\xca\x89\x29\x30\x37\xcd\x61\xae\x8a\xba\x9a\xa6\xbf\x0d\xab\xa2\x22\x72\x7d\xbb\xf4\x6b\xe5\xb8\x0c\xb1\x87\x51\x6b\x00\x6e\x58\x43\x81\x09\x62\x0a\x43\xd9\x34\xc3\x9c\xf7\x5e\xb1\xeb\x0f\xe5\xdf\x95\x2e\x6b\xb9\x73\xc5\x96\x84\x69\xd7\xae\x8b\x8b\x58\xd2\xa7\x5c\xf8\x0b\x96\xf6\x74\xe2\x51\xe2\x93\x33\xef\x06\x65\x58\x07\xb5\xce\x22\xcb\x49\x3d\x1d\x62\x3e\xa9\xfb\x60\x9e\x5a\x42\x6a\xe8\xa0\xf6\x9f\x49\x63\x1a\xb9\xfe\x66\xb9\x0d\x15\xf3\xbb\xb7\xb7\x4c\xe8\x8f\x7f\x42\x72\xa6\xab\x10\xb7\xaa\x7d\x6a\x62\x8c\xf6\xa3\xbf\x89\x2e\x8e\x1c\xa8\x52\xb8\xa9\x81\x78\xae\xde\x37\xe5\xe7\x3d\x8b\xea\xef\x91\xc4\xd6\xb9\x95\x0f\xbe\xff\x21\xf7\x46\x45\x5b\xb5\x2a\x94\xef\xbf\x77\xa2\xbd\xb6\xdf\x80\x6e\x38\x46\xda\x76\x90\x4f\xe4\x74\x17\xb3\xb6\x1e\x6c\x52\xa0\x3b\xb5\xf0\xbc\x4f\x03\x88\x2e\x30\x26\x42\xf1\xa0\x78\xd1\x4f\x2e\x2c\x8b\xe7\x93\x62\x1a\x56\xea\x6b\x5d\xad\x53\xf2\x49\x61\x79\x24\xce\xfa\x01\x5d\x0e\xfa\xc9\xa2\xae\xe9\xa8\x6a\x5c\x6a\x98\xee\x60\xd5\xeb\xcf\xed\xbb\x80\x7e\x67\xc9\xb6\x7e\x64\x9b\xaa\xa2\x48\x6c\x22\x75\xcb\x70\x0c\x4b\x6c\x5c\x62\xc1\x2d\xe6\x86\x59\x64\x90\xdf\x24\x2c\x83\x5b\xd9\x06\x60\x7a\x87\xc5\x75\xd9\x5a\xd6\xb9\xc5\x86\xeb\x18\x81\x65\x65\xe4\x04\xab\x6f\xd0\x04\x16\x96\xe8\x0b\x0c\xab\x8d\xc8\x5a\x56\x16\x56\x9b\x69\x06\x64\x36\x56\x81\x6d\x6f\x4d\xb5\xb0\xd0\x33\x2b\x65\x33\x36\x75\x65\xfa\x93\xf7\x2d\x9c\x33\xbe\x89\xd1\x36\x98\xc6\x1b\xe6\x18\x08\xbc\x63\x48\x81\x2c\xf4\x9c\x09\xee\xaf\x88\x0f\x68\x33\x33\x1d\xf7\xeb\x6a\x87\x4f\x73\x8b\x7d\x72\x3e\xcd\x77\x79\xd4\xd0\xaf\xc5\x99\x98\x58\xf6\x34\xa2\xc7\xe8\xc9\x88\xdc\x26\x65\x70\x63\x21\x53\x58\xe9\x67\x75\xd5\x0a\xd7\xd8\x56\xeb\x0e\x2e\xf3\x3c\x15\xb3\x6c\x90\x17\x83\x4b\x99\xcd\x8a\x9b\xc1\x9c\xc2\x4d\x03\xae\x50\x7f\x49\x25\xb3\xc5\x60\x95\xcf\x85\x01\x97\xdd\x87\xe9\x03\x62\xd4\xc1\x72\x56\x0e\x56\x79\x21\x06\xd5\x72\x96\x0d\xbc\xa7\x83\x52\x2e\x32\x99\xc8\x78\x96\x55\x1a\x48\x69\xc0\x39\x1a\xae\x37\xde\xdb\x7f\xfa\xec\xe0\xf9\xe1\xec\x32\x9e\x8b\x64\xb1\x94\x5f\xbe\xa6\xab\x2c\x5f\x7f\x2b\xca\xaa\xbe\xba\xfe\x7e\xf3\xe3\xe5\x6f\xaf\x5e\x1f\x1d\xbf\xf9\xd7\xc9\xef\xff\xdf\xe9\xdb\xb3\xf3\x77\xff\xff\xfb\x8b\x0f\x1f\xff\xf8\xf3\xaf\xff\xfa\xef\x27\x9f\x0d\x38\x43\x4f\x78\xfb\x70\x83\xde\x3e\x5c\xdc\x2f\xec\xf5\xe0\x3d\x4e\x3c\x32\x3f\x9e\xeb\x82\x27\xf6\xc0\x13\xfb\xe0\x89\xa7\xe0\x89\x67\xe0\x89\x03\xf0\xc4\x73\xf0\xc4\x21\x78\x82\x06\x09\xcf\xa3\x3f\x63\xfa\xb3\x37\x85\x97\xea\x43\x8e\x23\xf4\xc4\xa1\xfa\xa2\x4a\x55\x51\x1a\xdd\xf1\x6c\x8a\x9d\xe7\x22\x91\x99\x30\x4d\xfd\xeb\xcc\x56\x73\xae\x1f\xd9\x43\x53\x33\xbb\xdd\x7c\xb7\x69\xd4\x99\x1e\x37\xdf\x54\x7f\xab\x0b\x1b\x61\x9a\xfa\xd7\x21\x2f\xab\xa8\xf4\x05\xc0\xdd\x26\x9c\xc1\x70\xc9\xab\xe2\xe6\xe7\x12\x0b\xf1\xad\x96\x85\x60\x6d\x3d\xa8\xc1\x6f\xe3\x59\x15\x2f\xd9\x6b\xfe\xf3\x56\x73\xa0\x70\xfa\x2f\xcb\x70\x76\xdb\x66\x05\xfe\x63\x34\xfa\xcf\x41\x99\xd7\x45\x2c\xde\xce\xd6\x6b\x99\x2d\x3e\xbe\x3f\xc5\x79\x1e\xdf\xf9\xf7\x1a\xce\x6a\xb6\xfe\x8f\xff\x17\x00\x00\xff\xff\x2f\x88\x72\xca\xa2\x43\x00\x00") + +func bignumberJsBytes() ([]byte, error) { + return bindataRead( + _bignumberJs, + "bignumber.js", + ) +} + +func bignumberJs() (*asset, error) { + bytes, err := bignumberJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "bignumber.js", size: 17314, mode: os.FileMode(420), modTime: time.Unix(1484232218, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xf9\x7a\xdb\x38\xb2\x38\xfa\xbf\x9f\x02\xd6\x3d\x37\x92\x62\x46\xf2\xd6\xe9\x34\xdd\xee\x8c\xb3\x74\xc7\x73\x92\x38\x5f\x12\x4f\xcf\x1c\x8f\x4f\x3e\x4a\x84\x24\x74\x28\x52\x3f\x92\xf2\xd2\xb1\xdf\xe5\x3e\xcb\x7d\xb2\xdf\x87\xc2\xbe\x70\x91\xed\xf4\x36\xf6\x1f\x89\x08\x14\xb6\x42\xa1\x50\x28\x14\xaa\x72\xfc\x7f\x96\x24\xc7\xfb\xbd\xc9\x32\x1d\x97\x24\x4b\x11\xee\x95\x41\x1a\xe4\xfd\x2f\x32\xa5\xe8\x65\xc1\xb2\xff\x85\x4c\x7a\xeb\xe9\x49\x76\xca\x7e\x95\xf0\xeb\x2c\xca\x51\xb4\x5f\x5e\x2e\x70\x36\x41\xa2\xae\xfd\x8e\x28\xda\x79\xf0\x80\x27\xee\xd1\x32\xcb\x07\x0f\xa2\x7e\x8e\xcb\x65\x9e\xa2\xa8\x97\x05\xeb\x9b\x7d\x9a\x4e\x44\x1a\xe1\x69\xb4\xd6\xc9\x7e\x8a\xcf\xd1\xcb\x3c\xcf\xf2\x5e\xe7\x79\x94\xa6\x59\x89\x26\x24\x8d\xd1\x3c\x8b\x97\x09\x46\xdd\xce\x46\xb6\xd1\xe9\x76\xfa\x7b\xe5\x2c\xcf\xce\xd1\x64\x30\xce\x62\xbc\xdf\x79\x73\xf4\xe2\xf8\xf5\xcb\x4f\x6f\x8f\x3e\x7e\xfa\xf1\xe8\xf8\xed\x8b\x4e\x30\xb9\xa6\xf5\x25\xfb\xb4\xef\xfb\x5f\xf0\xc5\x22\xcb\xcb\x22\xfc\x72\x7d\xbd\x47\xc7\x70\xb2\x79\x3a\x18\x47\x49\xd2\x4b\x06\x3c\x2b\x10\xbd\xef\x61\x36\xc0\x74\x1f\x00\xb7\x4e\x4f\xf0\xe9\x1e\xef\x6a\xd1\x4b\x9f\xa6\x21\xee\x5f\x07\x49\xa0\x4a\xe2\x80\xe1\xee\x9a\x43\xd1\x26\x45\x26\xf4\x82\xb4\xc2\xd5\x24\xcb\x7b\x14\x3a\xdb\xdf\xdc\xcb\xbe\xcf\x07\x09\x4e\xa7\xe5\x6c\x2f\xdb\xd8\xe8\x17\xbd\x9c\x22\x5e\x76\xe3\xba\xdf\xfb\xb2\x15\x9e\xc8\x2e\xf3\x2a\x02\x86\xa5\x80\xb7\xdd\xff\xb2\xc6\x12\x44\x67\xf6\x4f\xd6\x10\xfa\xb2\x86\x10\x42\x9d\x71\x96\x16\x65\x94\x96\x9d\x10\x95\xf9\x12\x07\x2c\x95\xa4\x8b\x65\x59\x74\x42\x74\x02\xdf\x02\x1a\xf2\xd2\x68\x8e\x3b\x21\xea\x7c\xca\xce\x53\x9c\x77\x02\x95\x43\x47\x47\x73\xa2\x38\xce\x71\x51\x74\x78\xce\x35\xfc\x7f\xca\xab\x16\xc5\xe1\x7f\x9e\x96\x2d\xcb\xe6\xf6\xb2\x4f\x5a\x11\xa3\xbd\xd1\x65\x89\x8b\x9d\x6d\x7f\x7b\x02\x48\x62\x7a\x0d\xa1\xeb\xe0\x4e\x10\x70\xa3\xfe\xc8\xe1\x68\xd8\x6b\x87\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4b\x4b\x9c\x96\xb7\x1e\xfc\x9f\x72\xde\xe9\x8c\xfd\x61\xa6\x7d\x12\x25\xc5\x6f\x37\xf4\x1c\x17\x38\x3f\xf3\xad\xfa\x3f\xfa\xa4\x15\xcb\xd1\x7b\x3c\x25\x45\x99\x47\xff\x01\x93\x17\xd4\xd5\x81\xcf\x8f\x6e\xc5\xf7\xcb\x3c\x4a\x8b\x89\x97\xf5\xfd\x59\x70\x90\x5b\xa4\xb0\x3a\x12\x0a\x5c\x7e\xa8\x27\xa9\x3b\xc3\x85\xdd\xf4\x6f\xd2\xe8\x57\x9e\x80\xa8\x0d\xe2\xeb\x2a\x58\xe4\x64\x1e\xe5\x97\xde\x7e\x64\x59\xd2\x38\x79\x07\xbc\xad\x3f\x2f\x0a\xcd\x3d\xb8\xb6\x9a\x2a\x24\x3c\xaf\xdc\xc6\xff\x48\x48\xf0\xf6\x3e\x26\x45\x76\x9e\xde\xa2\xe7\x51\x9a\xa5\x97\xf3\x6c\x59\xac\xd0\x75\x92\xc6\xf8\x02\xc7\xc6\xde\x75\x67\x13\xab\x2a\xd7\xba\x63\xd6\x7e\x4e\xd2\xdb\x30\xee\x83\x25\x60\xe2\x65\x1a\xe3\xb8\x63\xa1\x09\x9f\x51\x42\xf8\x0b\xe0\x68\x44\xe2\xb8\x1d\x8e\x6e\x56\xff\x59\x94\x2c\xbd\xdd\x5f\x92\xb4\xdc\xfe\xe6\x71\xfd\x14\xbc\xc5\xe7\xcf\xc8\xef\x88\xfc\x5b\xad\xb9\xe7\xb3\x28\x9d\xfe\x9e\xa4\x73\x27\x94\x53\x51\xb7\x26\xd5\xd7\x52\x8d\x17\x33\xef\xd8\x6e\xd4\x88\xa0\xb5\xd3\xb5\xb5\xeb\xe0\xcb\xf5\x69\xb0\xfd\xbb\x1d\xfa\xff\x42\x67\xde\xdf\x49\x76\x9c\x2c\xd3\xf8\xc6\xa4\x72\xeb\x8d\xeb\xfe\xd8\xfb\xe7\x3e\xf6\xde\x1f\xfa\xfe\xc8\x67\x0e\xef\xe0\xf9\x79\xe1\x8f\x26\x6d\x7e\xdd\xcd\x5c\xed\x55\x3b\x77\xb6\x57\xad\x3a\xef\x93\x3c\x9b\xdf\x72\xda\xcb\xec\x96\x47\xcd\xdb\x09\x7c\xbf\xef\xba\xf9\x23\xe0\x8f\xa4\x31\xc9\xf1\xb8\x3c\xf4\xee\x99\x2b\xf4\xe4\x76\x13\x41\xc6\xd1\xe2\xe3\xef\x3a\x19\x7e\x4c\xb6\x3b\xed\xe2\x45\x56\x90\xba\x83\xfa\x22\xba\x8c\x46\x09\x36\x85\x82\xdf\x85\x2b\x55\xd1\xdc\x9d\x1c\xbf\x6e\x47\x03\x07\x62\xbc\x2f\x4c\x7c\xfe\xf6\x27\x99\x3b\x41\x52\x45\xdd\xed\xe8\xec\x77\x40\xff\x1f\x16\xeb\x77\x71\x7e\xbc\x31\x9f\xfc\xda\x58\xb7\x99\xde\x3d\xda\x5b\xa2\xfd\xd6\x1b\xd7\xd7\x9e\xd9\x43\xcf\x96\x56\x27\xc7\xed\xb6\x91\xe3\xc0\x78\x03\xed\x0b\x0b\x87\x5e\x77\x30\x9c\x64\xf9\x3c\x2a\x4b\x9c\x17\xdd\xfe\x1e\x00\x7c\xc8\x12\x12\x93\xf2\xf2\xe3\xe5\x02\x9b\xb0\xb4\x7d\x0a\xb5\x36\x7c\xf8\x70\x0d\x3d\x34\x20\xb9\xce\x1d\x91\x02\x45\x68\x91\x67\x19\x05\x46\xe5\x2c\x2a\x51\x8e\x17\xf4\x90\x95\x96\x05\xe2\x73\x87\x68\x26\xad\xe1\xb0\x44\xf3\xa8\x1c\xcf\x70\x11\xd2\x4f\x9e\xad\xfd\x3c\x39\xd5\x3f\x76\x8d\xaf\x53\x33\x73\xc7\xfa\x3e\x3d\x79\x7c\x7a\x72\x1a\xa0\xc1\x60\xb0\x86\x1e\x0e\x9d\xb1\x89\x1e\xef\x23\x69\x4d\xd3\xeb\xf3\x29\x2e\x67\xa4\x18\x7c\x82\x85\xf1\xa3\x40\x10\x05\x1c\x30\x74\x1d\xd2\x8c\xc3\xb4\xdc\xd3\x80\xd9\xbe\xed\x83\x3e\x82\x1c\xde\xdc\xde\xda\xf5\xde\xda\x9a\xa7\x1f\x83\x45\x9e\x95\x0c\x6b\xfb\x28\xc5\xe7\x46\x5f\x7b\x5f\xae\xfb\x7b\xf5\xa5\x06\x20\xbd\xe4\xcb\x71\x99\xd1\xc6\x3d\xb0\x4d\xed\x0e\x48\xc1\xe7\x5c\x21\x84\x92\xa3\x40\x0a\xb7\x6b\x59\x5f\xa7\x89\x03\x98\xb7\xde\x90\x63\xbb\xf7\xef\x93\xde\xc9\xe6\xa3\xef\x4e\x1f\xf6\xff\x7d\xda\x7f\x3a\xec\xb3\x71\x9a\x07\x87\xca\x6e\x5d\x07\x5f\x3a\x3a\x29\x76\xc2\xef\x82\x0e\xa3\xb7\x4e\xb8\xb5\x7b\x7d\x1a\x7c\xf3\x3b\x93\xf7\xb3\x2c\x4b\x1a\x68\x7b\x44\x41\x2a\x08\x9b\xe6\x89\xff\x19\x95\xc2\xaf\x5d\xf5\xf3\x54\x4b\xde\xd1\x3f\x9a\xc8\x18\x7a\x76\x53\x1a\xa6\x85\x57\x21\x62\x06\x6f\x53\x30\x4d\x5d\x91\x7c\xcd\x22\x35\xb4\xcb\x5a\xac\x2b\x7b\x13\xaa\xfd\x5f\x8a\x5a\x93\x66\x1f\xfe\x57\x2b\xa2\xe5\xfd\x69\xa6\xd8\xc7\xbf\x37\xc5\xd2\x3d\x4c\x92\x6c\xe9\xa7\xd9\x72\x86\x11\x6c\x76\x40\xb8\x03\x1f\xe5\xd2\x5c\xf9\x83\xd3\x25\xfc\xdc\xd5\x7e\x9f\xea\x19\x3b\xc6\x97\x49\xbf\x88\x6f\xad\xf2\xe7\x13\xa3\x1e\x5e\xd4\x43\xe5\xd0\xc9\x1b\x93\x39\x2d\xbd\x12\x9d\xb3\x02\x0e\xa1\xd3\xe4\x55\x29\xdd\x2c\x53\x47\xea\xac\xd1\xda\xd2\x37\x23\x76\x5a\x09\x23\xf5\x2f\x5b\xc1\x75\xff\x66\x84\xcf\x7b\xd7\x4c\xf9\xdf\xb6\xa1\xfc\xe1\x43\xe8\xf0\xc7\x19\x29\xd0\x84\x24\x98\x52\xea\x22\xca\x4b\x94\x4d\xd0\x39\x1e\xed\x0c\x7e\x29\x06\x6b\x00\xc2\xbf\x28\xc0\x24\xc7\x18\x15\xd9\xa4\x3c\x8f\x72\x1c\xa2\xcb\x6c\x89\xc6\x51\x8a\x72\x1c\x93\xa2\xcc\xc9\x68\x59\x62\x44\x4a\x14\xa5\xf1\x30\xcb\xd1\x3c\x8b\xc9\xe4\x12\xea\x20\x25\x5a\xa6\x31\xce\x81\xe0\x4b\x9c\xcf\x0b\xda\x0e\xfd\xf8\xe9\xed\x31\x7a\x8d\x8b\x02\xe7\xe8\x27\x9c\xe2\x3c\x4a\xd0\xbb\xe5\x28\x21\x63\xf4\x9a\x8c\x71\x5a\x60\x14\x15\x68\x41\x53\x8a\x19\x8e\xd1\xe8\x92\x53\x11\x46\x3f\xd2\xce\x7c\xe0\x9d\x41\x3f\x66\xcb\x34\x8e\xe8\x98\x03\x84\x49\x39\xc3\x39\x3a\xc3\x79\x41\x67\x68\x47\xb4\xc5\x6b\x0c\x50\x96\x43\x2d\xbd\xa8\xa4\x63\xc8\x51\xb6\xa0\x05\xfb\x28\x4a\x2f\x51\x12\x95\xaa\xac\x8b\x02\x35\xd2\x18\x91\x14\xaa\x9d\x65\x62\x65\x93\x12\x9d\x93\x24\x41\x23\x8c\x96\x05\x9e\x2c\x13\x26\x38\x8e\x96\x25\xfa\xf9\xf0\xe3\xab\xa3\xe3\x8f\xe8\xe0\xed\xbf\xd0\xcf\x07\xef\xdf\x1f\xbc\xfd\xf8\xaf\x3d\x74\x4e\xca\x59\xb6\x2c\x11\x95\x28\xa1\x2e\x32\x5f\x24\x04\xc7\xe8\x3c\xca\xf3\x28\x2d\x2f\x51\x36\x81\x2a\xde\xbc\x7c\xff\xfc\xd5\xc1\xdb\x8f\x07\xcf\x0e\x5f\x1f\x7e\xfc\x17\xca\x72\xf4\xe3\xe1\xc7\xb7\x2f\x3f\x7c\x40\x3f\x1e\xbd\x47\x07\xe8\xdd\xc1\xfb\x8f\x87\xcf\x8f\x5f\x1f\xbc\x47\xef\x8e\xdf\xbf\x3b\xfa\xf0\x72\x80\xd0\x07\x4c\x3b\x86\xa1\x86\x66\x44\x4f\x60\xce\x72\x8c\x62\x5c\x46\x24\x11\xf3\xff\xaf\x6c\x89\x8a\x59\xb6\x4c\x62\x34\x8b\xce\x30\xca\xf1\x18\x93\x33\x1c\xa3\x08\x8d\xb3\xc5\x65\xeb\x89\x84\xca\xa2\x24\x4b\xa7\x30\x6c\x49\x65\x08\x1d\x4e\x50\x9a\x95\x01\x2a\x30\x46\xdf\xcf\xca\x72\x11\x0e\x87\xe7\xe7\xe7\x83\x69\xba\x1c\x64\xf9\x74\x98\xb0\x0a\x8a\xe1\x0f\x83\xb5\x87\x43\xc1\x6c\xff\x06\x64\x3b\xce\x62\x9c\x0f\x7e\x01\x16\xf9\xb7\x68\x59\xce\xb2\x1c\xbd\x89\x72\xfc\x19\xfd\x77\x56\xe2\x73\x32\xfe\x15\x7d\x3f\xa7\xdf\x7f\xc3\xe5\x2c\xc6\x67\x83\x71\x36\xff\x01\x80\xe3\xa8\xc4\x68\x7b\x73\xeb\x1b\x60\x78\xcd\x5b\x41\x8d\x00\xab\x95\xe1\xf2\x98\x6f\xef\xe0\x92\x82\x06\x4c\x77\x41\x1f\xe4\x61\x5a\x9a\x80\x24\x2d\x7d\x70\xc7\x0e\xe0\xb2\x02\xf2\xc5\x65\x1a\xcd\xc9\x58\xb0\x71\xad\x44\xcc\x72\x80\x47\xf9\x4a\x7e\x28\x73\x92\x4e\xcd\x32\x05\xa4\xf9\xa0\xdf\xe3\xc8\x1a\x63\x8e\x23\xef\x18\x8f\x5d\xd0\x65\x15\xac\xa7\xdb\xb2\xbf\x00\x4c\x0a\x3e\x40\x83\x33\x17\x5a\x15\x01\xec\xb0\x9c\x4f\x0b\x0b\x71\x2d\x7f\x20\xab\x80\x6d\x84\x01\x5f\x5d\xc9\xd3\x23\xaa\x80\x3e\xc8\xf3\xe8\x92\x81\x33\x26\x6e\x89\x02\xcf\x29\x7d\x6a\x12\x00\x5f\x49\x8c\x43\xc4\xa8\xcc\x10\x4e\x29\x0d\x0f\x63\x4c\xff\x93\xad\x50\x66\x1c\x31\x36\x49\xb9\x12\x97\x6b\xcd\x8d\x99\xd5\xad\x8f\x98\x82\x15\xe6\xce\x0c\x49\x68\x1f\x6a\x28\x8c\x2e\x02\xef\x9f\xe3\x72\x96\xc5\x9e\x6e\x31\xe5\x7a\x96\xcf\x11\x93\x5c\x32\x63\x46\xd6\x10\x5b\x83\xbc\xf8\x27\x3e\x33\x3c\x0b\xfd\x0d\x7a\x8f\xbe\x30\xe2\xb9\x96\x62\xf9\xdf\x18\xe6\x0b\xf4\x45\xaf\xec\x1a\xb2\xe0\xad\x42\x81\xbe\xc0\xbb\x86\x6b\xc4\x3f\x09\xe5\x0d\x4c\x22\xa2\x64\x08\x7d\xa1\x3b\x11\x65\xf7\x80\x10\x03\x19\xda\x4e\xad\x77\xc9\xc1\x91\x40\x11\xc5\x66\x61\x8a\x77\x1a\xd6\x06\x13\x92\x94\x38\xef\x69\x65\xfb\x9a\x0e\x82\x53\x51\xc9\x85\x02\x41\x04\xa0\x53\xe8\x9f\x6c\x9e\xee\x31\xfe\x49\x26\xa8\xb7\xae\x37\xa2\xd7\xc1\x1e\x68\xb0\xa7\x1c\x5d\x92\x9e\x45\x09\x89\x15\x0d\xd0\x1a\xd7\x43\xd4\x45\x1b\x48\xaf\x7c\x4d\x97\x35\xf4\x9a\x4d\x0a\xac\xa0\x34\xb4\x48\x22\x92\x32\xfa\xb2\xa6\x91\x01\xbc\xe3\x39\xd5\xb3\xc8\xd3\x8f\x46\xbf\xe0\x71\x79\x6d\x55\x28\x26\x59\x95\x63\xd5\xc6\x16\x5c\xf5\xd4\x69\xdd\x70\x66\x2e\x60\xe5\x2d\x81\x0b\x26\x4d\x2b\x56\xf4\x4e\x28\xf0\x69\x80\x4e\x00\xfc\xb4\xdf\x0e\x35\x09\x29\x40\x02\x62\x8b\xaf\x1a\x3b\x85\x8e\x06\x60\x01\x0c\x3b\xbe\xf4\x85\x2a\x50\x85\x18\xa7\xd9\x56\xb8\x29\xdc\xa5\xcf\xb1\x53\x54\xd1\x77\x21\x08\x7c\x8a\x4b\x7d\x05\x16\x9c\x73\x70\x92\xa5\xc5\x78\xdf\x68\x09\xa3\x86\xc1\x3c\x5a\xf4\xaa\x78\x2c\x68\xe5\x3c\x6b\xc4\xe0\x9d\xac\xe6\x1e\xeb\xe9\x09\x14\x39\x65\xec\x59\x7c\xc9\x55\xa4\xf5\x87\xef\x53\x47\x93\x49\x81\x4b\xa7\x53\x39\x8e\x97\x63\xac\xf5\x2b\x1a\x8f\x03\xd4\xd0\x39\xc0\x4e\x19\x95\x64\xfc\x2e\xca\xcb\xd7\xf0\x92\xc8\xaa\x79\x60\xe7\xf7\x3c\xfd\x14\x75\xe5\x94\x29\xe1\xf8\x83\x5b\xe5\x9b\xa8\x9c\x0d\x26\x49\x96\xe5\xbd\x9e\xd3\xe2\x06\xda\xd9\xea\xa3\x21\xda\xd9\xee\xa3\x87\x68\x67\x9b\x0f\x5a\x43\x5f\x34\x1e\xa3\x0d\xd4\x93\x9b\x8e\x81\xf5\x0a\x14\xa2\xa7\xda\xde\x85\xd0\xce\x36\x0a\x8d\x84\x8a\xce\x0a\xd4\x07\x68\x53\xc7\x7e\x8e\x8b\x65\x52\x0a\xea\x61\x33\xf8\x66\x99\x94\xe4\x67\x52\xce\xd8\x9c\x08\x0a\x34\xfa\x16\x48\x3a\x0a\xcc\x19\x14\x95\xf3\x11\xb2\xfa\xcd\x13\x9f\x9f\xf4\xad\x56\x7d\x6b\xa0\x65\x0f\xb4\x35\x22\x87\xd7\xe9\xec\xa9\x85\x83\x93\x09\x1f\x31\xef\x2c\xdf\x15\xb2\xfc\x65\x34\x9e\xf5\x6c\xc6\x44\x74\xda\xa2\x5c\xbf\x72\xbe\xd4\x5c\x9d\xf6\xf5\x42\x0c\x21\xd0\x95\x0d\x57\xdb\xd9\x33\xbb\x2f\xd6\x91\x46\x84\x72\xed\x52\x2a\xc6\xc9\x84\x83\xd8\x73\x04\x1d\x70\xbb\x24\xf0\x04\x1f\xf6\x64\xe9\x4d\x98\x4b\x71\x63\x1f\x61\xfe\x0c\x0f\x0d\xd1\xb6\x02\xbd\x46\x38\x29\xb0\x35\xbc\xe1\x10\xc5\x59\xda\x2d\x51\x14\xc7\x88\x97\x2a\x33\xb3\xca\x01\x22\x65\xb7\x40\x51\x92\xe3\x28\xbe\x44\xe3\x6c\x99\x96\x38\xae\xc0\xd2\x57\x1a\xe7\xb5\x5a\x84\xc3\x21\xfa\x78\xf4\xe2\x28\x44\x13\x32\x5d\xe6\x18\xd1\x03\x5b\x8a\x0b\x7a\x02\xa4\xa7\xb4\xcb\xc2\x64\x56\xbf\x05\x91\xfc\x71\x26\xd9\x9c\x0c\xac\x23\x50\x60\xa5\x62\x99\x4b\xb4\xe6\x78\x12\x81\x3a\xe6\x7c\x96\x25\x98\xf5\x90\xa4\xd3\xf5\x06\x46\x50\xc3\x03\x6c\xce\xcf\x07\x1d\xa0\xcc\x59\xf9\xc6\x22\x17\x73\xd2\x28\xea\x7b\xb6\xb8\x9e\xab\x1a\xd3\x08\x88\x35\x8c\xce\x23\x45\xd6\x05\x2e\x9d\x39\x65\x64\xf5\x36\x9a\x63\x7b\x1f\x52\x39\xba\x9c\xe9\x96\xf5\x6c\x3e\xf5\xfb\x99\xaa\xd8\x53\xa7\xe4\x8b\x1c\x83\x4a\xaa\x15\x7f\x35\xc3\x16\x95\x2c\x72\x7c\x46\xb2\x65\x21\x3b\xb4\xbd\x47\x51\x42\x52\x44\xd2\xd2\x29\xd1\x84\x7f\xad\xbf\xbe\x06\xe9\xdf\x24\xcb\x11\x3c\x12\x26\x68\x1f\x6d\xed\x21\x82\xbe\x17\x03\x10\xef\x85\x11\xd9\xd8\xa8\x2a\x4e\xff\xac\x3e\x6f\xec\xa3\x8d\x9e\xc0\x01\x41\x8f\xd0\xd6\x29\x95\xf0\xd1\xd5\x15\xda\xdc\xab\xac\xa4\x86\x95\x73\x7a\xd8\x40\x04\x3d\xac\x9a\xb9\x0d\xbb\x17\x54\x38\xa8\x62\xfb\xe2\xef\xda\x49\x35\x53\xae\xfb\xbd\xbe\x35\x85\xc3\x21\x9a\x90\xbc\x28\x11\x4e\xf0\x1c\xa7\x25\x3d\x5f\x31\x34\x05\xa8\xf8\x4c\x16\x88\x94\xab\x4c\xb9\x81\xfd\x4d\x1f\xf6\x29\xfe\x6a\x67\x00\x9e\xce\xc7\x31\xa1\x8d\x44\x89\x5c\xe4\x1c\x9f\x0e\xff\x71\xf1\xed\xe7\x8b\x8a\x74\x2a\x18\xc4\x09\x41\x1b\x68\xeb\x54\xf0\x09\xb4\x81\x9c\x6e\x78\xd0\xde\x88\x60\x8b\xf9\x79\x20\xf9\x56\xe9\xa1\x7d\x46\x15\x37\x66\x3d\x7f\x68\xa6\x42\x85\x2d\x13\x53\xb7\x5c\xfc\x0d\x94\x89\xaa\x18\xd2\x66\x1d\x43\x42\xad\x68\xba\x91\xa3\x0c\x87\x68\x1c\x25\xe3\x65\x12\x95\x58\x08\x3e\xf4\xc8\xc7\xfb\x82\x48\x89\xe7\xb7\x60\x47\x94\x15\x9d\xfc\x89\x98\x52\xdf\x86\xbd\x5e\x69\x5f\xb9\xe5\x84\xfc\x7e\x0c\x46\x67\x2e\x5f\x9d\xb7\x20\x47\x5b\xc4\xfb\xd1\xa0\x0d\xe1\xba\x48\x7e\x33\x99\xd5\x68\x8c\x18\x64\x6b\x8d\x91\x48\x97\xb7\x9a\x52\x25\xe2\xd7\x25\x55\xeb\x41\xb4\x86\x3d\xe2\x1f\xd4\xef\xd3\x11\x69\xc5\x94\x8e\x88\x41\x83\x6c\xd3\x06\x2d\xb5\x4a\xa2\x0a\x84\x54\xe9\x88\xaa\x11\xc2\x4b\xc0\x09\x03\x5a\x53\x88\xa9\xd7\x10\xe9\x43\xf4\x9d\x8e\x0d\xdc\xac\xae\x20\x12\xa5\x18\x15\xeb\xf0\x8c\x88\x0b\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0d\xb9\x07\x23\x13\xeb\x4b\xa9\x45\x0c\xbd\x88\xa8\x51\x69\x98\xea\x54\x0e\x6a\x54\x8d\x7a\x06\x1d\xa3\x8c\x03\xd1\x32\x77\x3d\xd2\x36\xea\x28\x79\x12\xf5\xc9\xc1\xbc\x6b\x95\x4c\x72\x38\x44\xc5\x72\xce\x6e\xe8\x3c\xbb\x14\x17\x11\x25\x3c\xaf\xee\x84\x9c\x52\xae\x28\xbf\x60\x4b\xf2\xf1\x1f\xd1\xbc\x89\x08\x21\x6d\x3a\x28\x18\x0e\x51\x8e\xe7\xd9\x19\x5c\x63\xa2\xf1\x32\xcf\xa9\x7c\x2a\x85\xd3\x0c\x92\x79\x37\x49\x01\x3d\xf7\xf4\xb6\x58\x45\xe3\x27\x90\xd9\x5a\xf3\x67\x8c\x0c\x3d\x72\xea\x6f\x4d\x69\x1f\xac\x75\x58\x71\xad\xe3\x3d\xb5\x0a\x1e\xe7\xa1\xb2\xd2\xba\x72\x10\x64\x45\x77\x30\xfd\x92\xc4\xbc\xbf\x60\xbd\xa5\x6d\x8d\xf9\x2d\x93\x6e\x6a\x01\xbd\xef\x31\x7b\x55\xdb\x04\x83\x5f\x8b\xf6\xfa\x81\x37\xfb\x59\x96\x25\x55\x79\x54\x08\xa9\xc8\x3a\xae\xc9\xd3\x2f\x37\x2b\x9b\xad\xcb\x64\x5c\xb8\x2a\xf7\x3d\x8e\x2a\x7b\x7c\xcc\x32\xd7\x28\x41\xb8\xf6\x1b\x80\x3a\x69\xb3\x21\x0c\x67\xc3\xdd\xa0\xc3\xee\x7e\x3b\xe1\x37\xf0\x93\xf6\xad\x13\x3e\xa6\xbf\xf5\xeb\xd8\x4e\xf8\x24\xf0\xd9\x7a\x90\xb4\xec\x84\x5b\x9b\xf4\x67\x8e\xa3\xa4\x13\x6e\x6d\xd3\xdf\xec\x56\xb6\x13\x6e\xed\xd0\xaf\x25\x83\x82\x06\x96\x1c\xec\xf1\xf5\x69\xf0\xe4\xb7\xb4\x8b\x6a\xb8\x86\xbe\x99\x35\x91\x5e\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x48\xcf\x5d\xd1\xc4\xc8\x5f\xb4\xc6\xd2\xc8\xec\x49\x9b\xba\x6e\x61\x77\x54\x61\x6c\xd4\xaa\x51\xed\x4a\xdc\x3b\x5d\x82\xed\xe4\x4b\xdc\xc2\x84\xc9\x1a\x76\xb3\x25\xd3\x77\xf7\x96\x4c\xf7\x96\x4c\xff\x29\x96\x4c\x6a\x21\xdc\x95\x39\xd3\x33\x32\x7d\xbb\x9c\x8f\x80\x15\x4a\xee\x3c\x22\xd3\x14\x12\x07\xbf\x48\x4e\xbe\x2c\x49\x62\xda\xd7\x0c\x86\x90\xc6\xfe\x15\x60\x63\x2f\xc8\x38\x4b\x27\xc4\x31\x06\x12\x27\x33\x6d\x57\x80\xb3\x0b\x6c\x0b\x62\xe0\x8c\x57\x17\x08\xf8\x3d\x82\x07\x1b\xf4\x9c\x45\xf9\x96\xb2\x92\x85\xa5\x40\xe7\x06\x94\x33\x0f\x29\x8e\x19\x24\x29\x50\x8a\xa7\x51\x49\xce\x70\x20\x38\x11\x5c\x1c\x95\xe7\x59\xb7\x40\xe3\x6c\xbe\x10\xd2\x2a\x94\xa2\x73\x2b\x4b\x4e\x92\x2c\x2a\x49\x3a\x45\x8b\x8c\xa4\x65\xc0\xae\x43\x29\xd9\xc7\xd9\x79\x6a\x9d\xe9\x4c\x35\x89\x7b\x7c\xbb\x62\x58\xbe\x92\xf8\xbe\x16\x63\xa1\x4b\x29\xc5\x38\x86\x53\xf4\x48\xcd\x71\xec\x37\x86\x01\xa4\x5d\x4b\x3b\x1f\xb3\x5d\x83\x01\x43\xfd\x82\x0b\xcb\x76\x07\x6c\x2e\x7a\xe3\xc1\xcb\x8f\xaf\x3e\x3d\x3b\xfc\xe9\xed\xf1\x9b\x67\x2f\xdf\x7f\x7a\x7f\x74\xfc\xf6\xc5\xe1\xdb\x9f\x3e\xbd\x39\x7a\xf1\x52\x3b\xc3\x49\x4d\x1c\xcc\xe4\x60\x11\xc5\xaf\xf1\xa4\xec\xb1\xaf\x32\xfb\x78\x9e\x15\xcf\x25\x16\x79\x9b\x83\x32\xe3\xe2\xd2\xd6\xe3\x7e\x80\x1e\xef\x9a\x37\x3c\xfa\x6e\x09\xc3\xe9\xb1\x46\x4c\x03\x0c\x73\xe2\xc5\xe1\xb7\x02\xe7\xcf\xe4\xd9\xd8\x3c\x34\xaf\x8a\x43\x57\xea\x30\xb0\xe8\x41\x48\x99\xbd\xc2\x17\x62\xdc\xc5\x72\x54\x94\x79\x6f\x5b\xc3\x5f\x62\x5d\xed\xb3\xe2\x42\xcb\xbd\x81\x1e\xef\xf4\xd1\x50\x47\x91\x8d\xee\xf7\x64\x3a\x2b\x79\xb1\x00\x25\xe8\xe1\x57\xc6\x27\xdf\x81\xef\x14\xad\x95\x32\xdd\xad\xb1\x2b\x8e\x67\x26\x5a\xa5\x76\xee\x77\x9b\x01\x4b\x6d\xca\x1a\xeb\x0f\xd8\x9a\xdf\x40\xcd\x13\xd4\xc4\xe9\x98\x24\x5f\xbd\x22\x3e\x88\xfc\xdb\xce\x9d\x34\xee\x6c\x3f\x6b\x93\x3c\x9b\x1f\x97\x93\x27\xf7\x13\xe7\x99\x38\xfe\xce\xa8\x8a\x91\xf1\x57\x48\x62\xd2\xe8\x37\x8e\xd2\xd5\x19\x99\xfd\xe4\xa8\x7a\xce\xba\x9b\xb7\xfb\xeb\xa2\x0d\x5e\x3d\x7a\x8a\x50\x77\xab\x8b\x42\xd4\xdd\xec\xde\x9e\x47\x35\x61\x92\x9e\x58\x69\xa9\x7f\x50\xb8\x02\x51\xc1\x78\xbe\x4c\x4a\xc2\x84\xca\xd1\x25\xda\xfe\xdf\x39\x15\xcf\xa5\x0d\x5d\x44\x6b\x2e\xf1\x14\xe7\x35\x5b\xc9\x7b\x5e\x6b\xd3\xfe\xbd\xea\x8c\x70\x5b\xe6\x8a\x19\xe1\x68\xb2\xa8\x8f\x62\x4d\xb6\x28\x37\x57\x32\xc7\x85\x95\xb5\xdd\x1f\x2c\xb2\xf3\xde\xd6\xf6\x93\x7e\xdf\x44\xe9\xf3\x19\x1e\x7f\x46\x64\x62\xe0\x54\x13\x8b\x2c\x44\x14\x64\x9a\xe2\xf8\xb0\x78\xab\xb2\x1d\x45\xb4\xac\x63\x86\x2f\x78\x8f\x4d\x64\x08\xa2\x85\x43\x1f\xb4\x5d\x9a\x92\x58\x46\x8f\x2c\xe7\x84\x8a\xe1\x51\x52\x28\xab\x65\xbb\xf5\x46\x7c\xf9\x30\x24\xd8\xcd\x66\x80\xb6\xfa\x01\xda\x7a\xac\xc9\x23\xdb\x7d\x23\xb7\x8f\xf6\xf7\xf7\x29\xc9\x7a\xa9\x30\xa7\xec\xe3\x51\x94\x40\xa7\x10\x53\x1d\xa8\x0b\x0f\x26\x6a\xba\x44\xc4\x14\x09\xb6\x10\x68\x90\x87\x63\x07\x4b\x71\xa6\x04\xc3\x9a\x76\xa5\x70\x08\xcb\x82\x4c\x11\x93\xd3\x2d\x7a\x93\x5d\x30\xf0\x67\x18\xc5\x52\x60\x36\x8f\xfb\xac\x37\x9a\x2e\xb3\xd7\x47\x57\x57\xa8\xb3\xd9\xe1\x3a\xe2\xe1\x10\x8d\x25\x15\x51\xe1\x59\x4c\xa4\x6c\x9d\x01\x91\x92\x4d\xb4\x94\xb4\x5d\x21\x5b\xdc\xdf\x5a\xf3\xcc\xe7\xd6\xa3\x82\xf4\xcc\x2f\x9b\xd2\x39\x49\x97\xf6\x2a\xe8\x4e\x6e\xf9\xd7\x85\xba\x45\xe5\x5b\xf2\x7a\xac\x45\x87\x6e\x40\x41\xcb\x7a\x12\x3a\xae\xa5\x21\x1f\xf5\xe0\x95\xc8\x87\x37\xef\x12\xce\xf1\x5d\x50\xce\xd7\x41\x19\x67\xf9\x55\x28\x73\x78\x77\x23\xca\x00\x63\x9a\x48\x6c\xa2\x88\x37\xe7\xa2\xc8\x61\xe6\x3e\x8b\x73\x6b\x31\x72\x98\x41\x4c\xce\x48\x8c\xe3\x67\x97\x35\x3c\xfc\x26\xd4\xd4\x80\x9b\xe3\xbb\x46\xce\xb2\x12\x3b\xc7\x2b\xa3\xe7\xf8\x36\xf8\x71\x6f\x61\x59\xd5\x12\x45\x55\x12\x97\x7a\x30\xdd\x1a\x2f\x62\x67\x33\xe7\xa2\x12\x47\xbc\x69\x17\x45\x8e\x7c\xe6\xc3\x90\x67\x79\xc1\x7e\x75\x4b\x81\x6d\xab\x8b\x9e\xb2\xad\x99\x7b\xc6\x58\x0d\x9b\x95\x27\x47\xed\x5d\x6e\xcd\xde\x97\xe0\x89\x42\x1c\x95\x20\x6a\xce\x36\x8e\xe8\x91\x46\x73\xcc\x1e\xf8\xd0\x5f\x96\x08\xc6\x61\x68\x9d\xb2\x06\x0f\xe6\x9d\x43\x28\xb4\x11\x20\x5d\x59\x4e\x0b\xf1\x27\xd6\x68\x1f\x55\xbd\xd4\x7d\xd8\x1f\x6a\x47\x9a\x82\xfc\xca\x79\x62\x01\xb7\x54\xbc\xfc\xc9\xd6\xa9\x29\x0a\x77\x37\x2f\xa8\xc8\xec\x4e\xee\xa0\x48\xc8\x18\x53\xc9\x64\x1b\x3d\x84\xea\x56\xa4\xf3\x86\x99\xd1\x4f\xe1\x77\x36\x41\xab\xa2\xbf\x52\x15\xe0\x6c\x32\xf2\x88\x68\xf1\x01\x86\x38\x7e\x09\x66\x63\xee\xf1\x6e\x9f\xef\xe1\x65\xc6\xe1\xfb\xe8\xa1\x38\x55\xfa\x66\xc0\xaa\x88\x49\x87\x8f\x77\x03\xde\xfe\x6a\x53\x50\x73\x2a\x67\xc3\xf7\x1c\xcb\xef\x14\xfb\x51\x31\x26\xa4\x0e\xff\x9e\xe3\xfc\x6f\x88\x79\xa1\xd5\x01\xed\x40\x3b\xfc\xaf\x36\x01\xca\x3d\x4d\xd5\x0c\x1c\x28\x07\x36\x15\x53\x50\xc9\xdb\x2b\x50\x2e\x2b\x74\xb1\xed\x73\x60\xb3\x82\x34\x65\xe0\xae\xb3\x79\xd1\x41\x1b\x88\x9f\x71\x00\xed\xec\xb7\x34\x2b\xd8\xdd\x0c\x90\x9e\x54\xe5\x33\xe0\x8b\x30\xfd\xd0\xce\x9a\xa1\xf5\x1d\xd8\x30\xb0\x62\x43\x27\xc5\x81\xd3\x17\x78\x58\x95\xe1\x94\x62\xc8\x0c\xdd\x24\xb7\x1f\x59\x96\x84\x76\x82\x03\x45\x25\x90\xd0\x4e\xd0\xa1\xa4\x58\x16\xda\x09\x2e\xd4\xb1\x03\x76\xec\x85\xd3\x1b\x55\x29\x9e\xfa\x5c\xc0\x63\x3f\xa4\x3e\x58\x95\xe2\x81\xd3\xb1\xad\x25\xb9\x90\xbe\xe9\x71\x73\xdc\x72\xe6\x04\xe9\x69\x2e\x2c\xa7\xfa\xd0\xbb\xee\xae\xc5\xb5\xae\x79\x39\xd4\x09\xb7\x9e\x04\x1d\xf3\x52\xa9\x13\x6e\x83\x05\x03\x2c\x8c\x4e\xb8\xb5\x15\x74\xf4\xab\xa9\x4e\x68\x7e\x5e\x9f\x06\x5b\x9b\xbf\xb3\x4b\x97\x43\x66\x1b\x5f\xe3\x83\x88\xa4\x65\x95\x0b\x22\x7e\x7b\x45\xd2\x92\x79\x67\xa1\x3f\x76\xe5\xaf\x53\x95\xb8\xa3\xfd\xb6\x9c\xb7\x90\xb4\x64\xae\x5b\x48\x5a\x3e\xde\x95\x60\x4f\x54\x45\xdb\xdf\x3c\xae\xa8\x8b\xc2\x37\xb8\x32\xb2\x8f\x86\x5f\xd1\x1b\x17\x80\xdb\x66\x08\x87\x69\xb9\xa2\xe5\x85\x51\xa2\xc6\xe0\x02\x9a\xab\x29\x79\x23\xf3\x0a\x92\x96\x42\x54\x7c\x7a\x23\x97\x2e\xac\x57\xcd\x66\x10\x5b\xad\xa2\xd8\xdd\xdb\x41\xdc\xdb\x41\xfc\x79\xed\x20\x90\x32\x84\x60\xa2\xd2\x1d\xd9\x40\xb4\x30\x6d\xb0\x59\x3d\x33\x5d\xc8\xc0\x20\x5d\x79\xee\x18\x78\x24\xd4\xf3\x19\x4e\xe5\x7b\xc5\x80\xd9\x7e\x53\x01\x5c\x3a\x70\x10\x92\xe5\xd0\x6b\x1b\x61\xa9\xbf\xed\xe7\x89\xc0\x49\x85\xfc\xc8\xfe\xbf\xba\x42\xdd\xae\xc6\x67\x33\xf1\x72\x81\xfd\xd8\xd3\x9e\x1a\x92\x94\xb7\xde\xda\xe3\xc7\x14\x97\xba\xc9\x2f\x18\x90\x77\x0b\xf1\x10\x14\x78\x09\xad\xc4\xb0\x76\x57\xf2\x3d\x33\x76\x35\xa5\x68\xa1\x66\x52\xb5\xea\x95\xa1\x9e\xe8\x63\xdf\x30\x68\x07\xf4\xe8\x06\xed\x76\x23\xb5\xa6\x68\x60\xe5\x6f\x1c\x3b\xf4\xeb\xc7\xd6\xc8\x18\xe7\x98\x12\x93\x58\x0f\xa6\x5b\x16\x46\xee\x31\x99\x4c\x30\x18\x24\x33\x94\x5b\xe7\x92\x73\xf9\x2e\x44\x3f\x8e\x08\x94\xf0\x59\x12\xb6\xcb\xa9\xf7\x10\x62\x1e\x5d\xe8\x76\xe8\xeb\x47\xb4\x60\x1c\x46\xf6\xa2\x1a\x95\xe7\xfe\x37\xb3\x26\xdd\x55\xde\xea\x29\x82\x94\xa4\xba\x0a\x46\xb3\xf9\x88\xa4\xae\x87\x9b\x32\x9b\x62\xca\xdd\x69\x0d\x78\x3a\x60\x8b\x2a\x5a\x2c\x70\x0a\x6b\x29\x4a\xd9\x1b\x08\x0b\xbb\xbc\xb6\xa6\x7b\x18\xce\x98\x66\x64\x4c\xd9\x93\xe8\x55\x73\x61\x7e\x81\x9a\x4d\x38\x2c\xec\x43\xb5\xa8\x15\xc3\x6b\xd2\xfb\xd5\xa1\x55\xea\x2d\xd8\x95\xc9\x1e\x6a\xc6\xee\x38\x4a\x12\x8e\x5f\x71\x8d\xc3\x46\x34\x8b\xd4\xd2\x2d\xc8\xaf\xdc\xb9\x20\x5c\xd7\xcd\xa2\x22\xa0\xff\x0b\x42\x03\xf7\xbf\x9e\x7b\x3b\x1d\xdf\xd2\x16\xd4\xaf\x33\xad\x45\x8d\xdf\x3b\x93\x6f\xe1\xf2\x55\xb1\xbe\xbf\x0f\xd2\xc5\x84\xa4\xd6\x5b\xa5\x26\x24\x28\xaf\x45\xbc\x2a\x7e\xc3\x6c\x2b\x0d\x58\xee\x41\xf1\xac\xfa\xe8\xcf\x34\xbe\xae\x86\xa6\xc5\x32\x33\x6a\xaf\x1b\xf4\x3a\x8c\x5a\xb9\x00\xe8\xa3\xa7\xa8\xdb\x45\x61\x3b\x83\x2c\x0d\x65\x5e\xb3\xac\x15\xf0\x46\x79\x3f\x53\x4e\x48\x99\xd1\xf7\xdc\x4b\xe9\x2f\xfc\x38\x13\x7b\x8f\xb8\x15\x8e\x74\x86\x1f\xcd\x75\x22\x03\x12\xaf\xc5\xa2\x6a\xcc\x8b\x42\xf0\xab\x64\xe3\xcf\xe7\x9f\x49\x2e\xaf\x3d\xc4\xae\xfc\x50\x05\xdd\xf1\x09\xeb\xad\x8e\x3a\x63\x5b\xab\xc0\x9d\xb6\x29\xf9\x91\x27\x12\x22\x71\x09\xdf\x02\x8b\x78\xbe\x28\x2f\x75\x95\x60\x8b\x4d\xb4\x71\x15\x9a\xf4\xa8\xb1\xa7\x10\xa4\x8f\x15\x70\x23\x3c\x4e\x55\xfa\x9a\xf2\x62\xa2\x76\x20\xbc\xca\xa6\x31\x18\x17\x2b\x1b\x1e\xb1\xe0\x26\xe3\x50\x8f\xf1\xaa\xfd\x43\xbd\x26\x45\xe9\xbc\xfc\x3b\x31\x46\x73\xea\x71\x0a\x55\x3b\x7a\x55\xb3\xbb\xbd\xc8\x77\x41\xe2\xa6\x7e\xb9\x88\x99\x65\x2b\x7f\x07\x27\x55\x91\x65\x56\x6a\x6f\x5d\x59\x61\x21\x1c\x31\xbf\x43\xc8\x78\xdb\x27\x9f\x10\x72\x50\xf3\x59\x91\xb1\xb7\xc9\xf5\xc8\xb6\xaf\x8a\x05\x69\xdf\x7e\xd9\xce\x42\xcc\xe6\xd1\xbe\xde\x63\x05\xab\x0f\x63\x63\xdf\x55\xf4\xf3\xd7\x5a\xee\x0b\x2d\x06\xa9\x44\xa0\x5e\xa6\xbf\xba\x95\xaf\xe6\x86\x43\x31\xdd\xf8\x0c\xe7\x97\xe5\x0c\x7c\x91\x68\xf5\xe8\xd8\x71\x1d\x4f\x09\x8b\x34\x07\x3f\xc6\x4b\x5d\xff\x0d\x85\xf4\xbd\x74\xa7\x4d\xb8\x4a\xe7\xeb\x00\x75\xbb\x42\xf9\x5e\xa3\xa4\x78\xc7\x66\xc9\xd2\xe9\x49\xf5\xdd\xf5\x69\xb0\xd5\x2a\xd6\xde\x57\xd4\xc9\xc1\x6d\x74\xbd\x52\x2e\xa7\x20\x15\x5a\x39\x61\x66\x46\xff\x67\xaa\x32\xf8\xb5\xab\x7e\x9e\x6a\xc9\x3b\xfa\x87\xa5\x9b\xa3\x69\x4c\x39\x47\x7f\x09\xed\x1c\xfd\xfd\x44\xab\x4e\xd3\xcf\x39\x35\xb6\xd0\xd0\x39\x77\xef\xab\xa8\xe8\x68\xe1\x55\x74\x74\x0c\xde\x56\xd2\xd1\xd4\x15\xb5\x74\x66\x91\x1a\x35\x1d\x6b\xb1\xae\xec\x4d\x14\x75\x14\xb7\x15\x8a\xba\x76\x8e\xf2\x79\xb7\x5a\x28\xea\x5a\x45\xf3\xfa\x5a\x8f\xeb\x3c\xb7\x7f\xab\x90\x07\x2b\xbe\x0a\x81\x88\x12\x36\x89\xb0\xf4\x15\x89\xc4\x2e\x54\x43\x26\xa2\xdd\xfa\xf2\x37\xd2\xe9\x32\x49\xaa\xcd\x9b\x39\x4f\x7b\x77\xfb\x5a\x4e\x8e\xb2\x05\xdd\xdd\x7d\xf4\x91\xda\xf7\x3b\x1e\x3e\xac\xb9\xb8\x25\x45\x7b\xdf\xb6\x63\x9c\x97\x11\x49\xfd\xfe\x6d\x1d\x44\xb2\xdb\xa4\x06\xa2\x66\x40\x03\x33\xbd\x9e\xac\x79\x11\x2b\xa3\xd1\x1b\x44\x89\xf3\x39\x3d\xf2\x93\x09\xd4\x6c\xf6\x3b\xe6\x5e\x6b\xd1\x94\x9c\xe1\x54\x98\xb4\x98\x47\xea\x2a\x77\xb9\x96\xfd\x0b\x3b\x66\x2b\x8b\x5b\xc0\x32\xab\xdc\x69\xd7\x6f\x7f\xab\x43\xb4\x5f\x22\xcc\x39\x6d\xa7\xf4\x0a\xc7\xd9\x19\xce\xf3\xf3\x9c\x94\x25\x06\x73\x2f\xd6\xab\x0e\xda\x80\xde\xb7\xc6\xdd\x39\x68\xd9\x0b\xfd\x21\x3f\x58\x41\xa8\xa3\x28\x49\x39\x0a\x4b\xd7\xef\xb0\xfd\xd6\xbe\x15\x32\x5d\xad\xa4\xd5\x9c\xd2\xda\x56\xe0\xcd\xe3\x42\xc0\x8f\xc1\xe1\x10\x54\xe1\xd1\x9c\xae\x0a\xf0\x7a\xc8\xb5\x59\x74\xbc\x94\x13\x60\x76\xc7\x90\x90\xcf\x18\x45\xa8\x20\xe9\x34\xc1\xd2\x0f\x17\x40\x0e\x0c\x93\x68\xa0\x60\xe6\x66\x86\xb9\xe5\x60\xad\x5d\x5d\xa1\x93\xee\xc9\xd6\x69\xf7\xb4\x2f\x85\xc1\x06\x37\x00\xbc\x7b\x26\xde\xe9\x97\xee\xda\xb0\x42\x74\x67\x36\x50\x0c\x15\x60\xab\xb0\x15\xa0\x47\x60\x8f\xbd\x09\x7d\xd9\xd2\x1d\xd1\xa8\x0e\x39\x82\xac\x70\xd4\x10\x08\xd7\x0e\x55\xa7\x05\xe1\xd0\xe1\xa1\x00\x54\x0d\x0c\x87\x28\x4a\x12\x34\x8a\x0a\x32\x66\xfe\x0f\xe0\xb1\xc0\xce\x36\x57\xe0\x24\x19\x3d\x19\x8b\xde\x04\x68\x67\xbb\xc9\xe8\xc4\x5c\xd8\x9c\xa3\x89\x13\xb8\xd0\x45\x22\x3c\x05\x01\x12\x82\x42\x9d\x9c\x76\xd0\xfe\x0f\xb0\x3e\x55\xda\x2e\x4b\xac\x55\xa6\x1d\x88\xda\x56\xe5\x00\x33\x5c\xd9\xb3\x9a\xd5\xae\xb7\x5a\x49\xb3\xca\xed\x97\xe1\x10\xc6\x21\xba\x3d\x6b\x1b\xd5\x8a\x3c\x78\x80\xf4\xef\x13\xed\xb7\xe6\x02\xee\x54\xec\xba\x32\x32\xc6\x70\x7a\xa3\xb9\xe1\xcb\xb7\x6e\x6a\xc4\x2c\x98\x73\xc3\x27\xcc\x9c\x1a\xcd\xe3\xda\x2d\x67\xc6\xea\x57\xcd\xc4\x68\x6d\x7e\xed\x79\xb9\xcb\x89\x31\x5d\x9f\x28\x46\xaa\xcd\x04\x9c\x8d\x3a\x60\x8b\xb0\xcd\x90\xce\x0e\x49\x1d\x6e\xac\xb0\xc5\xa7\x62\x6b\x57\x02\x6e\x9f\x9e\xec\x70\x50\x91\xc6\x40\x24\xc4\xd6\xa9\x95\xa0\xbe\xdd\xdd\x01\xb0\x7a\x83\xed\x41\x1f\x0b\x1f\x62\xf3\x9e\xa0\x35\x76\x47\x13\x49\x26\xa8\xa7\x65\x69\x1c\xd2\xe6\xc7\x37\x9c\x58\x60\xd8\xbe\xd7\x10\x5b\x35\x53\xce\x37\x09\x71\xaa\xf6\xcd\x33\xcc\x9b\x6f\xaa\x3b\x32\xfe\x9e\x33\xe1\xfc\xb3\x63\xcc\xbb\x51\xd1\x89\x59\xb9\x3e\xdd\xca\xfb\x5a\xab\x79\x96\x19\x6c\x28\x3c\xbf\x72\x7e\x0d\x2f\x8a\x95\xbb\x3d\xf7\x56\x94\x44\x45\x89\x4e\x4e\xa9\x30\xc1\xea\xbd\xd1\xb4\xaf\xfb\xe7\x5d\xce\x01\xc8\x59\xc8\xf1\xb1\x04\x07\x1a\xf5\x12\x0a\x3e\x25\x0d\xb4\x21\x92\x1a\xe3\x58\xed\x08\x23\x39\xb0\x7d\xd3\x84\x46\x97\x28\xc6\x93\x68\x99\x80\x22\xb4\x58\x52\x39\x55\x6e\xcc\x1d\xee\xa6\x26\xe0\x61\x1e\xed\x59\x34\x8e\x51\x37\x60\xc0\x6a\x47\x5c\x51\x14\x6e\x79\x7a\xab\x34\xaa\x17\xbe\xda\x85\x8e\x58\x5b\x22\x85\xbd\x46\x80\xe2\x39\x29\x9f\x74\x28\xc5\x07\xa8\x43\x17\x01\xfd\xef\xb4\x73\xaa\xa8\x9d\x43\x68\x69\x50\x28\x5d\x26\xf6\xb3\x07\x6d\x36\x5b\xa1\xcd\x76\x30\x67\xf5\xb7\x61\x21\xb8\x4e\xaa\x9c\x95\xc0\xf6\x06\xee\x2c\x8f\xcd\x7a\x01\x37\xbc\x74\x38\xc6\x78\xe9\xbf\xb0\xea\x2d\x22\xe6\xdc\xaa\xf7\xef\x13\x76\x1a\xff\xf7\x69\xbf\x59\x44\xe0\xca\x5b\xe9\xed\xa1\xfa\xde\xc1\x0a\x63\x21\xa0\xdb\xb3\x0e\xf1\xf6\xd4\xbd\xcb\xb2\x70\xe6\xb9\xb4\xe0\xf7\xe8\xf6\xc6\xe0\xf5\x47\x6d\xde\xca\x70\x57\xa8\xc2\x09\xaa\xcd\x16\x1a\xbc\xc1\x4a\xfb\x6f\xdd\x98\x78\x0f\x55\xfe\xf9\x1d\xa3\xba\x7e\x65\x71\x32\xd1\xfd\xc9\x72\x56\xe6\x14\x92\x2f\x93\x4f\x4e\x7d\x4e\xc4\x07\x8b\x65\x31\xeb\x39\x9e\x49\xc5\x4b\x6d\xe1\x66\xd4\xad\x99\x8e\xc5\xf5\xb9\x7e\xe6\x73\x00\xaa\xb7\xa4\xf9\xf1\xec\x9d\x05\x48\xf7\x2f\x6b\xb9\x27\xbd\x95\x53\x5f\x3e\x81\xba\x33\xdf\x5b\xcf\x1f\x74\xdd\x91\x3a\x38\xe2\x7f\xfb\xf9\xf3\x79\x64\x6d\xf0\xc4\x5a\x39\x11\x74\x36\xc1\x55\x6a\xcd\x7c\xac\x3c\x1b\x6b\xce\x1d\xa1\xa5\x3b\x32\x96\xa4\xe6\xd1\xb6\x8d\x4f\x50\x76\x3f\x3a\xc9\xb3\xb9\xd7\xdc\x80\x41\xf9\x78\xcb\xc8\x7e\xb0\x63\x19\x08\x19\x96\x41\x2b\x3c\x98\x12\x4c\x8d\xb5\xdc\x82\x45\xf1\x81\xe8\x2c\xca\xf0\xa7\xd9\xc0\xaa\xbe\x0a\xaf\x82\xbd\x49\xbf\xb1\x64\x82\x2e\x7f\xe2\x03\xdd\x13\x82\x0e\x47\xd7\x43\xb4\x0d\xc6\x0f\x7d\xe1\xd1\x99\x23\xaf\x6a\x11\xd5\xd6\xa9\x37\xef\x54\xec\x5b\x51\x50\xe0\x43\xc9\xee\xd8\xf5\xd2\x1b\x68\x87\x39\xbd\x67\xbb\x6d\x41\x41\x0a\x14\x4d\x4a\x9c\xcb\x45\xa2\xf7\xf7\x46\x6b\xd5\x5f\xc6\xe7\xbb\x5b\x71\x8e\x0a\x9f\xdd\xa8\x16\x7b\x3c\x74\xcc\xdb\xaa\xfa\x75\xbf\x1e\x95\x6e\xa4\xed\x98\x37\xb5\x8c\xa6\x25\xa7\x41\x0f\xeb\xfb\x46\x61\x37\xf6\xeb\x61\x5a\x31\x2a\xd3\xe1\xac\x36\xed\x1b\x88\xdc\x2d\xd7\xfa\x43\xec\x21\xfa\x5f\x4b\xea\x17\x06\xa9\x2d\xff\xfe\x50\xc4\x7f\x4f\xfb\xda\xdf\xef\x42\xfb\xc8\x4b\xfa\x7a\x80\xc6\x9b\x92\xbe\x1d\x46\x6c\xc5\x4d\xc5\x21\x56\xbb\xfe\x76\x3b\x8b\xd9\x8b\x55\xea\xe7\xf3\xe7\xa5\xb7\xc4\xa1\x2f\xff\xfa\xab\x5e\xc2\x0b\x7e\xeb\xe7\x1a\xa9\x36\x75\xbf\x87\xb6\xd0\x86\xd9\xbb\x3e\xf3\xc9\xc4\x22\x89\x79\xa6\x9e\x79\x20\xb6\x2e\xdd\x8c\x07\xdb\x35\xfe\xec\x0d\x5c\x5b\x16\x5f\x06\x17\x5b\x5b\x71\x6c\xfa\x9c\xcb\x95\xb5\xdd\x37\xd5\xaa\xde\x8b\x44\xab\xeb\x8d\x17\xbc\xd5\x57\xbb\xf2\x4d\xdc\xf5\x69\xb0\xf5\x7b\x87\xde\x3f\x6e\x7e\xf6\xb6\xac\x79\xf7\xc6\x3d\x91\xc0\xff\xcc\xd6\x65\xa9\x9e\xbe\x2d\xb5\xb7\x6f\x4b\xfd\xc1\xda\xd2\xf3\xfa\x6d\x29\x9f\xbf\x2d\xb5\xf7\x6f\x4b\xed\x01\xdc\xd2\x7c\x01\xe7\xd4\xd8\xc2\xc2\xc6\xf1\x8f\xf2\x15\x1f\xc1\x1d\x7b\x5f\xc1\x1d\xaf\xfe\x0c\xee\xb8\xed\x3b\xb8\x63\xf7\x21\xdc\xf1\x1d\xbc\x84\x5b\xde\xfa\x29\xdc\x71\xeb\xb7\x70\xbf\x77\x5c\xff\xe3\x16\x16\x67\xcb\x3a\x93\x33\xe1\x5a\x85\xfd\xe0\xc4\xa9\x59\x9d\x2d\x75\xb3\xb3\xa5\x61\x25\xb6\xf4\x19\x9e\x2d\x95\xe5\xd9\x52\x37\x3d\x5b\xea\xb6\x67\x4b\xcb\xf8\xcc\x53\x6f\x9b\xc5\xf1\x9b\xda\x9f\x1d\xfb\x0d\xd0\x8e\x6f\x60\x81\x76\xdc\xda\x04\xed\xd8\x63\x83\x66\x97\xbe\xd9\x1a\xa9\x31\x43\x6b\xbb\x48\xda\x1b\xa2\x7d\xdb\x66\x95\x74\x97\x05\x06\xc5\xec\xb8\xec\xb2\x80\x7c\xd3\x0c\xe1\xf4\x0c\xc5\x19\x06\x6b\x05\x78\x1d\x18\xa5\x31\xf8\xb0\x45\xff\x7c\xf3\xfa\x55\x59\x2e\xde\xe3\xff\xb3\xc4\x45\xb9\x06\x82\xd9\xe5\x02\x67\x13\x2b\x87\xf9\xb1\x91\xef\x37\xba\x02\x2f\xbc\xe1\x81\x0d\x8d\xbe\x5c\xef\xad\x19\xc1\x22\x2b\x21\xcd\x04\x90\xd4\x7f\x29\x66\x74\xf7\x21\xd3\x34\xcb\x71\x98\x90\x14\xaf\x5d\x33\x8b\x55\x8a\x87\x56\xde\xee\xef\x5f\xce\xde\xbf\x9c\xfd\x13\xbf\x9c\x65\xaf\x66\xb9\x0d\x9b\xf1\x6c\x96\x6d\x38\xe8\x66\xaf\x67\xf9\xde\x77\x5c\x92\x04\xea\x64\xfa\x4c\x58\x3b\xec\x79\x92\x03\x46\xca\x4b\xc9\x12\x55\x91\x71\x12\x15\x05\x3a\x81\x22\xa7\xbc\x9b\x2c\x43\x31\x61\x56\xd5\xda\x10\xee\x8d\x60\x95\x72\xe5\x2a\xe5\x20\xa8\xc6\x99\x75\x7b\x3f\xe7\x00\x49\x6b\x3a\x7e\x7b\xf8\xf1\x03\x3d\x5b\xc3\x24\x74\xcf\x31\xe9\x32\xd2\xec\x7e\xd6\x7e\xbf\xd1\x7e\xff\xa4\xfd\x2e\x7e\x8d\x46\x99\xf8\x98\x90\x34\xc5\x97\xf2\x0b\xcf\xcb\x0c\x9e\x32\x8a\x94\x05\x19\x9b\x09\x69\x94\x9a\x09\x73\x32\xce\xed\x94\x24\x21\x4e\x21\x03\xde\x00\x15\x1f\x46\x91\x69\x1e\xa5\xb1\x1c\x8a\x91\xf5\x93\xf1\xf5\xd1\xf8\x7a\x67\x7c\xbd\x34\xbe\xfe\xc7\xf8\xfa\x97\xf1\xf5\xd6\xf8\x7a\x61\x7c\xfd\xc3\xf8\x3a\x66\x5f\x6b\xa7\xd5\xae\x6b\xe8\x1c\xbd\x3b\x78\x41\xa7\x38\x44\x3b\xdb\x81\x4c\xfc\x70\xf8\xd3\xdb\x83\x8f\xc7\xef\x5f\x7e\x7a\xfd\xf2\xed\x4f\x1f\x5f\x85\x68\x57\x65\xc2\xac\x86\xea\xa7\xca\xa9\xa0\x9c\x10\x7d\x41\x56\x82\xf2\xa3\x0e\x19\x9f\x5e\x1c\xfd\xfc\x16\x5d\xab\x9a\xde\x1d\xbd\x7e\x4d\xa1\x3f\x1e\xbe\x79\x79\x74\xfc\x31\x44\x5b\x9b\x9b\x9b\x43\xde\x43\x7e\xe3\xfd\x2c\xc9\xc6\x9f\x43\xd4\xa5\xac\xb3\x28\xbb\x46\xde\xc1\x18\x42\x19\x87\xea\x6d\x23\x7b\x80\x41\xf7\xf3\x26\xdf\x27\xf7\xa1\x30\xee\x37\xb2\xbf\xfa\x46\xb6\x26\x5d\x40\x14\xb3\x68\xe7\xae\x3c\x40\x3c\xcf\x2f\x17\x65\xf6\xf7\x0f\xfa\xe6\x30\x86\xb4\x47\x2a\x02\x06\x6d\xd0\x0b\x30\xa4\x39\x5d\x6f\x74\x27\xd7\x7d\x03\x50\x5c\xa1\x3f\x50\xe5\x49\xe8\xc1\x03\x91\x3b\x10\xfe\x22\x98\x98\x3c\xc3\x17\x5d\xfb\x15\x9d\xe1\xf9\xeb\x07\xb4\x4d\x4b\xdb\xde\x8f\xb7\x85\xbb\x48\xb3\x38\x12\x97\xe1\xf2\x82\xdf\xf2\xcf\x8e\xac\xd7\x76\x0c\x54\xe0\x88\x76\x6e\xf0\x0a\x5f\x0c\x40\x7b\xc9\x3d\xf7\xfa\x6c\x8c\x28\x56\xc4\xb0\x55\xeb\xec\x44\xc7\xd4\x6f\x21\xda\xfe\xe6\x31\x2b\xa9\x3d\x4e\x16\x6f\xce\x28\xcb\x93\x38\xee\x84\xdf\x7c\x17\x74\x4c\x94\x77\xc2\x27\x9b\xd7\xa7\xc1\x76\x2b\x9f\x4f\xf7\x7c\xef\x9e\xef\xfd\x79\xf9\x9e\x62\x7b\xec\x9d\xff\x1d\xf0\x3d\x4b\x76\x5f\x5d\x74\xf7\x48\xee\xa2\xa0\x4f\x70\x5f\x29\xda\x90\xcd\x6b\x07\x43\xce\xee\x55\x38\xa2\xc9\x13\x1d\x80\x7e\x4b\x11\x7e\x99\x92\xf2\x4d\xb4\x90\xe2\x62\x57\x48\xd4\x21\xe3\x41\xdd\xcd\x6e\x80\xc4\x73\x68\x90\xee\x43\xc5\x1a\xbb\x5b\x86\xac\x1f\x6a\x19\x9b\x9b\x9b\x22\xef\xbf\x6b\xf2\x46\xd1\x68\x14\x4d\xb1\x6c\x4d\xcf\xd3\x0e\x00\xa1\x9d\x37\xf7\xd4\xa9\x65\xbf\xa9\xcf\x4e\xb2\x33\x9c\x44\x63\xd1\xac\x9d\xad\xce\x19\xa1\x2f\x7b\xea\xaf\x5c\x83\xf8\xa9\x11\xa2\x98\x45\x69\x9a\xa5\xc6\xb8\x4d\x08\x75\xb6\x09\x6b\x20\x1a\x5a\x81\xd3\x55\xe8\x81\xd0\x51\xa9\xce\x4c\x61\x3d\x50\x53\x4d\xfc\xfc\x16\x7a\x81\x8c\xca\xe4\x99\xcc\x1e\x9b\x07\xd0\x3f\x44\x13\xd0\x20\x59\x0f\x9c\x06\xfa\xd9\x84\xf5\x81\xea\x73\x0d\x27\xbf\xda\x8a\xf5\xfe\xb6\xaa\x5b\xaf\xbe\x6d\x01\xad\x4c\xb9\x42\x19\x5a\xcc\x6f\x70\xa5\x9c\x31\x2c\xa2\x98\x9b\x93\x82\xb9\xe7\xc5\x02\x8f\xe9\x06\x26\x4d\xf4\x75\xc3\x2b\xee\x41\xc5\x67\x3d\xa5\xaa\x18\x61\x0a\x17\xf3\xa8\x5c\x96\x1d\xd6\x78\x16\xe5\xd1\xb8\xc4\x79\x21\xd4\xfc\x70\x37\xcf\x4b\x6b\x7b\x89\xb7\x0d\x32\x4d\x03\xcd\x1e\x1a\x6d\xae\xf9\x5d\x7f\x90\xe9\xac\x44\xc2\x2b\xad\xe5\xe1\x97\x8f\xc1\x90\x38\x19\x48\x00\xbd\x2b\x02\x68\xc7\xe3\x67\x88\x59\x89\x00\x0c\x04\xa6\x85\x17\xab\xf2\x96\x78\xab\x3f\xf8\x25\x23\x29\x04\x6c\x40\x4f\xa1\x0e\x14\xa2\xce\x66\xa7\x8f\x36\x38\x70\x85\xf1\xdb\x8d\xe7\x02\x82\xf6\xfc\xd9\x27\x03\x06\xb1\xe2\x6c\xf0\x1e\x6e\x30\xaf\xcb\x37\x9d\x97\x2a\x63\x44\xd3\x19\x0d\x6c\x9f\x60\x8a\x08\x01\x3d\x5c\x3f\xd3\xd6\xbc\x30\x8f\xcd\x35\xb3\x42\x52\x5a\x89\x1f\x59\xba\x4f\x6a\x8f\xb3\x24\xda\xb8\x32\x3d\x64\x5e\x48\x8e\xd9\xf6\x2e\xc5\xfa\x19\x8b\xf9\x3c\x1c\xa2\x1f\x49\x1a\x23\xf6\xc0\x8b\x77\x54\xc6\x6c\xa6\x52\x45\xa7\xa3\x6e\xf3\xc1\xfe\x25\x80\x30\x52\x33\x7c\x21\xcc\x98\xe5\xb9\x8b\xa6\xb1\x93\x0f\x3d\x75\x54\x9f\x97\x68\x35\xdb\xfa\xdb\x17\x30\xb0\xe1\x76\x35\x7b\x88\x6c\xec\x6f\xeb\xe0\x22\x1e\xb2\x6e\xdf\xa1\x9a\xea\x11\xda\x0e\x0f\x7f\x21\x5b\x98\xa0\x1e\x2b\xb2\xbf\x8f\x36\xfb\xc6\x49\x6d\x94\xe3\xe8\xb3\x02\xa5\xa3\xdc\xd8\x47\xfc\x65\x39\x9d\xc1\xe7\xb3\x28\x7f\x9e\xc5\x18\x6a\xf0\x1e\xc4\xe8\x64\x0b\x93\x9c\xa2\xcc\xdb\x51\x08\x9b\xb4\x95\x48\xe4\x80\x16\xf9\xed\x68\x04\x9a\xfb\xcf\x21\x92\x9b\xcc\x7c\x51\x56\xbd\x50\x37\x27\xdb\xe3\x67\xbe\xb7\xc8\xf1\x84\x5c\xb0\x40\x5a\x9b\x17\x7d\x3a\x0b\xc0\x35\xfc\x2e\xee\x79\xc4\xb7\xea\xd9\xf7\xda\x2f\xc3\x31\x34\x4a\x80\x9b\xd7\x06\x14\xf0\x45\xfa\x34\xfc\xed\x73\xd7\xeb\xbc\x1b\x3a\x55\x50\x8a\xe7\x98\x67\xb3\x0f\xcb\x81\x9b\x6e\xb3\xe5\x20\x66\x84\xb6\xa4\xa8\x63\x92\xe5\xb6\x19\x5d\x51\xe6\x55\x51\xf1\xb5\x19\xa5\x50\x63\x3e\x37\x07\x65\x8f\xdc\x6c\xa5\x83\x85\x22\x0f\x10\x6e\x78\x6e\x53\x20\xb4\xbf\x1b\xfb\x28\x15\xfb\xc2\xf7\x68\x1b\x3d\xa5\xa7\x1b\xb4\x81\xe8\x7e\x90\xfa\x68\x82\xbb\x91\x9f\xe1\x8b\xbb\x24\x0d\x2b\xee\x80\x4d\x1b\x0d\xac\xe1\x37\x23\x0e\x87\x67\x68\xd4\xf1\xdb\x50\xc0\xef\x36\xad\x96\xd7\xd2\xc9\x32\x49\x24\x1a\x86\xf8\x0c\xa7\x25\x7b\x2c\x00\x2c\xff\x97\x22\x4b\x51\x34\x22\x36\x8f\x17\xae\x13\x3f\x66\x3f\x2e\x93\xc4\x7e\x47\x29\x1e\x14\xd0\xd2\x8f\x58\x69\xf7\x41\x14\x6b\xd8\x69\x57\x31\x76\xb7\x0d\x43\x90\xa2\x95\xeb\xea\x53\xfa\x3d\x00\x33\x0a\x92\xc6\xf8\xe2\x68\xd2\xeb\xf6\xba\x7d\xf0\x0f\xf9\x68\xcb\xf3\x24\x52\xc2\x3b\xb6\x82\xe5\xe5\x02\xf3\xe6\x00\x08\xa8\xc8\xf4\x69\xd6\x23\xfd\x2f\x22\x8c\xf0\x80\xc2\xef\xa1\x6b\x2e\x8a\x99\xd6\x7f\xb2\x15\xb4\x81\xba\x3d\x3a\x73\xb2\xf6\x0d\xd4\xed\x77\x5b\xad\xbd\x98\x14\x8b\x24\xba\x64\xf3\x02\x7e\x46\xd3\x92\xca\xb6\x12\x1b\xf6\xbb\xb5\x0b\xc8\x7e\xc1\x8a\xd5\xbd\x72\xa5\xb5\x99\x93\xef\x5f\x5e\x46\x0f\xe8\x96\x66\x51\x0c\x9e\x0e\x44\xcc\xc5\xcb\x1e\x37\xad\xeb\xa3\x47\x3f\xc8\x44\x39\xad\x6e\xdf\x6a\x1f\x3f\x4b\xbb\x4d\x67\x66\x0d\x34\x73\x30\x36\xd9\xe8\xa9\xfd\xae\x95\xbf\x09\xa3\x6b\x46\x39\x1c\x19\x0e\xd5\x40\xb3\x33\x9c\x27\x59\x14\xe3\x58\x2a\x83\x3d\x6b\x42\x1f\xc0\x47\x45\x24\x55\xef\x1a\x87\xe8\xe3\xd1\x8b\xa3\x10\xcd\xa3\xcf\xa0\x1e\x26\xe9\xd9\x32\x49\x71\x1e\x8d\x12\x7c\x97\x03\x54\xa7\x01\xfb\x05\xef\x16\x7a\x84\xb4\xec\x7e\x7f\x90\xe3\x45\x12\x8d\x71\xaf\x8b\xba\xe0\xd8\x8d\x9e\x16\x3a\x66\xa0\xc8\x2c\x3d\xc3\x79\x59\xa8\xb0\x9b\x20\xf7\xc5\x78\x4c\xe6\x51\x62\x33\x59\x92\xfa\x99\x7d\x99\xbd\x60\x05\x5c\xca\xab\x0d\xa1\x69\xba\x36\x64\x02\x1e\xaf\xa9\x31\x08\x64\x99\xb9\x31\x32\x65\x08\x9a\x36\x63\x6c\x94\x6d\x29\x4f\xbc\xab\x71\x69\x75\xd5\x07\x68\x4d\x85\xa6\xd4\x1d\x9f\x27\x3c\x37\x57\xa1\x9a\x3b\x8a\x71\xd8\x67\x00\x09\x2e\x8a\x8f\xb3\x28\xed\x6d\x82\x23\xd9\x47\xcc\xf2\x9c\x5b\xf0\x73\xc2\xda\xea\x43\x08\x57\x2d\xc7\xc0\xe2\xc1\x12\x5c\x35\x73\x54\x46\xe9\x25\x77\xbe\xc3\x5d\x92\xa6\xd5\x68\x1d\x70\xbc\x1e\xa4\x31\xbb\x02\x60\x34\x44\x26\x97\x05\x77\xa6\x5e\xa0\x11\x9e\x64\x39\x1e\x38\x74\xf5\x8a\x1f\x1d\xea\x71\x7f\xc5\xf7\xa0\x06\xd2\x7a\x05\xfb\xbc\x81\x7c\xb9\x7e\x1f\x72\x73\xb1\x79\x74\xc1\x42\x57\x5e\x90\xf2\x32\x44\x4f\x40\x8d\x2d\x76\x1d\x52\x70\xb7\xc6\x50\xb4\x6f\x6f\x32\xda\x24\xf7\x36\x28\xc4\x9e\x51\x54\x9f\xce\xfa\xc2\x4e\x59\x36\xbe\xea\x82\x10\x59\xe9\xef\x1f\x8e\xde\x0e\x24\x6e\x19\xb0\x72\x5d\x09\x4e\x63\x0b\x14\xd9\x71\x3c\x03\xb4\x88\x8a\x82\x72\xac\x72\x96\x67\xcb\xe9\xcc\xa4\x7b\xd9\x05\x4e\x61\x50\xab\x7b\x2d\xa9\x78\xd9\x23\x38\x23\x79\x24\xdd\xca\x71\x0a\x00\xfe\xaa\xc3\xac\xae\xa1\xb6\x33\x61\x39\xaa\x55\x80\x7a\xeb\xa4\xf8\x91\xa4\xa4\xc4\x16\xc6\xac\x6e\x80\x5c\xa8\x75\xc2\x94\xad\xdc\x8e\x6a\xab\xe1\x3d\xdf\x4a\x18\xf5\xd3\x53\x52\x0a\x3c\x1f\xfd\x8c\x6d\xf1\x69\x8a\x4b\x88\x55\x7c\x34\x39\x4e\x89\x57\xc7\x05\x65\xcb\x19\xe6\x3f\xe4\x32\x43\x65\x16\x48\x9d\x94\x74\x85\xee\x8d\xd7\x28\xfb\x21\xab\xe9\xb1\xce\xf4\xa1\x08\x38\xec\x2a\x10\xce\xf3\x2c\x17\xce\x68\x58\x8f\x0b\x94\x66\x25\x1a\x67\x79\x8e\xc7\x65\x78\x2e\x57\x8b\xd9\x6b\x63\xd9\xd0\x82\x82\x04\x96\x2c\x13\xfe\x7b\x0a\xff\x0d\xca\xec\x75\x76\x8e\xf3\xe7\x51\x81\x7b\xc0\x52\x98\x96\x57\x71\x2f\x0a\xf5\x0f\x7e\xbf\xcc\x2f\x6d\x4e\xe8\xff\xa7\xea\x00\xae\x81\xe8\x1e\xbf\x75\xc2\x63\x3e\xc8\x52\x7c\x8e\x5e\xd2\x51\xf5\xba\x70\xc9\x0b\x1d\x01\x2b\xd5\x7f\x77\x4b\x84\x2f\x48\x51\x16\x01\x5a\x24\x38\x2a\x40\x18\x86\x91\x67\xa9\x44\xd5\x24\x4b\x92\xec\x9c\xa4\x53\x28\x59\x50\xde\x67\x2d\x23\xde\xc3\x00\x3c\x2b\x04\xea\xc1\x47\x4d\x7c\x58\xd9\x7b\xf0\x7b\x65\xfa\x13\x8e\x3e\x63\x58\x84\x8c\xcd\xc3\x35\x34\x01\x4b\x5a\xc9\x5a\x19\x09\x50\x06\x0b\x5e\x2a\xd8\xc4\x33\xd4\x72\xca\x7a\x97\x15\x05\x19\x25\x6c\x0a\xc1\x79\x06\x37\xe7\xfb\x70\x48\xa5\xca\xbc\x64\x3f\xa9\x20\x2d\xb0\xf5\x72\x32\x21\xd3\x4b\xfe\x71\x24\x48\xe9\x11\xfa\x4c\x9b\x67\x7f\xea\x92\x0a\x3e\xf9\x7d\x16\x03\x9b\x2b\x30\x79\xa5\xc4\x3e\xc5\x05\x14\x83\x9b\x2a\x38\x79\xeb\xc3\x3e\xf9\x35\x91\xca\x63\x05\x1e\x3d\x92\x0b\x53\xdd\xde\xb0\x02\xbf\x46\xa3\xcc\xc8\xf3\x94\x10\xb7\x2f\x6c\x00\x70\x69\xa3\xe7\xb1\x12\x5a\x2f\xb4\xc2\xec\x93\x63\x41\x03\x41\x16\x84\xf6\x01\x57\x28\x1c\x21\x58\xe1\x70\xaa\xfd\x2e\xc5\x6f\x5b\x90\x60\x7c\xc1\x3a\xef\x5e\x49\xe9\x9c\x91\xc3\x38\x4a\xe9\x79\x20\x92\xac\x99\xa7\x73\x0d\x59\x96\xa3\x08\xbd\x7a\xf9\x4f\x38\x7a\x0b\x19\xed\xce\x18\x8a\xdc\x5d\xc5\x81\xee\xe7\x19\x16\x1e\xf6\x22\xed\x12\x97\xc7\x3f\xd1\xc2\x04\xd0\xf5\x14\x15\xe8\x1c\xd3\x05\xa2\x5c\xab\x88\x61\xac\x69\x32\xd0\xcf\xd8\x38\x88\x8b\x71\xea\x2c\x85\x09\x38\xb4\x66\xc1\x24\x74\x51\x88\x95\xd0\xe3\xc5\x9a\x9c\x8a\x71\x27\x4b\x0a\xd2\x37\x5f\x5e\x01\x7a\x6a\x34\x12\xea\x5f\x9a\x3c\xd5\xb8\x7c\x23\x86\x63\xcf\x0a\x3e\xc7\xe4\x7e\xc1\xfe\xa7\x2c\xf1\x32\xab\x5b\xe0\xda\x29\xe1\x37\x5b\xea\x74\xb5\xfd\x8e\x8b\x1d\x10\x72\x37\x4b\xbd\x24\x73\x5c\xfc\x1e\xcb\x3c\xe5\x2a\x45\xba\xb8\xa5\x82\xaa\x60\x87\x7b\xd8\xa2\x91\xb4\x62\x71\xc8\x41\xf6\xa4\x15\x51\x28\x32\x10\x37\x86\x74\xee\x15\x2d\x98\xb5\x49\xff\x56\xaa\x02\x05\x20\xf1\xaf\x9b\xdd\x58\xb3\xd0\x70\xea\xf9\x86\x0a\x81\xb0\xec\x45\x79\xfe\xe3\xea\x0a\x6d\xee\x79\x8f\x34\xbc\x5e\xe7\x70\xc2\xd2\x8d\xb3\x0c\xc7\xb9\xe8\xc9\x83\x07\x88\xff\xf6\x09\xfd\xb4\x49\x3b\x57\x3f\x61\xf8\xbc\x9f\x19\xb2\x18\x2f\x2c\x35\x21\x9b\x17\xdd\xa0\xdb\xd5\xaf\x59\x2c\x1f\x69\xbe\xd2\x3a\xa1\x54\xca\x74\xa9\x88\x1a\xeb\x21\x15\x49\x27\x0c\x4c\xc4\xef\x90\x47\x31\x6e\x30\x09\xb0\xe5\x79\xd6\x2d\xd0\x58\x46\x73\x71\x48\xcb\x0c\xf6\xd2\x86\xbe\x2a\xa8\x46\x3b\x1a\x9b\x75\x9a\x6a\x2e\x83\x64\x28\xf8\x48\xa3\x2c\xdf\x82\x85\xc7\xde\x3d\xcd\x5f\x9d\x2c\xa0\x2b\x22\x1a\xa7\xae\x33\xb9\xe5\x5f\x07\x66\x79\xb0\x48\x96\x85\xea\x02\xff\xf6\x3a\x36\x94\x40\xa6\xfe\x68\x86\xc7\x9f\x0b\x71\x6c\x62\x3c\x52\x5c\x6e\x16\xfc\x99\x5c\x72\x09\x1e\x7c\xbd\x71\x88\x19\xc9\x8f\xbd\x31\x88\xcd\x68\xc2\x5a\x03\x74\xfd\x47\x0a\x5e\x77\x69\x07\x61\x95\xf8\xcc\x59\x75\x1b\x13\xc7\x2b\xb5\xf4\x66\xc3\xff\xdd\xbc\x38\xd9\x7c\xf4\x5d\xf4\x68\x72\xfa\x65\x77\xf3\xfa\xbf\x86\x64\x50\xe2\xa2\x94\xe0\x2b\x8c\xbd\x66\xc8\x5f\x67\xb0\x2d\x86\x09\xe7\xff\xe1\xff\xf6\x36\x2f\xfa\x4f\x6b\xc7\xa9\xd3\xdf\x70\xa8\xa2\x64\xb1\x38\x58\xd0\x3b\xe6\x3b\x98\x9b\x1b\xce\xe1\x05\x2f\xdd\x8f\xb5\x51\x9b\xf4\xcb\x5d\x00\x22\xd3\x49\x85\xb7\x33\x66\x5f\x28\x9b\xd3\xc0\x0e\x1e\xfd\xe8\x05\xb3\xba\x0c\x41\xbb\xba\x05\xb8\x39\x2e\xe6\xf4\xdf\x71\xb4\x28\x40\x76\x48\x12\x24\xbe\x03\xdd\x37\xa3\xdd\x63\xe6\x72\x5e\xeb\xb0\xd1\xc0\x91\xdc\xde\x19\x76\x70\x34\x9e\xa1\x71\x54\x38\xd5\x90\x82\x11\xca\x72\xce\x67\x48\xa3\x26\xb6\xca\x58\x40\x91\x76\x54\xc5\x5a\x2b\x96\xf3\x39\x8e\x2b\x09\xcc\x6a\xf0\x8e\x09\xcd\xaa\xbd\x82\xe0\x90\x16\x5d\xe7\xb9\x07\x43\x91\x2c\xcd\x7f\x39\xfb\x90\xd2\x8a\x70\x88\x57\x51\x01\xce\x68\x66\xd1\x8e\x68\xc8\xd4\xa8\x08\x99\xc7\xe7\xf0\x65\x77\x13\xee\x27\x11\xed\x9a\x3e\x8f\xe0\xbd\xbb\x9c\xa1\x04\xc3\x7b\x6a\x2d\x04\xdf\x62\x81\x73\xda\x5d\x31\x17\x29\x84\x2f\x9c\x12\x16\xe1\x2e\x2a\xf0\x3c\x5a\xd0\x39\xd9\x32\x14\x7e\x3d\x69\xbe\xa0\xf5\x1a\xfc\xb2\x6d\x3d\xee\xa3\x1f\xd0\xb7\x74\x57\xe7\x59\x27\xe4\x74\x50\x66\xc7\xb4\x21\xae\x12\x5a\xdf\xdf\xd7\x32\x81\xf6\xeb\x2b\xfc\x7e\xdf\x53\xa3\xae\x64\xb2\x6a\xac\x70\x16\xae\xad\x4e\xc5\xf9\x0d\xfe\x0f\xab\x01\x26\xd5\x20\xd7\x37\xfc\xd4\x27\xc8\xb2\x82\x26\xcb\xec\x2e\x69\x52\xa8\xaf\xe5\x16\xdd\x92\x24\x25\x13\xe4\x4f\xb0\x25\x0d\xda\x6f\xaf\x79\x43\xdd\x2e\x27\x28\x97\x58\x0d\x24\xdf\x88\x74\x35\xa0\xb1\xd3\x7d\x5a\x51\x0d\x31\x8b\x5e\x68\x17\xef\x0e\x61\x03\x07\x9c\x29\xeb\x3f\x4a\xaa\xdf\xd1\x53\xd0\x84\xf9\xd1\x17\x97\x71\x8a\xce\x0d\x3a\x6e\x22\x63\x93\x90\xec\x11\x6c\xec\x57\xd2\xb8\x46\x65\x36\x5b\x6d\xac\xa9\x96\x42\xad\x92\xa6\x1c\xaa\xe4\x4e\x83\xa5\x96\x19\x95\x2f\x49\x8c\xb6\x37\x99\xef\xa0\x47\xfc\x92\x90\xb5\xc9\xde\x29\x6c\x5e\x20\x66\xe0\xe1\x1a\x78\x35\x12\xb3\xff\xc6\x9f\x7b\x21\xd0\x39\xb8\x34\xe2\x6a\xb7\x8d\x5b\xc2\x8d\x77\x1b\x14\xce\x75\x05\x3e\x34\x89\x9e\xed\xbd\x75\xdb\xae\xa7\x22\x7e\x01\xe6\xab\xcf\x84\x10\x21\x18\xe1\x5a\x49\xd6\xa8\x5e\x55\x05\x68\x77\xd3\x7f\x67\x20\x1c\x12\x8b\xb3\x75\xa1\x64\xde\xe6\x60\x9b\xde\x73\xa5\xef\xfa\xcb\x08\xc0\xc9\x36\x35\xdf\x89\x10\xf5\x58\x37\x2c\x29\x51\xf4\x2d\x2d\xca\x28\x1d\x53\x3e\xa2\x0a\x5f\x5d\x49\xa4\xf1\xc2\xf0\x8a\x0d\x7e\x19\x0e\x34\xbc\xa9\xcc\x3e\x02\xb8\x91\xac\xb2\xdb\x16\x51\xe2\x74\xdc\x84\xa5\x0f\x8e\x81\x51\x4b\x14\x79\x42\x25\x79\xf1\xc3\x99\x2b\xef\x19\x8c\x86\xf5\xad\x7b\x77\xe8\x61\x7d\x69\x8d\x1b\xd1\xe3\x66\xec\xfc\xa8\x0c\x49\x56\xc5\x8f\x28\x7a\x23\x0c\x89\x12\xdd\x96\x23\xa2\x7d\x2a\x9b\x87\xc3\xba\x7e\x83\xc1\x1c\xf1\xbe\xdd\x60\x28\xec\x77\xdb\x81\x8c\x58\xdb\x2d\x56\x37\x03\xbc\xc9\xda\x66\x49\x37\x1a\x0c\xef\x5e\xdb\xd1\x80\x67\xbf\xe6\xb1\x38\x81\x32\x5a\x8e\xc4\x0d\x76\xd1\x92\x43\x41\xc1\xda\x31\xd8\x27\x0d\xb6\x2d\x82\xd9\x5b\x26\x88\xc8\x1c\xc4\xdf\x0b\x73\x99\x28\xa3\x82\xda\x31\xd0\x62\xf6\x43\x00\xe9\x11\x29\xbf\x74\xb7\x9d\xf5\x75\xb8\x76\x64\xaf\x6b\x95\x7d\xea\x35\x1a\x43\x34\xa7\x1e\xf6\x6c\x55\xfa\x69\xd3\xef\x23\x88\x15\xe1\x3b\x55\x28\x7e\x04\x22\x15\xde\x2b\x84\xf2\x17\x4b\x87\xfd\x2c\x64\xff\x89\x14\x7e\x0d\x1e\xaa\x9f\x2c\x47\xbb\x22\x0f\xf5\x0f\x51\xee\xb8\x9c\x3c\x09\xf9\xff\x22\x0d\x2e\xdd\x43\xf1\x43\xd5\xc3\x60\xc5\x2f\x95\xce\xe1\xe5\x4f\x5e\x8f\x6b\x2f\x18\xfa\x12\x19\xb4\x6b\x86\x16\x7a\xd2\x0c\x58\x61\xf1\x15\xda\x09\x62\x1c\x3f\x63\x18\xc5\xcf\x58\x1b\x03\xa4\xf1\x1f\x02\x4e\x6e\x72\xa1\xfe\x21\x72\x4d\xbd\x5b\xe8\xa4\x48\xac\x31\xf9\x22\x54\x3f\x59\x8e\xb6\xa9\x87\xfa\x87\xc8\x35\x04\xa8\xd0\x4e\x10\x50\x5a\xbe\x95\x63\x1d\x3a\x42\x37\x49\xf4\xd0\x81\x74\x92\x44\x9d\x62\x0f\x09\xb5\xdf\x7a\x7f\xd3\x69\x28\x7f\x89\x74\xc6\x3b\x42\xf9\x4b\x8e\x9e\xad\xbd\x50\xfd\x94\x63\xa2\xdc\x20\x14\x3f\x44\x2a\x5d\x98\xa1\x58\xd7\xd7\xd2\x65\x16\x7f\x67\xda\x09\xb7\xbe\x0b\x6a\x3d\x6e\x04\x9d\x65\x39\x79\xd2\x09\x9f\x7c\x73\x7d\x1a\x6c\x6f\xb5\x79\x83\x6e\xae\xca\x7d\xb6\x26\x3b\xfc\xe9\x75\x27\x44\x9d\xcd\xc1\xd6\x93\xc1\x56\x67\xed\x5a\x38\xa7\xda\x6e\x15\x3b\xf5\xfe\x6d\xfb\xfd\xdb\xf6\xbf\xc2\xdb\x76\x5e\xcb\x9a\xeb\x9d\xea\xef\x78\x32\xc9\xf1\x25\xfa\x99\x24\xe3\xcf\x18\x7d\xff\x0b\x9e\x4c\xec\x07\xee\x2d\x7d\x58\x01\x18\x89\x52\x74\x44\x85\x85\x08\xa0\x48\x94\xba\x60\x3f\x46\x23\x0a\xf6\x8f\x6c\x8a\x93\xa2\xc4\x49\x82\x73\xf4\xfd\x04\x12\x5d\xe0\x9f\xa2\x33\xf4\x73\x96\xc5\xe8\xfb\x69\xe5\xc3\xfb\x5d\xe5\x70\x84\x7b\xa7\x7b\x13\xa5\xd1\xd4\x7c\x0d\x3f\x18\x52\x2c\x0c\x73\x06\x30\x67\x00\xe2\xd5\xfb\xe1\x08\xe4\x3a\x1b\x98\x8c\xa2\x54\x80\xbc\x04\xa3\x62\x1b\x82\x49\x31\xc5\x10\x97\x33\x01\xf8\xe2\x59\x0d\x5c\x3c\x92\x1e\x30\x67\x75\xf5\x15\x33\x59\xdf\x5b\xf0\x95\x5c\x05\x98\xe2\x52\x00\xbe\xc3\x79\x01\x0f\x3b\xaa\xa1\x17\x1c\x44\x76\xe2\x3c\xca\xe7\x75\xdd\xa0\xf9\x12\x18\x97\x25\xc4\x91\x71\xe1\x0b\x9e\x25\x40\x05\x57\x31\x20\x05\xbb\xa0\xc2\xa0\x72\x37\x40\x12\xab\x42\x2d\xd0\x75\xb5\xd7\x02\x06\x24\xfc\xc3\x70\x33\x72\x9c\xc6\x9e\xbe\xb1\x0c\x01\xf6\x0c\x84\x3d\x17\x6a\x44\xd3\x25\x26\xf3\x6c\x81\xf3\xf2\xd2\x03\xb7\xe0\x59\x02\xf4\x55\x59\x2e\xde\xe5\xd9\x19\x89\xbd\xe4\x46\x17\xea\x82\x67\x4b\x62\x5b\x8c\x6b\x4a\x90\xc5\xd8\x2e\xd0\xce\xc7\xda\xda\x9a\x94\x85\x7f\xc6\xa3\x1d\xd4\x13\xd5\x98\x7e\x42\x73\x7b\x85\xa4\xf8\xdc\x5a\x36\xaa\xa4\xe6\x32\x94\x07\x7f\xd4\x7a\x2e\xa0\x34\x20\xcc\x2c\xef\xf1\x39\x5d\x2e\xe0\x3a\x5c\xaf\x22\x1e\xf1\xcc\x17\xcf\x9c\xbc\x62\x26\x4a\x7e\x98\xb9\x25\x53\x58\x03\x34\xf7\x2d\x2e\x9d\xdc\x85\x22\x7c\x0a\x22\xd6\x81\x03\x37\xfa\xf5\x57\xd1\x06\xa5\x6b\xb7\x0f\x8a\xc0\x01\x88\x7f\xf6\x74\x18\x45\xd9\xea\xb0\x10\x2d\x48\x28\x37\x43\xfe\x3f\x3b\x33\xe8\x9d\xe4\xd8\x2a\x8c\xa2\x3a\xf9\x84\xc6\x57\x20\x61\x34\x7a\x09\xf5\x0f\xa7\x89\x4f\x72\x0d\xb0\x1f\xce\x00\x39\x40\x4f\xb5\xcf\xc9\x99\xe0\x22\xd4\x7e\xf7\x98\x91\xc1\x75\x7f\x8f\x4a\x4c\xc3\x21\x38\x05\x2d\x30\x52\x63\xc8\xd8\x4e\x0c\x5e\x4a\xd6\x28\xb9\x79\xc6\xd7\x34\xb6\xca\x71\x51\xa1\x51\xd4\x29\x22\xfc\x61\x9d\xf2\xf4\x28\xa6\xcd\x34\xae\x17\x5e\x99\xb4\x3d\x7d\xc9\x31\x73\x5f\xaf\x7a\xf1\x19\xe3\xc5\x61\xf1\xe1\x32\x1d\x93\x74\x5a\xdb\x15\x28\x6b\xc1\xb7\xa3\x40\x4f\x47\x74\xbe\xf0\x4c\xdd\xab\x5b\x50\xc2\x28\x9f\x39\xb8\x81\x2f\x0f\x8c\x78\xc0\x27\xa0\xe0\xdb\x03\xc7\x5f\x81\x0a\x30\xfa\xe9\x40\xe9\x0f\x02\x19\xa0\x4c\xf1\xc2\x1a\x75\x8a\x04\x4f\xdb\xea\x75\x87\x68\x9e\xa7\x78\x6b\xb5\xa1\xb5\x34\x4f\xdd\x3a\x2e\x45\xed\x75\x38\x65\xa6\x57\x02\xf2\x67\xec\x1f\x99\x0e\xc5\xbf\x1d\x38\xfd\xca\x9d\x41\xca\x14\x0f\xac\x7b\x45\x25\xca\x3c\xb7\xef\x2d\x9c\x3e\x57\x95\x75\x72\x3c\xed\x1e\x3e\x3b\x78\xab\x35\x46\x3f\xe9\x9e\x63\x2f\x53\xb6\x51\xeb\xaf\x40\x99\x32\xd9\xf4\xce\x66\xaa\xf9\xe1\x36\xcb\x86\xac\xba\x76\x89\x49\x0e\x3a\xa9\x71\xb4\x00\x1b\x6e\xed\xe2\xc3\x83\xff\xc3\xe7\x07\xef\x8c\x95\x4a\xcb\xe9\x76\x36\x84\x09\x7e\x74\xb1\x51\x19\x90\xe5\x1b\x0f\xc5\x28\xc4\x80\x37\x23\xd6\x21\x38\xa3\x90\xdc\xb2\x2e\x62\xe1\x89\xe4\xb4\xb0\x33\x71\xe1\xa1\x67\xde\x55\xa5\xa0\x04\xe9\x8a\x1d\x24\xcd\x62\xdc\x0d\x0c\x88\x29\xdc\x2a\x87\xa8\x4b\x45\x84\x4f\xe3\x84\xe0\xb4\xfc\x07\x03\xef\xaa\xbb\xac\x7e\x70\x93\xd6\x70\x79\x9e\xe5\x9f\xab\x1a\x4c\x71\xf9\x89\x83\x5a\x20\xa6\xc3\xf1\xd0\x5e\x93\xb7\xec\x16\x58\x53\xe2\xe5\xbc\xaa\x5f\xb8\x9c\x7d\x82\xb9\x1e\x67\xc9\x3f\x7e\x87\xfe\x9d\xcf\x48\xb1\x90\xbe\x55\x9d\xee\x15\xb3\xd9\xad\xd1\x06\x3f\x4f\xbd\x9c\x9f\x14\xcf\xb3\x34\x65\xfe\x5e\xb4\xe5\xd6\x37\x68\xaf\xe7\xdd\xdc\x1e\x3c\xf0\x6e\x7a\x7a\x95\xbd\xbe\x7f\xbf\x61\x2f\x9c\x85\x04\x5d\x49\xf3\x60\x62\x06\x9e\xd7\xb9\xfc\xe1\x55\x9c\xd2\xba\x85\x37\x42\x5d\x9e\x67\x0a\x22\xe3\x18\xd0\x09\xb7\x37\x69\x92\x7e\x80\xe8\x84\xdb\x5b\x34\x4d\x09\xef\x9d\x70\x7b\x57\xa6\x30\x41\xa7\x13\x6e\x3f\x91\x49\xba\x28\xde\x09\x77\xb6\x65\x06\x5d\xe1\x9d\x70\x67\x47\x25\x28\x11\xbc\x13\xee\xa8\x4a\xd5\x21\xae\x13\xee\x7c\xeb\x24\xe3\x72\xd6\x09\x77\x9e\x38\xe9\x29\x2e\x3b\xe1\xce\x77\x4e\xba\x10\x5b\x3b\xe1\xee\xa6\x93\x59\xcc\x66\x9d\x70\x77\xcb\x4d\xa7\x92\x6b\x27\xdc\x55\xdd\x17\x27\x92\x4e\xb8\xfb\x8d\x4c\x34\x8f\xb9\x9d\x70\xf7\xb1\xcc\x12\x32\x46\x27\xdc\xfd\xb6\x5e\x13\x77\x7d\x1a\x6c\xef\xdc\xeb\xc9\xee\xf5\x64\x7f\x6d\x3d\x99\xe6\xfb\x36\x4a\x12\x78\x9d\x7e\x3b\x47\x90\x9a\x3e\xca\xd1\x5c\xf8\x54\x17\x22\xce\xc4\xcb\x33\x66\x18\xac\xa9\x04\xa0\x37\x02\x4e\x45\x9d\x68\x0a\xaf\xe2\xaa\x55\xbc\x7a\x95\x1f\x49\x52\xda\x4a\x88\x09\xa4\x09\x88\x73\x16\x3c\xc5\x04\x11\xbc\x88\x67\x4a\xf7\x90\x07\x49\x62\x0c\xc5\x94\x8c\xcc\x93\x50\x00\x77\x82\x01\xb2\x6c\x52\x2a\x74\x14\x66\x82\x7e\xa2\xfd\x85\x5d\x03\xd2\xff\xf4\x64\xc7\xd2\x8a\xed\x42\x4e\x0f\xeb\xe3\x04\x5b\x62\xab\x70\x28\xbc\x2f\x7f\x5d\x5d\x41\x00\x0d\x64\x3f\x1a\xa7\x89\x90\x7a\xd2\xa5\x62\x28\x38\x26\xef\x06\xa8\x5b\x66\xec\xe7\xe9\x80\xa1\x59\x0b\x98\x36\xf1\x5c\x3f\xf2\x66\x4e\x26\xa7\x60\x7f\x27\x4d\xcb\xf8\x8d\x64\xdf\x13\x75\xd7\xaa\x86\xf6\x87\x16\xdf\xd7\x88\x87\xf9\xdf\x80\x8e\xb0\xe3\x8d\x8a\xa2\xa5\x1a\x14\xb7\xa3\xea\xfd\x07\x3c\x64\x57\x78\x35\xf0\x6c\x3e\x12\xd1\x9f\xb6\xd7\x61\xdc\x13\xf6\x34\x8e\xca\x48\x8c\x80\xfe\x1e\xd0\x7f\xd0\xbe\xf6\xfb\xea\x0a\xec\xe9\x24\x40\x99\x2d\xc8\xb8\x10\x20\xfc\xeb\xea\x4a\x85\xef\x03\xe5\x20\x6d\xfa\x23\xcd\x33\x01\x4f\x36\x4f\x07\x05\xe5\x08\xd2\x47\x33\x85\x9e\x73\x09\x47\x51\x98\x3b\x5d\xbf\x78\xa6\x4b\x6f\x65\x9f\x5b\xe9\x71\xf1\xce\xbd\x36\xed\xfd\x22\x9f\xb9\xf6\x4f\x36\x4f\xb5\xf7\x1b\xeb\xd0\x7e\x1f\x7d\x01\x9b\xe9\x28\x4d\xb3\x12\x4d\x48\x1a\xb3\x7e\x91\x74\xca\x1a\x7a\x2a\x9b\x1f\x67\x69\x91\x25\x78\x70\x1e\xe5\x69\xaf\xab\x97\x60\xae\x36\x28\x2f\x4e\xb2\x69\x57\xb3\x99\xe3\x3d\xa6\xa8\x70\xdc\xb5\x60\xce\x86\xf4\xd0\x3e\x30\x77\x3d\xdf\xea\x0c\x58\xb7\x02\x93\x20\xcc\x33\x14\xd4\x28\x3c\xa5\xc1\x14\xb7\x58\x8e\x17\x78\x4c\x45\x00\xcf\x7a\x0c\xc0\x9d\xcb\x28\x1a\x7f\x96\x41\x08\xe1\x45\x33\x3f\x9b\x8a\x0b\xcf\x5e\x94\x4f\x97\x60\x52\x7e\x22\x7f\xe9\xb1\xf6\x0d\xdb\x34\x51\x23\x04\x8f\xad\x2d\xa6\x3b\x9d\xea\x39\x10\x74\xe2\xb7\xcc\xe7\xf0\x8a\x6d\xa4\xcb\x24\x71\xd0\x9d\x09\x4a\xe3\xae\xb3\xd4\x09\x58\x40\x4c\xb4\x30\x4d\x4c\x91\x0a\x98\x1c\x8c\x88\xa9\xe3\xd3\xe4\x6f\xc6\xd9\x2b\x26\x2c\x0b\x04\x5f\xa7\xc7\xac\x5e\x3f\x50\x2d\x68\xa8\x6d\x9e\xa2\xa8\x2c\xa3\xf1\xec\x63\xf6\x5c\xb8\xcf\xd1\xe7\x4a\xf8\xd4\xd1\x4f\xdb\x6a\x4e\xd9\x80\xd9\xa7\x33\x0e\x51\x74\x10\x25\x89\xdc\x48\x38\x70\xc5\x69\xc2\xe9\xa6\x3c\x5a\x78\xce\x16\xde\xc3\x05\xd0\x68\x27\xdc\x06\xb9\x9e\x2d\xf7\x4e\xb8\x0d\x52\xbb\x1e\xed\x69\x07\x80\xad\x1d\xb0\x13\xee\xee\x50\x61\x79\xf7\x5e\x58\xbe\x17\x96\xff\x63\x84\x65\x38\x75\xdf\x55\xa4\x88\xbf\x17\x59\x9a\x2f\xc6\xa6\xa0\xf9\x0b\x4b\x94\x57\x7c\x79\x9e\xd9\xb2\x2f\x4b\x93\x22\xa8\xab\x9c\xa0\x83\x35\xa4\x4b\x47\xb8\x04\x74\x7c\xaa\x14\x31\x79\x46\xc1\x43\x02\x37\xb8\x17\x8b\xe2\x58\x78\x82\xa3\x7c\x98\x17\x06\xe7\xba\xd0\x35\x9e\x60\x59\xc1\x45\x71\xec\x31\xe3\x43\x7c\xfc\xac\x50\xa9\x0c\xe8\x86\x6b\x30\x4e\x9d\x15\xc7\xb1\x4f\xd8\xf6\x0d\xbc\x60\xf1\x84\x05\x44\xe3\x88\x04\xd3\xae\xeb\x3f\x87\xf1\x76\xcd\xb7\x91\x9b\xaf\x93\x25\x7e\x8d\x6e\xba\x53\xa0\xee\x73\xd2\x98\x29\x98\x04\x6c\xa0\xd5\x8d\xf3\x3c\xe0\x22\x68\xe1\x0a\xc3\x8c\x7c\xd8\x2f\x2e\x25\x2a\x00\x8e\x1f\xdd\x31\x9d\x44\x65\x80\xe0\x75\x6c\xc5\xc3\x17\x5e\xe5\x09\xc0\x9c\xea\xe7\x82\x4a\x49\x9d\x15\xa9\xa8\x96\xca\x33\xa2\x3f\xbc\xd2\x81\x23\xf4\xd8\x05\xd6\xf9\x22\x1a\x90\xe2\x1f\x51\x42\xe2\xf7\xb8\x58\x64\x69\x81\x79\x53\xce\xa3\x1d\x67\x0c\xfe\xf6\x7a\x6c\x8d\x0d\x0e\xd3\x33\x6f\xad\x7b\x4e\xa5\xd7\x6e\xff\x2a\x2b\x67\x3e\x5f\x9c\xc1\xb2\x3d\x17\xde\x96\xfb\x32\x78\xe3\x03\xde\x07\x78\x75\xae\x27\x38\xf1\xaf\xd5\x54\xc8\x83\x0d\xf2\x8b\x12\x40\x59\x4a\x33\xc9\x06\xdf\x09\xb7\x41\x83\xc6\x57\x64\x27\xdc\x01\xeb\xb4\x56\xf1\x81\xef\x37\xfc\xfb\x0d\xff\xcf\xbb\xe1\xab\xfd\x5e\x8a\xe5\x77\xa4\x1b\x6b\xa9\xa4\xa2\x47\x9d\xdc\x02\x2b\xb8\xac\x3f\x84\xcc\x55\xf5\x68\x02\x4e\x7b\x69\xa1\x2b\xc0\xc4\x13\x0a\x0e\x7d\xa0\x1d\x42\x34\x30\xa9\x2a\x34\x82\x15\xfb\xf6\x4f\xa6\x57\xd2\x9f\xa5\xc0\x36\x6f\xbf\x6d\x64\x70\xcf\x15\xd8\x3b\x01\x25\xe5\x02\xb0\xb3\xbd\x46\xc2\x03\xac\x99\xea\x6d\x80\xfb\x08\xf5\x57\x6d\x3e\x0e\x1b\x91\x80\x97\xb3\xee\x73\xa2\x11\xf1\xe8\x3f\x34\x6f\xb1\xc8\x72\xcf\xca\x42\x03\xef\xef\xa3\xae\xd6\xa7\x2e\x7a\xf0\xc0\x70\xff\xaa\x1d\x98\x59\xb3\x86\x8f\xf0\xeb\xbe\xb5\x0d\xd7\x35\xe8\x71\x28\x8b\x7a\x90\x58\xb1\x5d\x43\x1e\xf3\x33\xeb\xd9\x19\xac\x8a\x28\x58\xe1\x69\x1a\x68\x8f\x9f\xda\x19\x42\x19\xa8\x44\xa3\xa6\xde\x11\x6a\xab\x16\xd2\xa3\x0c\x05\xc4\x5d\xcd\xb0\xa3\xb5\xf7\xf1\x44\x14\xc7\x82\x86\x0b\x75\x0c\xd7\x69\x43\xa4\x5d\xcb\x9a\x2a\xe9\x89\x91\x8a\xbf\xca\xda\x93\xbd\x3a\xae\xdf\x9c\x50\xb4\x77\x4b\xab\xcc\xbe\xae\xa2\x92\x6a\x1f\xd9\x9f\x4f\xb8\x9c\x09\x3d\xb3\xea\xa4\xf9\x6a\xbe\x51\x87\x3a\x71\xd4\x1c\x0a\x01\x4a\x47\xda\x62\x5e\x19\xb7\x68\x35\xa9\x8c\xdf\xdc\xdd\x8c\xda\xf5\x35\x2b\x6a\x04\xc3\xbb\x8b\xb9\x65\xbc\xd7\xd2\x27\x73\xce\xca\xd5\x8c\x92\xc7\x9a\x93\xe7\xaa\xae\x58\xc7\x2a\xa7\xf3\x20\x49\x6a\xa7\x0b\x80\xf8\x0d\xcf\xca\x04\xc6\x74\xa0\x0d\x1d\x5c\x9d\xda\x8c\x77\x47\xae\x52\xad\x8a\xda\xea\xc8\x4d\x3a\xda\x00\x1b\x3d\x31\xe9\x53\x5c\x16\xdc\x6c\x25\xb9\x44\x31\x5e\x24\xd9\x25\x8e\x85\x29\xdf\x28\xc9\xc6\x9f\xc7\xb3\x88\xa4\xb6\x8b\x58\xa8\xed\xc7\x2c\x17\x3d\xf2\xbc\x56\x16\x07\x56\x1f\x49\x8a\x75\x79\x2d\x55\x8b\x6b\x86\x8b\xcd\x63\x71\xab\xa1\x5e\x77\x55\xb4\xa8\x9b\x3a\x88\x96\x34\x85\xa5\x22\x5f\x08\xf5\x0a\xf1\x27\x1c\xfd\xee\x8f\x10\xdf\x78\x5f\xbc\xec\x82\xfc\xe1\x10\x9d\x47\x84\xe9\xc9\x41\xe4\x5a\x94\x4a\xf7\x2a\xae\xc8\xcc\x79\xe7\x4b\x41\x86\x9a\x55\x1d\xc3\x7d\xd3\x73\xeb\x3a\xa6\x1b\xdf\xba\xd1\xbe\xbd\x2b\x41\x7f\x37\x36\xf6\xcc\x63\xd3\x70\x88\x8a\x32\x5b\x30\x5d\x2d\x49\xa7\x28\x9a\xd0\xae\x7c\xb3\xc9\xe6\xaa\x40\xbd\x92\xcc\x71\xb6\x2c\xfb\xce\xd1\x91\x21\xe0\x07\xf4\xcd\xa6\xf7\xb0\xc8\x7a\x3f\xa0\xb5\xff\xcc\x2b\x57\x9e\xd8\xfb\xe8\xcb\xb5\xe7\x4c\x67\x23\x90\xb9\x35\xf1\x9e\x43\xe5\x8c\x78\x4f\x9b\xea\xe4\xa7\x1c\x8b\x4a\xc6\x04\x17\x25\x11\x5b\x19\x63\x4a\xd8\xe0\x64\x74\x44\x25\xe6\x65\x1a\xdb\x18\xe8\xfa\x0e\x9f\x38\xd1\x9c\xa7\xe8\x7f\x8e\x3b\xd3\x1b\xb7\x4a\x97\x9f\x5e\xb3\xf4\x40\xe0\x62\xcd\xa0\x9a\x29\x2e\x3f\xaa\xa6\xde\x33\x52\x53\x1c\x45\xeb\xc6\xab\xa8\x98\xe9\x44\x15\x08\xc2\xec\xfb\x8f\xf0\x64\xd2\xe3\x00\x7e\x6a\xf3\x16\xf2\x76\x10\x02\x9f\xf0\xba\x06\x63\x73\x01\x9a\x3d\x82\xe8\x28\xfe\xee\x88\xbf\x2a\x9f\xcf\x8f\xa5\xcf\xe7\xaa\x3f\x32\xe9\x99\x14\x77\x75\x85\xd6\xa1\xc5\xda\x62\x48\xb2\x6e\x0f\x6d\xea\x7f\x37\x59\x02\xfa\x5f\xcb\xe5\x60\x0f\x29\x8b\xb5\xe0\xb2\x3b\xb5\x33\x23\xfe\x86\x43\x79\xc1\x97\x64\x53\x8d\x6a\xe1\x58\x21\xd8\xf8\x7a\xb7\xdf\xd0\x3c\x32\x44\x35\xc9\x51\x2b\xa6\xba\x45\x65\xc3\x21\x62\x9b\x95\x10\x17\xa2\x34\x46\xfc\x66\x04\x45\xd3\x88\xa4\x7c\xe5\x9c\x63\x1e\x17\xac\xe1\xcf\x2f\x7b\xda\x1b\x60\x43\x0d\xb6\xac\xe3\x6c\xff\x0d\x43\x1a\x33\xa7\x4e\xfc\x36\x90\x6e\x09\x74\x77\x2c\xf0\x38\x4b\x63\x44\x19\x6e\x63\x25\x1a\xe9\x36\x13\x2b\x32\x38\x22\xe8\xc2\xda\x76\xd8\xeb\xf7\xe4\x8e\x3b\xa4\xfb\x7e\xd6\x44\x09\x7e\xa2\xd5\x38\x65\x51\x66\x39\x8e\xa5\x1f\x68\x26\x81\x80\xc6\x67\x1a\x15\x28\x9a\xd3\x0d\x69\xe0\xe5\xd7\xf6\x5f\x25\xff\xb6\xff\x3c\xee\xa9\xef\xa2\x8b\xf5\x3d\xbc\xae\xcc\xad\xe2\x18\x6e\x09\x1b\x52\xd3\x4e\xb6\x3d\x50\x68\x57\x0c\x82\xd0\x7f\x8c\xe8\x31\xfb\x52\x3e\xd7\xb7\xa4\x38\x0b\xac\xe1\xd0\x60\x57\xaa\x1f\x18\xe0\x54\x15\x8d\x88\x71\xb9\xc0\x5e\xfe\x60\x71\x7c\x87\xb4\x68\x44\xd0\x3e\x85\x14\x72\xd6\x43\xa6\x09\x6d\x1e\x93\x3a\x21\xa5\x28\xd2\x44\x53\x5e\x5c\xd4\x22\xc6\x96\xe2\x73\x99\x24\xc6\x94\x5e\x5e\xeb\xc4\x60\xe9\x46\xb6\x84\x31\x41\x94\xf4\x57\x2c\xba\x5d\x53\xd4\x96\x83\x0d\xc9\x82\xbb\x53\x10\x8a\xe2\xd8\x29\xed\x93\x94\x39\x84\x94\x96\xd5\xf1\x4f\x24\xc9\xb6\xd4\xc4\x43\xa1\xa1\x9a\x08\x8a\x52\xdf\xf5\x0b\x92\xf2\xb2\x3c\x17\xea\x14\xd5\x13\x33\x1b\xc8\xf9\xd4\x79\xd2\x70\xc8\x62\xac\x29\x83\x09\xa3\x52\x65\xf6\xf0\xe5\x7a\x8f\x02\x8b\x71\xaf\x9b\x6d\xf3\xa1\x6a\x15\xc3\xa9\x35\x87\x57\x30\x40\x9a\xfa\x03\x83\x84\x8c\x31\x5c\x1e\x28\xd3\x0b\x2b\x0c\x98\xcf\x0c\x04\x4c\x39\xaa\x8d\x3f\x90\x63\x00\x52\x0c\x16\xd9\xc2\x70\x32\x65\x76\x2f\x89\x8a\x92\x43\x3a\x55\xfb\xbb\xc3\xe3\x4b\xd0\x82\xe0\x91\x75\x5d\xbe\xf5\x80\x80\x94\x90\x6e\xf7\x49\xa1\xb0\xa1\x4b\x5a\xb8\xfb\x01\x8b\x53\xf0\x03\xda\x74\x63\xd3\xe7\x3a\x35\x1f\x88\xd5\xd9\x7c\xae\x17\x7f\xb7\x52\xf2\x69\x68\xb2\xd8\x1f\x57\x90\xfd\xff\xff\x9f\x34\x9b\xd3\xc7\xb5\x6e\xf6\x79\xb0\x88\x2e\xa3\x51\x82\x7d\xfd\x73\x25\x7c\x66\x0b\x55\xe0\x34\x56\xa1\x69\xd2\x2c\x7d\xc4\x2b\xd1\xf1\x61\x73\xfe\xeb\xaa\xb9\x07\x07\x5f\x94\xd9\xf9\xb5\xaa\x3d\xb1\x56\x02\x18\xb2\x56\xab\x98\x21\xb0\x63\xdb\xd8\x67\x15\xed\x99\xb3\x58\x79\xc9\xa7\x1f\x52\x8d\x63\xbd\x10\xe5\xa2\x38\xb5\x0f\xfe\x31\x46\xe7\x51\x21\x65\xc4\x35\x13\x57\x6c\x6d\xc3\x6d\xaa\x76\x2a\x51\x46\x56\xd6\x95\xea\x2c\x2a\x66\x3e\xa4\xd3\x5e\xe3\x3c\xaf\xba\x5c\xd4\x6f\x11\x7d\x57\x85\x75\x42\x0c\x95\x30\xe3\x98\xdd\x64\x69\x8c\x94\xf6\xc4\xdf\x56\xc5\x49\x0a\xed\x43\x99\x0a\x79\xaa\x52\xe8\x9b\x90\xbc\x28\xab\x65\xbe\x15\xc5\xb6\x0a\xa5\x86\x4f\x93\xe1\xbb\x51\x35\xbe\x9a\xbc\xdf\x41\xc8\x3d\x36\xf0\xa6\x79\xb6\x1a\x6b\x8b\xf2\x46\x54\xaf\x32\x74\x3f\x53\x93\x6a\x76\x06\xc4\xd5\x5f\x1c\xbb\x62\x5f\xa3\x47\xd6\x17\xcc\x46\x14\x92\xf8\xa7\x61\x53\x76\x63\x59\xca\x56\x84\x35\x29\x5c\x3d\xfb\x34\xaf\xe9\xda\x14\x6b\x26\xb2\xfe\xe1\xda\x70\x68\x6d\xc1\xc6\x9d\x8c\x8a\x7e\xa6\x69\x24\xad\xca\x7b\x6c\x63\x1e\x0e\x0d\x97\x9a\x95\x01\x68\xc7\x63\xf0\x8e\x99\xb1\xd8\x2d\x24\x9d\xd6\x88\x5b\xa6\x66\xda\x1c\x39\x9b\xc4\x6b\x97\x13\xe9\x12\x4e\x9d\x74\x83\xbe\x68\x82\x54\x5b\x21\x67\x82\xd2\x4c\xd5\x40\xd9\xdb\x22\x2a\x0a\x1c\x07\xb4\x0a\xe5\x30\x8b\x42\x14\xda\x92\x36\x79\x99\x24\x3c\x98\x01\x0b\x9d\x86\x85\xa3\xcf\x81\xa2\x69\x7f\x8a\x56\x16\xa2\x94\xd5\xbb\x52\x40\x96\x33\xcd\xbf\x1c\xc4\xaf\x80\xa8\x41\xc2\x4e\x40\x76\x29\x10\x05\x46\x78\x1c\x2d\x0b\x4c\x0f\xd7\x71\x96\x96\xe8\x3c\x4a\xc1\xcc\xa8\x58\x64\x24\x61\x17\xdc\x69\x89\xf3\x49\x34\x96\x8e\x72\x5b\x1c\xae\xdb\x1c\xa0\xed\x6d\xaa\x99\x1f\x22\xc7\xc7\xa6\x5c\xd3\xda\xda\xfc\x09\x97\xcc\x69\x2b\xdd\x1f\x03\x74\x3e\x23\xe3\x19\xd8\x01\xd0\xe5\x5d\x66\x7c\x1b\x43\x8b\x64\x59\x34\xdf\xa6\x72\x3e\xd0\x30\xbf\x8a\x79\xf8\x6d\x93\x1a\x64\xd8\xd5\x05\x55\x59\xac\x59\x80\xbc\x8d\xf0\x58\x2d\x38\x6a\x86\xc7\x37\x92\x63\xea\x64\x18\xf3\xd5\xc2\x80\x19\x97\xb7\x67\xbe\x9e\x83\x8c\xf7\x04\xdb\xe2\x46\xbc\x8a\x35\x39\xe7\x5b\xef\xc1\xb6\xe2\x55\x8a\xef\x88\xeb\xee\x7e\xca\xc6\x9b\xe1\xcf\x7d\x88\x82\x3c\xe7\x63\xaf\x25\x92\x45\xb7\x7b\xd2\xa2\xd9\x34\x7f\xe8\x84\xdf\x56\x19\x35\x4b\x23\x85\x4e\xb8\xbd\xe3\x5a\x39\xf3\x91\x77\xc2\x9d\xad\xeb\xd3\x60\xfb\xf1\xbd\x35\xd3\xbd\x35\xd3\x5f\xdb\x9a\x49\x33\x5f\xe6\x56\x8d\x77\x60\xbf\x5c\xe1\x14\x92\xdb\x4b\xb2\x37\x56\x47\x13\xc6\x55\xc3\x0a\x35\x8c\x26\xdd\xf1\xb3\x2b\x2f\xae\x05\xc2\x62\xb1\x4f\x80\xc7\x61\xf1\xda\x0f\x78\x7a\xa0\xb7\xc7\x9f\x71\x83\xf7\x3f\xd5\xd6\x2c\x2b\xf4\xbb\x23\xb7\xb9\xe7\x47\x6f\xdf\xbe\x7c\xfe\xf1\xf0\xe8\x2d\x7a\xf9\xfe\xfd\xd1\xfb\x10\x3d\x97\xca\xd3\x31\xab\x92\x1d\x9e\x63\x8c\xba\x1b\x88\xd6\x87\x36\xba\x03\x7f\x1f\x94\x5f\x97\xb6\xa3\x95\xef\xd3\xd9\x79\xbd\xa4\x84\x4a\x58\x65\xfe\x26\x84\x19\x6a\x88\x6c\x93\xda\xbe\xa9\xdc\x9a\xe3\xa2\x88\xa6\x18\xed\xa3\xf5\x75\xfe\x40\x8f\xee\xa0\xfc\xf7\x80\x05\x6c\x74\x52\x06\xa2\xd8\x53\xe4\x4d\x0e\x91\x9c\xa0\xbf\x7f\x38\x7a\x8b\xde\xbf\x7b\x4e\x01\x79\x97\x3c\x41\x0e\x79\xdf\x9c\x27\x58\x0a\x07\xbc\x6a\x73\xb4\x6a\x36\x3f\xb2\xcb\x5e\x7d\xbc\xf3\xa2\xed\x94\x7e\x3c\x7c\xf3\xf2\xe8\xf8\x63\x88\xf8\x95\x31\x25\x27\xda\xc9\x79\x81\x36\x50\x97\xfe\x17\x8d\x67\x74\x71\x76\x8d\x70\x12\xdc\x59\xe2\xb7\xf7\x1b\xc3\xfd\xc6\xf0\x9f\xb3\x31\xc0\x6b\xc5\x3f\xaa\x91\x6b\xfb\x47\xe0\xad\xde\x9e\xdf\xe1\x13\x70\xe1\xac\x87\x32\x00\x79\x10\xd2\x23\xa1\x14\x86\xc8\xcf\xdf\xa6\x42\x5b\x4a\x30\xb7\x6d\x78\xbf\xf6\xfb\xf1\x85\xb0\x84\xd5\xb4\xd6\x7a\x3e\xf3\x15\x8f\x6a\x9e\xf1\x16\x59\xda\x6f\x78\x7a\xae\x65\xa6\x59\x7a\x39\xcf\x96\xb2\x45\x99\x50\x71\x52\x12\x48\x9b\x62\x81\x2b\x16\x52\x3f\x9a\x83\x9b\x71\x27\x40\x0a\x4f\x93\x47\xa1\x67\x59\x96\x5c\x43\x74\xc3\x18\xfc\x73\xb3\x5d\x02\x33\xc8\x58\x9b\x1d\x78\x5e\x81\x63\xc3\xed\xb6\x38\x5d\x81\xbb\x70\xba\x2a\x79\xed\xc3\x35\x63\x9a\x74\x27\x53\x14\xc2\xf4\xb8\xc4\xea\xb5\x5d\xa4\x6b\xc8\x77\xef\x1f\x88\x47\x56\x20\x03\x5e\x13\x5c\x26\xf0\xdf\x15\xd6\xa2\xfe\xf2\xca\xdc\xb7\xf2\x82\x55\xc7\x36\xa3\xcf\x98\x39\xaf\x06\xf7\x3f\x16\xae\x63\xe5\xd7\xda\x1b\x9e\xc3\x5b\x41\x35\xea\xb4\xea\xea\xbc\xeb\x30\x4a\x74\x5d\x6b\xf7\x14\xbd\xb6\x1f\x1d\xac\x50\xcf\xd0\x4a\xee\x89\xbb\x66\x5c\x7a\xd1\x7a\x7a\x58\x69\x44\xc2\x07\xf8\x8d\x86\x53\x90\x69\x1a\x95\xcb\xdc\x1e\x8e\x9e\x5e\x35\x1e\x1d\xa6\x7a\x3c\x12\xaa\x6e\x40\xf0\xf0\xbf\x7d\xff\xf9\x03\x01\x41\xde\x9c\x23\x45\x69\x2c\xd5\x38\x65\x06\x31\x41\x27\x24\x8d\x12\x65\x34\x8c\xdc\xd7\x03\x3e\x9b\x4c\x7d\x61\x5b\x59\xbc\x7e\x03\x2b\x22\x0f\x9f\xe1\xfc\xb2\x9c\x31\xf5\xf0\x7c\x44\x80\x67\x64\x2c\x4a\x2b\x74\x8e\xb5\x18\xd7\xa2\xcb\xe3\x53\x83\x77\xc7\xf1\x09\x27\x57\xb7\xfc\xa5\x3d\xa2\xbb\xf7\xbc\xa1\xfc\x5c\x48\xc7\x16\x4d\x2e\x39\x84\x12\x71\xdd\xda\x7a\xdc\x7e\xf2\xca\xd9\xc3\x50\xee\x95\x8f\xf9\xa3\x14\xe4\xde\xeb\xbb\xfa\x43\x3e\x4f\x1f\x45\xc7\x6e\xcb\xd3\xb5\x40\x79\xb5\x0c\x1d\x1c\x0c\xf3\x60\xb6\xbc\xfc\x09\x81\xa0\x2e\xd6\xd5\xfb\x99\x1b\xde\x9e\xd2\x8d\x4a\x4e\x97\x49\x52\xf1\x42\x44\xa9\xf1\x90\x71\xfd\x66\x34\x60\x6a\x61\xa1\xde\xaa\xc8\x68\x90\x69\x8d\xea\xac\xe6\x8e\x9d\xcf\x82\xf3\xc6\xa4\xc7\xf6\xb1\x00\x9d\xd9\xd7\xd5\x7d\x5f\x77\x5b\xd7\x06\x7d\x6f\xa0\x3c\x93\x58\xc6\x59\x3a\x8e\xca\x9e\x41\x05\xfd\x6a\x47\x30\x95\xec\x8f\x7b\x81\xa9\x66\x7f\xf6\xb6\x8b\xab\x38\x5d\xcc\x14\xfe\x2e\x2f\xe3\xdc\x81\x1b\xe0\xc0\x59\x81\xd5\x12\xcb\x66\x1f\x3c\x00\xcd\x83\xd9\x8b\xfa\xfd\xba\xc6\x7b\x0d\x20\xe1\x0e\xfd\xd7\x44\xf9\xd4\x5a\x66\x4a\x90\x7c\x6a\x94\x0c\xf5\x2f\xee\xdb\x66\x4b\xf3\x25\xc2\x07\xc8\x6f\x3d\x64\xbd\xf6\x93\x27\x36\x9b\xe8\x8b\x94\xd7\xf4\xfa\xb6\xfb\x7b\x74\x89\xfe\x92\x91\xb4\xd7\xe9\xb8\x95\xcb\xd7\x65\x8c\xde\x18\xa2\xf4\x4b\x05\x90\x12\x7b\x74\xbd\xf7\x03\xbd\x47\xfd\x3d\xa4\xc6\x9c\x66\xe5\xa1\xd1\x59\x89\x43\x8f\xcb\x1e\x05\xdc\xb2\x71\xb0\xff\xef\x07\x56\x2b\xbc\x46\x77\x5b\xd1\x78\x78\xb6\x2c\x17\xcb\xf2\x75\x36\x55\xcc\x3b\x56\x45\x85\xb2\x88\x1f\x5f\x98\xbb\x16\x4d\x4a\x33\xc1\x14\xef\x86\x71\xd9\xde\x94\x18\x0c\xbb\x60\x32\xb8\x6b\x8e\xe3\xe5\x18\x6b\x13\x16\x8d\xc7\x01\xe2\x2e\x1d\x75\xae\x12\x8d\xc7\x27\x3c\x99\x71\x48\x8a\x18\xfe\x2d\x68\xfd\xa9\x39\x6f\x83\x62\x46\x26\x65\xaf\x8f\x42\x07\xab\x22\xcb\x51\x62\x45\xe3\xb1\xd0\x5a\x31\xd3\x69\x46\xdf\x38\xc1\x25\x16\xe3\x50\xbe\x86\xcc\x74\x46\x5a\x37\x60\x1c\xda\xd5\x11\x7f\xa5\xc1\x17\x38\xdd\xf8\x99\x54\x57\xe9\xa6\xe0\xae\xa4\x24\xa3\xe1\x7a\x51\xc8\xe3\x06\xc1\x96\x85\xfe\xe8\x8e\x8d\xb6\x9b\x1d\x1b\xd5\x15\xdf\xaa\xb6\x6f\x33\x2b\x40\x86\x3c\x68\x78\x52\xd0\xee\x92\xd5\x2d\xad\xe5\x41\xc9\x11\x31\xff\x18\xae\x94\x2a\x09\x59\xb7\xa2\x6f\xf1\x3e\xd0\x7a\x20\xe6\x7d\x1c\x58\x4b\x8a\x5f\xcb\x6f\x13\x05\x35\x4f\xb1\x55\xec\x4f\xd8\xf5\x41\x4b\x27\x1a\xc0\xa9\x41\xba\x3e\x00\xdd\x15\x94\xa2\x05\x2f\xe8\x89\xe4\xf7\xac\xed\xd3\xca\x01\x18\xd6\x0a\xde\xbb\x58\x03\x97\x9a\x73\xa9\xba\xab\xd8\x26\x97\x53\x37\xf4\x32\xf5\xa4\x8d\x36\xfe\xb6\xfe\x22\x87\x7e\x3d\xe5\x1b\x46\x83\x1e\x5d\x60\x7d\xec\x0c\x3d\x6c\xc6\xda\x70\x88\x3e\x1e\xbd\x38\x0a\x51\x8e\x99\x21\x54\x80\x8a\x8c\x9b\xac\xc8\x1b\x2e\x65\x03\x13\x31\xad\xd7\x80\x96\x83\x60\xdc\x29\x1e\xe3\xa2\x88\xf2\x4b\xba\x58\x20\xfe\x6c\x41\xc9\xad\x0b\x4e\x7f\xc1\xe5\x32\x3a\xcf\xf2\xcf\x4c\xd0\x9b\x2f\x93\x92\x2c\x12\x2d\x78\x81\x19\x2e\xc4\xef\x29\x68\xf8\x10\x79\x5f\x2e\x7d\x23\xec\xaa\x59\x1d\xa6\xf9\x80\x68\xde\xb0\xdd\x54\x8d\xe1\x98\xed\x1a\xe6\x21\x45\x96\x1a\x08\x1c\xf9\x7c\xc1\xac\xd3\xce\x9d\xb8\xb0\xa7\xbe\x23\x44\x15\xac\xc5\x4b\x91\x63\x57\x68\xf6\x93\xbb\x46\xf2\xd5\xd4\x60\x7e\xe8\xad\xa7\xd2\x70\x59\xd5\xcf\x09\xde\x1e\x93\x03\xe0\x39\x7d\xb3\x1c\x1f\x36\x58\x8e\x64\x7a\xdc\x94\xc6\xec\xa2\xc7\xe2\x92\x17\x2b\x70\x69\x05\x47\xf1\xb9\x8b\xaa\x3d\x8b\xd5\x4f\x37\xc1\x35\xe3\x55\x30\x9e\x21\x57\xd1\x0b\x52\x71\x3d\x2e\x57\x1e\xb6\x2c\x78\x07\x03\x47\x9a\xbd\x26\xbe\x18\x18\xec\x48\x7d\xec\x21\x01\x20\xb8\x10\xfc\xbf\x27\x52\x25\xcb\x61\x3f\x64\xba\xc6\x68\xc4\x4f\x53\x88\xc4\x17\xfc\xa5\xb4\xcb\xcd\x19\x1a\x94\x93\x9f\x0a\xfe\x5c\xc1\x91\x3b\xe1\x0e\x38\x03\xd2\x3d\x6f\x53\xc6\xfc\xdd\xfd\x35\xe9\xfd\x35\xe9\x5f\xfc\x9a\x94\x5d\x91\xf2\xd7\xb3\xff\x11\x21\xe5\xee\xd4\xeb\x36\x1c\x02\x1e\xa2\xe7\x59\x7a\x86\x29\x2b\x8a\x78\xc0\x5c\x38\x04\xc3\x59\x00\xcd\xf0\x85\x0c\xc2\x4d\x09\x38\x4a\x8a\x0c\x45\x49\x92\x9d\x17\x70\x4c\x62\xba\xba\x62\xb0\x46\x2b\x12\x82\xff\x1b\x72\x81\xe3\x6b\x96\xb5\xe6\xde\x71\xac\xc9\xb8\xf0\xc2\x09\xb2\x64\x54\x4c\xf3\x27\x4f\x9b\x3d\x53\x3b\x8a\xae\xae\x44\x38\x63\x95\xd1\x95\xea\xd4\x6e\xdf\xd6\x04\xb0\x83\x1c\x17\x91\x98\x8e\x96\xf5\xa1\x27\x54\x8c\x46\x43\x4c\x09\x71\x34\x01\xad\x73\x1f\x6a\xdf\x74\xea\x04\x48\xce\xf7\xf5\xc7\xa1\xc6\xfd\x91\x88\x1b\x24\xdb\x81\x23\x17\x15\x35\x29\xa7\x15\x17\x41\xb6\x05\x6a\x26\x55\xfd\xfc\xb0\x15\x40\x2c\x7f\x9c\x93\x09\xb8\xc8\xc8\xf1\x38\xa2\x1c\x47\x8b\xf6\xf2\xe0\x01\x4a\xa2\x5f\x2f\x51\x92\x45\x31\x8a\x2f\xd3\x68\x4e\xc6\x28\x4b\x71\x01\xad\xf1\x09\x51\x0d\xf1\x50\xc8\x99\x54\x12\x00\x94\xb0\x6b\x17\x8d\x3b\x50\x74\xb6\xa6\xb8\x3c\x92\x27\x64\xaf\x57\x72\x47\x45\xc0\x71\x2d\x40\xaa\x6f\x36\x0c\x6d\x7e\xe5\xf5\x0a\x93\x85\x87\x5c\xc8\x5e\xe6\x98\x2b\x02\x03\xb8\x74\x1b\x33\x2a\x66\x87\xd8\x19\xbe\xd0\xeb\x52\x7a\x4d\x2b\x41\x73\xba\xd8\x00\x6a\xe8\x24\x99\x4a\xd2\x35\x77\xb2\x74\xf0\x0c\xaa\x8f\x9e\xb2\x8e\x42\x15\x9c\xe8\xfb\x28\x14\xf4\xcf\xc1\x1c\x75\x37\x5b\x33\xb2\x09\xd6\x8d\xd0\xea\x96\xf2\x29\x42\x49\x4e\xe5\xd2\xaf\x40\x2b\xab\x97\x53\xe9\x91\x88\xdd\x2b\x32\x23\x3d\x72\xaf\xa8\x15\xae\x88\xc2\x95\x6e\x8e\x06\xb2\x5c\x5f\xef\xc5\x4d\x6a\xe2\xa5\xfa\x42\x80\x13\xca\x8b\x83\x38\x66\x06\xfa\x52\xa7\x14\xa5\x31\x2a\x70\x59\xa0\xe5\x02\x32\xb8\x7c\x0e\x8b\x88\x94\x38\xa7\xdc\x34\x3b\xe3\xe2\x07\xf7\x91\x39\x58\x5b\xd3\x8c\xf4\x5f\x67\xd3\xe2\xa0\xfc\x50\x46\x79\xb9\x66\x2b\xde\x0a\x9c\x4c\x64\x22\x25\x04\x52\x66\xa9\xe4\x65\x66\x61\x23\xea\x14\x4e\x26\x8e\x5f\x18\xf1\xca\x6b\x8a\x4b\xa6\xd1\xa1\x85\xad\xa7\x5e\x70\xd0\x56\xa3\x2b\xa0\x57\x62\x89\xad\x5b\x6b\x8c\xb6\x32\xf0\x2d\x34\xc8\x98\xe2\xb2\x67\x3d\x3a\xe1\xf6\x7d\x8e\xb8\x3f\x1c\xa2\x38\x4b\xbb\xfc\x99\x22\xed\x23\xc7\x16\x18\x13\xc2\xed\xaf\x48\x14\xb6\x38\xe0\x5d\x61\x30\x18\xa0\x5f\x96\xcc\xb9\x2c\x6d\x93\x32\x21\xe7\xe0\x58\xf1\x32\xaf\xe6\x55\xde\xb5\xfd\x04\xd3\x5a\x62\x72\x18\xfe\xc3\x16\xcb\xf4\x9e\xd0\x98\x79\x63\xd3\x3b\x41\xf6\x7a\xc4\x34\x86\x34\xfa\xd7\xec\xdb\xf3\xeb\x51\xec\x22\x4b\x12\x46\x3e\x7e\x6a\xe5\xb4\xa9\xc0\x6c\xba\x94\x5c\x1b\x14\x97\xe9\x1b\x69\x9c\x6a\x10\x4b\x56\x41\x2e\x7c\x46\x33\x67\x4e\x85\xe5\x01\x25\x3d\x31\x56\xdf\x24\xf8\xde\xed\xf8\x68\x22\x6b\x7d\xa4\x6d\x4b\x1d\x37\xa3\x0c\x65\xbc\x0b\x43\x53\xea\xdb\xa7\x56\x82\xaa\x24\x14\x85\x5c\xd2\xb9\x15\x7a\x6e\x47\xa4\x95\x07\x63\xe8\x93\xed\xe0\x98\x32\x9e\x77\x59\x92\x50\x3e\xa3\x7a\xc2\x68\x30\x64\x45\x88\x88\x5f\x0f\x67\xaf\x01\xa5\x38\x18\x9a\x62\xfe\x0b\x6e\x70\x7e\xc2\x30\x05\xe4\x78\x18\x9f\x06\xe2\xa2\xc6\x48\x0e\x14\x31\xf2\x1c\xdd\x35\x0e\xd3\x94\x02\xfd\xd2\xdd\x52\xc4\xc0\x73\x48\xdc\xb5\xd2\x93\x37\x1b\x72\xa1\x79\x14\xe4\x01\x3f\x50\x3c\xcf\x31\x8c\x06\xec\x97\x9f\x7b\xde\xd8\x01\x9b\x63\x4a\x5c\xed\xd6\xd1\xc0\x84\xa6\xe4\x5a\x45\x5b\x54\xa9\xee\xa9\xd4\x77\xf8\xf5\x4a\x66\x67\x34\x11\x03\x55\xfa\x1f\xaf\x34\x4e\x54\x62\x99\x92\x04\xad\x67\xbb\x00\xe7\x20\xc0\x4c\xd0\x20\xc5\x6c\xbb\xef\x94\xe4\xaa\xe0\x16\x26\x32\x83\x6f\xb3\xcf\xab\xf2\x15\xab\x73\xb2\xf4\xcb\x16\xf9\xbb\xb2\xdf\x83\x14\x9f\xeb\x77\x2d\xce\x3b\x74\xc1\x18\x49\x6c\xb8\x58\xf3\x33\xc4\x86\xa5\xde\x1b\x8f\x3c\xae\xab\xc6\xa3\x46\xde\x87\xa4\xef\x28\x2f\xb1\x3a\xe5\xbd\xfa\x51\x63\xe5\xd1\xf9\x8a\x5d\xd7\x6f\xfa\x53\xfe\x1c\x83\x32\x9c\xca\xdc\x0b\x9c\xc6\x60\x93\x25\xe7\x23\x2a\x40\x21\x90\x16\x94\x8c\xa4\xfb\x0f\x55\x51\x36\x01\x60\x5a\x88\x0a\x25\x7d\xa6\x04\x90\xad\x2f\xd3\xa8\x28\xc8\x34\xc5\xf1\xc0\xed\xa3\x3d\xf9\x3e\x96\xe9\x43\xa4\x14\x81\xc6\xa3\x06\x5c\x7a\x9b\xd1\xad\x9c\xb4\x91\x28\x1b\x58\x94\xe8\xc2\x5b\x94\xe4\x38\x8a\x2f\xd5\x7b\x66\x25\xc7\x15\xb7\x27\x0a\x53\xce\x14\xc2\x65\xd3\xb8\xc8\xa4\x67\xb5\x26\xdd\x7e\x6d\xba\x4e\x98\xd4\x22\x62\x4c\xd6\xe7\x09\x90\x0a\xb9\x65\xc6\xc7\x46\xe6\x73\x1c\x93\xa8\xc4\xc9\xa5\xdd\x2c\x3f\xae\xab\x7b\x5f\xc3\xbf\x6a\x8d\xc9\x11\xf4\x17\xaa\xef\x55\x78\x22\xf0\x39\x2a\xd2\x8f\x4e\x8c\x2f\xd3\xdd\x81\x0d\x46\xbb\x73\x3c\x77\x02\x33\xd8\x7b\xad\xc9\x86\x98\x2d\x9a\xd6\x0f\xa1\x36\x30\xf8\x98\x3e\x1a\x6b\x9e\xf8\xb5\x9e\x3b\x10\x0d\xd7\xda\xdd\xe5\x75\xdb\x81\xe8\xdb\x62\xf3\x78\x9c\x8d\x3d\x5b\x88\x7d\xdd\x1c\x48\x03\x2b\x86\xa7\xc7\xf3\xec\x4c\xa8\xde\x50\x54\x5c\xa6\x63\x79\x36\xf1\xc9\x2d\x3e\x16\xbb\x4c\xe1\x6d\xad\x81\x00\x4d\x04\xb0\xb0\xe5\xf0\x2e\xdd\x78\x7b\x95\x9a\x0d\xb9\xdc\xc1\xe8\xd4\x8a\xa6\xed\x7b\x5c\xef\x6c\xfc\x5e\xbb\x08\x59\xd2\x96\x99\xad\xcd\xaf\xc2\xf4\x6f\x38\x44\x87\x13\xc5\x19\x49\x21\x1f\xa3\x5d\x62\xee\x9e\x03\x91\x12\x29\xc7\x4c\xaa\xdc\xf9\x0c\x83\xb5\x00\x1f\x7d\x1f\x31\xa6\x5a\x20\x52\x9a\x6c\xd5\xbb\xa7\x3a\xc4\x2e\x97\x99\x6f\xf7\xf0\xa1\x9f\xd7\x68\x4f\xa8\xbe\x75\x42\x50\x0c\x0f\x7f\xfb\x8a\xfe\x5b\x2c\x71\x39\xc7\xb6\x9d\x59\x92\x4d\xab\xda\x45\x16\x63\xaa\x11\xfd\xa1\x96\x90\xee\x09\x15\x1e\xd8\xfc\x31\x2a\x4c\x10\x47\x3e\xb7\x07\xd6\x9e\x8e\x1c\x37\x44\x5c\x4e\x3e\x7c\xc1\x12\x42\x4e\x63\xbd\xfe\x80\xed\xc8\xe3\x48\xf8\xa8\x03\xb7\x1b\x38\x46\x74\x75\xcf\xf2\x2c\xcd\x96\x85\x74\x58\xc7\x2f\xb0\xe9\x6e\x6f\x7b\xaa\x61\xd5\x70\x89\xb4\xeb\xb5\x04\x05\xa7\x03\x99\x32\x25\x6b\x43\x40\xae\xa1\x17\xad\xa1\x79\x0e\x6f\x31\x6f\xd7\x0d\xfc\xd8\xb9\xca\x63\xb8\x75\xc2\x7d\xd5\x5c\xe4\x5d\x9f\x06\x3b\x9b\xf7\x57\x75\xf7\x57\x75\x7f\xed\xab\x3a\xf5\xa0\x51\x53\xfe\xde\xe4\x55\x23\x07\x5e\xe1\x8e\xcd\x17\xe0\xab\xf5\x43\xc8\x74\x42\xa6\x5e\x38\x96\x25\x00\x0f\x59\xe4\x7e\xed\x42\x8e\x8c\xa2\xd4\x13\x8c\x03\xd4\xbb\x2c\x9a\x10\x33\xdd\x65\xd7\x6c\x23\x32\xe5\x4f\xeb\x2d\xfb\x3a\x06\xf4\x8c\x4c\x2d\xf5\xb8\x6e\x67\xc7\x54\xc0\x57\x0c\xe2\x4a\xc2\x5e\x9b\x6e\x8c\x54\xba\x6e\x20\x0a\x8a\xbf\x8a\x36\x0c\x39\x88\xf5\xce\xfb\x58\xaa\xcc\x64\x59\x01\xb6\x27\xb5\x32\xa4\x78\x97\x63\x7e\x41\xa7\xe9\xf9\x8d\xba\x47\x2a\xdd\x6a\x60\xa4\x97\xa0\x47\x07\xee\xe2\x1c\x5d\x5d\xb9\x79\xfc\x34\xea\xcf\xc4\x51\x9e\x10\x5a\x54\xeb\x5a\xba\x58\x96\x2f\xf0\x24\x5a\x26\xde\x2b\x88\xa6\x3e\xd2\x3d\xd8\x6e\x47\x5e\x46\x7a\x43\x74\x50\x92\x19\xc4\x5a\x8b\x1e\x6f\x44\xd5\x37\x22\x7a\x17\xac\x51\xfc\x16\xdd\xb7\x9f\x1d\x31\x91\x84\xd6\x52\x31\xc7\x46\xa3\x9e\x0a\xb5\x6c\x0f\x1e\x04\x6d\xbd\xc2\x17\x9e\x91\xf3\x55\xc5\x06\x5b\x68\xe6\x7a\xd9\x04\x45\x86\xb3\xb8\x28\x8d\xc5\xdd\x60\x01\x57\x17\xec\xc6\x9a\xae\xbb\x57\x2f\xff\x69\x2d\x37\xa8\x83\x4a\xc2\xde\x85\x26\x94\xeb\x86\x1f\x55\xc7\x1e\x5b\x5c\xde\x0a\xf5\xbb\x5b\xa7\xf7\x02\xf5\x8b\x71\xfb\x09\x17\x68\xda\x25\x24\x7c\x5e\x5d\x59\x34\x74\x30\x06\xdf\xfa\x9a\x2b\x2c\x1d\xde\xe3\x83\x49\x54\x0b\x7d\xe2\x8e\x89\xfc\x97\x77\xa6\xe4\xa3\x57\x5d\x66\x3c\x1a\x30\x29\xd1\x9c\x4c\x67\x4c\x54\x94\x1e\x6a\xb9\x22\xca\x69\xb9\xcc\x1a\xdb\x2d\x33\xb3\xd5\x93\xee\x34\x2a\xde\xe5\x64\x8c\xbb\x01\xa2\xbf\xe9\x7f\x30\x7d\xf4\x47\x9a\xa5\x63\xec\x7b\x42\xf7\x19\x5f\xd6\x3c\xa2\xfb\x8c\x2f\xdb\x3e\xa3\x83\x9a\x1c\x1c\xb2\x1a\xf6\xb5\x9b\xfe\x17\x78\x4c\xe6\x51\xd2\xd3\x01\x2a\xee\x73\xe5\x85\xf7\xd7\x26\x62\xcd\xb9\xe2\x5d\xd3\xb2\xaf\xea\xbb\x27\xe9\x9b\x52\xed\x3d\xbd\xfe\x96\xf4\xca\x85\x18\x87\x60\xe1\x02\x53\x04\x78\xe1\xd4\xea\x15\x6d\x5a\xd3\xe9\x85\x29\xce\xf0\xf4\x35\x43\x86\x69\xa4\xcc\xf2\xa2\xff\x45\xea\xee\x2e\x06\xfa\xf6\xb7\x2e\xce\xcf\x4a\x67\x65\x02\x48\xbf\x0b\x99\xc0\x9f\x09\x20\x5f\x13\xd0\x74\x0d\x17\xf0\x68\xc9\x5f\xbd\x03\xe5\x6d\xc3\x86\x12\xea\xb9\x8b\x01\x90\x94\xbf\x10\x64\x29\xc8\x69\x54\xf8\xe1\xa6\x51\x61\x40\x01\xf9\x6a\xa0\x4a\xb4\xd3\xf2\x55\x09\x61\x56\xe5\x05\xd7\xdf\x71\x8a\xb3\xf3\xc5\xca\xa4\x24\x42\x89\xdc\x84\xa4\x78\x54\x93\x5a\xca\x92\x41\x76\x56\x21\x2f\xbb\x62\xeb\xda\x51\x8f\x8e\xa2\xa2\xa1\x34\xd0\x9b\x0f\xca\x9d\x33\x0f\x94\xa2\x3c\x91\xd9\x82\xfc\x2a\x41\xab\x9b\xac\x20\x44\x19\xb4\x63\x39\x5f\x26\x51\x49\xce\xf0\x4f\x51\x71\x5c\xc0\x73\xa9\xaa\xaa\x1c\x58\xab\xae\x69\x63\x0d\x53\x59\x4e\x0c\xde\xbc\xfb\x17\x70\x49\x36\xb5\x4d\xcf\x54\x86\x16\x72\xc4\x51\x26\x81\x46\xc8\xab\x4a\xf2\x3c\xba\xa4\xb0\x4d\x7a\x23\xde\x52\xab\x05\x00\xb3\xdb\x9e\xe4\x41\xea\xae\xa5\x72\xa8\xb0\x05\x8d\x9b\x35\xe9\x06\x23\x50\x83\xb4\x18\x19\x0e\x91\x74\x40\x03\xae\xd8\xf8\xe9\x15\x21\xd6\x14\x9d\x9f\xd7\x64\x4e\x4a\xcf\x14\x9a\x00\x1c\x57\x32\xb1\x62\xde\x8d\x7c\xa3\x4c\x41\x7e\xf5\x31\x41\x95\x69\x40\x97\x64\x8e\x8b\x32\x9a\x2f\x2a\x8b\x48\x08\xb5\xae\x58\x46\x5a\xb5\x72\x8d\xec\xaa\x6a\xe5\xd1\x58\xeb\x4c\x4c\x26\x13\x32\x5e\x26\xf0\x70\xc0\xe5\xa1\x36\x90\x39\x90\xac\x8c\x92\x17\x6d\x2a\xb0\x20\x75\x21\xc9\x5c\x33\x1c\x5c\x2d\x73\x73\xe5\xb8\xd9\xae\x08\x42\x4a\x3c\xef\xdb\x4f\x86\x1c\x73\x35\x80\x72\x6f\x1e\x8d\xf5\xe5\xdb\xcc\x59\xc1\xa6\x85\x36\x62\x47\xeb\x16\xcb\x2c\xc9\xa6\xde\xf5\xa4\xaf\x6d\xdf\x6a\x4a\xb2\xa9\xe6\xfa\xc4\x59\x52\x50\xaf\xb1\xac\xf4\x0a\xf5\x45\xa5\xe9\xab\xc9\x84\x7e\x35\xec\x11\x36\x84\x4b\x6c\x16\x84\xa2\x61\x9a\xd1\x62\x5f\xf0\x82\xf9\x9b\xa9\xd8\x0f\x78\x5b\x49\x36\xad\x6b\x43\x66\xfb\xeb\x16\xd9\x96\x3c\x0a\xea\xf9\xe6\xb3\xd3\xf9\x8c\x14\x94\x65\x2e\xb2\xa2\xbc\xc1\xe1\xe9\x5d\x56\xd4\xcb\x0c\x6e\x24\x95\x5a\xd6\xea\x56\xaa\xd3\x00\xed\xa4\xce\x57\xe9\xf7\x60\x11\x5d\x82\x4d\xf7\xbe\xa1\x08\xd1\xb3\x38\xb6\x21\xa9\x2c\x13\xaf\x84\x2f\x32\x75\xd8\xf3\x2c\xff\xfc\x31\x7b\x97\x67\x67\xb8\xba\x8c\x06\xa4\x97\x5d\xe4\x24\xcb\x89\xc6\x6c\x9c\x82\x02\x42\xf3\x26\x3e\xd1\xe3\xc7\x18\xc6\xab\x8c\xeb\xb0\x4e\x32\x27\x0f\x3a\xbb\xd1\xd2\xd1\xbe\xf1\xf5\x14\x9d\x68\x9f\xa7\x28\x94\x57\xd3\xd7\xaa\x55\xa6\x65\x65\x0a\xd7\x24\xc9\xce\xc1\xa4\x5d\x9c\x70\xeb\xaa\xaf\x37\xf5\x66\x11\xd0\x28\x31\xa1\x2c\x4d\x2e\x99\x0f\xf8\x52\x5a\x9c\x6b\xb4\xca\x8a\x7a\x1f\x15\x08\x93\x70\x14\xda\x0f\x06\xea\x8c\xc1\x69\x1f\x5b\xb1\x35\xa9\xf1\x07\xfa\xe7\x86\x81\x5e\x46\xd7\x44\xe9\x7e\xb2\x36\x35\xc7\xf5\x84\xcd\xe9\x1a\xf0\x8b\x2f\x16\x24\xbf\xf4\xac\x78\x2d\x57\x27\xb7\x82\xf9\xad\xf0\x42\xd3\xbc\xaa\x25\x60\x81\x7a\x16\x00\x50\xb6\x4f\xcc\xb0\x20\xfa\x7b\xbe\x55\xf9\x3e\x3a\x17\x24\xc3\x53\xbc\x60\x5a\xf5\x07\xc5\x98\x10\x7b\xf9\x8a\x32\xfa\x46\xfc\xf7\x82\x23\x4e\xc2\xa9\xa0\x0b\x6a\x55\xa8\x06\xc0\xb1\x2b\x44\x3e\xf2\x31\x87\xe1\x70\x95\x15\x01\x6b\x53\x5f\x8d\x95\x8b\x51\x2d\xb7\x5b\xac\x24\x4b\xd1\xcb\x50\xd4\x8e\xfe\x25\x53\xb5\x75\x33\xbe\x08\x09\xba\xe9\x06\x61\x17\x35\x29\x3e\x87\x3b\x9b\x9e\x19\x12\x17\x94\xd9\xa3\x28\x1d\x90\xe2\x1f\x51\x42\xe2\x1e\xf8\xb7\xe7\x29\x2f\x48\x8e\xc7\x65\xcf\xa7\xc9\xe6\x1e\x8d\x00\x90\xd7\xd8\xeb\x3b\x6a\x72\x5d\x06\x52\x61\x47\x44\x0f\x3c\xd5\x1a\xce\xb3\x3c\x15\xb5\xa8\x82\xf7\xcc\xac\x89\xdf\xc8\x5a\x76\x01\xdc\xc1\xb1\x80\xed\x8a\xd0\xbe\x6a\xa1\x7f\xb8\x4c\xc7\x24\xf5\x0b\x32\xdc\xa3\xb0\xd8\xc6\xb8\x3b\x15\x30\x14\x23\xe9\x14\x4e\x15\xde\xc3\x9c\x0b\x66\x3a\xa5\xe1\xfe\x61\x1a\x2a\xd0\xa1\xcc\xf2\x33\x32\x9d\xe1\xa2\xa9\xbc\x0e\xa5\xd1\x02\xcf\xfd\x9c\x66\xe7\xe9\x87\x32\x2a\xed\xc8\xeb\x76\x6e\x75\x03\x7a\x15\x7b\x76\x0d\x8b\x65\x92\xe0\xb8\xa9\x0a\x1d\xaa\xe2\x7c\xa9\xfc\xd4\x54\x78\x01\x6f\xba\xf2\x0a\x1b\x21\x02\x55\x4f\x4d\x05\x0d\x25\x8d\xdb\x90\xd0\x93\xa6\xc1\xfa\x8e\x01\x61\x75\x96\x56\xd2\xe6\x0d\xa1\x3f\x59\x2b\x61\xec\x64\xa1\x27\x8d\xc1\x56\x5d\x98\x86\x95\x39\x7a\x39\xff\x80\xaa\xf3\x2a\xca\xda\x1a\x28\x4f\x15\x36\x88\xd1\x7b\xe3\x9c\x1f\x7a\x53\x75\x78\xfd\x00\x13\x7a\xd2\x74\x58\x0b\x8d\x9e\x44\x1d\xda\xe6\x2a\x61\x45\x3a\xe3\x46\x86\x5d\x0d\xbb\x3a\xe8\x84\x5b\x4f\xaa\xbc\x9a\x50\x96\xdc\x09\x77\x76\xae\x4f\x83\x9d\xad\x7b\x33\x9b\x7b\x33\x9b\xff\x18\x33\x1b\x4e\xe9\x77\x11\x52\x62\x35\x9f\xe0\x2d\x6d\x6b\xee\xd0\x75\x78\x7b\x67\xdf\x51\x92\x0c\xad\xd0\x73\xf0\x60\x91\x0f\x44\xc5\xbc\x72\x5c\x80\x0b\xb3\x5e\x37\x46\x4f\x8d\xeb\x6f\x5f\x90\x9e\x4f\x6c\xeb\xe2\xae\xa9\xf5\xf0\x94\xab\xbb\x8d\x56\x95\x72\xde\xaf\xd7\xca\x92\x6e\x57\x2d\x44\xb6\x8a\xe0\x10\x06\x75\x8a\x6f\x1d\x46\x44\xaf\xe4\x20\xfc\x53\x87\xb8\x13\xb7\xe5\x94\xfb\xdb\x93\x61\x78\xb4\x83\xfb\xdc\xe7\xca\xc1\x9c\x76\x0e\xc8\xa7\x85\x6e\xf5\x72\x83\xa0\xac\x42\x22\x57\x01\xac\xe0\x25\x3a\x70\x72\xf6\xec\x23\x9f\x16\xcc\x37\xf9\x3a\x17\xcd\xda\x75\x58\x17\xb5\x6a\x3b\xad\x77\xef\x07\x87\x94\x44\x8e\x1e\x8e\x8b\x3f\x02\x73\x07\xe7\x1f\x9b\xfd\x2a\xbb\x46\x08\xec\x29\x3c\xb4\x44\x44\x93\xdf\x44\x71\xa0\x85\x9b\x7e\x2d\x84\xaf\xe9\x08\x3c\x7b\xc7\xc0\x3c\x41\xb7\x9c\x25\xac\xbc\xa6\x56\xf8\x50\x8c\x12\xd9\xaa\x32\x7c\x18\xd6\x4d\x99\x6c\xbf\x72\xa2\x9c\x30\x6d\xab\x4f\xdf\xaa\xb3\x67\x46\x57\xab\x08\xdf\xc6\x8e\x7c\x86\x69\x83\xc7\x3f\xb3\x11\x4e\x6d\x1f\x55\xf8\x5e\xf6\xb9\xee\xe5\xa6\xda\xda\x88\x8c\x38\x6d\x35\xc6\x04\x15\xc1\x05\x04\x76\x6f\xe6\x44\xdd\x5b\xbc\x76\x6a\x6f\xe4\x4a\x9d\xbb\x7e\xdd\x0c\xd0\x13\x71\x58\xae\x69\x62\x99\x2e\xa2\xf1\xe7\x23\xa6\xa6\x33\xcc\x65\x20\x49\x5f\xeb\xeb\x66\x92\xea\x82\xe9\x28\x43\x54\xc5\x7e\x48\xea\xda\x47\xdb\xe8\xa9\x48\x14\xde\x69\x91\x10\xab\xd5\x53\x55\xe9\x4f\xb6\xca\x39\xad\xbe\xab\x04\xbc\xb8\x39\xa3\xfc\x68\xaa\xbb\xd5\x94\x31\x8a\x4e\x36\x4f\x51\xe8\x73\x9e\xfa\x1c\xa2\x61\x46\x5a\x00\x52\x81\x2c\x3b\xc4\x69\x94\x24\xfa\xfa\x1d\x0c\x06\x62\x09\x3f\xb7\xcb\x1a\x4c\x03\xe9\x7e\x1a\x4a\x08\xf3\x79\xc8\xa4\x31\x88\x74\x28\x40\xa9\xdc\x1a\xc9\x1a\x02\x33\x92\xb1\x48\x66\x6e\x7a\xe0\xb9\x8d\x90\x61\x23\xe3\x41\x44\x94\xc6\xe6\xab\x7e\x01\xc6\x42\x81\x32\x41\x93\xd6\xc1\x82\x3b\x51\x70\x8e\x36\x2f\xed\xf2\x59\x85\x50\x86\x4d\x54\x0b\xbd\xaa\x0a\x17\xb9\x4a\x2c\x48\xd7\x28\x29\xea\xa3\x2f\x62\xd3\xb3\xcc\x8f\xa4\x8c\xa0\x47\x4c\x57\xde\x2c\x8d\x7d\xb8\xa7\x71\x01\xf0\xc8\x69\x6e\x77\x7a\x11\x7d\xbf\xb1\x8b\x29\x7d\x27\xf3\xa2\x29\x18\xb1\x80\x53\x17\x9a\xeb\xbe\xd7\x67\x4a\x85\x25\x19\x1f\x2e\x67\x0c\x09\xbc\xea\xc0\xe8\x9a\xfb\xd6\x04\x4a\xe9\x4b\xb8\x67\xac\x07\xcd\x13\xbd\xf3\x3a\xac\x4d\x83\x81\xeb\x72\xc1\xe5\x01\x9a\xc3\x05\x61\x52\x6c\xbc\xf4\x0d\xd8\x35\x9b\x15\xbc\x9e\x75\x1a\xc7\x8e\x63\xdb\x32\xbf\xb4\x9e\xca\x68\xa0\xf0\x3a\xa6\x7a\xbc\xc8\x78\xce\x33\x86\xf7\x94\x3d\xc7\x69\x02\xa3\xf8\x7d\x84\xbd\xee\x1a\xec\xce\x8b\xd6\x35\xed\x6f\xed\x46\xd1\x46\x90\xb7\xb7\x0d\xb3\x48\xe3\xae\x60\xb5\xf0\xa7\x5a\x6a\x8d\x6b\x46\x90\x14\x07\x54\xb1\xfa\x41\x8a\x34\x84\x7b\xf3\x29\xe7\xaa\x71\xed\xd5\x7b\x5f\xc7\xa8\xd8\xbb\x5c\x8d\xb0\x3c\xbe\x68\xe1\xe6\x25\xc7\xf5\x9a\xb5\xcc\x2a\xc0\xdb\xfb\x40\xc6\x45\x49\xe6\x51\x89\x7f\x8a\x40\x1f\xd3\x44\x55\x1a\x78\x13\x45\xe9\x35\xdf\x05\x35\x7d\x7d\xea\x68\x37\x43\xda\xb8\x9a\x66\xc7\x03\x5a\x35\x33\xef\x45\x33\x58\x04\x78\x61\xf1\x97\xb9\x6a\x85\xcb\x07\xfe\x48\xcb\xee\xce\x6a\x57\xd3\x34\x57\x4d\xf1\x98\x6f\x36\x4f\xad\x10\x2f\x2e\xbc\xf8\xca\xac\x09\x5e\xec\x95\x9a\x6f\x11\x27\x4a\x2f\x2a\xf0\xac\x91\x7d\x2d\xc2\x7e\xdb\xa0\x51\xb2\xfe\x1b\xc5\x8d\x92\x85\x56\x1d\xe4\xd7\x0c\x22\x65\xc6\xc6\xcf\x17\x63\xe1\x8e\xa5\x60\x07\xe3\x26\x46\xc4\xa1\xab\x2f\xed\x6b\xc6\xc5\xcb\xfe\xb1\xb9\x12\x32\x3c\xe7\x0b\x30\x5d\x4c\x31\xfc\xc0\xeb\x73\xe2\x7a\x2e\xb2\x54\x5c\x4f\x51\x17\x97\xb3\x4f\xb4\xc7\x5d\x14\xb2\x0f\x6b\x27\xe9\x06\x8e\xf0\x12\x2a\xbf\x41\xea\xae\x5c\xf8\x3a\xe2\xc3\x39\xd5\xfc\xe9\x41\xc7\x85\x0b\x3c\xc6\x20\x1b\x89\x41\x78\xbc\xaf\xda\x7e\x64\x20\x89\xfa\xad\xc7\x13\xf3\x41\x9f\xe0\xd2\x10\x74\xd6\x4d\xec\x28\x63\x05\xd8\xe6\x4b\x5d\x86\x12\xcf\xd1\x55\x5a\xd5\x56\x61\xa1\x73\x10\x2d\x16\xc9\x25\x77\x8e\xd1\x8a\xb0\xfa\xb6\x95\x0f\xdb\x02\xac\x66\x68\xe2\x8d\xea\x6e\x98\x07\x1e\x1a\x41\x31\x1e\x15\x1d\xe1\xd6\x61\x11\x3c\x13\xf6\xb5\x22\x23\x88\x74\xb5\xe2\x75\xef\x20\x95\xe0\xfc\xb0\xa9\x30\x5c\x05\xe8\x4a\xcd\xde\xc9\xaf\x2a\x6e\x8a\x48\x6c\x24\x2a\xa9\xb2\x98\xda\xad\x85\x57\x0b\xfa\xf9\xa7\x0c\x0b\x21\xca\x02\x81\x93\x7c\xbc\x4c\xa2\x7c\x7d\x7d\x7d\xbd\x3e\x18\x84\xa0\xa0\xbb\x8a\x07\xe1\xb8\x29\xd8\xbe\xbf\x3f\xbd\xbf\x3f\xfd\x6b\xdf\x9f\xf2\xcb\x53\x0a\x2b\x22\x74\xf8\xfd\x8a\xff\x6e\x1e\xc3\xed\xbb\xd9\xce\xb2\x60\x6e\x9d\xc7\x65\x87\x1b\xc6\x81\x3a\xc5\xba\x55\x65\x69\x22\xe0\xc9\x79\x96\x7f\x8e\x72\x4a\xd9\x74\x0f\xa3\x35\xe8\x23\xa6\xf4\x17\x93\xc9\x04\xe7\x38\x2d\x11\x4e\xcf\x0a\x5a\x86\xd6\xfc\xcf\x37\xaf\x5f\x95\xe5\x82\x3b\x25\x04\xfe\xc4\x03\x93\x90\x69\x9a\xd1\xa5\x9b\x90\x14\x43\x13\xa3\x3c\x3b\x2f\x70\xbe\x06\x92\x01\x73\x02\x76\x4e\xd2\x38\x3b\x07\x0d\x87\xe6\xb4\x1b\x3d\x78\xc0\x73\x06\x66\xf5\x82\xcb\x9a\xa9\x68\xdf\x0f\x5d\xd9\x99\xe1\x10\xa5\x59\x8c\xd7\x0c\x17\x3a\x4e\x9d\x12\x55\x17\xf3\x84\xe2\x82\x6f\x86\xdd\x7e\xdb\x66\xae\x19\xee\xff\xf9\xea\xfd\xb6\x51\xdd\x2c\xdf\xee\xf6\x6b\x30\xc5\xa4\x0d\xda\xc2\x3b\x81\x7e\xf7\xde\x1a\x64\x2e\x7a\x1c\x00\x2f\x72\xcc\x25\x2c\xed\xa5\xbc\xa5\x36\xca\xeb\x62\xc2\x2c\x2b\xca\x40\xc4\xd9\x37\x2e\xa7\x69\x0e\xda\x47\xf0\xdf\xd5\x15\xea\xf2\xf5\x91\x64\xe3\x28\xa1\x89\xe1\x93\x6f\x76\xbf\xe9\x6a\x1a\x4c\x11\xac\x7f\x5f\x86\xed\xbf\xba\x42\x9b\x8d\x42\xd3\x22\xc7\x0b\x08\x9e\x83\xcf\x2d\xb4\x5b\x32\x13\x07\x7c\xaf\x9d\x61\xb4\xb0\xd1\x38\x4a\xaf\x21\x28\x27\x8b\x1b\xcd\xe6\x4d\x61\x8a\x7b\x20\xd3\x0e\x7a\x66\x5b\x86\xd3\x0b\x1d\x5b\x9a\xbc\x65\x76\xc0\xbc\xe3\xa3\xd5\xeb\xa2\x16\xef\x81\xa6\x27\x31\x40\x10\x52\xc2\x15\x0c\xfc\xd5\xfb\x6d\x15\xa9\x4e\x48\x5a\x1a\x46\x35\x04\x73\x91\xc1\x70\xf8\x64\xd5\x66\x0c\xad\x67\x1b\x03\xb2\xca\xb3\x05\x4e\x7b\xdd\x77\x47\x1f\x3e\x76\x03\x35\xe3\x01\xc3\x94\xbc\xc5\x61\xb0\xca\xd1\xe8\x2b\x1c\xc5\x38\xef\x75\xa9\xcc\x89\xd3\xf2\x11\x3d\xbc\x76\x83\x2e\x15\xa6\xc9\x18\xb6\xc1\xe1\x2f\x85\xd2\xe4\xc9\x6b\x20\x8e\x8d\x06\x5a\x60\x9e\xdb\x2f\xd3\xb1\x76\x52\xb5\xd5\xb3\xbe\x0b\xdb\x85\x76\xed\xeb\x0f\xa7\x59\x37\xaf\x85\x1d\x5b\x49\x28\xa8\xdc\xf9\x14\x53\x61\x92\x02\x8f\xcf\xc8\x27\xdb\x54\x9b\x2b\x14\xa6\x71\x0f\x8c\xb1\x99\xf7\x7c\x32\xb9\x94\xed\x48\x53\x60\xd0\x93\xbb\x2e\x42\x99\x46\x95\xb1\xe8\xc1\x21\x33\xc3\x7d\x9e\xa5\x29\xe6\x76\xd3\x62\xf2\xdc\xbb\x06\x79\x85\x27\xba\x21\x1c\x75\x7f\xc4\x17\x65\x45\x7f\x79\x09\xcd\x70\x9c\xdb\xee\x5a\xbd\x6c\xea\xe1\x7b\xde\x54\xcf\xd7\x76\xa3\x81\x6a\xa5\x6a\x07\x68\x24\x6a\x20\x92\x03\xb9\xdc\x6b\x28\x85\x67\xfd\x28\xef\x00\xa5\x32\xa2\xcc\xc9\x74\x0a\xc1\xeb\xb3\x14\x51\xea\x00\xc1\x41\xfa\xa5\xa3\xc8\x68\x22\x28\xe8\x81\x8f\xaa\xcc\xb0\x89\xed\xe8\x0b\x82\x8f\xee\x59\xeb\x37\x05\x3f\xa2\x45\x19\x95\x78\x3c\x8b\x52\xc3\x2f\x77\xcf\xbe\x16\x52\x73\x10\xc5\x97\x60\x23\x0c\xb7\xb1\xbb\x74\x77\xb5\xd9\xcd\xba\x2f\xfa\x61\x0b\x6a\xb2\xc1\xc5\xed\x8e\x16\x65\x43\xd2\x8b\x73\xb5\xd4\x8a\xf6\xc4\x9f\x97\x06\xc5\x9f\xbc\x54\x5a\x99\x1a\x65\xf5\x66\x67\xed\x7b\x28\xa7\x4b\xa6\x8f\x6c\x35\x41\x8a\x7f\x7b\xe6\xc5\xac\xb5\x18\xa8\x05\xfd\x91\x15\xeb\xe9\x4c\x5f\xf2\x88\xaf\xc6\x64\xec\xfe\xd4\xf1\x19\xc7\x75\x3d\x5d\xa9\xea\xce\x3b\xb9\xa4\x2b\x88\xbd\xa0\x83\xe5\x4a\xd7\x49\xc5\x6a\x25\x05\x6f\x01\x9b\xec\x5b\xee\xe9\x42\x4f\x69\xef\xed\xf4\x4c\x90\x76\x4b\x34\x89\x48\x82\xe3\x01\x3a\xa2\x67\xaf\x73\x42\xcf\x13\x11\xc4\x5d\xa9\x5e\x9f\x5a\x9b\xbe\xb9\x31\x71\x2b\x75\x13\x3d\xeb\xd5\x6a\x1c\xa2\xef\xe4\x5f\x60\xe4\xd1\x2d\x30\x5f\x8c\x43\xd4\xdd\x1e\x6c\x76\xcd\x3c\xa1\x5d\xec\xa6\xb8\xfc\x94\x90\xa2\xc4\x29\x49\xa7\x16\x90\xd4\x10\x9e\x2a\x22\xf3\xdc\x39\xab\x18\xe4\xbe\x05\x21\xec\xb6\x28\x3a\xf4\x09\x73\x34\x04\x3a\x9a\xf6\x44\x94\x31\x46\x06\x9d\x70\xfb\x71\xd0\xa1\x52\x6a\x27\x7c\x42\x7f\x19\xe2\x6f\x27\xdc\xfa\x96\x9e\xfc\x77\xee\x4f\xfe\xf7\x27\xff\xbf\xf8\xc9\x5f\x99\x4e\xc3\x33\xae\x3b\x32\x9b\x96\x4f\x41\xf4\x43\xe1\x88\x4c\x99\x33\x80\xc1\x2f\xec\x48\xce\xae\x3e\xe2\xd7\x78\x62\x1e\x3b\x64\xe4\xad\x4b\xed\xe9\x91\xb1\x53\x33\x08\xb6\xfa\xcf\x67\xb4\xf7\x3d\xd3\x26\xeb\x7b\x56\x18\x3d\x44\xdb\xee\xdb\x25\x30\xf2\xeb\xa2\x0d\x29\x99\x21\xdb\x0b\x9b\x47\x64\x7b\xc7\x0f\x74\x51\x8a\x0e\x9f\x1d\xbc\xe5\x93\x1c\xa3\xef\xbe\x45\xe3\x6c\xbe\x58\x72\xc7\xff\xa3\x4b\x34\xcf\xce\x48\x3a\xd5\x42\xda\xec\xa2\xf1\x2c\xca\x61\xdf\x60\x97\xb1\x31\xb3\x9e\x12\x26\xc1\x02\x3a\xc1\xcc\x30\xbc\xcc\x68\x83\x0c\x57\x05\xea\x1d\xa0\x7d\xb4\xb5\x19\xa0\x67\xf4\xff\xad\x00\x0d\x06\x83\x00\xfd\x0f\xda\x47\x3b\xdf\xf4\xe9\xc1\x06\x15\x0b\x3c\x26\x13\xc2\x16\xd2\xe1\x87\xa3\xad\x9d\xc7\x5b\x8f\x6d\xab\x32\x52\x64\x90\xce\xc7\xe1\x7a\x52\xbc\x66\xaf\x04\x69\x47\xe8\x00\xcd\xdb\x34\xfd\x32\x99\x4b\x71\xb1\x00\xe3\x8f\xbd\xcd\xfa\xcd\x18\xdb\xa3\x28\xd5\xe7\x91\x8e\xa8\x7b\xd0\x1d\x50\xb4\x3c\xcf\x62\x7c\x50\xf6\x36\x35\x45\x35\x1d\x5b\xf7\x7f\x9c\x6c\xc6\x00\xd9\x4b\x46\x20\xd6\x32\x3b\x5e\x2c\x70\xfe\x3c\x2a\x94\xf6\x5a\xcb\x2e\x96\xa3\xa2\xcc\x7b\xbb\x7d\xf1\x46\x91\x27\x6c\x06\xbb\xd6\x25\x19\xcb\x5d\x24\xa4\xec\x75\xbb\x7d\xf3\xf9\x66\xda\x37\x0d\xaa\xc6\x59\x4c\x07\x97\xfa\x3a\x8f\x84\x2b\x70\x0a\xf3\xc3\x3e\x3a\xa0\x72\x28\x7c\x7c\xbf\x8f\xfe\xa7\xef\x78\xba\xf6\xcc\x2c\x9f\x58\x03\x52\x3a\x52\x8c\x31\x7a\x84\x0e\xd0\x06\xda\xda\xd4\xc4\x34\x9f\xf7\x67\x11\x09\xce\x91\xe6\xfa\x83\x5f\x32\x92\xd2\x61\xda\xc6\x89\xe3\x25\x78\x99\x84\x29\x7e\x73\xf4\x82\x12\xf6\xd6\xa6\x60\x4a\xdc\xa8\x0f\x28\xdf\x43\x71\xdf\x6e\x3e\xde\xb5\x09\x6e\x9e\xc5\xdf\x7d\xbb\xb5\x59\x45\x68\x26\x7d\x29\xdf\x9d\x8c\x9a\x78\xe1\x5a\x2a\xca\xf1\x3c\x22\x29\x53\xfd\xd0\x3c\x25\x6b\x70\x4f\x18\x26\x7b\xe0\xc0\xca\x1a\x79\xbb\x6f\xf9\xf8\x00\x66\x25\xc0\xa4\x91\xeb\x77\x86\x84\xa2\x9a\x04\x41\xfe\x30\x2d\x99\xfb\x90\x00\x6d\x6d\xf6\xd1\xff\x4b\xb1\xb6\xe1\xd4\xc2\x3c\x88\xb0\x86\xbd\x47\x44\x59\x97\x2c\xa9\xea\x33\xe6\xa9\xf9\xad\x07\x33\x2c\x87\x75\xa0\x87\xe2\x87\x84\x71\x96\xe7\x34\x85\xb1\x4f\x31\x5f\xfe\xc9\x19\xea\x1e\x5e\xfd\x93\xc0\x8d\x9f\xd5\x92\x73\xbb\xea\xc4\xfc\x6b\xea\x27\x86\x50\x1b\xcb\xb9\x78\x01\x61\x11\x15\x85\x39\x90\x39\x4e\xdf\x23\x2d\x4b\x88\xdc\x74\x08\xd7\x92\xad\xe9\x0a\x31\x9a\x33\xd0\x6a\x6c\x7a\x45\x1d\x15\xcf\xc4\x2b\x6a\xe5\x6b\x46\xc4\x8e\x43\x5b\x8f\x35\x16\x36\x8a\x0a\xbc\xf3\x18\xed\x43\x99\x41\x99\x71\x07\x30\x3b\x8f\x8d\x5b\xff\x38\x06\x51\x9d\xef\x81\x3d\x56\x28\x40\x5b\xdf\x98\x7a\x26\xd9\xcf\x67\xa3\x28\xed\xb1\x62\x26\xf3\xb3\x16\x33\x77\xd4\xa0\x2d\xdc\x67\x74\xe8\x65\x66\xec\x5e\x74\xfa\x10\xb8\x1d\xcc\x2f\xc5\x8a\x66\xca\x2e\x30\xd1\x7d\xc7\x3c\x9e\xa7\x59\xc9\x85\xb2\xef\xc9\x0f\x9d\x29\x48\x24\xcc\x01\xc8\x44\x21\xb5\x98\x45\x4c\x5a\x83\xfd\xed\x62\x9c\x2c\x0b\x72\x26\xe3\xa7\x91\x11\x49\x48\x29\x05\x9c\x51\x94\x7e\x1e\x8e\xf2\x28\x1d\xcf\x50\x81\xf3\x33\x32\x16\x1b\x60\xc4\xbc\x20\x76\xbe\x1f\x92\x1f\x06\x36\x0d\x49\x67\xe9\x85\xd8\x85\x26\x38\xa7\xdb\x50\x94\x4c\xb3\x9c\x94\xb3\x39\x8a\x71\x31\xce\xc9\x88\xb1\x25\x2e\xff\xe0\x74\x70\x4e\x3e\x93\x05\x8e\x49\x04\x42\x10\xfd\x1a\x1e\xa6\x25\xce\xd3\x88\x3d\x88\xf8\xf4\x2c\x4a\x3f\x7f\xe2\x2e\x18\x3f\xb1\x79\xfd\x7f\x7e\xe2\x23\x4d\xa7\x9f\xe8\x10\x3f\x41\x2c\x98\x4f\x31\x99\x12\xe7\x81\x86\x98\x1a\x1f\x45\x8e\xc4\x9e\x2a\x66\x40\x78\xd1\x28\x33\xcf\x36\xdb\x82\x56\x9f\xd9\x2b\x72\x64\xb1\x45\x3e\xa3\xcf\xd9\x3e\xd5\xfd\xe7\xcb\xee\xde\x9a\x97\x67\x72\x1e\xdb\xb3\x76\xee\x9e\x5e\xc1\x06\xea\x6e\x82\xa8\x04\xad\xe8\x16\x2e\x14\x1d\x2f\x28\x36\xd0\x3e\xea\x31\x71\xaa\xf7\xdd\x13\xf4\x48\x35\xd1\x17\x2f\x05\x1e\x6d\x5b\xfb\xad\xf4\x43\x60\x36\xa5\xd5\xc9\x1b\x6c\x50\x9a\x71\x26\xa2\xe1\x0a\x08\x9b\x85\x73\x25\x69\x51\x92\x72\x59\x0a\x47\xa2\x24\xc6\x69\x49\x37\x2d\xdb\xd9\x34\xab\xe5\x30\x8d\xc1\xd3\x41\xf5\xcb\x99\x22\x10\xb2\xac\x7c\x3a\x03\x21\x81\x3a\x5a\x4b\x1d\x68\xaa\xa3\xda\xea\xac\xc2\x8b\xcc\x9e\x54\x05\x1c\xad\xe4\x0c\xdd\x97\x1f\x5f\xd1\x79\x10\x0f\x5a\x74\x0c\x68\xa9\xb2\x6f\x7d\x8b\x5f\x67\x75\xfc\x5a\x84\x42\x62\xc8\xe5\xb1\x52\x49\x81\x98\xf7\x04\x8d\x8f\x3b\x72\x27\xf8\x94\xa8\x94\x37\xe5\x5e\xe4\x51\x92\x08\xe5\x08\x29\x55\x4b\x52\xe8\x3c\xd4\x3c\x56\xd4\xca\x09\x44\xf7\x7c\x41\x18\x59\xe9\xc2\x9f\x72\x7b\xd1\xa8\xc9\x97\x58\x80\xae\x03\xfb\xcc\xf3\xfa\x31\x2b\x4f\xe4\xde\x51\x05\x28\xf3\x68\x78\x60\x6c\xba\x66\xc7\x1d\xa5\x45\x09\xc3\xff\xfd\xe7\xcb\x93\xcd\x47\xdf\x9d\x7e\xd9\xbe\xee\xbd\xfc\xf8\x8a\xfe\x3e\x78\xf4\x3f\xa7\x5f\xb6\x76\xae\xaf\xe4\xc7\xce\x66\xb0\xb3\x75\xdd\xff\xaf\xe1\xa0\x04\x05\xac\xdc\xc0\xfb\xe8\xc1\x03\x29\xe5\x54\x31\x06\x0d\x9c\x79\xf4\xd9\x5a\x11\x61\xdc\x55\x1c\x9c\xfe\xbd\x68\x7b\xa1\x96\xe0\xdd\xe0\xed\x85\xbb\x92\x2c\xc4\xa9\x41\xe9\xef\x79\x76\x76\x21\xea\xb2\x3f\xef\x9b\x1b\x0e\x7b\x82\x48\x5a\x31\x70\x83\xfb\xdc\xcd\xd0\xbd\x6c\xa4\xd5\xe0\xb7\x9b\xaf\x37\xa7\xb8\xe4\x22\x25\x1d\x69\xb1\x9c\x53\xc0\xe3\x82\x1f\x1f\xe6\x59\xfc\xe8\xbb\x6f\x1f\x6d\x6d\xca\x6c\x38\xe3\x42\xef\xc6\x59\x82\x7a\x87\x1f\x8e\x86\x87\x2f\x9f\x23\x7a\x6e\x08\xb7\x37\x37\x77\xfa\x36\x4f\xd6\xaa\x75\x4f\xa1\x5a\xae\x33\x70\x91\xd7\x72\xd8\xfc\x4c\xb8\x1d\xa0\xed\x76\xe6\xa9\x3a\x53\x35\xb6\x14\x84\xa7\x03\xf4\xcf\xf7\x2f\x7f\x72\x5c\xab\xc9\x02\xfe\xd1\x54\xd6\xe8\x4e\xaa\x06\xd9\x34\x3c\x45\x00\x3d\xf0\xbb\xe5\x0c\xf9\xdb\x00\xed\xf6\x51\x88\xba\xdd\x56\xe3\x1e\x27\x04\xde\x8e\xc9\x0e\x82\xf2\x89\xa4\xf6\xf8\x28\x16\x7e\x3a\xf8\xc7\xd1\x8f\xff\x3a\x7a\xff\xdf\xf6\xac\x42\x1d\x15\x73\x6a\xd7\xef\x9d\x5c\x06\x74\xeb\xb1\x6f\x6d\xad\x3e\x72\xbe\x9a\xfc\xe7\x12\xf7\xe0\xe1\x0e\xcd\xa9\xc0\x19\x5e\xe4\x39\x87\xe8\x0f\x24\xf9\xe0\x7c\x2e\x99\x8c\x43\x87\x3b\xe0\x5d\xed\x10\x5b\x79\x94\x11\xe7\x0f\x79\x4a\x31\x4e\xa8\xec\x8c\x62\x9e\x67\xb6\x1e\xf7\x03\xb4\xbd\x29\x2f\x61\x0c\x29\x4f\xa0\xd7\x1a\xa4\x28\xdc\x6e\x81\x56\xf8\xf5\x39\x84\x2c\xa6\xd4\xd7\xf5\x8a\x9d\xd0\xfc\xbc\x3e\x0d\x76\x76\xef\xd5\xf8\xf7\x6a\xfc\xbf\xb8\x1a\x9f\xab\xf0\x17\xe3\x7a\xfb\xbd\xbb\xb5\xb8\x6b\xe9\xf2\xa4\xc1\x30\x8f\xe9\x99\x16\x63\xaf\x21\xd7\x22\x2a\x67\x01\x4a\xb1\x61\xf0\xfd\x09\x34\x17\xce\x5b\x53\x71\xf5\xad\x87\x30\x15\xbe\x08\x98\xf9\x41\x04\x8e\x49\xe8\x7f\x2c\x55\x65\x8d\xe5\x7d\x30\x70\xc5\x52\x24\xf4\xbe\x50\xe8\x50\x95\x97\xce\xf9\xac\x62\x83\x2c\xed\x75\x61\x54\x5d\x3d\xe4\x5f\xdf\x30\x99\x2e\x32\xca\xc4\xd8\xf3\xc1\xc3\x77\xcf\x91\xba\x85\x66\x8f\x0a\xbb\x01\xd2\x03\x59\x7f\x62\x6c\x90\xdf\x95\xf7\x6c\xf7\x80\xde\x1e\xa4\xb1\xde\xbe\xd6\x7c\x65\x65\x68\x4d\x3e\x2b\x78\x7d\xf8\xe1\xe3\xcb\xb7\xb0\x82\x9e\x1f\xbd\x7d\xfb\xf2\xf9\xc7\xc3\xa3\xb7\xe8\xfd\xcb\x0f\xef\x8e\xde\x7e\x78\xf9\xa1\xb2\xd5\x38\x2a\x23\xbd\x59\xfa\xad\x6f\x4e\xc3\x87\xdc\x0a\x70\x1e\x5d\x8c\xb3\xf9\x22\xc1\x17\xa4\xbc\x0c\xd1\x63\xa0\x2c\xab\x87\xa0\x0b\x95\x76\x0d\xb4\x2a\xb5\xdf\xf4\x3d\xd1\x1f\xb8\xdd\xc2\x97\x35\xc7\x52\x83\xc4\x7e\x33\x0d\x1e\xc6\x16\xf8\x4b\x8c\xce\x67\x64\x3c\x43\x73\x1e\xd5\x9f\x85\x9c\xa7\x9b\x10\x65\x68\xb1\x79\x37\xee\x3a\x5b\x87\xa6\xfd\xf1\x5c\xe1\x3a\xca\xe9\x2d\x18\x2d\xf8\xa3\x2d\x92\x49\xef\x93\x9f\x90\x4f\xe0\x39\x1c\x89\x4f\x5d\xdf\xd1\xb2\x30\x1d\x2b\x07\xdb\x73\xa0\x9c\x50\xe8\x55\x11\x23\xa1\x1a\xde\x77\xbb\xa2\x6b\x07\x8b\x13\x92\x63\xc3\x09\x80\x8d\xae\xaa\xf1\xd0\xa1\x78\x5a\xaf\x01\x57\xe1\x2b\x5d\xbb\x19\xfa\x17\xe3\x04\x97\xb8\xae\x06\x7b\x30\x36\x6e\xf4\x87\xd7\x3f\xd3\x5d\x0b\x08\x91\x13\x04\xab\x0f\x94\x3b\xcc\x52\x2b\x65\xce\x5e\x50\xc6\x9c\xcf\x92\x72\xb0\xb6\x26\x84\x41\x93\x84\xd7\x6c\xb5\x07\x3c\xc2\xa4\xc2\x9f\xe2\x79\x9a\x78\x64\x16\xd6\x0d\x39\xf4\x55\x65\xb3\xc1\xc0\x92\xd7\xfe\xc1\x7c\x3d\x2b\x97\xa5\x62\x89\xbf\x78\xf9\xe8\xf9\xab\xe3\xb7\xff\xfd\xf2\xbd\xac\x27\xc6\xe3\xd9\x32\xfd\x8c\x63\xfe\x90\x84\x3d\x12\xe5\x7f\x83\x1c\x2f\x92\x68\x8c\x7b\xc3\x7f\x5f\x9f\xfc\x3b\xfd\x77\x7e\xfa\xf4\xdf\x5f\x86\xd3\xa0\x7b\x7d\xf5\xe8\xd1\xd5\x97\x6e\x1f\x9c\xad\x7e\xf1\xc2\xff\xfb\x54\x94\x38\xe1\x65\x4e\x69\xa1\x13\x51\xea\xf4\xc4\x5f\xce\x2e\x65\x14\xaa\x28\xa3\xda\xd2\x5a\x92\x0d\x69\x65\xf8\x35\x1f\xcd\xee\x0a\x4e\x6a\x60\xc0\x5d\xb3\x80\x78\x8d\xbf\x0c\x87\x70\x07\x8a\xb9\x07\x0c\x70\xae\x01\x15\xac\x39\xa4\x4f\xf3\x9e\xd3\x2c\x73\xe5\x72\x57\x33\x16\x0c\xda\x40\xec\xc9\xab\x21\xaa\xcb\x3b\x6b\x8b\x93\xb9\xc6\x66\x3e\x43\x33\xe8\xbb\x56\xca\x30\xa9\x59\x73\x17\x9f\xea\xcc\xbe\xdd\x19\x64\x44\xc1\xe6\x46\x60\xe0\x5e\x2c\x1d\xe3\x04\x5c\x8c\x8b\x77\x9b\x46\x99\x71\x82\xa3\x5c\x58\x7f\x59\xad\xf0\x64\x6b\x41\xfb\x81\xc0\x3d\x43\x29\x2a\xf2\xed\x71\x66\x79\x7b\xaf\xd3\xff\x6a\x2d\x3b\x39\xce\x74\xf8\xeb\x00\x6d\x6d\x6e\x6e\xa2\x87\xec\x72\xc6\x73\xd7\xea\xf5\xf5\x00\x4f\xf5\x00\x3b\x02\x5f\x94\x83\x14\x98\xd3\x0b\x8b\x20\xc1\x9f\xf2\xad\x8e\x2a\x77\xc6\x2c\x12\x81\xd0\x28\xdc\xae\xd3\xe9\x30\x63\x11\x2c\xec\xb1\x69\x0a\x6b\x69\xeb\x75\x70\xee\xef\x87\xf2\xc8\x9f\xf8\x16\x1a\xc5\x71\xa1\xc7\xc3\xe5\x56\x0e\xae\x34\xc6\xd4\xc3\xc1\x1a\xdb\x70\xc5\xc1\x80\x9f\xb5\x09\x73\xe0\xcd\xb9\xde\x5c\x04\xf7\x36\xb8\xef\x61\xcc\x4a\x45\x79\x4e\xce\xb0\xce\x70\xa3\x58\xce\x9e\x68\xaf\x86\xc3\x7a\xa0\x0d\xff\xdd\x75\x16\xad\x84\x5d\xd8\x5d\xf2\xad\x16\x5d\x5d\x89\xaf\x93\xcd\x53\xb9\x65\xc2\x15\x36\xeb\x9b\x82\xe6\x09\x66\x09\x96\xa8\x4b\x74\xde\xcd\x0b\xed\xcb\xde\xd4\x49\xbc\x14\x74\x20\x1b\x16\x75\x8b\x5d\x4d\xac\x23\x7d\xa5\xb2\xa8\xd9\xdc\x2c\x85\x89\xe5\x70\xfa\x02\x8d\x3b\xdd\xdf\x63\x0d\xcd\x9c\x88\x6b\x50\x5b\x63\x1b\x3a\xc9\xf2\x1e\xc5\xcb\x67\x7c\xc9\x4e\x8a\xbe\x01\x98\x06\xbe\x3d\x3f\xd0\x60\x16\x15\x47\xe7\xe9\x3b\x88\x0f\x53\x5e\x42\xfc\xaf\xbe\x1d\xfe\xd9\x8b\x9e\xcf\xf8\xf2\xb4\xda\x12\xb4\x9b\xa5\xe8\xf0\xdd\xf3\xae\x1d\xaa\x9a\xcb\x16\x35\x75\x3a\x66\x16\x6a\x99\x3c\x17\xaa\x60\x90\x93\x98\xc3\x66\xa4\x1d\x37\x48\x81\x8a\x92\xb0\xf0\x0c\x24\xd6\x88\x5a\x37\x21\xad\x44\x78\x83\xcd\xa7\x7b\x5a\x12\x72\x00\xdd\x3d\x72\xcc\xfb\x11\x30\x2a\x30\x7b\x35\xcd\x52\xcc\x35\x4f\xbd\xf5\x4f\xb6\xd8\x7f\x9e\x93\x12\x5c\xa4\x58\xdc\x48\x03\xb1\x8e\x50\x9f\xdc\x33\x14\x67\x30\xeb\xeb\x55\xb5\x73\x05\x92\x77\xe8\x75\xef\x1b\xd6\x74\xfa\xb1\xea\xc5\x1f\x8c\x17\x2b\xfa\x26\xbb\x67\x70\xee\x15\x50\x24\xd0\xd4\x8c\x25\xe4\x39\x42\x35\x9e\x35\x45\x2f\x63\xed\xa5\xaf\x6f\x54\x35\x46\xd2\x37\x13\x1b\x24\x55\x57\x59\xa6\xb7\xd8\x47\x91\xf5\xe7\xdb\x27\x2d\xb3\x3b\xae\x4d\xb4\xce\x28\x8e\x07\x9e\x7f\x65\x4b\xb0\xc8\x56\x8f\xc5\x3a\xdd\x0d\x9b\xdd\x6e\x74\x3b\x7a\x24\xf7\xe4\x72\xa0\xdb\x74\x2b\x3e\x08\x8f\xb5\xb2\x12\x15\xcb\xc5\x22\xcb\x4b\xd0\xad\xb1\x9b\xda\x77\xcf\x91\xd4\xaa\x74\x0d\x1b\xf1\x6a\xc2\x5c\xe1\x9d\xc4\xea\x8b\xb1\x99\xca\x56\xa2\x30\xef\xb1\x1e\x68\xaa\xc1\xe8\x5e\xfa\x52\xb4\x77\xd3\x4a\x07\x37\xae\x1e\x57\x61\xb0\x3e\x7c\xbc\xb2\xdb\xbe\x3e\x0d\x76\xbe\xb9\x57\xe9\xde\xab\x74\xff\x23\x54\xba\xfc\xcd\xc5\xad\x9e\x63\x1f\x44\x79\x96\xa2\xff\x5e\xce\xa3\x33\x52\xa0\xef\x23\xfa\xf9\xb7\xcf\xec\x73\x30\xc7\x5e\x75\xef\x70\x88\x0e\x53\x52\x92\x28\x21\xbf\x62\xf4\x77\xd6\x0b\x4a\xa8\x11\x2a\xc0\x12\x4b\x18\xdc\xc0\x40\xe9\x52\x35\x1c\x49\x0f\x40\xab\x2b\x8a\x89\x38\x0c\x3c\x24\xcf\x61\x1c\xa2\xcd\xa6\x9b\x37\x66\xed\x41\x87\x6f\x3b\xcb\xf5\x9a\x99\x78\x9d\xe4\xaa\x37\x70\x22\xfa\xcf\x44\x20\x14\x5a\x52\x06\x3d\x1e\xd7\xba\xec\xb1\x4a\xa0\xa9\x7a\x26\xa2\x1a\x91\x25\x3c\xea\x7a\x3d\x0f\x69\x23\xa0\xed\x39\xbd\x1f\xae\x71\xf4\x54\x38\xd8\x65\x6d\x05\xbc\x31\xc3\x4f\x2a\xcb\xea\x57\xa9\x96\x45\x93\x8e\x31\x8f\x34\xdb\x5d\xef\x6a\x71\x78\xa2\xf8\x8c\x9e\x51\xc5\xec\xa0\xc3\x17\x90\x23\x7a\x27\x27\x6d\x63\xa3\xca\xb5\x50\xd5\xc3\x20\x12\x87\x6e\x35\x2a\x5b\xbc\x19\xe2\x23\x95\xe9\xe2\x99\x10\xfb\x9f\x1e\x98\xf8\x83\xa1\x66\x9f\x41\xd2\xf0\x42\xe0\x40\x1e\x1e\x85\x01\x91\xdf\x54\x47\x2a\xeb\x9a\x62\x41\x79\x1e\x65\x5b\x0d\xf8\xcd\x33\x04\x1a\xac\xf6\xac\x00\xaa\x2c\xd1\xba\x0c\x65\x6e\x7c\x34\x9d\x33\x07\x7a\x2a\xdb\x1e\xe0\x33\x9c\x5f\xf6\xa0\xf9\xa8\xc4\x1f\x48\x3a\x4d\xf0\x1b\x86\xf0\x3e\x0a\x91\x37\x43\xd5\xc4\xa7\x55\x76\xc4\x0f\xce\x27\xb0\xaf\x1e\x67\x73\xe1\x5d\xd0\x8d\x66\x41\x24\xd2\x18\x45\x1a\xb6\x45\x3c\x43\xcc\xcf\xfe\xfe\x3e\xa3\x1a\x1d\x88\x7b\x4e\x10\xb0\xf4\xcc\x4d\xc1\xd8\xb5\x6e\xd7\x57\x1d\x97\x61\x2d\x37\x92\xc3\x21\x0b\x56\x26\x93\xe8\x24\x51\x21\x4f\x63\x2e\x62\x3d\x56\xfb\xec\xd6\xa8\x8b\x31\xa2\x11\xf8\xfd\x6c\x60\x47\xcf\x28\x50\xb5\xe3\x6e\xde\x71\x8b\xbf\xb0\xba\x0a\xc6\x54\x79\x55\x42\xc0\x89\xfb\xa0\x3c\xe2\x8b\xa2\x27\x78\x4f\x1f\x4d\x08\x4e\x62\xcb\xf4\x80\xb7\x62\xf4\xd4\xe2\x39\x7a\x07\x2d\xc6\xc3\xba\x66\x91\xa1\x48\xb6\x3c\xeb\x0b\xb2\x70\x5f\xe8\x39\xec\x4d\xc0\x0e\x04\x6b\x13\xdf\x9c\xc5\x99\x7a\x78\x47\x56\xe4\xf5\x71\x39\x91\x8a\x81\x8f\xef\xc5\xc0\x7b\x31\xf0\xaf\x2d\x06\xaa\xf7\x79\x6c\xd1\xdc\xd5\x0b\xbd\xbb\xb9\xbb\xa7\x20\x6f\x84\xba\xb1\xd2\x58\x19\xce\x89\x3c\x1a\x86\xb0\x42\xa6\x9f\xda\x29\x92\x7b\x59\x13\xb9\xf4\xd3\xb8\xb8\x07\x9e\xa7\xf2\x95\x64\xb0\xa9\x81\x81\x1b\x7e\x3d\x4c\x9b\x32\x84\xd6\x33\xb4\x12\xcc\xb9\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x26\x4a\xa3\x29\x56\x8e\x00\x28\xcb\x62\xa8\x30\x54\x01\xc2\xc3\x88\x02\xd7\xf6\xfb\xb9\x81\x21\xa7\xe2\x7c\xde\x60\xff\x1e\x63\xca\x61\x48\x6a\xba\xf4\xb4\xc4\xbf\x51\x54\x30\x8f\x0f\x55\xf1\x25\xa6\x18\x1c\x53\x7a\x36\x29\xd3\xb9\xbc\xed\x4b\x54\xb4\x69\xb6\x07\x24\xe6\x20\x82\xb7\x51\x19\x41\xc2\xf0\x20\xaa\x85\x28\x91\xc4\x21\xed\xf8\x84\xfb\xc2\x82\x0a\x36\x32\xa5\xc9\xb3\x31\xf3\xbf\xa9\x2e\x29\x78\xc0\x0d\xbe\xed\xca\x71\x0e\xd0\x1b\xca\xca\x09\x2e\x78\x58\x5d\xc0\x87\xe3\x78\xd2\x70\xe6\xd9\x1a\x6f\x62\x50\x57\x6f\x97\x49\xa2\xdc\x72\x04\x54\x8a\xc4\x17\x04\xae\xcd\x7c\xb8\xfb\x63\xc6\x78\x69\x23\x84\xd3\xd2\xdc\xf8\x59\xc7\xb2\x69\x38\x8f\x74\xdc\x0a\xd1\xf3\x20\x9f\x16\x8d\x88\x05\x85\x60\x81\xbe\x80\x3a\xf0\x5a\x3d\x14\x48\xb3\xd2\x8f\x49\xbd\xf6\xd6\x51\x67\xa8\x4c\xa9\x71\xa1\x26\xff\x30\xcc\x96\xf2\x68\xc2\x23\x4a\x78\x7d\x4a\x78\x70\xc6\x5d\xbb\x32\xaa\x03\x8c\xcb\xe3\xa6\x8d\x23\x06\x7a\x48\x21\x5d\x14\x19\x14\x27\x93\x3c\xb8\xd0\x6a\xa9\x45\xc5\xba\x87\xb5\x56\x90\x8f\xef\x65\xa3\xa7\xb4\x25\x40\x4a\x8f\x8b\x01\x82\x98\xc1\x75\x31\x7a\xd0\x53\xf5\x9b\x91\x36\x14\x39\xa5\xbc\x40\xfb\x6c\xf0\xa4\xef\x60\x9d\x31\x7b\x19\xcc\x53\xc7\xbc\x8b\x78\xe6\x70\xb7\xfe\xa0\x68\xba\x1f\xae\xc0\xbd\x27\x26\x8a\x0a\x27\x6a\xa3\xd0\xde\xa9\xc0\xc1\x0d\x9c\x79\x9e\x7a\x01\x64\x55\xde\x58\x24\x1c\x17\xbe\x28\x44\xe2\xf1\x94\xa0\xc3\x15\x82\x11\x45\x62\xd1\xb6\x42\x42\xbb\xb0\x42\xba\xfb\x55\xbe\x89\xd8\x5e\x91\x57\x76\xb6\xcc\x85\x09\x00\xd6\x96\x81\x0e\x08\x79\x3a\xbb\xe8\xc9\x33\x8a\x5f\x05\x22\x74\x19\xa0\x56\xaa\xd0\x64\xd4\xb9\x51\xd6\xf5\x1b\x0e\xaa\x84\x4b\x5c\x86\x4f\x53\xd4\x1a\xfd\xa2\xa3\x8b\x66\x88\xa1\x8d\x96\x24\x89\x01\x61\x7c\x50\x34\xd3\xf1\x67\x0b\xdc\xfe\xe3\xd1\x8b\xa3\xf5\xf5\x75\x90\xed\xbb\x05\x5a\x4e\x93\xcb\x01\x62\x11\x1b\xe8\x69\x60\x59\xd0\x0d\xb1\x94\xad\xa4\x9a\x0b\x59\xfa\x5b\x18\xd5\xc8\xeb\x11\xca\x38\x20\x43\x3e\xb6\x36\x3c\x2f\x65\xa3\x5f\x4e\x68\xf6\xc9\xe6\xe9\x29\x95\xb9\xf4\xcf\xab\x2b\x69\xb4\x69\x83\xb2\x1f\x5b\x50\x86\x8e\x65\xcf\x7f\x4f\x64\xd5\x0e\x90\x48\xe3\xc2\x0e\x7a\x25\xa2\xaa\xae\x50\xe5\x8d\xba\xb2\x38\x65\x21\x4f\x52\xff\x9b\x2c\xe4\xf8\xf5\xe6\xc2\xbb\x3a\x0a\xaf\xe2\xf7\x19\x59\x11\x2b\x7c\xa1\x09\x8c\x83\x3a\xb4\x65\x8a\x93\xea\x56\x4a\x5d\xce\x18\xb1\x57\xa4\x6d\x9d\xc7\x2e\xcf\x6e\x98\xc1\xf3\x76\x74\x66\x26\x2d\x22\x2d\xeb\x19\x6f\xf8\x14\xb3\xbb\x46\x35\xd5\x43\x70\x1c\x3f\xb1\xff\x68\x56\x5b\xcf\xce\x22\xac\x15\x4e\xe3\x66\x7d\x22\xe7\x90\xcb\x1c\xc3\xf5\xe8\xfb\x77\xcf\xa5\xab\x26\x66\xc7\x32\x8e\x52\x29\x69\x92\x94\x6b\x5c\xfc\x4e\xa1\x72\xd7\xc7\xe3\x60\x30\xb8\xd6\x43\xb6\xd9\x6e\xfe\x94\x1a\x53\x14\xf5\x70\xd2\x26\x1f\xf6\x95\xee\xe5\x57\x21\x42\x41\x03\xa6\x0f\x7a\x7d\xd6\xaa\x10\x2d\x63\xc5\x7b\xb5\x3a\x6f\x84\x01\x4c\xeb\xcb\xbf\x6f\xef\xb5\x3e\xf7\x5a\x9f\xbf\xb6\xd6\x87\xab\x7c\xe2\xd1\x2d\xee\xfd\x7c\x5a\x1f\xa9\xab\xd1\xd5\x3e\x8c\x3b\x49\x7d\xce\x8b\x67\x06\x23\xa1\xc3\x30\x1d\x7e\x38\x7a\x0a\x18\xa9\x95\xbc\x57\x13\x19\x6c\x4d\x09\x4c\x45\xcf\x63\xd1\xcf\xaf\xb7\xd0\x17\x64\x89\x57\x96\x20\xd4\xa3\x35\x6b\x5b\x0b\x07\x72\x94\x2e\x3d\x5f\x07\x2d\x6d\xb3\xda\xe6\xab\x23\x14\x2d\x96\xa5\x7c\xba\x96\xe2\x73\x8e\x4d\xcd\x81\x1e\x95\x3a\x42\xd4\x95\x70\x56\xe0\x8c\x10\x75\xe3\xd1\x27\x5f\xae\x90\x13\x77\x64\x9f\x64\xa3\x53\xdc\xae\x51\x09\xe7\x6d\xd4\x97\x2b\x1a\xdd\x76\x1b\x5d\x2c\xcb\x57\xf8\xa2\x79\x98\xaf\xf0\x45\xd5\x18\xcd\xac\xfa\x01\x36\xb7\xc5\x80\xaa\x86\xe6\x6f\xcb\x1a\x17\xdf\x8d\x4e\x14\x9c\x98\x88\x40\x21\x39\xe0\x43\x0f\x78\xb7\x00\xf8\xb4\x62\xeb\x7a\xf1\x6c\x4f\xee\x5a\x8c\x76\x3a\xe1\x0e\x6c\x51\x4f\xee\xb7\xa8\xfb\x2d\xea\xaf\xbd\x45\xa9\x8b\x09\x5c\xce\x6e\x74\x2b\xc1\x81\xef\xf6\x4d\x62\x45\x7c\x75\x5f\x80\x75\xdf\x15\x88\xff\x16\xa4\x61\xdb\xa4\x20\xc2\x18\xd9\x02\x5a\xf0\x64\x01\x36\xae\x6a\x6f\x9c\xa5\x13\x32\x15\x60\x5a\xec\x1b\x1d\x5a\x84\x52\x11\x60\xe7\xfc\xd1\x9a\x71\x3d\xc3\x13\x05\xcc\x8f\x70\x8a\xb7\x91\x01\x89\x02\xe4\xb0\xf8\x70\x99\x8e\xd9\x16\x63\x04\xba\x67\xa9\x02\x8c\xb2\xe2\x1c\xdb\x40\x3c\x55\xd6\xc5\xdc\x13\xe9\x10\x64\x14\xa5\x22\x9b\xf9\x3c\x74\xfa\x23\x92\xa5\x10\x02\x1e\xd3\xda\xdc\x18\x48\x8d\x37\x7f\x21\x08\x5a\xc0\xcd\xd3\x3e\x7a\xf0\x00\xf1\xdf\x03\x50\x0a\x1e\x4d\x7a\xdd\xcd\x8b\x2e\x73\x5c\xb2\xd9\x47\x4f\x51\x07\x97\x33\xba\x7b\x40\x24\xd2\x67\x97\xaf\xa2\x62\xd6\x41\xa1\x9d\xcc\xf4\xb9\x1d\x25\x25\x68\x01\x9f\x7e\xcc\xb3\xf9\xb3\xdf\xa0\xa7\x5d\xde\x25\x2d\x8e\xd0\xb3\x4b\x68\x98\x76\xfa\x20\x8d\x0f\x69\x39\x19\xbe\xcb\x0b\xc9\xc6\x21\x61\xd5\x78\x96\xe9\x38\xc1\xbf\xd1\x00\x8e\x69\x5b\x0d\x5d\xd7\x61\x2a\x3b\x2d\xe6\x47\x1b\xe7\xf3\x6c\x99\xb6\xba\x64\xba\x83\x71\x78\xdb\x66\x24\xa4\x0f\xa5\x02\x8c\x8d\xca\x99\x82\xdf\xb0\xff\xc7\xb2\x41\x6d\x32\x9c\x49\xd0\x01\x8c\x3e\xcb\xee\xbd\x2c\x67\x77\x7d\x40\xf8\xcd\x0f\x07\x10\xf2\xb7\xfa\x70\xc0\x94\x1f\x8c\x8d\x13\xec\xed\xd2\xc2\xe8\xcd\xa2\xa1\x23\x8b\x1b\xf4\x41\xbb\xe3\x66\xfc\x95\xf9\xbf\x40\xba\x23\xef\xc3\x67\x07\x6f\xad\xf8\x63\x9c\xab\x32\xc5\x0c\x7b\x40\xcb\xd5\x33\xd7\x6b\x6b\xac\x77\x03\x66\x19\x25\xdf\xd2\xbc\x2c\x67\x4a\x21\x14\xa0\xae\x1e\xad\xb9\x1b\xf0\x69\x9e\xe2\x32\xac\x50\x7b\x0a\x5f\xa5\x03\xbd\x20\x1f\x49\xc0\x55\x75\x46\xe1\xb3\x28\x31\xfc\xb5\x0f\xac\x58\xd9\x67\x51\xe2\x38\x23\x91\x69\xd7\x6b\x80\x9e\x95\x86\xc2\xfd\xfc\xdd\x64\x30\xbc\xe8\x4d\x86\xc3\x8b\xb6\x1c\x50\x9b\xd3\x28\xe5\x2f\x51\x02\x96\x9b\x8d\x67\x27\x0e\xe8\x9e\x9f\x04\xa3\x72\xf2\xe5\x21\x4a\xb3\xe6\x34\x6e\xf1\x42\x74\xa2\x44\x2a\x76\xc3\xc7\xdd\x68\xfe\xa8\x2e\xf4\x6c\x08\x3d\xd8\x39\xe3\x28\x12\x58\x8b\x16\x69\x5d\x65\x85\x7a\x35\x2c\x4f\xfa\xac\x91\x40\x15\x07\xe7\x2c\x8f\xa6\xf8\xa0\x6c\x73\x76\xe6\xa0\x95\x38\xf2\x41\xc8\x63\x6d\x0d\x96\xd8\xba\x63\x3c\xbb\xcc\xe0\x6c\xb9\x0a\x5a\xbc\x03\xe3\xce\x1d\x1b\xc6\x44\xa1\x2a\x87\x63\x65\xfe\xf6\xf3\xed\x1d\x98\x58\xf5\x4d\xf4\xcc\xd8\x91\x35\x34\x29\x34\xde\x6e\x58\xbe\xde\x06\xce\x12\x57\xf6\xaf\x74\xf1\xa2\xeb\xd5\xe8\x97\x36\x51\x4f\xbb\xb0\x03\x37\x63\x02\xc0\x1c\x4c\x48\x99\xee\x6b\x60\x42\x23\xe5\x5b\x0c\x3a\x58\xab\xa0\xec\xf9\x82\x24\xec\xf8\xd6\x48\xde\x1c\xb4\x86\xc6\x5d\x08\x81\x87\xcd\x6a\xfa\xb3\x25\xb6\x96\xf4\x68\x17\x73\xba\x55\x27\xb4\xba\x1d\xdc\xba\xe5\x44\xd5\xcd\x8d\x98\xc2\x17\x78\x4c\xe6\x51\x52\x8d\x0a\x25\x07\xb6\x44\x82\x2a\x50\x41\x94\x7f\xdc\x01\x9b\xc2\x53\xc3\x60\xab\xc3\x23\x57\x1c\xc2\x40\xc2\xae\x1d\x74\xf3\x0a\xd2\x2a\xac\x67\x1e\x1f\x3d\x67\xd4\x95\xc6\x24\x4b\x39\x83\xab\x3a\xfe\xfe\x91\x38\xcd\x4d\xf0\xf4\x1e\x8f\x31\x59\xb4\x20\x73\xb7\x4c\x1b\x02\x70\x41\x6f\x4b\x01\xbc\xc6\xd6\x03\x6c\xb9\x8a\x1b\xb9\x98\x67\x70\x36\x60\x1b\x0a\x60\x62\xd1\x1d\x09\x88\x8d\xcb\x9b\x1e\x90\xde\x47\xe7\xed\x97\xb8\x5b\xc0\x8f\x88\x5a\xb8\x36\x9c\x8d\xe2\xc1\x23\x0b\xb9\xd1\xa4\x9b\x7a\xdb\xaa\xab\x37\xef\xa7\x3d\x53\xbe\x35\xe6\x1b\x07\x99\x36\x76\x9e\x4c\xab\x7a\x6c\xe6\xdc\x8d\x8c\x5a\x81\x70\x6e\x17\x55\xd7\x51\x88\x91\xef\xed\xa8\x95\x73\x93\x8e\x52\x1e\x7a\x67\x92\xb4\x19\x4c\xbc\x6e\x4c\x1a\xa4\x7f\x68\x7e\x80\x9b\x50\x8c\x31\xc2\x5b\xad\xe6\x31\x93\xeb\x44\x08\xf0\xa6\x69\x63\xd0\x03\x11\x16\xbc\x62\x0a\xcd\x3a\x7d\x63\xad\xec\xc8\xeb\xd7\xaf\x5b\xf6\x21\xa9\xa4\x20\x59\xd3\x4a\x2d\x7f\xc0\xf9\x02\x37\xb2\x75\x89\x01\x06\x5d\x8f\x00\x07\xa6\xa6\x17\xc5\x72\x34\x27\xe5\xcf\x59\xde\x24\x5d\x28\xc0\x8a\x95\xee\xcb\xaf\xbf\x34\x6e\xd1\x2a\x87\xaa\xdc\xc2\x2a\xda\xb3\x8e\x06\xce\xc5\xb1\x52\x98\x04\x7a\x9a\x54\x10\x18\xa9\xf4\x9c\x6d\x24\xc0\x12\x36\x52\x40\x66\xb6\x0a\xf1\x83\x8b\x5b\xd2\xde\x76\x5d\x08\x25\x82\x1b\x79\x5a\xc1\xaa\x74\x29\xd0\x55\x01\x70\x99\xa3\x2a\xdb\x6a\xd4\xb4\x85\xd5\x18\x89\x4a\x74\xb7\x4f\x33\xcf\x9f\x41\xa6\xda\x97\xb5\x70\x9d\x8c\xd7\xaf\x5f\xbb\xc0\x8c\xc8\xb5\x2a\x25\xfd\x19\x63\xa3\x09\xf0\xcd\x4d\x00\x58\xc8\x32\xa9\x0c\xae\x73\x61\xac\xc8\x85\x52\xa8\x54\x4a\x9a\xc6\x95\x72\x7d\x92\x74\x14\x15\xd8\x0a\x97\x38\xc5\x8c\x1f\xf2\xe5\xc9\x61\x24\xc8\x75\x3f\x58\xa1\x8d\x39\xf1\x04\x64\x34\x5a\xe0\x10\x37\xac\x7f\x16\x15\xb3\x3c\x2a\x6b\xc7\x50\x01\xd3\x6a\x03\x58\xbd\x47\xe2\xfa\xb2\xa6\x43\x7e\x90\x66\x31\x9c\xdf\x97\x9a\xb2\xf7\xea\x3d\x9c\x46\xc5\xbb\x9c\x8c\x6b\x71\x56\x01\x73\x63\x1d\xe9\xea\xbd\xe4\x51\x79\x8a\xba\x5e\x4a\x98\x1b\xb6\x31\xd2\xee\x98\x6a\x9a\xa9\x06\xfb\x4a\x34\x24\x42\x16\xfc\x83\x19\xa3\xd4\xf5\xcd\x06\xd5\x5a\xd4\x59\x88\x71\x2b\x31\x18\xab\x8b\x7e\xed\xce\x6f\x44\x0c\x9b\xfe\x68\x5c\x66\xb9\x90\x72\x84\x69\x00\x18\xda\x06\x88\xc2\x1a\xd6\xb6\x1c\xda\xd7\xd8\x44\x98\x02\x68\x8f\x6f\x48\xe2\x7a\x24\xd2\xa2\xf4\x30\xeb\x81\x9e\xef\x5e\x2f\x40\x50\x98\x9b\x1b\x0c\x70\x39\xeb\xf5\x03\x97\x0c\x5f\x67\x53\x4d\xac\xb5\xfc\x01\x99\xfd\x53\x06\x06\xf5\x8e\xe1\x05\xd2\x7a\xbc\xc0\x60\x9a\x64\xa3\x28\x19\x50\x5c\x0c\x22\x37\x99\x47\xf2\xf2\x35\x49\xc6\xd1\xe2\xed\x4d\x9b\xa5\x85\x9d\x46\x59\x62\x5d\x93\x9a\xb5\x85\x6a\xb0\x66\x0e\xa4\x79\x46\xc5\x34\x34\xf9\x58\x7a\x59\xce\x34\xf3\x6a\xcb\xe2\xa4\x13\x6e\x3d\x09\x3a\x8e\xe5\x0b\xb7\xbc\x56\x26\x27\x9d\x70\xfb\x1b\x48\x60\x44\xd4\x09\xb7\xbf\x63\x9f\x72\xbe\x3b\xe1\x0e\x2b\x42\x46\x51\xda\x09\x77\x76\x02\xd3\x2e\x0e\x3e\x39\x96\x3a\xe1\xee\x2e\x7c\x0b\xfb\x98\x4e\xb8\xcb\xaa\xe7\x1c\xb9\x13\xee\xb2\x6e\x89\x3b\xcc\x4e\xb8\x4b\x1b\x14\xd6\x2d\x9d\x70\x77\xe7\xfa\x34\xd8\xf9\xee\xde\xd0\xee\xde\xd0\xee\xaf\x6d\x68\x57\x65\x65\x77\x6b\x63\xf0\xf6\xf6\x6f\x2d\x8c\xdb\x00\xee\x2d\x2e\xbf\xa6\xed\x38\xa4\x7e\x6d\x53\x8c\x16\xb6\xe2\xc3\xe1\x50\xb9\x5a\xf1\xb9\x6f\xe1\x71\x08\x29\x8f\x87\xea\x70\x39\x43\xd1\x82\x68\x7d\xff\x4a\x07\x89\xaa\xc0\xeb\x52\x50\x31\xa3\xb3\xdf\x54\x28\xc2\x38\xb7\x55\xbe\x4e\x2b\x55\x40\x2b\x08\x6a\xba\xd8\xe4\xec\x6a\x6f\x71\xb9\xe7\x6e\x6a\xe6\xe6\xa5\xef\x2e\xd7\xa7\xc1\xee\xe6\xfd\x6e\x71\xbf\x5b\xfc\xb5\x77\x8b\x3f\xa8\x59\xf6\xdd\x59\x50\xb7\x34\xf0\x56\x36\x8a\xef\x70\x5e\x64\x69\x94\xfc\x6e\x86\x8a\x92\xa3\xfd\x11\xec\x14\x6f\xb5\x37\x36\x74\xe1\xba\x9d\xe1\x5a\x8a\xcf\x95\x35\x5c\x9d\xfa\x56\x01\xba\x1a\xdc\x05\x9f\xd5\x4f\x5e\xa0\x5b\x5c\xbc\x2d\xd3\x24\x1b\x7f\x6e\xd7\x41\x03\xb6\xa6\x8f\x55\x70\x6d\xcc\xc7\xda\x5d\x68\x55\x5e\x6b\xdd\xf1\x3d\xa2\x1c\x52\xf3\x65\xe2\x2a\x97\x5f\xbe\xcb\xc4\xca\x21\xb5\x9f\x9f\x76\xb3\x53\x3f\x37\xab\xdc\x70\xd9\x73\x63\x75\xde\x27\xd0\xf1\x86\x55\x23\x06\xad\xb4\x50\x86\x6b\xd0\xba\xbc\xf4\xf5\xe4\xcb\x83\x06\x75\xa0\xc2\xab\x0e\x5c\x21\xd6\xb9\x72\x9d\x60\xd7\x4a\xb8\xf3\xa8\x18\x2c\xad\x82\x2d\xe7\x6d\xdd\xcb\x79\xf7\x72\xde\x5f\x5b\xce\xe3\x42\x5e\x31\xbb\x6b\xad\x40\x0b\x49\x6d\x85\x37\x76\x2d\x5e\x9f\x35\x3d\x62\x03\xa0\x0f\xb3\xd9\x57\xd7\x2c\xfc\x81\x1e\x9d\x50\xd6\xf8\x61\xf6\x9b\xe9\xee\x8b\x99\x5f\x77\xff\x2e\x2b\xca\x6a\xe5\x7d\x8d\xac\x87\xb4\xa7\xe4\x59\xd1\xb4\x4d\x53\x90\x6e\x80\xec\x0d\xba\x98\xcd\x3e\xd9\x59\x37\xd9\x95\x8d\x51\x78\xe4\x89\x14\x9f\x1f\x42\xd4\xe1\x46\x4b\x12\x0d\xd2\x95\x27\x68\x77\xfd\x00\x35\x26\xc7\xb3\xa8\x68\xd9\xb6\x06\xe9\x6f\xdb\x0f\x50\x63\xbc\x91\xe2\xf3\x9f\xf2\x6c\xb9\x68\x1e\x34\x80\x55\x8e\xd8\xce\xad\x19\x6e\x14\xc7\x1f\xb3\x36\x8d\x2a\x40\x7f\xb3\xde\xfc\xb6\xf6\x1b\x94\xa8\x0c\x7d\x97\xc0\x9b\x4a\xd4\x90\x69\x40\x42\x93\x2a\x45\x75\xa3\x56\x57\xf5\x61\xa6\xdf\xc0\x34\xdf\xa0\x18\xe2\x8d\x75\xe5\xb1\xbb\x7d\x2f\xdc\xdc\x0b\x37\x7f\x6d\xe1\x46\x29\xb1\x46\xbf\xfe\x6a\x29\xb1\x0e\x12\x7c\x81\x9e\xe1\x1c\x4f\x8b\x5f\xa3\xe2\x57\x82\xbe\x8f\x12\x7c\xf1\xb7\xbc\x9c\x14\x83\xd9\xd2\x14\x6b\x1e\x73\x07\x5c\xef\xf1\x04\xe7\x38\x1d\xe3\x10\xd1\xf6\x8b\x70\x38\x9c\x92\x72\xb6\x1c\x51\x49\x68\x88\x29\x39\xe1\xe5\x7c\x38\xcd\x1e\xc9\xdf\xff\x97\xbd\x77\x5f\x6f\xdb\x56\x16\x47\xff\x4e\x9e\x02\xed\xef\xac\x46\x8a\x69\x5b\xd4\xcd\x97\xc4\xdd\xdb\x91\xed\xd8\x2b\x17\xe7\x67\x3b\x6d\xd7\xf6\xe7\xe6\xa3\x44\xc8\x66\x23\x91\x5a\x24\x65\xcb\x6d\xbc\xdf\xe7\x3c\xc7\x79\xb1\xf3\x61\x70\x21\xae\x14\xe5\x4b\x9a\x76\xd9\x6b\xef\x54\x24\x81\xc1\x00\x18\x0c\x06\x83\xb9\xf4\x47\x49\x7f\x35\xbb\x0a\xd2\xf1\x6a\x14\xe7\x38\x8d\x83\xd1\x2a\xe9\x12\x9e\xe5\xfc\xbf\x2b\xe7\xc9\xff\x79\xdb\x6a\x3d\xb0\xce\xab\x50\x64\x1d\x13\x6c\x1e\xb5\x58\xdf\x88\x16\x8b\x9a\xa2\xe0\xfc\x2a\x49\x3f\x1f\x61\x88\xae\x56\xb6\xa1\xe9\xc5\xcd\x6d\xad\xff\xfb\xef\x9f\x4a\x4a\xdd\xc5\x96\xfc\x3a\x1e\xec\xc6\x41\x7f\x84\xe7\x61\x29\x95\xb4\x23\x68\x2f\x70\x17\xdc\xae\x82\x49\x45\xdc\x8a\x92\x0e\xdc\xac\x05\xee\x80\x5b\x98\x5c\xc5\x2c\x70\x5e\x19\x62\xbc\x98\x1d\x2b\xcb\xd7\xea\xee\x11\x2e\x1d\xe5\xa4\x02\x5a\xb4\x90\x1d\x29\xe3\xdb\x9d\x51\x4a\x71\x9e\x46\xf8\x72\x9e\xc7\x22\x2f\x66\x47\xcb\xf2\xf5\x2e\xa4\x95\x93\xdd\x6e\x0e\x51\x91\x32\x0e\x72\xd2\x3e\xdd\x79\x88\xce\x71\x05\xf7\x1b\x3b\x2e\xea\x87\x3b\x8c\x09\x0d\x38\x3c\x27\xaa\x97\x1d\x07\xf5\xc3\x9d\x47\x83\x05\x18\x2f\x47\x86\x16\xb2\xe3\x63\x7c\xe3\x28\xb5\x2b\xa1\x54\xa2\x37\x37\x0e\x0c\x3a\x5b\x96\xd4\xb2\x05\x3f\x94\x5e\x16\x8c\xa8\x78\xc9\xf9\x80\xa4\xe9\x9d\xa8\xcf\x9c\xfa\x25\x40\x84\x04\x15\x83\x64\xa9\xb7\x53\xe9\x41\x92\xc5\x1f\x54\xff\x7b\x11\x5d\x3a\x8d\x55\xc9\x9c\xc0\x77\xf1\x79\x31\xab\x82\x28\x1e\x26\x65\xb0\xe1\xbb\x04\x5b\xee\xad\xe5\xc8\x45\x84\x25\x5b\x78\x36\xab\xc2\xb8\xf5\x78\xa6\x7a\x3c\x53\xfd\xbd\xcf\x54\xec\x40\xc5\x55\x7e\x5f\x37\xae\xe8\x6d\x2c\xa6\xb8\xca\x33\x98\x44\x5c\x18\xa7\x29\x69\xf2\x8b\xb2\xab\x65\xaa\xf5\x2c\x8d\xa3\xc4\x4b\xe7\xd7\x13\x22\x1f\xb0\x98\x49\x52\x8e\xdc\xec\x2a\xca\x07\x17\x35\xf2\x5d\x0f\x87\x3d\x08\x32\x8c\x9e\x11\x8a\xcf\xf2\x67\x9b\xca\x27\x98\xac\xf4\x3c\x5b\xc9\x2e\xa2\xa1\x23\xeb\xbc\x9c\xcc\xa6\x61\x16\x60\x2c\x19\xec\xc9\x63\x7c\x45\x23\x26\x50\x6d\xf9\x0b\x0b\x1a\x13\x1c\x87\x51\x7c\xfe\xe0\x78\x7c\xa0\xed\xc8\xb7\xb4\x36\xa4\x58\xa8\x1b\x13\x1b\x0d\x9c\x51\x99\x25\xcd\x56\x75\x93\xe2\xea\x60\x8e\x72\x92\x41\xd3\x65\x04\x85\x14\x2a\xe9\x43\xa7\x71\x14\x67\x79\x30\x1a\x55\x6a\x59\x2b\x6d\xf7\x31\x73\x17\x2a\xc1\xe3\x1c\xe7\x6f\x93\xf3\x0a\x01\x30\x48\x29\xa7\x6f\x1b\x6d\x51\x2b\x52\xd2\xea\x24\x99\xeb\xff\x4a\x8a\xcc\x69\xaf\x77\x11\xc4\xe7\xb8\x42\x93\x36\xe1\x83\x82\x90\xaf\xc2\x95\xd1\x53\x04\x21\xd2\x31\xa9\x91\x64\x34\x92\xe5\x81\x85\xf9\x4d\x76\x71\xb1\x02\xac\xd1\x60\x37\xd9\x45\x05\x76\x73\x6b\x2a\xe5\x3a\xf4\x45\xe8\xe3\x9e\xe9\x94\x60\xf0\x67\xd0\x29\x69\xf7\x1c\xe7\x2c\xbd\xe4\x43\x53\x29\x6b\xed\x9b\xa2\x52\x43\x68\xa5\x7d\xc1\xf9\xc5\x26\xf9\x87\x56\xcc\x2e\x2e\x36\xc9\x3f\x54\xce\xb5\x45\x1b\x6e\xb7\x1f\xa5\xd7\x47\xe9\xf5\x6f\x2e\xbd\x16\x57\x02\xdc\xd1\xea\x9e\x12\x21\x52\x27\xb1\x23\x7c\x4e\xe6\x39\x48\xb7\xfb\x91\x23\xe8\x6e\xb6\xfa\x5a\x2d\x0a\x39\x6c\xb9\x26\x3f\x1a\x04\x13\x19\x88\x0b\xc6\x41\x6f\xfb\x83\x09\x41\xc2\x84\x79\xa3\x31\x53\x32\xb4\x85\x9e\x35\x66\x83\x6e\xb8\x11\x36\x07\x61\xbb\xbd\x11\xac\x75\xda\x83\xf6\x46\xbb\xd9\x6d\x63\x7f\xbd\xb1\x31\xe8\x34\x70\xab\x1d\x76\xdb\x9d\x6e\xb3\xff\xac\xc0\xc5\x06\x26\xf0\x03\xdf\xf7\xfb\x83\xc6\x5a\x7b\xb0\x31\x18\x06\x6b\xeb\xfe\xb0\x31\x68\xad\xe3\x6e\xab\x1f\x76\xfc\xc1\x86\xdf\x5f\x0f\x86\x8d\xc6\x33\x37\x6f\xa2\x38\x6e\x4a\x42\x71\xd0\x8f\x36\x2d\x83\xa8\xdc\x91\x12\x14\x36\xad\xfd\xa3\xec\x96\x16\x26\x68\x1b\x90\xf5\x71\xb5\xc0\x35\xbb\x4b\xa1\x2a\x1c\xb3\x7c\x16\xbf\xdf\xf4\xbd\xef\xe7\xcc\xd3\xf7\x9b\x4d\xc2\x6c\x3b\x8f\xcc\xf6\x91\xd9\xfe\xbd\x99\x6d\xc1\x6b\xb9\x9e\x4c\x63\xb6\x65\xbe\x01\xc3\x34\xf9\x1d\x8f\x83\x78\x25\xc4\x3f\x7e\xad\x4c\xb5\xfa\x5d\xea\x5d\x32\xd1\x52\x9d\xa3\xf4\x9d\xbe\x50\x82\xed\x6a\x25\x32\xbd\xc4\x6d\xd2\xca\x2e\x9e\xbc\xb6\x24\x11\x2d\x1f\x8b\x87\x4f\x45\x5b\x35\x83\xe5\x9d\x13\x58\x5a\xba\x54\x92\xc2\xd2\xe6\xbf\xad\x8d\xf0\x7f\xd9\xde\xd2\xba\x90\x4c\xf2\x1b\x49\x1e\xe9\xec\xf7\x3d\xa5\x8f\xfc\x6e\x8b\x12\x8e\xf6\xaa\x48\x99\x7f\x87\xfc\x92\x7f\xd1\x0c\xbc\x96\x21\xff\x36\x73\xf0\x6a\xc3\x4d\x4d\x21\x0a\xcc\xf2\xc4\x4e\x8a\xaa\xc9\x84\x8b\x18\xd9\x2b\xde\x0b\x27\x35\x56\x4f\x4e\x09\x75\x84\x28\x89\x78\xec\xed\x22\x59\xe5\x6b\x9c\xd7\xa4\x3b\x23\x1c\x4f\xc7\x38\x0d\xfa\x23\xbc\x89\xf2\x74\x8a\x4d\x2d\x61\x30\xc6\x59\x69\x22\x4a\x29\x5b\x25\x14\x06\x3d\x2f\x92\x32\x54\x66\x73\x52\x54\x66\x5a\x8e\xca\xcc\x91\xa4\x52\x2f\xf2\x42\x51\x4b\x88\xe6\xfd\x33\x25\x49\xae\x3d\x70\x79\xd2\xff\xcd\x83\xf2\x1e\x1d\x32\xd6\x17\x02\x3f\xc8\xae\xe3\xc1\x6b\xd8\x6f\x88\xc8\x0b\x5d\xa8\x9f\x29\x19\x3f\xb7\x59\x91\x9a\x64\xa6\xab\x55\x53\x26\x09\x40\xa8\x2c\x03\x2e\xa2\xd1\x12\xe0\xb0\x32\xb8\x08\xd2\xed\xbc\xd6\xa8\xaf\xe4\xc9\xc7\xc9\x04\xa7\xbd\x20\xc3\xb5\x3a\xff\x0c\x39\x03\x6b\x7e\xdd\xb9\xf1\xf0\x99\x75\xa7\xd5\x2a\x36\xee\x22\x47\x18\x8f\x66\xc2\x6b\x9c\x93\x0e\x99\x2b\x46\x08\x28\x4a\xfa\x48\xf1\xd6\x96\x40\x52\xd5\xe7\xf3\xbc\xad\xa2\x0a\xdd\xee\x0b\xd5\xb4\x94\x68\xb2\xac\x83\x7c\xd4\x17\xeb\x65\x61\x16\xe0\x8e\x04\x82\x0a\x8b\x28\x6b\x87\x68\x52\xcf\x05\x7b\x55\x31\xf9\xa7\x9a\xf0\xd3\x3e\xd8\x66\xca\xcf\x1b\x35\x37\xe7\x39\xce\x17\x4c\xcd\x79\x8e\x5d\xdb\xc9\xb7\x9d\x99\xd3\x42\x1c\xd5\x73\x73\xea\x16\x76\x9b\xb2\x3c\x6a\x6a\x2a\x4f\xcf\x54\x1d\x27\x4d\x27\x6c\x4d\x80\x5c\x2d\xcd\xa7\x3c\x65\x0f\x95\xec\x93\x0f\x90\x3b\xdd\x27\x39\x62\x77\x1f\x8f\xd8\x8f\x47\xec\xbf\xf7\x11\x5b\xd2\x67\x32\x0e\x31\x66\x2c\x5d\x3d\x69\xff\x13\x0f\x87\x29\xbe\x46\x3f\x47\xa3\xc1\x67\x8c\x5e\xfe\x86\x87\x43\x97\xc7\xfe\x42\xee\xfd\xef\x82\x94\x1c\xe1\x0f\x83\x78\x80\x03\x28\x6b\x73\xec\xbf\x45\x2c\x00\x56\xe5\x75\x70\x89\x7e\x4e\x92\x10\xbd\x3c\x77\x1e\xf2\xdb\xc5\x21\xff\x9f\x8c\x9b\x2a\x4e\x60\x8c\xc5\x96\xe5\x6b\xb3\x04\xab\xd1\x53\xac\xd9\xf2\xab\xd1\x84\xc2\x6a\x6b\xf4\x1d\x35\x57\xa0\xdb\xce\x41\xfe\x2c\x23\x1b\xe3\x24\x89\xb3\xa8\x3f\xa2\x04\x36\x09\xb2\x2c\x8a\xcf\xd1\x98\xdd\x5b\x91\xbd\x68\x92\x26\x97\x51\x88\xd3\x4c\xd4\x0a\x46\x59\x62\x56\x4d\x46\x23\x52\x95\x50\x1b\x37\x34\x47\x71\x12\xd2\xaf\x51\x3c\x48\xc6\x32\x64\x02\x8c\x85\xfd\xa5\xf7\x5e\x79\x34\xc6\x64\xb1\x45\x19\xf2\x51\x86\x07\x49\x0c\x59\xf8\x8f\xa3\xf8\x7c\x84\xf3\x24\x86\xe1\x24\xdd\x2b\x39\xe8\x73\x54\x95\xe3\x3e\x7f\x89\xb6\x44\x57\x24\x3d\x03\x69\x1b\x34\xc0\x37\xd2\x4b\x8e\x8b\xac\x75\x70\x1e\xfe\x88\x84\x72\x91\x26\x71\x32\xcd\x46\xd7\x34\x6d\xba\x7d\x1f\x26\x9f\x2c\xe7\x11\x14\x06\x79\xe0\x3c\x21\xab\xbd\x55\x54\x1e\x5a\x7e\x76\x02\x46\x3e\xa9\x7d\xa7\xf4\x5e\xc9\xdb\x93\xc4\x59\x42\xb6\x2e\x42\x14\x35\x4a\x1a\x2b\x07\xf1\x65\x30\x8a\xc2\x0f\xac\x7c\x4d\x96\x79\xb8\xdf\x1d\x0c\x86\x24\xe1\xab\x7b\x3c\x23\x73\x29\x05\x37\x41\x69\x85\xf6\xde\x83\x6e\x32\xbb\x0c\xe9\xfc\xc2\x4e\xe5\x5b\xea\x5c\xa9\xd9\xd0\xd9\xa1\x08\x3a\xc5\x1b\x89\xb2\x9f\x08\xba\x47\x94\x0a\xb1\x10\xd4\xa4\x6e\xe6\x17\x69\x72\x85\xd4\xee\xe9\xe5\x95\xee\xb0\x6e\xd2\x4f\x2b\x95\x4e\xfe\xc1\x42\xb3\x0f\xd2\x6c\x29\x09\xe8\xe7\x52\x21\xfd\xcc\x27\x06\x00\x6e\x50\x84\xe9\x44\x59\x4e\x1b\x3c\x2b\x94\x24\x1b\x97\x51\xc7\xfd\x10\x82\x39\xf7\x8b\x26\xf3\x27\x9d\xc2\x69\xaa\x8b\xf8\x96\xde\x58\xb3\xf5\x2b\x70\x16\xa1\xb1\xf9\x43\x66\xd4\x96\xdb\x37\x84\x5c\x96\xc8\x4c\x21\x41\x3d\x40\x97\xfb\xd8\x60\xa3\xc6\xb2\x93\x01\x29\xf0\x8a\x7c\xb7\x28\x99\x68\xbd\xfb\x20\x4c\x68\xe1\x1b\x23\x4c\xc0\x49\xa6\x4e\xce\x64\x6e\x47\x8a\xd9\x3d\xd0\xa2\x4a\x83\x5c\xcf\x06\xb3\x51\xe3\xad\xdc\x89\xf4\xb2\x79\xb4\xa7\x74\x48\x10\x1d\x9a\xb3\xfd\xe1\x5c\xec\xab\x44\xda\xe4\x67\x42\x26\xf2\x19\x14\x97\xf3\xa9\xb2\xab\xe6\x4a\x69\x49\xd4\x55\x77\x7d\xe7\x76\x3f\x6f\xe7\xce\xc9\x91\x8a\x09\x2e\x3a\xa2\xe4\xdb\x07\xf1\x69\x2e\xc7\xa6\x71\x7b\x6f\x00\xda\x41\x38\x77\xc9\x58\xbe\x0a\x53\x1b\x8e\x49\x9e\x84\x09\x1a\x8c\x70\x10\x4f\x27\x28\x06\xf8\x64\x80\xc5\xb1\xbd\x6c\xa8\x24\xec\x2d\x2b\x8f\x22\xe9\x49\x3a\x62\xd1\xb8\x3a\x96\x44\x38\x3a\xa5\xa5\xcf\x88\x90\x44\xaa\x6f\x22\x0a\x24\x0a\x37\x0d\x40\x9b\x36\x90\x9b\xc5\xcf\x1b\x9e\xee\x71\x75\x55\x1f\x7d\x85\x01\x30\x01\x4c\xdd\xcd\x19\x42\x35\xb1\xc2\xe7\x4c\x6e\x32\x11\x42\x29\x11\x41\x99\xc5\x2d\x9c\x6e\xce\x23\x72\xa4\x8b\x74\xdd\x31\xa9\x63\x99\x73\x63\x6e\x4b\x47\x5e\x80\x50\x89\x14\xea\xf2\x0e\x85\x78\x84\x73\x6c\x19\xe4\x17\xd2\xf0\x14\xf8\xb3\xd1\xa9\x31\x8d\xea\x67\x7c\x9d\xd5\x8a\xba\x75\xae\xe5\x85\x4c\xa8\xe8\x87\x1f\x90\x6b\x0c\x09\x31\xa5\x27\xf4\x7d\x4d\x29\xf4\x42\x1d\x67\x5d\x00\x2e\x19\xef\x62\xf7\x49\x31\xe1\x05\x44\xfe\xe7\xc3\x3e\xc6\x83\x8b\x20\x8e\xb2\x31\x3f\x86\x96\x33\x07\x00\x50\x3e\xbc\xb4\x0d\x79\x60\x3f\x63\x3c\x11\x41\x84\x79\x67\x57\x9f\xff\x96\x5d\x44\x31\x69\x68\x36\x48\xc6\x93\x11\x9e\x45\xf9\xf5\x66\x07\x8e\x64\xa4\x00\x21\x88\x1a\xd9\x1c\x3e\xe3\x6b\xaa\x29\x10\xa3\x29\x8d\xd7\xea\x2a\x4a\xf1\x38\xb9\xc4\x28\x18\x8d\xa0\x57\x99\x87\xf0\x6c\x80\x27\x39\x88\xfd\xec\x95\x5c\x3e\xbf\xc0\xd7\x28\xc6\x74\x44\xfa\x98\xd5\x0f\x49\x8f\xa7\xc1\x68\x74\x8d\xfa\xd7\x30\x64\x64\x78\x58\x38\x60\xa0\x99\x9f\xc9\x86\x14\xc5\xe7\xb5\xba\xb4\x0f\xd4\xbe\x53\x7a\x87\xbe\x7c\x21\xf8\x16\xf9\x6f\x09\x00\x42\x6c\x9f\x58\x1a\xdc\x65\x5f\xdf\x20\x24\x0a\xfb\x8c\xaf\xcf\x56\xc4\x4a\xd4\x2d\xa7\x4d\x8a\x24\xe5\x0d\x2b\xe6\xbf\x30\x79\xc2\x29\x93\xcc\xfb\x80\x1a\x48\xa2\x24\xae\xc2\x13\xa8\x5d\x63\x19\x4d\x32\xb3\x4d\x53\x05\xea\xa0\x42\xd4\x25\xe0\x2c\x9d\xc9\x70\xae\xf4\x9e\x00\x96\x54\x91\x1e\x1a\xac\xec\x9e\xec\x7f\xfa\x70\xf8\xf6\xed\xc1\xfb\xd7\x9f\x4e\x0e\xde\xed\x1e\x7e\x3c\x91\x8f\x47\x55\x66\xc0\x14\xaa\x14\x89\xe9\x41\x8e\x8e\xa6\x4c\x46\xf0\xda\x09\xf2\x00\x6d\xa1\xd3\xb3\x17\xea\xfb\x03\xf0\x4c\xe6\xaf\xab\x2d\x55\x01\x70\x65\x32\xcd\x2e\x6a\x3a\xdd\x33\x11\x4f\x29\x7d\x10\x66\xb4\xf0\x67\x7c\x5d\x37\xc6\xa0\x00\xb8\xc0\xe0\x55\x12\x37\x05\x64\xd6\x28\x5f\x52\xe3\x60\xa2\x30\xc9\x08\xc8\x16\x18\x0a\x90\x18\x21\x4d\x75\x98\xde\x05\x13\x49\x75\x21\xe9\xb5\x55\xa7\x72\x2a\xb8\x02\xd7\xa8\xff\xa1\x8f\xc1\xbb\x60\x72\x0a\xd5\x22\xd8\xe2\xf9\xc8\x9c\x42\xf1\x33\x3d\x4d\xb3\xe1\x62\x8f\x16\x96\x99\x13\x55\x6a\x7e\x2a\x73\xcf\x93\xc3\x9d\xc3\x4d\x4e\x64\x68\x94\x9c\xff\x97\x2e\x55\x27\x0e\xb9\xfa\xae\x92\x74\x05\x65\x41\x66\x3d\x3a\xb2\x6f\x2b\xe3\x60\x52\x73\x19\x2b\xf0\x3f\xb0\x5f\x0c\x8b\x51\x26\x63\xcf\x8e\x7a\x51\x28\xfb\xe8\x08\x8a\xf8\x8c\x51\x36\x4d\x41\x4f\xcc\x99\x55\x94\xa1\x2c\x8f\x08\x3d\x50\x4e\x8e\x43\x14\x0c\xc1\x97\x28\x4d\xa3\xcb\x60\xa4\xed\xb5\x0a\x4c\x32\x20\x10\x21\x80\x2e\x8d\x28\x3c\xd3\x51\x2c\xba\xb4\x32\x28\xec\x01\xd4\x3a\xe2\x8b\xd3\xb7\x86\xeb\x4e\xe4\x4f\x37\x08\x8f\x98\x9e\xd9\x52\x63\x18\x8c\x32\x2c\xdf\xb2\x31\x0f\xa9\xb9\x63\x2a\x32\xd5\xb2\x36\xd1\x2d\x60\x90\x79\x81\x19\x97\x16\xad\xe3\xf0\xff\xc2\x18\xcf\xef\xa0\x66\x85\x71\xac\xae\x18\x40\x0a\x85\x21\x25\x95\x76\x14\xaa\xa3\xa4\x2d\x76\xf7\x30\xa9\xb8\xb8\xf5\x0c\x48\xbe\xe4\x74\xa5\x5d\x38\xd2\xe3\x6f\xa8\x37\x5e\x5a\xfa\x05\x33\x7c\x35\x53\x48\x7f\xbf\xd9\x84\x28\x40\x4c\x19\xfe\xfd\x66\x0b\x3c\x56\xd7\xaa\xdc\x91\xb1\xd8\x69\x38\xcf\xa3\xf8\xdc\xee\x04\x0c\x8c\x49\x4b\xb9\xce\xdd\xdb\x5e\x18\x25\x8a\xb0\x9b\xc2\x3e\xc8\x15\xf2\x88\x35\xca\xfa\x4d\x50\x5e\x7f\xbc\xd6\x7b\xbc\xd6\xfb\x9b\x5f\xeb\xb1\xa8\x8c\xec\xd4\x62\x75\xb2\xbd\x43\x60\xed\x92\xe0\x8b\x96\xd8\x8b\x55\x0d\x67\xf9\x92\xf6\xd9\xe1\x60\x3b\x0c\x33\x18\x3a\xb1\xbb\x05\x31\xa8\xa5\x32\x34\xa5\xe2\x17\xf3\x8f\xf3\x88\xf0\x15\xe5\x38\x25\x58\x26\x97\x6c\x19\xf1\xdd\xfe\xe9\x53\xf9\x7c\xc0\xce\x67\x4f\x75\x25\x11\xd9\x36\x9f\xb2\x6b\x2b\xa9\x9c\xc4\xab\x68\x48\x1f\x22\x06\xf1\x7d\x28\x89\x99\xd3\x98\xc2\xd1\x98\xdc\x44\xc6\xde\xa2\x6a\x74\x09\x45\x74\xdf\xe6\x3d\xcd\x2c\x9b\x85\xcd\x1e\x87\xff\xa9\xfb\x96\xbe\x3d\xb9\x74\x97\xc2\x42\x90\xc7\x2c\x02\x94\x7f\xf8\x01\x70\xa7\x8a\xa9\x28\x3e\x07\x6e\x5c\x57\x20\xf2\xeb\x8b\x79\xe9\xc8\x28\x44\xd9\xa1\xf9\xb6\x9d\x14\xd2\xd0\x28\xc8\xa0\x99\xe3\x9c\x4c\xf6\x77\x5b\x5b\xc6\x40\xf3\x3f\xe3\xc5\xea\x2a\x4d\xae\xaa\x90\x14\x2c\xb5\x3c\x9d\x12\x99\x2d\xcd\x72\x94\x25\xd4\xce\x71\x32\x01\xd6\x0d\x67\xe7\x20\xbe\xce\xc9\x81\xdf\x43\x7d\x3c\x24\x0c\x80\x2e\x71\x7e\x85\x0a\xa3\x41\x95\x8c\xda\x5f\x34\xac\x7d\x67\xc1\xfa\x87\x1f\x90\x6d\xe4\xeb\x46\x7d\x64\x5e\x37\x10\x54\x2d\x9e\xd4\xce\xce\x26\x94\x6f\xc6\x78\x96\xa3\xde\x87\x8f\x68\x70\x3d\x18\x61\x4f\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\xa4\xc9\x80\xf0\xac\x8c\x8e\x8e\xd1\x8a\x74\x0c\x16\xcb\xc4\x36\x17\x96\x8e\x30\xd2\xd0\x4b\xdd\x78\xa8\x51\xa5\x7f\x96\x61\xa5\xa4\xe0\x12\xcd\x24\x63\xb0\xa7\x02\x80\x6e\xc6\x26\xe9\x62\x6b\xa6\x1d\x94\x23\x55\x9f\x6e\x09\x75\xe3\x15\x42\xf8\x41\xe8\x15\x6c\x82\xbd\x97\x75\x48\x54\x67\x00\x9c\x85\xac\x13\x6e\x27\x79\x60\xcd\xcd\xe5\x4c\xb8\x55\x6e\x32\xaf\xc9\x7f\x48\xd6\x35\x1d\x10\x39\x5a\x52\x4e\x2d\x51\x2e\xbc\xb4\x24\x95\x13\xeb\x55\x3a\xe9\xc3\x87\x20\x0c\x85\x6d\x97\x14\x93\x55\x7c\xd7\xa7\x47\x3a\x38\x48\x2c\x96\x1b\x6f\xc1\x7b\xc9\x56\x9c\x0a\x74\x62\x24\x64\x4b\xdf\xa2\xdd\x52\x8b\xc5\x68\x58\xbc\x52\xb5\x52\x05\x0b\x02\xad\x82\x86\x7c\x25\x24\xe4\x59\x74\x4b\xb4\x06\x81\x09\x95\x73\x4d\x9a\x83\x7a\xc9\x68\x5b\xa5\x5a\x81\x90\xdb\x80\x8d\xc8\xea\x6a\x48\x4f\x22\xfb\x3e\xe6\x29\x7b\x94\x7d\xff\xee\xb2\x6f\x61\xd2\xc6\x93\xf6\xdd\x97\x8f\xee\x41\x3f\x88\x55\x69\x37\xea\x07\xc2\xf5\x16\xcf\xa8\xba\xba\xcc\x75\xf7\x78\x1c\xa4\xf9\x2e\x2b\x58\xb8\xdd\x3a\xaf\xc6\x40\xad\x04\xcd\xf2\xbe\x68\x3a\x6f\xe9\xb5\xb8\x04\x3b\xce\xd3\x28\x3e\xbf\x01\xd7\x16\xdb\x7b\x22\x2d\xf7\x83\x58\xfe\xf4\x53\x30\x9a\xe2\x1b\x74\x49\xfe\xc3\xae\x43\x08\xe4\x21\x4e\xf1\x9c\x1b\x52\x4f\x35\x2f\x80\x78\x36\x0c\x27\x55\x2c\xce\x2f\x3c\xc0\x88\x48\xeb\x1e\x6d\xc9\xdc\xc2\x40\xed\x46\x47\x19\x52\x4e\xf6\x83\xb8\x96\x27\x75\xa6\x2a\x02\x1d\x0e\xf9\xcc\x55\x3e\x35\x8b\x15\x11\xa9\xb7\x0b\x3a\xef\x67\x11\x55\xdf\x50\x88\xcc\x4f\xf7\x99\xa9\x3f\x66\x10\x77\xa2\x94\xc8\x62\x36\x87\x18\xde\xa3\x93\x84\x79\xf6\xca\xdd\x81\xea\x0c\x7a\xad\x6e\x76\x8d\xb7\x27\xe4\x18\xe8\x86\x4d\xd2\x65\xa1\xa9\x99\xa7\x34\xce\x2f\xe4\xbc\xa0\xb5\x3a\x34\xc2\xb0\x8d\xb3\x3c\xca\xa7\x54\xe0\x32\xcd\xbf\x42\x3c\x49\xb2\x28\x97\xb1\x64\x70\x05\x7a\x00\x66\x30\x8a\x70\x9c\xeb\x96\x18\x95\x1b\x36\x4c\x2c\x78\xbe\x51\x73\x04\x17\xc5\xc8\x1c\x3f\xae\x82\x2f\xbd\x4a\x16\xa4\x37\x9c\xc6\x21\xd8\x44\x0e\x70\x9a\x07\x91\x98\x7e\xc7\xf2\x11\x13\xbb\xd8\x3a\x7a\xf0\x25\x24\xf0\xba\xc5\x5a\x62\x23\x4f\x66\x53\xcb\xda\x22\xc9\xb6\xc2\x7b\x3d\x4f\x0a\x89\x96\x80\xde\xa4\x0d\x48\xb4\x39\x9a\xe2\x4d\xfa\x1f\x2e\xe6\x6a\x81\xf8\x9d\xb3\xc2\x26\xbf\x98\x94\x73\xb2\x0b\x44\x03\xc4\x39\x21\x12\xf9\x87\x6b\xe3\x69\x96\xc3\x56\x87\xc7\x38\xce\x05\xdd\xf4\xaf\x73\x9c\xb5\x9a\x75\x26\x8c\x7f\x57\xd7\x26\x92\x95\xbb\xf7\xe9\xcb\x8c\xf9\xe3\xd5\x29\xa5\xa2\x69\x1c\xfd\x7b\x8a\x51\x04\x51\xda\x87\x91\xca\x89\x2b\xcd\x35\x1f\x9d\x0a\x33\x0c\x4d\xda\xb9\x66\x00\xbb\x8e\xb4\x07\xbd\xd0\x89\x40\xe4\x0d\xe6\xa9\x82\xf3\xa4\xbe\xc2\xc7\x97\x83\xfe\xe3\xae\x44\x60\xc8\xaa\x7c\x14\xad\x41\x10\xcc\xfd\xf0\xfb\xcd\x16\x11\x5d\x79\xf2\xde\x9b\x33\xaf\x53\x29\x5d\x22\xd3\xee\x76\x2a\xe5\xdc\x79\x21\x2b\xe1\x13\x22\x5f\xb0\x74\xd5\x1e\x55\x28\x93\x81\x7d\x42\xd8\x34\x11\xf5\x93\x21\x12\xbd\xd9\xda\x42\xdf\xd3\xd8\x4d\xdf\x43\x99\x27\xab\xab\xa8\x97\x8c\xc7\x49\xfc\xcf\xe3\xa7\x4f\x9e\x18\x9d\x2f\x7e\xb1\x06\x38\x4e\xb5\xef\xc9\x30\xa4\xf8\xfb\xba\x87\xa4\x57\x38\x1e\x2c\xf7\x83\x0c\x77\xdb\xda\x87\x71\xd8\xd1\x8b\x5e\x4e\x3e\x87\x43\xed\xe5\x20\x9a\x5c\xe0\x74\x99\x42\xae\xbf\x78\xfa\xe4\xe6\xe9\x13\x3c\xca\x30\x92\x3a\x43\x15\xe6\xb4\x2f\x7c\x18\xbe\x47\x3f\xfc\xc0\x3e\xac\x04\xe3\x50\xf4\x6d\xfb\xdd\xce\xd3\x27\x4f\xe8\x87\xda\x29\xc7\xd9\x43\x2a\xaa\xf0\x4c\x30\xa4\x1f\x28\x62\xf0\x5b\xc6\xe7\x4c\x8c\xb2\x8c\x18\x6b\x88\x46\xc3\x40\xb5\x7e\x9a\x5c\x65\x38\xad\x3f\x7d\xf2\x44\x8c\x58\x92\xe4\x2b\xbd\xf4\x7a\x92\x27\xff\x3c\xa6\x55\x6f\x58\xd6\xf1\x62\x16\xc5\x77\xf4\xc7\xd3\xa7\x4f\x6a\xea\x71\xec\x09\xa2\x1a\x91\xe3\x8b\x24\xcd\x07\xd3\x3c\xa3\x6f\xc8\xb2\xe9\xa1\x2d\xc4\xeb\xbe\x90\x5e\x7f\x1a\x45\x7d\xf2\x69\x65\x14\xf5\xa5\xf7\xa0\x0c\xeb\x41\xa7\xc8\x57\x52\x6a\x45\x7a\xa7\x40\x08\x46\xe7\x09\x80\x20\x3f\x5e\x3c\x15\x58\xbc\x4d\x92\xcf\xd3\x09\xca\x83\xfe\x08\x4b\x98\x1c\xbf\x3a\xfc\x85\x9d\xf9\xc4\xbb\x83\xf7\x3f\x7d\xb2\xbd\x3f\xfe\xf8\xea\xd3\xbb\x83\x5f\x3e\x35\x5c\x1f\x7c\xd7\x87\xa6\xeb\x43\xcb\xda\xb6\xab\x1d\xf9\xa3\xd1\x96\xfc\xd1\x68\x4f\xfe\xc8\xdb\x14\x43\xd3\x4b\xc6\x13\x72\x50\x1c\x99\x43\x64\x9b\x52\xad\x56\x98\x4c\xfb\x44\xea\x27\xb5\x8a\x02\xc0\x62\x65\x2c\x90\x6c\xa9\x10\x41\xe0\x41\x14\xa1\x97\xa8\xd9\xe9\xbe\x40\xd1\xd2\x92\x02\x5e\xc8\x88\xe8\x25\xf2\x9b\xeb\xc6\x37\xf2\x17\x9e\x46\x67\x68\x8b\xc0\x78\x89\xfc\x17\xea\x77\x7a\x95\x5a\x52\xab\x46\xab\xd5\xd1\xaf\xa8\x31\xf3\xfd\xbe\x5e\xbf\x78\xbc\x79\xaa\xf4\xfa\xe7\x60\xf4\x19\xbd\xde\xab\x35\x7f\x5d\xaf\xab\xbd\x9d\xd1\x60\x8a\xea\xbb\x48\x7b\xb9\xd0\x08\x48\x83\x9c\xf5\x93\x99\xfa\x11\x0c\x0d\x48\x9b\xb3\x08\xfd\x8a\x6a\xb3\xa2\x43\xec\x77\x53\xfa\xdd\x92\x7e\xb7\xeb\x5a\x67\x01\x4a\x2d\x9b\xa1\x1f\x7f\xfc\x11\xad\x43\xc9\x6c\x86\x7e\x40\x8d\xd9\x70\x48\x07\xa8\xdb\xd2\xaa\x90\xd5\x71\x3a\x23\x03\x99\xcd\xb4\x4f\x7c\xf1\x9c\x66\xf0\x7d\xf6\xe2\xa9\xb3\x53\xe3\xe9\x28\x8f\x26\xa3\x68\x00\x5a\x02\xb3\x7b\x33\x42\xc6\xe1\xe9\xec\xec\x85\xe5\x5b\x9b\x7e\x6b\x5a\x3f\xae\xd3\x8f\xed\xb3\x92\xd6\xb3\x69\x1f\x81\x7c\xe3\xa1\x71\x34\x43\x83\x64\x34\x1d\xc7\x99\x42\xfd\x32\x4c\x22\x29\xd4\x42\xe8\xd5\x73\x42\x33\x0d\x9f\x8f\x14\x7b\x6c\xf8\x8d\x86\x3e\xb4\x62\x25\xd3\xc1\xaa\xe5\x30\x31\xed\x3a\xfa\x42\x7e\xd3\xf1\x76\x54\xf1\xe5\x2a\x7e\x57\xaa\xe2\x77\x5d\x75\x9a\x72\x9d\xf5\x3a\x2a\xea\x34\x8d\x59\x17\xdc\x80\xd6\xc9\x4b\x46\x2a\x8a\x2f\xe5\xd1\x22\x8f\x95\x47\x6c\xb6\x2e\x8d\x0f\x23\xcf\x36\x7b\xd5\xe0\x2f\x9a\xca\x90\x96\x8e\xa8\xc2\x1f\x19\x8d\x55\x19\x56\x85\x75\x2a\xf5\xe6\x8c\xad\xc2\x56\x95\x8a\x73\x06\x58\x61\xb9\xac\x62\xd9\x28\xc3\x65\x01\xe8\x81\x71\x6a\x72\xc2\xef\x66\x56\x26\xc8\x18\xc0\xd6\x02\x1c\x10\xaa\x34\xd1\xaf\x28\x3c\x25\xff\x9b\xad\xa3\x5f\xd1\xac\x79\x76\xa6\x2f\x24\x28\x1b\xa1\x5f\xb7\xa0\xe0\x2c\x32\x0a\x28\x4c\x12\x7e\xde\xc0\x99\x56\xec\x2b\x1f\x52\x3c\xa0\x9d\x0b\xd1\xd1\x20\x89\xd9\x06\x53\xec\x4a\x47\xbd\xc3\xf7\x64\x8f\x68\xcc\x1a\x0d\x0f\x35\x66\x0d\x1f\xfe\x6d\xc2\xbf\x6d\xf8\x77\xdd\x03\x5a\x20\xff\x36\xe1\xdf\x36\xfc\xbb\x0e\xff\xfa\x7d\xf2\x6f\xab\x5b\x6c\x66\xcf\x9f\x33\xa4\x9e\xa3\xed\xdd\x63\x1a\xba\x1d\x51\x71\x08\x11\x81\x20\x8d\xf2\x8b\xf1\x0a\x2f\xb3\x5a\xa0\x42\x4a\x6f\x31\xf1\x61\x85\x3e\x48\x12\xc6\x0a\x9e\xe5\x34\x7a\x80\xe8\xf2\xa7\x30\x39\xc2\x19\xce\x37\x91\x63\x8b\x64\x83\x70\xfc\x39\x9a\x30\xcb\xdf\x64\x88\xe2\xa3\x04\x4e\x63\x17\x41\x86\xfa\x18\xc7\xe0\x1d\xc0\xee\xb7\x82\x38\x04\x13\xbe\x30\x0a\x51\x9c\xe4\xcc\x0c\xd3\x24\x05\x9a\xf7\x85\x43\xe2\xe6\xa2\x9f\x3e\xe3\xeb\x0f\x69\x94\xa4\x47\xd4\x02\x78\x6b\xab\x78\x6f\x25\x1d\x6e\x16\xa6\xcd\xa9\xd9\x01\x55\x7c\xe3\x7f\xdc\xe0\x70\xcb\xde\x7c\xf1\xd6\xc2\x9f\x3f\xe3\xeb\x9f\x93\x14\x8c\x18\x3f\xe3\xeb\x95\x2b\xf2\xdb\x5e\xec\x38\xfa\x1d\xb3\x52\x59\x74\xfe\x8a\x30\x20\xb4\x8a\xda\x65\xcb\x48\xf8\x01\xa4\x30\x40\x26\x58\x3e\x72\x1c\xc7\xe2\x99\x37\xb8\x84\xba\x95\x5a\x20\xfd\xcf\x06\x17\x98\x1c\x3f\x10\x11\xa1\x2d\x7d\xc8\x8e\x92\x2b\x02\xbb\xc6\x9b\x59\x22\xbb\xf4\xf3\xd2\x3e\xc8\x70\xed\xc3\xc2\x1b\x95\xc6\x59\x7a\x77\xaa\x2f\xd5\xc2\x44\x94\xa0\x43\x45\x0f\xfa\xf3\x25\xc3\x90\x3d\x5b\xa4\x10\xc4\xc8\x4e\x94\xa7\x83\x64\x2d\x47\xfe\x24\x54\x4e\xa1\xce\x19\x1d\x59\x98\x71\xf6\xc6\xc2\x6a\xdc\x0c\x0b\x49\xfb\x89\x01\x1c\xa2\xe9\xe8\x43\x29\xa3\xfd\x1d\x43\xfc\x1f\x02\x71\x27\xe6\x6c\x16\x8e\x92\x1c\x11\x92\x74\x17\xca\xe5\x3d\x40\xdd\x02\x4a\x21\x1f\x4f\xfb\x55\x20\x83\xf8\xc4\x61\x9e\x49\x7b\x1b\x7c\x28\x76\x2a\x26\xa3\x9d\x49\xbb\x98\x5c\x62\x5d\x29\x00\x98\x32\xc8\xec\xf5\x1c\x6c\xdf\x45\x33\x60\xdb\x65\xd8\xfe\xba\x05\x4c\xfc\x94\x0d\xf2\x6a\x41\x1d\x5f\x50\x83\xa1\x6e\x99\x6c\x54\x4c\x38\x90\x16\x5b\x77\x3f\xa2\x2e\xe1\x67\xda\x84\xa1\xad\x2d\xd4\x9e\x37\x69\xdf\xdc\xd0\xda\xfb\xec\x18\x71\xd7\x9a\x31\x68\x9d\x0d\xc9\x19\xfa\x95\xc8\x12\xe6\x22\x9a\xcb\xcd\x65\x99\xae\x9c\xcd\x44\xf1\xe5\x1b\x0b\xa7\x31\x5e\xbb\x99\x0d\x29\x5a\xf0\x1b\xf1\x54\xb0\x1c\xfe\xca\xc1\x75\x64\x86\xc5\xf8\xe8\xb2\xa8\x63\x23\x5e\x38\x32\xf2\x66\xfe\x51\x42\x34\x4e\x76\x72\xbf\x9c\xa9\x6d\x05\x37\x0f\xf1\x97\xa8\x0d\x8e\x2c\xf4\xa1\x8c\xf6\xd5\xb9\x38\xe5\x10\x98\xa4\xb9\x60\x47\x4a\x80\xa9\x42\xb7\xba\x86\x08\x29\xaa\xc2\xb5\x63\x29\x9d\xa1\x5f\xdd\x8b\xd3\xf1\xa7\x0a\xdf\xf6\x15\xa8\x23\xd0\x3a\x55\x97\xa2\x7d\x0e\x9c\x92\xac\x27\x4d\x0f\x8e\x07\xe9\xf5\x84\x5a\xc6\xca\x72\xde\x3b\x0f\x25\xc3\x61\x86\x73\x63\x66\xe8\x1a\x09\x93\x9e\xa8\x57\x14\xf6\xcc\xbd\xda\x2b\x4e\x88\xc5\x4f\xbf\xf8\xd9\x2c\x7e\xb6\x3c\x60\x31\xf2\x29\x43\xc1\x35\xc4\x8b\xe2\x4a\xb8\xe6\x55\x30\x41\xcd\x38\x04\xd9\xb3\x9d\x5f\x38\x84\x18\x42\xdf\xef\x4e\x29\x18\x22\xbf\xe8\x43\xaa\x7c\x53\xcb\xb6\x4a\xca\xb6\xac\x47\xa2\x2a\x43\xa8\xd2\xaa\xa7\x12\xa8\xfa\xe8\xab\x8f\x4d\xf5\xb1\xe5\x09\x85\x85\xb1\x79\xaf\xae\xa2\x03\x72\xf2\xfd\x26\xc6\xc8\x3e\xe9\xca\x30\x59\x67\xdd\x43\x77\x23\x37\x1b\xd1\xb0\x03\x41\x65\xc9\xda\x32\xb0\xaf\x31\x8b\x15\x0a\x17\x92\x54\x54\x27\x98\x5a\x74\x5c\x0d\x69\xb0\xce\xe0\xf5\xaf\x0a\xb3\x6d\xd8\x34\x40\x99\xaf\x4f\x87\x56\xcb\x98\x1f\xa8\xd5\x54\x6b\x35\xf5\x5a\x56\x6d\x53\xd6\xd2\xa7\x53\xab\xd5\xb2\xa9\xa1\xde\x68\x67\x07\xfb\xd1\x5f\xde\x02\x6d\x27\x86\x23\xcb\x19\x47\xec\xbf\x74\x54\xb7\x90\xff\x82\xfd\x7c\xc9\x67\x88\xbd\x70\xec\xbb\x30\xc7\xd1\x30\x07\x4a\xf7\x1c\x8a\xb2\xd2\x89\xe3\xa8\xe7\x64\xf2\x24\x75\x4d\x43\x48\x5e\xbf\x4a\x8a\xae\x5a\xe6\x1b\x72\xd7\xaf\x92\x52\xab\x96\x35\x75\xa9\xeb\x57\x49\x7f\x95\xb5\xa4\xd7\xc6\x36\xbc\xb4\x64\xdb\x00\x00\x39\x5f\x45\xce\x77\x20\xd7\x9c\x83\x5c\xab\x14\xb9\xc6\x2d\x91\x6b\xaa\xc8\x35\x1d\xc8\xb5\xe6\x20\xd7\x28\x45\xce\xbf\x25\x72\x2d\x15\xb9\x96\x03\xb9\xc6\x1c\xe4\xfc\x52\xe4\x9a\x73\x91\xb3\x92\xee\xc7\x09\xd8\x10\x65\x79\x90\x63\xb3\x00\xb0\x93\xbc\x61\xe9\x18\xb0\x8c\x5c\xd7\xa3\xc1\x17\x32\x17\x79\xd3\xf6\x85\x0c\x44\xae\x6b\xc7\xad\x4a\x14\xeb\x7a\x9a\xc3\xfb\x60\xf9\xd4\xe8\xc9\x43\x5a\x3b\xfa\xa9\xc5\xb2\x7c\xf4\x63\x8b\xb9\x82\x94\x73\x4b\xb1\x84\xea\xd5\x28\x41\xac\x1f\x8e\x9d\xef\xc6\xce\x5c\x3f\x06\x76\xc6\x12\x52\xb1\x6b\xdc\x06\xbb\xa6\x84\x5d\xd3\x8d\x9d\xb9\x80\x0c\xec\x8c\x35\xa4\x62\xe7\xdf\x06\xbb\x96\x84\x5d\xcb\x8d\x9d\xb9\x82\x0c\xec\x8c\x45\xa4\x62\xd7\x9c\x8f\x9d\x49\xad\x98\x07\xb6\xb6\xcb\x25\x74\x1b\xb6\xac\x23\x5d\xc8\x31\x96\x93\xba\xb9\x5a\x56\x95\x21\xfa\xb4\x5c\xb2\x0f\x3b\x0a\x6f\xa2\x66\xa7\xbb\xda\x6a\x32\x0d\x74\xdd\xa6\x0a\xe6\x12\x8b\x10\x90\x32\xe6\x38\xcc\x54\xc3\xcf\x32\x96\x1a\x0a\x41\xb6\xef\x61\x30\xc0\x42\x47\x2c\x80\xfc\x37\x9e\x05\xe3\x89\x38\x29\x17\x1f\xf8\x9c\x52\x58\x39\x9e\xe5\xd2\xed\xf6\xca\xf6\xee\xf1\x0a\x3b\x47\xd4\xc6\xdc\x22\xfd\x33\xbe\xf6\xd0\x60\x78\x2e\xa4\xf9\x02\xca\x64\x14\x10\x24\x66\x39\xd2\xa1\x30\x09\xbf\x56\xb4\x63\x03\xc4\x74\xda\x3d\x8b\x12\xfb\x13\x8d\x9a\xba\x8f\x47\x13\x9c\xd6\xb6\x77\xe9\xb5\x3e\xd5\xd9\x3f\x7d\xc2\x6c\x56\xe4\x26\x5f\x3c\x7d\x0a\x11\x70\xc1\x80\x44\xb1\x2a\xd8\xec\x34\x3d\x6e\x97\xb0\xd9\x01\xdb\x11\xc9\x32\x61\xb3\xd3\xf6\x0a\x93\x84\xcd\x0e\xb8\x30\x8e\xc3\xce\xf7\x9b\x5d\xff\xe6\xcc\xeb\x34\xef\x64\x2d\xf2\x35\xcd\x44\x1e\xcc\x98\xe3\x2b\x9a\x65\xd0\x95\xf0\x1c\x31\x03\x0a\xd2\x3c\x1a\x24\xe3\x49\x12\x43\xc8\x75\xf2\x6d\xf5\xe9\x13\x31\xef\xa3\xa8\xbf\xc2\x8a\x7e\xf9\x22\x1b\x00\x08\xa7\xcf\x7b\x36\xee\x08\x32\x5c\x58\x75\x04\x19\x96\xbe\xfd\x9c\xa4\x21\xb8\xa5\x8b\x02\xe2\x8d\x0c\x61\x3a\x04\x7b\x31\xa0\xf5\x6d\x7e\xcb\x53\xc0\xb4\x7e\x56\x30\xc3\xe0\x59\xd5\x23\x0b\x55\x7a\xff\x31\x1f\xae\x03\x14\x1c\x0f\x56\xc8\x83\x86\x75\xb7\x2d\xbe\xd2\xc7\x32\x43\x14\xf1\x65\xf7\x72\xf2\x66\x67\xaf\xb8\x6c\xa2\xcf\xd6\x1b\xac\x7e\x46\xcd\xf3\xc8\xb2\xe2\xb7\x58\x39\x1e\x4f\x46\x41\x6e\x63\x50\x22\xc8\xf4\x1f\x31\x0b\xc8\xc3\x35\xa8\xe0\x54\x20\x78\x1d\xe8\xfd\xa2\xdf\xf1\x0a\x0f\x30\xb9\x89\xda\xa8\xe6\x37\xd7\x51\x3f\xca\xb3\x7a\x19\xc0\xe8\xd2\x02\xef\xe0\xa7\xdb\x82\xfb\xb4\xfb\xbe\xf7\xe9\x97\xbd\xc3\xa3\x77\x9f\xde\x1d\xee\xec\xa2\x6d\x08\x6d\x90\x07\x71\x8e\x52\x3c\x49\x71\x86\xe3\x3c\x8a\xcf\xb9\x22\x86\x90\xe1\x38\x09\x8b\xbe\x5b\x61\xee\xec\x56\x82\xc9\xd8\xa9\x01\x53\xba\x14\xd4\x4c\x8e\xc4\xa3\x9d\xa2\x2c\x97\x84\xc5\x6c\x52\x74\x7b\xe0\xf6\x3d\x4d\xc1\xe0\x41\xe4\xf8\x90\x8b\x28\xc5\xa5\xde\x09\xba\x27\x73\x80\x4e\x2e\x30\x19\xf5\x3c\x41\x53\xe6\x26\x40\x58\x00\x22\x85\x01\xb4\x02\x72\xb5\x78\x18\x0c\xcf\x37\x81\x74\x39\xae\x75\x79\x47\x35\xb0\x85\xed\x22\xa3\xb0\x19\xf9\x45\xb1\x6b\x32\x6c\xe8\x53\x7b\x4c\x09\x77\x42\x7a\x04\xf9\xcf\xf8\x7a\xc5\x5a\x96\x7b\x86\x0e\x86\xe7\xa8\x76\x08\xad\x04\xa3\x3a\xd4\x19\xd8\x06\xaf\xe2\x18\xa8\x6d\xf1\x38\xa2\x74\x42\x6f\x08\x89\xf0\xde\x11\x42\x19\x94\xf5\x89\x9c\x2b\xa2\x81\xfb\xbb\x2a\x25\x98\x05\x90\x22\x2d\xc8\x7b\x3c\xbf\x7a\x5e\xa1\xdb\xf4\x2e\x1d\xe6\x24\xad\xb1\xcb\x33\x18\x42\x0f\xfd\x81\xa2\xcb\x4d\x14\x5d\x16\xbc\xf1\x46\x31\x3d\x50\xe6\x5b\x85\xb4\xa9\x84\x85\x62\x92\x83\xae\x01\x90\x13\x87\xd0\xfa\xec\xc6\x59\x5d\xab\x16\xd9\x43\x97\xd0\x2a\xd2\x93\x63\x21\x3e\xd2\xd3\xfd\xd2\xd3\x0e\xbe\x2f\x7a\x12\x90\xee\x46\x4f\x2a\x9f\xbe\x05\x3d\x1d\xc4\x51\x1e\x05\xa3\xe8\x77\x9c\xa1\x00\xc5\xf8\x6a\x74\xcd\x30\x0c\xd9\x70\xcc\xa7\x25\xbe\x6b\xcc\x86\x49\x3a\x7e\x97\x84\x18\xed\x52\x5f\x35\x08\xd3\x5c\x70\xba\x24\x95\xe9\x14\xac\xab\xc1\xcd\x8f\x53\xad\xd8\x64\xec\x64\xf8\xcd\x91\xec\xbd\x91\x55\xcd\xfc\x60\xe3\x14\xb7\x24\xb8\x28\x8e\x14\x0b\x1b\x31\x4d\x12\xb9\x58\x54\xd4\xdb\x93\x09\xa1\x05\x18\x2d\x9e\x98\x3a\xb3\x5c\x33\x90\x21\xde\x12\x3f\xf9\xa6\x48\x69\xd0\x3c\x15\xe7\x44\x72\xa6\x86\xf5\x49\x3a\xa6\xd3\x1e\xd8\x74\x37\x94\xbe\x0b\x92\xda\x2a\xc8\xeb\x85\xad\x24\xb5\xa3\x01\x5b\x19\xeb\x59\x3c\xa2\x84\x4e\x3d\x00\x6c\xfd\x00\xfb\xa2\x5a\xe5\x85\x03\x36\x3a\x2a\x1f\x86\x58\x0e\x99\x68\x09\xb4\x67\x77\x24\x1f\xb6\x04\x4d\xdc\x94\x19\x4e\xab\x18\x51\x51\xa3\xa2\x30\xc8\x03\xd4\x07\xd9\x4b\x2d\xe1\x90\xc7\x00\x34\xcd\x74\xc1\xbd\x9d\x75\xc0\x1f\x70\x0a\x73\x39\x48\xe2\x41\x8a\x73\xbc\xcc\x86\x63\x94\x9c\x2b\x4c\x59\xba\x97\x3a\x5a\x6c\xac\x21\x9e\x06\x60\x4e\xdd\x5b\x18\x4f\xc1\xa1\xc4\x52\x70\xb8\xc0\xa6\xf7\x25\x63\xae\x30\x04\x28\x53\x76\x12\xde\xc0\xdb\x60\x0d\x48\xe0\x2b\xec\x5c\x12\x7f\x12\xb0\x68\xd0\x2c\x16\x8c\x20\x8a\xcf\xef\x81\x9b\x14\x9d\xdf\xe2\xe4\xc1\xe0\xd7\x9e\x91\x36\x9f\xa9\x64\x52\xa5\xde\x15\xc7\xdc\x49\x61\xac\xe4\xa6\x16\xe5\x95\x0e\x9d\x83\x7b\xe0\x38\xb4\xcd\x7e\x00\x5f\xe4\xea\x36\x9a\xa2\xed\xa1\xe0\x32\x88\x46\x41\x7f\x84\xa9\x19\x62\xe6\xde\x16\x3f\xf1\xce\x54\xa6\xaa\xbd\x28\x66\x1b\x5f\xe9\x3e\xc5\xe0\xaa\xfb\xcc\xfb\x24\x67\xde\xd1\x34\x68\x1a\x85\x54\xec\x1a\x28\xca\x10\x1e\x0e\xf1\x20\x8f\x2e\xf1\xe8\x1a\x05\x28\xc4\x59\x9e\x4e\xe1\xd9\x43\x29\x0e\xc2\xe5\x24\x1e\xe0\x4a\xfb\x4c\x55\xea\x05\x34\x1e\x8a\x86\x29\xf0\x87\xa6\x64\x3e\x92\xb5\xea\x44\x2c\xaa\x2c\x4a\xfd\xa2\xe2\x7c\xf2\xe7\x45\xab\xd3\xff\x5e\x31\x17\x53\x28\xa4\x96\x88\x86\xa5\x00\x50\xe9\x6a\x51\x8a\x5a\x2e\x4a\x16\x60\xc8\x10\x0f\x89\xa0\xca\x16\x1c\x0e\x59\xbc\x4c\xce\xa9\xf7\xa4\x09\xb1\x2e\x3e\xb3\xf6\x5c\x65\xb3\xdf\x5c\x5f\x6d\x35\xe5\x4f\x54\x25\x62\xfb\xa2\xc9\x41\x9b\xc8\x57\xbe\xaa\xf2\xef\x26\x6a\x56\x39\x3b\x65\x56\x55\x76\x30\x5f\x91\x8d\x9c\x6b\x93\x9f\x5a\xd8\x48\x9f\x5c\x60\x49\x28\x60\x89\xb6\x02\x74\x01\x5a\x63\x22\x64\x56\x58\x8a\x5c\x84\xdd\x8e\x39\x3e\x10\x60\x80\x2f\x6b\x22\x34\xb1\x75\x6d\xe9\xd0\x57\x38\x2c\x31\x6b\x6f\x53\xe5\xa9\xe9\xc8\x0d\xd9\xd6\xb9\xca\x94\x7a\x9b\x4e\xbf\x29\xf2\x27\x3e\x65\x78\x84\x07\x39\x6d\xf8\x38\x4f\x83\x1c\x9f\x5f\xd7\x5c\xe6\xda\x92\xf6\x19\xc4\xc5\x2d\xf4\x8c\xb2\xd2\x67\x4e\xf3\x30\x36\x1b\x1f\x82\x2c\x23\x6c\xe2\x55\x90\xe1\x50\xf1\x98\x93\xff\xca\x8d\xc3\x18\xa8\x63\x9c\xc2\x81\x8b\xec\x6a\x6e\x48\xe5\x8b\x5c\xcf\xed\xc7\xee\x33\x4a\x6c\xd4\x5d\x48\x31\x72\x92\x19\x9b\x79\xc3\x52\x66\x37\x5a\x04\x01\xb3\xcf\x83\xb8\xb8\xa1\x28\x7a\xc8\x7d\x81\xa3\x8f\x81\xe7\xb0\xf4\x64\x64\xbf\x69\xf4\x5f\xbb\xcf\xb9\x13\xda\xea\x4d\x91\x87\x4a\x6f\x8c\x74\xcc\x2d\x13\xaa\xb3\x6d\x99\x4b\xd6\xea\x4c\xc3\x6b\xbf\x7a\x53\x75\xd8\x59\x9e\xe2\x60\x7c\x2b\x55\x36\xc8\x50\x4c\xf9\x2c\xdb\xe0\xb7\x9a\xcb\xfd\x88\x1a\x6c\xab\x27\x1a\x2a\x9d\x40\x18\x6b\x49\x33\xed\xa3\x5a\xab\xa9\x2a\xa6\x25\x85\xef\x31\xe0\xa7\xa9\x7d\xf5\x97\x25\x1e\x21\x7b\x96\xbd\xd6\xb6\xc3\x72\x11\x71\x12\xa4\x70\xdc\xb2\x09\x88\xe6\xf6\x06\xc7\x9b\xc2\xba\x8a\x0b\x8d\xdf\x7d\xf7\x6c\x38\x9a\x66\x17\xcf\xaa\x6d\x73\x14\x8a\x6b\xa3\x13\xc3\xbc\x89\xfc\xb2\x79\x85\x73\x2d\x64\x35\x9d\xc8\xb7\xa5\xb2\xf2\xfc\xd3\x98\x9e\x7d\x7b\x2b\xec\xc7\x1f\x37\xf3\x29\x44\xf1\xd8\x81\x7a\x06\x95\x48\x6d\x48\xb7\x9b\xec\xa0\x6d\x38\x07\xb3\xf7\xb2\xd2\xbb\x4c\x41\x2f\xab\x28\xc7\x3c\x39\x57\x21\x5f\x2f\xbc\x9b\x6e\xab\x3d\xb2\x2a\x04\xf5\xcc\x32\x85\x82\x1f\xa8\xfa\x2b\xec\x87\x7c\xa6\xf8\x76\x07\x7a\xd8\xde\xab\x9e\xa1\x8a\xe6\x1c\x25\xba\xa4\x5e\x3b\xb7\xd1\x3c\x17\x30\x4a\x75\x85\xa2\x2e\x57\x34\x49\xf5\x6e\xa5\x71\x16\xd3\x59\x1c\x90\xfe\x33\xa7\xb3\xd0\x04\x2f\x38\x9d\x56\xc5\x6f\xc5\xe9\x14\x75\xef\x30\x9d\x65\x0a\xdf\x6a\x57\x07\x5f\x75\x3a\xef\x3c\x5d\x25\x4b\x60\xce\x7c\xe9\x7a\xd3\x92\x49\xa2\x9b\x89\xd0\xf3\x0e\x6c\x62\x1d\xb3\xba\xbe\x44\x5b\x28\xba\x94\x67\xab\x6c\x8b\x60\x3b\x26\x8d\x2b\xdd\xbb\x08\xa2\x18\x52\x9e\xb8\xee\x5a\x5f\x81\xdd\xc0\x27\xde\x79\xb4\xe5\x0e\x3e\xa0\xab\xd8\x94\x1d\x84\xd4\x35\x88\x41\x1a\x9a\xa2\x31\x6d\x97\x10\x77\xa2\x2f\xca\x38\xca\xab\x1e\xdf\x0e\xb4\x93\x90\xd4\x84\x32\x77\xa4\x57\xaf\x7a\x96\xbd\xc7\x04\x4f\x9b\xf8\x20\xc2\x7f\xe6\x5c\x8d\x41\xa9\x34\xc8\x99\x51\xf7\x8a\x5e\xc7\x80\xa1\xd1\x2c\x95\x8e\x84\x56\x84\x09\x4b\x09\x97\x91\x90\xca\x09\x91\xf5\x86\x84\xd9\x65\x11\x20\xec\xe7\xd5\x05\x66\x91\xf7\x29\x7e\x10\xc8\x33\xab\x80\x9c\xb9\x30\xec\x05\xc9\x1f\x4c\x25\x13\x75\xa8\x37\x00\xa4\xc7\x83\x2e\x08\xd7\x06\x5d\x96\x95\x27\x03\x15\x2a\x40\xc3\x4c\x5e\x85\xe2\xb4\x85\xb6\x3a\xc0\x22\xfd\x86\x44\x5e\x48\x0e\xc3\xd9\x42\x88\x15\x9a\x1c\xf1\xca\x61\xce\xfa\xcb\xe1\x11\x9c\x97\x19\xd1\x99\x65\x66\x49\x0a\xfd\x2a\x14\xdd\x1e\x52\xfa\xe5\x15\xcd\xda\x84\x7e\x86\x87\xec\xeb\x52\xd3\x47\xd7\x8a\xd9\x11\x1e\x63\x90\xc2\x61\x77\xa5\x24\xc0\xae\xa2\xe0\xb4\x0f\x0e\xed\xf0\xda\xac\xce\x25\x58\x7c\xc9\xc3\xce\x53\x66\x4a\xf3\xc9\x73\xbc\x85\x29\xa0\xb3\x03\xb2\xe7\xce\xdc\x75\x1b\xe2\x0a\xeb\x56\xec\x53\x8f\xeb\xf6\x71\xdd\xa2\xdb\xaf\xdb\xbb\xac\x0e\xb0\x10\xbe\x88\xb2\x85\xd7\x86\x15\x13\x46\xd1\xc0\x45\x7e\x39\x3c\x72\x72\x00\xd9\x83\xcc\xe0\x00\x77\x65\x3b\x56\xcc\x4e\x8a\xa1\xe9\xe3\x41\x32\x66\x4b\x87\xb0\x85\x28\x99\x66\xd5\x99\x87\x18\xac\xaa\xec\x41\x90\x12\xef\x46\xcd\x89\xfb\x42\x1e\x50\x20\x22\x71\x69\xc9\xe6\xe1\x7f\x91\x24\x19\x46\xe3\x68\x46\x64\x21\x4b\xff\xc0\x13\xd4\x14\xd2\x90\x4c\x88\x4c\x0a\x73\x91\x5d\x72\x09\xd2\x29\x39\xe9\x64\xd3\x7e\x86\xff\x3d\xc5\x71\x6e\x55\x31\x20\x55\xb4\x93\xb2\x7a\xa8\xa3\xe8\x54\x0d\xca\x28\x69\xb3\x32\x5f\xd5\x4f\x76\x36\x1b\x56\xb6\x18\x49\xc5\x6a\xb3\x46\x4a\x22\x7f\x30\x81\x85\xf5\x78\x74\x86\x7e\xdd\xa2\xf5\x4e\xa3\xd2\xd0\x25\xc5\x6f\x6e\x02\xfd\xaa\xc7\xca\x2b\x01\x4d\x24\xd1\xf6\x43\x10\x86\x64\x02\xe7\x28\x40\x26\x90\xe5\xaa\xb7\x42\xff\x6b\x57\x7f\x7c\x78\xd3\x3b\x46\xff\xa7\xb3\xba\x86\x26\x0c\x68\xc6\x74\x79\x36\x98\x1f\x3e\x0f\xb2\x35\x90\x93\x27\x41\xb8\xc2\x9f\x4a\x64\xe3\x0f\x01\xbf\x7e\x9e\x66\x3c\x74\xbe\x08\x84\xc2\xcc\x95\x21\x6e\xb2\xc0\x63\x21\xfb\x2b\x80\x2c\xdf\x3e\x13\xb4\xac\x95\xec\x7a\x3c\x16\x02\x4a\xba\x8f\x04\x40\x99\x08\x66\x49\x06\x05\xc2\x59\x3e\xf0\xb1\x59\x1c\xbe\xc4\xb8\x92\x5f\xc5\xf5\x9a\xa7\xc5\xcd\x52\x2e\x98\x83\x50\xbf\x5c\xbb\x35\x03\x11\xd5\x68\xac\x93\x2d\x69\xbc\x5c\x31\x43\xa6\x71\x2e\x68\x07\xfc\x8a\x4c\xa8\x31\x23\x58\x03\x28\x7d\xb1\x4c\x53\x4e\x8b\x08\x2b\xff\xd0\x0a\xd8\x9a\xa5\xf7\x42\xbc\x5d\x33\xf4\x02\xcd\xf4\x06\x5f\x09\xbd\x40\x04\x14\x05\x8b\xc2\xd7\xc5\x78\xcf\x1c\x5c\x8c\xf7\xe0\xd6\xa2\xbc\x9d\x8b\x59\x29\x52\x59\x79\xf8\x82\x82\xfd\xa8\x6d\xa2\x08\x2d\xb9\xdc\xf2\x65\xe8\x34\xcc\xbd\xf4\xa6\x44\x7a\xd5\xb0\x43\x5b\x85\xed\x3b\x3f\xfc\xcb\xa0\x3d\x15\x25\x9b\x19\xc2\x76\x18\xda\x07\x01\xe6\x7a\x90\xc4\x83\x20\xe7\x30\x2b\x6b\x60\x3e\xc6\x13\xc1\x50\x60\xc9\x5e\x04\x21\x0d\x64\xc4\x16\xea\xd7\xe1\x32\xd3\x58\xe7\x33\x5f\x85\x23\x40\xb3\x15\xae\xdc\xa1\x9c\xce\x12\x6c\x7c\xe0\x35\xce\x95\xc4\xc5\xd2\x22\x86\x18\xb0\x68\x14\x64\x39\x3c\xcf\x5f\xd3\x85\x78\x7d\x5a\x53\x97\xf3\x32\xf2\xeb\xd4\xc5\xec\x8c\x39\x83\xd9\x3c\x89\xa9\xe0\xe0\xa6\x98\x02\xdc\x96\xbe\x06\xa5\xcd\x94\x6e\x9b\x0b\xea\xf9\xff\x8c\x8b\x20\x9b\x8b\x82\xfd\x66\xc1\x76\xab\x50\x76\x0f\x74\x7f\x46\xff\xef\x92\x10\xdf\x50\xf5\xe0\x89\x38\xad\xd1\x4b\x11\x38\x49\x48\xdd\xe9\xbd\xea\xb9\xa0\xb0\xb9\xba\x11\xf4\x45\x60\xe9\xc2\x86\x09\x11\x48\xde\x41\xe0\xe0\x47\xc0\x06\x40\x32\x9c\xd4\x08\x9c\x60\x0a\x98\x79\xda\xa9\x8e\xb6\x6d\x34\x71\xa3\x78\x23\x2c\x60\x18\x48\x27\x5a\xfd\xd8\x93\xac\x0f\xcb\x6d\x00\x4b\x02\x9c\xa9\xf6\xa1\x16\x3f\x4e\x90\x9b\xc9\x08\x28\x6a\x51\xa4\x2a\x76\xc9\xf7\x31\xd8\x7e\x3a\xf0\x2f\x26\xd6\x3c\x0c\x18\xb6\xa4\x5c\xd2\x56\x8d\x4b\x9c\x27\x06\x02\x15\xb6\x44\xd0\x68\xc0\xa9\x5c\xbb\x9b\xb1\x4b\xfb\xab\xcf\xcb\x9b\x57\xad\x57\xea\xe8\xf9\xea\xc2\x18\x08\x55\x8b\xe3\x2c\xf3\x06\xe3\x09\x0a\x72\x34\xc2\x84\x0b\x26\x31\x5f\x01\x2c\xcb\x07\xb5\x04\x85\xfd\x1a\x18\xae\xc9\xb7\x90\x38\xdf\x8c\xa3\x98\x1a\x89\xb2\x43\xbc\x11\x2e\x51\x7d\x64\x95\xe8\xf4\x49\xf8\x53\x42\x1a\x83\xfd\x31\x3d\xf2\x46\x97\xe8\x87\x1f\xac\xfa\x78\x3d\x50\xc7\x87\x5b\xe9\x32\x0a\x4c\x54\x65\x8a\xf3\x7c\xae\x37\x5b\xf5\x4a\xda\x2d\x92\x16\x22\x89\x30\x94\x66\xaf\x2c\x04\xcd\x9b\xbb\x5f\x42\x5e\x5d\x25\x07\x19\x9a\xee\xcb\x25\x72\x81\xbc\xce\x4c\xbf\x40\x02\x87\xdf\x73\x75\x10\xfc\x2a\x9e\xda\x08\xba\x4e\xc9\xb7\xba\x8c\x7f\xb8\x65\xf5\xb0\x78\x5b\xdb\x03\xc9\x6f\xce\x0c\x50\xf9\xc8\xd6\xde\x3c\xcb\xbf\x3b\x5a\x2a\x80\xe9\x1d\x93\x3d\xec\x66\x28\x68\x90\x8c\x46\x98\xd2\x7f\x32\xe4\xa2\x01\x88\x9a\x18\x72\xe9\x95\x89\x1e\x92\x28\x2a\x39\x79\x93\x6d\x34\x0d\xae\xa4\x57\x56\xbf\x44\xbb\xeb\x07\x75\x40\x17\x42\x4a\x95\xda\xc5\xc5\x23\x64\x78\x60\x5c\x90\xd6\x27\xeb\xd3\x30\xc7\x75\x01\xca\x82\x11\xc5\x1e\x7e\x00\x30\x50\x49\x06\x34\xfc\x28\x4e\xa3\x4b\x2a\xab\x70\x8e\x61\x05\xc8\xaf\x52\x0b\x39\x5f\xb2\x1c\x34\x63\xad\x56\x93\x6b\x6e\xd3\xb3\x72\xf9\x66\x70\x81\xc7\xb7\x83\x6b\x17\x38\x99\xca\x1c\x2c\xa6\x87\x12\x3c\x2b\x08\x9a\x93\xf1\xa6\xc8\xd9\x48\x4f\x31\x54\xc4\xe2\x6f\x75\x31\x6c\x90\xc4\x97\x38\xcd\x15\x19\x96\x66\xbb\xe3\xc6\x94\x60\xf1\x49\xad\xff\xdc\x6e\xab\x1f\x68\x15\xd5\x79\x55\xbc\xac\x68\x0f\x33\xdf\xc5\x4a\x45\x6d\xfe\xb1\x4e\x78\x37\xc9\xf8\x68\x76\xa2\x41\x2c\x92\x58\x4d\x92\x2c\x8b\xfa\x23\xec\x5e\xb1\x96\xa6\x16\x73\x6e\x2a\x06\xca\xb4\x07\xa5\xdf\xf8\x09\xfc\x0f\x03\x0a\x12\xea\x73\xb2\x82\x37\xa5\xdf\x85\xc3\x93\xb5\xd2\x67\x7c\xbd\xa9\xfa\x45\x59\x8b\x69\x9e\x52\xf6\x42\x64\x19\x6f\xc2\xbf\x73\x0a\x8a\x55\xb9\x69\xba\x73\xd9\x6b\x30\x11\x5e\xb7\x4c\xb0\x17\x16\x72\xbd\x7a\x74\x7e\xd3\x3b\x5e\xb3\x57\x90\x58\x78\xdb\x5e\x42\x2c\x1c\x09\x28\x7d\xb7\x72\x38\xc1\xf1\xf1\xf1\x5b\xa3\x5a\x75\x67\x32\x79\xfa\xed\x82\xd7\x38\x9a\x1d\xc4\x6a\xb9\xca\xa6\x47\x74\x15\x67\x8b\x2d\x63\xe4\x5c\x37\x26\x2b\xd1\x7c\x03\x1d\xdc\x84\x1c\xea\xdc\xc0\xb9\x81\x2d\xf7\xca\x80\x5d\x01\x7e\x47\xc3\x48\x5f\xe3\x25\x70\x20\x09\x58\x46\x33\x80\x41\xf6\x38\x5c\x78\x51\x16\x18\xc7\x09\x7d\xa3\x31\x40\x96\xb3\x1f\x97\x71\x8f\xaa\x4b\x9a\x22\x2f\xae\xe9\xd8\xda\x5e\x42\xcf\x9e\xd9\x7d\x2b\xac\xe5\x57\xf2\x84\xe6\x1b\x72\xb9\x72\xcc\xa9\xe5\x20\x55\x27\x61\xf2\x8a\x32\x71\x8a\xb1\x71\x59\x55\x15\x25\xd0\x97\x2f\x94\x5c\x8b\x3a\x2b\x7c\x12\xaf\xf9\xb1\xd7\xd0\xd1\x58\xe5\x24\x4a\x65\xf3\xee\x35\x68\x3b\x70\xb5\x21\x7e\xda\x6f\x37\x58\xcf\x6d\xc4\x69\x03\xcd\x8a\x8b\x54\xc6\xb0\x7b\xa9\x83\x58\x7e\xdd\x21\x56\x5d\xe0\x5e\x72\x31\x6f\x66\x79\x90\x8c\x27\x41\x0e\xdb\x4b\xd5\x65\x28\x6f\x0b\xda\x26\x26\x89\x3f\x55\xf7\x44\xdb\xf2\xbb\x0d\x72\xf7\x65\x38\x98\xd0\xb6\x8f\x39\x79\x3b\x08\x59\xa2\x2e\x17\x6f\x54\xe8\x5b\x14\xaf\xcc\x7d\xe7\xa8\x65\xe4\x48\x4b\xca\x12\x2c\xbe\xd8\x02\x35\x12\x71\x57\xab\x40\xde\xd9\x8e\xb1\xd0\x5f\xf3\x10\x4b\x8a\x3b\x55\x2d\x57\x52\xb4\x1a\x43\x7b\x7f\xda\x98\x75\x5a\x5d\xbf\x3b\x58\x83\xc4\x06\xdd\x4e\xb7\xdd\x19\x76\x86\x67\x75\xae\x8a\x07\xd0\xfc\xa1\xe8\x87\xe3\x1c\x59\x01\x05\xe7\x58\x38\x0e\x5f\xa2\x6e\xc1\xc8\x68\x58\x9b\xc5\xf7\xbc\xb2\x35\x26\xfb\x2b\x2d\x2a\x3c\xf2\x75\x52\xd0\xe9\xad\x97\x8c\x1a\xb3\x81\x2f\xe8\x5b\xac\xe1\xfb\x0d\xe0\x60\x0a\xa3\xda\xd2\x9b\x04\x69\x86\x6b\xca\x42\x2d\xb9\x98\x4c\x33\x45\xf1\x53\x54\xb3\x7a\x25\x90\xe2\x88\xc6\xf0\x9a\xb3\xe8\x28\x61\x18\xc8\x94\xa9\x57\xcb\x20\xf2\xcb\x38\xe9\x30\xcc\x92\x42\x18\xe0\x4e\x70\x96\x53\xdb\x86\x60\x64\x59\xa0\x1a\xcc\xd3\xc6\x19\xda\xda\x42\xc5\xda\x43\x3f\xfc\xa0\xb7\x7b\xea\xb3\x32\x7c\x4d\xba\x54\x50\xbb\x33\x7a\x81\x61\xb6\x8c\x54\x0e\x63\x2c\x7e\xad\x45\x66\xca\xd3\xf4\x50\xbb\x5e\x62\x5d\x97\x5c\xb2\x23\x3a\x5c\x05\x15\x30\xcc\xf2\x06\xfc\x09\x34\xd0\xd0\x6f\xad\x8d\xe2\xca\xad\x8e\xdf\xad\xc6\x28\xac\x47\x23\xc7\x31\xc8\x93\x4e\x27\xaa\x68\x5e\x7a\x57\xc4\x17\xe1\x55\x1a\x4c\x26\x20\x47\x06\x39\x6b\x5e\x56\x99\xa0\x80\xec\xf4\x99\xe4\x95\x56\xba\x7a\x15\x57\x1f\xc3\x95\xad\x70\xf8\xb1\x7d\xaa\xea\x40\x72\xeb\xcb\x1e\x21\xf4\x70\x19\xbf\x4c\xaa\xe7\x3a\x02\xb9\xb7\xac\xb3\xd4\x21\x34\x0e\x29\xd5\x88\x03\x46\x71\xb1\x63\x39\x38\x95\x85\x88\xd2\xbd\x17\x01\xa1\x4d\x43\x54\x93\x26\xb6\x34\xa8\x14\xbb\x76\x20\xf3\xc6\xbc\xe9\xee\xe2\xa1\x5a\x28\x9f\x2c\x47\x9d\x12\xef\x73\xd6\x34\xb5\x41\x61\xbf\x0b\xbf\xf3\xbf\x48\x0c\x17\xfb\x16\xb6\xfd\xe7\x6e\x60\x64\x59\xda\x35\x2a\xe6\xb2\x12\xfe\x95\xa6\x36\x42\x71\xb5\x74\x9c\xc2\x1e\xae\xc1\x22\x48\x8d\xae\x4e\xf8\xaa\x8d\x7b\x62\xb5\x39\xa4\x81\x12\x65\x87\xc5\x39\xd6\xed\xc5\x7a\xbb\x10\x3a\x0b\x45\xcf\xd9\xb5\xd9\xaf\x4b\xd1\x0d\x92\xc2\xf9\xc4\x16\x00\xcd\xea\xb3\x6a\x88\x25\x85\x67\x86\x08\x90\xc0\x3a\x7b\x1b\xc9\xa4\x07\xfd\x2b\x60\xc2\x15\xb0\x01\x85\xd9\x1b\x11\x8e\x2b\x1c\x73\x5d\xfb\x51\xf5\xed\xb4\x6c\xd3\x56\xf6\x57\xb3\x20\x57\x2d\x5a\x3e\x11\xb2\x12\x7d\x5b\x89\x2e\x2d\x45\x24\x1d\x21\xa3\x17\xb3\x0c\xd5\x0a\x16\x80\xe0\x42\xd4\x2c\x26\xf4\x81\x45\x49\xf6\xca\x52\x58\xd2\x05\xea\x16\xd6\x96\xd2\x92\x5e\x90\x90\xde\xd0\x72\x5c\xbb\xa9\x7c\x6c\x61\xf7\xd0\x99\x98\x38\xa1\xf8\x92\xaf\x65\xd0\x83\x6d\x4f\x32\x01\x88\x1d\x4a\xbb\x68\x92\x1e\x21\xb5\xf7\x5f\x71\x9f\xd2\x02\xb4\x88\x48\xc7\x5f\x61\x6f\x2a\xa2\x2a\xcf\x67\xd3\xdc\x7b\xde\xc2\xa6\x39\xd9\xb1\x30\x0a\x92\x47\xfd\xad\x59\xf6\x7d\xa3\xa8\xef\x4b\xf7\xb8\xa5\x38\x63\x17\x38\x22\x0c\x7c\x85\x5d\x85\x69\x1c\x24\xd5\x82\xbc\x98\x34\xc0\xf2\x4e\xc1\x6e\xbf\xe1\xfc\x2a\x23\x5f\x70\x13\x5b\x73\x8c\x53\x98\x1b\x86\x3c\x79\xca\x26\xa6\x44\x5d\xa4\xc3\x52\xec\x4d\x12\x93\x51\x14\x3e\xd6\x6d\x42\x34\xb1\xb0\x36\xc6\xca\xd6\xf4\xb1\x52\xef\x5f\x40\xc7\x14\x64\xd9\x74\x8c\x43\xf5\x3e\x31\x18\xa5\x38\x08\xaf\xa5\xfd\x4e\x39\x90\x4d\x63\x9a\xb6\xb2\x42\x44\xb3\xc5\xd8\x9e\x9d\x7f\x2d\x74\x68\x22\x8c\x0b\x4c\xd4\xd3\x0c\x2f\xcc\xeb\xdd\xfa\xa2\x69\xbc\x28\xac\x3f\x51\xe2\x36\x48\x9e\xaa\x90\x0e\x39\x15\x20\x41\xfc\x76\x1e\xf0\xc9\xd0\x29\xc9\xab\x87\x55\xb6\xa5\xf2\x66\xb1\x6b\xe4\x45\x38\x27\x84\x0d\xb7\x09\xa1\xec\xc9\x5c\xaa\xfa\xc5\x06\x2a\xd5\x8e\x32\x68\x25\x4a\x51\x43\x33\x61\xbd\x21\x79\x63\x37\x91\x98\x77\x65\xf2\x39\x1c\xc2\x7d\x09\xfd\x6f\xf9\x65\xc9\x3c\x2b\x0c\xf3\xc2\xe4\x0d\x85\x4e\x5a\xa9\x76\x4f\xb2\x43\xc0\xc3\x9d\x3e\x69\x8c\xac\xe5\x83\x9f\xb8\xc2\x60\xc2\xe2\x05\x55\x57\xc7\xf2\x1a\xcc\xf2\x82\x3d\x80\x9c\x42\x9a\x01\xc0\xe5\x5e\x21\x45\xa0\x72\x4c\x6d\x2b\xa2\x98\x59\xf2\x32\x3b\x00\x66\x32\x73\x8e\x63\x30\xe6\x2d\x87\x26\xa2\x94\x3b\x80\xd1\xd0\xd9\xe5\xb0\x4c\x9d\x01\xa8\xb0\x24\x21\x69\x1b\x75\xdb\x60\x72\x0c\x1f\xb8\xfd\xec\xc1\x10\x25\xe3\x88\xc8\x08\x1e\x0a\xe8\xa7\xab\x68\x34\x42\x7d\x2c\x1a\x0c\x51\x1a\xc4\x61\x32\x1e\x5d\xdf\xd3\xe1\x9e\x5a\x4d\xb0\x61\xf2\xd0\xc1\x4f\x1e\x4c\x29\x69\xfc\x2b\x70\x21\x3a\xc9\xa1\xc9\x82\x24\x6a\x5c\xc1\x33\x3c\x98\xe6\xb8\xf6\x8c\x47\xa3\x7a\xe6\xb1\xc4\x1d\x1e\x33\xdf\x72\x88\x45\xf7\x04\xdd\x43\xcf\xc8\x70\x90\xff\x7f\xe6\x3e\x33\x53\x30\x32\x77\xe3\xd4\xec\x71\x12\xf5\x18\x75\x51\xc5\xa6\xdd\xa8\x9f\x4e\x33\x9b\x65\x87\xa2\xfa\x3b\xe7\x55\x92\xa1\x44\xa6\x70\x6a\xdd\xf6\xaa\x91\xd6\xdc\xe2\x56\x47\x97\xb6\xb4\xae\x4d\x69\x85\xc6\x9b\xa5\x89\x07\x0a\x05\xae\x88\x71\x57\xa4\x41\x66\x0b\xe9\xa6\xbe\xc2\x12\x79\x4b\xe3\x01\xf8\x5b\x03\xd6\x12\xda\xcc\xcb\x31\x00\xbb\x69\x43\x4d\x2e\x92\x41\x33\x05\x39\x4f\x26\xcb\xc7\x1c\x3d\x37\xf5\xd9\x4a\x6a\xe8\x22\x85\xb3\xdd\x59\xea\x88\x89\x52\x0b\x1e\xc6\x8b\x23\xb5\x90\xa2\x6f\xa7\xd5\xb6\x69\x06\x14\x15\x77\xc8\xf8\x32\x67\x79\x1a\x4b\xf6\x04\x2c\x87\xf8\x75\x7b\x7d\xb8\x25\x4a\x9c\x50\x88\xdb\xbf\xd9\x34\x5c\x0f\xa8\x1f\x7f\xb3\xb3\x77\x83\xc8\xf6\xc9\x2d\x28\x6d\xbb\x70\x21\xe5\x71\x66\x5b\xbe\xc5\x2d\xa4\x15\xb7\x74\xd8\xed\xfc\xf0\x39\x1c\x6e\x4a\xdb\xb3\x44\x21\x0b\xaa\xc7\x99\x4b\xd5\x22\xfb\xf2\xb7\xa1\x2f\x2f\x95\x0e\xbe\x01\x75\xc4\x5f\x44\x6d\x6e\x59\x7c\x95\x34\xc9\xcf\xf8\x50\xbb\xc2\xca\x3e\x7c\xc3\x1e\xfa\xe3\x81\x35\xd8\xc5\x76\xf4\x95\x14\x0e\xda\xee\x9a\xe4\x2e\xe5\xae\x4d\x76\x21\xe0\x89\xd8\xc2\xc5\x15\x09\x7b\x3a\xbc\x42\xc6\x60\xcf\x74\xdb\x73\x79\x77\x52\x31\x96\xf6\xcd\xe8\xd2\x0a\x6c\xb1\x0a\x06\x2b\xd6\x90\x04\x4e\xc5\xbc\xa2\x2f\x71\x5f\x67\xc8\x01\x20\x8c\xf9\x51\xdb\x97\xf4\xf8\x06\x1a\xef\xa2\x19\x4d\x06\x02\x15\xac\x43\x2a\x9d\xad\xa9\x61\xa6\x02\xdd\xa5\x37\xb1\x9e\xf8\xee\xa0\x0f\xfe\x13\xf8\xf1\x3d\x2b\x88\xbf\x75\xc6\xfc\x2d\xea\x89\x6d\xcc\x70\x51\x45\xf1\x9d\x18\xe3\xbd\xa3\x68\x2a\x8a\xef\x8b\x71\x57\xd4\x13\x7f\x75\xde\xfd\xd5\x95\xc5\x5f\x7f\xab\xf0\x14\xdb\x1e\xc7\x09\xed\xfe\xf6\x8e\x4a\xfa\x70\xf7\xfd\x85\x6d\xeb\x90\xc7\xb7\xe2\xee\x51\xa6\x20\x2f\x54\x79\x22\xd3\xa5\x9c\xd2\x92\xe5\xaf\xbc\x39\xf3\x3a\xad\x6f\x35\x29\xe5\xbd\xe7\xa0\x5c\x34\xf7\xa4\x92\x73\xd2\x40\xcc\x4c\x3f\xa9\xa5\x9d\xe4\x15\x1d\x89\x27\x41\x3f\x5a\x00\x17\x3f\xd5\xe4\x93\xef\x82\xfc\xc2\x43\x96\x14\x94\xc5\xf1\xfa\x6d\x32\x08\x46\x68\x92\x8c\xae\x87\xd1\x08\x25\x43\x44\x37\x2d\x76\x8a\xb7\x1c\x79\x59\x6c\xfb\x2d\xb5\xa0\xd6\xb0\xc2\x98\xc4\xeb\x3d\xf2\xfe\xe6\x85\x19\x3b\x48\xb2\xb5\xec\xff\x66\x30\x35\xb0\x11\x9c\xf6\xc9\x0c\xea\x44\xbc\xb7\x32\x49\x93\x3c\x21\x9f\xd0\x16\x39\x7d\xe8\x05\x58\x3d\xb4\x85\x62\x7c\x45\x10\x28\x87\x10\x4f\x47\x23\xc7\x42\x11\x18\x14\xcb\x44\x8a\x77\x64\x8b\xe4\xc9\xe7\xa4\x5c\xc9\xed\x54\x6c\xbf\x8d\xfa\x69\x90\x5e\xcf\xd3\x91\x4b\xf9\x41\x9d\xa0\x20\x5b\x28\xd3\x7a\x12\xe1\x82\x77\x39\x18\xa1\x28\xbe\xc0\x69\xa4\x04\x70\x55\x22\x3a\xe8\x79\x46\xcd\x08\xa3\xe6\x74\x56\x08\xfb\xc7\x63\x0c\x83\x7b\x9c\xf0\x33\xb8\x08\x72\x8e\x10\x0b\xe5\x41\xc5\x20\xe3\x54\x89\x50\x59\x1c\x40\x2e\x77\x25\x97\x38\x4d\xa3\x10\x67\xe8\x03\x55\x88\x44\x38\xa3\x0c\x7c\x72\x8d\xa2\x98\x65\x33\x2e\x10\xa8\xd0\x82\x9e\xab\xe1\x64\x51\x00\x86\xcc\xe5\x28\xb7\x48\xd4\x40\x32\x51\xef\xae\x4f\x28\x09\x2b\xd2\x4d\x89\x49\xa2\xec\x2f\x16\xe1\x51\xb8\x89\x9e\x41\xa6\xac\x67\xba\xe1\x88\xbd\x4d\xf2\x37\xc6\xf9\x45\x12\x96\xfa\xc8\x4b\xa5\xf5\x18\xf9\x36\xc7\x33\x84\xcc\x70\x86\x14\x7d\xc5\x20\x9b\xcf\xab\x33\x88\xe1\x24\xb8\x8a\xcd\x2f\x12\x23\x21\xc2\x42\x91\x56\xcf\x65\x4e\xbc\x3d\x3d\x1f\xe3\xd8\x62\x3a\x4c\x76\x94\x72\x2c\x50\xc1\x7c\xd8\xb9\xab\x28\x6f\x4d\xff\x60\x45\x80\x99\x49\x71\xd7\xaf\x48\x38\x96\xa6\x76\x9c\xbe\xe3\x4d\x5e\x04\xd9\xe1\x55\xcc\xc8\xfe\xba\xf6\x8c\xd4\x7c\x56\x17\x3e\x4f\xe4\x11\x36\x41\x5e\x9e\xbc\x98\xdb\x0f\x5a\xab\x74\xba\x2d\xb5\xfe\x9f\x6c\x3a\x21\xa2\x56\x1c\xe5\x2b\x01\x11\x4e\xd9\xd6\x17\xa4\xe7\x53\x32\xba\xd6\xf1\x40\x96\x0c\x0a\x25\xe3\x54\x78\xdc\xa6\xcf\x32\x54\x70\xf4\x88\x2a\x85\xf9\xa4\xd3\x55\x6a\x42\x90\x3b\xa8\xec\x07\x8e\x6d\x07\x71\xc5\xf8\x10\xa7\x38\x1e\x90\x06\x60\x9c\x27\xfa\x7a\x35\x86\x81\xc9\xc5\x36\x80\xce\x7d\x06\xd9\x52\x63\xd8\x98\xea\x2e\xac\x94\x4c\x66\x9a\x54\xe5\x3d\x8d\xe9\x38\xc0\x04\xd2\x55\x6b\x86\x40\xdd\xe6\xf3\x51\x64\xb0\xa9\xd5\xc5\x35\x1c\x11\xa5\x21\xa4\x1c\x00\xa9\xd5\xff\xca\xbc\x92\x47\x2c\x47\x5b\x8c\x6d\xf2\x3b\x8b\xb9\xbc\x88\x96\x2b\xe7\x78\x66\x23\xb0\xe4\x8a\x38\xd9\xe6\xca\xe5\x11\xd4\xa5\x35\xc2\xdf\xa9\xeb\xc4\x49\x35\xbc\xf8\x6d\xc8\xa6\xcc\x5d\xdd\x31\x57\xe8\x90\x31\x33\x96\x24\x00\x48\x0a\x4c\xe8\xc3\x10\x65\xc9\x18\xd3\xd4\x53\xe8\xea\x02\xc7\xe8\x3a\x99\xa6\xc2\xcc\x3e\x20\xe2\x2c\x05\x7e\xcf\xb1\x73\xef\xba\x0b\xea\x8e\xce\x65\x7b\x19\xa2\x0c\x60\x65\xc5\x1c\x19\x31\xf4\xb7\xdc\xee\xe6\xa2\x51\x69\x4e\x7b\xc9\x84\x08\x3b\x93\x42\xee\x61\xf2\xce\x1d\xc4\x29\x09\x18\x68\x98\x14\x99\x6a\x0c\x9a\xc8\x7b\x9e\x52\xb6\x3a\xe9\xfe\x59\x55\x7e\xb9\xe5\xb8\x43\x23\xca\x25\xb6\xe8\x9f\x75\x8d\x8b\x88\x87\xfc\xb2\xed\x7d\x30\x06\xa3\x89\x39\xf5\x10\xdb\xaa\x8b\x62\xfa\x66\x2d\x03\xac\x97\x6e\xb1\x64\x3a\x4f\xe5\xe2\x67\x68\x4b\x6a\x5f\xfd\xb4\x40\xea\x22\xc7\x26\xbb\x8b\xae\x92\xf8\x59\x4e\xe5\x67\xee\xee\x28\x05\x2f\x1c\x25\xc9\x04\x05\xfd\xe4\xd2\xb2\x0d\x96\x77\xf9\x19\x87\xf6\xcc\xdd\x61\xe0\xa2\xa2\x55\xb9\x9f\xe2\x6d\x85\xbc\x5a\x95\x16\x8f\x38\x9c\x40\x4f\xc1\xfe\x65\x91\x75\x63\xdb\xf8\x06\xa3\x24\xc6\x0f\xc0\xf1\x00\x2e\xda\x2a\xf6\x10\x78\x51\x61\x27\x23\xc5\xe6\x6e\x64\x72\x2e\x12\x55\x38\xe2\xfc\xd4\x6a\x4f\x66\x3f\x23\x5b\x6f\xf7\x63\x14\x80\xe7\xad\x16\x8b\xb0\x34\xb2\x90\x11\xe7\xbd\x1c\x84\x2d\x3c\x8d\x30\x7e\x50\xc3\x21\x66\xd1\x79\x1c\x0d\xa3\x41\x10\xe7\x2c\xa0\x64\x44\x7b\x0f\x20\x69\x3b\xb6\x63\xf2\xcf\x92\x07\x31\x3d\x2b\xcb\x6f\xee\x21\x6c\x8c\xd9\xbc\x4e\x16\x8e\x30\xf8\xb2\xe9\xd5\x9c\xb1\x46\x56\xb3\x30\x31\x52\xda\x0d\xc6\xdc\x41\xc3\xf7\x96\xea\x45\xf6\xcf\x56\x36\x76\xc3\x16\xc6\xa1\xfd\xaf\x0e\xe0\xb4\x31\x6b\x34\x1a\x7e\xa3\xd9\x68\x79\xa8\x31\x6b\xb4\x1b\x9d\x46\xb7\xb1\x76\xf6\x60\x80\x3d\xd4\xad\x1c\x7a\x85\x85\xaf\xe3\x33\x62\xac\xd8\x2b\xe6\x10\x0c\xcb\x95\x3f\xd0\xff\x7e\xf9\x02\x31\x7b\x35\x51\x63\x88\x6a\x62\x7a\xbf\xdb\xb2\x28\x0a\xe5\x3f\x80\x2a\x19\x0d\xf1\x9f\x95\x8d\x49\x75\x00\x94\x3c\x46\x38\x3e\xcf\x2f\xa8\xe9\x91\x93\x8b\x54\x8f\x19\x53\x2c\x94\xc5\x22\xc5\xec\xc6\x83\x24\x24\xf4\x8e\xe9\x0f\x9d\xdc\xe1\x75\x79\xec\x4f\x41\x00\x38\x1e\xac\xec\xe3\x99\xbb\xcd\x79\x01\x64\x2a\xad\xf6\x85\x83\xbb\x14\xc4\x5a\x21\xb2\x8b\x25\xae\xc1\xbc\xb0\x2e\x96\x2a\xca\x90\x7c\xcc\x87\xeb\x0b\x45\x73\x61\x53\xe1\x8c\xe5\xc2\xa7\xea\xcb\x17\xb4\x8f\x67\xa5\xe1\x5b\xe6\x10\xd0\x20\xc8\x71\xcc\xf6\x7c\x95\x82\x1c\xcc\xdf\x4d\x48\xd2\x3d\x6c\x31\xe0\x27\x8c\x1b\x4a\x94\x09\x69\x7e\x17\xbd\xd7\xad\x8a\x4b\x15\xda\x10\xd8\xf9\x3c\x7e\x86\x78\xd3\x74\xa7\x34\x83\x92\x3a\x53\xa2\x81\x9d\x17\x0b\x47\x42\x06\xf6\x67\x83\x61\x59\x7c\x15\xf3\x8b\x40\x84\x3a\x28\x48\xcc\x5d\x3a\xca\x8e\x0b\x1e\xa3\xf0\x1c\x07\xf0\x63\x95\x25\x51\xf8\x45\x1d\xa3\x53\xbd\x51\x30\x9e\x20\x3c\x83\x48\x92\xfd\x48\xef\x1c\xbd\x57\x25\x65\xcc\xdb\x06\x7a\x9f\x3a\xb0\x05\x49\x51\x10\xff\x87\x23\x50\x3a\xd4\x27\x22\x69\x8c\x61\xab\x45\x41\x8e\x02\x94\x47\x63\x8b\xc4\x6d\x0b\xc9\x2e\x77\xd7\x9d\x14\x42\x1e\x1c\x52\x14\x6d\x11\xf4\xd8\x2c\x9c\x46\x3c\x2a\x36\xf9\x4f\xad\xd9\x46\xcb\xa8\x16\x51\x8c\x9f\xa3\xf5\x7a\x5d\x44\xcb\x76\x4a\xf1\x14\x8e\xda\xe3\x25\x14\x89\x70\xdb\x5f\xb6\x8a\xa6\x5f\xbe\xe4\x6d\x58\xca\x8b\x46\x2b\x08\xfe\xce\x6d\x49\x1e\x53\xba\xb8\xee\x34\xa6\xee\x28\xf7\x55\xbb\xbf\x85\xcc\xc1\xae\x92\x31\xd8\xa4\x42\xb1\xd9\x2e\x6d\xa9\x68\xda\x72\xac\x04\x51\x1c\xf4\xf5\x93\x87\x74\x00\xa8\xca\x4e\x69\x0c\x0e\x22\x04\x2a\x82\x61\x94\xdf\x55\x14\x2c\x16\xa7\x58\x5d\x0e\x26\x45\x3e\x57\x0d\xdd\x6b\x61\x4d\xa6\x1c\x65\x8b\x8b\xe4\x64\x32\x76\x86\x61\x11\xd5\x4e\x05\x0c\x1e\x67\x7e\x0b\x96\x0e\xfd\x03\xd2\x6f\x35\x09\xe9\x67\x0a\x5f\xb0\x10\xbc\x22\x4a\x6d\xa1\x77\x41\x7e\xb1\x32\xc0\xd1\xa8\xa8\xb9\x8a\x16\x88\x48\x64\x3f\xff\x56\xda\x79\x1c\xe6\x48\xc6\xf1\xf7\xb6\x76\x9f\xec\xb8\x2b\xd3\x82\x71\xde\x55\x69\x61\xde\x39\x57\x06\x0b\x27\x35\x8a\xab\x1c\xfd\xdc\x3c\x39\xaf\x98\x34\xc2\xcc\xef\x1b\x4e\x93\x3a\x52\x6f\xf1\x29\x90\xc4\x86\x61\x34\x1a\xf1\xb0\xb3\xcc\x4d\x02\xce\x5b\xf3\x85\x12\x7e\x98\x8b\x6d\x87\x5e\x19\x94\xd3\xc5\xa7\xd2\x2c\x33\x48\x95\x08\xe5\xbe\x8c\xcf\x2a\x1c\xc1\x98\x2b\x88\xef\x3e\x69\xd1\x12\x32\x99\xc4\xf6\x23\x96\xcc\x1e\xcc\x03\x15\xf9\x9a\xaa\x37\xe4\xe3\x4f\x57\xee\x28\xf3\x9f\xae\xd0\x16\xf9\xd7\x91\x40\x6d\xfc\xe9\x77\xb2\xcd\xcc\x5a\x41\x88\xbb\xeb\x7d\x3d\xfc\xba\x28\x16\x64\x9f\x91\xcc\x39\x4a\xee\x09\x2a\xdc\xdd\xd1\x56\x6b\x8d\xd9\x46\xa3\xbb\x81\x9e\x93\x2e\xfc\x0e\x7b\xfa\xde\xde\xde\x5e\x1d\x2d\xd1\x17\x3f\xfe\x88\x1a\x33\xbf\x01\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\xd6\x1a\xb3\x76\xb7\xd3\xa0\xc0\xae\x74\x60\x57\x55\x81\xc1\xf0\xe2\x6c\x0a\x9e\x3e\x35\x40\xe3\xe5\x4b\x5a\x13\x2d\x21\x18\xe9\xd2\xfa\xac\xee\xea\x16\xd4\x61\x7f\xe5\x65\x97\xb6\x50\x63\xa5\xe3\x2c\x03\x63\xca\x8a\x3e\xa7\xf6\x36\x9c\xda\xea\xe8\x47\xb4\xd2\x41\xff\x85\x7c\xb4\x89\x96\xfd\x2a\x22\x8a\xc1\x39\x54\x71\xc3\x43\xe9\x20\x18\x5c\x60\x96\x5d\x67\xbe\xc0\x41\x6a\x7e\x22\xf4\x98\xd6\x6a\xb4\x2a\x39\x2a\x29\x48\x92\xdd\x44\x1a\x0c\xfb\x15\x13\xad\xba\x85\x3e\xa5\x35\x5a\x1e\x08\x72\xad\xbf\x66\xe9\xd3\x55\x91\xc3\xa7\x26\xca\x17\xf0\xd1\x17\xd4\xa8\x18\xd6\x3c\xc6\x57\x92\xb3\x13\xdc\x3a\x32\x05\x48\xcc\xd3\xf7\x3c\xd1\x46\xd2\xee\x7c\xca\x8e\xf6\xf3\x0c\x69\x70\x3c\x00\x43\x1a\xfa\x5f\xbb\x21\xcd\x3e\x9e\x99\x9a\x00\x1b\x38\x52\x70\x8b\x02\x5d\xa1\xbf\xab\xc5\xdf\xd4\xd5\x17\x17\x78\x56\x59\x85\x51\xe1\xe4\xb9\x60\x54\xcd\x4a\xad\xdf\x17\x23\xbf\xc0\x33\x33\x84\x26\x1b\x3f\xe9\x68\x3f\x3f\x91\x90\x35\x70\xe6\x6d\x8f\xa9\x57\x95\x4f\x9e\xd9\xa2\xc7\x48\x3a\xeb\x26\xa0\x0b\x3c\xeb\x5d\x04\x69\xe5\x3c\x5b\xd9\xdc\x03\x1d\xe4\x48\x8b\xe8\x41\xee\xea\x8e\x87\x38\x8e\x1d\x5b\xe3\x00\x96\x00\x69\xd7\x0b\xb5\x8f\xdf\xad\xdb\xf8\x9d\xad\x2a\x69\xa7\x31\x2c\xaf\xeb\x60\x10\x02\xdc\x6f\x49\x14\xd7\x9e\x3d\xbb\x45\xc4\x4d\x89\xc2\xe9\x7a\x5b\x44\xd3\xc3\x57\x0a\x25\xdc\xea\x0b\xc6\x21\x3c\xfd\xf9\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xea\x91\x32\x69\x95\xc5\x12\xa5\xd0\x3a\x6f\xf9\xd1\x85\x3e\xb2\xa3\xcc\x22\xab\xe6\x6a\x91\xd4\x74\x72\xa3\x6c\x0b\x6d\x96\xe4\xc7\xa4\xab\xa5\x05\x9a\x09\xe8\xf4\x41\x9c\xb3\xce\xae\x64\xd3\x7e\x96\xa7\xb5\xc8\x43\xcd\xba\x07\x49\xf8\x0a\x95\x05\x59\x51\xeb\x75\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x59\xd5\x7d\xf6\x6d\x90\x47\xb1\x5f\x6d\xd3\x62\x65\xf9\xbe\x25\x1e\x6f\xb7\x75\xb1\xea\x7f\xde\xee\x55\x15\x81\xfb\x5a\x53\x23\x68\xcf\xbe\x87\x51\x5c\xfe\xa3\xb6\x31\x3a\x1c\xdf\xf0\x4e\x26\x21\x48\x77\x24\x3a\x75\x2b\xc3\x34\x19\x93\xb7\xbd\x24\xc4\xb0\x49\x55\xdd\x90\x64\x80\x77\xd8\x93\x14\xba\xbd\xfd\xb6\x24\xc8\x71\xa1\xc5\xf0\x4d\x6f\x4e\x6c\x15\xd1\xfd\x49\x5e\x6e\xd5\xb7\x28\x51\x6b\xb1\x5d\x4a\x54\x13\x1b\x95\x78\xf3\xd0\x7b\x95\xd6\xf4\xbc\x5c\xce\x91\xa4\x45\x2f\x7a\xbb\x32\x60\x04\xbd\x9d\xd7\x22\xbe\x26\xf4\xad\xca\xae\x5b\x5c\x78\xab\xd2\x10\xae\xba\x53\x7d\x3c\xd9\x5b\x5e\xaf\xb6\x51\x7d\xcc\x87\xeb\x62\x9b\x62\x0f\xb7\xdb\xa4\x68\xa3\x7f\xde\x1e\x55\xb1\xfd\xfb\x5a\x59\xd3\x7c\xb8\x6e\xdf\xa0\xc8\x28\x3e\xe4\xf6\x94\xa7\xd7\x25\x06\x46\x21\x26\x47\xf4\x8f\x47\x07\x3d\xee\xe9\x54\xc3\xd9\x20\x98\xe0\x5a\xc9\xc6\x69\xb2\x65\x34\x08\xf2\xc1\x05\xaa\x99\xe9\xa3\x01\x85\x8b\x34\xb9\x02\xba\x85\x8c\x2b\xb5\x67\xef\x82\xd1\x30\x49\xc7\x38\x64\xd3\x10\x06\x79\x60\xa6\xa0\x5b\x9c\x81\xcb\x93\x7a\x7b\xfe\xcd\xe6\x6a\x11\x32\xf9\xa6\x99\x37\x50\x18\x65\xdd\x05\x19\x56\x67\xdc\xac\x8e\xcb\x18\x40\xd9\x1a\xa6\x31\xa3\x1e\x6a\x21\xa0\xd0\x15\x87\x53\xaf\x1c\x80\x46\xa4\xe0\x85\x5c\x98\x38\x64\xd9\xcc\x24\x2f\x74\x67\x26\x5e\xc9\x4e\xf6\x5a\x4a\x89\x36\x9e\x66\x39\xea\x63\x14\x91\x11\x1d\xe3\x38\xa7\x79\xd6\x02\xb8\x5e\x4f\x71\x2e\x3c\x16\x2a\xe5\xf6\xd5\xf2\x74\xaa\xca\x7d\x9a\xe3\x90\xba\x56\x15\x09\xe2\x3f\xe3\x49\x8e\xa6\xf1\x84\x27\x0d\x54\xb3\x83\x4a\x36\x2d\x0d\x0b\xf7\x7d\xc5\xc6\x01\x32\x0d\x6e\x8b\x51\x10\x5e\x62\xae\xcf\x15\xcd\xe0\x20\xbb\x2b\xb3\xe6\xd1\x46\xfa\x19\x4b\xa2\xcd\x92\x98\xe6\x09\x8a\xf2\x8c\x7b\xc5\x20\x42\xc1\x77\xbd\x63\xea\x5b\x91\xa7\x09\x71\xdd\x97\x4c\x95\xb2\xee\x32\xf3\x3e\x04\x56\xca\x36\x9b\x01\xc8\xc0\xc9\x3c\x15\xb5\x9d\x55\x67\x4a\xb4\x7c\xbc\x13\xe4\x01\x17\xd6\x1b\x55\x25\xcd\xed\x30\xcc\xa0\x0d\x9e\x17\xdc\x31\xd2\x8c\x16\xaa\x6f\x8a\x22\xc8\x82\x91\x79\x9c\x19\xbb\x20\xba\xe6\x99\x13\x00\xe5\x97\xd4\xa7\x24\x90\x2c\x28\xa9\x3d\x31\x70\xbc\x87\x99\xcc\x4f\x14\x9d\xda\x33\x93\xdf\x57\xaa\x37\x7f\x6f\x64\x25\xab\x24\x33\x37\xdd\xeb\x8b\x74\x74\x72\x40\x51\x69\x80\x58\x30\x51\x15\x94\xec\xe3\x0c\x64\x34\x27\x4e\x24\xa3\x35\x89\x29\x03\x86\xf3\x23\xa5\x6d\x43\xd7\x5c\xe4\xcb\x4d\x89\x6c\xc0\x0c\xa2\x5d\xda\x52\x93\xa4\x57\xa5\x60\x9e\xeb\x34\x43\xc1\x65\x10\x8d\x20\x62\x17\xe5\x0b\xc0\xec\xdc\x54\x73\x22\x39\xab\x44\xf1\x65\xf2\x19\x67\x7a\x92\xe1\x1a\x4b\x0e\xec\xa1\xab\x8b\x68\x70\x61\x65\xd5\xfd\xeb\x12\x56\x6d\xb6\xca\x17\x4a\x3f\x49\x46\x38\x88\x6f\x50\x98\xec\x8d\xa6\xd9\x05\xfa\xf9\x02\xe7\x34\x9e\x09\xcf\x45\x0b\xee\x5a\x93\x20\x05\x46\xc1\x5e\x15\x5c\x5b\xb0\xeb\x5b\x84\x03\x11\x9c\x1e\x46\xfc\xee\xdb\xbc\x00\xb8\x43\x09\xc9\xb5\x66\x78\xaa\x5c\x57\x5c\x8e\x05\xc1\xd8\x33\x05\xab\xb1\x56\x69\x51\x65\xf1\xd1\x01\x5f\x50\x67\xc2\x96\x48\x41\xdc\x16\x6d\x09\x79\xcd\x8d\xd3\x60\x64\x5d\x6a\x15\xf2\x51\x32\x34\x73\xd1\x3d\x2f\x5e\xc8\x0a\x5b\x5a\x4a\xe6\xb2\xc2\x1c\x7a\x51\xdb\x1e\xd1\xaf\x97\x4c\xe3\x9c\xd3\x97\x85\x99\x10\xa0\x31\x4d\x24\x7c\x04\x71\x8b\xb7\x54\xfc\x57\xb5\x26\x5f\x98\xbc\xc8\x35\xe4\x0c\x83\xa3\x64\x1a\x87\x68\x3a\xa1\x0e\x85\x83\xd1\x34\xc4\x1a\xdd\x9b\xd5\x34\x8c\x0a\x23\x17\xf9\x43\xf5\xd8\xb6\x02\x8b\x30\xb9\x8a\x65\x3c\x92\x78\x74\x8d\x86\x53\xb1\x28\x2d\x91\xf4\x57\x57\xd1\x08\x67\xd4\xa9\xd2\x2e\x6b\x01\xdf\x48\xf1\x38\x88\x62\x55\xb8\xaa\xd6\xaf\x71\x30\xab\x29\xfd\x82\x8b\x53\xb4\x6c\xcb\xcc\xee\xcd\xbf\x52\x15\x73\x4e\x35\x0f\xae\x29\x07\x4a\xe6\x78\x28\xad\x3f\x47\x12\x01\xba\xe8\x09\x68\xc3\x49\x4e\xe4\xab\xda\xc7\x28\xae\xc9\x4d\x3e\x47\x6d\x4f\xa1\x33\x9b\xf9\x24\xcf\xe0\x6d\x23\x12\x42\x77\x12\xc0\x72\xb7\x2d\xca\xe7\xa9\x9a\x85\xfd\x7e\x29\x8f\x80\x78\xbb\x24\xad\x27\xa7\xd1\x04\xc1\x0c\xa7\xe4\x34\x29\x36\x86\xe5\xe2\x80\x00\xce\x90\xf6\x8a\x8c\xbb\xa8\x7b\x90\xe0\x2a\xb6\x5c\xf5\xae\x39\x46\x4a\x0a\xac\x82\xe1\xc3\x94\x9b\x45\x15\xee\x2b\xb3\x30\x3d\x19\x96\x3c\xa2\x16\x34\x14\x4e\x86\x96\xb7\xe4\x99\x9e\x4f\x95\x3c\xb6\x68\x19\xb6\x6e\x85\x93\x8a\xbf\x27\x37\x7d\x57\x63\xb7\xca\x59\x28\x4b\x9d\xbc\xee\x68\xe5\xe6\xd8\x0d\xff\x24\x93\xb7\x4f\xc6\x86\x58\x60\x62\x9d\xb1\x52\x8b\x37\x95\x87\x89\x93\xa6\x23\x13\x3d\x3f\x83\x5f\x04\x19\x64\xc8\x75\x9e\xb8\xe7\xa6\x22\x2f\xd8\xb5\xec\x03\x45\x27\x9d\x41\xa7\x61\xd7\x70\x86\x92\x58\x3a\x0a\xfb\x5d\x54\xeb\xf8\x4d\xb0\x64\xad\x5b\x8e\xc5\xfb\xb4\x32\x3f\x06\x8b\x47\xfb\x79\xf8\x5e\xa2\xbe\x96\x65\x20\x2b\x0d\x98\x5a\xe6\x6a\x46\x07\x61\x81\x9c\xe4\xb7\x8d\x6e\x47\x1a\x42\x34\x44\xf2\xbc\x20\x77\x95\x6d\x48\xc4\x1c\x28\xa1\xdb\x8e\xf7\xb7\x9b\x9d\xae\xdd\x49\xac\x2c\xd5\xf5\xad\x23\xac\xf1\xd8\x6a\xd5\xc3\xac\x1d\x63\x11\xde\xc3\xad\x21\x30\xd5\x10\x73\x2c\xb1\x0b\x4d\x0a\x5f\x38\xf7\xaf\x32\x61\xf4\x72\x1f\x2a\x12\x40\x58\x56\xf1\xa8\x25\x1c\x2b\x09\x40\x2b\xcc\xcb\x94\x1a\xf4\xbd\x99\x0d\x87\x65\x63\xe6\x1b\xf2\xd1\x62\x63\xfd\x71\x12\x02\xcb\x90\x07\x9b\xa6\xe5\xaf\x9e\xb1\xcf\x19\x41\x98\x02\xd7\xe3\x08\x57\x76\x21\xa2\xac\x88\xf9\x0f\xcd\x5d\xde\x0b\xcc\xf9\x14\xf0\xaa\x3d\x63\x48\xd9\x74\x29\x6a\xc9\xf9\xaa\x13\x5a\x50\x26\x14\x65\x0c\x1c\xeb\xd1\xa1\x91\x60\x0a\x1b\x15\x82\x85\x3c\xd8\xf8\x12\x21\x9d\xe0\x6b\x03\x25\x9d\x63\x4d\xf1\xf7\xde\x7c\x27\xf6\x58\x92\x9b\x4c\xe0\xe2\x64\x90\xe8\x7d\x02\x28\x07\x39\xcd\x17\xcf\x6a\x16\x31\x43\x51\x94\x21\x3c\x1c\xe2\x41\x1e\x5d\xe2\xd1\x35\x0a\x50\x88\xb3\x3c\x9d\xc2\xb3\x07\x72\xfa\x72\x12\x0f\x70\xa5\x28\xa3\x15\x29\x54\x49\xf4\x00\x28\x15\x01\xb9\xa1\xc4\xe2\x9a\x0b\x32\x08\xf7\xb4\x33\xa0\x2d\x4e\x8e\x22\x99\x90\x43\x2d\xe1\x28\x5d\x46\xe8\x25\xd5\xe6\x53\x3d\x2f\xba\x10\xdd\xef\x59\xc6\xd7\x3c\x10\x95\x83\x41\xf3\xd6\xca\x3c\x01\x7e\x01\xce\x2a\x8d\x10\x67\xb2\x7b\xd2\x3c\x58\x17\x0f\x29\xef\x5a\x3c\x52\xf2\xbb\x8e\xdf\x5c\x6d\x35\xab\x89\xf9\x19\xd3\xf8\x28\xf1\xef\x03\x36\x69\xcf\x44\xe0\xa4\x28\xce\x71\x3a\x94\xac\x85\x91\x73\x55\x70\xfe\xca\xba\xce\xa9\x96\x6e\xb7\x2c\x3e\x62\x80\x2e\xf0\x68\x82\x53\x22\xfe\x54\x58\x04\x7b\x0c\x37\xe6\x1b\xac\xa3\xfc\x15\xee\xf1\xa8\xcc\xa4\x3b\x55\xd0\xae\xae\x7c\xa2\xbd\xda\x87\x2e\xd5\x6c\xc2\x96\x5b\x3f\x27\x57\x55\x8c\x07\x01\xb4\xeb\x7e\xcf\x58\x17\xf6\x00\xb8\x48\x3d\x2f\xb2\x95\x08\x87\x45\x35\x8b\x58\x91\xe1\x52\xa5\xf0\xc5\x8f\x8d\x56\x7a\x22\x2c\x79\xff\xdd\x76\xef\xfe\xe9\x89\x88\xd0\x3c\x28\x05\x69\x81\xd1\xd5\x5f\x82\xa6\xf6\xc7\xc1\xa0\x12\x5d\x8d\x83\xc1\x5d\x68\x4b\x54\xbf\x13\x7d\x7d\xc6\x76\x15\x92\x44\x5f\xbd\x4f\x80\x16\x99\x07\x4a\x64\xb4\x11\x5a\x77\x31\x62\x2b\x3d\xfe\x0a\x4d\xd2\x1c\x1f\x06\x82\x0d\x38\x31\xb0\x1f\x85\x17\x03\xcf\xd4\x02\x21\x7d\xdf\x05\xf9\x05\x0d\xeb\xfb\x84\xbf\x67\xc3\xfc\xa2\x88\xf4\x7b\x73\xe6\x75\xda\xdf\x6a\x78\x5f\x86\x4c\x8d\x87\x23\xae\xdf\x7b\xbc\x5f\x0e\x79\xd1\xb8\xbf\x02\x43\x39\xfe\xaf\x2b\xe8\xaf\xf8\x0e\xc1\x7f\x6d\x01\x74\xcd\x2b\x0a\x1e\x35\xb6\x98\x32\x89\x00\xa4\x68\xb0\xd2\xfb\x92\xf0\x34\x4a\x6d\xc9\x05\xc6\x15\x46\xb6\xdb\xae\x66\xa2\xc5\xca\x72\x23\x2d\xf1\x78\x3b\x33\x2d\x56\xfd\xcf\xb3\xd3\xaa\x8a\xc0\x7d\x71\xca\x3e\xb4\x67\x37\xd5\xa2\xb8\xfc\x0d\x6c\x89\x8d\xf2\xe3\x60\x22\x84\xc3\x71\x30\x59\x3c\xf6\x82\xc5\x45\xdc\x04\xe1\xb2\xca\xa4\x63\x7e\x5b\x83\x65\xb4\xb4\x85\x5a\x6e\x9b\xe5\xeb\x1c\xfb\x16\xa3\x65\xfa\xe7\x32\x5d\xa6\x7f\x4e\x03\x66\x0e\xb8\x59\x00\xae\x45\x68\x09\xf9\x75\x8b\x4d\x34\xff\x52\xc5\x32\x9a\x03\x6e\x69\x80\x9b\x4e\xc0\x4d\x2b\x60\x3b\xe4\x3c\x8d\x26\x23\xb8\x7a\xa9\xd1\x61\x79\xf9\x12\xfc\x26\xbe\xd0\xe7\x26\x79\x5e\x27\x8f\x80\x82\x0d\x8a\x98\x8a\xdf\xe8\x54\xd4\x7e\x43\x2f\x49\xeb\x3f\xfc\x80\x00\x9b\xdf\xd0\x73\xd4\x58\x59\xeb\x48\x33\x54\x7f\x81\x7e\x2b\x09\x77\x21\xcd\x3d\xb5\x05\x1f\x07\x13\xb0\x99\xdd\xce\x6b\x35\x8e\x30\x74\xba\x8b\x9e\xa3\x5a\x0b\x2d\xa3\xdf\xea\xac\xa7\xad\xa1\xd5\xdb\xc9\x88\xcf\x60\x2a\x2e\xc2\x90\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x2d\x24\xa1\xd3\x35\x9c\x49\x20\xb6\x5e\x51\xdc\x6e\x1c\x7c\x11\x8d\x30\xaa\xc9\xfd\x64\xe1\x02\x5c\xb1\x46\xac\xc3\x22\x37\xb3\x78\x9f\x19\x67\x95\xa1\xde\xc1\x4e\x5e\xe1\xc9\xb7\xb7\xb3\x14\xac\x76\x21\x46\xff\x4d\x9b\x5a\xb2\x1d\x82\xda\xf5\xc8\x5b\x49\x75\x73\x4b\x51\x6b\xc1\xcd\x41\xd4\x13\x86\xf2\xe2\x8d\x30\x94\x9f\xcf\xf7\x8d\x12\x29\xbe\xc4\x69\x86\xdf\x49\x05\x8b\x57\xb6\xb8\x66\xdf\x15\x9f\x9d\xd4\x5d\x0a\xd4\xb6\x05\xf0\x3f\x9d\xff\x10\xf6\x43\x56\x28\xeb\x60\x29\xa7\x51\x1b\x3e\xe5\x0b\x9b\xd9\xe6\xff\x56\x3f\x43\x5b\xe8\xb7\x6a\xb1\x3a\x2d\x2c\xe5\xe0\x3c\x4e\x52\xfc\xd5\xb8\x8a\x04\xf2\x20\x0e\xc1\xcf\xb9\x98\xee\x88\xbc\x39\x1c\xce\xe3\x19\x52\x3b\x14\xc6\x77\x5b\x5b\x68\xd9\x9f\xc3\x93\x64\x0a\x93\x6b\xdf\x8a\x11\x5b\x45\x82\x54\xa4\xbd\xcc\xf0\xdb\x24\x99\x14\x4b\xc2\xd3\x71\xf0\xa4\x19\x55\x44\x0e\xed\xc6\x33\x98\x6c\xa2\x67\xdb\xaf\x7a\x3b\xbb\x7b\xaf\xf7\x0f\xfe\xf9\xe6\xed\xbb\xf7\x87\x1f\xfe\xef\xd1\xf1\xc9\xc7\x9f\x7e\xfe\xe5\x5f\xff\x13\xf4\x07\x21\x1e\x9e\x5f\x44\xbf\x7d\x1e\x8d\xe3\x64\xf2\xef\x34\xcb\xa7\x97\x57\xb3\xeb\xdf\x1b\x7e\xb3\xd5\xee\x74\xd7\xd6\x37\x96\x56\xb7\x58\x84\x5b\x71\xb4\x13\x8b\x76\x61\x54\x8b\x21\x76\x78\xa5\x14\x96\x1b\x8a\x85\xa9\x4d\x14\xd2\xda\xb1\xb9\xa9\x90\x99\x8e\x1c\xfb\x0d\x73\xec\xca\x88\x90\x24\x2d\x8f\x82\x9a\x64\x07\x16\xb4\x8c\xfc\xfa\x19\x78\xaf\x14\x02\x53\xd3\x24\x2e\x0e\xb4\x59\x05\x68\xfd\x8c\x6f\xf0\xb2\x18\x66\x81\x4a\x05\xa2\x58\x89\xdc\xf3\x85\x08\x33\x80\xfe\x17\xda\xa2\xec\x5b\x13\x97\x07\xef\x41\x6c\x88\x97\x96\x94\x0f\x82\x6c\xc5\x0f\x46\x91\x46\x6c\x49\x6b\x58\x84\x9b\x22\x77\x8f\x7e\xc8\x97\xf6\x88\x17\xce\xcc\x3e\x9d\xc7\xa3\xff\xe3\xd1\x5f\x1c\xfd\x3f\x9e\xec\x2d\xfb\x5d\xf4\x6a\xb7\xb2\x83\x96\xdf\x7d\xb5\x2b\xfb\x68\xf9\x5d\xf5\x09\xbe\xde\xde\x69\x8b\x22\xf3\xe7\x3a\x6e\x55\xc4\xe1\x1e\x9d\xb7\xfc\xae\xd3\x7b\xcb\xef\xfe\x0d\x34\x02\xd5\x0f\xeb\x30\x18\x77\x39\xab\xdb\xfd\xfd\xc1\x32\x2a\x09\xf1\x87\x24\x8a\x73\x97\x93\xb1\xdf\x75\x38\x19\x5b\x0f\xd3\x05\xa6\x6e\x2f\x63\xd1\x64\x55\x57\x63\x09\xe8\x1d\x4e\x50\x3a\x11\xdf\xc9\x59\x0d\x68\x73\xd1\xb5\xf1\x4d\x1f\xa3\xe8\xaa\x12\x2e\x6b\x7c\xf1\x2d\xe4\xb3\x06\x95\x16\xf3\x35\xe6\xb5\x84\x7c\xcb\x5f\x3c\xb4\xa7\xb1\xda\x70\x35\x47\x63\x1f\x64\x1f\x81\xa1\xea\x66\x4c\x44\xa0\x62\xb1\x34\xc9\x62\xd1\x82\xb0\xb9\x29\xdc\x25\xe5\x68\xa3\xf3\xbc\x7a\x28\x0c\x46\x96\x6f\x2b\xec\x61\xd2\x3e\xf5\xf6\xce\xfb\xd4\xdb\x6f\x60\x9f\xaa\x82\xc3\x7d\xef\x53\xd6\xe5\xf4\x76\xf7\x71\x9b\x12\x7f\xf7\xb6\x4d\x65\x57\xc1\x64\x37\x0e\xa3\x20\xae\x2d\xba\x63\xd9\x8e\xe4\xdf\xfe\x96\xf5\xf6\x61\xb6\xac\x2a\xcb\xe4\xdb\xdf\xb2\xde\xee\x6a\x9b\xd6\xe3\x8e\x65\xec\x58\xd2\x8a\x59\x68\xf3\xfa\xaa\xbb\x97\x98\x17\x09\x5b\x02\x48\xe9\x23\x8f\x86\x0f\x5f\xd8\xdd\x09\x5d\xdc\x8d\x06\xf9\x7f\xb8\x58\xa1\x1f\x49\xf7\xd9\x57\xfa\xad\x58\xfe\xf3\xd4\x05\x40\x58\x6e\x6d\x41\xf7\x4e\xda\x02\x96\xa3\xf6\x6b\x2a\x0d\x3c\x24\xbd\xca\x2e\x02\x5f\x7b\x75\x31\x0e\x06\x0f\xa8\x5a\xf0\x10\x6f\x16\x7e\x41\x6b\x7f\x07\x75\x83\x91\x2f\xf6\x16\xaa\x08\xc5\x88\x45\xfa\xf2\x6e\xa7\x03\x35\xc1\xe4\xe6\xdd\x4e\xc7\x26\xe3\x81\x89\xf3\x67\x7c\x4d\xb3\x60\x53\x3b\x58\xd1\x57\x70\xfe\x0d\xe2\x9c\x27\xf1\x4e\xd2\x31\xb5\xd1\xde\xfd\xe9\xc3\x27\xd8\x74\x4f\x92\x37\xb8\x10\x06\xd1\xd5\xd5\xd5\x4a\x32\xc1\x71\x96\x8d\x56\x92\xf4\x7c\x35\x4c\x06\xd9\x2a\x24\xe1\x4e\x56\xb5\x3a\x17\xf9\x78\x64\x51\x84\xec\x5e\x4e\xde\xec\xec\x15\x68\x8b\xe7\x8a\xc1\x10\xe6\xfb\x80\x68\x7b\x9c\xe1\xfd\xc2\x52\x9e\xc3\x1e\x45\x06\x26\x23\x0f\x51\xcc\xdd\x5e\xa4\x70\xcf\x85\xab\x4b\x1b\xd5\xfc\xe6\xba\xe2\xe9\x62\xc0\x77\x18\xa9\xc9\x61\x31\xf4\x04\x29\xef\x76\x3a\xf3\xb0\x8d\x72\x66\x8b\xac\x07\xa9\x96\x3e\xe4\x09\x9a\x50\xab\x53\xd9\x3b\xc7\xb1\xc3\x19\x7e\x31\xda\xee\xc0\x86\x67\x13\xf9\xcd\x75\x30\x21\x55\xbe\xd2\xce\x01\xe6\xda\x97\x02\x1f\xa5\xed\x9b\x5b\xbb\xdd\x38\x88\xf6\xa1\xfd\x70\xb0\xd4\xe8\x3d\x98\x59\x7f\x0e\x87\x86\xf7\x0d\xa5\xf9\x39\x29\x9a\xe6\x57\xfc\xa3\x98\xab\x75\x2d\x9f\xdf\x6d\xc1\x78\xea\x34\x36\x1a\x0d\x1d\xf0\x82\xde\x41\x73\xfd\x7e\xaa\xc9\xbb\x3b\x90\xc2\x9f\xd0\x08\xa1\x0a\x48\x84\x1d\x40\x06\x56\xb2\x68\x6f\x63\xa5\xcf\xeb\xd2\x58\x00\x36\x40\x25\x95\xb3\x60\x94\xa3\x6d\xf8\xcf\xe2\x62\x31\x50\x17\x25\xef\xfb\x20\x2f\x4c\x36\x8f\xcf\xe1\x70\x85\xba\x45\xe0\x1a\xef\x8c\x07\xf8\x95\xe4\xad\x81\xe2\x4a\x7e\x47\xb5\xe6\x42\x02\xaf\x3a\xc5\x16\xf1\x96\xac\x74\xc6\x3d\xcc\xda\xc2\x4b\x8d\x90\x07\x33\x51\x2e\x56\x87\x15\x96\xcb\x2d\x0c\x42\x0b\xd0\x21\x7e\x03\x63\x63\x4b\x89\xb6\xc8\x19\xb9\x00\x26\x7c\x82\xc5\x1b\xe7\x71\x99\xef\x31\xb4\x47\xec\xc9\x52\x4e\x62\xe2\xb4\x68\xf1\xc2\x82\xe5\x6b\xb6\x31\x11\xf0\xea\x47\x66\xcc\xa2\xe1\xca\x0d\x5a\x5e\x72\x7c\xac\x47\x01\x22\xc6\x81\xe7\x80\xf3\x82\x59\x75\x59\xa2\x65\xe7\x5f\x2b\x23\x39\x18\x43\xe1\x04\xc2\xa0\x70\x62\x93\x8c\x82\x0d\x7a\x55\x9b\x17\xfe\x74\x66\x09\x42\x13\x62\xe0\xcc\xcf\xca\x41\xc9\xa7\x07\x25\x69\xa0\x4b\xd3\xfe\x68\xd8\x0b\x64\x9d\xa3\x60\xc3\xd8\x32\x54\xe6\x3b\x89\xac\x58\xcc\x18\x6b\x1b\xda\x28\x4b\xb5\x24\x1d\x0d\xa7\x3f\x4b\xb4\x0b\x11\x60\x8e\xd7\xab\x6a\x73\x5d\x89\x07\xcb\x7e\xc7\xb7\xe2\xbd\x0b\xf2\xdd\x7b\xf4\xbe\xb5\xf8\x95\x49\xbd\xa9\xce\xcd\xa5\x4a\x8a\x76\x43\x7a\xaf\x72\xf7\xe2\x03\x52\xb8\xba\xd8\xb4\xe9\x7e\xed\xe2\xec\x8b\x55\xf3\x90\x43\x6c\xb8\x0b\x98\x52\xb1\x41\xa8\x90\x0b\x59\xdf\xb5\xe7\x98\x2e\x2c\x6c\xd8\x55\x89\x05\x1c\x57\xca\xf7\xbb\x9b\x17\x25\xc7\x77\x0a\xcd\x7e\x76\xf7\xf8\xe1\x73\xb3\xb3\xee\xf1\x23\xe9\xe6\xda\x1a\x39\xd3\xaf\xfd\xa5\xcf\xf4\x83\x68\x72\x81\xd3\xe5\x07\x36\x11\x80\xd3\xbb\xdc\xd4\x9f\x73\x88\x37\x33\x77\xde\xcb\x69\xbe\x07\x1d\xfb\x40\x38\x4e\x26\x0e\xed\xf2\x4b\xb7\x09\x81\x78\xaf\x65\xc2\x50\x6a\x90\x33\x5c\x90\x43\x25\xfa\x93\x33\x62\x56\x71\x0f\x5e\xe6\x2c\xaa\x02\x2d\xb2\x40\x3a\x0d\x72\xba\xa1\x73\x93\xe3\x59\x4e\x4e\x91\x01\x7b\x46\x13\xda\x27\xe6\x9b\xc5\x53\x6d\x04\x21\x1e\x44\xe3\x60\x34\xba\x66\x69\x40\xc3\xca\x37\x37\xf2\xa8\xdc\xb0\x56\xd8\xc0\x9d\x08\x34\xd4\x66\x17\x4f\xc6\x71\x1b\xfc\x1e\x34\x3d\x47\x31\x25\xd2\xad\x8e\xdc\xf9\xc5\x2e\x76\x94\x9a\x0e\x47\x2d\xb9\xcc\x4a\x31\xbb\x45\x02\x89\x7d\x3c\xbb\x65\x26\x08\xcb\xf0\x4a\xe4\x23\xdf\x37\x2c\x38\x9d\xda\xcd\x43\x14\x4f\xa6\xf9\x5d\xe6\x94\x93\x87\x4a\x74\xb7\xa0\xb3\xfb\x22\x8e\x81\xc6\x28\x2c\xf4\x71\xeb\xa4\x12\x30\x5a\xf6\x10\x36\xc5\xe4\x6c\xa1\xa2\x0d\x5a\xe1\x85\x95\x7a\x7a\x0a\xf5\x70\x8d\x40\x01\x68\x53\x06\x7a\x63\xd7\xcd\xbb\x77\xda\xa2\xbb\xda\x6e\x2b\x6d\x10\x9b\x9d\xa6\xa7\x29\xcf\xd7\x1f\x4d\xed\xfe\xee\xba\x6f\xd7\xee\x68\x44\x32\x2f\xd3\x84\x9b\x87\x14\x70\x00\x16\x1a\x57\x6b\x22\x2a\x52\x62\x4b\x76\x54\xbd\x9f\x84\xf4\xe0\xf2\x3a\x97\xe3\x55\x56\x12\x57\x54\x45\x11\x59\x1d\x9c\x97\xf1\x20\xc5\xf9\x3d\x29\x95\x88\xfc\xbb\x6f\x0f\x1c\x04\xbd\x64\x6c\xc2\xe6\x89\x4c\x1d\x7d\xab\x6a\x0c\x65\xe7\x60\x47\x80\x60\xab\xce\x48\xe8\x8b\xa8\x8f\x82\x78\xd4\x3d\xdc\x4b\xbc\xdd\xee\x33\xbe\x2c\x1c\x98\xe6\x84\x97\xa5\x87\x2a\x29\xba\xac\x3e\x4e\x76\x43\xfc\x12\xc5\x14\xed\xe8\x2b\x29\x2e\x26\xeb\x7a\x59\x64\x4c\xad\x12\xd7\x17\xe8\xb0\xec\x51\x32\xb7\x47\xa3\xe4\x0a\x05\x69\x3f\xca\xd3\x20\xbd\x46\x4c\xbd\xf4\x19\x5f\x5b\xe2\x0e\x7e\x96\x35\x12\x3f\x5a\x1b\x2e\x19\x28\x5d\xdd\x52\x6d\xb4\xe6\x38\x43\x12\x94\x4a\xdc\x20\x21\xfe\x1b\xe8\x36\x92\x14\x45\x71\x8c\x53\x88\x3e\x9b\x4c\x73\x10\x20\xf4\x28\x7c\x10\x33\x91\xea\x18\x29\x19\xb2\x07\xda\x8a\x11\x90\x8e\x6b\xfc\xe4\x1a\x91\xa5\xc6\x22\x24\x90\x48\x5a\xc9\xa4\x4c\x1f\x19\x49\x05\x23\xa9\xa0\xd1\xd8\x2f\x87\x47\x30\x9f\xf4\x1a\x70\x12\x84\x68\x90\xc4\x59\x1e\xc4\x7a\xf3\xd6\x24\x52\xea\x1c\xbb\x15\x6b\x02\xef\xd3\xe8\x0c\xfd\xba\x85\x1a\xb3\xce\x80\xfe\xcf\xe6\x0e\x63\x14\x6e\x75\xe9\xff\xca\x35\x63\x89\xa6\x13\x8b\xb4\x67\x1b\x45\xfe\x09\x71\xc8\x60\x07\x7a\x88\x28\x64\x82\x89\xdf\x4b\x24\xb2\x92\x7c\x65\x36\x66\x6c\x19\x48\xe8\xb4\x8d\x8f\x3b\xf4\xa4\xaa\xbe\xb8\x58\x30\xb7\x8b\x40\x06\xc3\xfc\xcd\xc4\x1f\x7b\xb7\xdd\x63\xd1\xc7\x00\xaf\x08\x96\x58\x69\x24\x94\x05\xa7\xbc\x4a\x20\x32\xa3\xf4\xfd\x07\x23\x93\x49\x82\xb7\x32\x37\xf8\xd8\x43\x45\x0f\x83\xa1\xfe\x4f\x8f\x1e\x36\x47\x4c\x5d\x44\x44\x24\x3c\xb4\xa0\xa1\xb9\x11\xc4\xdc\x35\xe6\x46\x11\x73\x57\x7d\xa0\x48\x62\x77\xe7\x76\x3d\xaa\x9e\x86\xf1\xb6\xec\xc7\x44\xba\xd8\xb7\x07\x47\x2b\x0d\x38\x56\xca\x31\xe5\xb1\xd2\x80\x16\x12\x0a\x97\x34\xf8\x25\x93\x40\xa5\xee\x0c\x39\x36\x0e\x06\xf6\x4b\x22\x71\xf0\x77\x18\xc1\x6d\xfc\xa5\x15\xe6\xb3\x6e\x7b\xd9\xf2\x7a\x14\xf5\x97\x09\x2a\x21\xd8\xb6\x66\xda\x57\x1c\x0f\x96\xc1\xa6\xd1\xf2\x9e\xba\x59\x6a\x1f\xc6\x61\x67\xbe\xf1\x5d\x76\x11\x34\x3b\x3a\x48\xf2\xb2\xa9\x83\xcb\x2e\x82\x8e\xdf\x34\x5f\xb6\xd6\x2d\x25\x5b\xda\xab\x34\x9a\xe0\x71\xe8\x77\x1b\x56\xdb\x3f\xe5\xd5\xa4\xff\x39\x1c\xea\xed\xe0\xcb\xc9\xe7\x70\x58\x76\xef\xa0\x76\x3d\x09\xf1\xf2\x60\xd8\xb7\xbe\xce\x53\xc7\xeb\xe5\xf3\x51\x10\x8e\x83\xd8\xf6\x39\xb1\x03\xc3\x03\xfd\xf5\x24\x08\x97\x83\x38\x8b\x66\x1b\x4d\x7d\x10\xc8\xa7\x28\x4b\xfc\x86\xdf\xd4\x47\x9c\x7d\xda\x58\xdb\x58\xd3\x67\x88\x7c\xfa\x1d\xa7\x09\x73\xbd\xb6\x7c\x8d\x1d\xdf\xa8\x8e\x6c\xf9\x02\xcf\xb4\x0f\x01\xd6\x89\x8b\xc6\xdd\x08\x8d\xf7\xe9\x40\x9f\xdc\x34\xe8\xf7\xa3\xdc\xfa\x72\x79\x84\xcf\x83\xc1\xf5\x43\xdf\x01\x89\xd5\x03\x4f\xfa\xa2\x81\x97\xc5\x5a\x11\x8f\x6c\x89\xc0\x33\x59\x19\x9a\x59\x28\x5b\x07\xe2\x77\xb3\x2d\x7e\x13\xaa\xe7\xbf\x09\xb1\x8b\xdf\xf4\x57\x41\xda\x85\x7d\x29\xfc\x62\x84\x4c\x31\xa0\xf4\x6b\xdc\x61\x51\x74\x38\xb5\x4a\x4f\x79\xaa\x3e\x09\xda\x2c\xde\x26\x4a\x0d\x42\x89\xb4\x59\x99\x00\xc5\x1b\x41\x77\xf2\x1b\x4a\x6e\xe2\x8d\x4c\x65\xe2\x65\xac\xbe\x92\x68\x0a\x9e\x09\x29\xc1\x8f\x82\x82\xe8\xa8\x0c\xd8\x40\x31\x7a\x91\x7e\x73\x32\x59\x54\x11\xa9\x28\x20\x65\x5e\xbb\xb8\x62\xd2\x1d\x8a\x8d\x75\x69\xb3\xe3\x7b\xe5\xda\x64\x4f\xa5\xab\xcd\x4e\xdb\x53\x08\x6f\xb3\xd3\xf1\x8a\x89\xdf\xec\x74\x3d\x75\xf4\x36\x3b\x6b\xfa\x8d\xb0\x4e\xca\x9b\xdd\x86\xc7\xa8\x75\xb3\x0b\xf8\x08\x4a\xd9\xec\x36\x3d\x99\x56\x36\xbb\x6d\xcf\x46\x2d\x9b\xdd\x96\x27\x53\xc8\x66\xb7\xe3\xc9\xf4\xb3\xd9\x05\xbc\x14\x9a\xd9\xec\xae\x79\x3a\xd5\x6c\x76\xd7\x3d\x9d\x6e\x36\xbb\x1b\x9e\x41\x24\x9b\x6b\x0d\xcf\x42\x4e\x9b\x6b\x80\x3f\x5b\x12\x9b\x6b\x80\x3d\x23\x8d\xcd\xb5\xb6\x67\x10\xc7\xe6\x1a\x20\x4e\xc8\x68\x73\x0d\x70\x2e\xd6\xd9\xe6\x5a\x57\xbe\x40\xf7\x8a\x25\xbb\xb9\xc6\xaf\xd6\xc9\x62\xde\x5c\xdb\xf0\xf8\x52\xdd\x5c\x6f\x78\xc5\x12\xde\x5c\xf7\xbd\x62\x71\x6f\xae\x03\x3a\x05\x05\x6f\xae\x43\xe3\x82\xd1\x6c\xae\xb7\x6f\xce\xbc\x6e\xe3\xf1\xf2\xe0\xcf\xbf\x3c\xe8\x5d\xe0\xc1\x67\xd2\x29\x58\x29\xd4\x0d\x88\xa6\x39\xcb\xa6\x13\x32\x30\x98\xc5\xa7\x96\xfa\x0d\x72\x3c\x0d\x69\x8e\xbe\xdb\x42\xcf\x38\xe4\x67\x16\x8b\x10\xe1\xa4\x71\x8f\xd7\x15\xa5\xe6\xf8\xa2\x9d\x23\x3c\xc4\x29\x86\x83\x5e\x1a\x9d\xc3\x99\x2c\x8a\xa3\xbc\x00\x93\x4d\x27\x38\x05\xd5\xf5\x96\x96\x9e\x43\x82\xb2\x3d\x3d\x1f\xe3\x38\xd7\x0a\xa0\x3c\x41\x17\x41\x1c\x8e\xb0\x32\x6e\x32\xec\xbe\x15\xb2\x62\x53\x03\x55\x4d\x77\x40\x49\xf7\x4d\x63\xc9\x53\x13\xa8\x28\xce\xd7\x25\x0d\xfd\x50\xae\x2f\x14\x13\xea\xec\x98\xc7\xfc\xa2\x06\x55\xc2\x7f\x24\x50\xe1\x85\x8c\x8d\x72\x88\xb0\x22\x96\xd0\xf4\x5f\x00\xe9\x32\xc2\x57\x2e\x14\x9d\xcd\x4b\x08\x1f\x70\x14\xd0\x97\x2f\x6a\x79\x4e\x70\x80\x25\xe8\x8c\x79\xf5\xef\xc8\x9a\x13\xb6\x23\xb0\xe8\xec\xc0\x8d\xaa\x75\xa3\x15\x27\x56\x7e\xd7\x8e\x96\xbb\xa5\xc5\x6a\x1c\xc4\x79\xab\xb9\x68\x13\x8b\xd5\xd8\x1b\x25\xc1\x6d\xaa\x74\xdb\xf0\xbe\x28\x7f\x4b\x52\x5a\xa1\x14\xec\x21\xf9\xd5\x75\x8e\x0f\x21\x39\x90\xf1\xda\x96\x77\x59\xa1\xbf\x7d\xba\xe8\x8a\xb6\xaa\xac\x88\xa2\xf4\x62\x2a\x84\x02\xda\x2b\x81\x1b\xda\xb2\xe3\x6c\xd1\x2c\xec\xce\x58\xf6\xd5\xeb\xdc\x66\xfc\xbc\x90\xbb\xa0\x0d\x95\x45\xf2\x69\x17\xf5\x4f\xa3\xb3\x5b\x25\xcf\x2e\xcc\xb9\xa3\xdf\x31\x55\xd5\x16\x8e\xa3\x6a\x51\xc1\x58\x8b\xd4\x16\x1e\x62\x6e\x84\xb6\x8e\x28\xf3\x6d\xcd\x7a\x46\x46\x93\xbc\x26\xf0\x50\x4c\xa4\x3e\x99\x99\x9b\xed\x06\x93\xc9\xe8\x9a\x35\x1c\xa4\xe7\x53\xc2\xc2\xb3\x32\x7f\x45\xc6\xaf\x57\x26\x69\x92\x27\x04\x47\x99\x73\x97\x19\x4e\xe8\xbb\x8f\x5d\xc1\xd2\xf5\x1f\x65\x9d\x3f\x47\xd6\x81\x80\xd1\x7f\x42\x5c\x22\x6b\x4e\xa5\x0a\x26\x12\xb0\xc5\xd2\x7b\x3c\x94\x17\xba\x75\x52\xe5\x84\x31\x0b\xa9\x24\x55\x5d\x6a\x37\x7f\x36\x49\xcf\xc5\x57\xba\x6d\x3b\x17\x39\x21\x6c\x62\x8b\x0e\xdf\x4a\xd0\xcf\xe8\x8f\x2c\x8a\x59\x30\x56\xc2\x32\x1a\x33\xbf\xc1\xfe\xea\xe8\x8b\x9a\xc6\x97\x2d\xaf\x5a\xdd\x6a\xa1\xfe\x6e\xa7\xa3\x59\x53\xd8\x0c\x40\x74\xaf\x49\xb4\xc5\x46\xd5\x62\x00\xc2\xd3\xde\x94\xde\x8e\x15\x9a\x60\x7b\xae\xe2\x53\x93\x93\x36\x66\xdd\xb5\x76\xa7\xd9\x6a\xf8\x1e\x6a\xcc\xf0\x70\x10\x06\xfd\xf5\x0d\x4b\x5e\xc5\xc6\x6c\x63\xbd\x1f\x84\x83\x21\xf6\x60\x60\x5a\xcd\x4e\x7b\xad\xab\x96\x3b\x73\xde\x88\x69\x69\xf4\xe4\x5e\xbc\x13\x99\xf4\x6c\x7b\xd7\x55\x30\x41\x18\xdc\xab\xe7\xef\x21\x7e\xd7\xbd\x63\xb8\xaf\xaf\xf9\x6c\x50\x24\x3e\x11\x78\x3c\xbd\x20\x8a\x1c\x11\x78\xdf\x7d\x92\x4a\xbf\x3b\xe5\x0f\x67\x36\x97\x10\xe9\x33\x21\x38\xb3\x00\xf9\xab\xd5\x6a\x12\x4c\xea\x29\x8e\xbe\x20\xf9\x25\xec\x75\xed\xba\xe6\x23\x8e\xbe\x54\x04\xd8\x6c\xd7\x2d\x00\x21\x94\xb1\xe2\x92\x6e\x82\xbb\x9b\x71\xc8\xbe\x72\x43\x61\xbf\xee\x57\x86\xb4\x81\xa4\x31\x45\x4b\xa8\xa1\x8b\x0f\x4a\x69\x5f\x2b\xed\x97\x96\x6e\x6a\xa5\x9b\xa5\xa5\x5b\x5a\xe9\x56\x69\xe9\xb6\x56\xba\x5d\x5a\xba\xa3\x95\xee\x94\x96\xee\x6a\xa5\xbb\xa5\xa5\xd7\xb4\xd2\x6b\xa5\xa5\xd7\xb5\xd2\xeb\xa5\xa5\x37\xb4\xd2\x1b\xe5\xb3\xd3\xd0\x66\x67\xce\x64\xfa\x5a\xf1\xf2\xd9\xf4\x9b\x5a\xf1\xf2\xe9\xf4\x5b\x5a\xf1\xf2\xf9\xf4\xdb\x5a\xf1\xf2\x09\xf5\x3b\x5a\xf1\x8e\xc1\x0d\x56\x57\x09\x43\xfe\x1c\xc5\xe7\xa4\x6a\x14\x8c\xfa\x36\xb1\x39\x20\xdb\xc0\xa9\x75\xa0\xfa\xf0\xc9\x3a\x28\x03\xf8\x64\x1d\x80\x10\x3e\xb5\x6c\xe8\xf4\x8a\x3b\x68\xf5\x1b\x41\x62\x6f\xaf\x16\x78\xa8\xef\xa1\x81\x87\x42\x4f\x5a\xa0\x1e\x42\x6b\x1e\xd9\x42\x1b\x67\x3a\x6f\x08\x69\xbd\xd0\x43\xa2\x6a\x31\x42\x1e\x42\x7e\xd3\x43\x27\xa7\xbe\x51\x6f\x40\xeb\xd1\x96\x68\xd5\x62\xd1\x92\x7a\x6b\xa4\x5e\xd3\xa8\xd7\xa7\xf5\x04\x92\x81\x54\xaf\xe5\x21\xd4\x84\xf6\x5a\x46\xbd\xb2\xfe\xb5\x45\xff\xda\x0b\xf5\xaf\x23\xfa\xd7\x59\xa8\x7f\x5d\xd1\xbf\xee\x42\xfd\x5b\x13\xfd\x5b\x5b\xa8\x7f\xeb\xa2\x7f\xeb\x0b\xf5\x6f\x43\xf4\x6f\x63\xa1\xfe\xf9\x0d\x8f\xf5\xcf\x37\x09\xa6\xac\x83\xbe\xef\xb1\x0e\xfa\x26\xc5\x94\xf5\x90\x60\x49\x7b\xe8\x9b\x24\x53\x4a\xa2\x2d\x8f\x93\xa8\x49\x33\xa5\x7d\x6c\x8b\x3e\x9a\x44\x53\xda\xc7\x8e\xe8\x23\x50\x8d\xd9\xc9\xd7\xaf\x1d\x9d\xf4\x10\xea\xd0\x4e\x9a\x74\x13\xd2\x8a\xd6\x4e\x12\x7a\xdb\xa0\x15\x4d\xc2\x19\xd0\x8a\xf6\x4e\xfa\x1e\x22\x1d\x3d\x39\xf5\x4d\xca\xe9\xd3\x8a\xd6\x4e\x12\x8e\xd1\x6c\x40\x45\x93\x74\xca\xfa\xd8\x11\x7d\x6c\xda\x79\x8d\xab\x8f\x84\xe6\x68\x1f\x9b\x76\x66\xe3\xec\x63\x87\xf7\xb1\x69\xe7\x36\xae\x3e\xb6\x45\x1f\x9b\x76\x76\xe3\xea\xe3\x46\xd1\x47\x3b\xbf\x71\xf6\xb1\x2d\xfa\x68\x67\x38\xae\x3e\x12\xc6\xc8\xfa\x68\xe7\x38\xae\x3e\xae\x17\x7d\xb4\xb3\x1c\x27\xad\xb6\x3c\xde\x47\x3b\xcf\x71\xf5\xb1\x29\x68\xb5\x69\x67\x3a\xae\x3e\xae\x89\x3e\xb6\xec\x4c\xc7\xd5\x47\xb2\xfc\x69\x1f\x5b\xbe\x7d\x41\xee\xef\xbb\x89\xb5\x0d\xb8\xb6\xec\x5c\x67\x7f\xdf\xde\x49\x32\xac\x64\x6d\x9d\x9c\xb6\xec\x5c\x67\x7f\xbf\x64\x41\x76\xa1\xa2\x9d\xeb\xec\xef\x3b\x3a\xd9\xf6\x50\xb3\x05\x15\x4d\xd2\x29\xeb\xa3\x5f\xf4\xd1\xce\x74\x5c\x7d\x6c\x17\x7d\xb4\x33\x1d\x57\x1f\x61\x22\x69\x1f\xed\x4c\xc7\xd9\xc7\x86\xe8\xa3\x9d\xe9\x38\xfb\xd8\xf2\x58\x1f\xdb\x76\xa6\xe3\xea\x63\x43\xf4\xb1\x6d\x67\x3a\xae\x3e\xb6\x44\x1f\xdb\x76\xa6\xe3\xea\x23\x61\xe5\xb4\x8f\x6d\x3b\xd3\x71\xf5\x71\x43\xcc\x63\xdb\xce\x74\x5c\x7d\x24\xcb\x83\xf5\xd1\xce\x74\x9c\xb4\xda\xe1\xb4\xda\xb6\x33\x1d\x57\x1f\x9b\x45\x1f\xd7\xec\x0b\xf2\xe0\xc0\x2d\xa8\x76\x69\x27\xed\x5c\xe7\xe0\xc0\xde\x49\xa0\x39\xe0\x01\x6d\x3b\xd7\x39\x38\x28\x11\x03\x3a\x20\x02\xda\xb9\xce\xc1\x81\xbd\x93\x84\x77\x34\x61\x58\x3b\x76\x51\xc7\xd5\x47\x32\x1f\xb4\x8f\x1d\x3b\xd3\x71\xf5\xb1\x25\xfa\xd8\xb1\x33\x1d\x67\x1f\x1b\xa2\x8f\x76\xa6\xe3\xea\xa3\x5f\xf4\xd1\xce\x74\x5c\x7d\x5c\x17\xf3\xd8\xb1\x33\x1d\x57\x1f\x81\xe6\x68\x1f\xed\x4c\xc7\xd5\x47\x10\xc9\x69\x1f\xed\x4c\xc7\xd9\xc7\x96\xc7\xfb\x68\x67\x3a\xae\x3e\xb6\x45\x1f\xbb\x76\xa6\xe3\xec\xa3\xcf\xfb\xd8\xb5\x33\x1d\x57\x1f\x9b\xa2\x8f\x5d\x3b\xd3\x71\xf5\x71\x43\xcc\x63\xb7\x65\x2e\x48\xb8\x46\xc9\x71\x3a\xc6\x61\x14\xe4\xcc\xa9\x0c\xdc\x15\xd4\x72\xe4\x88\x8b\xb6\x50\x0d\xfe\xbb\x84\x02\x5d\xc3\x4a\xcb\xf8\xac\x8c\x4f\xca\xf4\xed\x65\x9a\xac\x4c\x93\x94\x19\xd8\xcb\xb4\x58\x99\x16\x29\x13\x1a\xda\x5c\x4d\x55\xb9\x67\xb1\xd4\x5d\x30\xa0\x2d\x64\x4a\x17\xd9\x74\x83\x3c\xb0\x1d\xcc\x83\x3c\x10\xa1\x7c\x82\x3c\x70\x2b\xc7\xe2\x57\x51\x9e\x9d\x24\x79\x30\x12\x30\xe3\x9d\x20\x0f\xa8\x07\xc9\x73\xb4\x6e\x81\x0e\x75\xde\xe2\x61\xce\xa1\x0b\x8f\x13\x28\x6f\x74\xc6\x99\xf2\x4a\xa0\x79\x5a\x80\xfc\xf1\xc7\x1f\x51\x07\x2e\xde\x1a\xb3\xf5\x46\x71\xdf\x56\x94\xf8\x07\x6a\x35\x0d\xe2\x50\xfb\xb2\x8f\xb6\x10\xa8\xdd\x87\xa3\x24\x49\x6b\x52\x27\x57\x15\xdd\xbb\xab\x73\x50\xf6\x2d\xda\x92\x9e\xf4\x85\x23\x50\xaf\xd5\x6a\x05\x6e\x4b\xa8\xdb\xa6\xf9\xd2\x36\x20\x98\x68\xbb\x4e\x15\x36\x76\xfd\x2c\xaf\xca\x70\x2e\x94\xb3\xf2\xdb\xea\xda\x59\x13\x1c\x53\xcd\xea\xe0\xe6\xe9\x66\x0d\x2e\xb1\x48\x67\xdb\x55\x3a\xfb\xd6\xda\xd9\xb7\xb7\xed\xec\x5b\x6b\x67\xdf\x56\xed\xac\xd9\x5b\xd9\x89\xaa\x26\xba\xcf\x83\x4d\x41\x4e\x3d\xbb\xff\x20\x18\xbc\x53\x37\x06\xf0\x51\xb4\x79\x52\x95\xe6\x95\x9f\xe3\x0d\xa9\xe8\xbc\x2d\xe4\xbb\xcf\x0c\xe3\x9d\xde\x6f\x0b\xdd\x7b\x38\xae\xb8\x50\xd9\xf5\xbf\xc0\x04\xae\x30\xf6\x4f\xed\x77\x17\xfb\xec\x96\xac\x56\xdb\x57\xae\x25\xf6\x17\xbe\x8f\xa0\xb4\xb0\xaf\xdc\x45\xec\x3b\x2f\x21\xe6\xdf\x38\x1c\xb1\xdc\xc0\x30\x87\x2c\x02\x4f\x08\x63\xaa\x16\xad\x90\xac\x1c\xdc\x10\x4a\x59\x3d\x28\x58\xc1\x29\x53\xdc\xd0\xc1\x63\x71\xfd\x6f\x6c\xbc\xf0\xf9\x93\x41\x0b\x2e\xef\x4a\x1e\x41\x83\x7c\xb5\x7b\x38\xd0\x5f\x02\x49\x4d\xf5\x35\xf3\x50\xe6\x21\xf5\x0a\x0d\xf8\x24\xda\x42\x01\x5a\x42\xb5\x5a\x1f\xfd\x40\x37\xc7\xda\xff\x92\x9f\x61\x9d\xb0\x81\x19\x5a\x42\xb9\xd4\x9e\x08\x58\x1c\x93\x69\xca\xe8\x4a\xa5\x71\xca\x5b\x4d\xb4\x8c\xb2\x3a\x54\xeb\x6b\x46\x6f\x02\x2b\xed\xfc\x5f\x0d\x2b\xd8\x8e\x6b\x03\xf4\x03\xfa\xdf\x87\xc1\x4a\x3b\x04\xcd\xc5\xaa\x8f\x7e\x45\x03\xf4\x2b\x41\xec\xfe\x91\xd1\x04\xc0\xb9\xc8\x10\x44\x6a\x7d\xf4\xe5\x9e\x07\x47\xbe\xad\x3e\x76\xa5\x49\x9f\x9b\x78\xbf\x4a\x90\x35\xee\x27\xa6\xb9\x28\xc2\x6a\x30\xc1\x38\x9c\xc5\x1c\xa5\x6f\x1b\xd6\x8c\xad\x4b\x61\xe4\xf2\x6e\xa7\x63\xf1\xfd\x2a\x2f\x6f\x3a\x7c\x15\xf1\xc5\x94\xcb\x7c\x35\x23\xff\xbb\x9d\x8e\xd5\x64\xc0\x39\x09\x73\x72\xd5\xdf\xd7\x14\xdc\x2a\xb4\xc3\xfc\x89\x93\xbd\xfc\xee\x63\xe2\xa8\x53\x99\x98\x88\xfd\x71\x30\x20\x93\xa1\x64\x86\x37\xe7\x83\x15\x33\xe7\xa4\xc8\x66\x4f\xe7\xa5\x34\x03\x3b\x8b\x6c\xed\xb0\x80\x6a\xfe\xa5\x5d\xcc\xfe\xfe\x31\xd9\xe8\x62\x7b\xce\xe2\x0c\xa1\x3d\x8c\xc3\x7e\x30\xf8\xcc\xe2\x6a\x8e\x93\x10\x96\x14\xa1\x19\x31\xdf\xf0\xb2\xb7\xf7\x8a\x88\x40\x16\xf1\x00\xcc\x9c\xe0\xab\x62\x2d\x07\x16\x2e\xb4\x95\x77\x04\x00\x33\xe6\x11\xab\xbe\xb7\xf7\x6a\x65\x37\xa6\xb1\xca\xc1\x80\x6a\xef\x95\xc5\xe0\x67\xe2\x30\x97\x61\x66\x86\x25\x26\x33\x6e\xd1\x94\x85\xa0\xe2\x02\x09\x7d\xb4\xdd\x33\x4b\xa1\x3c\x68\x21\x39\x94\x87\x5a\x9e\xc7\x28\x7f\x83\xaf\xb3\x3c\xc5\xc1\x78\x3b\x0e\x59\xef\x2c\xd6\x91\x09\x33\x8b\x15\xe0\x3c\xd6\x80\x4d\xc8\x3e\xc2\x63\x0c\x41\xc6\xc1\x18\x93\xce\x13\x8b\x95\x09\xfe\xf3\x31\x9e\xe5\xf4\xb5\x5d\x7c\xc7\x97\xaf\x58\xcc\x54\x68\x7d\x25\x1b\x45\x03\x5c\xe3\x28\x88\x9b\x7a\x81\x8b\xcd\x7e\x52\x99\xb5\x1d\xfc\x77\x99\xb5\x3b\x8c\x2e\x18\x0e\x5f\x44\xd9\xc2\x63\xfb\xd5\xe8\xe6\xa4\xe8\x50\x1f\x0f\x92\x31\xf3\xba\x27\x04\x11\x25\xd3\xac\x1a\xc9\x88\x2e\x56\x12\xc7\x4b\x7a\x53\x9b\xdb\x05\xcd\x37\xc2\x3c\xb0\xc1\x79\xef\xb2\x08\xd6\x72\xf9\x42\x35\x1a\x97\xc3\x31\xd3\xe6\x8b\xcf\x90\xd9\xf5\xd2\x7a\xa4\x11\xa5\xd1\x16\x8a\x2e\xd9\x14\x36\x1c\x2b\x31\xb9\xc4\xe8\xe0\x27\x38\x7f\x66\xd3\x7e\x86\xff\x3d\xc5\x71\x5e\x72\x7a\x06\x7c\x85\x03\xc3\x5c\x03\x68\x1d\x1f\x6d\x42\xcc\x49\x20\x7f\x8c\xca\x31\x1d\x68\x28\x58\x13\x40\x3c\xa4\x76\x65\x75\x15\xb1\x19\x29\xde\x59\xb3\xe5\x96\x47\x8d\xa1\xa6\xe7\x85\x85\x20\x44\x82\x11\x8d\xc2\x39\xda\xa0\x17\x86\x05\x17\x27\xf6\x5e\x95\x19\x5c\xf3\x4d\x67\x91\x38\x75\xdd\xd6\xa3\xf0\xf1\xad\x0b\x1f\xe8\xbf\x27\x29\xce\x70\x7a\x89\xa9\x18\x92\x4c\x89\x28\x2f\x89\x1f\xa0\xc6\x08\xf2\xa8\x3f\x62\x1c\x18\xed\xa4\xe8\x55\x1a\x05\x31\x7a\x4d\xdd\x33\xd1\x30\x1a\x61\x1c\x0f\x56\x06\x00\x82\x87\x7c\x86\x08\xd8\x1a\xfd\x9c\x1c\x41\x91\x7f\x06\x31\xda\x4f\xa7\xfd\x6b\xf4\xdb\x05\xf9\xcf\xca\x15\xee\xff\xf7\xf9\x38\x88\x46\x2b\x83\x64\x6c\x97\x77\x4e\x8e\x78\x73\x25\x62\x8f\x5c\xa8\xb2\xf4\xf3\xa4\xc8\xf7\x12\x0f\xc8\x41\x81\xa6\x4c\x7a\xfa\xe4\x09\x19\x74\x20\x3d\x91\x0e\x09\x94\x44\x54\x29\x54\x87\x59\xa7\xbf\xfe\x40\xab\xab\xc9\x25\x4e\x87\xa3\xe4\x8a\xd4\x81\x8d\xcf\xe7\xe9\x40\x49\x3d\xbf\x5b\xff\x81\x94\x7d\x21\x3e\x37\xe5\xcf\xeb\xfa\xd7\x16\xdb\xc3\x58\x63\x80\x27\xa0\x42\xc0\x8a\x76\x57\x57\x11\x6f\x16\xf5\x7d\x52\x04\x50\x86\xa6\x1b\x2f\x44\x95\x66\x51\x45\x94\x79\x02\x08\xd0\x42\xb4\x54\x4b\x2d\xc5\x8a\x3d\x01\x54\x58\xb9\x1b\xf8\x97\x10\xa4\x5c\x62\x69\xa9\xdf\x92\xbe\xc3\x3f\xbc\x0c\x2d\xb2\xb4\xd4\x6f\xbe\x78\xea\x2e\xb0\xb4\xd4\xf7\xd9\x77\xf2\x2f\x74\x9c\x37\x0a\x0f\x4b\x5b\xd0\xf3\x97\x2f\x59\x3e\x48\xf9\x75\x93\xaa\x00\x95\xb7\x0c\x21\xb3\x25\x51\xad\x31\x6b\xf8\x4c\xeb\x57\x14\x65\x5c\x8f\x14\x22\x2f\x6f\x74\xea\x60\xcb\xa3\x36\xa0\xff\x55\x69\x84\xbd\xa4\x37\x48\x9c\x94\x8a\x97\x75\x46\x30\xd2\x14\xac\xae\x22\xb2\x4b\xc0\x4d\x0c\x8a\xa4\x85\x44\x17\x8f\xb1\xd2\x9e\x65\x08\xe0\x65\x28\x89\x47\xd7\x74\x39\xee\xfc\x7c\x78\xb4\x83\x7e\x43\x2f\xd1\x3a\xc0\xe4\x0d\xfa\x36\x2c\xe8\x5d\x9c\xda\x59\xf6\x8d\xf7\x97\xaf\x25\xe5\x2c\x20\xd6\xd5\x8a\xe3\xf5\x9f\x28\x73\x2e\x2a\x72\x1a\xc5\x35\x19\xc6\x6c\x95\xf1\x44\xd1\x2c\x1f\x30\x03\xf5\x32\x89\x07\xb9\xa5\x1e\x10\x1a\xec\x8d\x94\xcb\x40\xe8\x16\x72\x10\x9a\x2f\x0b\x71\xe9\x80\x10\xb6\x49\xf3\x94\x15\x3d\xd1\x45\x23\xf6\x59\xc2\x55\x55\x3d\x2f\x22\x14\x21\x87\x60\x84\x6e\x27\x1c\xa1\x05\x05\x24\xa4\xca\x73\xe6\xa1\xab\xa0\x7b\xf9\xec\x25\x96\xc6\x0b\x4d\xb2\x12\xc5\x25\x01\xcb\x29\x62\x49\x85\x17\x90\xb4\xda\x8f\x92\xd6\xb7\x2e\x69\x39\xe4\x2b\x87\x7a\xe7\xe4\xa8\x5c\xce\x59\x54\xbd\x63\x61\xe9\x3a\x2f\x7f\x64\xe2\x7f\x3f\x26\x5e\x7a\x9a\x7d\x00\x96\x7d\x10\x0f\x52\x0c\x91\x1b\x18\x70\x0d\x24\x93\x43\x8a\xc9\x5d\x46\xd4\x98\xc6\xf1\x05\x6e\xcb\xbf\xa0\xc6\x5f\x6a\x73\xa8\xba\x2b\xcc\x3f\x6f\x93\x32\x0b\xec\x02\x9d\xc7\x5d\xe0\x2f\xb1\x0b\xec\x8e\xf0\x20\x4f\x93\x38\x1a\xa0\x5e\x12\xe2\x7e\x92\xcc\x57\xf8\xef\xf6\xca\x14\xfe\xf4\xeb\x42\x3b\xc2\x6e\x4f\x55\xf8\x93\xe7\xfb\xda\x01\x64\xd6\xae\x32\x10\xb5\x5e\x99\x16\x93\xe0\xa3\x2c\xa4\x87\xc2\x2f\xc4\xb7\xc2\x8f\xa7\x5e\xea\xcd\xd7\x9b\x41\x99\x05\xd6\xf1\x5f\x3b\x39\xf2\x7f\xce\x3a\x3e\x9c\xe6\x93\x69\x5e\xfd\xd2\xee\xb0\xf4\xd2\xee\x70\xf1\x4b\x3b\x5d\xaa\x3b\xd4\x2e\xf1\x0e\xff\xdc\xeb\xa0\x07\x97\xea\x4c\xdd\xbc\x78\x73\xbf\x92\x5d\x49\x43\xdf\x8a\x74\xf7\x77\x3a\x61\x1f\x6a\xd7\x9a\x2e\x21\xea\xb0\xc2\xa5\xc5\xe1\x82\x97\x16\x8f\x59\xec\xfe\x1a\xcc\x77\xfb\xfd\xf1\x01\xfa\x65\x65\xa3\xd9\xe2\x06\xe2\x28\xcb\xc9\xf2\x3e\xbf\x36\xb8\xef\x24\x08\x57\xb6\xe3\x2c\xfa\x85\x94\x16\xb9\xe0\x26\x41\x28\xb3\xbf\x30\xc8\x03\xe9\x22\xd4\x75\x01\x9a\xa9\x37\xa0\xa4\xd6\x71\x61\xf0\xab\x18\x00\xbf\x50\x8b\xf6\xf5\xb4\x22\x7d\x57\x42\x11\x20\x8a\x69\x9c\x8b\x9e\x69\xc1\xac\xc0\x16\xef\x03\xfd\x66\x00\xa3\x2f\x96\x55\xcc\xfe\xa1\x7d\x37\x5a\xa3\x31\x6d\x46\x41\x46\x23\x67\xa1\x49\x92\x45\xaa\x07\x3e\x69\x94\x7c\x27\xf5\x3f\x24\xbc\xb3\xa2\x85\x25\x0d\xa3\x65\xe4\x6b\x8d\x7c\x08\xc2\xe2\x19\x06\x4a\x64\x1b\x51\x5f\x53\x56\x22\xb7\x55\x84\xd4\x52\x1b\x29\x42\x6a\xc9\xa5\x6d\xc1\xb5\x54\xcb\xec\x25\x0d\x10\xb7\x43\xe4\x16\xb8\xd3\xd8\x42\x1c\x3a\x45\xbc\xc6\xb9\x94\x70\x5e\x99\x2a\xaa\xc0\x17\xa3\x59\x3e\x73\x52\x9f\x6b\x2a\x9a\xcb\xe4\xf8\xcb\xfa\x5e\x5c\x04\x49\x28\xb0\x7d\xc5\xf0\x90\xd0\xc0\x38\x7a\xfb\xf4\xc9\x8d\x95\x6f\xf2\xe5\x32\xdb\x68\xb6\x16\xe2\x9d\x77\x4b\x4c\xf6\xc8\x3b\xbf\x16\xef\x3c\x38\x3e\x44\x10\x12\xb7\x1a\xeb\x3c\x60\x01\x74\xef\xca\x3a\xff\x74\x76\x58\x2c\x89\x39\xfc\xd0\xc2\xaa\x68\x3a\x00\x7b\x04\xba\x95\x34\x88\xc3\x64\x5c\x33\x38\x60\xbd\xbe\xa2\x49\x4a\xe5\x70\x58\xea\xb0\x53\x83\xcb\x35\xdb\x67\x1e\x01\xf7\xc8\xa8\x74\x46\xc5\x89\x73\x21\x46\xf5\xd7\xce\xbc\xf0\x1f\xc5\xa8\x56\x0f\x76\x7b\x68\x63\x6d\x63\x6d\xd9\x47\x8c\x36\xd0\x3b\x9c\x5f\x24\x21\x6a\xba\xb8\x15\x84\xf6\xbe\x2d\xb7\xda\x0e\x43\xea\x3f\xa8\x2e\x88\x0a\x5c\x80\xaf\x5e\x52\x9b\xfe\xf1\x45\xab\x34\xf0\x3f\x38\x4d\x20\x77\x58\x7e\x81\x51\x8a\x33\x89\x2f\x2a\x1d\x21\xe5\x58\x8f\xc9\xb3\x81\xf7\xad\x78\x01\x5b\x88\xbf\x33\x1c\xd4\xd5\xe8\x6c\x1e\x40\x53\x78\xf6\x85\x9d\xc4\x18\x8d\x93\x14\x53\xe1\x71\x79\x19\xfa\xe6\x1a\x45\xbe\xde\x97\x97\x2b\x2e\x70\x98\xcf\x45\x16\xf8\xda\xdd\xa2\x9c\x3f\x2e\xf0\xaf\x76\x8a\x43\x71\x92\x4c\xaa\x89\x21\xef\x39\x39\x3a\x57\xb6\x20\x76\xf7\x9a\x28\x8a\x94\xd1\x9c\x68\x6a\x21\xa2\xbb\x5b\xb8\xd9\x47\xa2\xfb\x5a\x44\xf7\x3f\x12\xf3\x2b\x27\x39\x89\x07\xfe\x89\xc2\x6f\xe5\x83\xb3\x7c\xbe\x35\x04\xe0\x5a\xad\x5c\x04\xae\xa3\x2f\x5f\xf4\x57\xb7\xda\x62\xec\x3d\x9e\x1f\x57\x60\x75\x15\x7d\x24\xf0\xd5\x7a\x91\x11\x29\x00\x34\x0b\xa2\xcc\xd5\x45\x34\xc2\xa8\xf6\x5d\xad\xf0\xb5\x2e\x62\x70\x83\xc7\xa1\x11\x73\x5b\x98\x70\x1a\x8a\xcc\x48\x6c\x49\x48\x55\x51\xea\x8e\xdd\x10\x8f\xb7\xca\xee\x25\x51\xd0\x42\xbc\xe4\xaf\xed\xb8\x65\xc9\xd1\x45\x93\x64\x3d\x2c\x5f\x29\x32\x21\x41\x6b\x7f\x7e\x9e\x8f\x87\x4d\x12\x5e\x2d\x26\xb6\x11\xf3\x5a\x7c\x39\xde\xdf\xf6\x8b\x58\xcf\xe4\x49\xfa\x68\x26\x02\xb7\x39\x88\x7e\x08\xb2\x8c\x2c\xe4\x65\x82\x5a\x88\xde\xe0\x6b\xb4\x83\xd3\xe8\x92\xe6\x84\xdc\xe3\x83\xd2\x2c\x8f\x39\xfd\xe1\xd5\x9b\x9d\xbd\x66\xd1\x9a\x78\xae\x98\x78\xbc\x97\xc4\xc3\xe8\x7c\xca\x32\x51\x26\x90\x15\x32\x2b\xcb\x2f\x99\x26\x13\x9c\xe6\xd7\xe8\x0f\x7a\x2c\x06\x6f\x52\x60\xbe\x27\x17\x34\xc7\x71\x46\x1e\xa2\x98\xa5\x0b\xc8\x13\xe1\x4b\xb3\x82\x76\xf0\x30\x98\x8e\xf2\x4d\xd4\x46\x35\xbf\xb9\x0e\x89\x94\xeb\x2e\xf8\x8e\x84\xe6\x38\xe5\x89\xcc\x0b\x70\x64\xfc\xe7\xa1\x19\xe5\x2c\x79\x66\x06\xa0\x8a\x43\xbd\xf4\x21\x4f\xd0\x04\xa7\xc3\x24\x1d\x4b\xc0\x15\xc8\x52\xfa\xc7\xc1\xf0\x7c\xd3\x35\xca\x88\x5e\x7c\x1d\x43\xcc\x19\xbf\xb9\xbe\xda\x6a\x6a\x21\xb8\x69\x57\x28\xea\xda\xa7\x02\x21\xa5\xf1\x9b\x7a\x59\x42\xd2\xb2\x04\xf2\x64\x56\xc2\x82\xb4\xf8\x7a\x9b\x9f\x45\xf4\x10\xf8\xdc\x0d\xe9\xaa\x9c\x31\x94\x8c\xdf\xc0\x46\x37\xdc\xdf\x6c\x98\xa4\x70\x8a\x29\x1a\xbd\x87\xc4\xa0\x9f\xc3\xa1\x91\x34\x9e\x52\x3b\x3f\x3d\x2a\x66\x58\x8b\x54\xfc\xa3\x98\xac\x75\x9a\x7e\xf2\xce\x60\x3c\x75\x1a\x1b\x8d\x86\x0e\xb8\x24\x7b\xfd\x60\x78\x6e\x37\xbc\x20\x13\xb1\x25\x7e\x72\xc2\x23\xc5\x5d\xc1\x30\xcc\xf5\x0e\xd7\x15\xd4\x83\xae\x2a\x0b\xba\x4d\xbe\xd9\x09\x83\x0d\xd4\xc2\x1f\x56\x2a\x56\xce\x82\x51\x8e\xb6\xe1\x3f\x8b\x27\xa2\xe5\x6e\x34\x92\x5f\xfb\x5d\xc8\x8e\x26\x52\x0f\x87\x2b\x2c\x2a\x49\x8d\x77\xc6\x03\xfc\x9c\x93\xca\x8a\xcb\xf3\xaa\xd5\x5c\x28\xb7\x8b\x3a\xf5\x56\x03\xc2\x28\x77\x24\x85\x65\x5e\xf6\xe0\xbb\xcf\x68\x95\x90\x0f\xe5\x41\x9e\x98\x1d\xbb\x59\xa2\x3b\x41\x39\xc8\xa6\x74\xb0\x69\xba\x79\x43\x9f\x63\x0b\xf5\x04\x72\xf2\x41\x1c\xe2\x99\xad\xc6\x69\x63\xc6\x14\x40\x96\x68\x9d\x73\x42\x74\x09\x54\x84\xb0\x2c\xde\x38\xf3\xd7\x17\xd8\xf0\x4a\xc5\x1b\x67\x25\xbe\xe5\x6d\x91\x59\x59\x61\x4f\x36\x23\x8c\x62\x6b\xa1\x45\x8b\x17\x73\x8c\x2c\xd4\x8f\x4c\x50\xd7\x3a\xc8\xe3\x22\xbd\xe4\xf8\x58\x8d\x0b\x44\x27\x59\x9e\x63\x9e\x2c\x1b\x28\xb0\x48\xe3\x5b\xf4\x5a\x9f\x33\xc4\x32\x7a\x17\xa9\x81\xcd\xef\xf3\xb3\x31\x00\x7c\x65\x88\xad\xa3\x6b\x16\x17\x59\x8c\x8a\x57\xac\xe3\x0e\x44\x0e\xc4\x18\xdb\x41\x47\x72\x34\x3b\x06\xd6\x82\x85\x62\xcb\xe1\x53\x5b\x0e\x69\xfa\x9c\xc6\x1c\x08\xf8\xb9\xd2\x04\x8c\x9e\x18\x69\xf9\xa3\x6d\xac\xab\x8c\x37\x9a\x17\x0a\xca\xd6\x59\x3e\xfa\xf2\x3b\x7b\xc0\x2a\xa9\x89\x5f\x0e\x8f\xd4\xee\x80\xeb\x94\xc5\xe3\xda\x18\xb7\xdf\xa8\x0d\xcc\x6f\xdc\x06\x46\x9a\xcd\x17\xe8\xb7\x92\xd1\x23\x7f\x45\x8d\xd3\xdf\xc0\x1c\xc6\xe8\xc8\xe9\x6f\xba\x59\x0c\xff\xbb\x31\x5f\xeb\x01\xa7\xc8\x9f\xc4\x1c\x98\x6e\x1a\x1a\xb5\x4d\x89\xc6\x24\x4e\x1b\x67\x4b\x4b\xe5\x26\x45\x12\x70\xe9\xe8\xcb\xf9\x86\x25\x88\x19\xdb\xcb\x8a\x7a\x65\x06\x94\xf2\x31\xe2\x4e\x1b\x7a\x95\x60\x33\xa5\x1b\xf9\x82\x9b\xf8\x7d\x89\x96\x51\x66\x4b\xb7\x3f\x3f\x7a\x8d\x45\x34\xb8\x87\x20\x36\x54\x44\x10\x92\x21\x15\x0a\x5d\x62\xc2\x62\xd5\x3c\xe4\x90\x4d\xef\x02\xa6\x54\x36\x2d\x82\xec\x88\xa3\xa4\x4b\x80\xf1\x90\x2e\xa8\xb2\x61\x57\xc5\x62\x52\x68\x8e\xf0\x74\x53\x66\x8b\x46\xa1\xd9\x03\xf5\xe8\x29\x74\x79\x4e\xd8\x9b\x33\x6f\xed\xaf\xed\x43\xbf\x40\x5a\xf7\xf9\xc9\xd1\x1f\x56\x77\xe4\x4c\xaf\xed\xca\x7a\xfd\x77\xd0\x2e\x1d\x83\x71\x66\x8f\x1b\xef\x52\x25\x92\xfc\xb2\x4c\x8f\x24\xf0\x38\xc2\xd3\x2c\xe8\x8f\x30\x0b\x07\x26\xa1\x73\x8c\xe4\x54\x8b\x14\x8a\xfe\xe6\x35\x52\x33\xac\x49\xdb\xc2\x11\x64\x53\x46\xcc\xd0\x96\xd9\x18\x9b\x9a\x24\x51\x1e\x62\xac\x44\x19\x0a\x10\x4d\xc0\x8c\x2e\x71\x9a\x41\xd4\xb2\x8b\x20\x47\x31\x3e\x1f\xe1\x41\x8e\x43\xc2\x86\x07\x2c\xa5\x6a\xce\x14\x3e\x79\x82\x46\x51\x9e\x8f\xf0\x32\x0d\x70\xb9\xa2\x02\xc5\x69\x9a\xa4\x28\x4c\x70\x16\x3f\xcb\x51\x30\x1c\xe2\x01\xad\x4b\x91\x7a\x96\xa1\x0c\x0f\xa6\x69\x94\x5f\x7b\xa2\x62\x7f\x9a\xa3\x28\x87\x4a\xbc\x46\x94\x67\x22\xa0\x42\x34\x8a\x72\xe6\xc4\x4d\xf3\xba\x46\x84\x3f\x8f\x71\x4c\xf7\x83\xcc\xa6\x28\xa3\x03\xf2\x96\x76\x4e\xa8\xcb\xb4\xb7\xf2\xfc\xdd\x36\x69\x5b\xf9\x21\xe5\x8d\x6c\x06\xed\x3c\x60\x14\xd6\xdb\x70\x6a\xb8\x2c\x3b\x2d\x44\xec\x84\x46\x76\x2f\xec\x3c\xa7\xfd\x22\xda\x25\xbf\x2c\x89\xe3\xde\x9c\x36\xce\x3c\x54\x7b\x73\xda\x3a\x63\xc1\x02\xd0\x17\xf2\xc8\xae\x02\xfc\x6e\xdd\x92\x44\xee\xcd\xa9\x4f\x2b\x35\xd4\x4a\xad\xf2\x4a\x4d\x5a\xc9\x57\x2b\x35\xca\x2b\xb5\x68\xa5\xa6\x5a\xc9\x17\x95\xd4\x3a\xb6\xec\x48\xc6\x90\x71\x2f\x43\xd7\xa0\xf5\xc4\xa0\xf5\xec\x83\x66\xe2\x23\x0d\x17\xeb\x13\xbd\x30\x19\x0e\x79\xda\x41\x8a\x34\x0d\xb2\xda\x68\x90\x2f\xb6\xfe\x9a\x13\xd1\x52\x21\xfb\x56\xc8\xcd\x4a\x90\x1b\xce\x81\x97\x60\x68\x90\x5b\x95\x20\xfb\xae\xd9\xf1\x24\x18\x1a\xe4\x86\x06\x79\xfe\x44\xf6\x82\x34\xbd\x46\x7d\x3d\x9d\x2a\x9d\xaa\x3e\x8d\x7f\x61\x6a\x32\x72\x3a\xf9\x84\xf5\x64\xd7\x59\x8e\xc7\x68\x98\x4c\x53\x94\x47\x63\x7d\xee\x17\x0c\xca\x1b\xe3\x59\x7e\x4c\x56\x9f\x3b\x7e\xac\x25\xe2\xed\xbb\x24\x8c\x86\xd7\x94\x13\x52\x3a\xac\x80\xc5\xba\x1b\x8b\xde\x29\x75\x1c\xf8\xe5\x14\x52\x5e\x42\xb4\x15\x23\x53\x9c\x2d\x49\xee\x4f\x28\xc3\xf9\x74\xa2\x7e\x28\xf1\xe8\x98\x7f\xd8\x3f\xf8\x89\xba\x76\x94\x9d\xf0\x0f\x7e\xfa\xd4\x40\x5b\xe8\xe0\x27\x33\x35\x9a\x54\xc4\xa7\x45\x7c\x6b\x34\x63\x79\x49\xc3\x54\x66\xd3\xfe\x25\x26\xa2\x82\xeb\xe8\xdf\xa0\xc1\x8f\xa1\x6d\x1a\xfd\xf8\x0b\xa2\x4f\xae\xe8\xc7\x72\x71\x16\xe6\x58\x94\x2f\xae\x43\xed\x61\x8e\x45\xb3\x4d\xd1\xac\xaf\x34\xeb\xcf\x6b\xd6\x57\x9b\xf5\x17\x6b\x16\xc2\xe8\x44\x0d\xbe\x04\x09\x90\xa8\xa9\xae\x40\x57\xd5\x16\x54\x6d\xf2\xc5\x0c\x55\x1b\xea\x32\x75\xcc\x08\x23\xeb\x32\xd6\x8a\x80\x5a\x1b\xf4\x5c\xaf\xc7\xf6\xa7\x1f\x7d\xfa\xd1\xb7\x7e\x6c\xd2\x8f\x4d\xeb\xc7\x16\xfd\xd8\xb2\x7e\x6c\x97\xb5\xd9\x29\x6b\xb3\x5b\xd6\xe6\x9a\x68\xb3\x44\x23\x55\x89\xf3\xa0\xc5\xb9\x0f\xaa\xc6\x81\x90\xa9\xa4\x90\xfd\x88\xee\x25\xb9\xab\x53\x79\x2d\x49\x1f\x95\x38\xb3\x5a\xc4\xde\x3b\xf7\xf6\x0e\x83\x5b\x78\x99\x01\x17\x52\x4b\x1f\xd3\x50\x43\xbf\x00\x11\xa2\xda\x2f\x64\xee\xf9\x2a\x81\x67\xb1\xf7\xbe\xd0\x2b\xfa\xb4\x62\x93\x55\x5c\xd3\x2a\x76\x9c\x15\x9b\xb4\x62\x9b\x55\xf4\xb5\x8a\x6b\xce\x8a\x2d\x5a\xb1\x7b\x26\x50\x53\x2a\xfa\x45\xc5\x3b\xed\x62\x65\x51\xea\x29\x22\x3c\x76\xfc\x31\x4b\xc9\xce\x82\xc7\xc3\xe3\x6d\xa2\xc7\x73\x38\x8c\xc1\x09\x38\xb6\xf8\xf1\x56\x7c\xad\x4e\x78\x48\xca\xd1\x2b\xbc\xe9\x8e\xcb\xbd\xe8\x64\xea\x17\x76\x3c\xc5\xcd\x6d\xf1\x31\xba\xa4\x5f\xba\xed\xd5\x56\x53\x57\xcb\x89\x65\x22\x08\xb6\x56\xd1\x15\x4a\x59\x1f\xca\x17\x49\x04\xd5\x0c\x7e\x8e\x83\x4b\x8c\x92\x51\xe8\x64\xb5\x0b\xc8\x0f\xbd\x4f\x74\x72\x7b\x7a\xbc\x43\xa5\xc5\x5e\x30\x1a\x4c\x47\x64\x85\xc5\xf8\xca\xd9\x6c\x8f\x25\x82\xe9\xd1\x44\x30\x8d\x59\x3b\x6c\xc1\xff\xa1\x25\x2e\xa1\xe9\xf9\x5a\x7a\x2c\x2f\x4c\x8f\xe6\x85\x69\xcc\x58\x8d\x16\xc4\x94\xef\x71\x01\xb5\x51\x47\x2f\x51\xad\xf7\x49\x7a\xfe\x2f\xe4\xa3\x4d\xd4\xa8\x9b\x10\x9b\x0c\x62\x93\x42\x64\x00\xdb\x0c\xa2\xaf\x41\xf4\x2b\x40\x6c\x31\x88\x2d\xa3\x5b\x35\xda\x8e\x02\xb1\x59\x01\x62\x9b\x41\x6c\x5b\x7b\xdd\xd2\x20\xb6\x2a\x40\xec\x30\x88\x1d\x6b\xaf\xdb\x1a\xc4\x76\x05\x88\x5d\x06\xb1\x6b\xed\x75\x47\x83\xd8\xa9\x00\x71\x8d\x41\x5c\xb3\xf6\xba\xab\x41\xec\xce\x85\x58\x88\xfd\x14\xa8\x52\x7d\x4d\xaf\xae\x7b\xc7\x08\x9a\x26\xbb\xcf\xf9\xf2\x1d\x16\x11\x29\x75\x3e\x03\x5e\x1d\x91\xae\xf5\x2c\x49\x38\x78\xba\xfc\x74\x3a\xc8\xd1\x45\x74\x7e\x81\x82\x38\x44\xa3\xe4\x0a\x05\xe9\xf9\x14\xc2\xbf\x80\x9b\xf3\xbf\xa7\x41\x6a\x24\xee\x81\x06\x02\xb4\x45\x5a\xe1\x52\x9c\x45\x79\x70\xde\xa7\x45\xe8\x2e\x61\x3d\x3e\xf1\x3e\x2b\x18\xa4\x38\x9b\x8e\x72\x94\x0c\xcb\x9a\xbf\xa0\x5b\x40\xed\x3c\x40\xcf\xd1\x79\x40\x5d\x57\xfc\xb5\x3a\x5a\x42\xf4\x55\x9f\xbd\xea\xc0\xab\x3e\xbc\xb2\x21\x39\xa2\x80\xa4\xae\xd0\x23\xe1\x73\x74\x3e\x83\x19\xae\x03\x41\xf0\x02\x42\xec\x94\x0a\xd8\x12\xc1\x90\x0e\xfd\x72\x78\x84\x20\x9c\xa4\xfc\xf1\x35\xe5\x70\xe7\x17\xe8\x57\x74\x3e\xaa\xca\xe4\xec\x4a\x95\x5f\x18\x8b\x7b\x4d\x59\x5c\xad\xf6\xba\xd8\xbe\xc9\x4e\xf6\x5a\x12\x0b\xea\xac\x40\x57\x2d\xd0\x2d\x0a\xe8\xf4\xfc\x0b\xe3\x86\xaf\x29\x37\xac\xd1\x66\x8a\xfd\xf6\x35\xe7\x7f\xb0\xdf\x2e\x21\xd2\x9a\x09\xa3\xc9\x60\x34\x39\x0c\x5f\x45\xc0\x37\x30\x6c\xa8\x05\x1a\x65\x18\xb6\x18\xf4\x16\x87\xde\x54\x31\x6c\x6a\x18\xfa\x16\x0c\xdb\x0c\x46\x9b\xc3\x68\xa9\x08\xb4\x0c\x0c\x9b\x6a\x81\x66\x19\x86\x1d\x06\xbd\xc3\xa1\xb7\x55\x0c\xdb\x1a\x86\x2d\x0b\x86\x5d\x06\xa3\xcb\x61\x74\x54\x04\x3a\x06\x86\x6d\xb5\x40\xbb\x0c\xc3\x35\x06\x7d\xed\x4c\x21\x11\x81\x61\x57\xc3\xb0\xa3\x60\x58\x29\xf1\x47\xc6\x93\x4e\x08\x5d\x6b\x85\xb4\x13\xf3\xae\xbb\x28\xac\x1c\xcf\x72\xf9\xde\x49\xd6\xa4\xf2\x50\x0a\x4a\x1a\x07\x7a\x5b\x64\xde\x5f\x4d\x46\x01\xc1\x66\x96\x23\x27\x38\x16\x67\xa6\x56\xb4\x6c\x83\x28\x2e\xae\xca\x94\xba\x6a\xf2\x0e\xb9\x64\xbd\xec\x0e\x4a\x2e\x58\xd9\x18\xd9\x53\xef\x46\x36\x3b\x6d\xaf\xb8\x14\xd9\xec\x74\x3d\x76\x57\xb2\xd9\xf5\x6f\xce\xbc\xb5\xbf\x76\x24\xc2\xc7\xfb\xaa\xc7\xfb\xaa\x07\xbb\xaf\xd2\x96\x78\x71\x9f\xa3\xdf\xe4\xfc\xb5\xee\x70\xee\x2b\x2b\xdc\x1b\x71\x34\x7f\xa3\x1e\xcd\xdf\xdc\xf6\x68\xfe\x46\x3d\x9a\xbf\x29\x3b\x9a\xcf\x53\x30\x3f\xde\x54\x3d\xde\x54\x3d\xde\x54\x29\x5f\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\x8a\x66\x1f\x6f\xaa\xf4\x8f\x8f\x37\x55\x8e\xc7\xc7\x9b\xaa\xc7\x9b\xaa\xc7\x9b\x2a\xf8\x7b\xbc\xa9\xaa\xa6\xc4\x7d\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x92\xfe\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\xfe\x93\x6f\xaa\xee\xed\x8e\xea\x76\xb7\x53\x55\xee\xa5\x2a\xdc\x48\x3d\xd4\x5d\xd4\x5f\x3b\x1f\xca\xe3\x5d\xd4\xdf\xff\x2e\x4a\xbe\x3b\xea\xb5\xe7\x3a\x3a\xc9\x37\x47\xbd\xb6\x74\x6d\x04\x0f\x0f\x7f\x67\x44\xbd\x34\xc5\xad\x91\x3d\xa8\x00\xf7\xd0\x2e\xbb\x56\x02\x37\x4e\xd9\xa3\x58\x8a\x99\x6e\xea\x2b\xe2\x28\x47\x59\x3f\x99\x99\x70\x8e\x05\x3a\xc7\xf2\x35\x1d\xff\xb3\x49\x93\xcd\x4e\xd7\x7d\x28\x67\x87\xee\x68\xbe\x1a\xf7\x0d\xbe\xb6\xe9\x71\xd5\x16\x3d\xee\x3f\x3e\xb7\x61\x36\x28\x64\x08\x78\x54\x89\x08\xfd\x43\x1e\x27\x87\xea\x90\x55\x22\x5b\x1b\x1f\xfb\x53\x05\x90\x19\x09\x4d\xf9\x6c\x04\x45\xb3\x9d\xfd\x49\x2f\x6a\xbf\xa1\x25\x3a\x3e\x4b\xbc\xd1\x3a\xfa\x07\xf4\xca\x11\x4b\xe1\x2a\x98\xd8\x71\x86\x7d\xc3\xd4\x10\x48\x13\x70\x6c\x77\x8c\x27\xaf\xc9\x8c\xcf\x9f\x9e\x9e\x55\xc5\xcf\xb2\x6a\x08\xa2\xf9\x8d\x65\x99\x15\x80\xee\xac\x96\xe3\x9a\x10\xd0\x82\x18\xf9\xd7\xc9\xf4\xd8\x55\x86\x4a\xcb\xc2\xc9\xb9\xd9\xe9\x3a\x14\x22\x0d\xa7\x32\xc4\xda\x68\x55\xc5\x88\xb4\x9e\x34\xc5\x48\x31\x68\x91\xf6\xe5\xb7\x62\x38\xe7\x66\x80\x07\xe5\xa0\x5a\xfd\xb3\x8c\xa7\x36\x1f\x62\x35\xc5\x74\x19\xc5\x54\xa5\x16\x5b\x16\x51\x04\x1a\x74\x9a\x30\x8e\x51\xa5\xf2\x5d\x21\x61\x07\xe1\x5a\x89\xb6\x84\x60\xdd\xc4\x5a\x10\xaa\xfa\x5e\xed\xec\x17\x52\xb7\xc6\xd6\x14\xa9\xc2\xf0\x3a\x2b\xf2\x1a\xc4\x7a\x1e\x03\xed\xf8\xf4\x11\xe2\xa0\x58\x6e\xb4\x0a\x52\x8f\x8c\xb3\x3b\x19\x0b\x65\xae\x98\x58\xa6\x60\xf7\xad\xca\xbd\xbd\xf6\x7d\x08\xbd\xbd\xf6\xc2\x12\xaf\xb9\xc7\x6a\xe2\x6e\xaf\x6d\x8d\x6d\x01\x37\x34\x11\x0e\x6f\xb1\xc3\xef\xa4\xc9\x44\xd9\xe5\xd9\x0b\x18\x84\xaf\x10\x15\x2f\x24\xcd\xa9\x81\xe6\x34\x3d\x3f\x99\x78\x52\x4a\x84\x9a\x43\xfe\x46\x53\x06\xab\xc7\x9a\x23\xa8\x4b\x51\xbf\xb4\x55\x4c\x40\x6d\xaa\x20\xd4\x88\x71\x95\x84\x18\xd2\x06\x2f\x58\x7e\x87\x41\xc6\xb3\x64\x03\x17\x86\x2f\x04\x2f\xb2\x8b\xff\x08\x9b\xf9\xf2\xb2\x75\x0f\x5f\x80\xdd\xa3\x39\x09\x90\xbe\xa1\xd5\x46\x86\xe8\x7e\x56\x1c\x40\x5a\x7c\xd5\x31\x9a\x2f\x5f\x79\xa4\x50\xf9\x49\xb3\xd7\x7e\xa8\x63\xe6\xdd\xd2\xf5\x7d\xcd\xf3\xe5\x83\x9d\x02\xbf\x6e\x10\x67\xc2\xaa\x70\x86\xd3\x4b\xfc\xf4\x49\x6d\x50\x47\xcd\x86\xdf\x44\xfd\x6b\xd4\xfb\xff\xfe\xdf\x30\x8d\x06\xe8\x1d\xce\xe2\x68\xb4\x82\xb6\x47\x23\x94\x46\xe7\x17\x79\x86\x58\xf9\x70\xe5\xe9\xd3\x27\x47\x38\x8c\xb2\x3c\x8d\xfa\x53\x80\x1f\xc4\x21\x04\xe5\x89\x62\x94\x25\xd3\x74\x80\xe1\x4d\x3f\x8a\x83\xf4\x9a\xb0\x83\x71\xe6\xb1\x28\x0d\x29\xfc\x37\x99\xe6\x68\x0c\x3c\x7d\x00\x9c\xd5\x43\x41\x8a\xd1\x04\xa7\xe3\x28\xcf\x71\x88\x26\x69\x72\x19\x85\x38\xa4\x41\x27\xc8\x3a\x1d\x26\xa3\x51\x72\x15\xc5\xe7\x68\x90\xc4\x61\x44\xd7\x30\xa9\x34\xc6\xf9\x26\x5b\xf1\xcb\x48\x45\x2b\x03\xc5\x30\xc5\x67\x90\x84\x18\x8d\xa7\x59\x4e\x36\xea\x20\x8a\x01\x68\xd0\x4f\x2e\xc9\xa7\xc9\x35\x74\x11\xc5\x49\x1e\x0d\xb0\x47\xe3\x0a\x8d\xa2\x0c\x34\xcb\x72\x7b\x71\xa8\x21\x13\x46\xd9\x60\x14\x44\x63\x9c\xae\xb8\x70\x88\x62\x79\x20\x38\x0e\x93\x34\x09\xa7\x03\x7c\xef\x68\x20\xd6\xb5\x30\x19\x4c\x45\x1c\x0c\x52\x63\x35\x49\x59\x8c\x8c\x71\x90\xe3\x34\x0a\x46\x59\x31\xcc\x30\x37\x50\x4d\x42\x9d\xcc\xf3\xc9\xfe\xc1\x31\x3a\x3e\xdc\x3b\xf9\x79\xfb\x68\x17\x1d\x1c\xa3\x0f\x47\x87\x3f\x1d\xec\xec\xee\xa0\x57\xff\x42\x27\xfb\xbb\xa8\x77\xf8\xe1\x5f\x47\x07\xaf\xf7\x4f\xd0\xfe\xe1\xdb\x9d\xdd\xa3\x63\xb4\xfd\x7e\x07\xf5\x0e\xdf\x9f\x1c\x1d\xbc\xfa\x78\x72\x78\x74\x8c\xbe\xdf\x3e\x46\x07\xc7\xdf\xc3\x87\xed\xf7\xff\x42\xbb\xbf\x7c\x38\xda\x3d\x3e\x46\x87\x47\xe8\xe0\xdd\x87\xb7\x07\xbb\x3b\xe8\xe7\xed\xa3\xa3\xed\xf7\x27\x07\xbb\xc7\x1e\x3a\x78\xdf\x7b\xfb\x71\xe7\xe0\xfd\x6b\x0f\xbd\xfa\x78\x82\xde\x1f\x9e\xa0\xb7\x07\xef\x0e\x4e\x76\x77\xd0\xc9\xa1\x07\x8d\x9a\xd5\xd0\xe1\x1e\x7a\xb7\x7b\xd4\xdb\xdf\x7e\x7f\xb2\xfd\xea\xe0\xed\xc1\xc9\xbf\xa0\xbd\xbd\x83\x93\xf7\xa4\xad\xbd\xc3\x23\xb4\x8d\x3e\x6c\x1f\x9d\x1c\xf4\x3e\xbe\xdd\x3e\x42\x1f\x3e\x1e\x7d\x38\x3c\xde\x45\xa4\x5b\x3b\x07\xc7\xbd\xb7\xdb\x07\xef\x76\x77\x56\xd0\xc1\x7b\xf4\xfe\x10\xed\xfe\xb4\xfb\xfe\x04\x1d\xef\x6f\xbf\x7d\x6b\xed\x25\xc1\x5d\xe9\xe3\xab\x5d\xf4\xf6\x60\xfb\xd5\xdb\x5d\xda\xd2\xfb\x7f\xa1\x9d\x83\xa3\xdd\xde\x09\xe9\x4e\xf1\xab\x77\xb0\xb3\xfb\xfe\x64\xfb\xad\x87\x8e\x3f\xec\xf6\x0e\xc8\x8f\xdd\x5f\x76\xdf\x7d\x78\xbb\x7d\xf4\x2f\x8f\xc1\x3c\xde\xfd\xbf\x1f\x77\xdf\x9f\x1c\x6c\xbf\x45\x3b\xdb\xef\xb6\x5f\xef\x1e\xa3\xda\x9c\x21\xf9\x70\x74\xd8\xfb\x78\xb4\xfb\x8e\xe0\x7c\xb8\x87\x8e\x3f\xbe\x3a\x3e\x39\x38\xf9\x78\xb2\x8b\x5e\x1f\x1e\xee\xc0\x40\x1f\xef\x1e\xfd\x74\xd0\xdb\x3d\x7e\x81\xde\x1e\x1e\xc3\x68\x7d\x3c\xde\xf5\xd0\xce\xf6\xc9\x36\x34\xfc\xe1\xe8\x70\xef\xe0\xe4\xf8\x05\xf9\xfd\xea\xe3\xf1\x01\x0c\xda\xc1\xfb\x93\xdd\xa3\xa3\x8f\x1f\x4e\x0e\x0e\xdf\xd7\xd1\xfe\xe1\xcf\xbb\x3f\xed\x1e\xa1\xde\xf6\xc7\xe3\xdd\x1d\x18\xdd\xc3\xf7\xd0\xd5\x93\xfd\xdd\xc3\xa3\x7f\x11\xa0\x64\x0c\x60\xf0\x3d\xf4\xf3\xfe\xee\xc9\xfe\xee\x11\x19\x50\x18\xa9\x6d\x32\x04\xc7\x27\x47\x07\xbd\x13\xb9\xd8\xe1\x11\x3a\x39\x3c\x3a\x91\xfa\x88\xde\xef\xbe\x7e\x7b\xf0\x7a\xf7\x7d\x6f\x97\x7c\x3d\x24\x50\x7e\x3e\x38\xde\xad\xa3\xed\xa3\x83\x63\x52\xe0\x80\x36\xfb\xf3\xf6\xbf\xd0\xe1\x47\xe8\x32\x99\xa3\x8f\xc7\xbb\xf4\xa7\x44\xb1\x1e\xcc\x24\x3a\xd8\x43\xdb\x3b\x3f\x1d\x10\xb4\x59\xe1\x0f\x87\xc7\xc7\x07\x8c\x4e\x60\xc8\x7a\xfb\x6c\xb8\x57\x9e\x3e\x79\xbe\xaa\xea\xbc\xde\x05\xf9\xc5\xfd\xea\xbd\xaa\x45\x9d\xa6\x81\x8f\x45\x11\xfa\x58\xc9\x3a\x1b\x2e\xec\x82\x38\xcf\x50\x1e\xf4\xb9\xc4\x42\xaa\x7c\xfa\x7d\x64\x0d\xb6\x59\xc8\x51\x0d\x0f\x21\xdf\x43\xa8\xe9\x21\xd4\xf2\x10\x6a\x7b\x08\x75\x3c\x84\xba\x1e\x42\x6b\x1e\x42\xeb\x1e\x42\x1b\x1e\xf2\x1b\x1e\xf2\x7d\x0f\xf9\x4d\x0f\xf9\x2d\x0f\xf9\x6d\x0f\xf9\x1d\xc9\xc2\x72\x8d\xd6\x25\xdf\x08\x3c\x52\x9e\xc0\xf0\x3b\x14\x2e\xa9\x07\x6d\x6d\x30\xf8\x4d\x06\xc3\x87\x36\x0a\x38\x2d\xd6\x56\x9b\xe1\xb2\xc1\x60\xac\x4b\x78\xae\x31\x58\x5d\x86\x8b\x4f\x61\xfa\x72\xac\x65\x9f\xd5\xe5\xb8\x34\x28\x0c\xc0\x83\xe3\xd9\xa2\xb0\x08\x7c\x5f\xee\xb7\x0c\xa7\xcd\xea\x76\x18\xee\x6b\x0c\x46\x53\xc2\xd3\x67\xb0\xd6\x19\x2e\xac\xdf\x7e\xeb\xac\xfe\x42\x9e\x8b\x74\xce\x5c\x70\x3c\xd6\xa4\xb1\x6a\x32\x98\x1c\xe7\xae\x3a\x1e\xd0\xb7\x96\xd6\xf7\x2e\xab\xd3\x2a\x60\x41\xdd\x4e\x81\x33\x87\xc1\xc7\x03\xda\xf2\xb5\xbe\x43\xa1\x8e\xd4\xc1\x35\x86\x60\xb7\x18\x5c\x01\xa4\x29\x0d\x34\x45\xb6\x00\xb4\xce\xea\x48\x83\x05\x13\xd3\x29\x06\x57\xc0\x68\x49\x03\x4d\x91\x95\x10\x6a\xb2\x91\x6d\x48\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xd5\xd9\xc8\xe6\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6e\x89\xd3\x56\x4b\x1a\xcf\x6e\xf1\x4d\xa1\xe9\x35\x0f\x3e\xc1\x50\x71\x7a\xdd\x28\x68\x8f\xd3\x94\xdf\x91\x86\x75\x8d\x95\x55\xe6\xc3\x2f\x88\x40\xcc\xc5\x06\x2b\xc8\x89\x67\x5d\x2a\xc3\x11\x5f\x83\xdf\xf2\x59\x4a\xac\xe5\x76\x51\x95\xb7\x2f\xd6\xbc\xbc\x26\xd6\x15\x90\x05\x28\xbe\x3e\x3b\x05\xed\x8b\x7e\x36\x0b\x14\xc4\x38\x31\x92\xa1\x70\x91\x36\x25\xf3\x16\x08\x43\x4c\x19\xfc\x4e\x81\x00\xf4\x73\xad\x58\x88\xd0\x60\x9b\x21\xd2\xd5\x90\x6e\xa9\x83\x2f\x3a\xed\x17\x70\xc4\xd8\x89\x05\x0d\xdf\x15\x38\x82\x81\xf8\xd2\x20\x75\x8b\x76\xc5\xc2\x63\x0b\xd8\x6f\x59\xe6\x43\x74\x40\x43\x9c\x03\x12\x0b\xae\x29\xfd\xb7\x23\x56\xb1\x3a\x40\x1d\x4b\xb9\xb6\x3a\x33\x62\x26\x8b\x4e\x21\xdf\x47\x67\x4a\x96\xec\x4f\x17\x64\x85\x58\xe6\x03\x89\x50\xcd\x0d\x0f\x35\x66\x9d\xed\xf5\xe6\xda\xc6\xc6\x06\xf9\xdd\xdd\xdd\xd9\xd8\x7d\xb5\xed\x93\xdf\xeb\x7b\xfe\xab\x57\xbd\x9d\x1e\xf9\xbd\xbd\xd1\x69\xed\xed\xb4\x77\xd5\xf9\xbe\x48\x9d\x0d\x74\x1a\xdb\xcd\xf5\x57\xbb\x5d\x68\xa0\xd7\xde\xd9\xf1\x9b\x6d\x68\x60\x67\xad\xd1\xda\xdd\x6b\x91\xdf\x6b\xdb\xdd\x9d\xb5\xee\x2e\x34\xcc\x11\x3a\xb3\xea\x03\x8e\x0e\x3e\xec\xbe\xdb\xf1\xbb\x0d\x08\xbf\x3f\x47\x87\x24\xca\x16\x5a\x24\xe9\x15\xdd\x95\x6f\x7b\x57\x44\x95\x89\x80\x84\x23\x08\x76\x77\xad\xdd\x69\xb6\x1a\x30\x82\xbb\x7b\xbd\x9d\xed\x57\xeb\xd0\xc1\x8d\xf5\x57\xdb\x3b\xbd\xbd\x5d\xf2\xdb\x6f\xb4\x9a\x9d\xf6\x1a\x0c\x4e\xaf\xb5\xd3\xdc\xf5\xf7\x1a\x67\x4e\xd5\x78\x55\xa5\xbc\x55\xb1\x5b\xd9\x4b\xc9\x2f\xb9\xa9\x99\x6f\x8e\x4f\xb1\x00\xdd\x6b\x61\x16\xe9\xb8\xbe\x79\xf7\x49\x2a\xcd\x2f\x0f\x3e\x99\x86\x4c\xa8\xec\x4e\x45\xaa\x87\xb6\x50\xcd\x2c\x80\xa8\x01\xa8\xd4\x58\x61\xf8\x20\xbd\x5c\xcc\xa8\xd4\x00\xc8\xec\x4a\x35\x80\xa6\x75\xa9\x09\xae\x44\x35\x86\xe6\xd9\x3a\xef\x23\x71\xff\x40\x48\xd1\x79\xe5\x08\x0c\xe0\xd3\xc5\xc8\x5d\x20\x85\x02\xa9\xb3\x00\x88\x9f\x9f\x7e\x77\x43\x00\x99\xe8\xd3\xef\x6e\x08\xb0\x4d\x7f\xca\xdc\x10\x60\xd3\xf8\x94\xa5\xf6\x88\xd6\xab\xab\x64\x95\x7d\x26\x87\xe6\xcb\x20\x8d\x88\x74\x6c\xb9\xa4\x0d\x46\x1e\xea\x8f\x3c\x34\x18\x79\x28\x1c\x79\x08\x8f\x2c\x0d\x05\xa9\x87\xfa\xa9\x87\x06\xa9\x87\xc2\xd4\x43\x38\xd5\x1b\x0b\x08\x2a\x01\x41\x78\xdf\x74\x19\xe9\xa7\x10\x74\x1c\x3e\xfa\xfa\xc7\x01\xf9\x38\xa0\x1f\x9b\xfa\xc7\x90\x7c\x0c\xe9\xc7\x96\xfe\x11\x0e\x0c\x98\x7e\x6c\xeb\x1f\x45\x9a\xea\x40\xcd\x4b\xcd\xbb\xa4\xdf\x0a\x5a\x4d\x09\xe1\xbf\x4b\x5b\xc8\xb7\xae\xed\x9c\x2c\x9f\x60\x84\x96\x8a\x35\xb5\xf4\xfb\xe8\x34\x3a\x3b\xab\x7f\xb1\x39\x31\x80\xd7\xce\x4b\xbf\x5b\xff\xe3\xe9\x13\x95\x35\x92\x36\xd0\xd0\xaf\xf5\x47\xde\x60\xe4\x85\xa3\x3a\x5a\x42\x17\x23\xbb\xef\xcd\x0d\x12\x0a\xb9\xe8\x65\xab\x49\x55\x6d\x16\x68\x4d\x1d\x9a\x31\xf2\x06\xb4\xf6\xba\x13\x5a\x4b\x87\x66\x4c\x95\x01\xad\xdb\x76\x42\x6b\xeb\xd0\x8c\xb9\x95\xa0\xfd\xb1\xba\xca\x20\xae\x37\x9c\x10\x3b\x3a\x44\x83\x20\x90\x3d\x4c\x3a\x99\xc4\xdc\x3a\x5d\xe4\x0b\x4a\x93\x7c\x54\xcb\xbd\x8c\x4c\xab\xcd\x69\x03\x68\x20\x5f\xc2\x23\xfb\x94\xc3\x8a\x30\x96\x14\xf9\x03\xba\x0d\x6d\x5f\x80\xdc\xa1\x5d\xb2\x26\x7d\xab\x1b\x10\xac\x97\xbe\xad\x36\x2c\x33\xe3\x26\x51\xa0\x1a\xa4\x68\x49\xa2\xd6\xf4\xf6\xd4\xda\xa9\xf5\x53\x6f\x90\x7a\x61\x0a\x23\x9e\xde\x8d\x5a\xdb\x3a\xb4\xbb\x52\xab\x0a\xed\x4e\xd4\xda\xd4\xa1\xdd\x99\x5a\x7d\x1d\xe2\x3d\x53\x6b\x0a\xb7\xd6\x25\xe4\x9a\x3a\xc8\x15\x38\x6a\x6a\x23\x57\x60\xc4\xb6\x2f\xc0\xa2\x29\xb9\xa6\x4e\x72\x85\x0d\xc0\x56\x1b\xb6\x06\xd3\x42\x43\x67\xe5\x07\x72\x3a\x06\x90\x21\xc1\xea\x57\x93\x30\xc9\x3f\x5b\xa8\xb6\x4f\x4d\x73\x07\x84\x33\x87\x96\x9e\xee\x33\x13\xde\x7d\x6a\x7e\x1b\x92\x72\xb6\x11\xd9\x67\x66\xba\xfb\xd4\x90\x16\x93\x72\x81\xb5\x5c\x8b\x95\x03\x63\x59\xd8\x11\xfa\xd6\x72\x6d\x56\x0e\x0c\x93\xfb\xa4\xdc\xc0\x5a\x0e\x0c\x98\x95\x61\xd1\xc5\xda\x3d\x96\x5a\xe3\x0e\xe6\x59\x61\x90\x07\x42\x18\x22\x0f\x96\x8d\x7f\x7e\x1a\x46\x5e\x32\x7e\x15\xe5\xd9\x49\x92\x03\xc7\xa3\x30\xe3\x9d\x20\x0f\xa8\xd5\xd6\x73\xb4\x6e\x81\x0e\x75\xde\xe2\x61\x6e\x24\x6d\x84\xf2\x46\x67\xb6\xc3\xd0\xcc\x42\x8c\x58\xbe\x45\x6a\xcc\x54\x80\x24\xd2\x64\xe7\x0c\x7d\xd9\xa2\x89\x85\x0b\x1b\x09\x51\xe2\x1f\xa8\xd5\xd4\xa9\xb5\x80\x54\xab\xd5\x8a\xa2\x4b\x88\xf0\x07\x02\x72\xa3\x4e\x40\xb5\xc9\xba\xf5\xdb\x0e\x01\x9a\x57\xa5\xc3\x51\x08\xcf\xd2\xcb\xea\xc2\xb3\x01\x8c\x09\xce\x1a\xb0\x79\x82\xb3\xad\xa3\x72\x9e\x8e\x22\x1f\x26\xcf\xb1\x03\xc6\x31\x96\xb4\x1d\xab\xab\x70\x12\x44\x90\xdd\x85\x3a\x64\x59\x0d\xa7\x26\xf4\xe4\x65\x66\x73\x29\x27\x4b\x58\xdd\xb2\x8c\x6e\x21\x9c\x7d\xb4\x85\x64\xf1\xfd\x6e\xe7\xb7\x4e\xa5\xe3\x9b\xfd\x44\xb6\x0f\x47\xb1\x7d\x8b\x33\x09\x2a\x3b\x83\xed\x0b\x77\xbd\x7d\xe5\x78\xb5\xbf\xf0\xb9\x8a\x52\xc8\xbe\x72\xa6\xda\x77\x1e\xa6\xe6\x9b\xc2\x1d\xd1\x9b\x70\x3a\xb9\x2c\x83\x45\x08\x83\xad\x16\x65\x37\xe6\xda\x04\x29\x6c\x6a\x30\x4a\xe2\x72\x06\x05\xa6\x04\xa4\x54\xa1\x5d\x80\x47\xb7\x19\x04\xfd\xfc\xc9\x20\x12\x5a\xcf\xa4\x35\x86\x26\x7c\x55\xec\xa2\xe0\xe7\x0d\xbd\xfd\x47\xb2\x45\xdc\xd0\xaf\xcd\x3c\x74\xed\xa1\xdf\x6d\x69\x3e\x6a\xb5\x19\x78\x76\x5e\xc3\xbf\xbf\x17\xd9\xda\x6f\x0c\x38\xcd\x72\x38\xb5\x59\xfd\x87\xda\x75\x9d\xba\x93\xff\x2f\x79\xf8\xbd\x5e\xaf\xbf\x70\x41\x6b\xcd\x85\x46\x00\xfd\x2f\x81\x58\xa0\xe6\x80\xd5\x9e\x0f\xeb\x07\x80\x00\xb8\x5d\xd7\x7f\xa8\xfd\x2f\x20\xe7\x86\xd8\xa9\x32\x66\x64\xd0\xbe\x14\xa0\x1c\xb0\x40\x94\x98\x79\xb1\x15\xd2\xec\xe5\xcb\x18\xb0\x9a\xfd\xf8\xe3\x8f\xb5\x56\x73\x39\x96\x91\xa2\x3f\x4a\xad\x61\xb8\x31\x0c\xcd\x03\x57\xcd\x18\xc6\x99\xed\x87\xd9\xb7\x80\xcd\x13\xff\x9d\x27\x94\x33\x99\x60\x1c\xf9\x79\x1c\xa5\x6f\x9b\x98\x87\xad\x8c\xc2\x92\x85\x2b\xf0\x6a\xcf\x18\x8a\xcf\x2c\x56\x38\xee\x5a\x57\x1c\x5b\x9b\xb9\x8d\xa9\x1c\xd4\x4c\x6d\x78\x81\x6a\xa6\x4a\x7c\x72\xf6\xdf\x6d\xf7\xbe\xc2\xd4\x94\x54\xff\x8c\xaf\xa1\x6a\x86\x07\x29\xce\x1d\xb9\x93\x1c\x13\x0a\x29\x07\xef\x71\x42\x69\x22\x43\x31\x35\xfb\xe3\x60\x50\x4c\x8f\x6c\x62\x65\x99\x21\xa5\xb0\x39\x4b\xe3\x60\x60\x99\xa9\x27\x37\xf4\x1e\xd8\x61\x1a\xc5\x4b\xda\xb3\x13\xdd\x9c\x79\x6b\x6b\x8f\x26\x4e\x7f\x07\x97\x95\x87\xbe\xba\xd7\x02\xab\x49\x0d\x3b\x43\xa6\x1d\xef\x6f\x2f\xfb\x15\x6e\x32\xcc\x5c\xd5\xf7\x79\x7f\xb1\x05\xde\xa7\xc5\x15\x46\x14\x47\x79\xcd\x12\x80\x4a\xbd\xd2\xc0\xc3\x41\x18\xf4\xd7\x37\x2c\xb1\x99\x1a\xb3\x8d\xf5\x7e\x10\x0e\x86\x58\xb9\xe3\xb0\x15\x1c\xb4\xc2\x26\xf6\x87\x0d\xf5\xdb\xdd\xaf\x40\x5c\x12\xba\x5d\xf8\x36\x35\xe8\x06\x80\x2a\xba\x67\xbb\xba\x98\x7c\xea\xdb\x95\xc5\x20\x30\xda\x55\xc5\x70\x5c\xb5\x2b\x8a\xc9\x27\x2c\xd4\xc4\x06\xa6\x4e\x3d\xb1\x53\x27\xec\x38\x2d\x80\xde\x07\x51\x0f\x53\x47\x2c\x98\x9f\xa9\xe0\xaf\x86\xc0\x50\x7d\x4f\xf9\x1f\x57\x28\xd9\x01\x71\x3f\x87\x9f\x4f\x23\xb4\x8c\x5a\x67\xe8\x57\xf6\x73\xbd\xf8\xe9\xb7\xa5\xdf\x5d\x57\xee\x48\x86\x52\x2d\x06\xe7\x58\x7a\xb6\x84\xe3\x43\xcb\xb7\x87\xa9\xb1\x9f\x84\x40\xb5\x54\x0b\x08\x90\x0e\x00\x09\xe8\x49\x66\x0d\x1c\x64\x31\x5a\x82\x86\x5c\x8a\x46\xf4\x12\x35\x1b\xce\x51\x03\xb5\x59\xad\xd6\x47\x3f\xa0\x01\x95\x73\xc9\xcf\x10\x20\x37\x66\x9d\x80\xde\xc2\xce\x51\xf1\xa1\x97\xa8\x3d\xaf\x89\x3e\xfa\x15\x0d\xd0\xaf\x28\xa4\x90\xbb\x38\xdc\xc0\xfd\xc0\x16\x74\x48\x83\xdc\x5d\x00\x79\x8a\x3b\xf9\x35\x60\xbd\x58\x46\x8d\xd9\x5a\x03\xb7\xdb\xad\x66\xdb\xdd\xd6\xea\x73\xd1\xdc\x7a\xa3\x8e\x9e\xaf\x56\xee\x0b\x81\xdf\xea\x6c\x84\x2d\xdc\xd4\xb5\x3c\xc8\x31\xa5\x64\xbd\x84\x36\x75\x1f\xda\x42\x03\x9b\x8a\x0f\x41\x93\x2f\x5f\xa2\x56\x83\xf5\x12\xa6\xdf\x9a\x5b\x14\x6d\x21\x1b\x1e\x41\x35\x6f\xad\x4a\xca\x40\xa6\x44\xe3\xca\xb6\x40\xf7\xf0\x46\x8a\x22\x10\x14\x86\x46\xe4\x13\xa4\x28\x01\x41\x59\x38\xb0\x97\x69\xc9\x8a\xc2\xd0\x5e\xa6\x2d\x2b\x09\xb1\x5e\xe6\x51\xc1\xf7\xad\x2a\xf8\x88\x2c\xbc\x32\x1c\x25\x49\x2a\xeb\xdc\x56\x61\xa3\x66\x7f\x77\x6a\x04\x62\x21\x14\x90\xe7\xe8\xe9\x0c\x35\xdd\x03\x69\xe8\x16\xd4\x03\x59\xd5\x75\x7f\x45\x6d\xd0\xa3\x0a\xc1\x50\x06\x10\xf1\x79\x21\xed\x01\x54\x28\x53\x1c\xa8\x02\xb9\xaa\x33\x20\xdf\x1e\xd5\x05\xf7\xaa\x2e\x80\xf9\xa8\xa0\x29\xb0\x4f\x4b\xa1\x24\x60\x53\xe3\x76\x9b\x22\x05\xdc\x6a\x81\xf5\xbf\x74\x80\x8d\xec\x22\x68\x76\xba\x0f\x1d\x1b\x83\xb5\xf2\x9f\xa3\x3e\x30\xd4\x03\xf2\x19\xbe\xd9\xe9\x2a\xa7\x78\xc9\x0b\x5b\xd7\x0a\x34\x9b\xed\x6a\x7a\x01\x52\x50\x81\x09\xcf\x14\xf8\x57\xd5\x0d\x0c\xfc\x46\x67\x03\x87\xeb\xe4\xc8\xdf\xea\xae\x0d\xc2\x4e\x63\x0d\x7e\x37\xd6\x1a\x61\xe8\xc3\xef\xe1\x5a\x03\x77\x36\x5a\x76\x9d\xc1\x70\x38\x68\x34\xfa\x2d\x50\x2e\x74\xd7\x3b\xeb\x7e\xc7\xa7\xbf\xdb\xc3\x8d\xf5\x61\x00\x00\xfa\x78\x18\xb4\x87\x41\x7b\x01\x75\x41\x25\xc9\x53\x62\xfb\x6c\xe8\xa4\x9a\x25\x5e\xb4\xc0\x51\x85\x38\xb3\xbc\x65\x0a\x2f\x8e\x8b\xa5\xc7\x2d\x7a\xce\x8e\xdb\x6c\xb6\x17\xdd\xa4\x49\x95\x39\xdb\xb4\xb2\x3a\x8c\x8d\xba\xd9\xb4\x3b\xb1\x3f\x6e\xd5\x77\xd8\xaa\xc9\xac\x54\xdb\xac\xad\x93\xa3\x6c\xd7\x74\x82\x4a\x37\xec\x66\x53\x77\x75\x96\xfc\x9a\xd9\x76\xb4\xb9\xb6\x41\x36\xf0\x8d\x47\xbd\xfe\x9f\xb3\x31\xff\xf5\xdc\xf2\x0e\x68\x12\x87\xe8\x77\xe1\x95\x8b\xd2\x64\x1a\x87\x68\xa0\xfa\xeb\x49\x3d\xd8\xd7\x53\xa7\xbc\x51\xaf\x01\xb8\xa2\x16\x17\x30\xe8\x17\x9b\x04\x83\xe4\x2b\xe5\x28\xfb\x90\x46\x63\x5c\x8b\xad\xdb\x58\xf6\xef\x34\x7f\xcf\xcf\xf9\xe4\xa1\x16\xeb\xe7\x4c\xa1\x08\xa6\xd3\x89\xb6\x50\xf3\x05\xff\xfd\x72\x8b\x42\xe0\x2f\x4a\x74\xc3\xdf\xfd\xff\xec\xbd\xff\x7a\xdb\x36\xb6\x28\xfa\xf7\xf4\x29\x56\xf6\x37\x8d\xa5\x98\x96\x09\x92\x92\x28\x27\x4e\x4e\x9a\x38\x6d\xce\x38\x4e\x4e\xe2\x4e\xbb\x8f\xeb\x74\xf3\x07\x64\xb1\x91\x48\x85\xa4\x6d\x79\x26\x99\xef\xbe\xc6\x7d\x8c\xfb\x0a\xf7\x51\xee\x93\xdc\x0f\x0b\x20\x09\x92\x00\x29\x3b\xe9\xec\x3d\xfb\x54\xfd\xea\x48\x24\xb0\xb0\xb0\x7e\x61\x61\x01\x58\x18\xc4\xf0\xad\x28\x36\xd4\xc6\x0b\x85\x8e\xce\xbd\x65\x46\xfb\x77\x05\x36\xe3\x63\xc5\x7c\x3c\xbd\xac\xcf\x70\x15\x64\xb9\xa0\xf9\x8b\xd4\xc3\xef\xde\xf2\xbb\x28\xcf\x14\x04\x2a\x97\xf0\x63\xd8\x83\x41\x8c\x99\x3d\x87\xf0\xa0\x16\xfc\x68\x46\xb2\xa4\xb6\x8a\x28\xb5\x9c\x99\x1d\x9f\x21\x43\x1a\xf9\x7b\xae\x17\xd1\x92\xc2\x40\xbc\x7b\x04\x62\x4b\x66\x93\x8a\x15\x37\xb5\x84\x2e\x41\xb8\x5a\x2a\xff\x70\xc6\x0b\x61\xda\xd1\x16\x21\x50\x16\xd6\xc9\xf5\x20\x36\x80\xc0\x3e\x58\xc3\x2d\x32\xb6\x03\xde\x84\x72\x1b\xb0\xf6\x50\x99\x3c\x9b\x83\xd8\xdd\xed\x09\x85\xc6\xb5\x12\x85\x87\x34\xa8\x60\xde\x7d\x8d\x8d\x39\xde\xdb\x79\xd3\x6d\x0f\xfd\x77\x5f\x69\xfb\x61\x94\x2d\xa3\x80\x0e\xcc\xe1\x1f\xab\x5e\x5b\xaf\x7a\xb5\x5e\xcd\xf1\xd5\x58\xf5\xea\x02\x5f\xb5\x16\x8c\xd0\x67\xc1\x57\xd3\x2f\x5e\x46\x9b\x74\xe4\xba\xff\x67\x2f\xa3\x5d\x78\xab\x95\x67\x6e\xca\xc5\x34\xd2\x22\x4a\xbb\x34\x6e\x34\x1e\x14\x35\x1f\x3d\x02\x8b\x2f\x7a\x15\x4f\x1e\x3f\x7e\x0c\xd3\xe1\x10\xe0\xbd\x1a\x52\xfd\x53\x83\x44\x9c\x16\x24\xe2\x0e\x87\xdb\x41\xaa\xd7\xb3\x95\xe6\xa5\xd6\x13\x52\xf5\x5b\xb9\x49\xbe\x5e\x58\xea\x36\xe1\xc8\x4a\xdd\x26\x9b\x22\xdf\xf4\x96\xc8\xd6\x21\xd9\x6d\x48\xb3\x5b\x76\xbb\xa8\xa7\xbe\x93\x00\x2a\xc1\x11\x4c\xdc\x15\x3d\xc7\x24\xbf\xa2\x87\xbb\x9d\x0b\xa6\xba\xd5\xcf\x00\x4f\x35\x0e\x28\xdc\x87\x39\x6e\x76\xfb\x07\xfb\x7a\xa1\xbb\xc2\x65\xe5\x61\x86\x39\x0f\xee\x83\x8f\xc5\x3d\xbe\x3a\xf8\x1e\xc4\x3a\xa1\x0a\x7f\x74\x56\xa2\x0b\x86\x78\xb9\xd4\x2a\x16\xdb\xc4\x5a\x2b\xdf\xfa\xc7\xdf\x90\x99\xf4\x86\xd8\xb5\x57\xb5\x4a\xea\xb1\xad\x6c\x0c\xef\xa9\x19\x50\x94\x71\x9e\x39\x99\x62\xbd\x89\x80\xc8\xdf\x10\xe9\x0d\x21\xf2\xab\x29\xdf\xd9\xca\x5f\x59\x63\xf5\x88\x87\x0b\xc8\xac\xa5\x05\xec\x16\xcd\xee\x32\xa2\xee\xf2\x8b\xde\xb4\x8b\xc7\x58\xd1\x82\xc3\x82\x30\xbb\x8c\xb4\xaa\x16\x98\xe1\xba\x50\x00\x60\xb6\xae\x99\xa7\x9d\x7d\x98\x79\x54\xb9\x5f\x98\x3b\x13\x6f\x4b\x20\xaa\x65\x3e\xe8\x59\x22\x6d\x66\x5b\x87\x9e\xe5\xd0\x41\xce\x08\x91\x5b\xaa\xb6\xfe\x4f\x59\x1a\xe5\x65\xc6\xa2\x0c\xa6\x0c\x9f\xab\xcb\x4c\x44\x19\x4c\x09\x7e\xa1\x2e\x33\x15\x65\x50\xe7\x17\x7f\x2c\xc3\xfe\xb1\x0c\xfb\xc7\x32\x6c\xdb\xdb\xfc\x63\x19\xf6\xbf\x64\x8c\x77\x3c\xb9\x75\x8c\x77\x3c\xe9\x8d\xf1\xca\x73\xb6\x76\x8c\x77\x3c\xf9\x23\xc6\xfb\xd5\x63\xbc\xe3\xc9\xb6\x31\x5e\x15\x73\xea\x31\x5e\x64\x50\xf7\xa6\xed\x72\xed\x4c\xbd\x34\xeb\x9a\xff\xd2\x4b\xb3\x9b\x89\xf3\x4f\xb9\xb8\xa0\x6c\xe7\x8f\x28\x70\x3d\x0a\xbc\x99\xe0\x9a\xea\x68\x33\x71\xa4\xe7\x3f\x4f\x1c\x91\xa5\x1b\x4b\x8c\xa4\x3c\xd1\xb7\xca\xe9\x26\xf5\xef\xed\x0f\xaf\x7f\x7d\xfd\xe2\xc5\xbb\xa3\xd3\x77\xcd\x68\xf1\x9b\x97\xbf\xbe\x3c\x79\x7e\xf4\xf3\x51\xfb\x56\xee\xb7\xaf\x7f\x3c\x79\xfe\xeb\xb3\xd7\x27\xef\x4e\x9f\x9e\x94\x35\xa5\xe6\x78\x58\xf9\xd9\x76\x61\x65\xa9\x46\xba\x48\x8a\xa4\x2d\x8d\x98\x74\xd1\x34\x9b\x5d\x13\x03\x6e\x74\xa9\xca\x73\x1e\x12\xc9\xe1\x11\x58\xce\x43\xc8\x15\x21\x11\xa9\xcf\x67\x1b\xd8\x85\x31\x3c\x80\x1b\x7e\x7a\x30\x2f\x0e\x69\xe2\x37\x6b\x88\x91\x4a\xf8\x16\x26\x2d\x5f\x04\xdd\x40\x7a\xfd\x33\x1c\xc2\x0d\x7c\x0b\x63\x95\x97\x48\xaf\xff\x9d\x41\xb5\xe0\x01\xb0\x76\x6c\xd6\xce\x50\x51\x78\xc3\xc3\x72\x3f\x37\x1e\xdf\xf0\xc7\xff\xae\x09\x05\x4b\x64\x5b\x47\x10\xe1\x75\x02\x0a\xa2\x95\x94\xd9\x70\xca\x6c\xf8\x01\xcd\x8d\x82\x30\x65\x51\x4e\x5d\xb8\xe1\x45\x6f\x34\x61\xa5\x4a\x40\xea\x64\xbc\xc1\x0b\x7e\xda\xbd\x66\x74\x6d\x76\xfd\x73\x6f\xdf\x1a\xab\x1c\x75\x69\x38\x7e\xf1\xee\x2d\xc3\x75\x63\x12\x95\x30\xc8\xf7\x4e\x68\xe2\x63\xac\x18\x36\x51\x08\xeb\xab\xec\xba\x21\x5b\xca\x62\xc7\x45\x31\x0d\x09\xc5\xcd\x13\xbf\xc1\x23\x98\x3e\x84\xdf\x3a\x22\x73\xd8\x07\x3c\x9a\xaa\xce\x8a\x52\x34\xef\x47\xf9\x9b\x24\xc3\x3c\xae\x4c\xaa\xf0\xb2\xdc\xdf\x86\xb0\x07\xaa\xdd\xd4\x05\x70\xb9\xd2\x23\x10\xf9\x22\x54\x85\xd9\xa7\xd5\xc1\xf7\x87\x80\xcd\x48\x50\x34\x6d\xd5\x77\x54\xcb\xad\x3e\x3e\xc4\x66\xf5\x9b\xab\x5b\x2d\xbf\x92\x5a\xae\x81\xda\x53\xcc\x7b\x4a\x04\xb6\x0b\x2d\x49\x82\x15\xd3\x4d\x8e\x02\xd4\xc3\x16\x57\xbf\x13\x7d\x7f\x1f\xde\xa4\xd1\x2a\xca\xa3\x2b\x0a\xeb\x64\x79\x13\x27\xab\xc8\x5b\x42\x72\x45\x53\xf8\xfe\xc5\xc0\x1a\x1e\xc0\xe6\xbd\x0b\xbb\xb0\x79\x3f\xc1\xbf\x63\xfc\xeb\x30\x33\xa3\x06\x29\x24\x9a\x37\xcf\xcf\x0f\xbc\x07\x73\x33\xed\xd8\x32\xaf\x41\x4e\x40\x38\x54\xca\x47\xcf\xa2\x57\xc3\xc0\xf3\x18\x9f\x18\x7e\x8a\x04\x63\x4d\x9e\x19\x2d\xf9\x19\xde\x76\x35\x25\x43\xfd\xc9\xe9\x6a\x9d\xa4\x5e\x7a\x53\xbb\x89\x8e\xa9\xc0\xa9\x3c\x10\x69\x57\x29\x95\xb7\xce\xa8\xb5\xff\x54\xd9\xb3\x3e\xbc\x1b\x6b\x3b\xf6\x76\x2b\x3b\x76\x6d\x5d\xc7\xee\x5a\xd5\xf9\xfa\x57\x09\x24\x97\xf9\xfa\x32\x3f\xc6\xa9\x75\xad\x2c\xa0\x93\x1e\xd2\x2c\x4a\x69\x28\x5d\x34\xe0\x47\x79\x56\x24\x84\xe6\x95\x6b\xb3\x85\xa2\xf2\xeb\x78\x59\xb0\x49\xca\xc1\xed\xa5\xf4\x00\x2c\xcb\x31\xc0\x1a\x4f\x0c\xb0\x5d\xc7\x80\x31\xb1\x9a\x95\xc5\x9d\x05\x07\xec\x9d\xfc\xaa\x79\x69\x41\x31\x69\xd6\xde\x5b\x20\xf7\xae\x01\xed\x0e\xf7\x17\x60\xa4\x16\x6f\x42\x2c\xe6\xde\xc5\xaf\xb3\x73\x8d\xb5\xdf\x42\xd4\xd8\x07\xe1\x70\x91\x8b\xe9\x75\x29\x76\xb8\x08\xd7\x97\x4a\x00\x31\x29\x6f\xeb\xc5\x11\x60\x62\x9a\xb0\x07\x6c\xa0\x2d\x6f\x4a\x90\x29\xc1\xbc\x17\xdb\xfa\xbd\x56\xf4\x14\x81\x39\x05\xd1\x94\xc1\xb3\xa2\x13\xc7\x5e\x8c\xb1\x9f\x46\xd7\xf6\xc1\x52\xc5\xd0\xfc\x2c\x49\xfd\x7e\xfa\x37\xc0\x7f\x49\x26\xc1\x57\x56\x04\xf5\x45\x31\x46\x6b\x6d\xd8\xfc\x95\x85\x77\xd0\x37\x8b\x33\x5b\xdf\x95\xcc\x42\x7b\x05\x35\x6b\xbe\x33\x9f\xa0\x55\x4b\x24\x68\xdd\x25\x83\xa0\x55\x4b\x1d\x68\xdd\x3d\x67\xa0\x40\x98\xf4\x61\x4c\xea\x28\x93\x3b\xe1\x4c\xea\x48\x93\xdb\x60\xad\xe4\x03\x17\xae\x32\x34\x12\xc5\x79\xc2\xa5\x59\xcd\xe9\xa5\x87\xc1\xbc\x42\x9d\x15\xa4\x60\x25\x46\x78\xdf\xec\xfb\x43\xa4\x8b\xae\xcc\x32\xb9\x06\x51\xa6\x7f\x35\xe2\x2d\x1b\x60\x33\x8d\x0e\x70\x47\x19\xf5\x80\x7f\xe5\x4e\x2f\x7e\xd7\xab\xc0\xe9\x82\xe6\x5e\xfb\xcd\x2d\x66\x0d\x12\xb0\x57\x11\x9b\x82\x2c\x2f\x57\x31\x76\x4e\xa1\x56\x05\x05\x0b\x37\xdb\x80\xca\x93\x56\x16\xbe\xe5\x9c\x44\x6e\xa3\xc6\xa5\x6a\x86\xa2\x69\x88\x7d\x0a\xd7\xb3\xe4\x5e\x57\xd9\x63\xa9\xec\x32\xb9\xd6\xfa\xa5\x5a\x6a\x9d\x2a\xfd\x1c\x55\x4f\x4e\x19\x17\x4e\xcf\x36\x3a\xdc\x4f\x37\x5c\xd6\x0e\xb1\x07\xfa\x42\x28\x6c\x87\x88\xfa\x76\xbb\x6f\xee\x26\x06\x1d\x66\xb5\xea\x91\x83\x5d\x1a\x30\xbe\x38\x38\x3d\xec\x5a\x2c\x3f\xdd\x90\xaa\x38\xd9\xa6\x38\x97\xaf\xd3\x0d\xe9\xe2\xa3\x28\x7b\x5c\x96\x45\x3e\x76\x8a\x77\x76\x99\xa2\x46\xf1\xeb\x44\x98\xa8\xf7\x4b\xf9\xe9\xc6\x11\xb6\x00\x06\x03\x81\x5b\x79\x34\x58\xb4\x2f\xce\x07\xeb\xa6\x37\x08\xed\xb8\x84\xc6\xad\x06\x87\x76\xdc\x80\xf6\xaa\x1f\xda\x3f\x55\xa9\x6a\xa6\xb0\x43\x3e\xa1\x69\x12\x35\x62\x0a\xb7\x9a\xed\xbd\x5d\x24\xf0\x26\xea\x90\x6c\xd6\x64\x71\xe7\x23\x79\x28\xfd\xe4\xae\x5c\xf9\xfb\x8b\x45\xbe\x46\xb9\x12\x6c\x97\x18\xb3\x42\x5c\x82\xfa\x0c\x52\x51\xfa\xb8\x2a\xad\x37\x49\x38\x58\x2c\x92\xd7\xdc\x4b\x39\xac\xc5\xc3\x64\xbc\xb4\x9d\x7d\x9b\xa0\xa3\xd7\x61\xe2\xd9\x04\xba\x6a\xa2\x37\xf0\x20\xe9\xca\xa0\xe8\xf4\xa3\x47\x15\x92\x28\xda\x45\xff\xf0\x2a\x4d\xdb\x82\x3d\xe9\xbd\x4e\xd0\xa1\xae\x3a\x25\x0c\x25\xf0\x57\xb7\x04\x5e\x8f\x79\x54\xdd\xdd\x2a\xe2\xd1\xec\xb2\xc0\x4a\x02\x83\xd1\x8e\x36\x72\x13\xe7\xce\x3d\x7f\xd5\xd3\xc6\xf1\x2d\xdb\xe8\x1a\xdb\x52\x2f\xce\xd6\x49\xd6\x29\x25\x68\x7e\xdf\x44\xc7\x5c\x31\x4e\xcf\xa4\x80\x62\x25\x87\xda\x31\x8f\x57\xdc\x66\xe0\x13\x25\xfb\x46\x3f\xad\xfd\x58\x47\xe0\xe5\x38\x04\xa2\xbd\x54\xfb\x84\xa7\x26\xf6\x41\x99\xb4\xb5\x9c\x1c\x99\xa5\x01\x50\x96\x3b\x35\x8b\xee\xf0\xd2\x3a\x95\x3f\x35\x8b\xce\x88\x72\x9a\x71\x6b\x7f\x1f\x9e\x2d\xba\x8c\xdf\xf6\xc3\xfa\x1d\x87\x8c\x7e\xd3\x08\x92\xf9\x2a\xec\x70\x39\xae\xf4\x08\xf7\xed\x4c\x6a\x51\xeb\xb4\x14\xb8\xed\xab\x6c\x48\x59\x69\x20\x39\x21\xc3\x6d\x06\x40\x0e\xc0\x6a\x00\xb0\x5a\x00\x3a\xa9\xc8\x7c\x8f\x34\xb9\xee\x20\xe2\x52\xd2\x86\xd3\x4a\x35\xde\xc3\xe0\x1f\x02\x7d\xfe\xe0\x7e\x81\x0c\xfe\xec\xb2\x1f\x4b\x49\x6b\x4e\x2b\x15\x92\x21\xe2\x83\x0a\xe2\x32\xb9\xfe\xf2\x00\xed\xcb\x44\x35\x23\x69\xf1\x5b\xab\x69\xb5\x30\x24\x1b\xdf\x1a\xc1\x4c\x7c\xdf\x3b\x69\xab\x41\xd1\x29\x62\xcd\x5f\xa9\xd7\x60\x2a\xd9\xb1\xd8\xf1\x5f\x6b\x5b\x94\x22\x48\xf3\xd5\x77\x45\xb5\xca\x97\x11\x1f\x56\xaf\x1d\x06\x7a\x80\xc1\xab\x76\x1c\xe8\xae\x7b\xa9\xc8\x5d\xb6\x52\xe1\x26\xa9\x80\x46\xcb\xfa\x7e\x27\x32\x84\xfd\x3a\xfe\x43\x78\xd0\x7c\x80\x8d\xe3\x02\x4d\xb9\x9b\xeb\xbf\xc8\x26\xa8\x2f\x8e\xe1\xc9\x61\xc6\x02\x79\x65\x0c\x12\xf6\x95\xac\x97\x8b\x14\x51\xc0\x36\xcc\x7d\xe5\x66\xba\x77\x1f\x2f\x29\xfd\x1b\x6d\x03\x5d\x78\xd9\xa2\x10\xee\xad\xee\xa2\x6f\x61\xf1\x25\xc1\xc2\xfe\x98\xd0\xf6\x2e\xbd\xce\x9d\xbf\x7d\x0c\xb1\x6a\x4f\x1f\x95\x93\x5c\x43\x11\x98\x93\x1d\xce\x5b\xc5\xe6\x24\x50\x22\x3c\x27\x83\xba\x6b\x5c\xb1\x22\x45\x77\x27\x8e\x5b\x9d\x38\xbe\x6b\x27\x8e\x5b\x9d\x38\xbe\x5d\x27\xd4\xac\xe2\xa2\x2b\x94\x2c\x4f\x20\xa5\x79\x1a\xd1\x2b\xaa\xd8\x80\x08\xe2\x70\x37\xb7\x07\xeb\xcb\x6c\x51\xa0\xa1\x22\x91\xa2\xe4\xab\x76\xc9\x2f\x4f\x4f\xac\x38\x3d\x54\x36\x6d\xb4\x55\x58\x7b\x9e\xe8\x2b\xed\x9a\xd4\xdb\x2f\xb1\x85\x52\x61\xce\xca\xc3\x4e\x5b\x58\x88\x2d\x17\x73\x8a\xaf\xd5\xfe\xcc\x4e\xb2\xff\xb1\x5d\xf3\x8e\xdb\x35\xed\xdb\x6e\xd6\xb4\xfb\xb6\x6a\xda\x1d\x1b\x35\xed\x3f\xb6\x69\x7e\xed\x6d\x9a\xf6\x96\x9b\x34\x15\x6c\xa9\x6d\xd1\xb4\xb7\xd9\xa0\x69\xeb\x8f\xe1\x97\x1b\x0f\x0f\x5c\xe7\xf3\xb9\xe1\x92\xff\x26\xdb\x35\x9b\x09\x76\xc6\xc4\xfa\xa7\xed\xe1\x2c\xd2\xed\xb0\x36\xff\xb5\xd2\xed\xdc\x69\xb7\xa5\x78\x5d\xed\xf6\x2c\xca\xdc\x2a\x21\xcf\x98\x58\xb5\x6d\x21\x63\x62\x69\xb7\x99\xb8\x5b\x26\xe4\x61\x05\x6b\x5b\x4d\x5c\x91\xd5\x62\x4c\xac\xaf\x76\x84\x58\xee\xbe\x36\x27\x4f\x6b\x93\x83\xb9\x09\x7c\xdf\x9f\x85\xe3\xd0\x90\x12\xf6\x0c\x0d\x55\xc9\x89\x35\xf3\xac\x99\xe5\xc9\xe9\x7c\x86\x8a\xbc\x3d\x8a\xaa\x33\x32\x9e\x99\x64\xec\xc9\xd9\x7f\xd4\x8d\x90\xb1\x35\xa7\x01\xcf\x19\x54\xe4\x06\xda\xb2\x91\xc9\xd4\xb6\xad\xc9\x84\xa7\x15\x12\x99\x83\xd4\x8d\xb8\xd4\x77\x1c\xcf\x9d\xca\x79\x85\xb6\x6c\x24\xf4\xcd\xc0\xa2\x66\x28\xa7\x21\x52\x37\xe2\x4c\xfd\xb1\xe3\x92\x50\x4e\x52\xd4\x70\x4d\xbf\x76\x96\x22\x26\x4f\x77\xcc\x52\x44\x26\x7f\xa4\x29\xfa\x4a\x3e\x91\x7b\xeb\x34\x45\xac\x4a\x9f\x5f\x24\xdb\x8c\xb6\x67\xe4\xfe\x91\xa6\xe8\xeb\xfb\x46\xee\xb6\x69\x8a\x94\xcc\xa9\xfb\x47\x6e\x6f\x9a\x22\xdb\xed\x4e\x53\xc4\x86\xf1\x03\xd7\x52\x79\x4b\xd6\x7f\x13\x6f\xe9\xbf\xf5\xe1\x96\xaf\x7b\xb0\xe5\x77\x3a\xb2\x72\x77\x27\x8a\xbf\x2a\xbb\x2b\x00\xfd\x5a\xec\xe0\x55\xdc\x75\x53\xdf\xe4\x3b\xf2\xd6\xeb\xe5\xcd\x40\x3c\x34\xc0\x4b\x2f\x2e\x57\x34\xce\xb3\xe6\x9d\x3c\xf2\xf1\x99\x0a\x1f\x4c\xa5\x54\x35\xd1\x68\xde\xdc\x38\x96\xeb\x59\xf3\x19\xfa\x15\xe1\xd4\x72\x3d\x6a\x59\x43\xa3\x5d\x6e\x4a\xec\xa9\xe3\xcc\x30\xcd\xa0\x65\xd3\xf9\x64\x1c\x84\xb2\x6b\xd0\xaa\xe0\x8f\x03\x73\xee\x07\x73\xbc\x00\x21\x70\x42\xdb\xb7\xe6\x2a\xc0\x74\xe6\x8f\x43\xdf\x1b\xe3\xed\xd9\xc4\x9d\x85\xbe\x1f\x74\x02\xb6\x67\xe3\x49\x60\x8d\x7d\x74\x67\x6c\xc7\xf5\xc7\xb6\xab\x02\x3c\x9e\xcd\x09\x21\x73\xc4\xd8\x9f\x98\xe3\xd0\x24\xb3\x4e\xc0\x33\xcb\x9e\xbb\x96\x87\x57\x6e\x7b\x73\x32\x73\xe6\x33\x5f\x05\xd8\xf3\x49\x30\xa6\x21\x62\x1c\x7a\x93\xd0\x25\xc4\xed\x04\x1c\xba\xe6\xd4\xf3\x38\x8d\x3d\xdb\xb4\x4d\xcb\x51\xd2\x98\x58\xae\x3d\xf6\xf9\x9d\x11\xce\x78\x6a\x4e\xe6\x3e\xed\x04\x6c\x39\x36\x71\xc7\x3e\xde\x1d\xe1\x50\xea\xf8\x96\x1b\x28\x49\x31\x36\x83\x69\x18\xe0\x05\xe2\xe1\x78\x3e\xf7\x1d\x6a\x75\x02\x9e\x5a\x3e\x1d\x87\x53\x24\xc5\xdc\x9a\xfa\xee\x6c\xa2\x64\x9e\x6b\x86\xd4\x27\xfc\xf2\x0a\xdb\x27\x93\xd9\xc4\x27\xdd\x34\xf6\xc3\xc0\x9c\xf0\x0c\x95\xd6\x38\x98\x12\xcb\x1e\xab\x00\x07\x64\xe6\xcf\x09\x47\x20\x98\x4f\x66\xd6\x64\xe6\x74\x02\xa6\xce\xcc\x9f\xcc\x02\xa4\xdd\x8c\xce\x89\xe3\x85\x4a\x1a\xd3\xb9\x4f\x9d\xa9\x8b\xd7\x88\xdb\xae\x33\xb7\xc6\xd4\xee\x04\x6c\xce\x03\x32\x0b\x03\xac\xe0\xfa\x6e\x10\x8e\x7d\x25\xc6\x96\x63\x06\x1e\x09\x02\xbc\xa4\x7d\xea\x05\xb3\x60\x32\xee\x66\x5e\x48\x67\x56\x30\x41\x05\x19\xcf\x2c\xdf\xb4\xa6\x4a\xc0\x8e\x37\x75\x5c\xc7\xc3\x39\xc2\x84\x7a\x13\xea\xb8\xdd\x18\x8f\x03\xdf\xf4\x66\x21\x62\xe2\x87\x0e\x99\xfb\xa1\xa3\x54\xe9\xc9\x7c\xe6\xba\x21\x02\x76\x6d\x42\xc6\xb6\xdf\x8d\xf1\xcc\xb5\xe9\x98\x8c\x2d\x54\x69\x3a\x99\x84\x73\x4f\xad\x20\xae\x4d\x82\xc9\x04\x3d\x7c\x2b\xf4\x1d\xdb\x22\x66\xb7\xad\x30\x4d\xdb\x9a\x06\x2e\xbf\xf3\x7d\xee\x5b\xc4\x56\x8a\x9b\x3f\x1f\xcf\xa6\xf3\x40\xe4\x37\xa5\x73\x93\xd2\x6e\xa9\x08\x26\xd4\x34\xfd\x39\x0a\xbe\x1d\x7a\xae\x3b\x0f\x94\x52\x11\x8e\xbd\xe9\x8c\x38\x08\x78\x66\x9b\x9e\x37\xb5\xba\x49\x61\x4e\x02\x6f\x62\x8f\xf9\xf5\x2e\xa6\x69\xbb\x96\x5a\x41\x88\x63\xcd\xac\x19\x9f\x7b\x99\x9e\x49\x27\x74\xda\x4d\x0a\x6b\xea\x4f\x4d\xcf\x45\xe3\xe2\x4c\x42\xcb\x9a\xcf\x95\x2a\x6d\x51\xc2\xc8\x84\x24\x1b\x07\xd6\x24\x98\x59\x93\x4e\xc0\x4e\x68\x05\x93\x70\x8e\x52\x31\xf6\x02\xc7\xf2\x68\xa8\xb4\x15\xb6\xed\x9a\x21\x41\x92\xcd\xc2\xd9\xd8\xb7\xc3\x79\x27\xe0\xc9\xd8\xf4\xa6\xf6\xd8\xe1\x0a\xe2\xcd\x27\x76\x48\xd5\xe2\x36\xf1\x4c\xcf\x47\xbb\x6d\x07\xd3\xa9\x6f\x79\xdd\x66\xd3\x25\x81\x15\xcc\x2c\x6e\xdd\xa6\x34\xf4\x28\x9d\xa8\x00\xcf\xac\xa9\x65\x05\x9c\x64\xc4\x71\x2d\x7b\x6c\xfb\x9d\x80\x3d\xcb\x9f\x53\xd7\xe3\x76\x36\x98\x13\xd3\x9e\x28\x15\xc4\x73\x89\x37\x99\x38\x88\xb1\x1f\x38\x96\x6d\x9a\xdd\xd6\x2d\xb0\x1c\xdf\xf5\xa7\x26\xda\x59\x73\xee\xce\xa6\x33\xa2\xb4\x6e\xd3\x49\x30\x26\x1e\xd2\xd8\x9c\x8c\x1d\x9f\xda\xdd\x52\x11\x92\x99\x45\x5d\x32\x43\xc0\x13\x3a\x1f\x5b\x44\x39\xe6\x85\x93\xd9\xcc\x9c\x58\xc8\x8b\xf1\x78\x32\xf6\x66\x3d\x9a\x37\x77\x4c\x6a\x8f\x39\xed\xc6\xd3\x29\xb1\x4c\xcb\x53\xca\xb1\x39\xf1\x3c\x93\xf7\xcc\xb6\x7c\x3f\x24\x7e\x37\xf3\xc8\xcc\x73\x02\x42\xd0\x6c\xfa\x6e\x68\x85\x66\xa0\xc4\x98\x50\x7b\x3a\x09\x4c\x2e\xc7\xc4\x21\x9e\x3f\xee\xb6\x6e\xd6\xd4\x71\xa7\x53\x07\xe5\x38\x9c\xbb\x94\xfa\xb3\x99\x0a\xb0\xed\xf8\xa6\x1f\xf8\xd8\x33\x4a\x66\xbe\xe3\xf6\x88\x9b\x3d\x23\x81\x19\xf8\xc8\x94\x60\x1c\xcc\xc6\xde\xc4\x56\xda\x63\x1a\xba\x9e\xe7\xa0\xd9\xa4\xb6\x43\x5c\x2f\xe8\x16\xb7\xb1\x3f\x0b\x02\xcf\x99\xf3\x91\x61\x62\x53\x7b\xaa\x04\x3c\x71\x2d\x3a\x99\x73\x63\x15\x4e\x7c\xcb\x77\xbd\x6e\x52\x4c\x1d\x77\xee\x5a\x14\x15\x64\x1c\xd2\xb9\x6f\xa9\x6d\xc5\xd4\xf5\xc6\x13\x9b\x8f\x34\x8e\x4d\xa6\xd6\x7c\xd2\x2d\x15\xae\x13\xb8\x53\x97\x70\x4f\x88\xcc\x4d\xcf\x9f\x2a\xcd\xa6\x1b\x04\x53\xd3\xe2\xcc\x23\xde\xc4\xb1\x67\xb4\xdb\x77\x9b\x99\x3e\x9d\xcf\xe7\x1e\xf7\x22\x27\x36\xa1\x96\x52\x2a\x3c\x67\x6c\x4e\x02\x8a\x9a\x17\x52\xd7\xf2\x43\xda\xed\xbb\xf9\x74\x3e\xf3\xec\x39\x1f\x19\xac\x60\x32\x9d\x11\xb5\x5f\x31\x99\x92\xa9\x3b\xe7\x43\x98\x3d\xb5\xc6\xb6\xd5\xcd\xbc\xc0\xb3\xa6\x36\x0d\x90\xc6\xd4\xb3\x26\x13\x32\x53\xd2\x38\x24\xee\xc4\x77\xf9\xd0\x64\x31\x41\xb2\xea\x41\xc0\xb6\x23\xe2\x85\xde\x34\x0c\x51\x41\x82\x90\x9a\xd4\x27\x4a\xb3\x39\x1f\x4f\x43\x67\x3e\x9d\x8b\x41\x97\x86\x64\xda\x2d\xc7\xe6\x64\x6e\x4e\xa6\xdc\x5f\x98\x5a\x64\x3a\x99\xfb\x4a\x95\x36\xbd\x89\x3d\x0d\x03\x54\x10\xcf\x0a\xdc\x99\xeb\x75\x8f\x20\x84\xd8\xf3\x99\x6b\x3a\x22\x70\x37\x33\x43\x4f\x89\x31\xf1\xa7\xc4\xf4\x6d\x6e\x8f\x6d\x12\x38\x53\xd2\x4d\x63\xcb\x0d\xfd\xe9\x74\x3e\xe6\x52\x61\x3a\xd3\xd0\x55\xda\x63\xdb\x0a\x3c\xcf\x9f\xa2\x54\x38\x66\x30\xb5\x9c\x59\xb7\x82\xd8\xc1\x8c\xfa\xd4\x44\x52\x90\x71\x30\xf3\xa9\xaf\x64\x9e\x63\x93\x70\x32\x0d\xb0\x67\xb3\x80\x98\x66\xe8\x74\xcb\xb1\x13\x04\xe3\xd0\xe1\x8e\x77\xe0\xdb\xd4\xb1\x7c\xe5\xd0\xc4\xdc\x15\x6b\x36\x43\x63\x35\x0f\x26\xe3\x29\x65\xe6\xb5\xcb\x56\xcc\x03\x7f\x32\xf7\xf8\x20\xe9\x85\x93\xb9\x47\x95\x18\x4f\x02\xc7\x21\x33\x17\x01\x3b\x9e\x33\x1d\xbb\x64\x2a\x82\xa8\xe7\x1d\xc7\x56\xab\x79\xe1\x4f\x77\x3d\xa1\xaa\xbb\x06\xed\xa7\xda\x09\xd5\x5f\xef\x76\x42\x75\x4c\xac\xed\x96\x0e\x14\xcb\x11\x5f\x3f\xfb\xe8\x5d\x97\x0e\x26\x9e\x39\xa3\x45\xc0\xdd\xf6\x83\x60\x66\x6a\x96\x0e\x7c\x7f\x32\xf5\x28\x1f\x7e\x5d\x27\xf0\xbc\x69\xdd\x75\xe9\x68\xc4\x0e\x26\x74\x6e\x4f\xd1\x92\xcd\xe9\xcc\x99\xbb\xcc\x92\xa9\x4a\x7a\x63\x67\x3e\x1f\xdb\xa8\x05\xe3\x39\x09\xed\xc9\x7c\xdb\xa8\xfe\x98\x98\x74\x6c\x71\xe3\xe3\x85\x74\xe2\x5a\xa1\x66\xe9\x60\xe6\x9b\xe3\x89\xcb\x05\xd2\xf2\x6d\x3a\x09\xc8\x7c\xcb\x46\xc8\xdc\xb5\xc3\x19\x97\xf9\xb9\xef\x10\x3f\x9c\x68\x7a\x32\xf6\xa9\x19\x84\xdc\x0d\x22\xf6\x94\x5a\x64\x3a\xbb\xcd\xd2\xc1\xd7\x3e\x47\xba\x4d\x6a\x58\x2c\x67\xea\x33\xbf\xfe\x40\xf4\xa9\x5f\x7f\xb0\xf4\xb9\x5f\x7f\xb0\xf5\xc9\x5f\x7f\x70\xf4\xd9\x5f\x7f\x18\xeb\xd3\xbf\xfe\x30\xd1\xe7\x7f\xfd\x61\xaa\x49\x00\xcb\x3b\x88\xe9\x61\x95\xfb\xc0\xf9\xfb\x25\x7f\xdf\x3e\xec\xc1\x69\x80\xd5\x95\x47\xa0\xf8\xfb\x25\x7f\xaf\xa9\x6e\x61\x75\x4b\x5b\xdd\x5a\xf2\xf7\x9a\xea\x36\x56\xb7\xb5\xd5\xed\x25\x7f\xaf\xa9\xee\x60\x75\x47\x5b\xdd\x59\xf2\xf7\x9a\xea\x63\xac\x3e\xd6\x56\x1f\x2f\xf9\x7b\x4d\xf5\x09\x56\x9f\x68\xab\x4f\x96\xfc\xbd\xa6\xfa\x14\xab\x4f\xb5\xd5\xa7\x4b\xfe\x5e\xb1\xad\x6f\xcb\xa4\xc7\x5c\x32\x54\xc0\x3d\x2e\x14\xcd\x8c\x7b\xb8\xe5\x96\x0b\x84\xaa\x96\xcf\x65\x41\x55\x2b\xe0\x72\xa0\xaa\x15\x70\x11\x50\xd5\x0a\x39\xfb\x55\xb5\x42\xce\x79\x55\x2d\xca\xb9\xae\xaa\x45\x39\xc3\x55\xb5\xe6\x9c\xd9\xaa\x5a\x73\xce\x67\x55\xad\x0b\xce\x63\x55\xad\x0b\xce\x5e\x55\xad\x05\x67\xad\xaa\xd6\x82\x73\x75\xa9\xca\x3b\xd8\x75\x74\x77\xcb\xeb\x50\xb5\xf9\xb4\x8b\xf6\x7f\x8a\x78\xee\x61\xdd\x71\xf3\x23\x1c\xc1\x8b\xe5\xb3\x76\x91\x2d\x12\x45\xf3\x66\x18\x09\x7e\x8a\x8a\xd3\x06\x72\xd6\x68\x78\x00\xd6\x39\x96\x54\xe7\x72\xad\x60\x2c\x39\x0c\x71\xbe\xa0\x09\x03\x4f\xcd\xdf\x29\x03\xf5\xfe\x3e\x7c\x8f\xd9\x88\xf5\x8d\x17\x29\x9d\x6f\x95\xa1\x7a\xb3\x28\xf3\x1c\x6f\xfa\xce\xe2\x89\x62\x4b\xa9\x46\xf7\x79\x3c\x5e\x6a\x51\xcb\x82\xbd\xe0\xc9\x7f\xe5\xe4\xd5\x4b\x4c\x51\x5c\xa4\x03\xae\x95\x73\x5b\xe5\x70\xd3\xeb\x7b\xa8\x17\x9b\x76\x9d\x30\xe5\x25\x97\x35\x2c\x96\x6d\x2c\x16\x2a\x2c\x96\x6d\x2c\x16\x32\x16\xf5\x72\xd3\x76\x39\x4d\x26\x63\x99\xa5\x9a\x9c\x39\x57\x52\xee\xed\xdb\x24\xdf\xae\x38\x4a\xb6\xe3\x28\xa9\x38\x4a\xb6\xe2\x28\x59\xd4\x12\x7c\x2f\x8a\x2c\xdc\x52\x62\xee\xa5\xc8\xd5\x2d\x11\x89\x08\x0a\xd7\x8b\xe1\x3e\xe6\x99\xc4\xd2\x02\xde\xa4\x97\xa5\x64\x59\x43\x63\xa9\x40\x63\xa1\x42\x63\xd9\x42\x63\x51\x43\xa3\x0e\x70\xd2\x82\x67\x4d\x3a\x79\x7a\xab\xdc\xe1\x5d\xa6\x64\x5a\xb1\x7d\xda\xc5\xf6\x9f\xa2\x29\xb7\x5c\xca\x81\xb9\x51\x72\x29\x4a\x76\x9c\x09\xe7\x25\xc9\x44\x32\x24\xda\x5b\xa1\x8b\xb2\x1c\x01\xa2\xf4\x2c\x9a\x65\x97\x45\xd9\x5e\x1c\x2a\x4b\xb3\x64\x44\x8b\xa6\xcd\x91\xab\x5e\xbc\x32\x65\x0b\x5e\x7c\x81\x39\xdb\x18\x1c\xc6\x49\x73\x08\x8f\x0a\xed\x2c\x9f\x3c\x01\x02\x07\xd0\xda\x36\xdd\xc6\x83\xfd\x2d\x38\xd8\x8f\x06\xfb\xbb\x5b\x6a\x8b\x06\x0b\x72\x57\x2c\x90\x8a\x5b\xe2\xc0\xb9\xd3\xc6\x80\x73\xa2\xd5\xbe\x1a\x68\x35\x2a\xfe\x14\xe9\xd8\x5b\x8d\x7a\x3f\x45\x2a\xe4\xf4\x39\xf1\x45\x52\xfc\x05\xdc\x87\xf9\x42\xa4\xc5\x67\x3f\xd4\xe7\xf8\x78\x1d\xae\xfb\x74\xc9\xea\x2c\x45\x1d\xf6\xe3\x62\xd9\x91\x4c\x7f\x81\xd9\xf4\x19\x68\x9f\xb7\x83\xdf\x03\xfe\xdd\x17\xdf\xf5\xd5\x97\x58\x9d\xb5\xe2\xf3\x26\xf1\x7b\xc0\xbf\xfb\xe2\x7b\x77\x4a\xfe\x05\xcf\xc9\x2f\x0c\x0e\x1f\x57\xbc\x25\x4f\x2f\x3d\xe4\xc9\x0f\xbc\x45\x91\xb1\x5f\xbc\xac\xe5\xec\x5f\x48\xb7\x48\x78\xc5\xa8\xd3\x99\x99\x1f\x67\x53\x83\x12\x90\x68\x73\x51\x6f\x73\x59\x6b\x73\x51\x6f\x73\x29\xb7\xb9\xd8\xa6\x4d\xc2\xfb\x49\xc5\xd0\xc0\xcf\x9b\x50\x3e\x28\xb8\x45\xda\xff\x45\x71\x69\x85\xf4\xd2\xa9\x5e\xb2\x36\xed\xe2\x1d\x4f\xc3\xdd\xdd\x26\xef\xa7\x28\x5c\xb4\xb9\xa8\xb7\xb9\xac\xb5\xb9\xa8\xb7\xb9\x94\xdb\x5c\x54\x6d\x2a\xbd\xce\xfe\x7b\x08\xd4\xb8\xfe\x05\xb3\x2f\xfd\x45\x7f\x98\xea\x2f\xa8\xbc\x7f\x89\xba\x8e\x51\xfd\x05\x8d\xc1\x5f\x22\x9d\x09\xbd\xc2\x8b\x12\x58\x99\xc5\xb2\x44\x51\xa5\x94\xbc\x20\x6b\x70\x51\xf5\x85\x9b\x8b\x9c\xc8\xe6\x62\xb1\x8d\xad\xaa\x9a\x65\x7f\x19\x45\xba\xdb\xcc\xb1\xa9\x60\xa1\x6a\x30\xb8\x53\x8b\x7f\x51\x9a\x9e\x66\x8b\x7f\x89\x54\x2d\xfe\x25\xba\x4b\x8b\x6a\x63\xd7\x6c\xf1\x27\x65\x8b\x3f\xa9\x5a\x54\x4b\x5b\xf3\xf2\x0a\x4d\x93\x18\xbc\x28\xd4\x1e\x0b\x6a\xb1\xc3\x38\x48\x61\x95\x76\xb9\x79\x44\x14\x2d\x19\xc5\x02\xd6\x76\x68\xfe\xb8\x0e\xbd\x9c\xc2\x75\xf7\x4c\x9f\x7d\x70\xbe\xa9\x94\x6f\x9c\x6e\x5e\xa8\xd0\xc6\x01\x68\xae\xaa\x83\x13\xdb\xb9\xaa\x0e\xce\xa1\xa9\xaa\x0e\x4e\xa1\xa9\xaa\x0e\x4e\xc9\x07\xe1\x12\xaf\xef\x58\xea\xee\xef\xc0\x39\xfd\x20\x5c\x60\x29\x4e\x3a\x2a\x53\x2e\x6c\x11\x4d\x7b\x13\x08\x83\x14\xa8\x70\xc4\x90\x42\xa0\xc2\x11\xa3\x17\xbe\xaa\x0e\x06\x2f\x7c\x55\x1d\x8c\x93\x78\xaa\x3a\x18\x26\x69\xdd\x66\xc0\x3e\x18\x76\x19\x70\x51\xcf\x2d\x2d\x31\x30\x70\x33\xe0\x74\x60\x92\xb5\x5b\x8d\x38\x9c\x1a\x79\xdb\xd9\xf9\xaa\x97\x95\x48\x31\x43\xf4\x0c\x7e\x40\xf9\xf7\x5a\xde\xc0\x0f\x65\x32\x8a\xc1\x0f\x28\xf7\x1e\x47\xf6\x07\x53\xc6\xd6\x6b\x23\xdb\x84\x23\x45\x19\x79\x83\x48\x22\xbf\xdd\x20\xa9\x1a\x44\xf2\xf8\xa2\xc1\x9a\x25\xf0\xfb\x1b\x94\xe2\x92\xbc\x41\x0b\x4d\x6c\xbb\x41\xab\x6a\xd0\x5a\x14\xe3\xd2\x00\xcb\x4b\xe6\xb5\xbf\x41\x29\x92\xc9\x1b\xb4\x59\x83\x61\xbb\x41\xbb\x6a\xd0\x66\x6d\x85\xa2\x41\xbb\x47\x1d\x9a\x70\xa4\xd8\x27\x6f\xd0\x61\x0d\xd2\x76\x83\x4e\xd5\xa0\xc3\xda\xa2\xa2\x41\x47\x6e\x90\xf6\x37\x28\x45\x4b\x79\x83\x63\xd6\xe0\xbc\xdd\xe0\xb8\x6a\x70\xcc\xda\x9a\x8b\x06\xc7\x72\x83\xf3\xfe\x06\xa5\xf8\x2a\x6f\x70\x82\x93\x8a\x76\x83\x93\xaa\x41\xf4\xde\x2f\x44\x83\x93\xda\x24\xa2\xbf\x41\x29\x22\xcb\x1b\x9c\xb2\x06\x17\xed\x06\xa7\x55\x83\x38\x6d\x12\x63\x32\x2b\xdf\xe5\x04\x7c\xf1\xd9\x8b\x3f\x2e\xc5\xf9\x7a\x97\xe2\x10\xe6\xdc\x8b\x9b\xcd\x18\x30\xcc\xc3\x62\x9b\x5f\xfb\x5a\x1c\x75\x33\xe4\xbf\xe4\xc5\x38\xcf\x92\xf8\x8a\xa6\x3c\xcb\x2f\xe4\x09\xd8\xd6\x9e\x1f\xe5\xcc\x41\x09\xc1\xc3\xfd\xd9\x3e\x9d\x27\x29\x15\xdb\xa9\x5b\x5c\x93\xce\x9a\x48\x6b\x77\x79\xf2\xb3\x6d\x7d\x8d\x8b\x78\xfe\x55\xaf\xe0\x91\xf1\x2c\xf3\x83\x1c\x00\x31\x2d\x67\xdf\x16\x79\x8a\xff\x38\xdd\xa4\x3d\xaa\x34\x26\xd6\x6d\x4f\x37\xb1\x2a\x3d\xa7\x9b\x6a\xdb\x1a\x5a\xa7\x9b\xc6\xc4\xfa\xe3\x74\xd3\xd7\x3e\xdd\xc4\xb8\xb2\xdd\xe9\x26\x25\x73\x6a\xa7\x9b\x38\x83\x3a\x4f\x37\xf1\x73\xb4\x5b\x9e\xfe\xb6\xff\xa5\xcf\x33\xd1\x38\xd8\xf3\xbd\x8c\x4e\x9c\xc6\x8b\x55\x38\x6e\x16\xbd\x5a\x7f\x08\xe7\x8d\x87\x41\xb4\x5e\xd0\xf4\x9f\x72\x24\x4a\x42\x15\x7f\x33\x0c\xf9\x0b\x8e\x18\x7e\x97\xf1\xf9\xef\x70\x74\xea\xa7\xad\xee\x04\xc2\xcd\x33\xcf\xb0\xeb\x65\x39\xe9\x59\xff\x51\xa8\xfd\x7d\x78\x43\xd3\x15\x8e\xa2\xcf\x16\x49\x14\x50\x20\xcd\x6b\x53\x58\xf5\x37\xcf\x48\xfd\xec\xd2\x78\x6a\x80\x33\x33\xc0\x21\x06\xd8\xb6\x01\xd6\xd8\x00\x32\x35\x60\x66\x00\x10\x69\xab\xd1\xd8\x35\x60\x6c\x1a\xe0\x58\x06\xd8\x8e\x01\xd6\xc4\x00\xe2\x1a\x40\x4c\x03\x2c\xb9\xdc\xcc\x80\x31\x31\xc0\xb1\x0d\xb0\xc7\x06\x58\x53\x03\xc8\xcc\x00\xc2\xe0\x4b\xe5\x26\xa6\x01\x63\xcb\x00\xc7\x31\xc0\x9e\x18\x30\xb1\x0d\x18\x8f\x0d\x70\xa6\x06\xd8\x33\xa9\xa0\x4d\x0c\xb0\x6c\x03\xc8\xd8\x80\xa9\x01\x30\xb1\x0c\x18\x3b\x06\x38\x78\xb5\x80\x5c\x90\x61\x62\x19\x40\x1c\x03\x26\xac\x20\x31\x60\x6c\x1b\xe0\x8c\x0d\xb0\xa7\x52\x41\x6b\x66\x80\x45\x0c\x20\xac\x49\x03\xc0\x72\x0d\xb0\x4c\x03\x08\x43\x87\x17\x3b\xef\xa0\xab\xa5\xa6\xab\x55\xa7\x2b\xc3\x82\xd1\x91\xf5\xdb\x62\xdf\x0d\x80\xb1\x8c\xad\x68\x98\x75\x8b\x61\x8b\x08\x99\x32\x96\xb6\x20\x1c\xc3\x8a\x15\x98\x18\x20\x77\x97\x4c\x38\x3d\x18\x81\x11\x7b\xbb\xce\x08\xc6\x50\x46\x60\x46\x3f\x7b\xca\x09\x3b\x1e\x37\xe8\xe5\x98\x82\x5b\x63\xce\x7d\x47\x6e\x81\xb1\x86\x89\x86\xcd\x58\x3a\xe1\x6c\x1f\xcb\x3c\x64\x2c\x60\xf2\xc0\xe4\x82\xf1\x90\x11\xb6\xf0\x6a\x6a\x37\x42\x5d\xae\x2e\x97\x1e\x5e\x93\xc2\x9c\xca\x6c\x11\xcd\x5b\x37\x3c\xa1\x16\xbc\x3c\xfd\xf5\xdd\x0f\x2f\x5f\xf0\x3b\xa5\x18\xc5\x2c\x03\xb0\xf3\x8c\x42\x2e\x93\x48\xc1\x26\xa4\xae\x90\x54\x22\xd8\x69\x09\xe9\x45\x82\xb8\x72\xfb\xef\xbe\x7b\xfd\x33\xcd\xc0\x8b\x43\x91\x1b\x7d\x8d\x2c\xe5\xf7\x69\x28\xf0\x60\xe5\x7f\x7d\x53\xe7\x67\xc3\xa5\x34\x37\xe6\x01\x4e\x46\x5c\xcb\x34\x8d\xe6\xbb\x62\xae\xc0\x8b\x28\x0a\x58\xb5\x02\xae\x69\x5a\xad\x22\xb6\x54\xa4\xfd\xd6\x91\xdf\x2a\x1a\x18\xd7\x1b\xb0\x14\x0d\x4c\xea\x48\xaa\x8a\x4c\x1b\xfd\x50\x34\xe4\xd6\x10\x69\x83\x98\x35\x5b\x69\x83\xf0\xe4\x22\xaa\x02\x7e\x93\x5a\xed\x22\x41\xa3\x99\x56\x81\xb0\xd9\x95\x76\x11\x2a\x15\x69\xb7\x30\xaf\x63\xd9\xae\xee\x76\xd5\x26\x6e\x2f\x3f\x2c\xb7\xa7\x01\xdb\xed\x91\x2a\xa7\xd9\x88\x42\x2e\xdc\x6e\xb9\x99\xb8\xbd\x82\x39\x75\xbb\x04\xd3\x75\x7b\xf9\x3d\x73\x7b\xf8\xed\x35\x91\x50\x88\x44\xb3\x99\x36\x26\x81\xdb\xcb\xf1\xd0\xed\x91\x1a\xea\x76\x4b\xf7\xbc\xd9\x86\x82\xf3\x5a\x76\x09\x2b\x41\xd4\x84\xb4\xa4\xb7\x1a\x66\xda\xb5\x22\xca\xd6\x9d\x3a\x14\x55\x1f\xc7\x72\x11\xa5\x4c\xc8\x78\x2a\xde\x4f\xeb\x68\x74\xe8\x06\xe9\x10\xff\x59\x13\x53\xad\xa1\x20\x1d\x1c\xf5\xeb\x9d\x51\x48\x45\xad\x33\x5a\x3b\x41\x3a\xe4\x97\x36\x8a\xe8\x4c\x05\x51\x9b\x02\xb7\x97\x14\xc4\xed\x25\x85\xe5\xf6\xb2\xde\x76\xbb\xd9\xe6\x34\x40\xe8\x6c\x45\x17\xb9\x27\x6e\x97\x08\x4f\xdd\x1e\x66\xb8\x6e\x0f\x25\x67\x6e\xaf\x68\x79\x6e\x37\x43\xfd\x26\xbd\x15\x83\x47\xb3\x95\x76\x91\xd0\xed\x62\x29\x75\x7b\x54\x68\xde\xe4\xa8\x7c\x47\x95\xd1\xe7\x65\x38\xa6\xe9\x3a\x26\xd1\x5a\x10\x51\x46\xeb\x66\x94\x0c\xd4\x59\x90\xa2\x11\x53\xd5\x88\x53\x6f\x44\x59\x66\x5c\x87\xa3\x44\x66\x52\x87\xa3\x2c\x33\xad\xca\x28\x5a\x91\x8d\xad\xb2\xfa\xac\xd9\x84\x02\x88\xd7\xec\x8e\xde\xe1\x10\x0d\x29\x80\x04\x35\xc2\x2a\x0a\x84\x55\x01\xad\x01\xe1\x28\x28\x2a\xcf\x9b\x5c\xd1\xfa\x5d\x9d\xc4\x24\x6e\x4f\x2f\x2c\xb7\x8b\xda\x76\xb3\x09\x95\x6c\xb8\x0d\xbe\xab\x64\xc3\xed\x27\xf8\xc4\xed\x11\xd4\xa9\xdb\x2f\xa8\xae\xdb\xc3\x94\x99\xdb\xc1\x14\xcf\xed\xd6\x25\xbf\x89\x81\xde\x90\x74\xaa\x4a\xe8\xf6\x08\x31\x6d\xd2\x54\x6f\x4f\xb4\x12\x24\x4f\x40\x14\x6f\xc9\x16\x6a\x4f\xac\x2d\x94\x89\xd8\x5b\x28\x3e\x71\xb6\x90\x67\x32\xee\x54\x7d\x32\xe9\x53\x49\x32\xed\x31\x86\xb2\x0b\xae\x86\x30\xeb\x33\x97\xc4\xeb\xd3\x7b\xe2\x6f\x61\x2d\x49\xd0\x67\xc8\x48\xb8\x85\xb1\x24\x74\x0b\x53\x46\xe6\x4d\x0e\x29\xc5\xa5\xcf\x54\x10\xd2\xa7\xa1\xc4\xda\x42\x41\x88\xdd\xa3\x65\xc4\xd9\xc6\xb0\x8d\xb7\x30\x3b\x64\xd2\x69\xdd\xc8\x74\x0b\xb3\x44\xdc\x2d\x74\x91\xcc\xb6\xd0\x7a\xe2\x6d\x61\x4d\x89\xdf\x67\xc1\x48\xd0\x65\xc2\x48\xd8\x67\x16\xe8\x16\x66\x94\xcc\x1b\x16\xea\x36\xae\x0a\x31\x1d\x8d\x31\x52\xa3\x6c\xd5\xa8\x42\xb4\x2e\x0a\x87\xad\x82\xee\x48\xef\x4d\xc5\xfb\x71\x83\x39\xed\x12\x93\x1a\xd1\x54\x6d\x4c\x6b\x25\xfa\x87\x63\xbd\x6f\x52\xb5\xa2\xf3\x4c\x8a\x9e\xea\xbc\x92\x0a\x8b\x36\x9e\x41\x83\x9a\xed\x12\x61\x8d\x5a\x3a\xd7\x04\x21\x68\xdc\x12\x51\x57\x4d\x81\xae\xee\x11\xb7\x0f\x7d\xcb\xd5\x0b\x8a\xed\xf6\x09\x8a\xe3\xf6\x31\x7a\xec\x76\x77\x7e\xe2\x76\x8b\xd2\x54\x7a\xdf\x7e\xeb\xba\x7a\xd2\xcd\xdc\x2e\xd2\x79\x6e\x9f\x78\xf9\x6e\xb7\x12\x04\x6e\xb7\xe8\x84\x6e\x9f\x60\x50\xb7\x4f\x09\xe6\x6e\x9f\x88\xd7\xdc\x0a\x8d\x10\x90\x1e\x75\x25\x56\x8f\x84\x12\xbb\xd7\x64\x10\xa7\x53\x52\xc9\xb8\x57\xe1\xc9\xa4\xd7\x6a\x90\x69\x97\x25\x76\x7b\x35\x91\xcc\x7a\x4d\x06\xf1\x3a\xb4\x91\xf8\x3d\xe6\x82\x04\xbd\x56\x8b\xc8\xe6\x40\xd1\x04\xed\xb1\xbd\x64\xde\x6b\x92\x84\x6b\xd1\xd9\x4d\xd2\xa9\x57\xc4\xea\x37\x2d\x76\x87\xe5\x20\x4e\x8f\x5a\x93\x71\xaf\x6d\x21\x93\x4e\x05\x26\xd3\x5e\xdb\x46\xdc\x1e\xe3\x43\x66\xbd\x1a\x48\xbc\x1e\x33\x40\xfc\x5e\x1b\x48\x82\x5e\x53\x40\xc2\x5e\x7b\x44\x68\x87\xb1\x23\xf3\xba\x35\xba\x8d\xff\xe0\x9a\xbc\x49\xb5\x6d\x29\xbc\x4f\x62\x3a\x1a\x57\xa2\x40\x5a\xf1\xde\xae\x20\x38\x6a\x41\x74\xf4\x42\x34\xae\x53\x44\xed\x43\x94\xce\xb1\xaa\xf9\xa9\x59\x73\xff\xf4\xe3\x67\xb1\xa2\xa2\xf6\x20\x2a\xde\xaa\xfd\x07\xfe\x5e\xed\x3b\x54\xe4\xd3\xad\xa0\x54\xe4\x51\xc0\x08\x25\x2d\xd5\x78\x0e\x85\x78\xab\x7d\x87\x8a\xc1\x9a\xfe\x77\xf2\x97\xb8\xfa\xee\x59\x6e\x1f\xf2\xb6\xdb\x47\x00\xc7\xed\x66\xf1\xd8\xed\xeb\xc2\xc4\xd5\xca\xcf\xd4\xed\x13\x3e\xd7\xed\xa2\xdf\xac\xde\xb8\xce\x89\xe8\x90\x0e\xdf\xed\xe2\x5e\xe0\xf6\x49\x5f\xe8\x76\xcb\x2f\x75\xbb\xd5\x6f\xee\xf6\x69\x08\x31\x7b\x54\x84\x90\x1e\x2d\x24\x56\xaf\x1a\x12\xbb\x6b\xa4\xe8\x94\x70\x32\xee\x55\x11\x32\x31\xfb\xf8\x44\xa6\xbd\x96\x8c\xb8\xbd\xda\x42\x66\xbd\xe6\x82\x78\xbd\x06\x8f\xf8\x3d\x36\x93\x04\xbd\x76\x83\x84\x3d\x66\x89\xd0\x0e\xbb\x44\xe6\x9d\x66\x83\x7b\x0f\xdd\x7d\x20\xbd\x7a\x49\x2c\xbd\x62\x12\xbb\x47\xed\x89\xd3\x23\xf8\x64\xdc\xab\x3b\x64\xd2\x6f\xdd\xa6\x1d\xe6\x8d\xb8\xfd\xca\x33\xeb\xb4\x1f\xc4\xeb\xb5\x7f\xc4\xef\x35\xa2\x24\xe8\x34\x22\x24\xec\xb5\x52\x84\xf6\x98\x29\x32\xaf\xdb\x91\xdb\x39\x0f\x4a\x9b\x52\xe0\xab\x5b\x21\x29\xb1\x51\xba\x0c\x07\xd2\x76\x0d\xa5\xc7\x20\x0a\x60\x3c\x45\xe9\x37\x94\x3e\x9f\xe2\xfd\xa4\x00\xa0\x2b\x30\xad\x10\x54\xbc\x95\x79\xae\x73\x19\x2a\xfc\x34\x3e\x43\xd5\x43\x45\x0b\x7e\x85\xa0\x1a\x85\xa0\x56\x40\x35\x70\x68\x75\x8f\xca\xcc\x51\x80\x9e\xd7\x88\xa3\x8e\x39\x74\xd5\x27\x6e\x0f\x71\x2d\xd7\xd4\x09\x8e\xed\x76\x0b\x8e\xe3\x76\x09\xce\xd8\xed\x91\x8b\x89\xdb\x43\xb5\xa9\xdb\x23\x7a\xae\xdb\xc3\xda\x99\xab\xa3\xbb\xe7\xf6\xf0\xd4\x77\xbb\xa5\x36\x70\x7b\xa4\x26\x74\x7b\x38\x47\xdd\x6e\xc1\x9d\xbb\x5d\x62\x4f\xcc\x4e\xb5\x25\xc4\xd4\xf2\x95\x58\x7d\x3a\x4d\xec\x3e\x9d\x24\x4e\x8f\x56\x93\x71\x9f\x52\x90\x49\x9f\xe5\x20\xd3\x1e\xdd\x2e\xc7\x3d\x2d\x1b\xc9\xac\x4f\x81\x88\xd7\x63\x1f\x89\xdf\x67\x41\x48\xd0\x69\xa1\x48\xd8\x67\x61\x08\xd5\x0f\xce\xf3\x1e\x0b\x81\xfe\x41\x37\xaf\x48\x8f\xa4\x11\xab\x47\xd3\x89\xdd\xa7\xcc\xc4\xe9\x53\x56\x32\xee\x33\x55\x13\xbd\x29\x22\xd3\x3e\x63\x41\xdc\x6e\x75\x99\xf5\x29\x3c\xf1\xb4\xc6\x82\xf8\x7d\xba\x4c\x82\x1e\x73\x41\xc2\x4e\x63\x49\x68\x9f\x29\x23\xf3\x86\xc1\xb9\x8d\x57\x20\xd0\x76\x55\x56\xa4\x80\xa9\xf2\x0b\x78\x5d\x4b\xdd\x67\xbb\x7a\x6f\xa9\x60\x3b\x15\x45\x94\xf0\xc7\x72\x7f\x54\x5e\x41\xf9\xb6\x0d\x7b\x5a\x13\x68\xed\xa8\xa8\xf4\x06\x24\xa4\xda\x80\xbd\xa2\x59\x25\xca\xbe\x10\x50\x95\x07\x20\xd1\xaa\xfd\x3e\x94\xc0\xb6\xdf\xd2\xb2\xaf\xed\x77\xf3\x1a\x95\x55\x3d\xed\x64\x12\x71\xbb\x99\x64\xb9\x9a\x1e\xd9\x6e\x17\x77\x1c\xb7\xab\x3f\x63\xb7\x5b\xea\x26\x6e\xb7\x64\x4c\x5d\x3d\x3d\x5c\xb7\x4b\x2e\x66\xae\x5e\x9e\x3d\xb7\x9b\xf5\xbe\xdb\xcd\xc3\xc0\xd5\xc8\x54\xe8\x76\xb3\x88\xba\x5d\x32\x35\x77\xbb\x45\x99\x98\x3d\x7a\x44\x48\x8f\xf0\x11\xab\x47\x53\x89\xdd\x21\x80\xc4\xe9\xd4\x53\x32\xee\x51\x45\x32\x31\x7b\x6c\xd0\xb4\x53\xe7\x4a\x0f\x56\x83\xfb\x4c\x6b\xb5\x3d\x9d\xb6\x12\xbf\xc7\xb4\x91\xa0\xc3\x2e\x92\xb0\xc7\x86\x10\xda\xa3\xb3\x64\xde\x69\xdc\xd8\x88\xae\x41\x9c\x74\x8a\x12\xb1\x3a\x95\x96\xd8\x3d\x7a\x49\x9c\x1e\xc5\x24\xe3\x0e\xcd\x24\x93\x1e\x5b\x43\xa6\xbd\xc6\xaa\x47\x93\xc8\xac\x47\x47\x89\xd7\x61\x00\x88\xdf\x69\xb5\x48\xd0\x69\x5a\x48\xa8\xd3\x7f\x42\xfb\x54\x78\x5e\x37\x3d\xb7\x1f\xba\x15\x32\x52\xa0\xea\x98\x44\x31\x74\x0b\x57\x43\x31\x68\x0b\xa0\xaa\x6a\x4e\xe9\xe4\xa8\xde\x8e\x35\xdd\x9f\x70\x90\x8a\x31\xba\x72\x99\xda\x6f\x5d\xa9\x03\xaa\x61\xba\xec\x7b\xbb\xaa\x27\x09\x79\xfb\xad\x2f\x75\x42\x35\x55\x97\xfc\x38\xc5\x30\xcd\xe9\xd6\x86\x4a\x2b\xba\xa9\x26\xe9\x92\xe7\xdb\xee\x69\x17\x19\x88\xab\x26\xaa\xe5\x76\xf1\xd7\x76\xbb\xfa\xe8\xb8\x1d\x82\x33\x76\xbb\x88\x37\x71\xbb\x7a\x32\x75\x75\xe4\x71\xdd\x0e\xb1\x9a\xb9\x5d\xac\xf6\xdc\x2e\x8e\xf8\x6e\x87\x20\x04\xae\x4e\xcc\x43\xb7\x4b\x92\xa9\xab\x96\xd8\xb9\xdb\xc1\x64\x62\x76\x72\x99\x90\x4e\x75\xb5\x3a\xf5\x95\xd8\x9d\xba\x42\x9c\x2e\x75\x20\xe3\x4e\x55\x22\x93\x4e\x85\x20\xd3\x2e\x8b\x20\xc6\x1b\xe5\xab\x59\xa7\xb5\x20\x5e\x97\xc6\x10\x5f\x63\x34\x48\xa0\x33\xb2\x61\xa7\xe6\x12\xda\x69\x14\xc8\x5c\x6b\x11\x89\xd9\xc9\x75\xd2\xa9\x88\xc4\xea\xd6\x6e\x5b\x23\x69\xc4\xe9\x54\x34\x32\xee\x52\x61\x32\xd1\xea\x21\x99\x76\x5a\x06\xe2\x76\x6a\x3f\x99\x75\xea\x22\xf1\x34\xc6\x8a\xf8\x9d\xea\x46\x82\x2e\xeb\x40\x42\xad\x16\x13\xda\x69\x39\xc8\x5c\x32\x0e\xb7\x19\x53\x5d\x36\xc0\x5b\x0a\x80\x25\x71\xda\xf6\xf8\xa0\x5a\xdc\x68\x9b\x63\x5e\xaf\x6d\x88\x05\x3c\xc5\xab\x31\x87\x67\x29\xf1\x98\x94\x2f\x55\x46\x58\x60\xa2\x1e\x67\x5c\x53\x8d\xff\xac\xec\xb7\xca\x04\x73\x3c\x55\xaf\xfc\x12\xa8\x02\xcf\xe0\x80\x1f\xf6\x68\x9b\x5f\xb5\x9c\xd0\x92\x88\x8a\x3a\x73\x81\x84\xe2\x55\xb1\xa8\xa4\xed\x39\x7f\x4d\xba\x68\x2a\xca\x58\x5d\xfc\x17\x65\xec\x2e\x5e\x8b\xe7\x4e\x17\xb1\x45\x99\xb1\x9e\xac\xa2\xc4\xa4\xb7\xcf\x53\x8d\x68\x89\xd7\x6e\x17\x45\x45\x99\x99\x8e\x4b\xe2\xbd\xa7\x97\x52\x51\xc2\xef\x92\x47\x51\x26\x50\xb3\x5c\xbc\x0d\xbb\xc4\x48\x94\xa1\x5d\x22\x2a\xca\xcc\xf5\x1a\x5a\x78\xc4\x4a\xc5\x26\x5d\x3d\x20\x96\x86\xc8\xc4\xd6\x49\x1c\x71\xba\x90\x25\xe3\x2e\xb6\x90\x49\x17\x31\xc8\xb4\xa3\x8b\x3a\xfb\x3b\xd3\xb3\x90\x78\x5d\x92\x4a\xfc\x4e\x7b\x18\x74\x69\x14\x09\xf5\xf2\x4d\xa8\x4e\xe8\xc8\xbc\x5f\xbb\xaa\xc9\x8d\xb6\x04\xe9\xb6\x05\xc4\xea\x17\x38\x62\xf7\x69\x1f\x71\x3a\xb5\x8f\x8c\xfb\x8d\x40\xc1\xec\xce\xee\x4e\xfb\x8d\x12\x71\xfb\x8d\x1b\x99\xf5\x5b\x83\x42\x1c\xba\xb4\x8c\x0b\x85\xf6\x6d\xd0\x67\xd6\xb8\x60\x74\xe0\x49\xfb\x2c\x4e\x21\x24\xd8\x8a\x34\xb2\xf3\xaf\x72\x5e\x83\x57\x5e\xf6\x21\x83\x7c\xe1\xe5\x90\xd1\x25\x0d\x72\xcc\x47\xf4\xee\xbb\xd7\x3f\x43\x14\xaf\x8b\x6b\x22\xca\x8c\x06\xaf\x9e\xbe\x6b\x5c\x5c\x5c\x1d\x4c\x34\xa0\xda\xf8\x8f\x17\x28\x8a\x1f\xf8\x5d\xfc\x30\xe4\x8a\xa6\x78\xca\x0b\xf0\x1f\xc5\x77\xf6\xc3\x90\xfa\xd3\xc4\x5c\xca\xaa\xf4\xfc\xe8\x1d\x4f\x8c\x05\x3c\xf1\x4b\xf7\x1d\x55\xac\x74\x79\x41\x15\xff\x21\x65\x49\xb9\xeb\x15\x55\xdd\xa9\xf5\x3e\xd0\x9b\x32\x05\xd8\x07\x7a\xa3\x48\x7d\xf7\x81\xde\x14\x79\xf5\x3e\xd0\x1b\x75\x5a\x3d\xd6\x06\x67\xd1\x78\x02\x7e\x94\x67\xe0\x05\x41\x92\x86\x51\x7c\x01\x79\x02\x6f\x9e\x11\x25\xdc\xef\x22\x4c\x05\x74\xd6\xcc\x81\xac\xba\x3b\x64\x3c\xd1\xdf\x1d\x52\x81\x7b\x93\x30\x80\x6f\x9e\x91\xb3\xe8\x1c\xf6\x80\x28\x72\x94\x8a\x76\x79\x7a\xfe\x41\xd1\xbb\xb3\xaa\xbe\x48\xc7\xc7\xfe\x19\xd8\x04\xf6\x24\xd0\x98\x87\x6f\x08\xf7\x5b\x80\x15\x09\x4b\x9f\x66\x19\x5d\xf9\x4b\x0a\x64\x02\xd9\xa5\xff\x81\xde\x28\xc8\x9f\x5d\xfa\x7f\xa1\x37\x59\xc9\x82\xea\xb7\x9e\x28\xf1\x3b\x2c\xc4\x49\x53\xfc\x78\x04\x64\x52\xfe\xd2\x5f\xb1\xf2\x0c\x33\x4e\x09\x7c\xd4\x84\xcc\x0a\xe8\x02\x97\x33\x01\xf4\x5c\x20\xa5\x84\xdb\x7d\x75\x8b\x1f\xe5\xef\x30\x2b\xca\xa1\x94\x04\xa5\x84\xab\x03\xc9\x05\xca\x71\x95\x02\x65\xb5\xeb\xa8\xa4\xc6\x72\xf4\x52\x53\x6f\x67\x9e\x26\x2b\x34\x30\x4b\x3a\xcf\xc1\x72\x51\x33\x58\xcb\xea\x8a\x9c\x38\x67\x83\x08\xf6\xf9\xdd\x10\x26\x26\x70\x2c\x84\x6b\x30\x78\xf3\xcc\x12\x32\x38\x84\xdd\x92\x02\x43\xf8\x16\x2c\xf7\x1c\x73\x3c\xa2\x6c\x45\xf0\x2d\xde\x71\xb1\x35\x7a\x69\x74\xb1\xd8\x1e\x3f\x07\xd3\x77\x56\x48\x0e\x6b\x58\x5a\x2e\xbe\xe6\xb8\xc2\x2e\x58\x8e\x06\xe1\xa1\x02\xe3\x56\xb3\xaa\xcc\xfe\xac\x03\x51\x1c\x50\xa0\x5e\xb0\x10\x62\x07\x51\x06\xde\x7a\xbd\x8c\x68\xc8\x78\xe9\xc5\x40\x37\x6b\x2f\x0e\x69\x58\xe4\x65\x44\xf3\x6e\x28\xa1\x31\x12\x08\x30\x81\x17\x83\x4f\xc1\x4f\x93\x0f\x34\x86\x28\xce\x13\x70\x79\x52\xe0\x0c\xb2\xc0\x5b\x72\xf0\x1c\x64\xa6\x86\x76\xbd\x88\x82\x05\x78\xcb\x65\x72\x9d\x21\x68\x06\x37\x4f\x18\xd8\xcb\x8c\x86\x70\x1d\xe5\x8b\xe4\x32\xe7\x08\x66\x51\x12\xb7\xa1\x08\x42\x63\x7a\xcd\x41\xf5\xe3\xd1\x23\x71\xad\x4c\xf5\x88\x19\x14\x9b\xa8\x28\x57\x93\x5c\xc2\x25\x77\xda\x2d\xb8\x02\x2c\x1a\xb1\xea\x3b\xda\xac\x41\xc4\x99\xf8\x00\x18\xf7\x6d\x35\xab\x74\xfd\x98\xca\xfd\x98\x9e\x8b\xc4\x9e\x9f\xe4\x47\x78\x29\x40\xeb\xaa\x1d\x85\x05\x7c\xc6\x13\x5f\x42\x14\x5f\xd1\x34\xa3\x7a\x2b\x18\xc5\x57\xef\x1a\x86\xb0\xf6\x68\xab\x01\x82\x74\x0c\x10\x15\x34\x99\x62\xd9\x19\x19\x33\x81\x6e\x42\xff\x5c\x0b\x38\x54\x3f\x68\x1c\xa4\x37\xeb\xfc\x16\x57\x01\x8a\x8c\xb5\xc9\xb3\xb2\x5e\x55\xd8\xa8\x9b\x7c\x6d\x0a\xdd\x90\xfe\x1e\xad\x56\x14\xe9\xca\xdd\xfb\xac\xbb\x65\xa3\x20\xa4\xca\xe9\xf8\x9e\xe6\xb2\x9f\x56\x47\x6e\x89\x40\xa5\xab\xb1\x9a\x3c\xe0\xc5\xd2\x66\x31\xbc\x39\x4b\xe1\x7d\xbc\x8c\xa3\x3c\xf2\x96\x72\xea\xab\x7a\x19\xba\x09\x16\x5e\x7c\x41\x8f\xdf\x56\x69\x51\x79\xe6\x31\x73\x63\xce\xf9\x7f\x4d\x91\x56\xd7\xe1\xf7\x53\xe3\x8c\x75\x3e\xd7\xd6\x79\x7b\x2c\xd7\xb1\xb0\x1d\x5b\x7c\xb6\xab\xe3\x72\xdc\xcc\xf9\x9c\xfd\xbf\x25\x6e\x58\x67\x2c\x3e\xca\xcc\xb4\x5d\x57\xb5\xf1\xf4\x61\xa8\x51\xfc\x2b\xd7\x2a\xfc\xde\x7f\x6d\x9b\x62\x24\x52\xfa\x13\x08\x4e\x77\xed\x45\x29\x18\xb2\x9c\x68\xca\xa6\xf5\xb2\xa9\x28\xab\x44\xf2\x05\x8d\xb2\x9c\x2e\x4b\x29\x56\x43\x9c\x63\xe7\xb7\x73\x2d\xdc\x6e\x03\x3d\x67\x03\x2d\x4f\xb5\x76\x16\x9d\x9f\x0d\x06\x02\xdb\xf7\x95\xb9\x66\x8e\x64\x39\x75\xc1\xdf\x98\x56\x5b\x45\x1a\x85\xc1\x6e\x28\x52\xaa\xa3\x54\x43\x93\x96\x05\x1a\xf3\x7e\x03\xfe\x63\x1c\x26\x90\x5d\x7b\x6b\xee\x7e\x2c\xbd\x2c\xe7\xc2\xd0\x36\xe1\x79\x37\xcb\x1a\xc8\xd6\x19\xd6\xa5\xf8\xb9\x42\x86\x31\xa3\xf8\x6d\x55\xbd\xa5\x1a\x5f\x4d\x05\xef\xa2\xea\x77\x31\x29\x3d\xa6\x4b\x31\x23\xcb\x21\xb9\xcc\x5b\x16\xb8\x34\xb9\xdd\x2c\xab\x99\x5c\x3d\xcf\x6a\x43\xc6\x07\x7a\xc3\x53\x40\x4f\x9c\x7d\xdb\x92\xdf\x44\x57\x9a\x17\x52\xde\xe8\x89\x32\x6b\xf4\x3e\xbc\x63\x12\x28\x26\x01\x69\x92\x65\x95\x9b\x8e\x39\x0f\xd1\x21\xc6\x69\x29\xaf\x51\x0e\x54\x15\xe1\x06\xc5\x78\xb5\xf2\xb2\x0f\x35\x95\x2d\x64\x77\x30\xa8\x89\x28\x53\xc4\x62\x74\x7d\x5f\xeb\x3a\x53\x5a\x06\x45\x22\x41\x4d\x64\xdf\xa3\xcc\xfe\x49\x29\xf8\xec\x1d\xf3\xa8\x38\x64\x51\xaa\xd0\xbb\x16\xda\x6f\x8f\xb7\x47\x3b\xd5\xa3\xbd\xec\x46\x7b\xd9\x81\x76\xba\x05\xda\x9d\x49\xa4\xb3\x22\x8b\x34\x0f\x7f\x6c\x97\x47\xba\x2f\x09\x33\x87\x95\xd3\x4d\x2e\xa7\x62\x7e\x7e\xf4\x6e\x24\x1c\xb4\x5a\x2e\x66\x03\x82\xf9\x85\x22\xb9\xf6\x7a\xe9\x31\x24\x36\x39\x34\xa1\x08\x87\x6b\x50\xb5\xa3\x02\x54\x66\x76\x6e\x07\x6a\xea\x49\xb7\x9f\x1f\xbd\x53\x66\xdc\x3e\x4d\xa3\xf5\x92\xee\xdd\x2e\x44\xc4\x2b\xd5\x02\x45\xf2\xa3\x7f\x9d\x70\x91\x08\x44\x30\xb4\x23\xcc\x50\x1a\x34\xaf\x07\x12\x5e\x2c\xcd\x08\x1c\xb2\x72\x23\x4e\xd5\x23\xce\xe3\x24\x1d\x54\xf7\xac\x8b\x8b\xe3\x8b\xa6\x47\xd9\x32\x0a\xe8\xc0\x34\xc0\x1a\xb6\xee\xc2\x28\xc1\x5a\x77\x04\x6b\x19\xe0\x74\x80\xb5\xef\x08\xd6\x31\x60\x32\xd4\x5f\xa4\x71\xe7\xb9\x07\xcd\xc8\x48\xae\x2c\xd5\xd0\x52\x66\x24\xcf\x39\xb6\xa8\x60\x6f\xd1\xc2\xd7\x99\xd3\xb0\xb6\x6e\x89\x9c\x75\xdb\xee\x93\x2d\x5a\x50\x8f\x7a\x64\x66\x7d\xb5\x61\xef\xbf\x88\x59\x2d\xad\xcb\x57\x30\xae\x15\xac\x5b\x9a\x58\x9d\x89\xab\x1b\xda\xb2\x54\x67\xfe\xfc\xb2\x54\x23\x85\xbe\x94\x98\xfd\x60\x6c\x19\x8d\xac\xfa\x52\x72\xf7\x83\xb1\x63\x54\x59\xdd\x0f\xc6\x13\x43\x24\x7b\x3f\x98\x90\xcf\xe7\x86\xeb\x7c\x51\xc2\xfd\x7f\x66\xa6\xfd\xdf\x2d\x1f\xfe\x7f\x4e\x66\x7b\xbc\xa9\x20\x8a\x69\xf8\x75\x53\xdc\x7f\xe7\x65\xb4\xca\x5a\xef\x65\x54\x7a\xf7\xb3\x6d\x75\x66\xc0\x6f\xeb\xf2\x66\xe2\x40\xec\xad\x68\xb6\x96\xb5\x74\x5f\x46\x83\x15\x61\x68\xf0\x7f\xff\xfe\x59\x05\xe6\x29\x4c\x9c\xf2\x0a\x1b\x15\x98\x9f\x27\x0e\xc3\x03\x91\xda\x4c\x9c\x91\xf8\xc1\xf0\x57\x78\x06\x15\x68\x0e\x5e\x84\x53\xa2\xbf\xd1\x0c\x3c\x88\xe9\xf5\xf2\x06\xb8\xae\x85\xaa\x86\x65\x83\x02\xb5\xdb\x3c\xe2\xcb\x95\x4f\xd3\xcf\x80\xb7\x4a\xe1\xad\x2a\xec\x8b\x6d\xa1\x3b\x3f\xea\xac\xb2\x4c\xae\xb1\x06\xfb\x57\x55\xa1\x5e\xb9\x6e\xdd\xda\x05\x0a\xba\x6c\x2a\xba\x14\x16\xa1\x20\x4f\x31\x30\xf3\xd5\x3f\xd3\x32\x6d\x9c\x95\x39\xe6\xd8\x9c\x98\xf5\x78\x67\x41\x69\x34\xf1\x71\x54\xf3\xa8\x58\x0f\x0d\x86\xb5\x7a\x0c\x13\xf7\x6b\x29\x6e\xf5\xc4\xd7\xac\xb7\x87\x50\xbf\x7d\x5b\x9e\x99\x37\x39\xf5\x5d\x94\x5f\x47\x19\x85\x93\xd7\xa7\x19\x42\xe8\x63\x4c\x71\x51\x8a\x10\x90\xcf\xf0\x94\xf1\x97\xd1\x65\x0f\x09\x23\x46\x12\x6f\x9e\xd3\x14\x62\x7a\xe1\xe5\x51\x7c\xf1\x15\x08\x8f\xa0\x28\x23\xbc\x60\xc1\x28\x4e\xf2\x81\x96\xaa\xfb\xfb\x10\x27\xbd\x9e\x2a\xde\xc9\xc2\x09\xfa\x8f\x92\xba\x0f\x95\xc5\x38\x61\xff\x51\x10\x59\xe1\x92\x0a\xca\x08\xc2\x14\xd2\x50\xb1\xf3\x61\x0d\xbb\x9a\x07\xa0\xe3\xca\xd3\x93\xe7\x12\x57\x70\x39\x01\xc7\xed\xb5\x97\xe1\xf2\xc2\x56\x3a\x54\x72\x0a\x61\x30\x95\x28\x99\x95\x27\xac\x89\x02\xee\x57\x66\xfe\xd3\x93\xe7\x5f\x87\xf5\x7c\x6d\xa7\x62\xbc\x17\x87\x03\x2f\x4e\xf2\x05\x4d\x05\x22\x5d\x62\xe0\xc5\xa1\x2c\x06\xac\x87\x3d\xa2\x50\xe9\xd9\x7d\x4e\x90\x3e\xa9\x28\x35\x4f\x94\xff\xa7\xc9\xc7\xeb\xb7\xbf\xb7\x78\xbc\x7e\xfb\x3b\x49\xc7\xeb\xb7\x5f\x47\x38\x92\xb4\x26\x1b\x49\x7a\x0b\xd1\x48\xd2\x3b\x4b\xc6\xa7\x5b\x4a\xc6\xa7\x7f\xb2\x64\xfc\xfc\xfb\x8b\xc6\xcf\xbf\x9b\x6c\xfc\xfc\xb5\x84\x63\xd3\x90\x8e\xcd\xad\xc4\x63\xf3\x05\xf2\xf1\xfe\x96\xf2\xf1\xfe\x9f\x24\x1f\xb8\x28\x2f\x4b\x46\xcc\x23\xa3\x62\x42\xb8\xa4\xf3\x7c\x7b\xaf\x2c\x46\x99\xe0\xbf\x20\x99\x97\x90\xf0\x0a\x9b\xaf\x25\x0c\x08\xec\xeb\x88\x03\x82\xaa\x09\x04\x3e\x39\x1e\x58\xe3\x2e\x39\xe0\x85\x64\x51\x88\x55\x72\xc0\xa6\x40\x31\x3c\x02\xdb\xd2\xad\x74\x49\x92\x32\xa8\x44\xe5\xd1\x23\x88\x71\x89\xbc\x14\x06\xbe\x75\xc8\x82\x3d\x88\x95\x97\xd5\xab\x45\x88\xc1\x69\xcb\xda\x67\x28\x26\x4f\xdd\x08\xc9\x60\x06\x31\xec\x29\x6e\x0c\x6d\x35\xdd\x5c\xea\x62\xcd\xfd\x67\x4a\x2f\x86\xf2\xff\x8f\x13\xdf\xb7\x03\xfd\xe4\xa2\x90\xde\xb7\x5f\x49\x7a\x39\xdf\xeb\x92\x2a\x09\x6f\x21\xcf\x5b\x08\x6f\xcb\x62\x22\xa8\x3b\xc8\xaf\xa4\x05\x25\x9c\x7e\x01\x16\xcd\xff\xd3\x25\xf8\x6d\x92\x7b\x39\xfd\xbd\x0d\x70\x8a\xad\x7c\x2d\x11\x46\x68\x5f\x47\x84\x39\x62\xb2\x08\xa7\x49\xaf\xfd\x65\x45\x7a\xe5\x57\xf4\x08\xe5\x40\x58\xf5\x78\xc8\xdc\xc1\xea\xc9\xdb\xc1\xc4\x69\x89\xe5\x97\x32\xec\x2b\xd9\x9c\x7f\x2d\x8e\xf5\x98\x1c\x56\xe2\xf6\x0c\x7b\xdb\x62\xd8\xf1\x5d\x18\xf6\x34\x0c\x7f\x6f\xcf\xd7\x0b\xc3\xdf\xc9\xf3\xe5\x57\x7e\x7f\x8d\x39\x73\xd8\x98\x33\x87\xb7\x9a\x33\x87\x5b\xcf\x99\x9b\x23\xc2\x6e\xe9\xc8\xe2\x86\x51\xb5\xf3\x1b\x78\x69\x7a\xc3\xaa\x15\x63\x08\xbf\x18\xbe\x36\xac\x54\xd7\xc3\xab\x61\xb4\x1d\xa9\xdd\xca\xe7\x86\x5d\xde\x86\xc0\xe1\x4b\x2d\x3a\xff\xa5\x5e\x5d\x79\x1a\x8b\x2b\xc0\x93\xb9\x1c\xdb\xcc\x54\x37\x1c\xa7\xc9\x9a\xa6\xf9\x0d\xfc\x5d\x5c\x31\x8c\x05\x51\xbc\x4a\x10\xad\xb0\xa2\x10\x90\x6c\xa4\x82\x53\x98\x95\xf2\x4e\xf4\xba\x75\xc9\xa2\x8b\x38\x9a\x47\x81\x17\xe7\xe0\xe3\xfb\x28\x96\x74\x03\x1b\xed\x88\xfe\x56\x71\xe9\x02\x99\xe2\xc9\x57\x88\x03\xb7\x31\xd0\xab\x63\x8d\x5c\x83\xd7\x6b\x26\x96\xde\x72\x58\xa3\x7d\x2f\xe1\x40\x69\x90\x4b\xca\x49\x60\xb7\x22\x22\xad\xb3\xf9\x0b\x74\xf5\x5a\x26\x75\xb3\x17\xb5\x35\xdf\xba\xce\x7e\x21\xb0\xb3\x56\x7d\xf6\xb9\x6d\x58\xdb\xb8\x2d\x14\xe2\x92\x19\xf1\x88\x8f\x67\x6a\x02\x12\x12\x4a\xe6\xc3\x16\x90\xf3\xff\x83\xba\x6a\x00\x31\xb7\x5e\x1e\x40\xa1\x33\x4a\xb1\x6d\x99\xe5\x6b\xb1\x79\x02\xcd\x62\xf1\x83\xff\xfb\xe9\x93\xe2\x00\x06\xf3\xfb\x4b\x1d\xb8\x77\x08\xed\x55\x30\xf9\xc3\xc7\xe6\xa2\xf8\x61\x89\x46\x73\x2f\xa0\xd6\x69\x6f\x02\xe0\x3a\xb4\xa4\xf1\x45\xbe\x80\x07\xe0\x6e\xb9\x95\xba\x69\x68\x9e\x25\xf1\x15\x4d\x8b\xa9\xa1\x64\x86\x85\x7d\x60\x83\x76\x71\x3a\x60\x2b\xc3\x53\x8c\xda\x25\x77\x6b\x2b\x73\x9f\xe1\xb4\x6e\x44\x77\x32\x08\xbd\xdc\x03\x2f\xbb\x65\x3b\x5b\x47\xb2\xea\x2b\x85\x1b\xc9\x40\x8f\xf2\xe4\x67\xdb\xd2\x2f\x85\xe0\xeb\x2f\xd8\xb3\x23\xda\xaa\x0b\x95\x62\xe7\x4e\x51\xee\x98\x33\xb3\x44\xb2\x60\xaf\x6a\x17\x0f\x67\x9b\x02\x16\xef\xee\xd6\x9b\xf7\xeb\x6d\x77\x9f\xf4\xaa\x96\xf0\x8a\x5a\x67\xad\x2d\xfc\xec\x53\xe0\x30\x5a\x5f\x66\x8b\x41\xe1\x48\x31\x1f\x41\x35\xaf\x54\x97\x6e\xf8\x12\xa0\xd8\x27\x5b\xb8\x22\x12\x83\x0b\x0b\x52\xc0\x34\xea\x6a\xa3\xdd\x48\xd2\xd2\x0a\x04\xc3\x44\x32\x48\xd6\x38\x48\x6a\xc6\x7e\xe8\x75\x5b\x4b\xb1\xa7\x10\x2c\x93\xb8\x6b\xa6\xb2\xad\x48\x23\x9c\xa6\x2c\xe3\x43\xbd\x2c\xe3\xeb\x4e\x59\x96\x21\xa3\x97\xc2\xd1\x2d\x77\xbe\xaa\x76\xba\x3e\xc3\xf2\xff\x86\x82\xfd\x6f\x9c\x32\x6d\xa0\x85\x2d\xe5\xf0\xda\x66\xb6\xd8\x35\xa6\x6f\x00\xcf\x30\x15\x0b\xeb\xdc\x39\xd1\x34\x53\xaa\xd0\x75\x4d\x7f\x7a\xd5\xe0\x7a\x1b\x1d\xb8\x16\x22\x5f\x80\x3f\x8b\xce\x55\x64\xd7\x8b\x2a\x16\xae\xad\x2f\x97\xee\xb1\x76\xdf\x4c\x63\xb7\x8c\xd8\x1a\xf3\xf9\xdc\x70\xc7\xdb\xec\x77\xd9\x7f\x70\x0f\x16\x79\xbe\xce\x0e\xf6\xf7\x57\xf9\x22\x1b\xf9\x74\xff\x32\x9f\xbb\xbf\x65\x70\x65\x8d\xc8\xc8\x02\xff\x06\xfe\xc7\xca\xcb\x17\x91\x97\x31\x89\xa9\x36\xc8\xe0\xae\x10\xbe\xd9\x63\x7f\x1f\x9e\xd3\x9c\x1f\x87\xa3\x94\x91\x3b\xf2\xfc\x25\xcd\xe0\x3f\x44\x4b\xff\xf1\xcd\x9f\x70\x1b\x7f\x4a\xe9\x51\xb9\xff\xa5\xb5\x93\x06\x76\x38\xf3\x76\xe0\xfe\xfd\xe2\xf1\x43\x3d\x78\xf8\x0f\xde\x1d\x09\xf8\x2b\x7c\x50\xc1\x5e\x89\xdf\x75\xd0\xe2\xe9\xfd\xfb\x8a\xfd\x39\x87\x35\x24\xcb\xc2\x9d\x68\x5c\xe0\xce\x99\xff\x30\xf8\x6e\xfc\x93\x24\xa4\xa3\xdf\x32\x48\x52\xf8\x8e\x6f\xa5\x89\xe6\x11\x0d\x21\x48\x42\x6a\x20\x14\x2f\x0e\xe1\x32\xa3\x10\xe5\x6c\x5c\xfb\x0f\x46\x47\xa9\x0f\x62\x1f\x4e\xd9\x87\x0b\xf1\xbb\xde\x07\xfe\xf4\x21\xdf\x93\x54\x55\x1b\x95\xa5\x0f\x65\x60\x9f\x3e\x49\xbf\x46\xd7\x51\x1c\xb2\xd9\x65\xad\x0c\xdf\x3a\xc4\x70\x01\xf9\x31\x6e\xf6\xf9\xe6\x4f\xfb\x0f\xf6\xbe\xda\xe7\xc1\xfe\x37\xbc\xb7\x59\x9e\x46\xf1\xc5\x8b\x34\x59\x3d\x5b\x78\xe9\xb3\x24\x64\x9c\x7b\x87\x0f\x47\x73\xe9\xa9\x20\xfe\xa9\xf7\x81\xc6\x9c\xc6\x4d\x91\x5d\x5f\xc6\x37\x8c\xbe\xdf\xfc\xa9\xb4\x60\x97\x41\x66\x85\x94\x3d\x1c\xf0\x76\x78\x07\x71\x69\x13\x37\xdf\x17\x43\x20\x3e\x0a\x92\xcb\x38\xa7\xa9\x88\x5c\xe2\xa3\x65\x61\x2b\x78\xf5\xca\x58\xe0\x5b\x3c\xcf\x58\xfc\xa0\x9b\x3c\xf5\xd8\x8f\xeb\x45\xb4\xa4\x30\x28\xa0\x3d\x12\x40\x78\xd3\x7f\xc2\x3a\x15\xc0\x40\x74\xef\x69\x5e\x54\xd8\xdd\x65\xaa\xfe\x27\xe4\x29\x2f\xfc\xf8\x10\xcc\xcd\x73\xd7\x34\x19\xcf\xf9\xa3\x47\xf8\xe8\xbb\x17\x2f\xd8\x23\x4d\x4b\x8c\x5c\x38\x5d\xcf\x2e\xd3\x34\xb9\xf0\x72\x6a\xa0\xd4\xe5\x0b\x9a\x52\x3c\xe7\x09\x31\xdd\xe4\xc0\x50\xf0\x82\x9c\xa6\x58\x09\xbb\xb1\x0d\x7e\x88\xe0\x80\x17\xbf\x0f\xe6\xe6\xc5\x33\xd3\x1c\x32\x09\x35\x37\xcf\xf1\xeb\xdf\x99\x71\x5e\x26\xd7\x55\xfb\x58\xed\x4f\x9c\xf2\x7c\x28\x1f\x88\x2e\x32\x00\xf6\x8b\x17\x43\x3c\x9a\x69\x0e\x61\x17\x24\xc8\xf8\x62\xb7\xc8\x38\x24\x5a\xaf\xbc\x60\xd1\xd5\xcb\x78\xe5\xe5\xc1\x82\x86\x55\x7b\x0f\x21\x89\x97\x37\xe0\xad\xd7\x14\xfb\x1d\x65\xa8\x80\x70\x19\x47\xb9\xc1\x26\x9a\x81\x97\x51\x9c\x6d\x32\x42\x94\x90\xca\x32\x8c\x48\x79\xb1\x2f\xaa\x84\xca\x86\x7a\x4f\xfa\xb9\xf6\xa2\xb4\xdd\x33\xec\x97\xc0\xf5\x4f\x82\x74\x7b\x7b\x02\xf7\x6f\x9a\x1d\xd0\xd4\x64\x05\xd9\xff\xc2\xde\xf3\x52\x85\x36\xde\x45\x19\x68\x8c\xca\x80\xa3\x70\xa5\x0b\xa5\x94\x73\xbf\xa5\x2e\xe4\x51\x1c\xd2\x0d\x1c\xc2\x1e\x51\x8a\x7d\xa9\x47\x3b\x3b\x92\xf0\xef\xee\xf2\x6a\x1a\xe1\xc7\x76\xce\xb0\xc8\x79\x53\xd8\x99\x28\xbd\x60\x1c\xe7\x94\xe1\x4f\xf7\x0e\x0b\xf6\x3f\x94\xe8\x05\xbb\x87\x0a\xfb\x51\x00\x7a\xfc\x18\x88\x59\x08\x10\x7c\x12\x3a\x24\x58\x52\x60\xc2\x85\x15\x3e\x41\x4d\x0e\x4b\xe2\x6f\xd1\x10\x02\xd4\x31\xa9\x24\x7e\xb0\xa0\xc1\x87\x77\x81\xb7\xf4\xd2\xbf\xb2\x5a\x03\xc6\x87\x37\x49\x14\xf3\xdd\xd4\x48\x80\xf2\x51\x5d\xe3\xab\xc7\x5c\xeb\x2b\xe2\xe4\x8b\x34\xb9\x86\xa3\x34\x4d\xd2\x01\xf6\x6a\xe7\x98\xb9\x42\x95\x68\xfe\xb8\xbb\x03\xbb\x15\x80\x51\x9e\x70\xcb\x3a\x20\x93\xe1\x28\x4f\x7e\x5c\xaf\x69\xfa\xcc\xcb\xe8\x60\x08\xbb\x1c\x00\x13\xf9\x38\xc9\x99\x80\x23\xb2\x9c\x2e\x3b\xec\x65\xd1\xd1\xcf\xbf\xc3\x48\x50\xd1\x09\xbd\x6a\xe6\x89\x57\xe4\x30\xf8\x32\x9b\x18\x9c\x38\x95\x15\xdc\x18\xc8\x04\x7c\x5c\xd4\xe1\x1c\xc5\x50\xe5\xc6\x35\x87\x4d\xbe\x70\x85\x78\x56\x54\x54\xb1\x45\x02\x7b\x5f\x08\xe7\x8b\x17\xae\xb0\x75\xc2\xcc\x91\x3d\xff\x26\xa7\x90\xd1\x8f\x97\x34\x0e\xd0\xd0\xe9\x11\xad\xda\x28\x44\x07\x07\xc2\x9b\x95\x9f\x2c\x4b\x45\xd2\xb5\xec\x9a\xf5\x96\xad\x76\xcb\x25\xa4\x7e\x22\x4d\x38\x81\x88\x20\xd0\x33\xb3\x44\xa9\xdc\x78\xac\x40\x02\xcd\xb0\x8c\x84\xdd\x46\xa2\x43\xe0\x1f\xde\x12\x49\x62\x71\x2c\x4d\x81\xe5\x91\x59\x03\xb1\x7b\xa8\x91\x9a\xc9\x16\x9d\x39\x32\x5b\x9d\x71\xbe\x88\xa2\xc4\x15\xc8\x4e\x39\xb2\x2f\xb6\x44\x96\x58\xb7\xed\x54\x55\x52\x85\x55\xbd\xa3\x75\x0d\x28\x65\x13\x21\x34\x55\x82\xb9\xfe\x62\x9c\x68\x3a\x4d\x25\x50\xe6\xba\xb7\x9d\xab\x96\xd7\x54\x95\xef\x1d\x54\xca\xa2\xc5\x03\xc6\x04\x6e\xad\xb6\x1c\x5c\xaa\x1e\xcb\x0d\xcb\xa3\x8c\x04\x72\xf7\xb0\x43\xf5\x1b\x16\xbd\xaa\xf6\x7b\x39\xc2\x25\xed\x53\xea\x85\xcf\x92\x38\x8f\xe2\x4b\x3c\x3c\x8b\xdc\xaf\x4c\x11\xc3\xe4\x25\xf6\xfd\xf1\x21\xa2\xf5\x8c\x39\x16\x8a\xd1\x60\xe7\x65\x7c\xe5\x2d\xa3\x10\x0b\x71\x6a\xef\x88\x6e\x95\xf4\xae\xb7\x02\x1c\x20\x06\x0a\xce\xca\x76\xce\x85\x9a\xb0\xaa\xe5\xc3\xdd\x5d\xe6\x8c\x17\x16\xaa\x01\xe6\x3e\x37\x23\xdc\x11\x64\x56\xf2\xef\x92\x31\x54\x96\xb6\x5f\x94\x88\xed\xef\xc3\xcb\x39\x5c\x53\x60\xfe\xda\xe5\x1a\x98\xa7\x6a\x40\x94\xff\x7f\xff\xd7\xff\x5d\x0c\x4b\x32\x08\xc4\xf8\x1b\x4d\xcf\x5b\x05\x77\x5a\xc6\x9f\x4b\xef\x3b\xd4\x82\x41\x25\xe5\xac\x30\x91\xc5\xd0\x92\x7f\xd8\xf2\x0f\x47\x21\xbe\x6d\x5e\x7d\x01\xab\xea\x90\x0e\xdb\x5c\x17\x94\x9d\x7b\x4b\x3c\xfc\x50\xd2\xf1\x2d\xf5\x42\x98\x47\x69\x96\x17\x54\xc2\x6e\xdd\x9e\xcd\xed\xd1\x0d\x06\x71\xd2\x26\x6f\x36\x2c\x64\x82\x37\x74\x5f\xf0\x5f\x58\x56\x09\xd7\x92\xbe\x05\xae\xed\x31\xac\x01\xe7\xa8\x10\xa8\x67\x05\x28\x64\x0b\x1c\x6a\x14\xe6\x61\xd3\x1e\xc8\xc0\x08\x9f\x66\x60\xce\x9d\x92\xbb\x2a\x07\xac\x94\xde\x4a\x7c\x25\x1b\x55\x77\xe0\x6f\x21\x82\x85\x5b\xcf\xfb\x6e\x37\x69\xbb\xf2\x6e\x20\x8a\x83\xe5\x25\x4e\x42\xd8\xe4\x42\x9e\xd2\xa8\xa8\xfc\xa2\xa0\xce\xd1\x2d\xa8\x83\xa2\x7c\x37\x02\x9a\x62\x9e\x66\xe1\xde\x24\xde\x96\x4c\x50\x5b\x47\x50\x13\x9d\x17\x4e\xb0\x3e\xff\xe0\xf7\xa4\x79\x7b\x84\x6f\x52\xd4\x15\x14\x7d\xf1\x75\x29\x8a\x26\xe3\x8e\x44\x9f\x22\xd1\xcd\x4d\x93\xec\xe6\xc6\x7c\x36\x84\x4f\x48\x91\x01\xc7\x81\x3f\x2d\xf9\xe1\x68\xf9\x81\x33\x2a\xc5\x1c\x83\x98\xf2\x14\x4c\xcd\x89\x82\x9e\x4a\x2e\xfc\x78\xfa\x62\xcf\x85\x10\x23\x65\x34\x2c\x2d\x6f\x61\x36\xc5\x09\xac\xf2\x37\x1a\x34\xe9\x37\xda\x9f\x87\x0d\x9f\x44\xf8\x1a\xd5\x68\xcc\xf1\x2b\xe1\xd5\x5d\x12\xa9\x58\x61\xd5\xb0\x15\xd9\x00\x4a\x4e\x89\x64\x63\xab\xe8\x4f\xcd\xdd\xa9\xe2\x44\xf9\x6a\x2d\x79\x23\x83\x7c\xb5\x86\xc3\xc6\x58\x32\x84\x7b\x87\x87\xdc\x28\x37\xbd\x13\xb1\x88\x91\xaf\xd6\x4d\x3f\x43\x9a\xa0\x57\xa5\x87\xbf\x67\xf0\x8d\x91\x15\x0e\x11\xc1\x9d\x2b\x9a\x66\x51\x12\xef\x1c\xc0\x0e\x06\x7d\x77\x0c\xf6\x94\xe3\xb3\x73\x20\x79\x85\xf8\x9c\x77\x57\x3c\xe7\x3f\xbe\xf9\xd3\x67\x11\xa4\x7b\x97\xac\x28\x3c\x7d\xf5\x1c\xfc\xcb\x68\x19\x42\xb2\xce\xa3\x55\xf4\x37\x9a\x66\x06\x2c\xa3\x0f\x14\xd2\xd1\x6f\x99\xc1\xa7\xc4\x18\x69\xcf\xd6\x34\x88\xe6\x51\xc0\x94\x37\x8c\x90\xe1\x6b\x2f\xcf\x69\x1a\x67\x08\x0f\x2b\xe5\x0b\x0a\xf3\x64\xb9\x4c\xae\xa3\xf8\xe2\x80\xc7\x3c\x99\xf8\x35\xce\x45\xc2\x4e\x21\x34\x3b\x3c\xb8\x5b\x2b\x30\xf2\x56\x61\x23\x8a\x5a\x1e\x91\x64\xef\xbe\xf9\x13\x67\x97\x38\x34\x59\x86\xb9\xeb\x03\x18\xeb\x33\xf2\x0e\x99\x53\xcd\x2e\x1a\x51\xe3\x7b\xd2\xef\x51\x9c\x84\xf4\xf4\x66\x4d\x2b\x67\xae\x8a\x55\x8b\x89\x47\x14\xcb\x71\xe3\xb7\x51\x7c\x91\xfc\xcf\x77\x70\x65\x8e\xdc\x91\x89\xd3\xf3\xaa\x86\x74\x96\xb4\x44\x46\x98\xc6\x02\x92\x97\x5e\x2f\xbc\x65\x03\xd2\x74\x64\xee\xf1\x40\x4c\x5a\xec\x8d\xe2\xa7\x18\xc5\xb3\x85\x97\xbd\xbe\x8e\xdf\x14\x5b\x60\x0e\x45\xa1\x51\xfd\x39\x16\x2f\x97\x48\x30\x6b\x1c\x27\x4a\x61\x31\xea\xc5\xf9\xfa\x10\x7b\x8f\x07\x89\x87\x8c\x36\x32\xad\xce\x3e\xf0\x04\x86\xac\x04\x7e\xaf\x05\xbf\x1a\xfd\x7a\xbb\x88\xe2\x84\xf5\xca\x83\x6b\xea\x83\x38\xa8\x2a\xa2\xd6\x23\x21\xd0\x82\x26\x9f\xbf\x11\x47\x54\x71\xd9\xe4\xb3\xf1\xf7\xcf\xe7\x86\x3b\xd9\x66\x49\xa4\x75\x62\xf7\xe7\x57\xc7\x3f\xe4\xf9\xfa\x2d\x1b\x32\xb2\xbc\x84\xf6\x6f\x7e\x74\xc1\x37\xb3\x8c\x7e\xcb\xfe\x6d\xdb\xc5\x16\xb9\x12\x5c\x59\x23\x73\x34\x2d\x03\x78\x17\x51\xbe\xb8\xf4\x47\x41\xb2\xda\x7f\x15\x7d\xa0\xaf\x82\xe5\xbe\x5c\x7c\xff\xf8\xe5\xb3\xa3\x93\x67\x47\xc0\x74\x58\x3e\xa8\x7c\x51\x06\xf0\x01\x00\x76\x2e\x33\x8a\xd3\xc2\x20\xdf\x79\xf8\x0d\x3e\xda\x7f\xf0\x0d\x5f\x51\x52\xb4\x2e\xde\x3c\x85\xff\xe9\x5d\x79\xef\x82\x34\x5a\xe7\xb0\x8c\xfc\xd4\x4b\x6f\x50\x41\xbd\xd4\x8f\x72\xf6\x6b\x6f\x9d\xd2\x20\x62\x76\x02\x3c\xcc\x83\x41\xf3\x28\x18\x89\xea\x5b\x76\x41\x94\x7e\x96\xac\x6f\x78\x7a\x98\x41\x30\x04\xcb\x24\x63\x78\x15\x05\x0b\x8f\x2e\xe1\x55\xb0\xf4\x2e\x2f\x16\xcb\x28\x86\x47\xaf\xdc\x60\xe1\xba\xcb\xff\x71\xb1\xf2\xa2\x25\x83\xf9\x58\xd4\x7f\xf5\xf2\x14\x8e\x36\x6b\x2f\x87\xe3\x28\xc0\x61\x5c\xac\x67\xf2\xee\xe2\xe9\xe0\xe8\xe2\x04\x5b\x35\x80\x67\x7a\x30\x60\xed\xa5\x19\x3d\xb9\x5c\xd1\x34\x0a\x8c\x6f\x8a\x45\xb6\x28\x13\x8f\xe0\x10\xf6\xdf\xef\x3d\x19\xfc\x12\xee\x0e\x7e\x19\xfd\x12\x3e\x18\x3e\xf9\xc4\xfe\xdd\x1d\x0e\xe8\xd9\xee\xde\xf9\x13\xf6\xf5\xc9\x9f\xf7\xa3\xaa\xee\xca\xcb\x17\x01\x8d\x96\x70\x08\xaf\xbc\x7c\x31\x62\xdf\xeb\x6f\xe7\xcb\x24\x49\x8b\xd7\xf8\xa3\x7a\x1f\x27\xf9\x77\x09\x0f\xfb\x88\x09\x8e\x9f\x24\x4b\xea\xc5\x4c\xc4\xfd\x28\x66\x1c\x08\xa3\x8b\x28\xdf\xa9\xea\x60\x96\xa7\x28\xbe\x78\xc5\x17\x4b\x76\x8a\xdf\xb0\x62\x26\xb9\x2a\x97\x27\xc9\x2b\x2f\xbe\x79\xce\xaa\x33\x19\xde\x11\xdb\xae\x98\x45\x64\x9a\x0e\xab\x24\x65\x76\xd5\x8b\x81\x8c\x6b\x3b\xb1\xb0\xc5\x4c\x02\xf5\xf4\xf8\xcd\x0f\x4f\xbf\x3b\x3a\x65\x50\x4c\x62\xd9\xce\x78\x32\x75\x67\x9e\x1f\x84\x74\x7e\xb1\x88\x7e\xfb\xb0\x5c\xc5\xc9\xfa\x63\x9a\xe5\x97\x57\xd7\x9b\x9b\xbf\x3d\xfd\xee\xd9\xf3\xa3\x17\xdf\xff\xf0\xf2\x7f\xfe\xe5\xf8\xd5\xc9\xeb\x37\xff\xeb\xed\xbb\xd3\x1f\xff\xfa\xd3\xcf\xff\xfe\xbf\xff\xfc\xab\x04\xf6\xbb\xa7\xef\x8e\xe0\x10\x08\x25\x4e\xf5\xf0\xf8\xf5\xf7\xbf\x16\x2f\xa4\xc7\xaf\x9e\xfe\xfc\xeb\xbb\xa7\x2f\x8e\x7e\x7d\x79\x72\x7a\xf4\xfd\xd1\x5b\x0c\xde\x92\xb9\xfc\x31\xca\xc5\x54\x36\xa9\x78\x3f\xb6\x61\x0f\xc8\x37\xd2\x33\x06\xe3\xe5\xc9\xa9\x6d\x61\xe5\x69\xab\x1a\xd4\x01\x60\x4a\xcd\x0a\xc0\x9b\xd7\x3f\xbd\xfb\xf5\xf4\xe8\x84\x39\x03\xc4\x00\x62\xb2\xff\xd9\x1f\x6a\xb3\x3f\x0e\xfb\x33\x66\x7f\x26\xec\xcf\x94\xfd\x71\xd9\x9f\x19\xfb\x83\xa5\x29\x21\xf8\xd7\xc2\xbf\xf6\x79\xd5\xbb\x77\xff\xeb\xed\x69\xd9\x6b\x3a\x35\xbe\xa9\xd0\x7e\x50\x7e\x85\x07\xfc\xd0\x75\xb4\x8a\x72\x48\xf8\x3e\x39\x1e\xb7\x4e\xe6\xf0\xfc\xe8\xd9\xcb\x57\x4f\x8f\x7f\x7d\x73\xfc\xf4\xd9\xd1\x3b\x03\x4e\x5f\xff\x7a\xf4\xf3\x9b\x5f\x4f\x8e\xbe\x2f\xbf\xbf\x79\xfd\xce\x80\x57\x2f\x4f\xd8\x0f\x03\x89\x81\x5f\xbc\x38\x94\x9b\xc8\x71\xc7\xe4\xc5\xe5\x8a\xc6\x62\x27\x76\xc2\x0c\x75\x4c\xe3\x3c\xf2\x96\x06\xe4\xc9\x8b\x68\x43\x43\xfc\x92\xa4\x2b\x2f\x17\xcb\x4a\xc9\x9b\xc2\x36\x18\xe0\xd3\x9b\x24\x0e\x8b\x14\x9e\xb1\x0c\x9e\x6e\x02\x8a\x9b\x01\xf9\xea\x4a\x9a\x5c\xc7\x30\x88\xe6\x70\xf4\xf6\xed\xeb\xb7\xef\xf0\x61\x7a\x49\x87\x23\xa9\xce\xbe\x2c\x04\x8c\x40\x47\xb3\x87\x0a\x9e\x29\x78\x68\x32\xfc\x4b\xae\x7f\x53\xb7\x84\xc5\x1e\x0e\x9e\xa9\x8a\x0f\xed\x5e\x65\x34\x98\x37\x92\xe5\xe9\x65\x90\x27\xa9\xc0\x46\x60\x52\x5a\x5d\xb1\xe9\x77\x10\x24\xf1\x3c\xba\x78\xed\xff\x56\xd8\x5f\x10\x16\x28\x8c\xae\x24\x56\x0a\xa4\xa2\x10\xf2\xd4\x0b\x3e\xf0\xc5\x25\x36\x38\xd2\xb4\x84\x69\x40\x96\x00\xd3\xd7\xd8\x5b\xd1\x22\x8d\xaa\x98\x0f\x86\x6c\x00\xa4\xcc\xd3\x06\x91\xe8\x23\x1b\xd5\x80\x47\x98\x20\xd0\xa8\x3d\x7b\x03\x87\x55\x9f\x46\xeb\x34\xc9\x13\x66\x00\xea\x85\x5e\x9f\x30\xc1\x8b\xe9\x75\x55\x74\x40\x86\xc6\x37\x0d\xdc\x1f\xf4\x7d\xe0\xe8\xf9\xcb\xd3\xa7\xdf\x1d\x1f\xc1\xf3\xa3\x17\x4f\x7f\x3c\x3e\x7d\x07\xbd\x75\x4a\x93\xad\x92\x78\x28\xa5\x3e\xa4\x73\xef\x72\x99\x17\x49\x64\x7d\xba\x4c\xae\x61\x75\x99\xe5\x9c\x40\x39\xbd\xa0\x69\x86\x3b\xc7\xc5\x0e\x52\x24\x5a\x16\x5d\x51\x48\xbd\xf8\x82\x66\x90\xe1\x66\xfb\x91\x0a\xb8\x00\xca\xc8\xed\x2d\x33\xcc\x31\xcb\xf3\x7c\x85\xe0\xe5\x90\x5e\xc6\x7b\x79\xb4\xa2\x70\x99\x31\x33\x5b\x51\x93\xf3\xbd\x09\x71\xbf\xc5\x71\xd6\xc4\xca\xdb\x44\xab\xcb\x95\xb4\xf7\x35\xa4\x41\xb4\xf2\x96\xb0\x5e\x7a\x01\xcd\x70\xa0\x65\x0e\x93\xc7\xb3\xc8\x44\xf1\x55\xb2\xbc\x62\xed\x85\xd1\x15\xaa\x55\xbd\x9d\xba\xbe\xc3\x21\x58\xa6\xca\x90\xd5\xd4\x60\x1b\x3a\xd7\x46\x13\x91\x69\x77\x41\xe3\xea\xb9\x38\x9b\xe1\xf9\xc9\x15\x6d\xf4\x81\x5b\x02\x2c\x8e\xa4\x6a\xc2\xef\xb3\x24\x6d\x43\x82\xaa\x89\xe9\x2e\x07\x35\x01\x18\xb6\xd8\xf8\xe3\x9b\xf2\x87\x09\x4f\xaf\xbd\x1b\xbe\x30\xfa\x37\x9a\x26\xad\xb2\xcf\x5f\xff\x74\x22\x7e\x10\x38\x4d\xae\xbd\x34\xcc\xd4\x25\x9f\x1d\xbd\x3c\x16\x3f\xac\xb2\xe4\xee\xcb\x78\x1e\xc5\x51\x7e\xd3\x2a\xfe\xe2\xf8\xf5\xeb\xb7\xfc\x87\x5d\x16\xdf\xd3\x16\xff\xe1\xe9\xf1\x8b\x5f\x39\xe2\x4e\x59\x3c\xa6\x5e\x4a\xb3\x1c\x62\x1a\x5d\x2c\xfc\xe4\x32\x1d\xc1\xcb\x39\x30\x7f\x32\x8c\xb2\xdc\x8b\x73\x03\x2e\xd7\x6a\x50\xbc\x5f\xe3\xdb\x80\x0a\x93\xeb\x58\x0d\xec\xe8\xaf\x47\x27\x00\x93\xdb\x00\xcb\x45\x51\x7a\x45\x63\xa9\x9c\x12\x3c\xa7\xec\xf4\x2e\xe0\xf5\xf4\x47\xc8\x9c\x09\xee\x5d\x20\x6b\x59\xb5\x5f\xfb\xfd\xf6\xf5\x8f\x27\xcf\x5f\x9e\x7c\xff\xeb\xab\xd7\xcf\x99\xe1\x74\xd4\x9a\x57\xe9\x9e\xdb\x32\x09\x47\x3f\xbf\x79\x7d\x72\x74\x72\xfa\xf2\xe9\xf1\xaf\x4f\x4f\xe1\x00\xce\xaa\x01\x1b\xe4\x11\xfb\x5c\x69\x4d\xa8\xd0\x23\x31\xfe\x0b\xd5\xf1\x69\x4c\xbd\x7c\x21\xc6\xdd\x62\xb5\x57\x8c\x6d\x59\x59\x29\xf2\x96\xcc\xd9\x44\x43\x33\x6a\x42\x3f\xa9\x9c\xc4\x03\xd8\x9b\xd6\x5e\x4b\x28\x1e\xc2\xde\x54\xd7\xeb\xaa\xdf\x7b\x6d\xa3\xd3\x85\x3f\xb7\x29\x5f\x0d\x7b\x8b\xa8\xb0\x7f\xf3\x1a\xad\x25\xe9\xc7\x5e\x85\xfc\xdb\xa7\x27\xdf\x1f\x31\x76\x35\xfd\x28\x35\x9f\x56\x51\x8c\x56\xbf\xde\x5f\xa3\xc1\xa9\xcb\x38\xa4\xe9\x9c\x0d\x68\x79\x82\x76\x08\x92\x20\xb8\x4c\xb3\x1e\xe6\xd8\x96\x03\x30\x18\x53\xf6\x65\x58\x2b\x2a\x90\xc3\x15\x36\xda\xc1\x27\x06\x73\x8f\x74\x72\xaa\x18\xb7\x9a\x3d\x90\x79\x95\x5c\x55\xe8\x17\x1a\xb4\x55\x17\xc0\x36\x5d\x80\x01\x19\x4d\x67\xd3\xc9\xcc\x26\xb6\xe3\x4e\x2c\x9b\x8c\xa7\x74\xd7\x36\xdd\x61\xb3\xee\x8b\x24\x2d\xc8\x0d\x8f\xb9\x8f\x4d\x47\x17\xa3\x86\xe3\xb2\xc3\xbc\x6d\xf1\xd9\x19\x8e\xd6\xcb\xcb\x6c\x40\x86\xb0\xf2\x6e\xd8\xb8\x9e\x2d\x93\xeb\x3a\x52\x05\x44\xee\x79\xeb\x69\x85\x0b\xfd\x3a\xb9\xf8\x69\x41\x99\x2b\x28\xb9\x8f\x18\x12\xcd\xc0\x4b\x29\xb3\x87\xa9\xf0\x75\xeb\x4d\x0b\xa7\xf7\x10\x7d\xde\x9e\xa6\x59\x11\x36\x2b\xc4\xa8\x62\xab\xfd\x67\xe8\xae\x30\xec\xa2\x38\xff\xab\xb7\x8c\x42\x2f\x4f\xd2\x93\x44\x60\x51\x73\xb0\x11\x42\xc3\x73\xcc\xb0\xce\x4b\x0c\x90\xca\x10\x7e\x8a\xf2\x05\x87\x61\x14\x4d\xa9\x5f\xef\xab\xda\xd5\x92\x29\x4f\x70\xf7\x23\x9f\x96\x5f\xa4\xde\x7a\x11\x31\x47\xf8\x66\x2f\xa3\xc1\x65\x8a\x2e\x5b\x98\x94\xce\xd2\x05\x8d\x85\x5f\x64\xb0\x9e\x78\x57\x5e\xb4\xf4\xfc\x65\xa3\x0f\xcf\xde\xfe\xfb\x9b\xd3\xd7\x20\xe2\xae\xdd\x42\xdf\x45\x4c\xa5\x5b\x84\x81\x9c\xa4\xe9\x14\x05\xde\x32\xb8\x5c\xe2\x71\x50\x74\x8a\xb0\xd4\x65\x76\x00\x1e\xfb\x0a\xed\x91\x95\x81\xfa\x78\x99\xe4\x11\xd3\xa4\xc1\x47\x38\x04\x0f\xf6\x21\x1e\x32\xb6\x14\xc0\x98\xd3\x29\x5f\xa8\x81\xb3\x84\x24\x4d\x69\xb6\x4e\xb8\x0b\x56\xf3\xd1\x94\x6d\xa4\x74\xe5\x45\xcc\xaa\xc0\x20\x6d\x01\xcf\x0e\x20\xc5\x96\xf7\x20\x86\x07\xf0\xb1\x09\x41\xef\x5a\x99\x0d\xe0\x51\x06\xeb\x24\x8b\x72\xe6\x65\x47\x73\xc4\x94\x79\xab\x21\x8d\x43\xdc\x49\x84\x29\xaa\xae\xa8\xc1\x63\x78\xd2\x93\x0e\x97\x8c\x34\xda\x58\x78\x7c\xa2\x94\xb1\x09\x51\x16\x5d\xc4\x20\x1e\x14\x2d\xb5\x60\xc9\x1f\x3c\x4b\x22\x33\x0f\x37\x01\xae\x56\xb8\x33\xf0\x43\xcc\x66\x9f\x5e\x06\x3b\x79\x7a\x19\x07\x48\x9d\xc2\xdb\xde\xc1\x11\x29\xca\xba\x80\x33\x1f\xe2\xca\x5b\x32\x5e\xe6\x09\x0c\x3c\xf8\x16\x59\x19\x4b\x71\xb4\x0e\x17\xd1\xbe\x4d\x47\xb3\x24\x85\xc1\x9b\x9b\x7c\x91\xc4\xf0\x6d\xdb\xfb\xad\x3c\xb6\x89\xa2\xcb\xab\xf5\x92\x8a\xe9\xfc\x82\xc2\xcb\xa3\xa3\x23\x98\x8e\x1d\xa9\xe9\x62\xf6\xd9\x82\x7b\xf4\xe3\xb3\xe3\x97\xcf\xd9\xf7\x19\x1c\x5d\x06\xcb\x28\x8c\xbc\xb8\x9a\x91\xc0\x47\x7e\x4e\x29\x1e\xc4\x43\x78\x00\x18\xdc\x1a\x30\x89\xf6\xfc\x6c\x10\x0f\xdb\x78\xd6\x59\xd3\x10\x25\x6f\x79\xed\xdd\x54\x12\xd5\x27\x96\xac\x7e\x9b\x6f\x06\xc7\xa2\xf6\xa4\x8d\x39\x32\xb7\x4d\x88\x66\x13\x8c\x7c\xdc\x84\x97\x32\x83\xca\xcf\xe6\x6a\x92\xbe\x57\xf3\xb6\x56\x87\x9f\x2e\xf3\x45\x72\x79\xc1\x8f\x49\x63\xb4\xa0\xae\xbd\xf5\x49\x27\x03\x6e\xb0\xa2\x37\x38\x5c\xc5\x49\x0e\x17\x4c\xb7\x2e\x33\x3a\xbf\x5c\x42\x4a\xb3\xcb\x65\x9e\x75\x3b\xa7\xaf\x5e\x3f\xff\xf1\xf8\x75\xe1\x9a\x6e\xe1\xe6\xcc\xb6\x9c\xaf\xb6\x23\x84\xfc\xd4\x0b\x15\x78\x15\xbf\xf2\xe4\x4d\x72\x4d\x53\x1d\x51\xf8\x26\x8f\x37\xaf\x7f\xfa\xf5\xcd\xdb\xa3\x67\x2f\xdf\xbd\x7c\x7d\xc2\x98\x6f\x1a\x62\x67\xf2\x75\xb4\x5c\x22\x2d\x62\x0c\x74\xd1\x50\xd1\x70\x1d\x62\x1d\xd6\x21\x8f\xca\x75\xf5\x58\xe7\xeb\xcc\xf9\x2c\xb4\x58\x8f\xe2\x8b\xc8\xc8\x70\xff\x06\xbb\xa6\x08\xa4\x8c\xca\xc9\xeb\x8a\xe6\x8b\xa4\x61\x8d\x5e\xbc\x7e\xfb\xea\xe9\x29\xae\xb1\x35\x91\x11\x53\xe7\x77\x74\xed\xa5\x6c\xcc\x3c\x80\x9d\xd1\x8e\xd1\x2a\x76\x91\x26\x97\x6b\xb9\x90\xa1\x2d\x84\x19\x6b\xed\xf6\xdb\x8c\x06\x49\x1c\x7a\xe9\xcd\xf7\x55\x31\xb3\x5d\x6c\x9e\x7a\x68\x04\xbe\x6f\xb6\xf8\xcb\xe6\xa9\xb9\x63\x94\x94\x8a\x93\x78\xcf\x4f\xa9\xf7\x81\xc9\x30\xe6\xca\xec\x01\xc5\x1b\xac\x15\xfa\xfc\xf0\x1b\x39\xc4\xf9\xbb\x7d\xe4\xd8\x12\xf3\x92\x5e\x9f\xbc\x3b\x7d\xfb\xe3\xb3\xd3\xd7\x6f\xbf\xe9\x0a\xb1\x2a\x83\x80\x68\x33\xf8\x4a\x0e\x33\x01\x6d\x93\xa9\x0e\x28\x32\xe7\xb4\x48\xa3\xcd\xf7\x8e\x57\xd0\xc5\xf2\x9a\x04\x42\x86\x16\x17\x27\xa9\x3f\xf1\x0d\x76\x9f\xca\x8a\x98\xe8\x40\x2c\x59\xa0\x3f\x5e\xc3\xe2\xcc\x3f\xaf\xce\x60\xb3\xee\xf8\x5e\x86\x4d\xc7\x23\x78\xc9\x23\x65\x06\x58\x4c\x11\x26\x4e\x15\x26\x53\x47\x5c\xcb\x58\x67\xe5\x5f\x43\x6c\x80\x0f\xc3\x86\x48\xe3\x82\xb9\x01\xd4\x80\xc8\x60\xb8\x19\xb0\xa4\xb1\x01\x59\x9e\xb6\x25\x6d\x23\x0e\x77\x3d\x6c\x4f\x8b\x63\x3c\x4e\x23\x53\xfd\x32\xf3\x2e\x68\x79\xdb\x53\x4c\x1b\xde\x7b\x34\x87\x01\xdc\x1b\xc0\xa6\x24\x72\x32\x97\x28\x3c\x44\x44\x5b\x18\xec\xef\xc3\x4e\xd5\xa3\x61\xad\x41\xe6\x88\xca\xed\x1d\xc0\xdf\xe3\xcf\x3b\x2d\x10\xac\x61\xee\x53\x0f\x21\xf5\xa2\x8c\x0e\xc0\x9a\x18\xb0\xd3\x05\x6a\xc7\x80\x18\x86\x0f\x5b\xb0\x84\xb0\xd4\xe7\x31\x82\xce\xf5\xd2\x9f\x5b\x24\xdb\xa9\xd7\x1a\x72\x6e\xe3\x62\x52\x5c\x04\x46\x0f\xe0\xef\x7e\xa3\x0b\xda\x9a\x0c\xd5\x64\xce\x83\xa6\x8a\x7a\x48\x6f\x1f\x0e\x0f\x21\xbe\x5c\xe2\xa9\xa2\x7b\xd5\xe4\x61\x00\x3e\xde\xb2\x31\x71\x0c\x88\x42\x03\x76\x18\xc4\x9d\x0e\x26\x3c\xbf\x5c\x2f\x99\xa1\x6d\x0e\xf6\x45\x43\xb1\x8e\xab\x6d\x7b\xca\x3e\x9b\x51\x06\x87\x10\x8f\xb2\x36\x89\xf9\x6b\x8a\xaf\xa9\xee\x75\x00\x87\xd8\x28\x2b\x14\xc0\x10\x9e\x30\x58\x78\xf4\x70\x08\x07\x10\xab\xab\xf1\xa8\xbb\xfa\x1d\x67\x6c\xfb\xdd\xe7\x36\x35\xb0\xbf\x03\xa6\x3a\xd5\xb9\xaf\x18\x37\x2b\x70\x55\x66\x64\xbc\x7f\x1f\x3d\x76\x13\x77\x27\xf5\x50\x81\xb0\x89\x05\x3c\x02\x13\x9e\x88\x3e\xed\xc5\x06\x9b\xed\xb3\xae\x90\x87\x6d\x04\x40\x4c\xb2\xbd\x2c\x87\xb5\x97\x2f\xd0\xcb\x29\x22\xeb\x6d\xfe\x40\xc5\xa3\xc3\xc3\x43\xf8\xc7\x3f\x62\x2d\x42\x50\x1c\xa9\x04\x3c\x73\x61\xe0\xb1\xca\xf8\x21\x44\xf0\x98\x8d\xd6\xec\xcb\xfe\x21\x2e\xa8\xd1\xdd\x5d\x95\x7e\x54\x3d\x63\x00\x34\xec\x83\x92\x85\x67\xf1\xb9\xbe\x48\x17\xbb\xa0\x83\x65\xa0\x66\x1b\xfb\x64\x39\x9b\x4f\xc5\xb0\x8b\x1b\x95\x5b\x95\x8a\x4d\x6b\x4a\x6c\xd0\x7c\x95\x0b\xd0\xa3\x9c\x66\xf9\xa0\x06\x10\xb5\x47\x18\x08\x79\xf9\x7a\x00\x1b\x6e\x5e\x51\x64\x34\x44\xe3\xa2\x90\xe5\xa9\x7c\x92\x0b\xf7\x0b\x1e\x82\x33\x46\xc9\xe0\x6d\xb1\x22\x5c\xd2\xc9\x50\x96\x92\x36\x05\xb6\xea\x9a\x0f\x87\xe0\xf3\xfc\x30\xdd\xc4\x52\x19\x85\xa3\x38\xc3\x18\x00\xef\x33\x8f\x10\x46\x19\xf7\x9b\xf9\xc5\x81\x8d\xc5\x0f\x8f\x2f\xfc\x08\xff\x9a\xd9\x1c\x85\xbc\xee\xef\xc3\xd3\xe5\x32\xb9\x56\x06\x12\x9b\x17\x0b\x72\x53\x48\xcc\x72\x25\x54\x63\x9f\xd0\x10\x92\x0e\x55\x6c\x2d\xab\xe9\x4c\xda\x13\x88\xe1\x00\x89\xa3\x61\xa5\x20\x07\x52\x01\x79\xdf\x20\xc2\x2e\x6a\xc7\x2e\xf3\xf8\xeb\xf1\x69\x05\x3c\x85\x1c\x33\xf2\x5c\x25\x51\x08\xeb\x24\x17\xc4\xc1\x1b\x0b\xd6\x29\x15\x24\x4a\xe6\x55\x70\x8f\x79\x38\x27\xde\x09\xa3\x3c\x52\xca\x71\x76\xc5\x22\x9a\x9a\xf0\xf1\x4d\x31\x91\x88\xe2\x1a\x03\x98\xcb\xcd\x5d\xfe\xb9\x17\x2d\x21\xbc\xa4\x45\xc8\xe3\xec\x88\x9e\x9f\xed\xee\x9d\x6b\x28\xbf\xad\xa5\xbc\xc7\x2c\xe5\xa7\x4f\x0a\x92\xde\x1b\x20\x6b\xde\xd2\x8b\xa3\xcd\x7a\x00\x3b\xef\xf7\x9e\xec\xc0\x2e\x0c\x80\x99\x90\x9d\x33\xf6\xbd\xd8\xf7\x20\x54\x83\x99\x2e\xe6\xf9\xec\xc2\xce\xf9\x2e\x6b\x65\x57\xc9\xa9\x9d\xc1\x93\x83\x5f\x7e\x19\xe1\xd9\x2b\x56\x76\xf8\xe4\xcf\x3b\x86\x0f\x8f\xc0\x9e\xc2\x13\xd8\x89\x76\xe0\x40\xe8\x35\x57\xf7\x2c\x4f\x87\x5a\x09\xea\xd3\x7c\x85\x8f\xa0\xe1\x30\x66\x21\xbc\x5c\xdd\x66\xd4\xe8\xb6\x0d\x7a\x83\x26\x62\x8d\xf7\xef\x63\xdd\x94\xe2\xf2\xe0\x00\xf6\xdf\x9b\xbf\x8c\xcc\x07\x9f\x7e\x19\xed\x1b\x48\x81\x22\x39\xc9\x63\x20\x63\xb5\x8f\x50\x7c\x54\xee\xca\x2d\xf7\xb3\x68\xfc\xb8\x92\xd0\xdc\x87\x8b\x70\x3d\x52\xda\x39\xa3\x71\xd9\x34\x44\x16\xb8\xbe\x49\xe9\x15\x8d\x73\x58\x7a\x39\x9b\x44\x94\x5b\x1d\x45\x7f\x13\x3c\x0b\x71\x45\x71\x26\x21\x76\x46\x29\x41\x71\x19\x17\xbb\xfb\x5b\xcd\x77\x0d\x2c\xbf\x83\xe5\xd7\x98\x72\xd1\x93\xef\x3c\x46\x3e\x14\x4b\x36\x92\xfb\x06\xa2\xd0\xeb\xbe\x3e\x2f\x56\x90\x93\x28\xce\x9f\xb4\x5d\x4d\xee\x33\x30\x24\xf1\x6c\xc4\xeb\xf9\x60\x67\xb4\xc3\xd4\xe5\x31\xc7\xb5\xea\x44\x29\x66\x6c\x1e\x8d\xe2\xa5\x98\x5f\x34\x4c\x8f\xb2\xc1\xa8\xa0\x0a\xf5\xd2\x60\x31\x80\x7d\xba\x1f\x01\x6f\xd2\xd4\x7b\xb2\x34\xa7\xe9\x2a\x8a\xab\xb5\x2d\x8d\xe1\xa2\xa8\x5d\x43\xec\x56\xd4\x26\x33\x85\xdd\x43\xd8\xad\x98\x02\x11\xb3\xe9\x2a\x09\x94\xd8\x77\xe9\xf3\x19\xe2\x80\x3b\x57\x4d\xa2\x43\xb9\x45\xb5\x6c\x5d\xdd\x09\x31\x3b\x6c\x63\x5e\xf0\xa0\xd8\x31\x5d\x03\xaf\xe0\x69\x41\x8b\x25\xf5\x30\xd4\xf5\x37\x9a\x26\x8d\xa1\x81\x3b\x84\x22\xc1\x46\x43\x4e\x23\x21\xa7\xfc\x1e\x47\x15\x23\xab\x26\xf2\xd4\x8b\x96\x9d\x6d\x2c\x69\x5c\x47\xbf\xd9\xdc\xde\xde\x92\xc6\x55\x93\x0d\xf2\x35\xd5\x84\x4d\x70\x19\x48\xc1\x96\x96\x04\xa1\x39\x6f\x2b\x65\x0b\x0b\xb5\x18\x45\x19\x5e\x79\x2c\xac\x42\xe1\xd8\x5c\xd1\x54\x6d\xd4\x58\x8b\x92\x1d\x54\x8e\xbe\xbf\x93\xe1\xe4\xce\xff\xe5\x8a\x99\xf9\xca\xe0\xb3\x6e\x0a\x73\xae\xb7\xa8\xcc\x32\x3c\xe0\x76\x55\x29\x6a\x14\xef\xb2\xde\x53\x0f\x32\xfb\xfb\xf0\x5a\x2c\x38\x3e\xd1\xa9\xd8\xe3\x72\x55\x4f\x3b\xaa\xa0\xb8\x2b\x57\xfb\xc5\x87\xcf\x25\xc4\x9c\xf1\x72\xb9\x54\x33\xec\xc7\x62\xed\xb6\x8d\x4b\x53\xef\x8a\x55\xd9\x2e\x94\xfe\x77\x7b\x17\x4a\x0d\x9d\x33\x81\x90\x09\x8a\x29\x4e\xcf\x80\xd0\x31\x77\x12\xc0\xcf\xf5\xd3\xc2\xd3\xd4\x8b\x33\xf4\xd6\x98\xc3\xa7\x2d\x46\x8b\xa4\x09\x85\x03\xad\xb7\x86\xa2\x46\xc4\x6a\x5c\x63\xe8\x37\x4f\x00\x55\x0c\x55\x2e\x4f\xe0\x82\xe6\x7c\xdf\x3f\x9e\x63\xa3\x7c\x11\xa3\x08\x33\x07\x09\x9d\xcf\xa3\x00\x57\xd2\x44\xc2\x25\x55\x0b\x11\x4e\xe9\x29\xd7\x56\xf8\xb6\xdc\x5e\xaa\x99\xcb\xcb\x36\x32\x62\xa6\xb8\x2a\xaf\xaf\x10\xf1\xb3\xa8\x9d\xf3\x5f\x56\x30\x1a\x32\x42\xf3\x03\x20\x35\x1b\xcf\xad\xb6\x52\x1d\x8a\x4f\x65\xca\xf6\x24\xa4\x8a\xa6\x1f\x76\xb6\x0d\x9c\xc3\x8a\x86\x23\xa3\xde\x4b\x8e\x84\x0e\x86\xc6\xdf\x01\x85\x91\x8c\x3a\xc0\x30\x96\x94\x0d\xee\x69\xc7\x95\xb2\xd5\x2e\xa9\xe6\xf0\xf6\x0e\x91\x08\xb7\x71\xd2\x38\x3d\x1f\x42\xb4\xb7\x87\xe3\x01\xa3\xc2\x8e\xb9\xa3\x9f\x48\xcb\xe4\x53\x7b\xdc\x4a\xa4\x55\x36\x43\xad\xe4\x3d\x0a\xde\xe8\x47\x33\x8c\xf1\x59\x1b\xe9\x86\x37\x6f\x5f\xbf\x39\x7a\x7b\xfa\xf2\xe8\x9d\x54\xa6\x5a\xca\x10\xbb\x58\xe1\xb0\xd8\xcf\xfa\x50\x55\x0a\xe7\x94\xbf\xfe\xf8\xa6\xde\x6a\xf3\x3d\x2e\xe3\x1e\xca\xee\x63\xb3\x04\xee\xfb\x3a\x04\x4b\x5f\x82\xaf\x90\x1e\x82\xad\x2f\x52\x6c\x9c\x3b\x04\xa7\xa7\x90\x40\x68\xdc\x53\x0c\x97\x4e\x0f\x61\xd2\x53\x4c\x20\x3f\xed\x29\x56\xf4\xc0\x55\x95\x13\x4b\xaa\x87\x30\x7b\xa8\x5d\x83\x78\x86\x1b\x4b\x2f\x53\x0a\x51\x3c\x4f\xf1\x9c\x62\xbe\xbc\xd9\xc3\x3d\xa9\xcc\xd5\x11\x87\x33\xf6\xae\xa3\x90\x42\x46\xf3\x3c\x8a\x2f\x32\xdd\x32\xc2\xd3\x20\xa0\x6b\x8c\x06\x8b\x23\x3f\xb8\x94\x51\x46\x36\x60\x19\x65\xb9\x21\xfc\x8c\x18\x77\x45\xac\xbc\xf8\xa6\x30\xb3\xe5\x99\x2b\x10\x99\x71\x23\x9a\x41\x92\xca\x0d\x60\xd6\x57\xe6\x8f\x65\x90\xd2\x6c\x4d\x83\x3c\xba\xa2\xcb\x9b\x03\x0d\x3e\xad\xbd\xab\xd5\x62\x45\xb5\x3c\x51\xac\xd3\x55\xeb\x13\x75\x10\xf5\x10\x87\x1e\x84\xab\x03\xd0\xd8\x80\x57\x2e\xb5\xf0\x7f\xce\xce\x65\x48\x7b\x0c\x91\x26\x3e\x0d\x22\x6c\xf5\x39\x13\xe1\xd4\x12\xa2\x89\xf0\x46\x8d\xfe\x8e\xce\x1b\x9d\xc5\x4d\x67\xd5\x47\x81\xeb\x49\x12\xef\xe1\xce\xb1\xe8\xf7\x47\x7a\x8f\x14\x58\x17\x6f\x48\x17\xf6\xc2\x37\xac\xb0\x17\x47\x5c\x3e\x95\x2c\x13\xdb\x9f\xc4\xbe\x1d\xc2\x44\xd0\xac\xc3\x10\x7b\x7b\xbe\x08\x86\xbc\x30\x0e\x35\x91\x29\x3f\x7c\x3d\x5c\x27\x32\xf5\x75\x66\x3d\x80\x0e\xb1\x15\xeb\xc1\x55\x3f\xb8\x4a\xca\x20\xde\xd1\xee\x75\xe6\x16\xff\x9a\x0b\xc9\x05\x68\x3e\x25\xfc\xdc\x2a\x5f\x5f\x51\xae\x50\xe9\x2e\x1f\xfd\x8d\xd6\xc5\xa2\xe8\x7d\xab\x7c\x7b\xb9\xb9\xbb\xbc\x7a\xdd\x59\x8f\x4f\x6b\x71\x59\x8b\x8f\x5c\x75\x20\xed\xf3\xf7\x32\x36\xc9\xe1\xa1\xe4\x6a\x2f\xbb\xe0\x8d\x30\x92\x92\xb5\xf3\x52\xbe\x9e\x86\x61\x1c\xb1\xfb\x03\xcf\x39\xb3\xa9\xc3\x50\xd3\xde\xd1\xe8\xa2\xb6\x28\xdb\x3c\x35\x30\xb0\x4c\x03\x1c\xdc\x9b\x55\xdb\x46\xd4\x59\xe7\xef\x4d\xd3\x79\x80\xdb\xfe\xeb\xc6\xf0\x00\x1c\xf8\xac\x43\xeb\xe5\x45\xcc\x26\x7b\x55\xef\xf6\x25\xeb\x9d\x51\xdc\xc8\x84\x0b\x7b\x49\x5a\xa5\x2b\xae\x75\xe4\xad\x58\xd8\x2e\x87\x93\xea\x16\x80\x8a\x64\xc1\x65\x9a\x96\x9b\x45\x33\xf5\xd2\x72\xb3\x77\x70\xd8\xc8\x3d\x2a\x4b\x1b\x26\x1e\x33\x60\xdd\x5e\x4c\x8e\xda\x67\x5e\xd8\x27\xc5\x93\xad\xed\xe7\x1e\xe6\x1f\x13\xc7\x9b\xda\xaf\x13\xf6\xfa\xcc\x3c\x6f\xbf\x61\x13\xe5\x43\x48\xd8\x1c\x57\xc4\x9c\x13\xf9\x28\xb1\xc2\x6b\x7c\x52\xef\x0f\x9f\x2b\x24\x8d\xc3\xb4\x83\xb5\xb4\xbe\x33\x80\x2b\xd6\xc6\xd9\xfa\x1c\x86\x70\x4f\x4c\x3b\x1b\x9e\x25\xff\x1c\xa8\x60\x7b\x55\x3c\x35\x6a\x02\xf5\xce\xa2\xdd\xdd\x3a\x58\x45\x54\xa5\x2e\x5d\xa5\x81\xeb\x18\x96\x5b\x9b\x7b\x76\x84\xac\x0e\x9b\xd0\x5a\x4b\xd2\x57\x8a\x25\x69\x5d\xe5\xc6\xaa\x74\xb3\x2a\x76\x7f\xe1\x65\x03\x58\xc3\x21\xec\xd4\x2b\x8b\xf5\x00\x79\x99\xfa\xca\x60\x73\xae\x57\x4f\x7f\xc6\xe5\xea\x35\xa8\xe3\xef\xad\x33\x36\x57\xed\x25\xad\x3a\x73\x52\xc6\xba\xc3\x46\xc5\x36\x9d\xeb\x0a\xab\x21\xb3\xbb\x15\x91\xeb\xa0\x6e\x47\xe3\x7a\xdd\xdb\x91\xb8\x56\x57\x4b\x61\xb7\x93\xbe\xcd\x73\x14\xdb\x92\xb7\x56\x4f\x11\xe4\xad\xbb\x76\x2d\x6f\xa9\x59\xbe\xd7\xcf\xd3\xf9\x6d\xf8\x5e\xa5\x14\xe7\x1d\x0c\x6b\x60\x77\x3b\x8e\x35\x2a\xdf\x8e\x65\xf5\xca\xda\x6d\x19\x3c\xa4\x91\x61\x8a\x8c\xc1\x95\x7e\x65\x4a\x94\x93\x58\xce\xec\x26\x92\x08\x79\x2f\x18\xdf\x94\x8b\x33\x72\xbe\x95\xf2\x15\x9f\xda\xa1\x13\xd6\x84\x7a\x59\xb9\x51\x9c\x9f\xf2\x60\x8d\xe9\x8b\xb7\xed\xaa\x1c\xb1\x6b\x08\x33\xef\xd6\x36\x48\xd7\x4f\xc9\x0c\xea\x18\x31\x63\xcc\x57\xd7\xf6\xae\xe0\x00\xae\x00\x13\xb3\x6d\x11\x4f\x50\xe9\xc1\x99\xe6\x94\x2f\x34\xe3\x78\xe5\xc1\x95\xf6\xcc\xe1\x2e\x13\x07\x0e\x51\x37\x2f\x10\x5a\xa1\x9c\x1b\xf4\x6a\x07\x47\xf3\x96\x66\x0c\xeb\x04\x5e\xcc\xaa\xf9\x14\x17\x02\xb6\xab\x73\x4b\x93\xc7\xea\xfc\xbe\x6a\xb3\x47\xba\xf5\x86\x6c\xaf\x37\xd5\x19\xa0\x7e\xa5\xa9\x8e\xc0\xfc\xa7\x68\x0c\xd6\xbe\xe2\x8a\x20\x9f\x5d\x1a\x48\x88\x6d\xaf\x38\xec\x53\x22\xd5\xdc\xa1\xc7\x10\xd9\x85\x9d\x86\xb4\xec\x18\x0c\xe0\x1d\x75\xb0\x79\x12\x4c\xa5\x7d\x62\x0e\xdc\x9a\xb6\x2a\x26\xad\x5d\x43\x07\x87\xd2\x99\x28\x62\x8b\x61\x00\xa1\x74\xcb\xf1\x15\x2e\x86\xdd\xbb\x77\x05\x9f\x3e\x89\x1f\xa4\xfa\xaa\xdf\xef\xd2\xb5\xb9\xaa\x76\xc6\x68\x50\x9d\x7d\x62\xad\x0c\xe1\x89\xe6\x5c\x11\x1c\x28\x4f\x34\x69\x17\x3f\x64\xa6\x6b\x76\x54\xd4\x44\x41\x64\xe0\xb8\xbb\x00\xf0\xc6\xda\x2c\x17\x21\x8b\x2f\x64\xb9\x80\x72\x5b\x96\xd7\x60\xf0\x53\x56\x70\x19\x97\x07\xa7\x0e\xe0\xef\xfc\x61\xb7\xa4\xf0\xc6\x7f\x1f\x49\x29\x4f\x6b\xdd\xbb\xc7\x80\xdc\xbf\x5f\xa0\x59\xcd\xaf\xc4\x03\x39\x5f\x93\x46\xdd\x05\x26\xf7\xef\xc3\x3d\x01\xb7\x5a\x93\x94\x75\x7f\xa7\x4d\x8a\x9d\x22\x39\x8c\x92\xfd\xff\x49\x32\xc5\x3b\xd1\x96\x29\x39\x84\xa5\x99\x35\xcc\xb6\x9a\x35\xc8\x80\x6e\x37\xd8\xca\x35\x6f\x37\x7c\x4a\x35\xb5\xf3\x85\x59\xe7\x58\x51\x3f\xda\xb2\xed\x6c\x41\xaa\xd5\xa6\x68\x3d\xa6\xf7\x85\x13\xde\x3a\xb0\xdb\xd1\xb5\x5e\xf7\x76\x94\xad\xd5\xbd\xf3\x6c\xb7\x79\x8c\x66\x5b\xfa\xd6\xea\xb5\x29\x2c\xc2\x6a\x45\xa4\x53\x4f\x01\x51\x50\x90\x8d\x17\xef\xef\x3b\xaf\xd5\x6d\xa3\x84\x3d\xb9\xaa\x9b\x12\x8d\x2a\x97\x47\x76\xae\xbe\x9a\x3d\xd8\xa9\x77\xea\x0b\xbc\x0d\x8e\x5c\x83\xc8\xc5\xce\x50\x69\x31\x50\xbf\xb4\xf4\x56\x3e\x97\x52\x6d\x3f\xbd\x5e\x24\x19\xad\xf6\xda\xe6\xd2\x09\x30\xb1\x10\x54\x06\xcd\x74\xeb\x4c\x55\xd2\x20\xed\xb1\x95\x9e\x38\xe0\xca\xdb\x34\x83\x80\x45\xe7\x56\xde\xe6\x75\xfa\x2a\x8a\x07\x52\xf0\x0e\xde\x8c\x96\x39\x0c\x1f\x7e\xa5\xee\x8a\xa3\xfa\xff\xc4\xee\x46\xf1\x2d\xbb\x7b\x71\xd7\xee\x46\xf9\x02\xbc\xe2\x54\x35\xef\x36\xfd\x78\xe9\x2d\x99\x75\x4b\x52\xb8\xc0\x73\x4b\x29\xdf\x03\x64\xe2\xee\xde\x25\xcd\x32\xb1\x27\xc8\xa8\x75\x3b\x16\x3b\xa4\xc3\xb5\xc1\xaa\x36\x62\x64\xd1\x1c\xc2\x35\x23\x69\xb2\x8a\xf2\x9c\x86\x46\x33\xcd\xcd\x00\x77\x41\x66\xb8\x71\xa9\xd8\xba\x25\xc3\xc7\x5d\x5c\x18\x72\x5f\xa7\x49\x78\x19\xd0\x70\xa8\xe3\xc0\x59\xb8\x96\x0e\x3a\x3d\xaf\xb5\x33\xda\xd2\x84\xd7\x00\xee\x70\x02\x0d\x86\x4d\xa4\x5b\xa6\x3c\x5c\xcb\x56\xa9\xa3\x66\xc3\x90\xeb\xeb\x6d\xe9\xa4\x29\x05\x49\xf0\xf5\x10\x06\xdd\x01\xf4\x75\x72\x6d\xfd\x3a\xb6\x31\xfb\x99\x65\xca\x1f\x45\xbc\xa0\x90\xa4\xb1\x0d\x7e\x94\x97\xd3\xf9\xd8\x10\x9b\x75\x4c\x78\x74\x88\x5b\x85\x67\xa6\x39\x25\xb3\x99\x35\x76\xa6\x8e\x39\x9b\x59\xad\x01\xf2\x19\x6e\x7f\x8d\xe6\x3c\x03\x5e\xd9\x63\xc1\x5f\x79\xfb\x99\x6d\xf1\x4b\x9b\x39\xc5\xc2\x64\x15\xd3\xac\x9d\x42\xe2\xe5\x1c\xa2\x1c\xc2\x04\x13\x0e\x65\xd9\xe5\x0a\x13\x88\x2c\xa9\x97\xe5\x02\xdb\xba\x00\x19\xfc\x60\xc0\x75\x94\xd1\x56\x79\xdb\xc4\xf2\xad\x36\xcc\x8d\x23\x48\xc3\x84\xd9\x7a\x6f\x9b\x06\xa6\x84\xae\x9e\x58\x78\x81\x25\xcf\x40\x27\x9e\x60\xbe\xb8\x51\x8b\xec\xbc\x2b\x63\xdb\x8f\x72\x31\xf9\xa9\x13\xe2\x41\xc1\x18\x71\x1b\x03\x82\x6c\x8c\x0e\xcd\xb5\x84\xd2\x4e\x88\xfc\x82\x03\xd0\xc0\x44\x83\xd1\x00\xd6\x5c\x3c\x28\x16\x0a\x5a\x78\x49\x54\xf8\x04\x26\x7f\xe2\x8a\x2b\x13\x54\x7b\xdf\x15\x00\xdc\xaa\x7a\x7b\xc9\xa1\xc8\x5e\x5e\x62\x13\xae\x55\x23\x2a\x5e\xba\x8c\x5b\x8a\xa9\x01\x1f\x0c\xb8\x6a\xaf\xce\x80\x7e\x25\x88\x7d\xf8\xee\x36\xf5\x3b\x86\x6e\xeb\x98\xc6\xeb\x93\x23\xd5\x6e\xac\x10\x73\xfd\xae\xb5\xc7\xdf\x98\x51\x2c\x5c\x2d\xe2\xe0\xb4\xb6\xb5\x62\x17\xae\xd5\x01\x96\x0f\x70\x58\x66\x93\x64\x90\x60\x5f\xda\x96\xa5\xc0\x85\x79\x21\x7c\xa2\xd0\xb5\xa1\x50\xdc\x3f\x96\x41\x76\xb9\x5e\x27\x29\xa6\xaa\xe0\x66\x65\x74\x41\xf3\xb7\xc8\xaa\xbf\x6a\x4e\x6e\x14\xad\x48\x53\x33\x75\xd5\xee\xbd\xfb\x1e\x1c\x6a\xea\xf1\x13\x18\x3f\x46\x71\x6e\x5b\x3c\x70\x06\x1f\xe0\xc1\x21\x58\x5b\x6d\x86\xe3\xbb\xdf\x3e\x3c\xec\x6e\x5d\xd0\x41\x18\x86\x83\xbe\x72\x42\x09\xd6\xc9\xf5\xc0\x32\xc0\xb6\x86\x4c\xa7\x99\x28\xcb\x8f\x2d\x32\x1c\x56\xb7\xe9\x58\xc3\x3e\xa0\x84\x7d\xf8\x5f\xed\x17\xae\x27\x85\x41\x2e\xbf\xdc\x11\x5f\xbc\xfe\x88\xdc\x0a\xc9\x6d\x3e\x1a\xfc\xfb\x40\x8b\xe1\xa6\x30\x93\x6a\x49\x2b\x3e\x62\xb5\xf2\x1c\xcd\x07\xaf\xb7\x0b\x03\xef\x0c\x37\xc2\x9f\x17\x3d\xeb\x90\x0e\x28\x46\x30\xe6\x6c\x33\xb3\x92\x79\xab\xf5\x12\xd3\x46\xf7\xe1\xc9\xc6\xb4\x2b\xc5\x98\xd6\x57\xf1\x4d\x9a\xf8\x9e\x1f\x2d\xa3\xfc\x86\x8d\x64\x39\x5c\xc1\xe3\x43\x98\x51\x32\x36\x5a\x39\x44\xda\xb5\x6b\x4d\xc1\x7e\xab\x75\xf8\xc7\x21\x98\x23\xd3\x34\x5d\x03\xa2\x11\x1d\x01\x81\x28\x06\x62\x8d\xbb\x09\x2f\xa2\x22\x02\x91\xde\x1d\xa2\x20\xce\xed\x6d\xab\xaa\x56\xe7\x5e\xd1\xe2\xe3\xf1\xdb\x30\xfd\x33\xb3\xe3\x4c\xa6\x54\x98\x33\x99\x55\x20\x3d\x15\xf4\x5b\x2c\x9b\x1f\x89\xb3\x87\xe0\xce\xea\x9f\xad\x6b\x0f\xae\xe0\x5b\xcc\x55\x3b\x64\xbf\x6e\x09\xa4\xd8\x3d\x5a\xc0\xd8\x86\x74\xb8\x33\xd7\xea\x21\x82\xf6\xad\xfe\x0d\x1b\x27\x3f\xc0\x3e\x03\xad\x1b\x35\x8a\x44\xe4\xed\x41\x83\x0f\xee\x78\x57\xae\x5a\x8d\xe5\x15\x81\xd6\xb8\x21\xd5\xee\x3d\xef\xe5\x5f\xce\xe7\xcd\x94\x2c\xd2\x47\x1a\x55\x24\xa8\x62\x00\x99\x7e\xed\xe1\x43\x5c\x3f\x67\xd6\x9c\x42\xc7\x35\xea\x2f\xc4\xe3\x5e\xa3\x2d\x55\x12\xee\xa5\x25\x41\x12\x66\xd2\xf9\x1a\xa3\xca\x5d\x0d\xf6\x9d\x0c\xe1\x15\x06\xf6\x07\x5c\xe7\xef\x83\x4d\x80\xfb\x80\x75\xd2\xe1\xf5\x8e\x95\xaa\x37\x0a\x68\x4f\x55\xd6\x3f\x05\x00\xab\x01\xa0\x06\xdd\xae\xbd\xbc\x25\x64\xe7\x1c\x6f\x39\x99\xd4\x20\x8e\xf1\xa1\x8b\xcf\xf8\x93\x89\xee\xcc\x44\xf1\xb9\x83\x15\x56\x88\xf5\x74\x38\x0a\x92\xf5\xcd\x80\xf9\xc1\xad\x03\x5e\xcd\xcf\xed\x2d\xe3\x7f\x8e\x6d\x9b\xfe\x7e\xb6\x4d\x03\x7a\xeb\x00\x1e\x54\x41\x3c\xe2\xdc\x7e\xe1\x40\x8d\xa4\xfa\xd8\xf5\x8f\x19\x95\x27\x6f\x07\xc5\xd2\x48\x91\xa9\x0f\x92\xb4\x68\x29\xca\x64\x0c\x30\x16\xd3\x99\xd6\x0f\x84\xfc\xdd\x8b\xb4\x36\x4e\x61\x0b\x75\xf4\x60\xea\x5d\x9f\xcb\x0e\xba\x4e\x79\xcc\xc5\x9a\xad\x10\xfb\x80\x6f\x40\x3b\x2c\xc5\xe4\x0b\xa8\xc6\xa6\x4a\xc1\xd9\xde\x5e\xa4\x70\x11\xc2\x35\x7c\xdb\x79\x6c\xa7\xba\x1b\xbf\x3a\x3b\x28\x0e\xd3\x89\xf4\x9b\x59\x3d\x01\x5f\xd8\x4c\xb7\x5b\xf4\xee\x03\x1b\xd9\xc2\xb5\x96\x68\x57\x3c\x3a\x8e\x59\xe3\xcf\xa4\x13\x2f\xe1\x5a\xe3\xda\x04\xdc\x59\x92\x66\xf5\x4c\x96\xaf\xd0\x8c\xaa\xc2\xd1\xca\xce\xbd\xa5\xab\xe4\x4a\x3a\x17\x49\x8b\xe4\x6f\x22\x1d\x7b\x4a\x55\xb9\x8e\x25\x51\xe0\x58\x1c\xe2\xc1\xcc\x60\xb4\x4e\xd6\x83\xa1\x01\xd1\xde\x9e\x72\x4c\x15\xc7\x59\x34\x27\xf1\xa2\xf2\xb8\xa9\xb2\xbb\xb8\x7e\xde\x7f\x9c\x4d\xe7\xa5\x88\x9e\x16\x87\x4c\x35\x1d\xe5\xa9\x65\xc3\xdf\x2e\xb3\xbc\xca\x62\x5a\x32\x78\xa9\x39\x37\x56\xe5\x2b\xd9\x23\x48\x13\x53\xa2\x09\x5e\x2e\xca\xa8\x42\xe5\xd3\x58\x3a\x97\x03\x05\xee\x32\xce\x45\x32\x3f\x39\x73\x5a\xeb\x64\x5b\x80\x02\xa7\x3e\x3f\x8b\xe9\xb0\x47\xa3\x2e\x7c\x23\x9e\xf2\xed\x0a\xf5\xc3\x3c\x7f\xc8\x07\x1e\x62\xb2\x2f\x22\xc5\x4a\x84\x37\x38\xeb\x10\x15\x74\xca\xe5\x1c\xbd\xbd\xb4\x2a\x59\x2d\x1d\x23\xab\x51\x06\xf6\x54\x47\x9d\x15\xe2\xcb\xcc\x8b\xee\x9c\x22\xbe\x63\x22\x13\x68\x13\x28\xb1\x22\x8d\x15\x2b\x69\x51\x64\x38\x18\x3e\xac\x9f\x92\x7a\xf3\xf6\xe5\x5f\x9f\x9e\x1e\xc1\x8b\x1f\x4f\x9e\x9d\xbe\x7c\x7d\xf2\xae\x71\x88\x4a\x98\x09\xaf\x4c\xb6\xc5\x43\xfc\x8c\x51\xbe\x97\xd1\x97\x98\x37\x44\xf7\xf6\xf5\xa5\x74\xe8\xb1\xba\xe1\xb6\x75\x5a\x5e\x94\x35\x04\x48\x83\xe7\x7e\x54\xc5\x6f\xc3\x22\x00\x96\x1a\xb0\x31\x60\x13\x18\x70\xa3\xde\x10\x2d\x9f\x97\x87\x9d\xd1\x0e\x0c\xdb\xe5\x30\x96\xd5\xb8\x36\xa2\x4d\xd8\x55\xcf\x7e\x4b\x9e\x06\x85\x13\x03\x33\x5c\xc8\x47\xf2\xf3\xe4\x38\xb9\x2e\xee\x18\x6e\xc7\x98\x4f\x92\x78\x2f\x52\x1d\x39\x17\x02\xf5\x58\xb7\x81\xe0\x43\xcf\xb2\x23\x14\x67\x73\x8b\x14\x82\xe5\xbd\x35\x6d\x01\x6e\x2e\x7b\x6a\x53\xd6\xe8\x72\x0c\x34\x4b\xdf\xb4\x82\x89\x9c\x3c\x8a\xa2\x1b\x38\x84\x1b\x8c\xdd\x48\xa7\x20\xf1\xec\xb3\xa2\x70\x13\xd1\x0f\xdd\x63\x1b\x43\xda\xc3\x65\x96\x6a\xe5\x02\x73\x2c\xc6\x90\xd2\x2c\xe7\x31\x77\x5a\x9e\xa6\x80\xb5\x97\xe6\xe0\xdf\xf0\xb4\xaa\x22\xa1\xad\x0a\xbe\x48\xf6\xe8\xdf\xe0\x35\x15\x78\xcc\x17\x1d\xa5\x90\xeb\xc2\x9a\x31\xbc\x4d\xe5\x1b\xd4\xdc\x3c\xf9\x8e\x0b\xfb\xa0\x48\xfe\xcf\xef\x5e\xe5\x87\x79\x4f\x8b\x58\x15\x1e\x85\x1c\x1a\x78\x12\x72\x28\xd2\x49\xf0\x7a\x4a\x7a\xa3\xc1\xb8\x19\x05\x5b\x66\x27\x28\x07\xff\x05\x2d\x4e\xc2\x33\x3a\xa9\xe4\x70\xd3\x40\xba\xd4\x57\xa6\xa6\x1a\x94\x28\x7a\xa0\x87\xb0\x09\xd4\x07\xfd\x15\x23\xb4\x36\x73\xc1\x43\xd8\x30\x37\xe7\xc3\x39\xf0\x91\x67\x23\x86\xe3\x66\x9b\x3c\x8b\xd4\x06\x07\xa9\x72\xdf\xff\x8e\xd9\xcc\xaf\xd4\x33\x24\xef\xed\x51\x65\xe2\x88\x76\x49\x71\x34\x5e\x61\x88\xcb\x53\xe6\x2a\xd1\x41\xc3\x86\x59\x80\x69\x28\xce\xd1\x60\x62\xe3\x20\x2f\x93\xa2\xaa\x8e\xc5\x66\x22\xc9\xac\x5a\x7f\xc2\xe8\x0a\x13\xe2\xdc\x18\x18\x7d\x4f\x57\x9d\xc2\xb2\xe1\x67\xfa\x55\x43\x08\xbe\x48\x55\x19\x39\x30\x0b\x40\xa7\x58\x85\x98\xb4\x60\x97\xd9\xd4\xdd\x56\xd6\x02\x91\x5d\xb4\xcc\xfb\x8a\x2e\x80\x88\xe4\x95\x2e\x41\x71\x16\x89\xdf\x89\x95\xcc\xe5\x37\x0b\x2f\x2f\xd2\x97\x17\x49\xb0\x9a\x57\x40\x44\xc8\x8f\xb3\xb0\xe1\x46\x31\x41\x2c\xa8\xb1\xdf\x8c\x25\xb1\x1e\xa7\xf0\xe9\x13\x84\x28\x12\x9f\x3e\x21\x04\x3e\x2b\xbf\xa7\xcc\x79\x80\x35\x56\xf0\x08\x1c\x4c\x20\x13\x15\xc5\x58\xdd\x94\xef\x3b\x19\xe0\x90\xc1\x93\x2e\x89\xaf\x03\xe4\x21\xdf\xbc\x69\xc3\x01\x8f\xee\xeb\xe6\x0d\x07\xcc\xee\xc3\x07\x56\x3b\x62\x95\x99\x8f\x5d\xc0\x74\x78\x3b\x25\xe0\x09\x6b\x10\x71\xde\x63\x38\xdf\xc7\x9d\x67\xfa\x29\x65\x1b\x1b\x17\x0e\x60\xaa\x58\x6b\x40\x45\x61\x54\xc1\xad\x6c\xa5\x62\xa9\x4f\x73\x93\xf7\x7b\xe1\x5a\xb1\x8f\x0f\xca\x91\x23\x85\x27\x0d\x7b\xb7\x43\x76\x0c\xd8\xc3\xa9\xc3\x01\x57\x54\xb9\x96\x5e\xef\x0a\x9b\xc2\xe4\x5e\xb3\x24\x94\x76\xad\x06\xbd\x2d\x64\xf0\x72\x8d\x12\xb5\xa2\x5e\x2c\xce\x6e\xd1\xab\x28\xb9\xcc\x84\xc8\x61\x2e\xea\xa4\x2e\x70\xe8\x47\x67\x09\xa8\x06\x51\x28\x4d\xd6\xde\x9e\x90\xb7\x87\xb0\xbb\x8b\xd6\x2b\x3c\x87\xc7\x50\x3e\xec\x9a\x60\xa2\x00\xf3\x21\xb8\x7b\x5a\x79\x2f\xec\x8d\xa3\xec\xee\x76\xa4\x09\xe4\x8d\x8d\x2e\x63\xee\xc8\x93\xce\x9c\x07\xdb\x3d\x55\xcf\xc5\xb6\x4b\x53\x03\x25\xf5\xea\x03\x07\x4a\x1e\x33\xff\x0f\x75\x53\xaf\xa3\xd1\xc5\x08\xce\x1c\x03\xf0\x66\xb1\xf1\x39\xf8\x34\x48\x56\x34\x03\xc7\x9f\xeb\xda\xe0\xcb\xa4\x42\x34\x77\x76\x70\xea\xcf\xbc\x89\x22\x0b\x42\x99\xf4\x2c\x58\x78\xe9\xd3\x7c\xc0\xd8\x22\xce\x8e\x69\xb3\x10\xd5\xa5\x1b\x87\x49\xda\x9f\xfb\xe9\xb4\xba\xff\x0a\x53\xc0\x79\x61\xc8\x33\xa0\x47\x17\x0d\x19\x2b\x6e\xac\xcf\x53\x6d\xc6\x83\x37\x34\xc5\xf4\x24\x65\x8a\x6f\x71\x0f\x94\xc8\xe6\x4c\x43\x94\xc1\x11\x3c\x63\x0d\x86\xc2\xd3\x41\x99\x96\x5c\xf2\xaa\x55\xf6\xb2\xb5\xa1\xa2\xd9\x81\xa7\x7c\x37\x41\x5c\x9c\xa1\xd8\x20\xbc\x0f\x8d\xa1\xbc\x00\xb1\xba\x5c\xe6\xd1\x7a\x79\x83\xc3\xd5\x07\x3e\x4c\x29\xe5\x98\xf9\xfa\x2b\x03\x72\xba\x5a\x1b\xb0\x59\x26\x06\x6c\x16\x91\x66\x61\xdb\x4b\xd3\x1b\xfd\xba\x37\x0e\x0b\x42\x9c\xd4\x25\x3e\x2c\x13\x74\x58\xbe\xad\xee\xa0\xd3\x14\x5c\x14\xb1\xb5\xea\xb2\xba\x4f\x4a\x55\xe5\x62\xb6\xc1\xa6\x45\xf6\x54\x91\x67\x43\x9b\x7b\x0e\x91\xd8\x9c\x45\xe7\x32\x1e\x9a\x4c\x1c\x88\x07\x96\x6d\xa1\xa2\x2a\xcf\x26\x30\x0c\xf7\x07\xd8\xca\x2e\xd6\x7f\xc0\xba\xad\x2e\xce\x88\xce\x6a\x2c\x93\xb2\xc6\x00\x06\xb0\x92\x11\xc3\x78\x8c\xfc\x6b\x97\xf3\x41\x13\xd4\x11\x2c\x1a\x70\xd8\xfb\x9c\xef\xfc\xb0\xc3\x2e\x82\x6e\xf4\x03\x9f\x0b\x94\x17\x8a\x79\x33\x52\x81\x47\x8a\x10\xe2\xb7\x08\x71\xeb\x1c\x80\x88\xcf\x10\x36\xa5\xf9\xe3\x0f\x14\x8c\x14\x5a\xb7\xe9\xd4\x63\x69\x66\xbb\x5a\x7b\x29\x1d\x88\xad\x1a\xde\xb1\x01\xfe\xb1\x56\xbe\x23\x03\x82\xd5\x5a\x33\x86\x81\x77\xcc\x5c\x0b\x4d\x75\x24\x2a\x72\xc9\x3b\x66\x43\xcb\x31\x3c\x01\x02\x07\xb0\xa7\xca\x5a\xd7\x19\x43\xaa\x0c\x22\x87\x67\xf2\x38\xa8\x77\x2c\xf2\x8e\x75\x85\xd0\x39\x9e\x8c\x0d\x0c\x53\xf6\x6f\xdf\x90\x24\x70\x66\x45\x1f\xf3\x1a\x7a\xbc\xe5\x0f\x26\x5b\xff\x1a\x23\x54\xf3\x49\x71\xb3\x37\xe3\x43\xbd\xa4\x9a\xc3\xd9\xa5\x9f\xb3\xe9\x62\x8d\xc5\x5d\x46\x2c\xd2\x0c\xe6\xfb\xfb\xf0\x4e\xc0\x02\x9f\x5f\xd4\xe6\xe9\xe3\x91\xde\x71\x97\xed\xf0\xce\xbc\xe3\x73\xd8\x53\x26\xd3\x03\x61\x02\x79\x99\x47\xe0\xe3\xbf\x9c\xea\x1a\x73\xc1\x8b\x1e\x02\xd3\x3e\xec\xdb\x6e\xd1\x02\xaf\x7d\xcb\x28\x6c\x47\x02\x3c\xb9\x8b\xf7\x3c\xe6\x61\xde\xbf\x2f\x1f\xe1\x26\x0f\xc1\x2b\x22\x8d\xfd\x63\xe9\xe6\xa0\xbc\x10\xc5\x80\x9b\x83\xe2\xd2\x10\xe5\x30\x5a\x0d\x6a\x8a\x89\x93\x96\x99\x01\x1b\x8d\x78\x7e\xf7\x55\x92\x52\x03\x62\x03\xb7\xd8\xf1\xbf\xc7\x06\x7c\x34\xe0\x63\x60\x40\x4a\x57\xf8\xe7\x18\xff\x9a\x06\x6c\x22\x03\x36\xc7\x06\xdc\x04\x9a\x51\xea\x86\xbd\xfc\x9b\xfa\x5d\xc6\x87\x11\xe6\xb9\xdf\x8c\xb2\x52\x63\xd4\x85\x8b\xd9\x9d\xa6\x99\x80\xc7\x09\x34\xae\x54\x84\x59\x90\x4e\xbc\x13\x43\xba\xc4\x2a\x05\x53\x13\xda\xbe\xb7\x09\xa4\xc9\x01\xfb\x76\x13\x88\x7f\xb4\xb3\x05\xd0\xe5\x7a\xd7\x99\x99\x6a\x23\xe6\x89\x77\xc2\xda\xa5\x12\x96\x49\x0a\x7e\x92\x2f\xea\xd8\xea\xf6\xd6\xdc\x63\x44\x44\xf4\xf8\xbf\xcc\xad\x83\x27\x8c\x26\x7c\x0e\x85\xe1\x6d\xe0\xb8\x1f\x60\x5f\x86\xf0\x04\x5b\x3d\xe8\x5c\xa5\x17\xe8\xfd\xbf\xff\x8f\xc9\xd0\xdb\xb0\xe9\x3d\xfb\x9e\xa4\x70\xc3\xbf\x17\xd8\x21\xbe\x69\x51\xba\xca\x24\x9c\x15\x05\xb5\xa8\x23\x5f\x65\x2c\xcd\x82\xdc\x4f\x20\xc3\x24\xbf\x07\x90\xc1\x3e\xa8\x97\xdd\xb7\x4b\x8e\xfb\xb1\x15\xb4\xcb\x14\x15\x3f\x32\x09\xfa\x58\xe6\xca\x6b\xbe\x16\x91\x02\xd8\x83\x1b\x55\x8e\xf9\x8c\xef\x1d\xdc\xe5\xd9\xe8\x74\x43\xdf\x3d\xad\x12\xb2\x0f\xbe\x3c\x04\xbd\x7b\xc4\xde\xfa\x51\xfe\x82\x2f\x1f\x31\x6c\xe4\x6d\x84\xcc\x8a\x95\x2f\x6f\x1a\x2f\xd5\x00\x31\xfa\x22\x97\x53\x7a\x5a\x3a\x23\x88\xb1\xc2\x72\x25\x41\x04\x31\x12\x5c\xd9\xc0\xfd\xe5\x94\x3b\xe9\xb5\x7c\x21\x90\xcc\x41\x9d\xc9\xf2\xb4\x9e\xf7\xaf\x5c\x46\x29\xb9\x96\xf1\xc1\x44\xf2\xe8\xb1\xcd\x85\xd7\x13\x6e\x83\x66\x42\xd2\x1b\xb1\x08\x06\x7c\xfa\x83\x0a\x6e\xc2\xb0\xcc\x47\xaa\x64\x1d\xaf\xf4\xb8\x51\x07\x86\x40\xf7\xf6\x74\xec\xce\x3a\x57\xc8\x3e\x8a\xb5\x75\xdd\x0c\x15\xb7\x4e\xf3\xeb\xe3\x6e\x9b\x0e\xf2\xb8\x1e\xa3\x54\x95\xb9\x61\x65\x6e\xba\xcb\x44\xfa\x03\x9f\x99\xd8\xbf\xa4\x0b\x46\x9c\x24\xe9\xca\x5b\x46\x19\x65\xfa\xcd\x26\x4e\x37\x01\x64\x09\x2c\xa2\x8b\x05\xcd\x72\x48\xd2\x90\xa6\x22\x1e\x91\xcc\xd9\xcb\x28\x83\xc7\x3c\x9e\x05\xfb\x60\x8d\xd4\x80\xe3\xfa\x02\xaa\x28\x3d\x10\xa6\x8d\xa7\x81\xec\x58\x00\x3b\x49\x72\x88\x69\x40\xb3\xcc\x4b\x6f\x0c\xf0\x2f\x31\x32\xb7\xf0\xe2\x70\x49\x21\x09\xf9\x2c\xb2\x48\x58\x79\x53\x58\xa4\xb2\x1d\x0b\x75\x4c\xb3\xc3\x71\x7f\xbf\xb8\x31\xe1\x31\x8f\x2c\xc5\xbb\xbb\x98\x40\x9e\x59\x37\x0e\xeb\x91\x0c\xa8\xe3\xb4\x34\x07\xd1\xe5\x71\xe2\x68\x57\x4d\x39\x6f\x02\x1c\xb0\xb9\x71\xd1\x3b\x93\x9b\x7a\xad\xcd\x76\xb5\xb6\x11\x14\xd8\x52\xe8\x34\xf9\x1a\x37\x4c\xd0\x6e\x8e\xd5\x75\x52\xba\xe2\x80\xab\x7c\x9a\x37\xc7\xfa\x44\xfa\x2b\x86\x46\x4a\x57\xfa\x4c\xbc\x20\xe6\xf7\x61\x28\x36\x04\xe4\x09\xac\xbc\x0f\xf2\x85\x65\x5e\x06\xcb\x24\xbe\x60\xff\x2a\x7d\xad\xe2\x53\xb8\x78\xd8\xec\x23\xd6\x05\xf6\xf5\x8c\xfd\xe4\xfb\x21\xb4\x87\xd8\x6f\xfe\xc6\xa9\x5a\xcc\xa1\x35\x85\xca\x79\x9c\xa9\x2b\x12\x98\x20\x86\xf6\x8e\xe3\xb3\x37\xc1\x19\x39\x97\xf5\x0b\x86\xac\xe6\xee\xae\xba\x8e\x56\x55\xd6\x22\xf5\x79\x9e\x46\xde\x52\xa8\x6f\x2c\x22\x80\x86\x74\x39\x34\x6f\xc7\xd6\x6a\x4a\xb5\x8b\x90\x8f\x79\x87\x60\x73\x35\x31\xb9\xca\x0c\x45\xbf\x08\xd3\x69\xba\x47\xc6\x1a\x36\x86\x49\x87\x92\xc4\x3d\xd1\x46\x5c\x2e\xc2\x69\x6d\x79\x0b\x1f\xbf\xdc\x49\x48\x81\xde\x65\xe1\x93\xbd\x72\x4e\x7c\x53\x38\xc6\x37\xdc\x2d\x3e\xee\xdc\xa0\xc8\x0f\xb9\x14\x2d\x3e\xda\xa6\x3d\xbe\xdf\x72\xb5\xd6\xe7\xf7\x6e\xf6\xab\xb8\x11\x52\xe6\x94\x01\xb1\xc6\xac\x16\x1f\xe6\xd2\x73\xed\xe9\xdd\xd1\xcb\xc5\x0a\x67\xf2\xbc\xc7\x52\x65\xb3\x9a\x5e\x0d\x10\x18\x29\x87\xd8\x5e\xcc\x71\x0d\x69\x91\x5c\xf3\x3c\x95\x79\xb4\xa2\xf5\x7b\x12\x2f\x12\x8a\xeb\x79\x49\xcd\xb5\xd8\x82\x86\xd0\x1e\x3e\x10\xd5\x7d\x14\xb5\x2d\x30\x83\xa7\xcb\x8b\x24\x8d\xf2\xc5\xaa\x77\xab\x39\x90\x91\x38\xa0\x94\xf3\x45\x2c\x44\xfd\x41\x4d\x6d\x06\x71\xff\xa6\x7d\x6b\xc4\xe8\x5c\x40\x7a\x5c\x75\xf3\xa0\x7c\xb8\x57\xc2\x37\x20\xde\xdb\xeb\x05\x69\x8f\x24\x33\xb7\x77\x58\xc0\xe9\xad\xe7\xd4\x50\xb9\xf6\x32\x59\x72\xc1\xcb\xc1\xea\xa7\x0a\xc0\x78\x54\x68\x0d\x3a\xe4\x12\x80\x38\x2c\xfa\xb1\x05\x98\x09\xde\xf2\x5d\xd5\x7e\x5c\xd4\x3d\xa8\x77\xae\xa2\xcc\xee\xee\x16\x1b\x39\xab\x71\xb7\xb3\x2c\x14\x92\x2a\x3c\xde\xc7\xe2\x6e\xad\x78\x79\xc3\x4d\x20\xfe\x8c\x32\x9d\xfd\x6b\xb6\x1b\x17\x86\x79\x88\x02\x8a\xb5\x35\xe9\xd1\xdb\x58\xf4\x88\x59\x3f\x06\x0c\xc0\x5d\x9c\x09\xb9\x3e\x1b\x72\xd9\xbf\xbd\x3e\x42\xf1\xd9\x76\x98\x6e\x74\xb6\x30\xd7\x45\xa7\xb7\x34\xd7\x12\x84\x97\x4a\x75\xda\xaa\xea\x69\x6d\xcc\xcb\x13\xee\xcc\x6e\x55\x15\x8d\x1a\x29\xeb\x80\xe7\x27\x97\x39\x8c\xbf\x2d\x6f\xd5\x8c\x56\xd4\x10\x77\xd2\xe2\x79\x54\x7e\x79\x2d\xfa\xa6\x57\xed\x8d\x1a\x8a\x16\xf0\x16\x6a\x9f\xd2\x58\xce\xf7\x7f\x0b\x24\xaf\x17\xd1\x92\xb2\x41\xa6\x18\xd3\x78\xf0\x07\x47\x35\x11\x01\x12\x66\x5e\x8c\xcf\xfd\x1b\x9d\xd9\x27\x56\xce\x8d\x34\x7d\x28\xa3\x86\x85\x24\xe3\x74\x4f\xb0\xab\xbf\x0b\xec\x53\x05\x31\x39\xfe\x37\xcc\x2b\xe3\x12\xfa\x84\xb9\x5c\x07\x28\xdd\xa2\x43\xdb\x4a\x38\xdc\x55\xca\xa1\x74\x15\x7a\x82\xc0\xd0\xb9\xed\x19\x6e\xbb\xeb\x1b\xc5\x0d\xc3\x34\x04\x03\xf1\xec\xe7\x5e\xcf\xc1\x2c\x28\xf5\x23\xae\xdd\xd8\x8a\x02\x89\x9b\x3a\x98\x38\x16\xc6\xfb\x26\x28\x74\x0f\xbc\x0b\x2f\x8a\xc1\xa7\xcb\xe4\x5a\x1d\x99\x6b\xb4\x90\x25\x10\xf0\xfb\xcc\x19\x66\x79\xc2\x73\x88\x79\x78\x3d\xd5\x36\xf6\x4a\x42\x92\x18\xb0\xa4\x6c\xf6\xcf\x20\x79\x19\x26\xd8\xca\x92\x1a\x72\xfc\x76\x5f\x86\x73\xc8\x31\xdd\xce\x24\xf3\x2b\xa7\xb6\xf0\xb5\x24\xac\x14\x1e\x1d\xa2\x13\xc3\xea\x32\xc3\x6c\x54\xe5\xc1\xe4\x2d\x78\x01\xa5\xec\xc4\xdb\xca\xcf\x9d\x86\x8b\x6d\xc7\x87\xbe\xa9\x4a\xb3\xca\xad\x94\xa5\x07\x77\xe4\x08\x07\xfa\xa8\x30\x43\x08\x5b\x9e\x1c\xf5\xb9\x0d\xa5\x75\x29\x48\x80\xd6\x65\xcb\x21\xa0\xb2\x2b\xa5\x45\x2c\xc2\xe0\xdb\x58\x91\x5b\x0f\x77\xf5\x91\xaa\xe1\x6d\x75\xa3\x5a\xce\x15\x0e\x0f\xf9\x25\x4a\xdb\xd9\x0c\xd5\x4c\xa8\xe6\xa2\x6d\xab\x99\x95\x1a\xd4\xaa\x1b\x25\x0d\xeb\xe6\xfd\x4b\xc7\xe0\x65\x72\x7d\xdb\x21\x78\x99\x5c\x77\x8c\xc0\x57\x34\xbd\x81\xd4\x4b\xe9\xf2\x06\xef\xd1\xdd\xb2\x89\xd6\x00\xaa\x98\x14\xe2\x9e\xa3\xad\x87\x4e\x36\x39\xff\xb2\xa1\xf3\x16\xb4\x85\xb6\x90\xdf\x94\xda\x26\x0d\x9d\xdb\xcb\x7c\xf1\x51\xc8\x7e\x5f\x95\x9e\xa1\x50\xfb\xb6\x76\x3a\x11\x35\xa0\x2b\x49\x59\xf1\x89\x75\x51\x10\xa9\x07\x70\x08\x9d\x33\xe2\xcf\x65\x50\xa3\x68\x97\x70\x0d\x2a\x6f\x3c\x37\x3b\x03\x02\x4f\xc5\xe6\x98\x98\x6e\xf2\x72\xb2\x6e\x94\xdb\x06\x79\xa4\xbd\xe3\x22\x1c\xc0\x70\x72\x71\x08\x27\xee\x8e\x3e\xfc\xb8\x0e\x31\x32\x20\x5f\xd1\xdf\x13\x7b\xe0\x11\x81\x5e\x52\xd6\x63\x5f\x9b\xe0\x6c\x23\x02\xe5\x5d\x94\xeb\xb9\x00\x06\x2a\x0e\x14\x10\x7b\x42\x13\x42\xe2\x3a\xc6\x4c\xcd\xaa\x7a\xa9\xc4\x03\xd8\x44\xbb\xbb\xf0\x08\x36\xc7\xb8\x21\x91\xf7\xbe\xd8\x0f\x89\x5b\x21\x33\xcd\x91\x19\xa8\x02\xf7\xf5\x6a\xfa\x40\xe4\xb1\xb4\x8c\xdc\x5e\x8f\x84\x72\xed\xe8\xa3\x58\x7b\xfc\x58\x9e\x51\xd9\x7a\x5b\x48\x19\x66\x13\x2b\x45\xfa\x4d\x84\xa7\x09\x04\x65\xf0\xe8\xe3\x88\x1a\xe2\x18\x4b\x71\x5d\x93\xd8\x54\x9d\xcc\xa5\xc3\x2e\x88\xd8\x16\x27\x57\x32\x38\xe4\x65\x1f\x42\x56\x1c\x5d\xc9\xa4\xa3\x2b\xda\xa0\x2e\xbf\x16\xf5\xa3\xc1\x57\xd8\x06\x0c\x2f\x5c\xc5\xdf\x05\x0a\x0f\xe4\x83\x28\x04\xb7\xd7\x10\xbe\xe0\x8d\x6c\xd0\xec\xad\x7b\xc6\x77\xa6\x45\x99\x7a\x83\x58\x49\xce\x2e\xe9\xfc\xd8\x75\xe9\xd6\xc7\x51\x0a\x87\xb0\xcb\x70\xd8\xee\x70\x0c\x5f\x3c\xfd\xb8\xfd\x09\x17\x75\x6e\x28\x71\x4c\x25\xa5\xeb\x94\x66\x34\xce\xc5\x66\xff\x6a\xf1\xad\x4a\x1c\x85\xbb\xe8\xe6\xd1\x86\x86\x7b\x78\x41\x23\x9b\x30\x48\xd7\xb7\xca\xe0\xcb\xab\x74\xa5\x7b\x7a\xeb\xfb\xef\x9a\xa9\x91\x52\xc5\x5d\x73\xfa\xbb\xf0\xa3\x4c\xbe\x3a\xbf\x76\x25\x41\x54\xe4\xf0\xc2\x23\x2f\xc5\x78\xbd\x64\x6e\x34\xf7\x03\x52\xfa\xf1\x32\x62\xfe\xfd\xa0\xb9\xe9\x5a\xb9\xb5\xba\x96\x71\x0a\xd2\x55\x01\xbe\xdc\xc1\xbd\x4a\xc2\xfa\x95\xfb\x41\x25\x2b\xe2\x5b\x78\x00\x79\x22\x5f\x37\x49\x66\x46\xb1\x4d\x12\x2f\x6d\x28\xee\xf3\x00\x8b\xb0\x1f\x6f\x8a\x13\x2a\x60\x39\x3d\x57\xf1\xcf\xb1\x1e\xde\x0f\x1f\x71\x31\x16\x8d\x2a\xef\xe4\x37\x71\xd3\x46\x4c\xab\x0b\xf9\x9b\x9b\xba\x57\x7c\x57\x77\x61\xbb\x1a\xc9\x03\x19\x7c\x9e\xc9\x9d\xb7\x62\x94\x64\x78\x95\x84\xb4\xb5\x91\xfb\x09\x03\xf5\x09\xd7\xe4\x7b\x8f\x0d\xdd\xe3\xb7\xac\x17\x3b\x22\xaa\x64\x25\x0d\x25\xc7\x38\x7f\x3c\x6a\x2f\x60\xc4\xe5\x7d\xee\x6d\xe0\x51\x99\xa4\x47\x35\x24\x15\x57\x97\xd6\x8e\x9d\x20\x3a\xba\x2d\xae\x82\xc6\x6c\xdc\x9e\x31\x8b\x5f\xfd\xb6\x1c\xbc\x6e\x98\xc2\xa3\x43\x29\x21\xb7\x42\xe5\x9f\xd4\x45\x42\x6c\x94\x8d\xdb\x44\x04\xdc\x08\xaf\xd8\x54\x1b\xb7\x77\xd5\xea\xcc\x0f\x9b\x13\x0a\xab\x58\xdf\xd8\x10\x0f\x85\xdc\xe8\x0c\x5f\x3c\x92\x56\xcd\xf9\x44\x3c\xc4\x74\x6d\xa5\x8d\x60\xb3\x0d\xdd\x49\x04\x10\x9b\x10\xda\x6c\xb9\x2d\xdd\xb7\xbb\x31\x53\x56\x1d\x2e\x49\x99\xfa\x8a\x6f\xd1\x83\x6a\x74\x6a\x5b\x1f\x15\xfc\xca\x7e\x45\x99\x94\x15\x4f\x39\xd0\x95\xab\x61\xcc\xf8\x95\xf6\x55\x98\x26\x3c\x5f\xa4\x6a\x01\x4f\x5e\x25\x32\x7d\x1b\x46\xb7\xe8\x82\x62\x69\xa6\x71\xa7\x6d\x55\xb2\x59\x90\x7b\xbc\x3d\x32\x3c\xe0\x7b\xc1\x29\x7b\xd7\x10\x67\x5d\x82\x4d\x81\xc4\xd3\xf5\x9a\xc6\x62\xb1\x54\xed\xa0\x14\xeb\xa0\x8c\xab\x8f\x20\x92\x2f\xdd\x43\xd3\xa4\x1f\xdb\x8b\x2d\xe6\x6d\xd5\xd1\x8e\xdd\x2f\x94\xd4\x6b\x16\xec\xbe\x99\x1f\xf6\x0e\x21\xd6\x0c\xdd\xdd\xbb\xde\xef\x4e\x24\x71\xff\xe4\x2e\x10\x78\xbc\xd5\x95\x92\xb0\xb7\x17\x89\x7b\x89\x39\x81\x0b\xaa\x8e\x76\xb6\xbe\xdb\x70\x8b\x0b\x16\x77\xf9\x9d\xac\xda\x4b\x16\x41\x3a\xb4\xb9\xc5\x7c\x4a\xea\xe6\xe1\xa1\xe8\xa7\x84\x78\x67\xdd\x5b\x5e\xdc\x08\x77\xde\x21\xfb\x59\x99\xab\x2e\x16\x87\x87\xee\xdf\x67\xc3\xd2\x13\xd8\xd9\xdb\x81\x5d\xc4\xe3\xa0\xf3\x78\xc2\x0f\x7c\x07\x49\x3d\xd9\x29\x9b\xff\xd5\xf2\x81\x2a\x0e\x0c\xd7\x52\x81\x66\x06\xac\x68\xbe\x48\xda\xe7\x5f\xc4\x99\x81\x58\x73\x41\x92\x72\x84\x14\x97\x15\x20\x60\x3e\x6f\x18\xe2\x77\x7e\x45\x52\xd6\x1a\x6e\x57\xad\x4d\x72\x55\xd5\x46\x03\x05\x97\x76\x77\x71\x43\x75\x7a\x91\x95\x87\x5a\x54\xc2\x11\x6b\x20\x47\x6d\xc8\x50\x86\x91\xbc\xf8\xa6\x30\xc1\x51\xc6\xb7\x44\xa6\xe5\x66\x49\x8d\x05\xbc\xc7\xf8\xa7\x13\xcf\x15\x9f\x18\xab\x5e\x69\xf6\x60\xcb\xe1\x04\xce\x98\x11\xb3\xa9\x03\x64\x45\xc7\x8d\x07\x9a\x96\xb6\x11\xc0\x95\x46\xc4\x54\xbe\x7e\x9e\x5e\x22\x72\xdc\x7b\x2e\x8f\x01\xb3\xf1\x05\x33\x92\xca\x99\x31\xf3\x45\x5a\x0b\x62\x3d\xc0\xc4\x24\x8c\x8f\xe5\x4d\x91\x57\x3c\x0b\x3f\x93\x4a\x5c\xc4\xac\x72\x8f\xb0\x86\x7a\xbc\x56\x75\x7a\x7f\xf4\x62\x57\x11\xfb\xe3\x6d\x2a\x17\x33\xf6\x56\xed\x3d\x90\x22\xea\xfe\x88\x95\xc7\xdd\x53\xf0\x98\xd5\xe2\x5f\xef\xe1\x2e\xb8\x38\xf0\x72\x3a\x88\xd5\x84\x17\xf9\x5c\x8a\x36\x06\xbc\x95\x4f\x9f\x60\xa7\x3e\x33\xd9\xd1\x64\x0a\x52\x36\xfe\x04\x76\x6a\x49\x5e\x77\xe0\xa0\xca\xf7\x2c\xe8\xbd\x03\x43\x83\xdf\x76\xdd\xcf\xde\xfa\x56\x3e\x3d\x87\x99\xef\xb4\x6e\xec\x69\x34\xa4\x79\x79\xf3\xfe\x65\x7e\xd2\x09\x33\xb1\xe2\x22\x47\x96\x97\x37\x6d\xb0\x37\xe2\xee\x8a\x1a\xff\xab\x93\x52\xab\x28\xbe\xcc\x0c\x58\x2f\x2f\x33\x2c\x8d\x7b\x2e\x7a\xf8\x1d\x17\xbb\xfb\x90\xc5\x01\x0e\x8d\x0a\x9b\xc5\xe7\xfd\x2d\x6a\xff\xc6\xbc\x44\x8d\xdf\x77\xbb\x13\xd4\xf7\x82\xb3\xbd\xbd\xdf\xce\xcb\x54\x26\xaa\xdb\xe4\xa5\xbd\x30\xaa\xab\xab\xe1\x45\x6f\x7c\xa3\x1d\xde\xe0\xed\xff\x56\x66\xe2\xf8\xad\x08\x67\xfc\xd6\x08\x67\x34\x91\x51\x5f\x6e\x8e\xc2\xcf\x13\x92\x68\x82\x1a\x3d\x77\x9e\x77\xde\x77\x1e\xe3\x9e\xe6\x58\x77\xd7\xb9\xfe\x9e\xf3\x5b\xdd\x71\xae\xbd\xfa\x38\x16\x57\x1f\xc7\x9a\xab\x8f\xb5\x73\x1c\x5d\x78\x25\x56\x25\x0a\xd1\x0c\xe8\x7d\x43\xb6\xb8\x34\x12\x43\x05\x73\x2f\x5a\x16\x7e\x3a\xde\xff\x08\x39\xcd\x72\x66\x4f\x15\x91\x89\xb5\x97\x66\xf4\x44\xe4\x06\xe9\x4d\xce\xcc\xa4\xee\x4d\x4a\xe7\xd1\x06\x0e\x61\xff\xfd\x60\xef\xc9\xd0\x1c\x9c\x6d\xfc\xe4\x7c\xb8\xaf\x38\xfc\x17\x26\xf9\xd3\x79\x8e\xd7\x36\xef\xbf\x1f\x9c\xbd\x1f\x9d\xef\x0e\x7f\x19\xfd\x79\x5f\x59\xf2\x3b\x3a\xe7\xf1\xc6\xfd\xf7\xbf\x8c\x44\x61\x55\xd1\x28\x2b\x44\xe4\x75\x7a\xe2\x9d\x60\x85\xbd\x27\x83\xe2\xe1\xa7\x13\xef\x44\x59\xef\x7a\x11\xe5\x34\x5b\x7b\x01\x7d\x9d\xbe\x61\x46\x02\x5b\xca\x1e\xfc\xb2\xfb\xe9\xfd\x2f\xd9\xee\xa7\x5f\xb2\xdd\x3f\xef\x5f\xf4\x25\xff\x85\x8d\x21\x66\xb9\x97\x2b\x03\x7c\xed\x41\x14\xdc\x49\xa8\x76\xcb\x51\x7c\x57\xf0\xa4\x72\xc8\xaa\x94\x1d\x4d\x24\x8b\xfc\x1d\x2a\x31\x3d\x49\x80\x6e\x02\xba\x46\xc4\x92\xda\x81\x85\x24\xed\x70\x2f\x1a\x04\x1c\x31\xe9\x18\x64\x7a\x3f\x80\x27\x37\x88\xb2\x13\xef\x84\x15\x7b\xc2\x03\x16\x07\x50\x1c\x50\xdf\x23\x70\xa0\x8a\x52\x77\xcf\x5c\xb8\xa3\x73\xb9\xea\x4f\xb1\xa8\x97\xb9\xc1\x93\xc3\x5f\xae\xcf\x7e\xb9\x1e\x9d\x3f\xf8\xf3\x70\x3f\xd2\x42\xc1\xe3\x01\x15\x91\x2b\x88\x86\xcc\xd9\x95\x01\x6b\x62\xc0\x5a\xbf\xab\xb9\xf8\x88\x03\x0e\x03\x56\xf6\x10\xd6\x56\x3d\x87\x0c\xdf\x5c\xb2\xb3\xd9\x81\x27\x40\x26\x70\x80\xa5\x0e\x61\xc7\x67\x0f\x2c\x38\x90\xaf\xdc\x56\x7d\x84\xcc\xdd\xf3\xd9\x10\xee\xb3\xaa\xd8\xde\x13\x58\x33\x42\xaf\x3a\xa6\x10\x5d\xcb\xc7\x8c\xe0\xfe\x96\x1d\xf3\xfb\x97\xa1\xf1\x50\xf7\x0e\x19\xed\x40\x9e\xf0\x4c\x01\x3b\x23\xc2\x7f\x98\x23\xa2\xba\x69\xb4\xfa\x34\xd8\x51\x58\x09\x03\x76\xfe\x4c\x76\x60\x58\x7b\xc3\xad\x82\xc1\xa0\xe2\xcb\xae\x09\x54\xf7\xac\x8f\x69\xdb\xbd\x43\xc8\xa4\x60\x5e\xdd\x99\xcf\xba\x17\x04\x35\xf0\xf7\xf7\x61\xa7\x0e\x68\x28\x6e\x42\xe2\x43\xf0\x01\xfc\x3d\xfe\xac\xa6\x87\xbe\x2a\xe2\xf1\x77\xff\x73\x3f\x0c\xc5\xfd\x61\x51\x68\xc0\x0e\xc2\xd9\xc1\x55\x06\x1f\xfd\x3f\x04\xc9\x1e\xf8\xcc\xf9\x43\xf7\x91\xf9\x80\x08\x7f\x87\x9f\xb4\xd7\x74\x9c\xeb\x3f\x1f\x71\xb7\x20\x8a\xc8\xbb\x22\x0d\xd3\x2d\x9c\x15\x17\x70\x55\xb3\x0a\x45\x0a\xac\x53\xe6\xf6\xcb\x91\x75\x40\xc7\x5c\x31\x11\x6d\xb8\xcf\xab\xec\xc2\x60\xc3\xa0\xd2\xab\xa3\x0c\x86\x98\xd3\x21\xbc\x01\x9c\xb5\x70\xad\x33\x68\xc7\x28\x50\x6a\x9f\xe3\xda\x09\x56\x6b\xf1\xbe\xfa\xec\xef\x43\x3b\x8f\xa9\xb8\x4e\xa6\x5e\x78\x7f\x1f\xda\x89\x4b\x77\xc2\xe8\x4a\x05\xd3\x56\x96\x3c\x4d\x5e\xc6\xb9\x5c\x7c\x7f\x1f\xda\x09\x5a\x77\xe8\xc7\x16\x48\x4c\x3e\xde\x2e\x79\x91\x2b\x4b\x4e\x54\x25\xa9\x0a\xcf\x69\xbb\xe4\x52\x0d\xd3\x55\x95\x54\xc2\x6c\xe7\xfa\xdc\x41\xd7\xbf\x51\x96\x51\x5e\xc1\xa4\x55\x12\x2a\x99\xa4\xe0\x12\x9b\x49\x34\xcb\xb2\xa2\x0a\x36\x95\xa9\xbb\xa4\xf2\xac\xa8\x82\x4f\x3c\x39\x65\x8b\xf7\x44\xc1\x28\x8c\x5e\x2b\xba\xa5\xe0\x14\x2e\xa4\x2a\x8a\x2a\x58\x85\xf3\x22\x45\x51\x05\xaf\xf2\xe4\x39\x4e\x1e\x1a\x32\x45\x14\xcc\xaa\x45\x3e\x2b\x35\x21\x0a\x6e\x89\x88\x64\x0d\x05\x26\xfd\x0a\x6e\x15\xeb\x4f\x0d\x04\x2c\x05\xb7\xf2\xe4\x85\xc8\x12\x56\x15\x66\x45\x55\xdc\x4a\xae\x55\x32\x60\x29\xb8\x25\x05\xee\xcb\x2a\xac\xa8\x82\x5b\xc5\xc2\x50\x13\x57\x05\xb7\x2a\x7b\x52\x2f\x5a\xe7\xd6\xf9\x19\x37\x63\xe7\xcc\x52\x0f\x86\x68\xbc\x57\xd9\x05\xfb\x75\x80\x3f\xd0\xb0\x35\x86\x6a\x34\x6a\x23\x0c\x1a\x1c\x4a\x0d\x71\x03\xd7\x88\x5a\xaa\xec\x30\x46\x58\x38\x94\xad\x62\x38\x4c\x44\x61\xc3\xc6\xfd\x2c\x54\x2c\x56\x88\x63\x34\xb5\x45\x49\x48\x57\x23\x71\xdf\x0a\x9b\x7f\x26\x57\x34\xdd\xbf\x64\x33\xb7\xbd\xf9\xb2\x11\xdd\x79\x39\x87\x54\x84\x6e\xf2\xc5\x8d\x01\x51\xce\x7e\x89\x5d\xc5\x6c\xae\xc3\x37\x77\x7a\x29\xe5\xab\xe4\xa2\x51\x6f\xce\x2f\x0a\x6a\xe6\xb3\xea\x89\x06\x88\x95\x28\xe6\xe1\x87\x7c\xcd\x52\xbd\x5c\x19\xe2\xda\xd4\x6f\x98\x1f\x25\x36\x20\x8e\x0c\x48\xc3\xb6\xb3\xaf\x3f\xe8\xbd\x4e\xae\x33\x62\x4a\x89\x57\xdb\x53\xd9\xe2\x9c\x32\x1b\xc7\x9b\x6e\xfd\xa8\x9d\x88\x70\x13\xe8\x26\xb3\x69\xd8\x5a\x19\x56\xe7\xf6\xaa\x88\xb6\x7d\x46\x2f\x90\x36\xa5\x09\xaf\x05\x93\x2b\x73\xb7\xc2\xa8\xaf\xd8\x63\xbe\xce\x2a\x8b\x28\x6e\x03\x42\x6f\x21\x48\xe2\xdc\x8b\x62\x14\x94\x50\xdd\x40\x7b\xf5\x3c\xc6\x1b\x9e\xa2\x98\x41\x50\xd6\x29\xfb\xad\x0a\x85\x28\x16\x5a\x18\xd1\x5b\xad\xa4\x61\xd1\x4c\xcc\xaf\x66\x42\x02\xf6\x24\x4c\xd8\xdf\x87\xdf\x0a\x48\x5e\x90\x5f\x7a\x4b\x35\xc0\x41\x34\x67\x53\x28\x43\xf0\xc8\xab\xc1\x1d\xb6\xe1\x26\x97\xf9\x41\xc7\xf2\xd6\xf7\x1d\xa1\x1f\x65\x1e\xd7\x8d\x82\x70\x50\x46\x85\x42\xbe\xd9\x85\x67\x7b\xc2\xe8\xd0\x87\x22\x3a\xf4\xa1\x88\x0e\x85\xfa\x05\x31\xcc\xf0\x19\xc2\x9e\x32\x07\x18\x94\xf1\xf1\xb6\x60\x32\x52\x88\xb4\x48\x2a\x8c\x35\xa9\x65\xb7\xc8\xe4\x0b\xc5\x2a\x51\x95\x77\x59\x57\xec\x37\x44\x5e\xff\x3e\xe6\x44\x61\x72\x29\x42\x41\x5d\x33\xd8\x82\x31\x8d\x7e\x7a\xb9\x10\x8b\xdf\x34\x12\x59\x7c\x52\xbc\xfa\x07\xf6\x85\xdd\x38\x03\x46\xd6\xdf\x30\xa2\x76\x0e\xdf\x02\x31\x3b\x2e\x43\xee\x5b\x36\xc3\x1e\x54\x37\xfa\x0c\x30\x70\x47\x60\xd8\x77\xb1\x4f\xf1\xe1\xf1\x6e\x4c\x83\x5a\x25\x7e\xeb\xdd\xac\xdb\x9d\x03\x4e\xfe\xec\xef\xc3\x09\xcf\xc4\xe8\xdf\x40\xf6\x31\xdd\x62\x67\x7d\x95\x96\xb2\xc0\xe7\xd1\x21\xc4\x11\xcf\x4c\x79\x99\x2d\x06\xe6\x56\x97\x82\xe0\xde\x84\x8e\xab\x82\xe5\x4f\xb8\xdd\xde\xf6\xa8\x9e\xf5\xbb\xaf\xf8\x6f\x18\x47\xdd\xab\x18\xb1\xdb\xd7\xc8\x56\xbb\x10\xa1\x58\x2a\x62\x06\xa5\x6f\x43\xfb\x17\xb5\x14\x97\x69\x46\xcf\xe2\xa8\xef\x0e\x82\x1e\x0b\xd6\x73\xe2\x41\xb2\x59\x1d\x86\x6a\x5b\x04\x54\xe6\xba\x67\xbb\x7a\x4f\x42\xf7\x5b\x34\x64\x88\x04\xd7\x22\xfb\x68\xcf\x60\xd3\x00\x7c\x5a\x23\x5f\xad\x2e\x1f\x35\xa3\x0c\x2e\xa2\x2b\x1a\x33\x7d\x92\xd3\xbd\x77\x43\x56\x88\xa2\xce\xae\x2b\x7a\x79\x57\xc3\x07\x85\xf1\xfb\xff\xb9\xfb\xba\xed\xb6\x91\x24\xcd\xfb\x7a\x8a\x70\xef\xba\x48\x9a\x00\x09\xd2\x52\xd9\x96\x4c\xeb\xd8\xd5\xaa\x6e\xcd\x71\x51\x5a\x5b\x35\x3d\xbb\x2a\x6d\x1f\x90\x48\x49\x90\x48\x80\x06\x40\x89\x94\xe5\x79\xa7\x79\x85\x79\xb2\x3d\x19\x99\x09\xe4\x2f\x08\xaa\xe4\xde\xe9\xd1\x85\x2d\x01\xc8\xc8\xc8\xbf\xc8\xc8\xc8\x88\x2f\xae\xb9\xa9\x31\x80\xbd\x47\x0a\x42\xa8\xb9\x62\xb7\x3e\x2e\x81\x47\x73\x81\x3c\xea\x76\x99\x60\x30\xc5\x54\x11\x4d\xd6\x15\xd0\xde\x06\x65\xd4\xee\x5c\xc1\x87\x90\xac\x16\x19\xc9\xd1\x5f\x08\x12\x78\x6e\x6d\x70\xe9\x49\x14\xce\x66\xf2\x42\x51\x50\x5a\x5d\x95\x28\xe0\xad\x61\x41\xb5\x59\xd2\xbb\xec\x95\x17\xb1\x6f\x82\xd7\xaf\x06\x3b\x78\x91\x86\xba\xcc\xd0\x13\x78\xeb\x82\x2f\x3a\x8b\x72\x78\x35\xd8\x71\x8d\x1f\xae\x7a\x05\xa9\x95\x61\xfa\x88\xc1\x4c\x70\x30\xed\x6d\x73\xad\xd5\x0a\xdc\xd5\x51\xe9\x01\xb4\xe9\x94\x79\x32\xac\xd7\x3d\x4a\xed\x1d\xec\x62\xe9\x88\x96\xde\x95\xa9\xda\xd0\x5e\xeb\x23\xf5\xf1\xdc\x73\x77\x45\x10\x22\xc9\x40\xd6\x9d\x91\x8b\xd2\xcd\xca\x54\x8b\xd2\xa8\x66\x99\xb6\x4b\xcf\x96\x03\xb8\xe6\xff\x9b\x6b\xe5\x1c\xf6\x70\x15\xb1\xc1\x41\x48\xda\x0e\x5b\x3a\x9d\x8d\xd8\xb4\x5b\x80\xd3\x8a\x1f\x66\x79\xb5\xa1\xd4\xba\xea\x90\xe1\x63\x37\x61\xac\x6e\x56\x21\x64\xac\xf1\x88\xa5\x2d\x50\x32\xb0\xd6\xdb\xa9\x23\xf0\x99\x25\xd3\x0e\x85\xa4\x55\x34\xf0\x20\xe8\xe1\x3f\x01\xfb\x57\xfc\x17\x0c\x80\x14\x0e\x5d\xbb\x6a\x36\xa2\xb4\x94\xa3\x95\x47\xf0\xbc\x92\xb6\x1b\x22\x05\x98\xb1\xd5\xcf\xa3\xa6\x41\x0a\x9b\x9a\x62\xbf\x01\xb5\xf1\xcb\xaa\xae\x15\xba\x6e\xbd\xd6\x0a\xdd\xb8\xa1\x60\x75\xb1\x4e\x56\x53\x92\xe7\xa6\x4f\xb6\xf4\x53\x39\xd9\xd6\x1f\x0b\xe4\x49\x97\x38\x30\xfa\x80\x43\x55\xd7\xa8\x60\x49\xec\xfb\x8f\xd4\xc3\x55\x0e\xea\x35\xbd\x1b\x69\x9e\xc8\xd9\x2b\x36\x9d\x43\xf0\xce\x66\xf7\xa7\x57\x41\x50\x42\xf0\xee\xfe\x84\xb9\xbc\x2e\xe0\x95\xdd\x4a\xe0\x9e\x04\x78\xc8\xa5\x52\x66\x4e\xc2\x24\x47\xc9\x53\xa7\x77\xd4\xe1\xa6\x51\xe5\x10\x37\x79\x26\xb4\x24\x64\x0b\x9b\xfc\x2a\x37\x8c\xeb\x73\xc4\x38\xbd\x71\xe3\x24\xd6\x4c\x21\xb4\x5b\x2d\x17\x6e\x0f\xc7\x7a\xd9\x22\xce\x18\xcd\x12\xb3\x1d\x5d\xa8\x92\x5e\x85\xac\x7e\xd4\x79\x57\x66\x15\x0f\x71\x8d\xc3\x7c\xd1\xce\x21\xc2\xb9\xd8\x9e\x83\xf3\x0e\xab\xa3\x8b\x7a\xc2\x2e\xe2\x6f\xd0\x26\x11\x45\x64\x83\x6a\x08\x7a\x78\xcc\x75\x65\x31\x70\xfa\x93\x6c\x22\x58\xd2\xa0\xe7\xf5\x1a\x5c\x51\x95\x01\xb6\x3c\x2d\xb5\xde\x34\x50\xff\xa1\x34\xbc\x21\x64\xfc\x8d\xdc\x37\x57\x98\x7c\x61\x9a\x91\x30\x6f\xd2\x1d\x5c\xec\x20\x95\xa6\x71\x92\xab\x1e\xd9\x14\xc0\xa7\x90\x2f\x81\x06\x79\x28\x94\x10\xc8\x4f\x13\x62\xbd\x01\xce\x15\xb6\x39\x6b\xf2\x05\xde\x6c\x20\x45\xdb\x12\x86\x59\xcb\x1b\xd7\x80\x9d\xb2\x26\xdf\x3f\x6f\x76\x68\xdf\x20\xcc\xe1\x11\xd9\xdd\x36\xee\x59\x9b\xf0\xfd\x40\x59\x4c\x12\xca\xfa\x8a\xa5\x12\x13\xe9\x9d\x5c\x49\x36\x1c\x4c\xc8\x1e\x5b\x35\x1e\x56\xac\xf3\x7b\x44\x73\xd3\xb2\x31\x69\xde\xf2\xda\x2a\xad\x1c\xb2\x1c\x2a\x85\xec\x9a\x45\x89\xa9\xce\x59\xee\x8a\xcf\x84\xe2\x61\xcd\xc9\xf5\x43\x4d\x77\x98\x4a\x87\x8e\xd1\xfe\xe9\xf8\xf4\xf8\xf4\x7f\x9f\x1c\xf6\x8f\xc6\x9f\x4f\xdf\x8f\x7f\x3e\x84\x5f\x0f\x4f\xff\x7a\xfc\xe7\xcf\x9b\xa3\xd6\x94\xcb\x62\xb8\xbb\x4a\xf3\x32\x5a\x82\x9b\x83\x27\x79\x3a\x5b\x16\x8a\x55\x3c\xce\xed\x21\x63\xd5\x0d\xc5\x49\x4f\x94\xc3\x3c\xb7\x30\x62\x4f\x60\x04\xf5\xee\x5a\x2b\xc3\x63\x99\xd6\x66\xcb\xcb\x22\x34\xfa\x0e\xbf\xed\xd7\xd6\x85\xa5\xd3\x1a\xc4\xf0\x6d\xec\x0d\x47\x27\xc8\xd1\x79\x21\x2d\x37\x23\x32\x7d\xe1\x4e\x9d\xf0\x4d\x35\xe3\x19\x94\xd3\x0b\xdb\xd4\x56\xba\x71\x4a\xe2\x59\x6d\xaf\x89\x1c\x5d\xb6\x98\x24\xec\x3b\x0f\x79\x65\x47\x02\x8f\x9e\x1c\xb7\xe8\x12\xf9\xc9\x40\x0d\x56\x32\xfb\x20\xce\xe1\x32\x23\x21\xb3\x1d\xf0\x58\x1e\x33\xf8\xb1\xbd\xf6\x60\x22\xe7\xcd\x7a\x01\x7e\x13\xd2\x6a\x8c\x50\x23\xba\x01\x27\x2b\x90\x4a\xaf\x08\xe4\xe1\x9c\x17\x56\xbe\x4c\x33\x76\xda\xd7\xd9\xe0\xb8\xc0\xcc\x0f\xde\x39\x44\x1c\xe6\xe4\x34\xc5\x69\xce\xb0\x43\x24\x2f\xad\xb5\xcd\xeb\x2e\xb6\x18\x60\x05\x70\xb8\x00\x32\xa0\x9d\xe0\xe9\x3e\x3f\x9c\xda\x36\xa3\xa8\x5b\x28\xb5\x80\xd1\xda\xae\xf7\xac\x7d\xa3\xf4\x9d\x6d\xb4\xea\x3c\xfb\x94\xce\xe3\xcc\x9c\x30\x5e\x68\xff\x45\x8b\x8d\x52\x22\xf1\xe0\xd6\xbc\xa3\xc4\xac\x52\x74\xaa\xeb\x50\xd4\xcc\x73\x4f\x8e\x89\x34\x9c\x7c\x12\x9e\x63\xf7\x56\x72\x90\xe6\x0e\xc0\x32\xb6\x2f\x5f\x49\x0a\xf6\x6f\x47\xf2\x19\x36\x6f\x45\x4b\x7c\x08\x75\x0c\xd4\x5d\x55\x89\xad\x4d\x74\xb1\x5a\x65\xfd\x1c\xc1\xf4\xec\xf6\xbc\x0c\x4a\xda\x67\x49\x3f\x03\x9e\xc6\xaa\xcc\x45\x98\xb0\x08\x79\x83\x40\xc2\x05\xa6\x0d\x9e\xd1\xe2\xb6\xeb\x9e\x56\x78\xca\x09\x60\x04\x47\xc6\xd3\x31\x8c\x60\x6c\x3c\x3d\xa2\x35\x2a\x4f\x03\xe8\x33\x3e\x8c\xa7\x81\x41\x21\xb0\xd2\x0d\xac\x74\xc7\x9c\xee\xd8\x78\x6a\xd2\x1d\x5b\xe9\x8e\x39\x5d\xf5\xe9\x11\xa7\x7b\x64\x3c\x35\xfb\xe1\xc8\x4a\xf7\xc8\x42\xf7\x69\x37\x21\x86\x54\x8f\xf7\x4e\xf2\x67\x72\x25\xba\xb8\x2c\x37\x2e\x25\x35\xab\x9a\xbe\x10\x4d\xaa\x4a\xd4\xb1\x73\x31\x33\x06\x3e\xac\xd9\x42\xc6\x5c\x2f\x8d\x04\xe1\x4b\xab\x20\xc4\x1c\x64\x35\x42\xd0\xd3\xf3\x2c\xaa\x5c\x6e\x27\x23\x1b\xf4\xbb\x88\xf3\x11\x91\xa5\x72\x4e\x3f\x75\x50\xac\x5d\xae\x8f\x8b\x3e\x18\x1b\x7a\x15\x7d\xe5\x68\xfd\x55\xf7\xe2\xa3\xa6\x7d\xbc\xf3\xc8\x3e\x0e\x3c\x2a\x06\xb7\xda\x6e\x78\x7c\xd4\x86\x1d\x9d\x7c\x59\x86\x33\x61\x46\x6e\xb4\xa1\x57\x61\x55\xe2\x22\x41\xcf\xce\xac\x74\x1c\x56\xc0\x36\x15\xf2\xa5\x69\x37\xed\xfe\x81\x3d\x99\x9f\x75\xfe\x8b\xab\x9c\xfe\x26\x9d\x13\x4d\x59\x4f\xa9\x74\xbe\xfc\x2e\xf3\xe7\x71\xca\xe6\x96\x73\x88\x57\x72\x4a\xeb\xa0\x13\xe9\xb2\xf1\x7a\xfb\xe9\x8f\x4c\xa4\x77\xdb\x4d\xa3\xc7\xf4\x58\x9a\xd9\x57\x60\xed\x76\xf1\xf8\xde\x3b\xce\x0e\x69\x6d\x5c\x49\xbe\x2c\x48\xd3\x7e\x7c\x65\xed\xc7\x36\x4c\x64\x20\xe8\xfa\xde\xec\x70\x60\x28\x1e\x9d\xa0\x6a\x3f\x4f\xd6\xbb\x21\xe0\xb2\x22\xa5\x2f\xd9\x96\x9d\x15\xe7\xbf\xb0\xf2\x0d\x56\xde\xb3\x67\x42\xd3\x7d\xfa\x66\x48\x79\x6d\xf5\x16\x6c\x6a\x00\xdf\xa1\x70\x88\xf1\xaf\x6d\xda\x02\x3f\xfe\xb8\x41\xd5\x7e\xc7\xf5\xfb\x4a\x41\x1f\x7e\x87\x0e\x60\x59\x60\xb6\x1d\x3b\x16\xd8\xb5\xb9\xb1\xd8\x82\xfc\x3b\xb0\x9d\x90\xcb\xb0\x88\x6f\xc9\x63\x78\xe7\x45\xf9\xb8\x8d\xc9\x65\x93\xa6\xb0\x96\xd0\x33\xc5\x77\x68\x0d\x82\xa8\xfa\xc1\x23\x1a\xf3\x7f\x48\x96\x6e\x3b\xed\xd8\x6f\x22\x0f\xce\x77\x68\xce\x23\xac\x17\x5b\x36\x9c\xd6\x50\xee\x52\xb3\xc6\xbb\xd4\xeb\x3f\xb2\x4b\x7d\x9f\xa1\xaf\xfa\xea\x1f\xb1\x45\x89\x7e\x53\xf7\xa7\x59\xf3\xfd\xe9\xcd\x53\xed\x4f\xbe\xba\x41\x35\xe8\x57\x48\xc0\xc7\xf3\x67\x62\x3c\xb5\x9d\xc3\x7d\x3c\x7f\xfa\x47\xda\x31\xda\xc7\x83\xad\x9f\x18\x8f\x03\xcb\xf9\xdc\x46\x39\xb0\x53\x1e\x73\xca\xfa\xf1\xda\xb7\x1e\xc5\x6d\x84\xc7\x9c\xb0\x7e\x90\xf6\xad\x47\x71\xdf\x7a\x14\xb7\xd1\x3d\xb2\xd0\x7d\x5a\xe5\x1c\xe3\x7b\x9a\xce\x59\xd7\xd4\x64\x44\xe8\x74\xcc\x97\x93\x06\xd3\x91\x27\x80\xbc\xf6\xa0\xf0\x60\xf5\xf1\x74\x6d\xf1\xf0\xe7\x36\x32\xf3\x4d\xc8\x32\xc2\xe9\xa6\x33\x34\x53\x6a\x57\x53\x66\x2a\x7e\xce\x8f\xfa\xd9\x04\xf3\xc2\xe9\x14\x95\x9c\x70\x96\x60\xfc\x67\x21\xba\xdd\x4c\x5c\x41\x8f\xe3\x70\x6c\x89\xee\xff\x1c\x5f\x26\x39\x44\xf1\xc5\x05\xc9\x2c\x44\x43\xcc\x26\x69\xbd\xa1\x59\xe3\xed\x81\x3f\x31\x2f\x65\xc4\x15\x42\x6f\x31\x5b\xe6\xed\x75\x3d\xce\x04\x5e\x5e\x88\x4c\x65\x95\xd2\x62\xf6\xf3\x9a\xa5\xd5\xaf\xff\xc8\x1d\x88\xe1\xc8\xb6\xc7\x73\xe7\x11\x96\xc2\x8d\x38\x81\x02\x78\xd7\x8b\x13\xe0\x86\x24\x7c\x6b\xc9\x66\x8a\x79\xed\xda\x65\x77\x79\xb0\xc6\x2c\xdb\xfa\x3c\xa0\x5f\xad\x60\x0f\x53\xdc\xb9\xb2\x2b\x33\x1e\xec\xa0\x9b\x72\xce\xfd\x06\x69\xff\xaa\x54\x79\x6b\x5a\x74\xcd\x02\x50\x98\x3b\xa3\x07\x2b\x29\x28\x45\x3c\x4b\x59\xcd\xf4\x0d\xa6\xf9\x0b\x33\x82\x0f\xec\x37\x9b\xbc\xf1\x8c\x8b\x06\xed\x5f\xf1\x0f\x57\xee\xfc\x7e\xfd\x3e\x1c\x1d\x1e\x1e\xc2\xab\xdd\x1d\x68\x0f\x83\xe0\x75\x07\x7e\xea\xbd\xdc\x43\xf9\x8c\x82\x38\x60\x30\x30\xa5\x5b\x4b\x91\x56\x47\x76\x07\x4d\xd5\xee\x85\x49\x7d\x0e\x28\xa1\x3d\x7b\x1a\xa4\xda\xeb\xc6\x95\x92\xeb\x6e\x45\xb4\xf2\x6b\xe5\xf5\x5a\x7f\xcd\x66\xae\x04\x59\xae\xaf\xd4\x2a\x77\xf7\xdd\x55\x3c\xbd\x12\x82\x74\x12\x5f\xd2\x83\x83\xd3\xf8\x8d\xe2\x09\x73\x00\x3a\x26\x37\xbb\x14\xfc\x78\x4a\x85\x53\x58\x1b\x55\x41\x49\xf9\xa1\xfd\x1a\xbd\x40\xee\xb7\x44\x06\xc0\x2e\x59\x39\x30\xcd\x0a\x84\x70\x6f\x74\xe7\x5d\xf4\x32\x72\x4b\xb2\xdc\xec\x37\xe0\x17\xbd\x19\xa9\xa0\xce\xe8\xbc\x40\xed\x88\xaa\x3a\x02\x54\xc5\x99\x80\x8f\x0a\xe3\x70\x1f\x26\xbe\xbf\x0f\x45\x5d\x50\x81\xc2\x84\xb5\x0b\xac\x6b\x5a\xd4\xcf\x58\x12\xd1\x72\xcc\x7f\x08\x93\x82\x5b\x5d\xb3\xae\xf1\xda\x85\x8f\x1a\x1f\x64\x29\x26\xe3\x2d\x67\x7c\x2d\x3d\xeb\xc0\x01\x84\xb0\x67\x8d\xf8\x67\x4d\xa5\x54\x26\x2c\xdf\xe0\x04\xde\xc2\xf5\x3e\x4c\xea\xb2\x0e\x0b\xff\x8d\x09\xba\x6f\xac\xf1\x97\x5a\xcf\x3b\xc6\x2d\x2b\xf1\x96\x15\x70\xfb\x64\xd4\xf8\x81\x6c\x89\x94\xd6\xef\xc3\x8a\xd6\x77\x00\x88\x8c\x47\x57\x1a\x57\x8b\x59\x5c\x1a\xbf\x43\xda\xb0\x90\x28\xf7\x1d\x3e\xcd\x3d\xb6\x5a\xd7\x53\x8f\x6d\x2b\x85\x27\xc4\x9b\xb9\x6d\x4f\x70\x80\xae\xb5\xc1\xf0\x75\xb7\x0f\x1b\xf2\x8e\x8c\xcf\x47\x59\x5e\x4d\x29\x27\xf9\x55\x9a\x15\x3a\x87\x0c\x23\x44\xe4\xbc\x08\xe5\x3c\x71\x6b\xb9\x14\x84\x79\x85\x6a\x8f\xe9\x7f\x68\x19\xfc\x2e\x2f\xc2\x0c\xfd\xc3\x4b\x3e\x2d\xd2\x64\xa2\x80\xfb\xf1\x65\xb1\x2a\x61\xbc\x0d\xa1\x39\xe1\xc9\x41\x2d\xd9\x81\xe4\x3b\xbe\xf5\x94\xc1\xbf\xeb\x61\x6a\xa2\x9a\x6b\x78\x47\x17\x61\x8d\xfc\x62\xa8\x4a\x6c\x5a\x5d\xbb\xe7\x61\xe5\x70\x73\xbd\x0f\x31\x3d\xc1\x3e\xe3\xee\x36\xfb\x3c\x49\xe6\xc8\xd4\xc7\xc4\x8f\xef\xe3\x27\x0e\xe8\x04\xac\xb7\xeb\xca\x85\x6a\x43\x4f\xc0\x12\xfe\x88\xb1\x5c\xab\x24\x39\x12\x38\xe3\x05\x13\x8b\x20\x91\x80\xb5\xc4\x85\xd4\x6c\x6d\xef\xcc\x2a\x6f\x2d\x3a\x17\x71\x60\x6e\x0f\x7c\x7f\x6d\xc2\x45\x72\x0f\x61\x9b\xbe\xb9\x72\x6b\x18\xfd\x3e\xfc\x92\xce\x66\xe9\x1d\x65\xd5\xb2\x69\x9b\xea\x19\x86\x96\xb2\x7d\xbc\x1b\x00\x26\xef\x93\x37\x76\x63\x67\xbf\x0b\xb3\x28\xb7\x5a\xe4\xf9\x0f\x5b\x90\xf6\xfd\xdd\x81\x62\xb3\xe6\xfe\x46\x6b\xb7\xbf\x91\x50\x6b\x36\x8d\x96\xb4\x18\xa7\x65\xf8\xb3\x9c\x64\xb8\xbb\x02\x1f\xba\x6b\x2a\x39\xcb\xc7\x3f\xfe\x08\x3e\x7d\xec\x2b\x8f\x75\xd2\x94\x12\xb7\x97\x32\xc4\xc6\xb5\x35\xa9\xb6\x84\x77\xb6\xf6\x50\x66\xad\x49\xc3\xdb\x04\x16\x87\x42\xcf\x84\xda\xf1\x0f\x9f\x8f\x1d\xcf\xe9\xb9\x50\x3b\x4f\x43\x00\xcf\x71\x04\xb5\xd3\xb0\x2f\x9e\xfb\x81\xf9\xbd\xad\xde\xc0\x51\x6f\x20\xea\xd5\xe8\x8c\x45\xbd\x63\xf3\xb9\x8d\xfe\xd8\x41\x7f\x2c\xe8\x6b\xcf\x8f\x1c\xf4\x8f\x1c\xf4\x8f\x1c\xf4\x8f\x6c\xf4\x9f\xf8\x40\x9d\x46\xcb\x59\x63\x2b\x50\x0f\x43\x92\x78\xf6\x86\x08\x95\xa6\x1c\x52\xcd\xf8\xf6\xeb\xf1\x9f\x7f\xfb\x78\x5c\x7b\xa1\xcd\x6b\x1d\xb1\x5f\x1b\x1e\xc1\xbf\x78\x60\x39\x5f\x8b\x93\xb7\xf5\x80\x3d\x68\x7c\xc0\x36\xe5\xa9\x9c\xc7\x1c\xcf\x3a\x9a\xe7\x8d\x57\x66\x0d\xa7\x1f\xf1\xe3\x8f\x65\x4f\x7c\xb6\xea\x4d\xe5\x54\xe6\x6b\x66\x1a\x7d\xb6\xee\x39\x23\x60\xb6\x38\xa0\x73\x3e\x57\xe5\x29\x4d\xe6\x12\xd9\x36\xd9\x92\x5d\x2d\x29\x1b\x94\xab\x15\xe7\x6a\xb5\x25\x57\xab\xfa\xe3\x3b\xd6\x21\x4d\x08\x2a\x63\xdf\xb8\x4f\xd1\xcb\xe9\x2c\x8e\xe2\x30\x61\x59\x5f\xe2\x34\xd9\xc3\x54\xe7\x79\x7c\x99\xb4\xd7\x1d\x78\x01\x2c\x1a\x60\x05\x7d\x08\x27\x79\x7b\xdd\x31\x83\xb5\xfa\x7d\x0c\x0d\xa3\x82\xf2\xcb\x1a\x10\xed\x8d\x64\x84\xca\x82\xb7\x23\xc8\xe0\x2d\x2f\x68\x94\xcb\x85\x55\x45\x7f\xb1\xb6\xb9\x41\x02\x4f\xc2\x8e\xd7\xfc\x2b\x8f\x4e\xa3\x40\xbb\x8f\x55\xcb\x5b\x08\x7f\xe9\xe5\xf0\xc2\x78\xe3\x3c\x11\x99\xd5\xc9\xfd\xda\x08\xad\x73\xc5\x4c\x5f\x6d\xf8\xd2\x43\xec\x17\xda\xa7\x4f\xed\xcc\xe1\x12\x34\x78\x8d\x42\x22\xc5\x14\x8f\xb8\x13\x3c\x4f\x63\xcc\x1c\x6c\x94\xac\x6e\x8a\xcc\xe0\x04\x50\x68\x24\x1b\x6e\x52\x1a\xbb\xbc\x32\x1f\x57\x7f\xc5\x96\xa6\xe9\xbf\xb6\x95\xbb\x2b\x24\xd0\xb5\x9a\x8d\xbb\x56\xb3\x71\x17\x65\xbb\x6e\x35\xee\x32\x74\x61\xe3\xa9\xcd\x68\x6c\xa3\x1b\x58\xe9\x8e\x39\x5d\xdd\x0e\xdc\xb5\xda\x8c\x6d\x74\xc7\x9c\xae\x6e\xf1\xed\x5a\x6d\xc6\x5d\xab\xcd\xd8\x46\xf7\xc8\xc2\xef\xd3\xce\x3c\xc4\x81\xfd\x83\x26\xe3\xc5\x8c\x5b\x8c\xe9\xd9\xa9\xd9\x76\x55\x3c\x95\x95\x78\xf8\x4f\x6c\x25\x7e\x0a\x33\x31\x93\x59\xff\x3d\xec\xc4\x7c\x36\x4b\x6e\xbc\x71\xe9\x0d\x5d\x3d\x74\xa1\x81\x5b\xcc\xc7\x3a\x0c\x39\x3a\x4e\x3e\xc6\x40\xfc\x3d\x2d\xbe\x2e\xe3\xb3\x66\x05\x5e\xd7\xda\x7d\x21\x44\xd7\xf3\xfa\x69\xf0\x9d\x8d\xac\x4d\x0c\x85\xf0\x4b\x98\xa3\x33\x50\x0a\x4b\xbc\x2c\x45\xab\x1f\x15\x40\x09\x44\x29\xf0\x0c\x8f\x79\x03\x5b\xac\xb5\x1b\xc3\xda\x94\x05\x8f\x33\x99\xd6\x19\x61\x1f\x65\xd5\xdd\x60\x83\xd5\x5f\x0a\xd3\x43\xf8\x07\xac\xa8\x6a\x95\x8a\xc5\xd3\x22\x18\x1d\x60\xdd\x74\x88\x35\x23\xe0\x2c\x4d\x2e\x49\xc6\x6c\x81\x2c\x9f\xe2\xa4\xcc\x15\xc5\x8d\x66\x4e\x73\x58\x08\x3e\x9a\x4a\xe9\x78\x15\xb2\x45\xb0\xb4\x11\x16\x1e\xb7\x20\x9b\xb8\xda\xc9\x6c\x2d\x8c\x6e\x11\x5a\x35\x64\xdb\x1b\xfa\xd0\x87\x6c\x5b\xbb\x58\x66\xb8\xae\x2b\xe0\x87\xd5\x14\xa6\x61\x02\x13\x02\xf1\x65\x92\x66\x7a\xa0\x62\x65\xce\x0d\xf6\x61\x62\x4f\x71\x10\x32\x0b\xf2\xf4\xcc\xf7\x27\xe7\xa5\x59\xb6\xcb\xed\xb8\x5d\x08\x11\x18\x08\x6d\x77\xd6\x40\x73\xf6\xfd\x73\x9b\xbd\xcb\x72\x44\x08\x6d\x2c\xac\xa6\x65\x3a\xd4\xd0\x32\x15\xba\xdd\x35\x79\x94\x9d\x85\x49\x2f\x66\x64\xe9\x72\x23\x4b\xc0\xad\x2b\x5d\x6e\x5d\x09\x74\x4a\xb8\xb0\x44\x28\x5c\x17\x06\xb0\x48\xf3\x3c\x9e\xcc\x88\x4d\x5d\xfc\x03\xc6\x15\x6b\x64\x89\x05\x4d\xaf\x3e\xba\xc4\x95\x97\xec\xec\xfe\x1c\xbe\x4e\xd2\x74\x46\xc2\xe4\x81\xd1\xff\x06\x7f\x13\x10\x14\x29\x4c\xd3\x65\x52\x08\x37\x35\x1f\xfd\xb1\xd5\x88\x8a\x3d\x74\x36\xf1\x98\xeb\x87\x07\x03\xba\x09\x04\x4e\xa5\xa9\xcc\x74\x84\x77\xed\xaa\xe2\x74\xdf\x3c\x00\xc5\xad\x36\xf1\x6d\xda\x5c\x3d\x15\x0e\x67\xbb\x53\x65\x84\xe0\x90\xba\xac\xfd\x94\xf5\x49\x9c\x84\x19\xbf\x45\xd9\x83\xaf\xf7\x1a\xb4\x2e\x2e\xe2\x7b\x39\xd5\x18\xfd\x63\x04\xcf\x9e\xdd\x57\x7f\x0c\xaa\x5f\xed\x72\xd9\x02\xcc\x3b\x78\xe9\x41\x4b\xb0\xd5\xa2\x3a\x74\x5a\x7c\x48\xd3\x99\x07\xf7\x36\xa9\x57\xf1\x41\x6b\xee\xc0\xbd\x15\x50\xd7\x76\xf4\xae\x8f\xce\xd1\xa3\x72\xcc\xd8\x9d\x5b\x19\xb8\xdf\xc4\xbe\xd0\xe3\x67\xec\x4a\xcf\xf6\xb1\x3a\x3c\xf0\xdd\x75\xe9\xb6\x21\x40\xc7\xc6\x43\x99\x8f\xb4\x09\x24\x9e\xab\xde\xdb\x32\x3d\xc2\xad\x08\x2c\xaf\xea\x36\xc3\xd9\x6d\x03\x82\x13\x87\x03\x89\xc0\x3b\xcc\xba\x92\x80\x0b\x5a\x64\xab\xe0\xa1\x27\xf6\x7c\x9f\x87\xab\x78\xbe\x9c\x6b\xc7\xa5\x68\xa1\x87\xb9\xd9\x61\x3d\x51\x3b\x2c\x52\x08\xcc\x20\x17\xc8\x48\xbe\x20\xd3\x22\xbe\x25\x33\xaa\x6a\xca\xe4\xd3\x79\x5c\x14\xca\x76\xa5\x8a\xaf\x68\x71\x0e\x5f\x85\xdc\xfa\xb3\x0a\xe0\x02\x47\xc2\xbb\x36\xa0\x35\xff\xfa\xfe\xdf\x18\x4e\x63\x1e\xdf\xaa\xd9\x0f\xcf\xb2\xb9\x44\xe5\x93\x92\x25\x51\x23\xf2\xda\x4e\x42\xa6\xc6\xb0\x79\xdb\x1d\x23\xfc\x4f\xca\xb2\xb2\x07\x5f\xa3\x85\x2c\x5b\xdc\xc5\xd4\x54\x2c\x75\x05\xd5\x2e\x37\xca\x65\xf3\x46\xe5\x34\x36\xb5\x52\xb2\x34\xc7\x72\xea\xd9\x37\x5a\xb0\xc4\x7c\x36\x49\xee\xb2\xbe\x98\x4b\x22\x5a\x94\xb9\x0f\x1f\x1e\x94\x64\x8e\x94\x7e\xe0\xd1\xa1\xf4\x60\xb0\xeb\x48\x50\x24\x82\x25\x3c\xf8\xf7\x7f\xc7\x6c\xaa\x72\x84\x04\xc3\x29\xe2\xb4\x2d\x3a\xec\x33\x7b\xee\xc8\xc1\xae\x9e\x37\x12\x0e\xb4\x59\xbc\x27\x32\x47\x36\xb2\xbc\xfd\xa3\x96\x30\xaa\x4b\xcc\x92\x76\xc3\xa7\x94\x5c\x41\x7b\x91\xde\x91\x0c\x85\xde\x20\xe8\xf4\xe0\x33\xfd\x5c\x41\x09\x63\xb0\x5f\xef\x68\x3f\x60\xea\x1c\x09\x8c\x0a\xdf\xbc\x55\xb7\x7b\x99\xf8\x4d\xb5\xa8\xca\x55\xe4\x53\x95\xe9\xf3\xfb\x5f\x0e\xff\x7e\x34\x3e\x3d\xfc\xcb\xe1\x27\xbe\x34\xd5\x67\x1b\x17\xd9\xd1\x05\x03\x21\x91\x67\x2b\xf2\x57\xe5\x74\xe2\x1a\x89\x94\x5d\x5a\x20\x9b\xfc\xe7\x7f\x60\xc0\xf2\x0d\x43\x58\x4d\xe5\x83\xb6\x22\x7b\x84\x7f\xab\x73\xa5\xf3\xab\x50\x4d\x9f\x90\x17\xdd\x8d\xb6\xe6\x8c\x12\xda\x72\xbb\x71\xae\x36\x2c\xa9\xac\xb6\x1b\xd7\x32\x63\xb7\x1f\x96\x49\x27\xcf\xed\x1b\xcb\x58\x78\xc6\x48\x78\x30\xf8\x49\xd1\x4c\x3a\xfa\x66\xda\xef\x63\x47\x0e\x48\x77\x38\x60\x42\x5e\xa4\xb1\xba\xe9\xb0\x1e\x67\x39\xfe\x89\x35\xab\xa5\xbe\xb1\x1e\x40\xc2\x2d\xd1\xd0\x1a\x10\xaa\x08\xc9\xe4\x74\x23\xbd\x61\x1d\x48\xd8\xa5\x05\xcb\xb5\xca\xc0\xd8\x28\x73\xe6\xa4\x7b\x78\x80\x1b\x0e\x65\xa1\x3c\x37\xaf\x01\x0e\x30\x65\xde\x0b\x4e\x49\x20\x0d\x0e\x98\x59\xc5\xf8\x7a\x4f\xcd\x93\x55\x67\x1b\xce\xbf\x64\x45\xdb\x4f\x3a\xe6\x35\x1f\xbe\x81\xb1\xeb\x8d\x7f\xe4\x2c\xc3\xde\x1c\x59\xde\x04\x1d\xf3\xda\x93\x51\xc3\x37\xca\xc5\xea\xe3\x24\x50\xfe\x65\x19\x66\x04\xb2\x34\x2d\x36\x85\xba\xcb\x15\x3c\x69\x5c\x2c\xe3\xe1\x13\x65\x01\x4f\x18\x5f\xb2\xfa\xc0\x98\x32\xeb\x20\x64\x1e\x64\x64\xe1\x6d\x67\xa4\x75\x1a\x05\x73\x66\xbd\x35\x5f\x70\x83\xa4\x25\xcb\xd1\x02\x46\x7a\xb3\xbb\xb0\x63\x7e\x78\x15\xce\x2e\x8c\xad\xb4\x15\xf4\x76\x5b\x16\xf3\x94\x88\x37\xe9\x8f\xc3\x71\x5f\xc8\xb8\xbe\x69\xef\x63\x58\x7c\xfc\xf4\xf2\xf0\x40\x4f\x09\xf8\xef\x56\x37\x7f\xf0\x0c\xef\x4c\xca\xfc\x92\x6d\x4e\x86\x53\x39\xc0\x6b\xd1\x3d\x10\x8e\xab\x03\x61\x99\x94\x09\x9b\x27\xf6\xa3\x24\x46\x89\x41\xf2\x22\x9e\x87\x85\x96\xc1\x9c\x76\xf4\xaf\x61\x71\xd5\x63\xb3\xbc\xbb\xb2\x5d\xdd\x96\x1f\xc0\x52\x80\xce\xf4\x53\x6b\x96\xb2\x7e\x1f\x4e\xc2\x3c\x67\xa8\xf5\x55\x31\x84\x78\xe2\x1b\x18\x9a\xed\xb8\x0f\x0d\x87\xbd\x64\x7e\x34\x02\x9a\x11\xf7\x1a\x8b\xed\x27\x2f\xa1\x26\xf1\x37\xde\x7c\x47\x32\x49\x35\xc3\xf0\xd4\x75\x04\xa4\x12\x8f\x9f\xd6\xba\x98\xa0\xee\x39\x0c\x05\xde\x57\xc2\x33\x8b\x9a\x45\xd5\x4e\x4b\x2c\xc4\x15\xab\x68\x99\xeb\xb4\x03\x7d\x04\xc4\xf4\x79\xb2\xb4\x80\xe5\xd9\x7d\xce\xb0\x4e\xec\x1c\x6e\x68\xab\x68\x2f\x17\xf6\xb6\x44\xf2\x75\xb6\x48\x4c\xb0\xdc\x53\x53\xeb\x3a\x9c\xb1\x50\xff\xe4\x86\x5c\xba\xed\x27\x3d\x04\x9a\x3d\xbe\x68\xb7\x48\xab\xc3\xdb\x67\xe7\xc0\x6c\x5a\x66\xac\x40\xbd\x17\x9d\x6c\x9b\x45\x21\x87\x2e\xcf\xe5\x55\x53\x6d\x09\x10\x7a\x61\xf5\x2c\xc0\x5b\xee\x69\xba\x9c\x45\x54\xc3\x11\x66\x78\x81\x98\x14\xe7\x65\x22\xec\x0a\x73\x16\x85\x72\x95\x10\x7b\x8a\x21\xa4\x91\x41\xf6\x94\x7e\x76\x87\x94\xa7\xe1\x32\x27\x10\x96\x97\xf1\x54\xab\xc4\xaa\xda\xab\x7e\xd1\x41\x31\x7d\x45\x92\x29\xa9\x7c\x0e\x26\x64\x96\xde\x79\xdc\x03\x59\x22\xa2\x57\xa2\xcc\x77\xa6\x64\xaa\xe9\x3c\xc5\x7c\xca\xdc\xee\x08\x74\xd2\x66\x3d\xcb\x00\xd2\xf9\x4e\x67\x70\xb4\x70\x2c\x23\x2a\xb2\x5e\x42\x07\x3f\xb4\x01\x8b\xa2\x24\xbd\x2b\xd2\xc4\xff\x14\x2e\xae\xf2\x34\x81\xb8\x20\x99\x23\x2b\xb3\x8a\xff\xa7\xbf\x05\x6e\x25\xcf\xec\xb3\x94\xce\x0e\x2a\xe1\x85\x12\x54\xb0\x98\x88\xf2\x9e\xbf\xf0\xf0\x0c\x34\x60\xd1\x4c\x75\xc8\xaa\x5a\x8e\xf2\xa2\x37\x05\x80\x8e\xb4\x00\x72\x1e\x0d\xd5\xa6\x4b\xc3\x4a\x07\x7f\x34\x3a\x19\xa6\xbc\xd7\xe9\x6c\x4a\xd4\x76\xaa\x09\xca\x8c\x25\x68\x27\x19\xa1\xb3\x35\x4d\x88\x16\xb7\x77\x11\x27\xe1\x4c\xa8\xed\xa2\xa0\x3d\x67\x1e\xaf\x81\xf4\x2e\x19\xa0\xea\x9b\x37\x6f\xde\x40\x9b\xf8\x3b\x1d\xf0\xfd\x77\x0c\x6b\x95\xfe\xfd\xb2\xe3\x41\x9e\x0a\xc9\x9d\xd3\xdf\x4d\x4c\x4b\x33\x83\xba\x54\x05\xd5\x6e\x50\x66\x90\x08\xa6\x69\x96\x91\x69\xa1\xbb\x4c\x1a\xa3\x90\x21\x70\x19\x95\xcc\xbe\x6f\xf1\xf8\x10\x3f\x8a\x70\xca\xc1\x87\x97\x1e\x4a\x85\x41\x2d\x34\x21\xef\xd8\x9d\xe2\x4a\x07\x04\xe6\xb9\x39\xe2\x84\x67\xb2\x42\x3f\x0a\xda\x64\x1e\x14\xb8\xb3\x6d\xbb\xb1\x57\xd3\x0c\x76\xb0\x77\xd1\x4d\x23\x5c\x2c\xb2\x34\x9c\x5e\xe1\x4d\x44\x45\x6f\x42\x7f\x09\xb3\x75\x07\x73\x77\xc4\xc9\x12\x6f\xba\xea\xe8\xd7\xac\x25\xa5\x2f\x13\x4c\x95\x47\x39\x69\xa1\x76\x92\x91\x05\x2a\xfb\xf8\x78\x07\x1f\x37\x41\xdc\x3c\x96\x01\x35\xcb\xba\xd1\xb9\xd9\xe3\x97\x02\x45\x0a\x39\x41\x7f\xa8\xb2\x59\xcb\x05\x47\xb5\xae\x6b\x0c\xaf\x81\xac\xc2\x69\x21\x66\x2f\xbf\x93\x49\xe2\x84\xe4\x38\x2e\x31\x93\x8d\x64\xb6\xa6\x1a\x27\x09\x37\xa4\x2d\x60\x56\x5b\xda\xd6\x26\x70\x91\xdc\xf6\x51\x78\x50\xa0\xc5\xc3\xd0\x28\x87\x9e\xe3\x3a\xd8\x5a\x6f\xc1\xe5\x50\xd1\xe9\x91\x2f\xed\x95\x3b\x11\xa4\xc1\x07\xd5\x99\x9b\x41\x56\x36\x04\x70\x74\xe3\x2d\xc2\x66\xdc\xca\x68\x41\x95\xa1\x9d\xfa\x5a\xf2\x06\xdf\xd0\x71\xa8\x85\x86\x6c\x8c\xa3\x7c\x74\xa1\xaf\x41\x5c\x68\xc9\x72\x36\xf3\x20\xf8\x1a\x78\x3b\xdf\xe8\x82\xdb\xa5\xbf\xbe\xfc\xe6\x49\xd7\x55\x38\xbd\x36\x91\xe7\x6a\x28\xad\x26\x49\x0b\xae\xb6\x56\x29\x91\xb4\x2b\x42\xba\x73\xcf\x2b\xe4\x57\x4c\xa9\xd4\x64\x5a\x76\x31\x97\xf4\xb3\xae\x90\x5d\x83\x0e\x3f\x7e\x5f\x85\xd9\xfb\xa2\x1d\xb0\x74\x97\xbb\x0d\x16\x26\x67\xfb\x94\x9f\xf6\x85\x9d\x89\xad\xd2\xa6\xc0\xc3\xe2\x87\xaf\x02\x7a\xa6\x73\xae\x82\x41\x13\xac\xd9\x39\x8c\xe0\x59\xc6\x17\x41\xc6\x17\xc1\x26\x5c\xd0\xda\xd7\x1b\x66\xfb\x53\xa6\xfa\xdf\xd8\x0b\x03\x0d\xc7\xc9\x83\x79\x53\xe3\x45\x82\xae\x17\xba\x03\x1a\x7d\x6a\x73\x6c\x7b\x61\x75\x6c\x7b\x61\xc5\x25\xb3\xd1\x0d\xac\x74\x03\x4e\x57\x77\x4b\x7b\x61\x75\x6c\x7b\x61\x75\x6c\xb3\xd1\x1d\x5b\xe9\x1e\x71\xba\xba\x0b\x9b\x8d\xee\x91\x95\xee\x91\xa5\x1f\x1e\x67\x6a\x71\x19\x7b\x71\x9e\xfe\x51\xcf\x36\x46\x04\x3d\xb1\x97\xb3\x86\xae\x6d\x53\x0f\x88\x94\xf9\x6c\xee\xc1\x6a\xfa\xd1\x83\xd5\x2c\xf5\x60\x75\x15\x7b\xb0\xa6\x7f\xae\xe9\x9f\x6b\xfa\xe7\xbd\xc5\x66\x82\x69\x91\xd1\x26\xf5\xc1\x9a\x20\xd9\x6d\x86\xd9\xe0\x9c\xd5\xe6\x6e\x73\xaf\x3c\xb7\xab\x1c\x74\x6c\x97\xc6\x95\x67\x9c\xa7\x01\x2d\xfe\xe7\x7f\x04\xf6\x10\x17\xe1\x8f\x55\x65\x34\xd8\x10\x5c\x6b\xb8\x96\x2b\x50\x98\x68\x51\xa5\xfa\x31\x22\x75\xa0\x01\xfe\x8a\x30\xcb\xb4\xec\xdb\xed\xf4\xb0\x62\x6e\xac\xc2\xd3\x7c\x35\x15\x11\x4c\xcc\x3a\xca\x19\x5d\x73\xff\xf3\xf2\xf1\x6a\xea\xf6\x27\xea\x4d\xb9\xbb\xdc\x88\xbb\xea\x39\xb2\xbf\xd6\xc6\x71\x32\x47\x67\xd3\xbf\xd1\xec\x14\xab\x63\x9c\x82\x7b\x59\x03\xef\xaf\xf8\xc7\xb9\x15\x17\xb9\x4d\x0e\xc0\x62\x9d\xa9\x40\xe7\x26\xb0\xb3\xb1\x11\x09\x9b\xc7\x10\x05\x35\x91\x8d\xeb\xba\x3c\x0a\xb6\xad\xc1\x65\xae\xab\x8f\x42\x52\x6d\x3e\x2b\x1d\x12\xa8\x2b\xbd\x5c\x6b\x2f\x35\x9f\x3a\x69\x74\xe5\xe7\xab\xe9\x47\xb7\x13\xd6\x1a\x5f\xd6\xb8\x61\x1d\x26\xf9\x32\x23\x74\x0a\x2f\xd2\x38\x29\xd0\xd9\x4e\x76\xc5\xc2\xc5\x41\xab\x28\x52\xa0\x2a\x8d\xd3\x07\x8b\x7e\xf3\x16\xeb\xeb\xc0\xfd\xd4\x1a\x9f\x79\x3f\xf5\x78\xb4\xe5\x47\x8f\xb3\xcd\x04\x18\xfe\x1a\x9b\xcc\x71\x23\x65\xcc\xdc\xfa\x4a\xd5\x1f\xf9\xba\x8b\x8b\x2b\x1b\xa6\xb7\x8c\xe5\xfd\x11\x1d\xa9\x3e\x7a\x8c\xa1\xb3\xf3\x7d\x88\x7d\x7f\x1f\xee\x95\xac\x64\x4a\x69\x9e\x8e\xdb\x74\xa6\x12\x02\x14\x46\xf0\xf9\x7f\x7d\x3a\xb5\xa1\xa5\x56\x35\xaf\xa7\x1f\xf7\xc1\xf7\x31\x47\x5b\x60\x37\x58\x4c\xed\x73\x6f\x8d\xd1\x3a\x6b\x0c\x81\x7c\x5e\x56\x6a\xf9\xee\x2a\x2e\xbf\xeb\x57\xcc\x3d\x58\xed\x2c\x15\x54\x3f\xf6\x3c\xcb\x2c\xd5\x85\x1b\x16\xd0\x19\xbb\x2d\x2a\x2b\xe4\x06\x9d\xd3\x6e\xea\xf9\xc1\xaf\xaf\x58\xaf\x9f\xdd\x98\x3c\xd9\xbe\xa7\x5a\x20\x6d\xc7\x0b\xac\xa7\x8b\xe5\x5f\xd0\x1e\x70\x90\x67\x5d\x33\x4b\xcb\x02\x6d\x68\xc3\x5c\x62\x0b\xa1\x6c\xa5\x3f\xba\x70\xcf\xa2\x41\xc1\xe2\x3b\x29\x06\xa1\x8d\xc4\x58\xd2\x7b\x76\xcf\x8c\x94\xe7\x5a\x13\xf0\x31\xe7\xf6\xca\x91\xaf\x84\xd6\xc6\xb0\xf0\x29\xc9\xe7\x48\xb2\x91\xdd\x93\xb1\x39\xd2\xd9\xb4\xf8\xb5\x4c\x6d\x43\xd5\xed\xea\x0e\x7a\x2e\xf1\x78\x5f\xc6\x99\x36\xba\x4c\x57\xdc\xeb\xe8\xea\xdd\xd2\xbb\xee\xfb\xfa\xc8\x6c\x93\xfe\x16\x37\x7c\xd5\xb1\x20\xbe\x80\x6c\x8e\x97\xdd\x1b\x9c\x61\xf2\x48\x72\x63\xf9\x6c\xd4\x28\xf9\xb2\x0c\xfe\xa1\x0e\x31\x22\x57\x74\xbb\x03\x95\x07\xa0\x76\xf1\x9d\x47\xda\x4d\xb9\xb5\x90\x71\xbf\x5e\x57\x6c\x5b\x7f\x18\x67\xd1\xe6\x2e\x31\x82\x84\xaa\x36\xb3\x94\xc1\xdb\x79\xc5\xc8\x1f\xa2\xa3\x64\x1e\xc9\x4e\x31\x8a\x9b\x0a\xa5\x3f\x10\x5e\x31\xaf\x3d\x39\xe7\x38\xde\xbb\x61\xa1\x3d\x4a\xc1\x10\x71\xd9\x9c\x65\x4d\x73\x91\x96\x3c\x60\x5e\x37\xf6\x80\xb1\xfa\x1e\xe4\x11\x1c\x48\xae\x39\x65\x9f\xec\x6d\xeb\x0a\x93\xb3\x3b\x82\x8c\x2c\x32\x92\x93\xa4\xb0\xe2\x02\xcb\x40\x67\x89\xd5\xf3\x80\xea\x0b\x32\xfd\x32\xeb\x0e\x2e\x4b\xb5\x61\x45\x0a\xd1\x02\x2e\xe2\x15\x89\xdc\x59\xc2\xfe\xeb\x3b\xa6\x69\xf7\x64\xdb\x7b\x9a\x6d\x20\xb0\xc9\xc3\x4d\x2f\xbe\xfd\x02\xad\x2b\xbf\xcd\x2a\x95\xe8\x34\x72\x60\x13\xa0\x9c\x98\x71\xbe\x6d\x3d\x86\x46\x0b\xd9\x2d\xd8\xe9\xb4\xf6\x06\xd7\x0d\x77\x4c\x1b\xd0\xd9\x8f\x06\x3f\xba\xce\xe8\xbb\xef\xbe\x14\x70\x12\xfb\xa8\x42\x57\x4b\x41\x74\xa4\x5c\x89\x7b\xce\x3f\xfd\xce\x35\x4e\x0b\xb2\x07\x61\xce\x74\xe5\x7f\x09\x6f\xc3\xcf\xd3\x2c\x5e\x14\xad\x5c\xb8\xe7\x16\xeb\x05\xf1\xa0\xed\x07\x9d\x5e\x91\xfe\x42\x99\xa2\x4a\x71\x9c\x43\x2b\x68\x29\xee\x24\x93\x65\xc1\xb2\x55\xb6\x7d\xbc\x0e\x0a\x06\x46\x09\x3f\x68\xfd\x93\x2d\x5b\xc6\xfe\xa3\x16\xac\xa3\xe8\xe6\xa5\x2a\x0a\x6e\xbf\x48\xed\x25\xb7\x59\x9e\x48\xe1\xd1\x0b\xb3\xe1\x52\x1c\x9a\xfe\x5b\x07\xa6\xc7\xa8\xb2\x42\x87\xc1\xff\xd7\x15\x4a\x94\x0d\x8b\xaf\xc2\xb9\xb9\xec\xd8\xd2\x55\x47\x9c\x79\x6f\xb2\x7e\x2a\x74\x77\x2b\xca\xd1\x22\x4b\x17\x24\x2b\x62\xd5\x3f\x94\x3b\xb5\xfc\x72\xfc\xe9\xd7\xf7\xa7\x90\x4e\xae\xc9\xb4\x80\x76\x4e\x88\x14\x5a\x32\x4d\x93\x8b\xf8\xb2\xe3\x9a\xbf\xbc\xec\x48\x1e\xb9\x17\xec\x3f\xce\xe2\x67\xb2\x08\xb3\xb0\x48\x33\xd8\x83\x56\x4f\x5d\xcf\xf8\x73\x99\xa5\xcb\x85\xf2\x95\xe7\xfc\x2a\xbe\x27\xb0\x07\x2f\xcd\xd7\x39\x99\xa6\x78\x33\xf9\x17\xe9\xbb\xc0\xfc\xee\x22\x0b\x71\xd2\xfd\xc5\xa8\xf4\xf7\xd5\xfb\xa0\xe5\x01\x47\x9f\x49\x13\x1f\x8d\xf9\xb4\x0f\xf3\x45\x38\x25\x1b\x28\xf1\x0a\xe5\xaf\xbe\xed\xff\x93\xc9\x21\xb6\xcc\x1e\x27\x88\x1c\x65\x1b\x48\x22\x51\xf2\x11\xa2\xc8\x5e\x74\x2b\x59\x84\x24\x1a\xbb\xb9\xe7\x05\x02\xfa\x3f\x46\x1e\x0d\xb6\x97\x47\xa6\x7f\x00\xbb\xb6\x65\x78\xcb\xb6\x23\x38\x83\x2e\xb5\x5a\x05\xc2\x8c\xb2\x9e\x17\x59\x2f\x5f\xcc\xe2\xa2\xdd\xea\xb5\x3a\xf6\x2f\x2f\x07\x30\x82\x2e\x5b\xd8\xbd\x72\xd9\x39\xbe\x1d\x4a\xdf\x9a\x6b\xd0\x51\x48\x5d\x7b\x23\x50\xea\x12\xcf\xed\x65\xe3\xa4\x38\x09\xd1\xef\x33\xcc\xb2\xb3\xe0\xdc\xfe\x95\x58\x9b\xd2\xa7\x03\xc7\xa7\x02\xa7\xbb\x82\xe1\x76\x56\x5c\x9e\xfe\x58\x99\x03\xc1\x4c\x75\x3d\xba\x27\x1e\xd9\x69\xcc\x08\x3d\x11\x96\x94\xec\xd6\x50\xe0\xe3\x7c\x39\xec\xa0\x15\xef\x72\xe0\xb1\x21\xb9\x1c\x7a\xac\xbf\x63\x0f\x29\xf9\xa6\xb1\x52\x94\xa5\x05\xde\x31\x3f\x4d\xfa\x65\x5d\x08\x31\xad\x82\x7e\xf3\x9c\x96\x79\x78\x80\x4b\xc7\xf5\x77\xd5\xf1\x15\xff\xf9\x72\x92\x17\x19\xfa\x09\xc5\x4e\xb7\x03\xe1\x31\x15\xc3\x5b\x5a\x11\xfd\xa5\x4b\x5b\x55\x6b\xb4\x17\xb5\xd1\x2f\xd5\xd9\xd2\xb5\xd4\x1f\x63\x0f\x39\xae\x6e\x1d\x57\xb6\xac\x9b\x86\xbc\x6f\x1a\xd6\x87\xe3\x1c\x3b\x2a\xa2\x14\x71\x66\x74\xa4\xce\x6a\xf9\x2d\x46\x82\xfe\xdd\xc8\x0a\xc7\x65\x8c\x34\x83\x2d\xb5\x1d\x54\x1c\x8b\xd5\x63\x6c\xb7\xcc\x32\xa9\xac\x50\x73\xcf\xb2\xa7\x3f\x3f\x50\xea\xef\x65\x04\xc5\x39\x4b\x51\xf2\x89\x5c\x1e\xae\x16\x6d\x68\xfd\xfe\x7b\xf4\x95\x36\xee\x72\x08\x5d\x68\x7d\xfb\xfd\xf7\x0f\x2d\x0f\x5a\x97\x2d\x70\xc8\x15\x80\xd6\xff\xfc\xb1\x55\x31\xec\xd8\x8a\xed\x1c\xed\xa9\x6b\xda\xf6\xd1\x9e\xbd\x9b\xed\x46\xc5\xbc\xc8\x1e\xa5\xeb\x31\xab\x7f\x63\x8d\x2f\xcc\x69\xd1\x78\xbe\x98\x91\xb2\x01\x78\x0e\x92\xeb\xa8\xf6\x39\x7a\x20\x22\xac\x1b\x10\xcc\xaf\x7a\x11\x91\x24\x9d\xc7\x09\x7d\xc5\x50\xb9\xa4\x07\xa5\x6b\x47\x08\x8b\x34\x8f\x8b\xf8\x56\xcd\x61\xc3\x21\x1c\x38\x93\x6e\x5c\xf8\x7c\x41\xa6\xf1\x45\x4c\xa2\xd2\xd2\xa9\xd4\x7a\x74\x51\xd9\x40\x65\xfa\x32\x27\x31\x53\x17\x4a\x4a\x2c\x42\xc7\xc6\x2b\x0b\xb6\xbf\x23\x79\xc1\xf9\x4a\xc8\x94\xe4\x79\x98\xad\xa1\x48\x15\xd3\x8d\xe8\x6b\x39\xa0\x13\xbd\x64\x14\xff\x3c\x55\xc3\x9a\x57\xc6\xd2\x07\x36\x72\x0f\xe5\xa0\x94\xf1\x4a\x18\xd4\x89\x1d\xfd\xb6\xba\x6b\xc5\xce\xb5\xb5\xbf\x46\x71\xe2\x03\xdb\xee\xd0\x82\x4a\x6b\x0d\x35\x66\x6e\x9a\x34\x6b\x8a\x6b\xaa\x8c\x56\x58\x53\x65\xc4\xf4\x92\x95\x99\x79\x64\x53\x63\xc2\x2c\xf3\x20\x0a\x3c\x88\x86\x78\xb5\x4f\x56\x0b\x0c\x93\x48\x02\xcf\x01\xbb\x76\x03\x23\x1e\x7c\xf5\x34\x57\xf7\x91\x61\x19\x3d\x1e\x1f\x5a\xe4\x46\x42\x37\xbe\x28\x68\xf8\x75\x44\xbf\x4e\xec\x5f\xdb\x34\xa9\x79\x54\xaa\x6e\xb6\xcd\x88\x47\x9b\x8d\x58\xb0\x99\x29\xbc\x4d\xf3\xee\x3c\xb2\xec\x0d\x25\x99\x1b\xd7\x6e\xfd\x8c\x5d\x8b\x25\x2c\xd9\x4b\xbb\x03\x1d\x84\xa7\xea\xcd\x0a\x64\xbd\x1e\x74\x57\xc4\x9a\xbb\x37\x53\x1e\x85\x3e\x1c\xba\x7d\x6c\x01\x5a\xfa\xdc\x6b\xe1\x06\x72\x03\x07\xd0\x92\xa7\x61\x8b\x9e\x95\xd4\x69\x4d\x25\x3e\xed\xcb\xed\x36\xe0\x7e\x5f\x0f\xe7\xdb\x73\x7d\x77\x74\x41\xc9\x2b\xb9\x81\xd2\xc4\x8f\xa5\x55\xcc\xad\xd7\xc0\xd2\xac\x4b\x72\x93\xae\xee\x65\x4e\xc0\xe5\x58\x36\xa7\x33\xf1\xd9\x0d\x77\x6c\xa3\xff\x55\x56\xec\x44\x84\x95\x0e\xa0\xd3\xbb\x2c\x08\x1b\x8c\x03\x48\xb8\x9a\xbe\x25\x22\x78\xe9\xb4\x50\x81\xc1\xf7\x0a\xe1\x7a\xad\x5f\x0a\x18\x51\x22\xab\x69\x2d\x0a\x78\xcc\x03\x69\xec\x12\x8b\x7d\xcd\xbb\x10\x83\x42\x59\x4c\x68\xe9\x73\x32\x8f\x13\x14\x78\xfa\x24\x28\xae\xc2\x4a\x9a\xcb\x57\x65\xa6\x00\xe6\x4e\x06\x11\xfa\x33\xe4\x15\xcc\xc0\xaa\x47\x4c\xb0\x81\x88\xa7\x89\x81\x93\xe3\xbf\x7d\xfe\xfb\xe9\xe1\xf8\x0c\xda\x54\x0c\xa1\x2f\xff\x73\xd9\x33\x81\x45\xe5\x49\xa0\x04\xf4\xab\x3d\xfc\x57\xf3\xaa\x60\x43\x39\x8f\xd8\xda\x99\xce\x17\xed\x08\x33\x80\x21\xe0\x3c\xe1\xbf\x45\x74\xec\x06\xe2\x66\x42\xe5\x1e\xab\xe7\xa0\x1f\x2a\x69\x81\x04\xc2\x83\x5d\x4c\xe0\x04\x55\x0c\x18\x81\xcf\xfd\x3e\x13\x4b\x28\xa0\x54\xb0\x91\x24\x10\x5d\xa1\xdf\x99\xcb\xa1\x06\x75\x00\x81\x89\x07\x91\x99\x5f\xb0\xec\xe9\x21\x4a\x50\x1e\x69\x20\x90\x00\xa3\x81\x15\x6f\x87\x05\x68\x0f\xb1\xf3\xe8\xd6\x81\xd1\x3d\xce\x2c\xec\x11\x6b\x92\xe5\x05\x4a\xed\xa1\x45\x62\x32\x01\xad\x31\xc3\x78\xc4\x51\xb1\xf0\xc4\x3a\xce\x42\x0c\xf7\x10\x1d\xe2\x90\xb7\xd7\x41\xca\x42\x49\x5b\xa4\xac\x38\xed\xd6\x79\xc4\x69\x47\x41\xc7\x83\x68\x60\xef\x62\xb6\xdb\x88\x16\x45\x43\xce\x46\x62\xf6\x2f\xeb\x2d\xe3\x4b\xcb\x48\x24\x01\x73\xbd\x1a\xf4\x72\xb0\x78\xda\x10\x78\x31\x82\xe1\xe6\x94\x00\xa5\xbe\x19\xe7\x30\x9d\xa5\x39\xc3\x7c\x59\xd1\x6d\xbe\x1f\x61\x62\xa8\x64\xd0\x8f\x06\x0a\x19\x66\x25\x60\xd3\x6a\xc0\x5a\x4d\x8c\x7c\xa0\xbc\x5f\x56\x9d\x5e\x38\xc9\xdb\x1d\x9c\x2d\x16\xe1\xca\xa8\x04\x4c\xd3\xd8\x48\x05\x97\xfa\xc0\x71\x1a\x39\xa3\x9d\x51\xc9\x4a\xca\x98\xf4\x27\x9c\x3b\x8e\x0c\x67\xb4\x2b\x95\x62\x81\x52\x4c\xeb\xc3\x6a\x95\x93\xd5\xc2\x7a\xcd\x19\x66\x5b\x9d\x1a\xea\x4e\x06\xd3\x34\xb9\x25\x59\x21\x7c\x0b\xb8\x5e\xbb\xc8\xe2\x39\x2a\xf0\x4e\xff\xcd\x94\x97\x6f\x82\x87\x69\x01\xca\xad\x7c\xad\xf0\x48\x70\x15\xe6\x22\xda\x05\xbd\x18\xac\xe0\xd1\xdd\x15\x15\xa9\x2c\x57\xfc\x01\xfe\xfb\x02\xc3\xa4\x79\x72\x92\xe6\xfd\x11\x3e\xd2\x09\x83\xea\x2f\x25\x38\x01\xdb\xbe\x12\xc5\x16\x7a\x74\x01\x89\x9c\xbc\x8d\x6b\x03\x8f\x8e\x3a\xa6\x04\x4f\x8e\xff\xf6\xf7\x93\x4f\x87\x3f\x1f\x7d\x3e\x3a\x1e\x8b\x83\x4d\x20\x14\x8d\x22\xd5\x3e\xb0\xdc\x2c\xbb\x8e\x0c\x89\x0d\x39\xe1\x4d\x10\xbc\x1a\xbc\x79\x33\xdc\xdd\x79\xb5\x13\xbc\x79\x33\xa4\x35\x18\xcf\xec\x96\xe0\xf6\x09\xc9\x2e\xd2\x6c\x9e\xc3\xee\x0e\xcc\xd2\x74\x51\x05\xbe\xe4\xb8\x8b\x60\xbe\x52\x9d\x58\xaf\xe3\x3a\xd1\x2c\xd2\xbb\x76\xa7\x8a\xe8\x32\xce\x30\x89\x76\x84\xd1\xbe\xd7\x0e\x2d\x49\xcd\x99\xe5\x04\xc7\x72\x04\x27\xbd\x45\x7a\xa7\x4c\xe9\xc4\x36\xa7\x6f\x3c\xb0\x24\x63\x8a\x61\x04\xf3\xb0\xb8\x62\x60\xc1\x3c\x3d\xf7\x01\xf8\x54\x4d\xeb\x26\x36\x6b\x84\x7b\x6d\x60\x7c\xb0\xe4\x95\x2a\x02\x85\x29\x7b\xf1\x45\xd5\x46\x0d\x9c\xc2\x86\x03\x2d\x5b\x82\x93\x86\x70\x0c\xc3\x97\x1e\xb4\x44\x25\x2d\xe8\xc0\x8f\x3f\x6a\xcc\x23\x61\x96\xe0\x92\xf6\xd1\xc3\x03\xc4\x36\xa4\x03\x0c\xd0\x4e\xa0\xcf\x82\x85\x2d\x58\x28\x8b\x30\xcb\xc9\x2f\xb3\x34\x2c\x28\x19\x7a\x0a\x42\xdf\xe0\x36\xf3\xae\xa7\x0b\xdb\x85\xbf\x62\x0d\x0e\x17\x9d\xd4\x86\x2e\xdd\x60\xcc\xed\xd7\x8c\x77\xe5\xc1\x20\x74\xd5\x90\x70\x7a\xc5\xd4\xdd\x78\x1a\x63\xd6\x02\xb4\xb2\xa0\x60\xe4\x7a\x64\x7a\x01\x37\x3c\xba\x15\xbf\x16\x30\xc2\x53\x76\x83\x46\xbe\x2c\xc3\x82\xe4\x7a\x1d\x54\x64\x54\xd5\x58\xdc\xb4\x8c\x65\xdc\x85\xb3\xe1\x6b\x0f\x76\x06\xe7\x1e\xc3\x2b\x66\x51\x35\xa5\x85\x45\xaf\x41\x28\xce\xe9\x05\x0c\x5f\xc3\xe5\x32\xcc\x22\x41\x3b\x23\x45\x18\x27\x24\xea\x41\xfb\x37\x14\x0e\x5d\x18\xf4\x76\x79\x9c\xec\x25\x95\x52\x67\x6f\x3c\x18\x0e\xce\x95\x62\x3d\xd5\xb4\x75\xc3\x74\x64\x89\xc3\x03\x9c\xec\x53\x12\xcf\xda\xda\x9b\xbe\xac\x26\x0f\x51\xc7\xdd\x98\x72\xcc\x72\x34\x56\x43\x5c\xed\x3a\x62\xcc\xe2\xc2\x5d\x1e\xe0\xe8\x70\xcd\xf4\x1b\x57\x44\x4d\x05\x47\xee\xd4\x2f\xcb\xcf\xf0\x78\xb6\xae\x12\x96\xbe\x83\x1b\xe8\xc8\x0f\xf0\x6c\xad\x17\xb5\x9c\x3b\x35\x51\x11\xb3\x90\x77\x87\x1e\xfc\x2c\xae\x58\xb3\x8a\x90\x55\x4d\x13\x2b\xbe\x39\xd4\xfa\x4a\x67\x7f\x55\xc3\xbe\xed\xe4\x98\x70\x64\x49\xda\xb9\xc7\xe3\xc3\x1e\x55\xaf\x74\x94\x5c\xbe\x38\x6f\x2a\x67\xac\xb5\xa7\x4e\x12\x43\x0d\x83\x3d\xd9\x73\xfc\x7b\xdc\x7a\x4b\x3e\x94\x56\x5f\x49\xb9\x12\xbb\xf3\x89\x71\x09\x8e\xc6\xc8\x3c\x32\x53\x82\xea\x78\x6f\xca\xb6\x2b\x19\x18\x35\xab\xa2\x9e\x92\xbe\x6a\x8e\xe3\xca\x9e\x87\xdc\x29\xb1\xee\x2f\x36\x61\xe0\xfc\xb3\xf8\x73\x9e\x48\x60\x8a\xdb\x79\x67\xda\x4b\x6e\x76\x06\x95\xcb\x6d\x7f\x0f\xec\x2e\xbd\xcd\x55\xf0\x89\x04\x5f\xb9\xd9\xbd\xd3\xea\x9a\x92\x47\xae\xab\x60\xd9\x89\x73\xb8\xa3\x39\x71\x6a\xc2\xe3\x80\xfb\x72\xaa\x97\xc1\x3b\xff\x00\xe7\x14\x74\x3e\x9f\xa0\xab\x17\xfe\x3a\xc0\x40\x98\x09\xc4\xaa\xd7\x08\x77\xf7\x62\x17\x02\xf8\x61\x9c\xcb\x26\x7f\x9c\x63\x2c\x5c\x9c\x51\xf1\x9a\xe9\xe3\x4a\x33\x8c\xe5\x5e\x55\xa5\xdd\x30\x30\x1b\x96\xd2\x96\xab\x30\x97\x2e\x43\xca\x75\x29\x57\x80\x96\xad\x38\xaf\x6e\x40\xd2\x4c\x4d\xec\x7e\x7a\x4c\x0f\x83\x7f\x3f\x39\xfe\x8c\x1d\x12\x56\x87\x8b\x52\x0b\x94\xcb\x96\x72\x48\xae\x84\xd3\x18\x1f\xfe\xc5\x13\x33\x66\x2b\x19\x31\x39\xb7\x1c\x15\xf0\x68\xf0\xd3\x4e\xa3\xa5\x5c\x9e\x75\xb1\xef\x8c\xc5\x34\x31\xd6\x92\x5a\x40\x5b\x3f\x93\x9a\xe5\xc3\xc1\x3d\xe4\xb5\x33\x71\xb8\x50\x58\xcc\xfc\xce\x2b\x84\x9c\x61\x27\xd8\xb1\x96\x92\x1e\xb1\x45\xeb\x28\x79\x5b\x2c\x81\x7c\x04\x91\x31\x4a\xb3\xbf\x75\xef\xce\x5d\xba\x0d\xbb\x9e\x6d\x89\x5a\x2c\x00\x3c\x20\x03\x8f\xd0\x1d\x9b\x17\xc1\x9b\x60\xe5\xde\x51\xfc\xd4\xc6\x91\xf1\xd2\xe3\x70\x6c\xa9\x4b\x33\x38\xbb\xe8\x30\x1a\x1a\xd8\x47\x42\x75\x2f\xd7\x95\xc4\xc4\xe9\x34\x3e\xf1\x60\xe8\xc1\x4f\x3b\x1e\x0c\x77\x3d\x68\xd1\x79\xd2\x72\x9c\x15\xaa\xaa\x09\xbc\x1d\x49\xab\x81\x41\x0b\xbd\x1b\x49\x8b\xcc\x5a\x98\x0a\x43\xd5\x2f\x18\xe7\x0f\xb8\xee\xad\x01\xf6\x80\x3b\x18\x22\xb0\x77\xf5\xf9\xa3\x3a\x9d\xdb\x68\x3e\x84\x39\x69\x3b\xe8\x7a\x30\xa1\xb2\xda\x43\x29\x97\xdb\xb3\x84\xda\x7b\xb8\x44\xd2\x4a\x04\xe8\x4d\xed\x3c\x79\xa2\x6b\xec\xc7\x47\x1f\xf3\xd8\x7d\x6e\xaf\xba\xbb\x4a\x67\xea\x2d\xb3\x81\x44\xad\xc8\x87\xb2\xf4\x48\xfc\xd5\x24\xb9\xbc\xb8\x8b\x31\x63\x28\x3c\x05\xf7\x73\x60\x6c\x8e\x1b\xfa\x81\x9e\xfc\xd8\x3a\xf0\xd0\xa9\x38\x4a\x99\x70\x9c\x4e\xc9\xa2\x10\x5b\x8d\xc0\x45\x74\x35\x0a\xbb\xea\xf8\x82\x35\x29\xfd\x97\xcf\xc7\xe3\xc6\xf9\xfe\x6d\xf7\x3e\xea\xf0\xf5\xe1\xfd\x2c\x0e\x73\xc2\x2c\x38\x1f\xe2\x4b\xe1\x27\x38\x27\xc5\x55\x1a\x49\xb1\x09\xfd\xbe\xc8\x53\xc2\x12\x97\xec\x83\x44\x84\xbd\x62\x3b\x32\x89\x20\x9c\xa4\x92\x73\x00\x2d\x59\xe6\x7f\x1c\x89\x54\xd9\xfb\xa2\x64\xbe\x9c\xd4\x95\xe4\x47\xf0\x35\x6b\x3f\x3d\x09\x95\x25\xe7\xcb\x59\x5d\xc9\x28\xbe\x8d\x23\x82\xe5\xa2\xf8\x76\x5f\x79\x97\x91\x79\x18\x27\x11\x37\x0b\xcd\xd3\x48\x7d\xcd\x53\xc1\xf3\xfc\xf2\xd3\xf9\x42\x7d\xcd\xb2\x06\x89\xa4\x41\x72\x87\x72\xd8\xa4\xe4\x22\xbe\x3c\x9e\x5c\x4b\xf7\xbe\xba\xab\x6b\xbb\xfc\x48\x16\x8e\x7c\xf0\xca\x8f\x59\xb5\xdf\x78\x05\xb4\xd1\x9f\x8e\xfe\xf5\xfd\xe9\x21\xfc\xf5\xf0\xe3\xc9\xe1\x27\xf8\xe5\xb7\xf1\xcf\xa7\x47\xc7\xe3\xcf\xfc\x8b\x72\x5a\x94\x71\xb6\x8a\x85\x0b\x1d\xf7\xe8\x8e\xa6\x46\xd8\x08\x63\x0b\x5e\x5e\x3d\x3c\x20\x1a\xce\x08\x62\x38\x80\x18\xf6\x20\xae\xee\xd5\x24\x46\xca\xa5\x6e\x1a\x54\x98\x37\x0a\xdb\xa8\xd3\x8b\x52\xc7\xe3\x07\x0e\x95\x4f\x75\xb3\x08\x75\x66\x73\x0f\xee\xd5\x4d\x99\xb2\x3f\x50\x1f\x5d\xc3\x08\x42\x7e\xc8\x55\xdf\xd0\xd1\x0d\xa9\xd4\xeb\x42\xab\x25\x75\xb3\xec\x2c\x76\x6d\x46\x84\xe6\x58\x0c\xd3\x8b\xb2\x82\xf2\xcb\x7b\x18\x55\x06\x10\x1f\x72\x6b\x24\xb2\xa8\xe0\xde\xf7\xf7\x91\x5c\x2b\x40\x71\xab\x4b\xee\x0c\xba\x4a\xae\xae\x6f\xca\xd2\xac\xae\x5a\x54\x54\xf3\x9e\xd6\x90\x6b\x04\x2d\xe3\x7c\x40\x86\xc8\x29\x3f\xa7\x11\x79\x5f\xb4\x7d\xff\x9a\x21\x74\xed\xbc\xde\x97\x2b\x17\x92\x4f\xc2\xe0\xba\x46\xd7\xd0\x87\x87\x4a\xcc\x49\xc3\xfd\x33\x5b\x10\xaa\xec\x2e\xa7\x69\xae\x66\x85\x94\x46\x17\x4b\xb1\xec\x62\xa0\x0f\x6e\xe8\xc1\x44\x1d\x2f\xbb\x27\x87\xc8\x8e\x63\x4e\x04\x03\x1e\xf3\x9a\x21\x0a\x78\x86\x71\xcb\xc0\xcb\x9c\xb1\x68\xfd\x7d\xa5\xc3\x6d\x59\x8c\x84\x91\x86\x2a\x28\xd7\x3a\xee\x7d\xf9\x15\x4b\x0d\x52\x21\x23\x54\x3d\xcd\x32\x83\x54\xe0\x08\xd6\x1a\x55\x34\x4d\x9e\xe6\xe3\xe1\x01\xa4\x94\x49\x21\x1c\xc0\x84\x23\xc7\xfa\xd7\x74\x5d\xaa\x94\xec\xe9\x92\xb8\x19\xed\xd9\x08\x24\xde\x63\x8d\x71\xba\x0c\x02\x95\xe3\x1b\xaa\x98\xcd\xac\xbc\x9a\xe9\xe8\x9d\x79\x84\x04\xbf\xf4\xdd\xff\xc5\x16\x0c\x28\xf7\x03\x95\xae\x98\x5b\x96\xb4\xd8\x8c\xb2\xd4\x0b\x37\xf0\x0e\x66\x2e\x5a\x2c\x41\xf5\x8d\x25\x2f\xf5\x4c\x4b\x85\x7c\x00\x37\xb0\xa7\xb7\x4f\xf0\xe1\xca\x83\x5d\x85\xbc\x07\xa5\xe8\x88\x31\x59\xb5\x48\x07\x1c\x8b\xac\xd4\xf1\xb9\x9c\x8b\x9f\xfe\xf9\x8e\x3f\xde\xdc\x0d\x8c\x49\xa9\x13\xca\xa6\xd3\x21\xe1\x3d\x6a\xeb\x07\x90\x97\x2c\xd7\x47\xaa\x4b\xc2\x6c\x49\x18\xd8\x36\x7a\x67\xdc\x52\x45\x5b\xd8\xb1\xe2\x84\x9d\xbd\xbc\x0a\xae\x9a\xb9\xca\xf4\x04\x91\xdf\xe8\x13\x7a\x3c\x15\x90\xd3\x58\x9e\x19\xc5\x31\x4b\xae\xe6\x63\x53\x16\x64\x51\x59\x78\x0d\x40\xd5\xfa\xd6\x80\x74\x07\x2d\x7e\xe3\x4f\x35\x22\xe9\x82\x40\x7e\x27\x90\x30\xb8\x22\x54\x0a\x94\x38\x29\xfe\x95\xd5\x9c\x66\xe3\xf4\x30\xcb\xd2\x2c\xc7\x7b\x8f\x79\x4c\xff\x09\x57\x8a\x90\xe1\x1d\xc7\x6e\x1b\x4a\x0c\xe8\x84\x9e\x22\xde\x8d\x68\x11\x06\xfd\xf6\x76\x44\x4b\xaa\xfd\x57\xd5\x98\xbf\xa7\x9b\x5a\x3b\x9d\x5c\x5b\x48\x1f\x63\x30\x4b\x6f\x91\xa5\x45\x5a\xac\x17\xa4\xd4\xb8\x7a\xd3\x70\x36\x63\x85\x46\x23\x68\x9d\xf1\xa8\x17\xa4\x75\xde\x72\x8d\xd5\xcf\xec\x24\xa0\x6d\x9e\x47\x09\xf7\x36\x2a\x53\x87\x27\x5c\xea\xf2\x0f\x8e\x97\x45\xd9\xe1\x87\x97\x3d\xe5\x40\xd1\x1a\xee\xee\xb6\xd8\xb9\x61\xf0\x93\x98\x93\x39\x9c\x0d\x76\x3d\x18\xec\x9e\xbb\xcb\x5d\x5c\xb4\x18\x70\xf7\x20\x90\x8a\x0d\x3d\xd8\xf5\xa0\x2a\xa7\x8d\x4f\x91\x7e\x60\xfc\xf0\xa3\x0b\x63\xdf\x13\x5c\x1a\x5b\xc0\xb5\x2a\x94\x99\x5f\x80\xe1\x6f\x1f\x66\xd9\x47\x53\xf8\x6b\x1e\xf4\xcc\xeb\x3d\x2f\x32\xd3\xdf\xdd\xf0\x0d\xd7\x37\x7c\x9e\xbb\x28\xcb\x3e\x32\x37\xfe\x72\x2f\xa5\x8f\xe8\x16\x1e\x66\xd9\x19\xfd\xfd\x1c\x5e\x8c\xc4\x98\x68\x5b\x39\xfd\x04\x25\x50\x00\x98\x92\xfb\xfd\xc7\x93\xbf\xbe\xff\x70\x78\x5a\x42\xc4\x22\x6f\x1c\xd4\x8c\xcb\x0d\xd7\x9d\xc8\x35\xbc\x55\xd8\xb8\x76\xa5\xc4\x67\x3b\x45\x96\x9d\x5d\x53\x09\x23\x7a\xd9\x07\xb7\xdb\x7b\x55\x82\x6e\xf7\xe7\x50\x99\x28\xe4\x87\x2e\x14\x0b\xe9\x9b\xee\x48\x54\xdc\x2f\x2b\x76\xa2\x5f\xf0\x2f\x9f\x8f\xc4\xa7\x9b\x9d\xda\xaa\xdf\xf4\x45\x47\x7b\x46\x4b\xd8\x65\x2c\x5b\xc7\x71\xde\x26\x1c\xaa\x29\x03\xef\x60\x00\x07\xf2\x38\x05\x1d\xaa\x01\xf6\xf8\x51\x59\x8e\xc0\xa0\xa7\xe8\x0e\x74\x25\xa6\x05\x9e\xf1\x01\xb4\x98\xfb\x22\xe9\xb6\x64\x38\x60\x0b\x8f\xb6\xa3\xbe\xb6\x42\x66\x24\xf1\xe0\x5e\xdd\x29\xc6\xba\x89\x50\xdb\x8a\x09\xb7\x08\x7d\xad\x4f\x35\x67\x83\x92\xb9\x47\x5d\xb5\xd7\xda\x47\x9c\x0d\xb8\xe7\xf8\xcf\xfa\x64\x67\x36\x84\x7b\x61\x41\x90\x99\x3b\x71\x9a\x43\xad\xa6\x90\x9a\x65\xcb\x09\xbe\x5f\xb8\x58\xc6\xc6\x76\xbb\x84\xee\x85\x24\xb1\x4e\x79\xb9\x55\x2d\xda\xbf\xfe\x88\x89\x00\xdf\xaf\x69\x9e\x68\x62\x77\x44\xbb\x5e\x7e\x2a\x67\x39\x26\x4c\x9c\x58\xeb\x65\x1d\x54\xcd\x19\xaa\x60\x33\x94\x16\x7d\x36\xe9\xe9\x02\xeb\x66\x7e\x69\x87\x31\xe7\x92\xb4\xbb\x55\xdc\xd0\xae\x55\xae\xe0\x2d\x67\x3e\x36\x65\xcb\xcb\xde\x84\x4e\xee\xea\xf2\x32\x31\x8f\x02\x87\xff\x76\x72\xfc\xe9\x94\xff\x5d\x59\x6e\x46\x10\x26\xa8\x3e\x94\x29\x0e\xe9\xd8\xfd\xfa\x67\x36\x64\x2c\xa4\x6c\xbd\x20\xe9\x05\x44\xe4\x82\x9e\x69\xe8\xbe\x28\xf8\x6f\xd1\x9d\x98\x3d\xef\x85\xf3\x48\xe9\x52\xf6\xb8\xad\x9a\x3c\xcc\x93\x32\x7c\x03\xa9\xe2\x71\x1a\xb1\x5c\x1e\x0c\xe6\x8c\x24\xb7\x71\x96\x26\x54\x75\xc9\xb9\xb7\xe9\x72\xb1\x48\xb3\x82\xa5\x2a\x27\x3d\x3a\x59\x33\xa1\x79\xca\x83\xcc\x79\x66\x9f\x51\xed\xae\xb5\x4c\x18\x47\x11\x32\xad\x96\x57\x18\xd7\x5e\x8d\xf4\x63\x3d\x54\xf9\xb4\xb2\xf5\xa2\x48\xa1\x03\x45\xb6\x86\xaf\xc0\xff\x1c\x41\x46\xbe\x2c\xe3\x8c\xb4\x5b\xec\x49\xab\x43\x5b\x39\x0d\x8b\xe9\x15\xb4\x49\x07\xbe\x7e\x2b\xdb\xfb\x21\x4b\xef\x72\x61\x18\x33\x16\xda\xe5\x2c\x9d\x84\xb3\x9e\x3c\x58\x86\x89\xe1\x5b\xa7\x4c\xa6\xf3\xcd\xfb\xfa\x27\x56\xe3\x9f\xf6\x76\x83\x6f\xe7\xde\x9f\xee\xc8\xe4\xe5\x9f\xf6\xce\xc4\x10\xb4\x39\x63\x1e\x6b\xa2\xc7\x9b\xd8\xf9\xfa\x03\x15\x59\x7f\x23\x93\x97\x32\xf3\xbd\xfe\x2c\x9e\xf4\x29\x09\x4c\x2f\xd0\xef\x43\x94\x26\x05\xa4\xb7\x24\xcb\xe2\x88\x70\xee\xa8\xb4\x8b\xc3\xc9\x8c\xfc\x40\xfb\x84\x77\xfb\x5d\x9c\x44\xe9\x1d\xe6\x14\xd0\xfa\x5d\xf9\xa0\xc7\xaa\x54\xbf\x12\x43\xa1\x7c\x82\xcc\xed\xff\xf0\xed\x87\x1f\x8c\xd1\x61\x6f\xb0\xf1\x15\xc7\x7f\xda\x1b\x0e\xbf\x9d\x7f\xf3\xbe\x7e\xf3\xce\x58\x2f\x9c\x77\x7e\xe8\xf7\xff\x07\xe4\xe9\x32\x9b\x92\x5f\xc3\xc5\x22\x4e\x2e\x7f\xfb\xf4\x71\x44\x5f\xf6\xae\xf3\xde\x3c\x5c\xfc\xf0\xff\x02\x00\x00\xff\xff\xd2\x48\xd8\x13\xdc\x80\x07\x00") + +func web3JsBytes() ([]byte, error) { + return bindataRead( + _web3Js, + "web3.js", + ) +} + +func web3Js() (*asset, error) { + bytes, err := web3JsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "web3.js", size: 491740, mode: os.FileMode(420), modTime: time.Unix(1484232456, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "bignumber.js": bignumberJs, + "web3.js": web3Js, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "bignumber.js": {bignumberJs, map[string]*bintree{}}, + "web3.js": {web3Js, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/deps.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/deps.go new file mode 100644 index 0000000..8d0e1a4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/deps.go @@ -0,0 +1,20 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package deps contains the console JavaScript dependencies Go embedded. +package deps + +//go:generate go-bindata -o bindata.go bignumber.js web3.js diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/web3.js b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/web3.js new file mode 100644 index 0000000..1aa6545 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/deps/web3.js @@ -0,0 +1,16151 @@ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. +*/ +/** + * @file coder.js + * @author Marek Kotewicz + * @date 2015 + */ + +var f = require('./formatters'); + +var SolidityTypeAddress = require('./address'); +var SolidityTypeBool = require('./bool'); +var SolidityTypeInt = require('./int'); +var SolidityTypeUInt = require('./uint'); +var SolidityTypeDynamicBytes = require('./dynamicbytes'); +var SolidityTypeString = require('./string'); +var SolidityTypeReal = require('./real'); +var SolidityTypeUReal = require('./ureal'); +var SolidityTypeBytes = require('./bytes'); + +var isDynamic = function (solidityType, type) { + return solidityType.isDynamicType(type) || + solidityType.isDynamicArray(type); +}; + +/** + * SolidityCoder prototype should be used to encode/decode solidity params of any type + */ +var SolidityCoder = function (types) { + this._types = types; +}; + +/** + * This method should be used to transform type to SolidityType + * + * @method _requireType + * @param {String} type + * @returns {SolidityType} + * @throws {Error} throws if no matching type is found + */ +SolidityCoder.prototype._requireType = function (type) { + var solidityType = this._types.filter(function (t) { + return t.isType(type); + })[0]; + + if (!solidityType) { + throw Error('invalid solidity type!: ' + type); + } + + return solidityType; +}; + +/** + * Should be used to encode plain param + * + * @method encodeParam + * @param {String} type + * @param {Object} plain param + * @return {String} encoded plain param + */ +SolidityCoder.prototype.encodeParam = function (type, param) { + return this.encodeParams([type], [param]); +}; + +/** + * Should be used to encode list of params + * + * @method encodeParams + * @param {Array} types + * @param {Array} params + * @return {String} encoded list of params + */ +SolidityCoder.prototype.encodeParams = function (types, params) { + var solidityTypes = this.getSolidityTypes(types); + + var encodeds = solidityTypes.map(function (solidityType, index) { + return solidityType.encode(params[index], types[index]); + }); + + var dynamicOffset = solidityTypes.reduce(function (acc, solidityType, index) { + var staticPartLength = solidityType.staticPartLength(types[index]); + var roundedStaticPartLength = Math.floor((staticPartLength + 31) / 32) * 32; + + return acc + (isDynamic(solidityTypes[index], types[index]) ? + 32 : + roundedStaticPartLength); + }, 0); + + var result = this.encodeMultiWithOffset(types, solidityTypes, encodeds, dynamicOffset); + + return result; +}; + +SolidityCoder.prototype.encodeMultiWithOffset = function (types, solidityTypes, encodeds, dynamicOffset) { + var result = ""; + var self = this; + + types.forEach(function (type, i) { + if (isDynamic(solidityTypes[i], types[i])) { + result += f.formatInputInt(dynamicOffset).encode(); + var e = self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + dynamicOffset += e.length / 2; + } else { + // don't add length to dynamicOffset. it's already counted + result += self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + } + + // TODO: figure out nested arrays + }); + + types.forEach(function (type, i) { + if (isDynamic(solidityTypes[i], types[i])) { + var e = self.encodeWithOffset(types[i], solidityTypes[i], encodeds[i], dynamicOffset); + dynamicOffset += e.length / 2; + result += e; + } + }); + return result; +}; + +// TODO: refactor whole encoding! +SolidityCoder.prototype.encodeWithOffset = function (type, solidityType, encoded, offset) { + var self = this; + if (solidityType.isDynamicArray(type)) { + return (function () { + // offset was already set + var nestedName = solidityType.nestedName(type); + var nestedStaticPartLength = solidityType.staticPartLength(nestedName); + var result = encoded[0]; + + (function () { + var previousLength = 2; // in int + if (solidityType.isDynamicArray(nestedName)) { + for (var i = 1; i < encoded.length; i++) { + previousLength += +(encoded[i - 1])[0] || 0; + result += f.formatInputInt(offset + i * nestedStaticPartLength + previousLength * 32).encode(); + } + } + })(); + + // first element is length, skip it + (function () { + for (var i = 0; i < encoded.length - 1; i++) { + var additionalOffset = result / 2; + result += self.encodeWithOffset(nestedName, solidityType, encoded[i + 1], offset + additionalOffset); + } + })(); + + return result; + })(); + + } else if (solidityType.isStaticArray(type)) { + return (function () { + var nestedName = solidityType.nestedName(type); + var nestedStaticPartLength = solidityType.staticPartLength(nestedName); + var result = ""; + + + if (solidityType.isDynamicArray(nestedName)) { + (function () { + var previousLength = 0; // in int + for (var i = 0; i < encoded.length; i++) { + // calculate length of previous item + previousLength += +(encoded[i - 1] || [])[0] || 0; + result += f.formatInputInt(offset + i * nestedStaticPartLength + previousLength * 32).encode(); + } + })(); + } + + (function () { + for (var i = 0; i < encoded.length; i++) { + var additionalOffset = result / 2; + result += self.encodeWithOffset(nestedName, solidityType, encoded[i], offset + additionalOffset); + } + })(); + + return result; + })(); + } + + return encoded; +}; + +/** + * Should be used to decode bytes to plain param + * + * @method decodeParam + * @param {String} type + * @param {String} bytes + * @return {Object} plain param + */ +SolidityCoder.prototype.decodeParam = function (type, bytes) { + return this.decodeParams([type], bytes)[0]; +}; + +/** + * Should be used to decode list of params + * + * @method decodeParam + * @param {Array} types + * @param {String} bytes + * @return {Array} array of plain params + */ +SolidityCoder.prototype.decodeParams = function (types, bytes) { + var solidityTypes = this.getSolidityTypes(types); + var offsets = this.getOffsets(types, solidityTypes); + + return solidityTypes.map(function (solidityType, index) { + return solidityType.decode(bytes, offsets[index], types[index], index); + }); +}; + +SolidityCoder.prototype.getOffsets = function (types, solidityTypes) { + var lengths = solidityTypes.map(function (solidityType, index) { + return solidityType.staticPartLength(types[index]); + }); + + for (var i = 1; i < lengths.length; i++) { + // sum with length of previous element + lengths[i] += lengths[i - 1]; + } + + return lengths.map(function (length, index) { + // remove the current length, so the length is sum of previous elements + var staticPartLength = solidityTypes[index].staticPartLength(types[index]); + return length - staticPartLength; + }); +}; + +SolidityCoder.prototype.getSolidityTypes = function (types) { + var self = this; + return types.map(function (type) { + return self._requireType(type); + }); +}; + +var coder = new SolidityCoder([ + new SolidityTypeAddress(), + new SolidityTypeBool(), + new SolidityTypeInt(), + new SolidityTypeUInt(), + new SolidityTypeDynamicBytes(), + new SolidityTypeBytes(), + new SolidityTypeString(), + new SolidityTypeReal(), + new SolidityTypeUReal() +]); + +module.exports = coder; + +},{"./address":4,"./bool":5,"./bytes":6,"./dynamicbytes":8,"./formatters":9,"./int":10,"./real":12,"./string":13,"./uint":15,"./ureal":16}],8:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +var SolidityTypeDynamicBytes = function () { + this._inputFormatter = f.formatInputDynamicBytes; + this._outputFormatter = f.formatOutputDynamicBytes; +}; + +SolidityTypeDynamicBytes.prototype = new SolidityType({}); +SolidityTypeDynamicBytes.prototype.constructor = SolidityTypeDynamicBytes; + +SolidityTypeDynamicBytes.prototype.isType = function (name) { + return !!name.match(/^bytes(\[([0-9]*)\])*$/); +}; + +SolidityTypeDynamicBytes.prototype.isDynamicType = function () { + return true; +}; + +module.exports = SolidityTypeDynamicBytes; + +},{"./formatters":9,"./type":14}],9:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file formatters.js + * @author Marek Kotewicz + * @date 2015 + */ + +var BigNumber = require('bignumber.js'); +var utils = require('../utils/utils'); +var c = require('../utils/config'); +var SolidityParam = require('./param'); + + +/** + * Formats input value to byte representation of int + * If value is negative, return it's two's complement + * If the value is floating point, round it down + * + * @method formatInputInt + * @param {String|Number|BigNumber} value that needs to be formatted + * @returns {SolidityParam} + */ +var formatInputInt = function (value) { + BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); + var result = utils.padLeft(utils.toTwosComplement(value).toString(16), 64); + return new SolidityParam(result); +}; + +/** + * Formats input bytes + * + * @method formatInputBytes + * @param {String} + * @returns {SolidityParam} + */ +var formatInputBytes = function (value) { + var result = utils.toHex(value).substr(2); + var l = Math.floor((result.length + 63) / 64); + result = utils.padRight(result, l * 64); + return new SolidityParam(result); +}; + +/** + * Formats input bytes + * + * @method formatDynamicInputBytes + * @param {String} + * @returns {SolidityParam} + */ +var formatInputDynamicBytes = function (value) { + var result = utils.toHex(value).substr(2); + var length = result.length / 2; + var l = Math.floor((result.length + 63) / 64); + result = utils.padRight(result, l * 64); + return new SolidityParam(formatInputInt(length).value + result); +}; + +/** + * Formats input value to byte representation of string + * + * @method formatInputString + * @param {String} + * @returns {SolidityParam} + */ +var formatInputString = function (value) { + var result = utils.fromUtf8(value).substr(2); + var length = result.length / 2; + var l = Math.floor((result.length + 63) / 64); + result = utils.padRight(result, l * 64); + return new SolidityParam(formatInputInt(length).value + result); +}; + +/** + * Formats input value to byte representation of bool + * + * @method formatInputBool + * @param {Boolean} + * @returns {SolidityParam} + */ +var formatInputBool = function (value) { + var result = '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); + return new SolidityParam(result); +}; + +/** + * Formats input value to byte representation of real + * Values are multiplied by 2^m and encoded as integers + * + * @method formatInputReal + * @param {String|Number|BigNumber} + * @returns {SolidityParam} + */ +var formatInputReal = function (value) { + return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); +}; + +/** + * Check if input value is negative + * + * @method signedIsNegative + * @param {String} value is hex format + * @returns {Boolean} true if it is negative, otherwise false + */ +var signedIsNegative = function (value) { + return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; +}; + +/** + * Formats right-aligned output bytes to int + * + * @method formatOutputInt + * @param {SolidityParam} param + * @returns {BigNumber} right-aligned output bytes formatted to big number + */ +var formatOutputInt = function (param) { + var value = param.staticPart() || "0"; + + // check if it's negative number + // it it is, return two's complement + if (signedIsNegative(value)) { + return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); + } + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to uint + * + * @method formatOutputUInt + * @param {SolidityParam} + * @returns {BigNumeber} right-aligned output bytes formatted to uint + */ +var formatOutputUInt = function (param) { + var value = param.staticPart() || "0"; + return new BigNumber(value, 16); +}; + +/** + * Formats right-aligned output bytes to real + * + * @method formatOutputReal + * @param {SolidityParam} + * @returns {BigNumber} input bytes formatted to real + */ +var formatOutputReal = function (param) { + return formatOutputInt(param).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Formats right-aligned output bytes to ureal + * + * @method formatOutputUReal + * @param {SolidityParam} + * @returns {BigNumber} input bytes formatted to ureal + */ +var formatOutputUReal = function (param) { + return formatOutputUInt(param).dividedBy(new BigNumber(2).pow(128)); +}; + +/** + * Should be used to format output bool + * + * @method formatOutputBool + * @param {SolidityParam} + * @returns {Boolean} right-aligned input bytes formatted to bool + */ +var formatOutputBool = function (param) { + return param.staticPart() === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; +}; + +/** + * Should be used to format output bytes + * + * @method formatOutputBytes + * @param {SolidityParam} left-aligned hex representation of string + * @param {String} name type name + * @returns {String} hex string + */ +var formatOutputBytes = function (param, name) { + var matches = name.match(/^bytes([0-9]*)/); + var size = parseInt(matches[1]); + return '0x' + param.staticPart().slice(0, 2 * size); +}; + +/** + * Should be used to format output bytes + * + * @method formatOutputDynamicBytes + * @param {SolidityParam} left-aligned hex representation of string + * @returns {String} hex string + */ +var formatOutputDynamicBytes = function (param) { + var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2; + return '0x' + param.dynamicPart().substr(64, length); +}; + +/** + * Should be used to format output string + * + * @method formatOutputString + * @param {SolidityParam} left-aligned hex representation of string + * @returns {String} ascii string + */ +var formatOutputString = function (param) { + var length = (new BigNumber(param.dynamicPart().slice(0, 64), 16)).toNumber() * 2; + return utils.toUtf8(param.dynamicPart().substr(64, length)); +}; + +/** + * Should be used to format output address + * + * @method formatOutputAddress + * @param {SolidityParam} right-aligned input bytes + * @returns {String} address + */ +var formatOutputAddress = function (param) { + var value = param.staticPart(); + return "0x" + value.slice(value.length - 40, value.length); +}; + +module.exports = { + formatInputInt: formatInputInt, + formatInputBytes: formatInputBytes, + formatInputDynamicBytes: formatInputDynamicBytes, + formatInputString: formatInputString, + formatInputBool: formatInputBool, + formatInputReal: formatInputReal, + formatOutputInt: formatOutputInt, + formatOutputUInt: formatOutputUInt, + formatOutputReal: formatOutputReal, + formatOutputUReal: formatOutputUReal, + formatOutputBool: formatOutputBool, + formatOutputBytes: formatOutputBytes, + formatOutputDynamicBytes: formatOutputDynamicBytes, + formatOutputString: formatOutputString, + formatOutputAddress: formatOutputAddress +}; + +},{"../utils/config":18,"../utils/utils":20,"./param":11,"bignumber.js":"bignumber.js"}],10:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeInt is a prootype that represents int type + * It matches: + * int + * int[] + * int[4] + * int[][] + * int[3][] + * int[][6][], ... + * int32 + * int64[] + * int8[4] + * int256[][] + * int[3][] + * int64[][6][], ... + */ +var SolidityTypeInt = function () { + this._inputFormatter = f.formatInputInt; + this._outputFormatter = f.formatOutputInt; +}; + +SolidityTypeInt.prototype = new SolidityType({}); +SolidityTypeInt.prototype.constructor = SolidityTypeInt; + +SolidityTypeInt.prototype.isType = function (name) { + return !!name.match(/^int([0-9]*)?(\[([0-9]*)\])*$/); +}; + +module.exports = SolidityTypeInt; + +},{"./formatters":9,"./type":14}],11:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file param.js + * @author Marek Kotewicz + * @date 2015 + */ + +var utils = require('../utils/utils'); + +/** + * SolidityParam object prototype. + * Should be used when encoding, decoding solidity bytes + */ +var SolidityParam = function (value, offset) { + this.value = value || ''; + this.offset = offset; // offset in bytes +}; + +/** + * This method should be used to get length of params's dynamic part + * + * @method dynamicPartLength + * @returns {Number} length of dynamic part (in bytes) + */ +SolidityParam.prototype.dynamicPartLength = function () { + return this.dynamicPart().length / 2; +}; + +/** + * This method should be used to create copy of solidity param with different offset + * + * @method withOffset + * @param {Number} offset length in bytes + * @returns {SolidityParam} new solidity param with applied offset + */ +SolidityParam.prototype.withOffset = function (offset) { + return new SolidityParam(this.value, offset); +}; + +/** + * This method should be used to combine solidity params together + * eg. when appending an array + * + * @method combine + * @param {SolidityParam} param with which we should combine + * @param {SolidityParam} result of combination + */ +SolidityParam.prototype.combine = function (param) { + return new SolidityParam(this.value + param.value); +}; + +/** + * This method should be called to check if param has dynamic size. + * If it has, it returns true, otherwise false + * + * @method isDynamic + * @returns {Boolean} + */ +SolidityParam.prototype.isDynamic = function () { + return this.offset !== undefined; +}; + +/** + * This method should be called to transform offset to bytes + * + * @method offsetAsBytes + * @returns {String} bytes representation of offset + */ +SolidityParam.prototype.offsetAsBytes = function () { + return !this.isDynamic() ? '' : utils.padLeft(utils.toTwosComplement(this.offset).toString(16), 64); +}; + +/** + * This method should be called to get static part of param + * + * @method staticPart + * @returns {String} offset if it is a dynamic param, otherwise value + */ +SolidityParam.prototype.staticPart = function () { + if (!this.isDynamic()) { + return this.value; + } + return this.offsetAsBytes(); +}; + +/** + * This method should be called to get dynamic part of param + * + * @method dynamicPart + * @returns {String} returns a value if it is a dynamic param, otherwise empty string + */ +SolidityParam.prototype.dynamicPart = function () { + return this.isDynamic() ? this.value : ''; +}; + +/** + * This method should be called to encode param + * + * @method encode + * @returns {String} + */ +SolidityParam.prototype.encode = function () { + return this.staticPart() + this.dynamicPart(); +}; + +/** + * This method should be called to encode array of params + * + * @method encodeList + * @param {Array[SolidityParam]} params + * @returns {String} + */ +SolidityParam.encodeList = function (params) { + + // updating offsets + var totalOffset = params.length * 32; + var offsetParams = params.map(function (param) { + if (!param.isDynamic()) { + return param; + } + var offset = totalOffset; + totalOffset += param.dynamicPartLength(); + return param.withOffset(offset); + }); + + // encode everything! + return offsetParams.reduce(function (result, param) { + return result + param.dynamicPart(); + }, offsetParams.reduce(function (result, param) { + return result + param.staticPart(); + }, '')); +}; + + + +module.exports = SolidityParam; + + +},{"../utils/utils":20}],12:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeReal is a prootype that represents real type + * It matches: + * real + * real[] + * real[4] + * real[][] + * real[3][] + * real[][6][], ... + * real32 + * real64[] + * real8[4] + * real256[][] + * real[3][] + * real64[][6][], ... + */ +var SolidityTypeReal = function () { + this._inputFormatter = f.formatInputReal; + this._outputFormatter = f.formatOutputReal; +}; + +SolidityTypeReal.prototype = new SolidityType({}); +SolidityTypeReal.prototype.constructor = SolidityTypeReal; + +SolidityTypeReal.prototype.isType = function (name) { + return !!name.match(/real([0-9]*)?(\[([0-9]*)\])?/); +}; + +module.exports = SolidityTypeReal; + +},{"./formatters":9,"./type":14}],13:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +var SolidityTypeString = function () { + this._inputFormatter = f.formatInputString; + this._outputFormatter = f.formatOutputString; +}; + +SolidityTypeString.prototype = new SolidityType({}); +SolidityTypeString.prototype.constructor = SolidityTypeString; + +SolidityTypeString.prototype.isType = function (name) { + return !!name.match(/^string(\[([0-9]*)\])*$/); +}; + +SolidityTypeString.prototype.isDynamicType = function () { + return true; +}; + +module.exports = SolidityTypeString; + +},{"./formatters":9,"./type":14}],14:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityParam = require('./param'); + +/** + * SolidityType prototype is used to encode/decode solidity params of certain type + */ +var SolidityType = function (config) { + this._inputFormatter = config.inputFormatter; + this._outputFormatter = config.outputFormatter; +}; + +/** + * Should be used to determine if this SolidityType do match given name + * + * @method isType + * @param {String} name + * @return {Bool} true if type match this SolidityType, otherwise false + */ +SolidityType.prototype.isType = function (name) { + throw "this method should be overrwritten for type " + name; +}; + +/** + * Should be used to determine what is the length of static part in given type + * + * @method staticPartLength + * @param {String} name + * @return {Number} length of static part in bytes + */ +SolidityType.prototype.staticPartLength = function (name) { + // If name isn't an array then treat it like a single element array. + return (this.nestedTypes(name) || ['[1]']) + .map(function (type) { + // the length of the nested array + return parseInt(type.slice(1, -1), 10) || 1; + }) + .reduce(function (previous, current) { + return previous * current; + // all basic types are 32 bytes long + }, 32); +}; + +/** + * Should be used to determine if type is dynamic array + * eg: + * "type[]" => true + * "type[4]" => false + * + * @method isDynamicArray + * @param {String} name + * @return {Bool} true if the type is dynamic array + */ +SolidityType.prototype.isDynamicArray = function (name) { + var nestedTypes = this.nestedTypes(name); + return !!nestedTypes && !nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g); +}; + +/** + * Should be used to determine if type is static array + * eg: + * "type[]" => false + * "type[4]" => true + * + * @method isStaticArray + * @param {String} name + * @return {Bool} true if the type is static array + */ +SolidityType.prototype.isStaticArray = function (name) { + var nestedTypes = this.nestedTypes(name); + return !!nestedTypes && !!nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g); +}; + +/** + * Should return length of static array + * eg. + * "int[32]" => 32 + * "int256[14]" => 14 + * "int[2][3]" => 3 + * "int" => 1 + * "int[1]" => 1 + * "int[]" => 1 + * + * @method staticArrayLength + * @param {String} name + * @return {Number} static array length + */ +SolidityType.prototype.staticArrayLength = function (name) { + var nestedTypes = this.nestedTypes(name); + if (nestedTypes) { + return parseInt(nestedTypes[nestedTypes.length - 1].match(/[0-9]{1,}/g) || 1); + } + return 1; +}; + +/** + * Should return nested type + * eg. + * "int[32]" => "int" + * "int256[14]" => "int256" + * "int[2][3]" => "int[2]" + * "int" => "int" + * "int[]" => "int" + * + * @method nestedName + * @param {String} name + * @return {String} nested name + */ +SolidityType.prototype.nestedName = function (name) { + // remove last [] in name + var nestedTypes = this.nestedTypes(name); + if (!nestedTypes) { + return name; + } + + return name.substr(0, name.length - nestedTypes[nestedTypes.length - 1].length); +}; + +/** + * Should return true if type has dynamic size by default + * such types are "string", "bytes" + * + * @method isDynamicType + * @param {String} name + * @return {Bool} true if is dynamic, otherwise false + */ +SolidityType.prototype.isDynamicType = function () { + return false; +}; + +/** + * Should return array of nested types + * eg. + * "int[2][3][]" => ["[2]", "[3]", "[]"] + * "int[] => ["[]"] + * "int" => null + * + * @method nestedTypes + * @param {String} name + * @return {Array} array of nested types + */ +SolidityType.prototype.nestedTypes = function (name) { + // return list of strings eg. "[]", "[3]", "[]", "[2]" + return name.match(/(\[[0-9]*\])/g); +}; + +/** + * Should be used to encode the value + * + * @method encode + * @param {Object} value + * @param {String} name + * @return {String} encoded value + */ +SolidityType.prototype.encode = function (value, name) { + var self = this; + if (this.isDynamicArray(name)) { + + return (function () { + var length = value.length; // in int + var nestedName = self.nestedName(name); + + var result = []; + result.push(f.formatInputInt(length).encode()); + + value.forEach(function (v) { + result.push(self.encode(v, nestedName)); + }); + + return result; + })(); + + } else if (this.isStaticArray(name)) { + + return (function () { + var length = self.staticArrayLength(name); // in int + var nestedName = self.nestedName(name); + + var result = []; + for (var i = 0; i < length; i++) { + result.push(self.encode(value[i], nestedName)); + } + + return result; + })(); + + } + + return this._inputFormatter(value, name).encode(); +}; + +/** + * Should be used to decode value from bytes + * + * @method decode + * @param {String} bytes + * @param {Number} offset in bytes + * @param {String} name type name + * @returns {Object} decoded value + */ +SolidityType.prototype.decode = function (bytes, offset, name) { + var self = this; + + if (this.isDynamicArray(name)) { + + return (function () { + var arrayOffset = parseInt('0x' + bytes.substr(offset * 2, 64)); // in bytes + var length = parseInt('0x' + bytes.substr(arrayOffset * 2, 64)); // in int + var arrayStart = arrayOffset + 32; // array starts after length; // in bytes + + var nestedName = self.nestedName(name); + var nestedStaticPartLength = self.staticPartLength(nestedName); // in bytes + var roundedNestedStaticPartLength = Math.floor((nestedStaticPartLength + 31) / 32) * 32; + var result = []; + + for (var i = 0; i < length * roundedNestedStaticPartLength; i += roundedNestedStaticPartLength) { + result.push(self.decode(bytes, arrayStart + i, nestedName)); + } + + return result; + })(); + + } else if (this.isStaticArray(name)) { + + return (function () { + var length = self.staticArrayLength(name); // in int + var arrayStart = offset; // in bytes + + var nestedName = self.nestedName(name); + var nestedStaticPartLength = self.staticPartLength(nestedName); // in bytes + var roundedNestedStaticPartLength = Math.floor((nestedStaticPartLength + 31) / 32) * 32; + var result = []; + + for (var i = 0; i < length * roundedNestedStaticPartLength; i += roundedNestedStaticPartLength) { + result.push(self.decode(bytes, arrayStart + i, nestedName)); + } + + return result; + })(); + } else if (this.isDynamicType(name)) { + + return (function () { + var dynamicOffset = parseInt('0x' + bytes.substr(offset * 2, 64)); // in bytes + var length = parseInt('0x' + bytes.substr(dynamicOffset * 2, 64)); // in bytes + var roundedLength = Math.floor((length + 31) / 32); // in int + var param = new SolidityParam(bytes.substr(dynamicOffset * 2, ( 1 + roundedLength) * 64), 0); + return self._outputFormatter(param, name); + })(); + } + + var length = this.staticPartLength(name); + var param = new SolidityParam(bytes.substr(offset * 2, length * 2)); + return this._outputFormatter(param, name); +}; + +module.exports = SolidityType; + +},{"./formatters":9,"./param":11}],15:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeUInt is a prootype that represents uint type + * It matches: + * uint + * uint[] + * uint[4] + * uint[][] + * uint[3][] + * uint[][6][], ... + * uint32 + * uint64[] + * uint8[4] + * uint256[][] + * uint[3][] + * uint64[][6][], ... + */ +var SolidityTypeUInt = function () { + this._inputFormatter = f.formatInputInt; + this._outputFormatter = f.formatOutputUInt; +}; + +SolidityTypeUInt.prototype = new SolidityType({}); +SolidityTypeUInt.prototype.constructor = SolidityTypeUInt; + +SolidityTypeUInt.prototype.isType = function (name) { + return !!name.match(/^uint([0-9]*)?(\[([0-9]*)\])*$/); +}; + +module.exports = SolidityTypeUInt; + +},{"./formatters":9,"./type":14}],16:[function(require,module,exports){ +var f = require('./formatters'); +var SolidityType = require('./type'); + +/** + * SolidityTypeUReal is a prootype that represents ureal type + * It matches: + * ureal + * ureal[] + * ureal[4] + * ureal[][] + * ureal[3][] + * ureal[][6][], ... + * ureal32 + * ureal64[] + * ureal8[4] + * ureal256[][] + * ureal[3][] + * ureal64[][6][], ... + */ +var SolidityTypeUReal = function () { + this._inputFormatter = f.formatInputReal; + this._outputFormatter = f.formatOutputUReal; +}; + +SolidityTypeUReal.prototype = new SolidityType({}); +SolidityTypeUReal.prototype.constructor = SolidityTypeUReal; + +SolidityTypeUReal.prototype.isType = function (name) { + return !!name.match(/^ureal([0-9]*)?(\[([0-9]*)\])*$/); +}; + +module.exports = SolidityTypeUReal; + +},{"./formatters":9,"./type":14}],17:[function(require,module,exports){ +'use strict'; + +// go env doesn't have and need XMLHttpRequest +if (typeof XMLHttpRequest === 'undefined') { + exports.XMLHttpRequest = {}; +} else { + exports.XMLHttpRequest = XMLHttpRequest; // jshint ignore:line +} + + +},{}],18:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file config.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] config + * @constructor + */ + + +/// required to define ETH_BIGNUMBER_ROUNDING_MODE +var BigNumber = require('bignumber.js'); + +var ETH_UNITS = [ + 'wei', + 'kwei', + 'Mwei', + 'Gwei', + 'szabo', + 'finney', + 'femtoether', + 'picoether', + 'nanoether', + 'microether', + 'milliether', + 'nano', + 'micro', + 'milli', + 'ether', + 'grand', + 'Mether', + 'Gether', + 'Tether', + 'Pether', + 'Eether', + 'Zether', + 'Yether', + 'Nether', + 'Dether', + 'Vether', + 'Uether' +]; + +module.exports = { + ETH_PADDING: 32, + ETH_SIGNATURE_LENGTH: 4, + ETH_UNITS: ETH_UNITS, + ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }, + ETH_POLLING_TIMEOUT: 1000/2, + defaultBlock: 'latest', + defaultAccount: undefined +}; + + +},{"bignumber.js":"bignumber.js"}],19:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file sha3.js + * @author Marek Kotewicz + * @date 2015 + */ + +var CryptoJS = require('crypto-js'); +var sha3 = require('crypto-js/sha3'); + +module.exports = function (value, options) { + if (options && options.encoding === 'hex') { + if (value.length > 2 && value.substr(0, 2) === '0x') { + value = value.substr(2); + } + value = CryptoJS.enc.Hex.parse(value); + } + + return sha3(value, { + outputLength: 256 + }).toString(); +}; + + +},{"crypto-js":59,"crypto-js/sha3":80}],20:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file utils.js + * @author Marek Kotewicz + * @date 2015 + */ + +/** + * Utils + * + * @module utils + */ + +/** + * Utility functions + * + * @class [utils] utils + * @constructor + */ + + +var BigNumber = require('bignumber.js'); +var sha3 = require('./sha3.js'); +var utf8 = require('utf8'); + +var unitMap = { + 'noether': '0', + 'wei': '1', + 'kwei': '1000', + 'Kwei': '1000', + 'babbage': '1000', + 'femtoether': '1000', + 'mwei': '1000000', + 'Mwei': '1000000', + 'lovelace': '1000000', + 'picoether': '1000000', + 'gwei': '1000000000', + 'Gwei': '1000000000', + 'shannon': '1000000000', + 'nanoether': '1000000000', + 'nano': '1000000000', + 'szabo': '1000000000000', + 'microether': '1000000000000', + 'micro': '1000000000000', + 'finney': '1000000000000000', + 'milliether': '1000000000000000', + 'milli': '1000000000000000', + 'ether': '1000000000000000000', + 'kether': '1000000000000000000000', + 'grand': '1000000000000000000000', + 'mether': '1000000000000000000000000', + 'gether': '1000000000000000000000000000', + 'tether': '1000000000000000000000000000000' +}; + +/** + * Should be called to pad string to expected length + * + * @method padLeft + * @param {String} string to be padded + * @param {Number} characters that result string should have + * @param {String} sign, by default 0 + * @returns {String} right aligned string + */ +var padLeft = function (string, chars, sign) { + return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; +}; + +/** + * Should be called to pad string to expected length + * + * @method padRight + * @param {String} string to be padded + * @param {Number} characters that result string should have + * @param {String} sign, by default 0 + * @returns {String} right aligned string + */ +var padRight = function (string, chars, sign) { + return string + (new Array(chars - string.length + 1).join(sign ? sign : "0")); +}; + +/** + * Should be called to get utf8 from it's hex representation + * + * @method toUtf8 + * @param {String} string in hex + * @returns {String} ascii string representation of hex value + */ +var toUtf8 = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + if (code === 0) + break; + str += String.fromCharCode(code); + } + + return utf8.decode(str); +}; + +/** + * Should be called to get ascii from it's hex representation + * + * @method toAscii + * @param {String} string in hex + * @returns {String} ascii string representation of hex value + */ +var toAscii = function(hex) { +// Find termination + var str = ""; + var i = 0, l = hex.length; + if (hex.substring(0, 2) === '0x') { + i = 2; + } + for (; i < l; i+=2) { + var code = parseInt(hex.substr(i, 2), 16); + str += String.fromCharCode(code); + } + + return str; +}; + +/** + * Should be called to get hex representation (prefixed by 0x) of utf8 string + * + * @method fromUtf8 + * @param {String} string + * @param {Number} optional padding + * @returns {String} hex representation of input string + */ +var fromUtf8 = function(str) { + str = utf8.encode(str); + var hex = ""; + for(var i = 0; i < str.length; i++) { + var code = str.charCodeAt(i); + if (code === 0) + break; + var n = code.toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return "0x" + hex; +}; + +/** + * Should be called to get hex representation (prefixed by 0x) of ascii string + * + * @method fromAscii + * @param {String} string + * @param {Number} optional padding + * @returns {String} hex representation of input string + */ +var fromAscii = function(str) { + var hex = ""; + for(var i = 0; i < str.length; i++) { + var code = str.charCodeAt(i); + var n = code.toString(16); + hex += n.length < 2 ? '0' + n : n; + } + + return "0x" + hex; +}; + +/** + * Should be used to create full function/event name from json abi + * + * @method transformToFullName + * @param {Object} json-abi + * @return {String} full fnction/event name + */ +var transformToFullName = function (json) { + if (json.name.indexOf('(') !== -1) { + return json.name; + } + + var typeName = json.inputs.map(function(i){return i.type; }).join(); + return json.name + '(' + typeName + ')'; +}; + +/** + * Should be called to get display name of contract function + * + * @method extractDisplayName + * @param {String} name of function/event + * @returns {String} display name for function/event eg. multiply(uint256) -> multiply + */ +var extractDisplayName = function (name) { + var length = name.indexOf('('); + return length !== -1 ? name.substr(0, length) : name; +}; + +/// @returns overloaded part of function/event name +var extractTypeName = function (name) { + /// TODO: make it invulnerable + var length = name.indexOf('('); + return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; +}; + +/** + * Converts value to it's decimal representation in string + * + * @method toDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var toDecimal = function (value) { + return toBigNumber(value).toNumber(); +}; + +/** + * Converts value to it's hex representation + * + * @method fromDecimal + * @param {String|Number|BigNumber} + * @return {String} + */ +var fromDecimal = function (value) { + var number = toBigNumber(value); + var result = number.toString(16); + + return number.lessThan(0) ? '-0x' + result.substr(1) : '0x' + result; +}; + +/** + * Auto converts any given value into it's hex representation. + * + * And even stringifys objects before. + * + * @method toHex + * @param {String|Number|BigNumber|Object} + * @return {String} + */ +var toHex = function (val) { + /*jshint maxcomplexity: 8 */ + + if (isBoolean(val)) + return fromDecimal(+val); + + if (isBigNumber(val)) + return fromDecimal(val); + + if (isObject(val)) + return fromUtf8(JSON.stringify(val)); + + // if its a negative number, pass it through fromDecimal + if (isString(val)) { + if (val.indexOf('-0x') === 0) + return fromDecimal(val); + else if(val.indexOf('0x') === 0) + return val; + else if (!isFinite(val)) + return fromAscii(val); + } + + return fromDecimal(val); +}; + +/** + * Returns value of unit in Wei + * + * @method getValueOfUnit + * @param {String} unit the unit to convert to, default ether + * @returns {BigNumber} value of the unit (in Wei) + * @throws error if the unit is not correct:w + */ +var getValueOfUnit = function (unit) { + unit = unit ? unit.toLowerCase() : 'ether'; + var unitValue = unitMap[unit]; + if (unitValue === undefined) { + throw new Error('This unit doesn\'t exists, please use the one of the following units' + JSON.stringify(unitMap, null, 2)); + } + return new BigNumber(unitValue, 10); +}; + +/** + * Takes a number of wei and converts it to any other ether unit. + * + * Possible units are: + * SI Short SI Full Effigy Other + * - kwei femtoether babbage + * - mwei picoether lovelace + * - gwei nanoether shannon nano + * - -- microether szabo micro + * - -- milliether finney milli + * - ether -- -- + * - kether -- grand + * - mether + * - gether + * - tether + * + * @method fromWei + * @param {Number|String} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert to, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var fromWei = function(number, unit) { + var returnValue = toBigNumber(number).dividedBy(getValueOfUnit(unit)); + + return isBigNumber(number) ? returnValue : returnValue.toString(10); +}; + +/** + * Takes a number of a unit and converts it to wei. + * + * Possible units are: + * SI Short SI Full Effigy Other + * - kwei femtoether babbage + * - mwei picoether lovelace + * - gwei nanoether shannon nano + * - -- microether szabo micro + * - -- microether szabo micro + * - -- milliether finney milli + * - ether -- -- + * - kether -- grand + * - mether + * - gether + * - tether + * + * @method toWei + * @param {Number|String|BigNumber} number can be a number, number string or a HEX of a decimal + * @param {String} unit the unit to convert from, default ether + * @return {String|Object} When given a BigNumber object it returns one as well, otherwise a number +*/ +var toWei = function(number, unit) { + var returnValue = toBigNumber(number).times(getValueOfUnit(unit)); + + return isBigNumber(number) ? returnValue : returnValue.toString(10); +}; + +/** + * Takes an input and transforms it into an bignumber + * + * @method toBigNumber + * @param {Number|String|BigNumber} a number, string, HEX string or BigNumber + * @return {BigNumber} BigNumber +*/ +var toBigNumber = function(number) { + /*jshint maxcomplexity:5 */ + number = number || 0; + if (isBigNumber(number)) + return number; + + if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) { + return new BigNumber(number.replace('0x',''), 16); + } + + return new BigNumber(number.toString(10), 10); +}; + +/** + * Takes and input transforms it into bignumber and if it is negative value, into two's complement + * + * @method toTwosComplement + * @param {Number|String|BigNumber} + * @return {BigNumber} + */ +var toTwosComplement = function (number) { + var bigNumber = toBigNumber(number).round(); + if (bigNumber.lessThan(0)) { + return new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(bigNumber).plus(1); + } + return bigNumber; +}; + +/** + * Checks if the given string is strictly an address + * + * @method isStrictAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isStrictAddress = function (address) { + return /^0x[0-9a-f]{40}$/i.test(address); +}; + +/** + * Checks if the given string is an address + * + * @method isAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isAddress = function (address) { + if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { + // check if it has the basic requirements of an address + return false; + } else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) { + // If it's all small caps or all all caps, return true + return true; + } else { + // Otherwise check each case + return isChecksumAddress(address); + } +}; + + + +/** + * Checks if the given string is a checksummed address + * + * @method isChecksumAddress + * @param {String} address the given HEX adress + * @return {Boolean} +*/ +var isChecksumAddress = function (address) { + // Check each case + address = address.replace('0x',''); + var addressHash = sha3(address.toLowerCase()); + + for (var i = 0; i < 40; i++ ) { + // the nth letter should be uppercase if the nth digit of casemap is 1 + if ((parseInt(addressHash[i], 16) > 7 && address[i].toUpperCase() !== address[i]) || (parseInt(addressHash[i], 16) <= 7 && address[i].toLowerCase() !== address[i])) { + return false; + } + } + return true; +}; + + + +/** + * Makes a checksum address + * + * @method toChecksumAddress + * @param {String} address the given HEX adress + * @return {String} +*/ +var toChecksumAddress = function (address) { + if (typeof address === 'undefined') return ''; + + address = address.toLowerCase().replace('0x',''); + var addressHash = sha3(address); + var checksumAddress = '0x'; + + for (var i = 0; i < address.length; i++ ) { + // If ith character is 9 to f then make it uppercase + if (parseInt(addressHash[i], 16) > 7) { + checksumAddress += address[i].toUpperCase(); + } else { + checksumAddress += address[i]; + } + } + return checksumAddress; +}; + +/** + * Transforms given string to valid 20 bytes-length addres with 0x prefix + * + * @method toAddress + * @param {String} address + * @return {String} formatted address + */ +var toAddress = function (address) { + if (isStrictAddress(address)) { + return address; + } + + if (/^[0-9a-f]{40}$/.test(address)) { + return '0x' + address; + } + + return '0x' + padLeft(toHex(address).substr(2), 40); +}; + +/** + * Returns true if object is BigNumber, otherwise false + * + * @method isBigNumber + * @param {Object} + * @return {Boolean} + */ +var isBigNumber = function (object) { + return object instanceof BigNumber || + (object && object.constructor && object.constructor.name === 'BigNumber'); +}; + +/** + * Returns true if object is string, otherwise false + * + * @method isString + * @param {Object} + * @return {Boolean} + */ +var isString = function (object) { + return typeof object === 'string' || + (object && object.constructor && object.constructor.name === 'String'); +}; + +/** + * Returns true if object is function, otherwise false + * + * @method isFunction + * @param {Object} + * @return {Boolean} + */ +var isFunction = function (object) { + return typeof object === 'function'; +}; + +/** + * Returns true if object is Objet, otherwise false + * + * @method isObject + * @param {Object} + * @return {Boolean} + */ +var isObject = function (object) { + return typeof object === 'object'; +}; + +/** + * Returns true if object is boolean, otherwise false + * + * @method isBoolean + * @param {Object} + * @return {Boolean} + */ +var isBoolean = function (object) { + return typeof object === 'boolean'; +}; + +/** + * Returns true if object is array, otherwise false + * + * @method isArray + * @param {Object} + * @return {Boolean} + */ +var isArray = function (object) { + return object instanceof Array; +}; + +/** + * Returns true if given string is valid json object + * + * @method isJson + * @param {String} + * @return {Boolean} + */ +var isJson = function (str) { + try { + return !!JSON.parse(str); + } catch (e) { + return false; + } +}; + +module.exports = { + padLeft: padLeft, + padRight: padRight, + toHex: toHex, + toDecimal: toDecimal, + fromDecimal: fromDecimal, + toUtf8: toUtf8, + toAscii: toAscii, + fromUtf8: fromUtf8, + fromAscii: fromAscii, + transformToFullName: transformToFullName, + extractDisplayName: extractDisplayName, + extractTypeName: extractTypeName, + toWei: toWei, + fromWei: fromWei, + toBigNumber: toBigNumber, + toTwosComplement: toTwosComplement, + toAddress: toAddress, + isBigNumber: isBigNumber, + isStrictAddress: isStrictAddress, + isAddress: isAddress, + isChecksumAddress: isChecksumAddress, + toChecksumAddress: toChecksumAddress, + isFunction: isFunction, + isString: isString, + isObject: isObject, + isBoolean: isBoolean, + isArray: isArray, + isJson: isJson +}; + +},{"./sha3.js":19,"bignumber.js":"bignumber.js","utf8":85}],21:[function(require,module,exports){ +module.exports={ + "version": "0.18.1" +} + +},{}],22:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file web3.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Fabian Vogelsteller + * Gav Wood + * @date 2014 + */ + +var RequestManager = require('./web3/requestmanager'); +var Iban = require('./web3/iban'); +var Eth = require('./web3/methods/eth'); +var DB = require('./web3/methods/db'); +var Shh = require('./web3/methods/shh'); +var Net = require('./web3/methods/net'); +var Personal = require('./web3/methods/personal'); +var Swarm = require('./web3/methods/swarm'); +var Settings = require('./web3/settings'); +var version = require('./version.json'); +var utils = require('./utils/utils'); +var sha3 = require('./utils/sha3'); +var extend = require('./web3/extend'); +var Batch = require('./web3/batch'); +var Property = require('./web3/property'); +var HttpProvider = require('./web3/httpprovider'); +var IpcProvider = require('./web3/ipcprovider'); +var BigNumber = require('bignumber.js'); + + + +function Web3 (provider) { + this._requestManager = new RequestManager(provider); + this.currentProvider = provider; + this.eth = new Eth(this); + this.db = new DB(this); + this.shh = new Shh(this); + this.net = new Net(this); + this.personal = new Personal(this); + this.bzz = new Swarm(this); + this.settings = new Settings(); + this.version = { + api: version.version + }; + this.providers = { + HttpProvider: HttpProvider, + IpcProvider: IpcProvider + }; + this._extend = extend(this); + this._extend({ + properties: properties() + }); +} + +// expose providers on the class +Web3.providers = { + HttpProvider: HttpProvider, + IpcProvider: IpcProvider +}; + +Web3.prototype.setProvider = function (provider) { + this._requestManager.setProvider(provider); + this.currentProvider = provider; +}; + +Web3.prototype.reset = function (keepIsSyncing) { + this._requestManager.reset(keepIsSyncing); + this.settings = new Settings(); +}; + +Web3.prototype.BigNumber = BigNumber; +Web3.prototype.toHex = utils.toHex; +Web3.prototype.toAscii = utils.toAscii; +Web3.prototype.toUtf8 = utils.toUtf8; +Web3.prototype.fromAscii = utils.fromAscii; +Web3.prototype.fromUtf8 = utils.fromUtf8; +Web3.prototype.toDecimal = utils.toDecimal; +Web3.prototype.fromDecimal = utils.fromDecimal; +Web3.prototype.toBigNumber = utils.toBigNumber; +Web3.prototype.toWei = utils.toWei; +Web3.prototype.fromWei = utils.fromWei; +Web3.prototype.isAddress = utils.isAddress; +Web3.prototype.isChecksumAddress = utils.isChecksumAddress; +Web3.prototype.toChecksumAddress = utils.toChecksumAddress; +Web3.prototype.isIBAN = utils.isIBAN; + + +Web3.prototype.sha3 = function(string, options) { + return '0x' + sha3(string, options); +}; + +/** + * Transforms direct icap to address + */ +Web3.prototype.fromICAP = function (icap) { + var iban = new Iban(icap); + return iban.address(); +}; + +var properties = function () { + return [ + new Property({ + name: 'version.node', + getter: 'web3_clientVersion' + }), + new Property({ + name: 'version.network', + getter: 'net_version', + inputFormatter: utils.toDecimal + }), + new Property({ + name: 'version.ethereum', + getter: 'eth_protocolVersion', + inputFormatter: utils.toDecimal + }), + new Property({ + name: 'version.whisper', + getter: 'shh_version', + inputFormatter: utils.toDecimal + }) + ]; +}; + +Web3.prototype.isConnected = function(){ + return (this.currentProvider && this.currentProvider.isConnected()); +}; + +Web3.prototype.createBatch = function () { + return new Batch(this); +}; + +module.exports = Web3; + + +},{"./utils/sha3":19,"./utils/utils":20,"./version.json":21,"./web3/batch":24,"./web3/extend":28,"./web3/httpprovider":32,"./web3/iban":33,"./web3/ipcprovider":34,"./web3/methods/db":37,"./web3/methods/eth":38,"./web3/methods/net":39,"./web3/methods/personal":40,"./web3/methods/shh":41,"./web3/methods/swarm":42,"./web3/property":45,"./web3/requestmanager":46,"./web3/settings":47,"bignumber.js":"bignumber.js"}],23:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file allevents.js + * @author Marek Kotewicz + * @date 2014 + */ + +var sha3 = require('../utils/sha3'); +var SolidityEvent = require('./event'); +var formatters = require('./formatters'); +var utils = require('../utils/utils'); +var Filter = require('./filter'); +var watches = require('./methods/watches'); + +var AllSolidityEvents = function (requestManager, json, address) { + this._requestManager = requestManager; + this._json = json; + this._address = address; +}; + +AllSolidityEvents.prototype.encode = function (options) { + options = options || {}; + var result = {}; + + ['fromBlock', 'toBlock'].filter(function (f) { + return options[f] !== undefined; + }).forEach(function (f) { + result[f] = formatters.inputBlockNumberFormatter(options[f]); + }); + + result.address = this._address; + + return result; +}; + +AllSolidityEvents.prototype.decode = function (data) { + data.data = data.data || ''; + data.topics = data.topics || []; + + var eventTopic = data.topics[0].slice(2); + var match = this._json.filter(function (j) { + return eventTopic === sha3(utils.transformToFullName(j)); + })[0]; + + if (!match) { // cannot find matching event? + console.warn('cannot find event for log'); + return data; + } + + var event = new SolidityEvent(this._requestManager, match, this._address); + return event.decode(data); +}; + +AllSolidityEvents.prototype.execute = function (options, callback) { + + if (utils.isFunction(arguments[arguments.length - 1])) { + callback = arguments[arguments.length - 1]; + if(arguments.length === 1) + options = null; + } + + var o = this.encode(options); + var formatter = this.decode.bind(this); + return new Filter(this._requestManager, o, watches.eth(), formatter, callback); +}; + +AllSolidityEvents.prototype.attachToContract = function (contract) { + var execute = this.execute.bind(this); + contract.allEvents = execute; +}; + +module.exports = AllSolidityEvents; + + +},{"../utils/sha3":19,"../utils/utils":20,"./event":27,"./filter":29,"./formatters":30,"./methods/watches":43}],24:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file batch.js + * @author Marek Kotewicz + * @date 2015 + */ + +var Jsonrpc = require('./jsonrpc'); +var errors = require('./errors'); + +var Batch = function (web3) { + this.requestManager = web3._requestManager; + this.requests = []; +}; + +/** + * Should be called to add create new request to batch request + * + * @method add + * @param {Object} jsonrpc requet object + */ +Batch.prototype.add = function (request) { + this.requests.push(request); +}; + +/** + * Should be called to execute batch request + * + * @method execute + */ +Batch.prototype.execute = function () { + var requests = this.requests; + this.requestManager.sendBatch(requests, function (err, results) { + results = results || []; + requests.map(function (request, index) { + return results[index] || {}; + }).forEach(function (result, index) { + if (requests[index].callback) { + + if (!Jsonrpc.isValidResponse(result)) { + return requests[index].callback(errors.InvalidResponse(result)); + } + + requests[index].callback(null, (requests[index].format ? requests[index].format(result.result) : result.result)); + } + }); + }); +}; + +module.exports = Batch; + + +},{"./errors":26,"./jsonrpc":35}],25:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file contract.js + * @author Marek Kotewicz + * @date 2014 + */ + +var utils = require('../utils/utils'); +var coder = require('../solidity/coder'); +var SolidityEvent = require('./event'); +var SolidityFunction = require('./function'); +var AllEvents = require('./allevents'); + +/** + * Should be called to encode constructor params + * + * @method encodeConstructorParams + * @param {Array} abi + * @param {Array} constructor params + */ +var encodeConstructorParams = function (abi, params) { + return abi.filter(function (json) { + return json.type === 'constructor' && json.inputs.length === params.length; + }).map(function (json) { + return json.inputs.map(function (input) { + return input.type; + }); + }).map(function (types) { + return coder.encodeParams(types, params); + })[0] || ''; +}; + +/** + * Should be called to add functions to contract object + * + * @method addFunctionsToContract + * @param {Contract} contract + * @param {Array} abi + */ +var addFunctionsToContract = function (contract) { + contract.abi.filter(function (json) { + return json.type === 'function'; + }).map(function (json) { + return new SolidityFunction(contract._eth, json, contract.address); + }).forEach(function (f) { + f.attachToContract(contract); + }); +}; + +/** + * Should be called to add events to contract object + * + * @method addEventsToContract + * @param {Contract} contract + * @param {Array} abi + */ +var addEventsToContract = function (contract) { + var events = contract.abi.filter(function (json) { + return json.type === 'event'; + }); + + var All = new AllEvents(contract._eth._requestManager, events, contract.address); + All.attachToContract(contract); + + events.map(function (json) { + return new SolidityEvent(contract._eth._requestManager, json, contract.address); + }).forEach(function (e) { + e.attachToContract(contract); + }); +}; + + +/** + * Should be called to check if the contract gets properly deployed on the blockchain. + * + * @method checkForContractAddress + * @param {Object} contract + * @param {Function} callback + * @returns {Undefined} + */ +var checkForContractAddress = function(contract, callback){ + var count = 0, + callbackFired = false; + + // wait for receipt + var filter = contract._eth.filter('latest', function(e){ + if (!e && !callbackFired) { + count++; + + // stop watching after 50 blocks (timeout) + if (count > 50) { + + filter.stopWatching(function() {}); + callbackFired = true; + + if (callback) + callback(new Error('Contract transaction couldn\'t be found after 50 blocks')); + else + throw new Error('Contract transaction couldn\'t be found after 50 blocks'); + + + } else { + + contract._eth.getTransactionReceipt(contract.transactionHash, function(e, receipt){ + if(receipt && !callbackFired) { + + contract._eth.getCode(receipt.contractAddress, function(e, code){ + /*jshint maxcomplexity: 6 */ + + if(callbackFired || !code) + return; + + filter.stopWatching(function() {}); + callbackFired = true; + + if(code.length > 3) { + + // console.log('Contract code deployed!'); + + contract.address = receipt.contractAddress; + + // attach events and methods again after we have + addFunctionsToContract(contract); + addEventsToContract(contract); + + // call callback for the second time + if(callback) + callback(null, contract); + + } else { + if(callback) + callback(new Error('The contract code couldn\'t be stored, please check your gas amount.')); + else + throw new Error('The contract code couldn\'t be stored, please check your gas amount.'); + } + }); + } + }); + } + } + }); +}; + +/** + * Should be called to create new ContractFactory instance + * + * @method ContractFactory + * @param {Array} abi + */ +var ContractFactory = function (eth, abi) { + this.eth = eth; + this.abi = abi; + + /** + * Should be called to create new contract on a blockchain + * + * @method new + * @param {Any} contract constructor param1 (optional) + * @param {Any} contract constructor param2 (optional) + * @param {Object} contract transaction object (required) + * @param {Function} callback + * @returns {Contract} returns contract instance + */ + this.new = function () { + var contract = new Contract(this.eth, this.abi); + + // parse arguments + var options = {}; // required! + var callback; + + var args = Array.prototype.slice.call(arguments); + if (utils.isFunction(args[args.length - 1])) { + callback = args.pop(); + } + + var last = args[args.length - 1]; + if (utils.isObject(last) && !utils.isArray(last)) { + options = args.pop(); + } + + if (options.value > 0) { + var constructorAbi = abi.filter(function (json) { + return json.type === 'constructor' && json.inputs.length === args.length; + })[0] || {}; + + if (!constructorAbi.payable) { + throw new Error('Cannot send value to non-payable constructor'); + } + } + + var bytes = encodeConstructorParams(this.abi, args); + options.data += bytes; + + if (callback) { + + // wait for the contract address adn check if the code was deployed + this.eth.sendTransaction(options, function (err, hash) { + if (err) { + callback(err); + } else { + // add the transaction hash + contract.transactionHash = hash; + + // call callback for the first time + callback(null, contract); + + checkForContractAddress(contract, callback); + } + }); + } else { + var hash = this.eth.sendTransaction(options); + // add the transaction hash + contract.transactionHash = hash; + checkForContractAddress(contract); + } + + return contract; + }; + + this.new.getData = this.getData.bind(this); +}; + +/** + * Should be called to create new ContractFactory + * + * @method contract + * @param {Array} abi + * @returns {ContractFactory} new contract factory + */ +//var contract = function (abi) { + //return new ContractFactory(abi); +//}; + + + +/** + * Should be called to get access to existing contract on a blockchain + * + * @method at + * @param {Address} contract address (required) + * @param {Function} callback {optional) + * @returns {Contract} returns contract if no callback was passed, + * otherwise calls callback function (err, contract) + */ +ContractFactory.prototype.at = function (address, callback) { + var contract = new Contract(this.eth, this.abi, address); + + // this functions are not part of prototype, + // because we dont want to spoil the interface + addFunctionsToContract(contract); + addEventsToContract(contract); + + if (callback) { + callback(null, contract); + } + return contract; +}; + +/** + * Gets the data, which is data to deploy plus constructor params + * + * @method getData + */ +ContractFactory.prototype.getData = function () { + var options = {}; // required! + var args = Array.prototype.slice.call(arguments); + + var last = args[args.length - 1]; + if (utils.isObject(last) && !utils.isArray(last)) { + options = args.pop(); + } + + var bytes = encodeConstructorParams(this.abi, args); + options.data += bytes; + + return options.data; +}; + +/** + * Should be called to create new contract instance + * + * @method Contract + * @param {Array} abi + * @param {Address} contract address + */ +var Contract = function (eth, abi, address) { + this._eth = eth; + this.transactionHash = null; + this.address = address; + this.abi = abi; +}; + +module.exports = ContractFactory; + +},{"../solidity/coder":7,"../utils/utils":20,"./allevents":23,"./event":27,"./function":31}],26:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file errors.js + * @author Marek Kotewicz + * @date 2015 + */ + +module.exports = { + InvalidNumberOfParams: function () { + return new Error('Invalid number of input parameters'); + }, + InvalidConnection: function (host){ + return new Error('CONNECTION ERROR: Couldn\'t connect to node '+ host +'.'); + }, + InvalidProvider: function () { + return new Error('Provider not set or invalid'); + }, + InvalidResponse: function (result){ + var message = !!result && !!result.error && !!result.error.message ? result.error.message : 'Invalid JSON RPC response: ' + JSON.stringify(result); + return new Error(message); + }, + ConnectionTimeout: function (ms){ + return new Error('CONNECTION TIMEOUT: timeout of ' + ms + ' ms achived'); + } +}; + +},{}],27:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file event.js + * @author Marek Kotewicz + * @date 2014 + */ + +var utils = require('../utils/utils'); +var coder = require('../solidity/coder'); +var formatters = require('./formatters'); +var sha3 = require('../utils/sha3'); +var Filter = require('./filter'); +var watches = require('./methods/watches'); + +/** + * This prototype should be used to create event filters + */ +var SolidityEvent = function (requestManager, json, address) { + this._requestManager = requestManager; + this._params = json.inputs; + this._name = utils.transformToFullName(json); + this._address = address; + this._anonymous = json.anonymous; +}; + +/** + * Should be used to get filtered param types + * + * @method types + * @param {Bool} decide if returned typed should be indexed + * @return {Array} array of types + */ +SolidityEvent.prototype.types = function (indexed) { + return this._params.filter(function (i) { + return i.indexed === indexed; + }).map(function (i) { + return i.type; + }); +}; + +/** + * Should be used to get event display name + * + * @method displayName + * @return {String} event display name + */ +SolidityEvent.prototype.displayName = function () { + return utils.extractDisplayName(this._name); +}; + +/** + * Should be used to get event type name + * + * @method typeName + * @return {String} event type name + */ +SolidityEvent.prototype.typeName = function () { + return utils.extractTypeName(this._name); +}; + +/** + * Should be used to get event signature + * + * @method signature + * @return {String} event signature + */ +SolidityEvent.prototype.signature = function () { + return sha3(this._name); +}; + +/** + * Should be used to encode indexed params and options to one final object + * + * @method encode + * @param {Object} indexed + * @param {Object} options + * @return {Object} everything combined together and encoded + */ +SolidityEvent.prototype.encode = function (indexed, options) { + indexed = indexed || {}; + options = options || {}; + var result = {}; + + ['fromBlock', 'toBlock'].filter(function (f) { + return options[f] !== undefined; + }).forEach(function (f) { + result[f] = formatters.inputBlockNumberFormatter(options[f]); + }); + + result.topics = []; + + result.address = this._address; + if (!this._anonymous) { + result.topics.push('0x' + this.signature()); + } + + var indexedTopics = this._params.filter(function (i) { + return i.indexed === true; + }).map(function (i) { + var value = indexed[i.name]; + if (value === undefined || value === null) { + return null; + } + + if (utils.isArray(value)) { + return value.map(function (v) { + return '0x' + coder.encodeParam(i.type, v); + }); + } + return '0x' + coder.encodeParam(i.type, value); + }); + + result.topics = result.topics.concat(indexedTopics); + + return result; +}; + +/** + * Should be used to decode indexed params and options + * + * @method decode + * @param {Object} data + * @return {Object} result object with decoded indexed && not indexed params + */ +SolidityEvent.prototype.decode = function (data) { + + data.data = data.data || ''; + data.topics = data.topics || []; + + var argTopics = this._anonymous ? data.topics : data.topics.slice(1); + var indexedData = argTopics.map(function (topics) { return topics.slice(2); }).join(""); + var indexedParams = coder.decodeParams(this.types(true), indexedData); + + var notIndexedData = data.data.slice(2); + var notIndexedParams = coder.decodeParams(this.types(false), notIndexedData); + + var result = formatters.outputLogFormatter(data); + result.event = this.displayName(); + result.address = data.address; + + result.args = this._params.reduce(function (acc, current) { + acc[current.name] = current.indexed ? indexedParams.shift() : notIndexedParams.shift(); + return acc; + }, {}); + + delete result.data; + delete result.topics; + + return result; +}; + +/** + * Should be used to create new filter object from event + * + * @method execute + * @param {Object} indexed + * @param {Object} options + * @return {Object} filter object + */ +SolidityEvent.prototype.execute = function (indexed, options, callback) { + + if (utils.isFunction(arguments[arguments.length - 1])) { + callback = arguments[arguments.length - 1]; + if(arguments.length === 2) + options = null; + if(arguments.length === 1) { + options = null; + indexed = {}; + } + } + + var o = this.encode(indexed, options); + var formatter = this.decode.bind(this); + return new Filter(this._requestManager, o, watches.eth(), formatter, callback); +}; + +/** + * Should be used to attach event to contract object + * + * @method attachToContract + * @param {Contract} + */ +SolidityEvent.prototype.attachToContract = function (contract) { + var execute = this.execute.bind(this); + var displayName = this.displayName(); + if (!contract[displayName]) { + contract[displayName] = execute; + } + contract[displayName][this.typeName()] = this.execute.bind(this, contract); +}; + +module.exports = SolidityEvent; + + +},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./filter":29,"./formatters":30,"./methods/watches":43}],28:[function(require,module,exports){ +var formatters = require('./formatters'); +var utils = require('./../utils/utils'); +var Method = require('./method'); +var Property = require('./property'); + +// TODO: refactor, so the input params are not altered. +// it's necessary to make same 'extension' work with multiple providers +var extend = function (web3) { + /* jshint maxcomplexity:5 */ + var ex = function (extension) { + + var extendedObject; + if (extension.property) { + if (!web3[extension.property]) { + web3[extension.property] = {}; + } + extendedObject = web3[extension.property]; + } else { + extendedObject = web3; + } + + if (extension.methods) { + extension.methods.forEach(function (method) { + method.attachToObject(extendedObject); + method.setRequestManager(web3._requestManager); + }); + } + + if (extension.properties) { + extension.properties.forEach(function (property) { + property.attachToObject(extendedObject); + property.setRequestManager(web3._requestManager); + }); + } + }; + + ex.formatters = formatters; + ex.utils = utils; + ex.Method = Method; + ex.Property = Property; + + return ex; +}; + + + +module.exports = extend; + + +},{"./../utils/utils":20,"./formatters":30,"./method":36,"./property":45}],29:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file filter.js + * @authors: + * Jeffrey Wilcke + * Marek Kotewicz + * Marian Oancea + * Fabian Vogelsteller + * Gav Wood + * @date 2014 + */ + +var formatters = require('./formatters'); +var utils = require('../utils/utils'); + +/** +* Converts a given topic to a hex string, but also allows null values. +* +* @param {Mixed} value +* @return {String} +*/ +var toTopic = function(value){ + + if(value === null || typeof value === 'undefined') + return null; + + value = String(value); + + if(value.indexOf('0x') === 0) + return value; + else + return utils.fromUtf8(value); +}; + +/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones +/// @param should be string or object +/// @returns options string or object +var getOptions = function (options) { + + if (utils.isString(options)) { + return options; + } + + options = options || {}; + + // make sure topics, get converted to hex + options.topics = options.topics || []; + options.topics = options.topics.map(function(topic){ + return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic); + }); + + return { + topics: options.topics, + from: options.from, + to: options.to, + address: options.address, + fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock), + toBlock: formatters.inputBlockNumberFormatter(options.toBlock) + }; +}; + +/** +Adds the callback and sets up the methods, to iterate over the results. + +@method getLogsAtStart +@param {Object} self +@param {funciton} +*/ +var getLogsAtStart = function(self, callback){ + // call getFilterLogs for the first watch callback start + if (!utils.isString(self.options)) { + self.get(function (err, messages) { + // don't send all the responses to all the watches again... just to self one + if (err) { + callback(err); + } + + if(utils.isArray(messages)) { + messages.forEach(function (message) { + callback(null, message); + }); + } + }); + } +}; + +/** +Adds the callback and sets up the methods, to iterate over the results. + +@method pollFilter +@param {Object} self +*/ +var pollFilter = function(self) { + + var onMessage = function (error, messages) { + if (error) { + return self.callbacks.forEach(function (callback) { + callback(error); + }); + } + + if(utils.isArray(messages)) { + messages.forEach(function (message) { + message = self.formatter ? self.formatter(message) : message; + self.callbacks.forEach(function (callback) { + callback(null, message); + }); + }); + } + }; + + self.requestManager.startPolling({ + method: self.implementation.poll.call, + params: [self.filterId], + }, self.filterId, onMessage, self.stopWatching.bind(self)); + +}; + +var Filter = function (requestManager, options, methods, formatter, callback, filterCreationErrorCallback) { + var self = this; + var implementation = {}; + methods.forEach(function (method) { + method.setRequestManager(requestManager); + method.attachToObject(implementation); + }); + this.requestManager = requestManager; + this.options = getOptions(options); + this.implementation = implementation; + this.filterId = null; + this.callbacks = []; + this.getLogsCallbacks = []; + this.pollFilters = []; + this.formatter = formatter; + this.implementation.newFilter(this.options, function(error, id){ + if(error) { + self.callbacks.forEach(function(cb){ + cb(error); + }); + filterCreationErrorCallback(error); + } else { + self.filterId = id; + + // check if there are get pending callbacks as a consequence + // of calling get() with filterId unassigned. + self.getLogsCallbacks.forEach(function (cb){ + self.get(cb); + }); + self.getLogsCallbacks = []; + + // get filter logs for the already existing watch calls + self.callbacks.forEach(function(cb){ + getLogsAtStart(self, cb); + }); + if(self.callbacks.length > 0) + pollFilter(self); + + // start to watch immediately + if(typeof callback === 'function') { + return self.watch(callback); + } + } + }); + + return this; +}; + +Filter.prototype.watch = function (callback) { + this.callbacks.push(callback); + + if(this.filterId) { + getLogsAtStart(this, callback); + pollFilter(this); + } + + return this; +}; + +Filter.prototype.stopWatching = function (callback) { + this.requestManager.stopPolling(this.filterId); + this.callbacks = []; + // remove filter async + if (callback) { + this.implementation.uninstallFilter(this.filterId, callback); + } else { + return this.implementation.uninstallFilter(this.filterId); + } +}; + +Filter.prototype.get = function (callback) { + var self = this; + if (utils.isFunction(callback)) { + if (this.filterId === null) { + // If filterId is not set yet, call it back + // when newFilter() assigns it. + this.getLogsCallbacks.push(callback); + } else { + this.implementation.getLogs(this.filterId, function(err, res){ + if (err) { + callback(err); + } else { + callback(null, res.map(function (log) { + return self.formatter ? self.formatter(log) : log; + })); + } + }); + } + } else { + if (this.filterId === null) { + throw new Error('Filter ID Error: filter().get() can\'t be chained synchronous, please provide a callback for the get() method.'); + } + var logs = this.implementation.getLogs(this.filterId); + return logs.map(function (log) { + return self.formatter ? self.formatter(log) : log; + }); + } + + return this; +}; + +module.exports = Filter; + + +},{"../utils/utils":20,"./formatters":30}],30:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file formatters.js + * @author Marek Kotewicz + * @author Fabian Vogelsteller + * @date 2015 + */ + +var utils = require('../utils/utils'); +var config = require('../utils/config'); +var Iban = require('./iban'); + +/** + * Should the format output to a big number + * + * @method outputBigNumberFormatter + * @param {String|Number|BigNumber} + * @returns {BigNumber} object + */ +var outputBigNumberFormatter = function (number) { + return utils.toBigNumber(number); +}; + +var isPredefinedBlockNumber = function (blockNumber) { + return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest'; +}; + +var inputDefaultBlockNumberFormatter = function (blockNumber) { + if (blockNumber === undefined) { + return config.defaultBlock; + } + return inputBlockNumberFormatter(blockNumber); +}; + +var inputBlockNumberFormatter = function (blockNumber) { + if (blockNumber === undefined) { + return undefined; + } else if (isPredefinedBlockNumber(blockNumber)) { + return blockNumber; + } + return utils.toHex(blockNumber); +}; + +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputCallFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputCallFormatter = function (options){ + + options.from = options.from || config.defaultAccount; + + if (options.from) { + options.from = inputAddressFormatter(options.from); + } + + if (options.to) { // it might be contract creation + options.to = inputAddressFormatter(options.to); + } + + ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { + return options[key] !== undefined; + }).forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); + + return options; +}; + +/** + * Formats the input of a transaction and converts all values to HEX + * + * @method inputTransactionFormatter + * @param {Object} transaction options + * @returns object +*/ +var inputTransactionFormatter = function (options){ + + options.from = options.from || config.defaultAccount; + options.from = inputAddressFormatter(options.from); + + if (options.to) { // it might be contract creation + options.to = inputAddressFormatter(options.to); + } + + ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) { + return options[key] !== undefined; + }).forEach(function(key){ + options[key] = utils.fromDecimal(options[key]); + }); + + return options; +}; + +/** + * Formats the output of a transaction to its proper values + * + * @method outputTransactionFormatter + * @param {Object} tx + * @returns {Object} +*/ +var outputTransactionFormatter = function (tx){ + if(tx.blockNumber !== null) + tx.blockNumber = utils.toDecimal(tx.blockNumber); + if(tx.transactionIndex !== null) + tx.transactionIndex = utils.toDecimal(tx.transactionIndex); + tx.nonce = utils.toDecimal(tx.nonce); + tx.gas = utils.toDecimal(tx.gas); + tx.gasPrice = utils.toBigNumber(tx.gasPrice); + tx.value = utils.toBigNumber(tx.value); + return tx; +}; + +/** + * Formats the output of a transaction receipt to its proper values + * + * @method outputTransactionReceiptFormatter + * @param {Object} receipt + * @returns {Object} +*/ +var outputTransactionReceiptFormatter = function (receipt){ + if(receipt.blockNumber !== null) + receipt.blockNumber = utils.toDecimal(receipt.blockNumber); + if(receipt.transactionIndex !== null) + receipt.transactionIndex = utils.toDecimal(receipt.transactionIndex); + receipt.cumulativeGasUsed = utils.toDecimal(receipt.cumulativeGasUsed); + receipt.gasUsed = utils.toDecimal(receipt.gasUsed); + + if(utils.isArray(receipt.logs)) { + receipt.logs = receipt.logs.map(function(log){ + return outputLogFormatter(log); + }); + } + + return receipt; +}; + +/** + * Formats the output of a block to its proper values + * + * @method outputBlockFormatter + * @param {Object} block + * @returns {Object} +*/ +var outputBlockFormatter = function(block) { + + // transform to number + block.gasLimit = utils.toDecimal(block.gasLimit); + block.gasUsed = utils.toDecimal(block.gasUsed); + block.size = utils.toDecimal(block.size); + block.timestamp = utils.toDecimal(block.timestamp); + if(block.number !== null) + block.number = utils.toDecimal(block.number); + + block.difficulty = utils.toBigNumber(block.difficulty); + block.totalDifficulty = utils.toBigNumber(block.totalDifficulty); + + if (utils.isArray(block.transactions)) { + block.transactions.forEach(function(item){ + if(!utils.isString(item)) + return outputTransactionFormatter(item); + }); + } + + return block; +}; + +/** + * Formats the output of a log + * + * @method outputLogFormatter + * @param {Object} log object + * @returns {Object} log +*/ +var outputLogFormatter = function(log) { + if(log.blockNumber !== null) + log.blockNumber = utils.toDecimal(log.blockNumber); + if(log.transactionIndex !== null) + log.transactionIndex = utils.toDecimal(log.transactionIndex); + if(log.logIndex !== null) + log.logIndex = utils.toDecimal(log.logIndex); + + return log; +}; + +/** + * Formats the input of a whisper post and converts all values to HEX + * + * @method inputPostFormatter + * @param {Object} transaction object + * @returns {Object} +*/ +var inputPostFormatter = function(post) { + + // post.payload = utils.toHex(post.payload); + post.ttl = utils.fromDecimal(post.ttl); + post.workToProve = utils.fromDecimal(post.workToProve); + post.priority = utils.fromDecimal(post.priority); + + // fallback + if (!utils.isArray(post.topics)) { + post.topics = post.topics ? [post.topics] : []; + } + + // format the following options + post.topics = post.topics.map(function(topic){ + // convert only if not hex + return (topic.indexOf('0x') === 0) ? topic : utils.fromUtf8(topic); + }); + + return post; +}; + +/** + * Formats the output of a received post message + * + * @method outputPostFormatter + * @param {Object} + * @returns {Object} + */ +var outputPostFormatter = function(post){ + + post.expiry = utils.toDecimal(post.expiry); + post.sent = utils.toDecimal(post.sent); + post.ttl = utils.toDecimal(post.ttl); + post.workProved = utils.toDecimal(post.workProved); + // post.payloadRaw = post.payload; + // post.payload = utils.toAscii(post.payload); + + // if (utils.isJson(post.payload)) { + // post.payload = JSON.parse(post.payload); + // } + + // format the following options + if (!post.topics) { + post.topics = []; + } + post.topics = post.topics.map(function(topic){ + return utils.toAscii(topic); + }); + + return post; +}; + +var inputAddressFormatter = function (address) { + var iban = new Iban(address); + if (iban.isValid() && iban.isDirect()) { + return '0x' + iban.address(); + } else if (utils.isStrictAddress(address)) { + return address; + } else if (utils.isAddress(address)) { + return '0x' + address; + } + throw new Error('invalid address'); +}; + + +var outputSyncingFormatter = function(result) { + + result.startingBlock = utils.toDecimal(result.startingBlock); + result.currentBlock = utils.toDecimal(result.currentBlock); + result.highestBlock = utils.toDecimal(result.highestBlock); + if (result.knownStates) { + result.knownStates = utils.toDecimal(result.knownStates); + result.pulledStates = utils.toDecimal(result.pulledStates); + } + + return result; +}; + +module.exports = { + inputDefaultBlockNumberFormatter: inputDefaultBlockNumberFormatter, + inputBlockNumberFormatter: inputBlockNumberFormatter, + inputCallFormatter: inputCallFormatter, + inputTransactionFormatter: inputTransactionFormatter, + inputAddressFormatter: inputAddressFormatter, + inputPostFormatter: inputPostFormatter, + outputBigNumberFormatter: outputBigNumberFormatter, + outputTransactionFormatter: outputTransactionFormatter, + outputTransactionReceiptFormatter: outputTransactionReceiptFormatter, + outputBlockFormatter: outputBlockFormatter, + outputLogFormatter: outputLogFormatter, + outputPostFormatter: outputPostFormatter, + outputSyncingFormatter: outputSyncingFormatter +}; + + +},{"../utils/config":18,"../utils/utils":20,"./iban":33}],31:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file function.js + * @author Marek Kotewicz + * @date 2015 + */ + +var coder = require('../solidity/coder'); +var utils = require('../utils/utils'); +var formatters = require('./formatters'); +var sha3 = require('../utils/sha3'); + +/** + * This prototype should be used to call/sendTransaction to solidity functions + */ +var SolidityFunction = function (eth, json, address) { + this._eth = eth; + this._inputTypes = json.inputs.map(function (i) { + return i.type; + }); + this._outputTypes = json.outputs.map(function (i) { + return i.type; + }); + this._constant = json.constant; + this._payable = json.payable; + this._name = utils.transformToFullName(json); + this._address = address; +}; + +SolidityFunction.prototype.extractCallback = function (args) { + if (utils.isFunction(args[args.length - 1])) { + return args.pop(); // modify the args array! + } +}; + +SolidityFunction.prototype.extractDefaultBlock = function (args) { + if (args.length > this._inputTypes.length && !utils.isObject(args[args.length -1])) { + return formatters.inputDefaultBlockNumberFormatter(args.pop()); // modify the args array! + } +}; + +/** + * Should be used to create payload from arguments + * + * @method toPayload + * @param {Array} solidity function params + * @param {Object} optional payload options + */ +SolidityFunction.prototype.toPayload = function (args) { + var options = {}; + if (args.length > this._inputTypes.length && utils.isObject(args[args.length -1])) { + options = args[args.length - 1]; + } + options.to = this._address; + options.data = '0x' + this.signature() + coder.encodeParams(this._inputTypes, args); + return options; +}; + +/** + * Should be used to get function signature + * + * @method signature + * @return {String} function signature + */ +SolidityFunction.prototype.signature = function () { + return sha3(this._name).slice(0, 8); +}; + + +SolidityFunction.prototype.unpackOutput = function (output) { + if (!output) { + return; + } + + output = output.length >= 2 ? output.slice(2) : output; + var result = coder.decodeParams(this._outputTypes, output); + return result.length === 1 ? result[0] : result; +}; + +/** + * Calls a contract function. + * + * @method call + * @param {...Object} Contract function arguments + * @param {function} If the last argument is a function, the contract function + * call will be asynchronous, and the callback will be passed the + * error and result. + * @return {String} output bytes + */ +SolidityFunction.prototype.call = function () { + var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; }); + var callback = this.extractCallback(args); + var defaultBlock = this.extractDefaultBlock(args); + var payload = this.toPayload(args); + + + if (!callback) { + var output = this._eth.call(payload, defaultBlock); + return this.unpackOutput(output); + } + + var self = this; + this._eth.call(payload, defaultBlock, function (error, output) { + if (error) return callback(error, null); + + var unpacked = null; + try { + unpacked = self.unpackOutput(output); + } + catch (e) { + error = e; + } + + callback(error, unpacked); + }); +}; + +/** + * Should be used to sendTransaction to solidity function + * + * @method sendTransaction + */ +SolidityFunction.prototype.sendTransaction = function () { + var args = Array.prototype.slice.call(arguments).filter(function (a) {return a !== undefined; }); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + + if (payload.value > 0 && !this._payable) { + throw new Error('Cannot send value to non-payable function'); + } + + if (!callback) { + return this._eth.sendTransaction(payload); + } + + this._eth.sendTransaction(payload, callback); +}; + +/** + * Should be used to estimateGas of solidity function + * + * @method estimateGas + */ +SolidityFunction.prototype.estimateGas = function () { + var args = Array.prototype.slice.call(arguments); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + + if (!callback) { + return this._eth.estimateGas(payload); + } + + this._eth.estimateGas(payload, callback); +}; + +/** + * Return the encoded data of the call + * + * @method getData + * @return {String} the encoded data + */ +SolidityFunction.prototype.getData = function () { + var args = Array.prototype.slice.call(arguments); + var payload = this.toPayload(args); + + return payload.data; +}; + +/** + * Should be used to get function display name + * + * @method displayName + * @return {String} display name of the function + */ +SolidityFunction.prototype.displayName = function () { + return utils.extractDisplayName(this._name); +}; + +/** + * Should be used to get function type name + * + * @method typeName + * @return {String} type name of the function + */ +SolidityFunction.prototype.typeName = function () { + return utils.extractTypeName(this._name); +}; + +/** + * Should be called to get rpc requests from solidity function + * + * @method request + * @returns {Object} + */ +SolidityFunction.prototype.request = function () { + var args = Array.prototype.slice.call(arguments); + var callback = this.extractCallback(args); + var payload = this.toPayload(args); + var format = this.unpackOutput.bind(this); + + return { + method: this._constant ? 'eth_call' : 'eth_sendTransaction', + callback: callback, + params: [payload], + format: format + }; +}; + +/** + * Should be called to execute function + * + * @method execute + */ +SolidityFunction.prototype.execute = function () { + var transaction = !this._constant; + + // send transaction + if (transaction) { + return this.sendTransaction.apply(this, Array.prototype.slice.call(arguments)); + } + + // call + return this.call.apply(this, Array.prototype.slice.call(arguments)); +}; + +/** + * Should be called to attach function to contract + * + * @method attachToContract + * @param {Contract} + */ +SolidityFunction.prototype.attachToContract = function (contract) { + var execute = this.execute.bind(this); + execute.request = this.request.bind(this); + execute.call = this.call.bind(this); + execute.sendTransaction = this.sendTransaction.bind(this); + execute.estimateGas = this.estimateGas.bind(this); + execute.getData = this.getData.bind(this); + var displayName = this.displayName(); + if (!contract[displayName]) { + contract[displayName] = execute; + } + contract[displayName][this.typeName()] = execute; // circular!!!! +}; + +module.exports = SolidityFunction; + + +},{"../solidity/coder":7,"../utils/sha3":19,"../utils/utils":20,"./formatters":30}],32:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file httpprovider.js + * @authors: + * Marek Kotewicz + * Marian Oancea + * Fabian Vogelsteller + * @date 2015 + */ + +"use strict"; + +var errors = require('./errors'); + +// workaround to use httpprovider in different envs +// var XMLHttpRequest; // jshint ignore: line + +// browser +if (typeof window !== 'undefined' && window.XMLHttpRequest) { + XMLHttpRequest = window.XMLHttpRequest; // jshint ignore: line +// node +} else { + XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore: line +} + +var XHR2 = require('xhr2'); // jshint ignore: line + +/** + * HttpProvider should be used to send rpc calls over http + */ +var HttpProvider = function (host, timeout) { + this.host = host || 'http://localhost:8545'; + this.timeout = timeout || 0; +}; + +/** + * Should be called to prepare new XMLHttpRequest + * + * @method prepareRequest + * @param {Boolean} true if request should be async + * @return {XMLHttpRequest} object + */ +HttpProvider.prototype.prepareRequest = function (async) { + var request; + + if (async) { + request = new XHR2(); + request.timeout = this.timeout; + }else { + request = new XMLHttpRequest(); + } + + request.open('POST', this.host, async); + request.setRequestHeader('Content-Type','application/json'); + return request; +}; + +/** + * Should be called to make sync request + * + * @method send + * @param {Object} payload + * @return {Object} result + */ +HttpProvider.prototype.send = function (payload) { + var request = this.prepareRequest(false); + + try { + request.send(JSON.stringify(payload)); + } catch(error) { + throw errors.InvalidConnection(this.host); + } + + var result = request.responseText; + + try { + result = JSON.parse(result); + } catch(e) { + throw errors.InvalidResponse(request.responseText); + } + + return result; +}; + +/** + * Should be used to make async request + * + * @method sendAsync + * @param {Object} payload + * @param {Function} callback triggered on end with (err, result) + */ +HttpProvider.prototype.sendAsync = function (payload, callback) { + var request = this.prepareRequest(true); + + request.onreadystatechange = function() { + if (request.readyState === 4 && request.timeout !== 1) { + var result = request.responseText; + var error = null; + + try { + result = JSON.parse(result); + } catch(e) { + error = errors.InvalidResponse(request.responseText); + } + + callback(error, result); + } + }; + + request.ontimeout = function() { + callback(errors.ConnectionTimeout(this.timeout)); + }; + + try { + request.send(JSON.stringify(payload)); + } catch(error) { + callback(errors.InvalidConnection(this.host)); + } +}; + +/** + * Synchronously tries to make Http request + * + * @method isConnected + * @return {Boolean} returns true if request haven't failed. Otherwise false + */ +HttpProvider.prototype.isConnected = function() { + try { + this.send({ + id: 9999999999, + jsonrpc: '2.0', + method: 'net_listening', + params: [] + }); + return true; + } catch(e) { + return false; + } +}; + +module.exports = HttpProvider; + +},{"./errors":26,"xhr2":86,"xmlhttprequest":17}],33:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file iban.js + * @author Marek Kotewicz + * @date 2015 + */ + +var BigNumber = require('bignumber.js'); + +var padLeft = function (string, bytes) { + var result = string; + while (result.length < bytes * 2) { + result = '0' + result; + } + return result; +}; + +/** + * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to + * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. + * + * @method iso13616Prepare + * @param {String} iban the IBAN + * @returns {String} the prepared IBAN + */ +var iso13616Prepare = function (iban) { + var A = 'A'.charCodeAt(0); + var Z = 'Z'.charCodeAt(0); + + iban = iban.toUpperCase(); + iban = iban.substr(4) + iban.substr(0,4); + + return iban.split('').map(function(n){ + var code = n.charCodeAt(0); + if (code >= A && code <= Z){ + // A = 10, B = 11, ... Z = 35 + return code - A + 10; + } else { + return n; + } + }).join(''); +}; + +/** + * Calculates the MOD 97 10 of the passed IBAN as specified in ISO7064. + * + * @method mod9710 + * @param {String} iban + * @returns {Number} + */ +var mod9710 = function (iban) { + var remainder = iban, + block; + + while (remainder.length > 2){ + block = remainder.slice(0, 9); + remainder = parseInt(block, 10) % 97 + remainder.slice(block.length); + } + + return parseInt(remainder, 10) % 97; +}; + +/** + * This prototype should be used to create iban object from iban correct string + * + * @param {String} iban + */ +var Iban = function (iban) { + this._iban = iban; +}; + +/** + * This method should be used to create iban object from ethereum address + * + * @method fromAddress + * @param {String} address + * @return {Iban} the IBAN object + */ +Iban.fromAddress = function (address) { + var asBn = new BigNumber(address, 16); + var base36 = asBn.toString(36); + var padded = padLeft(base36, 15); + return Iban.fromBban(padded.toUpperCase()); +}; + +/** + * Convert the passed BBAN to an IBAN for this country specification. + * Please note that "generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account". + * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits + * + * @method fromBban + * @param {String} bban the BBAN to convert to IBAN + * @returns {Iban} the IBAN object + */ +Iban.fromBban = function (bban) { + var countryCode = 'XE'; + + var remainder = mod9710(iso13616Prepare(countryCode + '00' + bban)); + var checkDigit = ('0' + (98 - remainder)).slice(-2); + + return new Iban(countryCode + checkDigit + bban); +}; + +/** + * Should be used to create IBAN object for given institution and identifier + * + * @method createIndirect + * @param {Object} options, required options are "institution" and "identifier" + * @return {Iban} the IBAN object + */ +Iban.createIndirect = function (options) { + return Iban.fromBban('ETH' + options.institution + options.identifier); +}; + +/** + * Thos method should be used to check if given string is valid iban object + * + * @method isValid + * @param {String} iban string + * @return {Boolean} true if it is valid IBAN + */ +Iban.isValid = function (iban) { + var i = new Iban(iban); + return i.isValid(); +}; + +/** + * Should be called to check if iban is correct + * + * @method isValid + * @returns {Boolean} true if it is, otherwise false + */ +Iban.prototype.isValid = function () { + return /^XE[0-9]{2}(ETH[0-9A-Z]{13}|[0-9A-Z]{30,31})$/.test(this._iban) && + mod9710(iso13616Prepare(this._iban)) === 1; +}; + +/** + * Should be called to check if iban number is direct + * + * @method isDirect + * @returns {Boolean} true if it is, otherwise false + */ +Iban.prototype.isDirect = function () { + return this._iban.length === 34 || this._iban.length === 35; +}; + +/** + * Should be called to check if iban number if indirect + * + * @method isIndirect + * @returns {Boolean} true if it is, otherwise false + */ +Iban.prototype.isIndirect = function () { + return this._iban.length === 20; +}; + +/** + * Should be called to get iban checksum + * Uses the mod-97-10 checksumming protocol (ISO/IEC 7064:2003) + * + * @method checksum + * @returns {String} checksum + */ +Iban.prototype.checksum = function () { + return this._iban.substr(2, 2); +}; + +/** + * Should be called to get institution identifier + * eg. XREG + * + * @method institution + * @returns {String} institution identifier + */ +Iban.prototype.institution = function () { + return this.isIndirect() ? this._iban.substr(7, 4) : ''; +}; + +/** + * Should be called to get client identifier within institution + * eg. GAVOFYORK + * + * @method client + * @returns {String} client identifier + */ +Iban.prototype.client = function () { + return this.isIndirect() ? this._iban.substr(11) : ''; +}; + +/** + * Should be called to get client direct address + * + * @method address + * @returns {String} client direct address + */ +Iban.prototype.address = function () { + if (this.isDirect()) { + var base36 = this._iban.substr(4); + var asBn = new BigNumber(base36, 36); + return padLeft(asBn.toString(16), 20); + } + + return ''; +}; + +Iban.prototype.toString = function () { + return this._iban; +}; + +module.exports = Iban; + + +},{"bignumber.js":"bignumber.js"}],34:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file ipcprovider.js + * @authors: + * Fabian Vogelsteller + * @date 2015 + */ + +"use strict"; + +var utils = require('../utils/utils'); +var errors = require('./errors'); + + +var IpcProvider = function (path, net) { + var _this = this; + this.responseCallbacks = {}; + this.path = path; + + this.connection = net.connect({path: this.path}); + + this.connection.on('error', function(e){ + console.error('IPC Connection Error', e); + _this._timeout(); + }); + + this.connection.on('end', function(){ + _this._timeout(); + }); + + + // LISTEN FOR CONNECTION RESPONSES + this.connection.on('data', function(data) { + /*jshint maxcomplexity: 6 */ + + _this._parseResponse(data.toString()).forEach(function(result){ + + var id = null; + + // get the id which matches the returned id + if(utils.isArray(result)) { + result.forEach(function(load){ + if(_this.responseCallbacks[load.id]) + id = load.id; + }); + } else { + id = result.id; + } + + // fire the callback + if(_this.responseCallbacks[id]) { + _this.responseCallbacks[id](null, result); + delete _this.responseCallbacks[id]; + } + }); + }); +}; + +/** +Will parse the response and make an array out of it. + +@method _parseResponse +@param {String} data +*/ +IpcProvider.prototype._parseResponse = function(data) { + var _this = this, + returnValues = []; + + // DE-CHUNKER + var dechunkedData = data + .replace(/\}[\n\r]?\{/g,'}|--|{') // }{ + .replace(/\}\][\n\r]?\[\{/g,'}]|--|[{') // }][{ + .replace(/\}[\n\r]?\[\{/g,'}|--|[{') // }[{ + .replace(/\}\][\n\r]?\{/g,'}]|--|{') // }]{ + .split('|--|'); + + dechunkedData.forEach(function(data){ + + // prepend the last chunk + if(_this.lastChunk) + data = _this.lastChunk + data; + + var result = null; + + try { + result = JSON.parse(data); + + } catch(e) { + + _this.lastChunk = data; + + // start timeout to cancel all requests + clearTimeout(_this.lastChunkTimeout); + _this.lastChunkTimeout = setTimeout(function(){ + _this._timeout(); + throw errors.InvalidResponse(data); + }, 1000 * 15); + + return; + } + + // cancel timeout and set chunk to null + clearTimeout(_this.lastChunkTimeout); + _this.lastChunk = null; + + if(result) + returnValues.push(result); + }); + + return returnValues; +}; + + +/** +Get the adds a callback to the responseCallbacks object, +which will be called if a response matching the response Id will arrive. + +@method _addResponseCallback +*/ +IpcProvider.prototype._addResponseCallback = function(payload, callback) { + var id = payload.id || payload[0].id; + var method = payload.method || payload[0].method; + + this.responseCallbacks[id] = callback; + this.responseCallbacks[id].method = method; +}; + +/** +Timeout all requests when the end/error event is fired + +@method _timeout +*/ +IpcProvider.prototype._timeout = function() { + for(var key in this.responseCallbacks) { + if(this.responseCallbacks.hasOwnProperty(key)){ + this.responseCallbacks[key](errors.InvalidConnection('on IPC')); + delete this.responseCallbacks[key]; + } + } +}; + + +/** +Check if the current connection is still valid. + +@method isConnected +*/ +IpcProvider.prototype.isConnected = function() { + var _this = this; + + // try reconnect, when connection is gone + if(!_this.connection.writable) + _this.connection.connect({path: _this.path}); + + return !!this.connection.writable; +}; + +IpcProvider.prototype.send = function (payload) { + + if(this.connection.writeSync) { + var result; + + // try reconnect, when connection is gone + if(!this.connection.writable) + this.connection.connect({path: this.path}); + + var data = this.connection.writeSync(JSON.stringify(payload)); + + try { + result = JSON.parse(data); + } catch(e) { + throw errors.InvalidResponse(data); + } + + return result; + + } else { + throw new Error('You tried to send "'+ payload.method +'" synchronously. Synchronous requests are not supported by the IPC provider.'); + } +}; + +IpcProvider.prototype.sendAsync = function (payload, callback) { + // try reconnect, when connection is gone + if(!this.connection.writable) + this.connection.connect({path: this.path}); + + + this.connection.write(JSON.stringify(payload)); + this._addResponseCallback(payload, callback); +}; + +module.exports = IpcProvider; + + +},{"../utils/utils":20,"./errors":26}],35:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file jsonrpc.js + * @authors: + * Marek Kotewicz + * Aaron Kumavis + * @date 2015 + */ + +// Initialize Jsonrpc as a simple object with utility functions. +var Jsonrpc = { + messageId: 0 +}; + +/** + * Should be called to valid json create payload object + * + * @method toPayload + * @param {Function} method of jsonrpc call, required + * @param {Array} params, an array of method params, optional + * @returns {Object} valid jsonrpc payload object + */ +Jsonrpc.toPayload = function (method, params) { + if (!method) + console.error('jsonrpc method should be specified!'); + + // advance message ID + Jsonrpc.messageId++; + + return { + jsonrpc: '2.0', + id: Jsonrpc.messageId, + method: method, + params: params || [] + }; +}; + +/** + * Should be called to check if jsonrpc response is valid + * + * @method isValidResponse + * @param {Object} + * @returns {Boolean} true if response is valid, otherwise false + */ +Jsonrpc.isValidResponse = function (response) { + return Array.isArray(response) ? response.every(validateSingleMessage) : validateSingleMessage(response); + + function validateSingleMessage(message){ + return !!message && + !message.error && + message.jsonrpc === '2.0' && + typeof message.id === 'number' && + message.result !== undefined; // only undefined is not valid json object + } +}; + +/** + * Should be called to create batch payload object + * + * @method toBatchPayload + * @param {Array} messages, an array of objects with method (required) and params (optional) fields + * @returns {Array} batch payload + */ +Jsonrpc.toBatchPayload = function (messages) { + return messages.map(function (message) { + return Jsonrpc.toPayload(message.method, message.params); + }); +}; + +module.exports = Jsonrpc; + + +},{}],36:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file method.js + * @author Marek Kotewicz + * @date 2015 + */ + +var utils = require('../utils/utils'); +var errors = require('./errors'); + +var Method = function (options) { + this.name = options.name; + this.call = options.call; + this.params = options.params || 0; + this.inputFormatter = options.inputFormatter; + this.outputFormatter = options.outputFormatter; + this.requestManager = null; +}; + +Method.prototype.setRequestManager = function (rm) { + this.requestManager = rm; +}; + +/** + * Should be used to determine name of the jsonrpc method based on arguments + * + * @method getCall + * @param {Array} arguments + * @return {String} name of jsonrpc method + */ +Method.prototype.getCall = function (args) { + return utils.isFunction(this.call) ? this.call(args) : this.call; +}; + +/** + * Should be used to extract callback from array of arguments. Modifies input param + * + * @method extractCallback + * @param {Array} arguments + * @return {Function|Null} callback, if exists + */ +Method.prototype.extractCallback = function (args) { + if (utils.isFunction(args[args.length - 1])) { + return args.pop(); // modify the args array! + } +}; + +/** + * Should be called to check if the number of arguments is correct + * + * @method validateArgs + * @param {Array} arguments + * @throws {Error} if it is not + */ +Method.prototype.validateArgs = function (args) { + if (args.length !== this.params) { + throw errors.InvalidNumberOfParams(); + } +}; + +/** + * Should be called to format input args of method + * + * @method formatInput + * @param {Array} + * @return {Array} + */ +Method.prototype.formatInput = function (args) { + if (!this.inputFormatter) { + return args; + } + + return this.inputFormatter.map(function (formatter, index) { + return formatter ? formatter(args[index]) : args[index]; + }); +}; + +/** + * Should be called to format output(result) of method + * + * @method formatOutput + * @param {Object} + * @return {Object} + */ +Method.prototype.formatOutput = function (result) { + return this.outputFormatter && result ? this.outputFormatter(result) : result; +}; + +/** + * Should create payload from given input args + * + * @method toPayload + * @param {Array} args + * @return {Object} + */ +Method.prototype.toPayload = function (args) { + var call = this.getCall(args); + var callback = this.extractCallback(args); + var params = this.formatInput(args); + this.validateArgs(params); + + return { + method: call, + params: params, + callback: callback + }; +}; + +Method.prototype.attachToObject = function (obj) { + var func = this.buildCall(); + func.call = this.call; // TODO!!! that's ugly. filter.js uses it + var name = this.name.split('.'); + if (name.length > 1) { + obj[name[0]] = obj[name[0]] || {}; + obj[name[0]][name[1]] = func; + } else { + obj[name[0]] = func; + } +}; + +Method.prototype.buildCall = function() { + var method = this; + var send = function () { + var payload = method.toPayload(Array.prototype.slice.call(arguments)); + if (payload.callback) { + return method.requestManager.sendAsync(payload, function (err, result) { + payload.callback(err, method.formatOutput(result)); + }); + } + return method.formatOutput(method.requestManager.send(payload)); + }; + send.request = this.request.bind(this); + return send; +}; + +/** + * Should be called to create pure JSONRPC request which can be used in batch request + * + * @method request + * @param {...} params + * @return {Object} jsonrpc request + */ +Method.prototype.request = function () { + var payload = this.toPayload(Array.prototype.slice.call(arguments)); + payload.format = this.formatOutput.bind(this); + return payload; +}; + +module.exports = Method; + + +},{"../utils/utils":20,"./errors":26}],37:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file db.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var Method = require('../method'); + +var DB = function (web3) { + this._requestManager = web3._requestManager; + + var self = this; + + methods().forEach(function(method) { + method.attachToObject(self); + method.setRequestManager(web3._requestManager); + }); +}; + +var methods = function () { + var putString = new Method({ + name: 'putString', + call: 'db_putString', + params: 3 + }); + + var getString = new Method({ + name: 'getString', + call: 'db_getString', + params: 2 + }); + + var putHex = new Method({ + name: 'putHex', + call: 'db_putHex', + params: 3 + }); + + var getHex = new Method({ + name: 'getHex', + call: 'db_getHex', + params: 2 + }); + + return [ + putString, getString, putHex, getHex + ]; +}; + +module.exports = DB; + +},{"../method":36}],38:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file eth.js + * @author Marek Kotewicz + * @author Fabian Vogelsteller + * @date 2015 + */ + +"use strict"; + +var formatters = require('../formatters'); +var utils = require('../../utils/utils'); +var Method = require('../method'); +var Property = require('../property'); +var c = require('../../utils/config'); +var Contract = require('../contract'); +var watches = require('./watches'); +var Filter = require('../filter'); +var IsSyncing = require('../syncing'); +var namereg = require('../namereg'); +var Iban = require('../iban'); +var transfer = require('../transfer'); + +var blockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber"; +}; + +var transactionFromBlockCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex'; +}; + +var uncleCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex'; +}; + +var getBlockTransactionCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber'; +}; + +var uncleCountCall = function (args) { + return (utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber'; +}; + +function Eth(web3) { + this._requestManager = web3._requestManager; + + var self = this; + + methods().forEach(function(method) { + method.attachToObject(self); + method.setRequestManager(self._requestManager); + }); + + properties().forEach(function(p) { + p.attachToObject(self); + p.setRequestManager(self._requestManager); + }); + + + this.iban = Iban; + this.sendIBANTransaction = transfer.bind(null, this); +} + +Object.defineProperty(Eth.prototype, 'defaultBlock', { + get: function () { + return c.defaultBlock; + }, + set: function (val) { + c.defaultBlock = val; + return val; + } +}); + +Object.defineProperty(Eth.prototype, 'defaultAccount', { + get: function () { + return c.defaultAccount; + }, + set: function (val) { + c.defaultAccount = val; + return val; + } +}); + +var methods = function () { + var getBalance = new Method({ + name: 'getBalance', + call: 'eth_getBalance', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: formatters.outputBigNumberFormatter + }); + + var getStorageAt = new Method({ + name: 'getStorageAt', + call: 'eth_getStorageAt', + params: 3, + inputFormatter: [null, utils.toHex, formatters.inputDefaultBlockNumberFormatter] + }); + + var getCode = new Method({ + name: 'getCode', + call: 'eth_getCode', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter] + }); + + var getBlock = new Method({ + name: 'getBlock', + call: blockCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, function (val) { return !!val; }], + outputFormatter: formatters.outputBlockFormatter + }); + + var getUncle = new Method({ + name: 'getUncle', + call: uncleCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], + outputFormatter: formatters.outputBlockFormatter, + + }); + + var getCompilers = new Method({ + name: 'getCompilers', + call: 'eth_getCompilers', + params: 0 + }); + + var getBlockTransactionCount = new Method({ + name: 'getBlockTransactionCount', + call: getBlockTransactionCountCall, + params: 1, + inputFormatter: [formatters.inputBlockNumberFormatter], + outputFormatter: utils.toDecimal + }); + + var getBlockUncleCount = new Method({ + name: 'getBlockUncleCount', + call: uncleCountCall, + params: 1, + inputFormatter: [formatters.inputBlockNumberFormatter], + outputFormatter: utils.toDecimal + }); + + var getTransaction = new Method({ + name: 'getTransaction', + call: 'eth_getTransactionByHash', + params: 1, + outputFormatter: formatters.outputTransactionFormatter + }); + + var getTransactionFromBlock = new Method({ + name: 'getTransactionFromBlock', + call: transactionFromBlockCall, + params: 2, + inputFormatter: [formatters.inputBlockNumberFormatter, utils.toHex], + outputFormatter: formatters.outputTransactionFormatter + }); + + var getTransactionReceipt = new Method({ + name: 'getTransactionReceipt', + call: 'eth_getTransactionReceipt', + params: 1, + outputFormatter: formatters.outputTransactionReceiptFormatter + }); + + var getTransactionCount = new Method({ + name: 'getTransactionCount', + call: 'eth_getTransactionCount', + params: 2, + inputFormatter: [null, formatters.inputDefaultBlockNumberFormatter], + outputFormatter: utils.toDecimal + }); + + var sendRawTransaction = new Method({ + name: 'sendRawTransaction', + call: 'eth_sendRawTransaction', + params: 1, + inputFormatter: [null] + }); + + var sendTransaction = new Method({ + name: 'sendTransaction', + call: 'eth_sendTransaction', + params: 1, + inputFormatter: [formatters.inputTransactionFormatter] + }); + + var sign = new Method({ + name: 'sign', + call: 'eth_sign', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, null] + }); + + var call = new Method({ + name: 'call', + call: 'eth_call', + params: 2, + inputFormatter: [formatters.inputCallFormatter, formatters.inputDefaultBlockNumberFormatter] + }); + + var estimateGas = new Method({ + name: 'estimateGas', + call: 'eth_estimateGas', + params: 1, + inputFormatter: [formatters.inputCallFormatter], + outputFormatter: utils.toDecimal + }); + + var compileSolidity = new Method({ + name: 'compile.solidity', + call: 'eth_compileSolidity', + params: 1 + }); + + var compileLLL = new Method({ + name: 'compile.lll', + call: 'eth_compileLLL', + params: 1 + }); + + var compileSerpent = new Method({ + name: 'compile.serpent', + call: 'eth_compileSerpent', + params: 1 + }); + + var submitWork = new Method({ + name: 'submitWork', + call: 'eth_submitWork', + params: 3 + }); + + var getWork = new Method({ + name: 'getWork', + call: 'eth_getWork', + params: 0 + }); + + return [ + getBalance, + getStorageAt, + getCode, + getBlock, + getUncle, + getCompilers, + getBlockTransactionCount, + getBlockUncleCount, + getTransaction, + getTransactionFromBlock, + getTransactionReceipt, + getTransactionCount, + call, + estimateGas, + sendRawTransaction, + sendTransaction, + sign, + compileSolidity, + compileLLL, + compileSerpent, + submitWork, + getWork + ]; +}; + + +var properties = function () { + return [ + new Property({ + name: 'coinbase', + getter: 'eth_coinbase' + }), + new Property({ + name: 'mining', + getter: 'eth_mining' + }), + new Property({ + name: 'hashrate', + getter: 'eth_hashrate', + outputFormatter: utils.toDecimal + }), + new Property({ + name: 'syncing', + getter: 'eth_syncing', + outputFormatter: formatters.outputSyncingFormatter + }), + new Property({ + name: 'gasPrice', + getter: 'eth_gasPrice', + outputFormatter: formatters.outputBigNumberFormatter + }), + new Property({ + name: 'accounts', + getter: 'eth_accounts' + }), + new Property({ + name: 'blockNumber', + getter: 'eth_blockNumber', + outputFormatter: utils.toDecimal + }), + new Property({ + name: 'protocolVersion', + getter: 'eth_protocolVersion' + }) + ]; +}; + +Eth.prototype.contract = function (abi) { + var factory = new Contract(this, abi); + return factory; +}; + +Eth.prototype.filter = function (fil, callback) { + return new Filter(this._requestManager, fil, watches.eth(), formatters.outputLogFormatter, callback); +}; + +Eth.prototype.namereg = function () { + return this.contract(namereg.global.abi).at(namereg.global.address); +}; + +Eth.prototype.icapNamereg = function () { + return this.contract(namereg.icap.abi).at(namereg.icap.address); +}; + +Eth.prototype.isSyncing = function (callback) { + return new IsSyncing(this._requestManager, callback); +}; + +module.exports = Eth; + + +},{"../../utils/config":18,"../../utils/utils":20,"../contract":25,"../filter":29,"../formatters":30,"../iban":33,"../method":36,"../namereg":44,"../property":45,"../syncing":48,"../transfer":49,"./watches":43}],39:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file eth.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var utils = require('../../utils/utils'); +var Property = require('../property'); + +var Net = function (web3) { + this._requestManager = web3._requestManager; + + var self = this; + + properties().forEach(function(p) { + p.attachToObject(self); + p.setRequestManager(web3._requestManager); + }); +}; + +/// @returns an array of objects describing web3.eth api properties +var properties = function () { + return [ + new Property({ + name: 'listening', + getter: 'net_listening' + }), + new Property({ + name: 'peerCount', + getter: 'net_peerCount', + outputFormatter: utils.toDecimal + }) + ]; +}; + +module.exports = Net; + +},{"../../utils/utils":20,"../property":45}],40:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file eth.js + * @author Marek Kotewicz + * @author Fabian Vogelsteller + * @date 2015 + */ + +"use strict"; + +var Method = require('../method'); +var Property = require('../property'); +var formatters = require('../formatters'); + +function Personal(web3) { + this._requestManager = web3._requestManager; + + var self = this; + + methods().forEach(function(method) { + method.attachToObject(self); + method.setRequestManager(self._requestManager); + }); + + properties().forEach(function(p) { + p.attachToObject(self); + p.setRequestManager(self._requestManager); + }); +} + +var methods = function () { + var newAccount = new Method({ + name: 'newAccount', + call: 'personal_newAccount', + params: 1, + inputFormatter: [null] + }); + + var unlockAccount = new Method({ + name: 'unlockAccount', + call: 'personal_unlockAccount', + params: 3, + inputFormatter: [formatters.inputAddressFormatter, null, null] + }); + + var sendTransaction = new Method({ + name: 'sendTransaction', + call: 'personal_sendTransaction', + params: 2, + inputFormatter: [formatters.inputTransactionFormatter, null] + }); + + var lockAccount = new Method({ + name: 'lockAccount', + call: 'personal_lockAccount', + params: 1, + inputFormatter: [formatters.inputAddressFormatter] + }); + + return [ + newAccount, + unlockAccount, + sendTransaction, + lockAccount + ]; +}; + +var properties = function () { + return [ + new Property({ + name: 'listAccounts', + getter: 'personal_listAccounts' + }) + ]; +}; + + +module.exports = Personal; + +},{"../formatters":30,"../method":36,"../property":45}],41:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file shh.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var Method = require('../method'); +var formatters = require('../formatters'); +var Filter = require('../filter'); +var watches = require('./watches'); + +var Shh = function (web3) { + this._requestManager = web3._requestManager; + + var self = this; + + methods().forEach(function(method) { + method.attachToObject(self); + method.setRequestManager(self._requestManager); + }); +}; + +Shh.prototype.filter = function (fil, callback) { + return new Filter(this._requestManager, fil, watches.shh(), formatters.outputPostFormatter, callback); +}; + +var methods = function () { + + var post = new Method({ + name: 'post', + call: 'shh_post', + params: 1, + inputFormatter: [formatters.inputPostFormatter] + }); + + var newIdentity = new Method({ + name: 'newIdentity', + call: 'shh_newIdentity', + params: 0 + }); + + var hasIdentity = new Method({ + name: 'hasIdentity', + call: 'shh_hasIdentity', + params: 1 + }); + + var newGroup = new Method({ + name: 'newGroup', + call: 'shh_newGroup', + params: 0 + }); + + var addToGroup = new Method({ + name: 'addToGroup', + call: 'shh_addToGroup', + params: 0 + }); + + return [ + post, + newIdentity, + hasIdentity, + newGroup, + addToGroup + ]; +}; + +module.exports = Shh; + + +},{"../filter":29,"../formatters":30,"../method":36,"./watches":43}],42:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file bzz.js + * @author Alex Beregszaszi + * @date 2016 + * + * Reference: https://github.com/ethereum/go-ethereum/blob/swarm/internal/web3ext/web3ext.go#L33 + */ + +"use strict"; + +var Method = require('../method'); +var Property = require('../property'); + +function Swarm(web3) { + this._requestManager = web3._requestManager; + + var self = this; + + methods().forEach(function(method) { + method.attachToObject(self); + method.setRequestManager(self._requestManager); + }); + + properties().forEach(function(p) { + p.attachToObject(self); + p.setRequestManager(self._requestManager); + }); +} + +var methods = function () { + var blockNetworkRead = new Method({ + name: 'blockNetworkRead', + call: 'bzz_blockNetworkRead', + params: 1, + inputFormatter: [null] + }); + + var syncEnabled = new Method({ + name: 'syncEnabled', + call: 'bzz_syncEnabled', + params: 1, + inputFormatter: [null] + }); + + var swapEnabled = new Method({ + name: 'swapEnabled', + call: 'bzz_swapEnabled', + params: 1, + inputFormatter: [null] + }); + + var download = new Method({ + name: 'download', + call: 'bzz_download', + params: 2, + inputFormatter: [null, null] + }); + + var upload = new Method({ + name: 'upload', + call: 'bzz_upload', + params: 2, + inputFormatter: [null, null] + }); + + var retrieve = new Method({ + name: 'retrieve', + call: 'bzz_retrieve', + params: 1, + inputFormatter: [null] + }); + + var store = new Method({ + name: 'store', + call: 'bzz_store', + params: 2, + inputFormatter: [null, null] + }); + + var get = new Method({ + name: 'get', + call: 'bzz_get', + params: 1, + inputFormatter: [null] + }); + + var put = new Method({ + name: 'put', + call: 'bzz_put', + params: 2, + inputFormatter: [null, null] + }); + + var modify = new Method({ + name: 'modify', + call: 'bzz_modify', + params: 4, + inputFormatter: [null, null, null, null] + }); + + return [ + blockNetworkRead, + syncEnabled, + swapEnabled, + download, + upload, + retrieve, + store, + get, + put, + modify + ]; +}; + +var properties = function () { + return [ + new Property({ + name: 'hive', + getter: 'bzz_hive' + }), + new Property({ + name: 'info', + getter: 'bzz_info' + }) + ]; +}; + + +module.exports = Swarm; + +},{"../method":36,"../property":45}],43:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file watches.js + * @authors: + * Marek Kotewicz + * @date 2015 + */ + +var Method = require('../method'); + +/// @returns an array of objects describing web3.eth.filter api methods +var eth = function () { + var newFilterCall = function (args) { + var type = args[0]; + + switch(type) { + case 'latest': + args.shift(); + this.params = 0; + return 'eth_newBlockFilter'; + case 'pending': + args.shift(); + this.params = 0; + return 'eth_newPendingTransactionFilter'; + default: + return 'eth_newFilter'; + } + }; + + var newFilter = new Method({ + name: 'newFilter', + call: newFilterCall, + params: 1 + }); + + var uninstallFilter = new Method({ + name: 'uninstallFilter', + call: 'eth_uninstallFilter', + params: 1 + }); + + var getLogs = new Method({ + name: 'getLogs', + call: 'eth_getFilterLogs', + params: 1 + }); + + var poll = new Method({ + name: 'poll', + call: 'eth_getFilterChanges', + params: 1 + }); + + return [ + newFilter, + uninstallFilter, + getLogs, + poll + ]; +}; + +/// @returns an array of objects describing web3.shh.watch api methods +var shh = function () { + var newFilter = new Method({ + name: 'newFilter', + call: 'shh_newFilter', + params: 1 + }); + + var uninstallFilter = new Method({ + name: 'uninstallFilter', + call: 'shh_uninstallFilter', + params: 1 + }); + + var getLogs = new Method({ + name: 'getLogs', + call: 'shh_getMessages', + params: 1 + }); + + var poll = new Method({ + name: 'poll', + call: 'shh_getFilterChanges', + params: 1 + }); + + return [ + newFilter, + uninstallFilter, + getLogs, + poll + ]; +}; + +module.exports = { + eth: eth, + shh: shh +}; + + +},{"../method":36}],44:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file namereg.js + * @author Marek Kotewicz + * @date 2015 + */ + +var globalRegistrarAbi = require('../contracts/GlobalRegistrar.json'); +var icapRegistrarAbi= require('../contracts/ICAPRegistrar.json'); + +var globalNameregAddress = '0xc6d9d2cd449a754c494264e1809c50e34d64562b'; +var icapNameregAddress = '0xa1a111bc074c9cfa781f0c38e63bd51c91b8af00'; + +module.exports = { + global: { + abi: globalRegistrarAbi, + address: globalNameregAddress + }, + icap: { + abi: icapRegistrarAbi, + address: icapNameregAddress + } +}; + + +},{"../contracts/GlobalRegistrar.json":1,"../contracts/ICAPRegistrar.json":2}],45:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file property.js + * @author Fabian Vogelsteller + * @author Marek Kotewicz + * @date 2015 + */ + +var utils = require('../utils/utils'); + +var Property = function (options) { + this.name = options.name; + this.getter = options.getter; + this.setter = options.setter; + this.outputFormatter = options.outputFormatter; + this.inputFormatter = options.inputFormatter; + this.requestManager = null; +}; + +Property.prototype.setRequestManager = function (rm) { + this.requestManager = rm; +}; + +/** + * Should be called to format input args of method + * + * @method formatInput + * @param {Array} + * @return {Array} + */ +Property.prototype.formatInput = function (arg) { + return this.inputFormatter ? this.inputFormatter(arg) : arg; +}; + +/** + * Should be called to format output(result) of method + * + * @method formatOutput + * @param {Object} + * @return {Object} + */ +Property.prototype.formatOutput = function (result) { + return this.outputFormatter && result !== null && result !== undefined ? this.outputFormatter(result) : result; +}; + +/** + * Should be used to extract callback from array of arguments. Modifies input param + * + * @method extractCallback + * @param {Array} arguments + * @return {Function|Null} callback, if exists + */ +Property.prototype.extractCallback = function (args) { + if (utils.isFunction(args[args.length - 1])) { + return args.pop(); // modify the args array! + } +}; + + +/** + * Should attach function to method + * + * @method attachToObject + * @param {Object} + * @param {Function} + */ +Property.prototype.attachToObject = function (obj) { + var proto = { + get: this.buildGet(), + enumerable: true + }; + + var names = this.name.split('.'); + var name = names[0]; + if (names.length > 1) { + obj[names[0]] = obj[names[0]] || {}; + obj = obj[names[0]]; + name = names[1]; + } + + Object.defineProperty(obj, name, proto); + obj[asyncGetterName(name)] = this.buildAsyncGet(); +}; + +var asyncGetterName = function (name) { + return 'get' + name.charAt(0).toUpperCase() + name.slice(1); +}; + +Property.prototype.buildGet = function () { + var property = this; + return function get() { + return property.formatOutput(property.requestManager.send({ + method: property.getter + })); + }; +}; + +Property.prototype.buildAsyncGet = function () { + var property = this; + var get = function (callback) { + property.requestManager.sendAsync({ + method: property.getter + }, function (err, result) { + callback(err, property.formatOutput(result)); + }); + }; + get.request = this.request.bind(this); + return get; +}; + +/** + * Should be called to create pure JSONRPC request which can be used in batch request + * + * @method request + * @param {...} params + * @return {Object} jsonrpc request + */ +Property.prototype.request = function () { + var payload = { + method: this.getter, + params: [], + callback: this.extractCallback(Array.prototype.slice.call(arguments)) + }; + payload.format = this.formatOutput.bind(this); + return payload; +}; + +module.exports = Property; + + +},{"../utils/utils":20}],46:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file requestmanager.js + * @author Jeffrey Wilcke + * @author Marek Kotewicz + * @author Marian Oancea + * @author Fabian Vogelsteller + * @author Gav Wood + * @date 2014 + */ + +var Jsonrpc = require('./jsonrpc'); +var utils = require('../utils/utils'); +var c = require('../utils/config'); +var errors = require('./errors'); + +/** + * It's responsible for passing messages to providers + * It's also responsible for polling the ethereum node for incoming messages + * Default poll timeout is 1 second + * Singleton + */ +var RequestManager = function (provider) { + this.provider = provider; + this.polls = {}; + this.timeout = null; +}; + +/** + * Should be used to synchronously send request + * + * @method send + * @param {Object} data + * @return {Object} + */ +RequestManager.prototype.send = function (data) { + if (!this.provider) { + console.error(errors.InvalidProvider()); + return null; + } + + var payload = Jsonrpc.toPayload(data.method, data.params); + var result = this.provider.send(payload); + + if (!Jsonrpc.isValidResponse(result)) { + throw errors.InvalidResponse(result); + } + + return result.result; +}; + +/** + * Should be used to asynchronously send request + * + * @method sendAsync + * @param {Object} data + * @param {Function} callback + */ +RequestManager.prototype.sendAsync = function (data, callback) { + if (!this.provider) { + return callback(errors.InvalidProvider()); + } + + var payload = Jsonrpc.toPayload(data.method, data.params); + this.provider.sendAsync(payload, function (err, result) { + if (err) { + return callback(err); + } + + if (!Jsonrpc.isValidResponse(result)) { + return callback(errors.InvalidResponse(result)); + } + + callback(null, result.result); + }); +}; + +/** + * Should be called to asynchronously send batch request + * + * @method sendBatch + * @param {Array} batch data + * @param {Function} callback + */ +RequestManager.prototype.sendBatch = function (data, callback) { + if (!this.provider) { + return callback(errors.InvalidProvider()); + } + + var payload = Jsonrpc.toBatchPayload(data); + + this.provider.sendAsync(payload, function (err, results) { + if (err) { + return callback(err); + } + + if (!utils.isArray(results)) { + return callback(errors.InvalidResponse(results)); + } + + callback(err, results); + }); +}; + +/** + * Should be used to set provider of request manager + * + * @method setProvider + * @param {Object} + */ +RequestManager.prototype.setProvider = function (p) { + this.provider = p; +}; + +/** + * Should be used to start polling + * + * @method startPolling + * @param {Object} data + * @param {Number} pollId + * @param {Function} callback + * @param {Function} uninstall + * + * @todo cleanup number of params + */ +RequestManager.prototype.startPolling = function (data, pollId, callback, uninstall) { + this.polls[pollId] = {data: data, id: pollId, callback: callback, uninstall: uninstall}; + + + // start polling + if (!this.timeout) { + this.poll(); + } +}; + +/** + * Should be used to stop polling for filter with given id + * + * @method stopPolling + * @param {Number} pollId + */ +RequestManager.prototype.stopPolling = function (pollId) { + delete this.polls[pollId]; + + // stop polling + if(Object.keys(this.polls).length === 0 && this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } +}; + +/** + * Should be called to reset the polling mechanism of the request manager + * + * @method reset + */ +RequestManager.prototype.reset = function (keepIsSyncing) { + /*jshint maxcomplexity:5 */ + + for (var key in this.polls) { + // remove all polls, except sync polls, + // they need to be removed manually by calling syncing.stopWatching() + if(!keepIsSyncing || key.indexOf('syncPoll_') === -1) { + this.polls[key].uninstall(); + delete this.polls[key]; + } + } + + // stop polling + if(Object.keys(this.polls).length === 0 && this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } +}; + +/** + * Should be called to poll for changes on filter with given id + * + * @method poll + */ +RequestManager.prototype.poll = function () { + /*jshint maxcomplexity: 6 */ + this.timeout = setTimeout(this.poll.bind(this), c.ETH_POLLING_TIMEOUT); + + if (Object.keys(this.polls).length === 0) { + return; + } + + if (!this.provider) { + console.error(errors.InvalidProvider()); + return; + } + + var pollsData = []; + var pollsIds = []; + for (var key in this.polls) { + pollsData.push(this.polls[key].data); + pollsIds.push(key); + } + + if (pollsData.length === 0) { + return; + } + + var payload = Jsonrpc.toBatchPayload(pollsData); + + // map the request id to they poll id + var pollsIdMap = {}; + payload.forEach(function(load, index){ + pollsIdMap[load.id] = pollsIds[index]; + }); + + + var self = this; + this.provider.sendAsync(payload, function (error, results) { + + + // TODO: console log? + if (error) { + return; + } + + if (!utils.isArray(results)) { + throw errors.InvalidResponse(results); + } + results.map(function (result) { + var id = pollsIdMap[result.id]; + + // make sure the filter is still installed after arrival of the request + if (self.polls[id]) { + result.callback = self.polls[id].callback; + return result; + } else + return false; + }).filter(function (result) { + return !!result; + }).filter(function (result) { + var valid = Jsonrpc.isValidResponse(result); + if (!valid) { + result.callback(errors.InvalidResponse(result)); + } + return valid; + }).forEach(function (result) { + result.callback(null, result.result); + }); + }); +}; + +module.exports = RequestManager; + + +},{"../utils/config":18,"../utils/utils":20,"./errors":26,"./jsonrpc":35}],47:[function(require,module,exports){ + + +var Settings = function () { + this.defaultBlock = 'latest'; + this.defaultAccount = undefined; +}; + +module.exports = Settings; + + +},{}],48:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** @file syncing.js + * @authors: + * Fabian Vogelsteller + * @date 2015 + */ + +var formatters = require('./formatters'); +var utils = require('../utils/utils'); + +var count = 1; + +/** +Adds the callback and sets up the methods, to iterate over the results. + +@method pollSyncing +@param {Object} self +*/ +var pollSyncing = function(self) { + + var onMessage = function (error, sync) { + if (error) { + return self.callbacks.forEach(function (callback) { + callback(error); + }); + } + + if(utils.isObject(sync) && sync.startingBlock) + sync = formatters.outputSyncingFormatter(sync); + + self.callbacks.forEach(function (callback) { + if (self.lastSyncState !== sync) { + + // call the callback with true first so the app can stop anything, before receiving the sync data + if(!self.lastSyncState && utils.isObject(sync)) + callback(null, true); + + // call on the next CPU cycle, so the actions of the sync stop can be processes first + setTimeout(function() { + callback(null, sync); + }, 0); + + self.lastSyncState = sync; + } + }); + }; + + self.requestManager.startPolling({ + method: 'eth_syncing', + params: [], + }, self.pollId, onMessage, self.stopWatching.bind(self)); + +}; + +var IsSyncing = function (requestManager, callback) { + this.requestManager = requestManager; + this.pollId = 'syncPoll_'+ count++; + this.callbacks = []; + this.addCallback(callback); + this.lastSyncState = false; + pollSyncing(this); + + return this; +}; + +IsSyncing.prototype.addCallback = function (callback) { + if(callback) + this.callbacks.push(callback); + return this; +}; + +IsSyncing.prototype.stopWatching = function () { + this.requestManager.stopPolling(this.pollId); + this.callbacks = []; +}; + +module.exports = IsSyncing; + + +},{"../utils/utils":20,"./formatters":30}],49:[function(require,module,exports){ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file transfer.js + * @author Marek Kotewicz + * @date 2015 + */ + +var Iban = require('./iban'); +var exchangeAbi = require('../contracts/SmartExchange.json'); + +/** + * Should be used to make Iban transfer + * + * @method transfer + * @param {String} from + * @param {String} to iban + * @param {Value} value to be tranfered + * @param {Function} callback, callback + */ +var transfer = function (eth, from, to, value, callback) { + var iban = new Iban(to); + if (!iban.isValid()) { + throw new Error('invalid iban address'); + } + + if (iban.isDirect()) { + return transferToAddress(eth, from, iban.address(), value, callback); + } + + if (!callback) { + var address = eth.icapNamereg().addr(iban.institution()); + return deposit(eth, from, address, value, iban.client()); + } + + eth.icapNamereg().addr(iban.institution(), function (err, address) { + return deposit(eth, from, address, value, iban.client(), callback); + }); + +}; + +/** + * Should be used to transfer funds to certain address + * + * @method transferToAddress + * @param {String} from + * @param {String} to + * @param {Value} value to be tranfered + * @param {Function} callback, callback + */ +var transferToAddress = function (eth, from, to, value, callback) { + return eth.sendTransaction({ + address: to, + from: from, + value: value + }, callback); +}; + +/** + * Should be used to deposit funds to generic Exchange contract (must implement deposit(bytes32) method!) + * + * @method deposit + * @param {String} from + * @param {String} to + * @param {Value} value to be transfered + * @param {String} client unique identifier + * @param {Function} callback, callback + */ +var deposit = function (eth, from, to, value, client, callback) { + var abi = exchangeAbi; + return eth.contract(abi).at(to).deposit(client, { + from: from, + value: value + }, callback); +}; + +module.exports = transfer; + + +},{"../contracts/SmartExchange.json":3,"./iban":33}],50:[function(require,module,exports){ + +},{}],51:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Lookup tables + var SBOX = []; + var INV_SBOX = []; + var SUB_MIX_0 = []; + var SUB_MIX_1 = []; + var SUB_MIX_2 = []; + var SUB_MIX_3 = []; + var INV_SUB_MIX_0 = []; + var INV_SUB_MIX_1 = []; + var INV_SUB_MIX_2 = []; + var INV_SUB_MIX_3 = []; + + // Compute lookup tables + (function () { + // Compute double table + var d = []; + for (var i = 0; i < 256; i++) { + if (i < 128) { + d[i] = i << 1; + } else { + d[i] = (i << 1) ^ 0x11b; + } + } + + // Walk GF(2^8) + var x = 0; + var xi = 0; + for (var i = 0; i < 256; i++) { + // Compute sbox + var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; + SBOX[x] = sx; + INV_SBOX[sx] = x; + + // Compute multiplication + var x2 = d[x]; + var x4 = d[x2]; + var x8 = d[x4]; + + // Compute sub bytes, mix columns tables + var t = (d[sx] * 0x101) ^ (sx * 0x1010100); + SUB_MIX_0[x] = (t << 24) | (t >>> 8); + SUB_MIX_1[x] = (t << 16) | (t >>> 16); + SUB_MIX_2[x] = (t << 8) | (t >>> 24); + SUB_MIX_3[x] = t; + + // Compute inv sub bytes, inv mix columns tables + var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); + INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); + INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); + INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); + INV_SUB_MIX_3[sx] = t; + + // Compute next counter + if (!x) { + x = xi = 1; + } else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + }()); + + // Precomputed Rcon lookup + var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; + + /** + * AES block cipher algorithm. + */ + var AES = C_algo.AES = BlockCipher.extend({ + _doReset: function () { + // Skip reset of nRounds has been set before and key did not change + if (this._nRounds && this._keyPriorReset === this._key) { + return; + } + + // Shortcuts + var key = this._keyPriorReset = this._key; + var keyWords = key.words; + var keySize = key.sigBytes / 4; + + // Compute number of rounds + var nRounds = this._nRounds = keySize + 6; + + // Compute number of key schedule rows + var ksRows = (nRounds + 1) * 4; + + // Compute key schedule + var keySchedule = this._keySchedule = []; + for (var ksRow = 0; ksRow < ksRows; ksRow++) { + if (ksRow < keySize) { + keySchedule[ksRow] = keyWords[ksRow]; + } else { + var t = keySchedule[ksRow - 1]; + + if (!(ksRow % keySize)) { + // Rot word + t = (t << 8) | (t >>> 24); + + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + + // Mix Rcon + t ^= RCON[(ksRow / keySize) | 0] << 24; + } else if (keySize > 6 && ksRow % keySize == 4) { + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + } + + keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; + } + } + + // Compute inv key schedule + var invKeySchedule = this._invKeySchedule = []; + for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) { + var ksRow = ksRows - invKsRow; + + if (invKsRow % 4) { + var t = keySchedule[ksRow]; + } else { + var t = keySchedule[ksRow - 4]; + } + + if (invKsRow < 4 || ksRow <= 4) { + invKeySchedule[invKsRow] = t; + } else { + invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ + INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; + } + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); + }, + + decryptBlock: function (M, offset) { + // Swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + + this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); + + // Inv swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + }, + + _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) { + // Shortcut + var nRounds = this._nRounds; + + // Get input, add round key + var s0 = M[offset] ^ keySchedule[0]; + var s1 = M[offset + 1] ^ keySchedule[1]; + var s2 = M[offset + 2] ^ keySchedule[2]; + var s3 = M[offset + 3] ^ keySchedule[3]; + + // Key schedule row counter + var ksRow = 4; + + // Rounds + for (var round = 1; round < nRounds; round++) { + // Shift rows, sub bytes, mix columns, add round key + var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++]; + var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++]; + var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++]; + var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++]; + + // Update state + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Shift rows, sub bytes, add round key + var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; + var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; + var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; + var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; + + // Set output + M[offset] = t0; + M[offset + 1] = t1; + M[offset + 2] = t2; + M[offset + 3] = t3; + }, + + keySize: 256/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg); + */ + C.AES = BlockCipher._createHelper(AES); + }()); + + + return CryptoJS.AES; + +})); +},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],52:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Cipher core components. + */ + CryptoJS.lib.Cipher || (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var Base64 = C_enc.Base64; + var C_algo = C.algo; + var EvpKDF = C_algo.EvpKDF; + + /** + * Abstract base cipher template. + * + * @property {number} keySize This cipher's key size. Default: 4 (128 bits) + * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits) + * @property {number} _ENC_XFORM_MODE A constant representing encryption mode. + * @property {number} _DEC_XFORM_MODE A constant representing decryption mode. + */ + var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + * + * @property {WordArray} iv The IV to use for this operation. + */ + cfg: Base.extend(), + + /** + * Creates this cipher in encryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray }); + */ + createEncryptor: function (key, cfg) { + return this.create(this._ENC_XFORM_MODE, key, cfg); + }, + + /** + * Creates this cipher in decryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray }); + */ + createDecryptor: function (key, cfg) { + return this.create(this._DEC_XFORM_MODE, key, cfg); + }, + + /** + * Initializes a newly created cipher. + * + * @param {number} xformMode Either the encryption or decryption transormation mode constant. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @example + * + * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); + */ + init: function (xformMode, key, cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Store transform mode and key + this._xformMode = xformMode; + this._key = key; + + // Set initial values + this.reset(); + }, + + /** + * Resets this cipher to its initial state. + * + * @example + * + * cipher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-cipher logic + this._doReset(); + }, + + /** + * Adds data to be encrypted or decrypted. + * + * @param {WordArray|string} dataUpdate The data to encrypt or decrypt. + * + * @return {WordArray} The data after processing. + * + * @example + * + * var encrypted = cipher.process('data'); + * var encrypted = cipher.process(wordArray); + */ + process: function (dataUpdate) { + // Append + this._append(dataUpdate); + + // Process available blocks + return this._process(); + }, + + /** + * Finalizes the encryption or decryption process. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt. + * + * @return {WordArray} The data after final processing. + * + * @example + * + * var encrypted = cipher.finalize(); + * var encrypted = cipher.finalize('data'); + * var encrypted = cipher.finalize(wordArray); + */ + finalize: function (dataUpdate) { + // Final data update + if (dataUpdate) { + this._append(dataUpdate); + } + + // Perform concrete-cipher logic + var finalProcessedData = this._doFinalize(); + + return finalProcessedData; + }, + + keySize: 128/32, + + ivSize: 128/32, + + _ENC_XFORM_MODE: 1, + + _DEC_XFORM_MODE: 2, + + /** + * Creates shortcut functions to a cipher's object interface. + * + * @param {Cipher} cipher The cipher to create a helper for. + * + * @return {Object} An object with encrypt and decrypt shortcut functions. + * + * @static + * + * @example + * + * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES); + */ + _createHelper: (function () { + function selectCipherStrategy(key) { + if (typeof key == 'string') { + return PasswordBasedCipher; + } else { + return SerializableCipher; + } + } + + return function (cipher) { + return { + encrypt: function (message, key, cfg) { + return selectCipherStrategy(key).encrypt(cipher, message, key, cfg); + }, + + decrypt: function (ciphertext, key, cfg) { + return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg); + } + }; + }; + }()) + }); + + /** + * Abstract base stream cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits) + */ + var StreamCipher = C_lib.StreamCipher = Cipher.extend({ + _doFinalize: function () { + // Process partial blocks + var finalProcessedBlocks = this._process(!!'flush'); + + return finalProcessedBlocks; + }, + + blockSize: 1 + }); + + /** + * Mode namespace. + */ + var C_mode = C.mode = {}; + + /** + * Abstract base block cipher mode template. + */ + var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({ + /** + * Creates this mode for encryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words); + */ + createEncryptor: function (cipher, iv) { + return this.Encryptor.create(cipher, iv); + }, + + /** + * Creates this mode for decryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words); + */ + createDecryptor: function (cipher, iv) { + return this.Decryptor.create(cipher, iv); + }, + + /** + * Initializes a newly created mode. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @example + * + * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words); + */ + init: function (cipher, iv) { + this._cipher = cipher; + this._iv = iv; + } + }); + + /** + * Cipher Block Chaining mode. + */ + var CBC = C_mode.CBC = (function () { + /** + * Abstract base CBC mode. + */ + var CBC = BlockCipherMode.extend(); + + /** + * CBC encryptor. + */ + CBC.Encryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // XOR and encrypt + xorBlock.call(this, words, offset, blockSize); + cipher.encryptBlock(words, offset); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + /** + * CBC decryptor. + */ + CBC.Decryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + // Decrypt and XOR + cipher.decryptBlock(words, offset); + xorBlock.call(this, words, offset, blockSize); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function xorBlock(words, offset, blockSize) { + // Shortcut + var iv = this._iv; + + // Choose mixing block + if (iv) { + var block = iv; + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var block = this._prevBlock; + } + + // XOR blocks + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= block[i]; + } + } + + return CBC; + }()); + + /** + * Padding namespace. + */ + var C_pad = C.pad = {}; + + /** + * PKCS #5/7 padding strategy. + */ + var Pkcs7 = C_pad.Pkcs7 = { + /** + * Pads data using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to pad. + * @param {number} blockSize The multiple that the data should be padded to. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.pad(wordArray, 4); + */ + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Create padding word + var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; + + // Create padding + var paddingWords = []; + for (var i = 0; i < nPaddingBytes; i += 4) { + paddingWords.push(paddingWord); + } + var padding = WordArray.create(paddingWords, nPaddingBytes); + + // Add padding + data.concat(padding); + }, + + /** + * Unpads data that had been padded using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to unpad. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.unpad(wordArray); + */ + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + /** + * Abstract base block cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits) + */ + var BlockCipher = C_lib.BlockCipher = Cipher.extend({ + /** + * Configuration options. + * + * @property {Mode} mode The block mode to use. Default: CBC + * @property {Padding} padding The padding strategy to use. Default: Pkcs7 + */ + cfg: Cipher.cfg.extend({ + mode: CBC, + padding: Pkcs7 + }), + + reset: function () { + // Reset cipher + Cipher.reset.call(this); + + // Shortcuts + var cfg = this.cfg; + var iv = cfg.iv; + var mode = cfg.mode; + + // Reset block mode + if (this._xformMode == this._ENC_XFORM_MODE) { + var modeCreator = mode.createEncryptor; + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + var modeCreator = mode.createDecryptor; + + // Keep at least one block in the buffer for unpadding + this._minBufferSize = 1; + } + this._mode = modeCreator.call(mode, this, iv && iv.words); + }, + + _doProcessBlock: function (words, offset) { + this._mode.processBlock(words, offset); + }, + + _doFinalize: function () { + // Shortcut + var padding = this.cfg.padding; + + // Finalize + if (this._xformMode == this._ENC_XFORM_MODE) { + // Pad data + padding.pad(this._data, this.blockSize); + + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + + // Unpad data + padding.unpad(finalProcessedBlocks); + } + + return finalProcessedBlocks; + }, + + blockSize: 128/32 + }); + + /** + * A collection of cipher parameters. + * + * @property {WordArray} ciphertext The raw ciphertext. + * @property {WordArray} key The key to this ciphertext. + * @property {WordArray} iv The IV used in the ciphering operation. + * @property {WordArray} salt The salt used with a key derivation function. + * @property {Cipher} algorithm The cipher algorithm. + * @property {Mode} mode The block mode used in the ciphering operation. + * @property {Padding} padding The padding scheme used in the ciphering operation. + * @property {number} blockSize The block size of the cipher. + * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string. + */ + var CipherParams = C_lib.CipherParams = Base.extend({ + /** + * Initializes a newly created cipher params object. + * + * @param {Object} cipherParams An object with any of the possible cipher parameters. + * + * @example + * + * var cipherParams = CryptoJS.lib.CipherParams.create({ + * ciphertext: ciphertextWordArray, + * key: keyWordArray, + * iv: ivWordArray, + * salt: saltWordArray, + * algorithm: CryptoJS.algo.AES, + * mode: CryptoJS.mode.CBC, + * padding: CryptoJS.pad.PKCS7, + * blockSize: 4, + * formatter: CryptoJS.format.OpenSSL + * }); + */ + init: function (cipherParams) { + this.mixIn(cipherParams); + }, + + /** + * Converts this cipher params object to a string. + * + * @param {Format} formatter (Optional) The formatting strategy to use. + * + * @return {string} The stringified cipher params. + * + * @throws Error If neither the formatter nor the default formatter is set. + * + * @example + * + * var string = cipherParams + ''; + * var string = cipherParams.toString(); + * var string = cipherParams.toString(CryptoJS.format.OpenSSL); + */ + toString: function (formatter) { + return (formatter || this.formatter).stringify(this); + } + }); + + /** + * Format namespace. + */ + var C_format = C.format = {}; + + /** + * OpenSSL formatting strategy. + */ + var OpenSSLFormatter = C_format.OpenSSL = { + /** + * Converts a cipher params object to an OpenSSL-compatible string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The OpenSSL-compatible string. + * + * @static + * + * @example + * + * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams); + */ + stringify: function (cipherParams) { + // Shortcuts + var ciphertext = cipherParams.ciphertext; + var salt = cipherParams.salt; + + // Format + if (salt) { + var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext); + } else { + var wordArray = ciphertext; + } + + return wordArray.toString(Base64); + }, + + /** + * Converts an OpenSSL-compatible string to a cipher params object. + * + * @param {string} openSSLStr The OpenSSL-compatible string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString); + */ + parse: function (openSSLStr) { + // Parse base64 + var ciphertext = Base64.parse(openSSLStr); + + // Shortcut + var ciphertextWords = ciphertext.words; + + // Test for salt + if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) { + // Extract salt + var salt = WordArray.create(ciphertextWords.slice(2, 4)); + + // Remove salt from ciphertext + ciphertextWords.splice(0, 4); + ciphertext.sigBytes -= 16; + } + + return CipherParams.create({ ciphertext: ciphertext, salt: salt }); + } + }; + + /** + * A cipher wrapper that returns ciphertext as a serializable cipher params object. + */ + var SerializableCipher = C_lib.SerializableCipher = Base.extend({ + /** + * Configuration options. + * + * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL + */ + cfg: Base.extend({ + format: OpenSSLFormatter + }), + + /** + * Encrypts a message. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Encrypt + var encryptor = cipher.createEncryptor(key, cfg); + var ciphertext = encryptor.finalize(message); + + // Shortcut + var cipherCfg = encryptor.cfg; + + // Create and return serializable cipher params + return CipherParams.create({ + ciphertext: ciphertext, + key: key, + iv: cipherCfg.iv, + algorithm: cipher, + mode: cipherCfg.mode, + padding: cipherCfg.padding, + blockSize: cipher.blockSize, + formatter: cfg.format + }); + }, + + /** + * Decrypts serialized ciphertext. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Decrypt + var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); + + return plaintext; + }, + + /** + * Converts serialized ciphertext to CipherParams, + * else assumed CipherParams already and returns ciphertext unchanged. + * + * @param {CipherParams|string} ciphertext The ciphertext. + * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext. + * + * @return {CipherParams} The unserialized ciphertext. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); + */ + _parse: function (ciphertext, format) { + if (typeof ciphertext == 'string') { + return format.parse(ciphertext, this); + } else { + return ciphertext; + } + } + }); + + /** + * Key derivation function namespace. + */ + var C_kdf = C.kdf = {}; + + /** + * OpenSSL key derivation function. + */ + var OpenSSLKdf = C_kdf.OpenSSL = { + /** + * Derives a key and IV from a password. + * + * @param {string} password The password to derive from. + * @param {number} keySize The size in words of the key to generate. + * @param {number} ivSize The size in words of the IV to generate. + * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. + * + * @return {CipherParams} A cipher params object with the key, IV, and salt. + * + * @static + * + * @example + * + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); + */ + execute: function (password, keySize, ivSize, salt) { + // Generate random salt + if (!salt) { + salt = WordArray.random(64/8); + } + + // Derive key and IV + var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); + + // Separate key and IV + var iv = WordArray.create(key.words.slice(keySize), ivSize * 4); + key.sigBytes = keySize * 4; + + // Return params + return CipherParams.create({ key: key, iv: iv, salt: salt }); + } + }; + + /** + * A serializable cipher wrapper that derives the key from a password, + * and returns ciphertext as a serializable cipher params object. + */ + var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({ + /** + * Configuration options. + * + * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL + */ + cfg: SerializableCipher.cfg.extend({ + kdf: OpenSSLKdf + }), + + /** + * Encrypts a message using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password'); + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Encrypt + var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); + + // Mix in derived params + ciphertext.mixIn(derivedParams); + + return ciphertext; + }, + + /** + * Decrypts serialized ciphertext using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Decrypt + var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg); + + return plaintext; + } + }); + }()); + + +})); +},{"./core":53}],53:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(); + } + else if (typeof define === "function" && define.amd) { + // AMD + define([], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(); + } +}(this, function () { + + /** + * CryptoJS core components. + */ + var CryptoJS = CryptoJS || (function (Math, undefined) { + /* + * Local polyfil of Object.create + */ + var create = Object.create || (function () { + function F() {}; + + return function (obj) { + var subtype; + + F.prototype = obj; + + subtype = new F(); + + F.prototype = null; + + return subtype; + }; + }()) + + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + var subtype = create(this); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init') || this.init === subtype.init) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + + var r = (function (m_w) { + var m_w = m_w; + var m_z = 0x3ade68b1; + var mask = 0xffffffff; + + return function () { + m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; + m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; + var result = ((m_z << 0x10) + m_w) & mask; + result /= 0x100000000; + result += 0.5; + return result * (Math.random() > .5 ? 1 : -1); + } + }); + + for (var i = 0, rcache; i < nBytes; i += 4) { + var _r = r((rcache || Math.random()) * 0x100000000); + + rcache = _r() * 0x3ade67b7; + words.push((_r() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; + }(Math)); + + + return CryptoJS; + +})); +},{}],54:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * Base64 encoding strategy. + */ + var Base64 = C_enc.Base64 = { + /** + * Converts a word array to a Base64 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Base64 string. + * + * @static + * + * @example + * + * var base64String = CryptoJS.enc.Base64.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + var map = this._map; + + // Clamp excess bits + wordArray.clamp(); + + // Convert + var base64Chars = []; + for (var i = 0; i < sigBytes; i += 3) { + var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; + var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; + + var triplet = (byte1 << 16) | (byte2 << 8) | byte3; + + for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { + base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); + } + } + + // Add padding + var paddingChar = map.charAt(64); + if (paddingChar) { + while (base64Chars.length % 4) { + base64Chars.push(paddingChar); + } + } + + return base64Chars.join(''); + }, + + /** + * Converts a Base64 string to a word array. + * + * @param {string} base64Str The Base64 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Base64.parse(base64String); + */ + parse: function (base64Str) { + // Shortcuts + var base64StrLength = base64Str.length; + var map = this._map; + var reverseMap = this._reverseMap; + + if (!reverseMap) { + reverseMap = this._reverseMap = []; + for (var j = 0; j < map.length; j++) { + reverseMap[map.charCodeAt(j)] = j; + } + } + + // Ignore padding + var paddingChar = map.charAt(64); + if (paddingChar) { + var paddingIndex = base64Str.indexOf(paddingChar); + if (paddingIndex !== -1) { + base64StrLength = paddingIndex; + } + } + + // Convert + return parseLoop(base64Str, base64StrLength, reverseMap); + + }, + + _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + }; + + function parseLoop(base64Str, base64StrLength, reverseMap) { + var words = []; + var nBytes = 0; + for (var i = 0; i < base64StrLength; i++) { + if (i % 4) { + var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); + var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); + words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); + nBytes++; + } + } + return WordArray.create(words, nBytes); + } + }()); + + + return CryptoJS.enc.Base64; + +})); +},{"./core":53}],55:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * UTF-16 BE encoding strategy. + */ + var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = { + /** + * Converts a word array to a UTF-16 BE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 BE string. + * + * @static + * + * @example + * + * var utf16String = CryptoJS.enc.Utf16.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff; + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 BE string to a word array. + * + * @param {string} utf16Str The UTF-16 BE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16.parse(utf16String); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + /** + * UTF-16 LE encoding strategy. + */ + C_enc.Utf16LE = { + /** + * Converts a word array to a UTF-16 LE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 LE string. + * + * @static + * + * @example + * + * var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff); + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 LE string to a word array. + * + * @param {string} utf16Str The UTF-16 LE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16)); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + function swapEndian(word) { + return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff); + } + }()); + + + return CryptoJS.enc.Utf16; + +})); +},{"./core":53}],56:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./sha1"), require("./hmac")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./sha1", "./hmac"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var MD5 = C_algo.MD5; + + /** + * This key derivation function is meant to conform with EVP_BytesToKey. + * www.openssl.org/docs/crypto/EVP_BytesToKey.html + */ + var EvpKDF = C_algo.EvpKDF = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hash algorithm to use. Default: MD5 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: MD5, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.EvpKDF.create(); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init hasher + var hasher = cfg.hasher.create(); + + // Initial values + var derivedKey = WordArray.create(); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + if (block) { + hasher.update(block); + } + var block = hasher.update(password).finalize(salt); + hasher.reset(); + + // Iterations + for (var i = 1; i < iterations; i++) { + block = hasher.finalize(block); + hasher.reset(); + } + + derivedKey.concat(block); + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.EvpKDF(password, salt); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 }); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.EvpKDF = function (password, salt, cfg) { + return EvpKDF.create(cfg).compute(password, salt); + }; + }()); + + + return CryptoJS.EvpKDF; + +})); +},{"./core":53,"./hmac":58,"./sha1":77}],57:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var CipherParams = C_lib.CipherParams; + var C_enc = C.enc; + var Hex = C_enc.Hex; + var C_format = C.format; + + var HexFormatter = C_format.Hex = { + /** + * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The hexadecimally encoded string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.format.Hex.stringify(cipherParams); + */ + stringify: function (cipherParams) { + return cipherParams.ciphertext.toString(Hex); + }, + + /** + * Converts a hexadecimally encoded ciphertext string to a cipher params object. + * + * @param {string} input The hexadecimally encoded string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.Hex.parse(hexString); + */ + parse: function (input) { + var ciphertext = Hex.parse(input); + return CipherParams.create({ ciphertext: ciphertext }); + } + }; + }()); + + + return CryptoJS.format.Hex; + +})); +},{"./cipher-core":52,"./core":53}],58:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var C_algo = C.algo; + + /** + * HMAC algorithm. + */ + var HMAC = C_algo.HMAC = Base.extend({ + /** + * Initializes a newly created HMAC. + * + * @param {Hasher} hasher The hash algorithm to use. + * @param {WordArray|string} key The secret key. + * + * @example + * + * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key); + */ + init: function (hasher, key) { + // Init hasher + hasher = this._hasher = new hasher.init(); + + // Convert string to WordArray, else assume WordArray already + if (typeof key == 'string') { + key = Utf8.parse(key); + } + + // Shortcuts + var hasherBlockSize = hasher.blockSize; + var hasherBlockSizeBytes = hasherBlockSize * 4; + + // Allow arbitrary length keys + if (key.sigBytes > hasherBlockSizeBytes) { + key = hasher.finalize(key); + } + + // Clamp excess bits + key.clamp(); + + // Clone key for inner and outer pads + var oKey = this._oKey = key.clone(); + var iKey = this._iKey = key.clone(); + + // Shortcuts + var oKeyWords = oKey.words; + var iKeyWords = iKey.words; + + // XOR keys with pad constants + for (var i = 0; i < hasherBlockSize; i++) { + oKeyWords[i] ^= 0x5c5c5c5c; + iKeyWords[i] ^= 0x36363636; + } + oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes; + + // Set initial values + this.reset(); + }, + + /** + * Resets this HMAC to its initial state. + * + * @example + * + * hmacHasher.reset(); + */ + reset: function () { + // Shortcut + var hasher = this._hasher; + + // Reset + hasher.reset(); + hasher.update(this._iKey); + }, + + /** + * Updates this HMAC with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {HMAC} This HMAC instance. + * + * @example + * + * hmacHasher.update('message'); + * hmacHasher.update(wordArray); + */ + update: function (messageUpdate) { + this._hasher.update(messageUpdate); + + // Chainable + return this; + }, + + /** + * Finalizes the HMAC computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The HMAC. + * + * @example + * + * var hmac = hmacHasher.finalize(); + * var hmac = hmacHasher.finalize('message'); + * var hmac = hmacHasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Shortcut + var hasher = this._hasher; + + // Compute HMAC + var innerHash = hasher.finalize(messageUpdate); + hasher.reset(); + var hmac = hasher.finalize(this._oKey.clone().concat(innerHash)); + + return hmac; + } + }); + }()); + + +})); +},{"./core":53}],59:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./x64-core"), require("./lib-typedarrays"), require("./enc-utf16"), require("./enc-base64"), require("./md5"), require("./sha1"), require("./sha256"), require("./sha224"), require("./sha512"), require("./sha384"), require("./sha3"), require("./ripemd160"), require("./hmac"), require("./pbkdf2"), require("./evpkdf"), require("./cipher-core"), require("./mode-cfb"), require("./mode-ctr"), require("./mode-ctr-gladman"), require("./mode-ofb"), require("./mode-ecb"), require("./pad-ansix923"), require("./pad-iso10126"), require("./pad-iso97971"), require("./pad-zeropadding"), require("./pad-nopadding"), require("./format-hex"), require("./aes"), require("./tripledes"), require("./rc4"), require("./rabbit"), require("./rabbit-legacy")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./x64-core", "./lib-typedarrays", "./enc-utf16", "./enc-base64", "./md5", "./sha1", "./sha256", "./sha224", "./sha512", "./sha384", "./sha3", "./ripemd160", "./hmac", "./pbkdf2", "./evpkdf", "./cipher-core", "./mode-cfb", "./mode-ctr", "./mode-ctr-gladman", "./mode-ofb", "./mode-ecb", "./pad-ansix923", "./pad-iso10126", "./pad-iso97971", "./pad-zeropadding", "./pad-nopadding", "./format-hex", "./aes", "./tripledes", "./rc4", "./rabbit", "./rabbit-legacy"], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + return CryptoJS; + +})); +},{"./aes":51,"./cipher-core":52,"./core":53,"./enc-base64":54,"./enc-utf16":55,"./evpkdf":56,"./format-hex":57,"./hmac":58,"./lib-typedarrays":60,"./md5":61,"./mode-cfb":62,"./mode-ctr":64,"./mode-ctr-gladman":63,"./mode-ecb":65,"./mode-ofb":66,"./pad-ansix923":67,"./pad-iso10126":68,"./pad-iso97971":69,"./pad-nopadding":70,"./pad-zeropadding":71,"./pbkdf2":72,"./rabbit":74,"./rabbit-legacy":73,"./rc4":75,"./ripemd160":76,"./sha1":77,"./sha224":78,"./sha256":79,"./sha3":80,"./sha384":81,"./sha512":82,"./tripledes":83,"./x64-core":84}],60:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Check if typed arrays are supported + if (typeof ArrayBuffer != 'function') { + return; + } + + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + + // Reference original init + var superInit = WordArray.init; + + // Augment WordArray.init to handle typed arrays + var subInit = WordArray.init = function (typedArray) { + // Convert buffers to uint8 + if (typedArray instanceof ArrayBuffer) { + typedArray = new Uint8Array(typedArray); + } + + // Convert other array views to uint8 + if ( + typedArray instanceof Int8Array || + (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || + typedArray instanceof Int16Array || + typedArray instanceof Uint16Array || + typedArray instanceof Int32Array || + typedArray instanceof Uint32Array || + typedArray instanceof Float32Array || + typedArray instanceof Float64Array + ) { + typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); + } + + // Handle Uint8Array + if (typedArray instanceof Uint8Array) { + // Shortcut + var typedArrayByteLength = typedArray.byteLength; + + // Extract bytes + var words = []; + for (var i = 0; i < typedArrayByteLength; i++) { + words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8); + } + + // Initialize this word array + superInit.call(this, words, typedArrayByteLength); + } else { + // Else call normal init + superInit.apply(this, arguments); + } + }; + + subInit.prototype = WordArray; + }()); + + + return CryptoJS.lib.WordArray; + +})); +},{"./core":53}],61:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var T = []; + + // Compute constants + (function () { + for (var i = 0; i < 64; i++) { + T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0; + } + }()); + + /** + * MD5 hash algorithm. + */ + var MD5 = C_algo.MD5 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + + // Shortcuts + var H = this._hash.words; + + var M_offset_0 = M[offset + 0]; + var M_offset_1 = M[offset + 1]; + var M_offset_2 = M[offset + 2]; + var M_offset_3 = M[offset + 3]; + var M_offset_4 = M[offset + 4]; + var M_offset_5 = M[offset + 5]; + var M_offset_6 = M[offset + 6]; + var M_offset_7 = M[offset + 7]; + var M_offset_8 = M[offset + 8]; + var M_offset_9 = M[offset + 9]; + var M_offset_10 = M[offset + 10]; + var M_offset_11 = M[offset + 11]; + var M_offset_12 = M[offset + 12]; + var M_offset_13 = M[offset + 13]; + var M_offset_14 = M[offset + 14]; + var M_offset_15 = M[offset + 15]; + + // Working varialbes + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + + // Computation + a = FF(a, b, c, d, M_offset_0, 7, T[0]); + d = FF(d, a, b, c, M_offset_1, 12, T[1]); + c = FF(c, d, a, b, M_offset_2, 17, T[2]); + b = FF(b, c, d, a, M_offset_3, 22, T[3]); + a = FF(a, b, c, d, M_offset_4, 7, T[4]); + d = FF(d, a, b, c, M_offset_5, 12, T[5]); + c = FF(c, d, a, b, M_offset_6, 17, T[6]); + b = FF(b, c, d, a, M_offset_7, 22, T[7]); + a = FF(a, b, c, d, M_offset_8, 7, T[8]); + d = FF(d, a, b, c, M_offset_9, 12, T[9]); + c = FF(c, d, a, b, M_offset_10, 17, T[10]); + b = FF(b, c, d, a, M_offset_11, 22, T[11]); + a = FF(a, b, c, d, M_offset_12, 7, T[12]); + d = FF(d, a, b, c, M_offset_13, 12, T[13]); + c = FF(c, d, a, b, M_offset_14, 17, T[14]); + b = FF(b, c, d, a, M_offset_15, 22, T[15]); + + a = GG(a, b, c, d, M_offset_1, 5, T[16]); + d = GG(d, a, b, c, M_offset_6, 9, T[17]); + c = GG(c, d, a, b, M_offset_11, 14, T[18]); + b = GG(b, c, d, a, M_offset_0, 20, T[19]); + a = GG(a, b, c, d, M_offset_5, 5, T[20]); + d = GG(d, a, b, c, M_offset_10, 9, T[21]); + c = GG(c, d, a, b, M_offset_15, 14, T[22]); + b = GG(b, c, d, a, M_offset_4, 20, T[23]); + a = GG(a, b, c, d, M_offset_9, 5, T[24]); + d = GG(d, a, b, c, M_offset_14, 9, T[25]); + c = GG(c, d, a, b, M_offset_3, 14, T[26]); + b = GG(b, c, d, a, M_offset_8, 20, T[27]); + a = GG(a, b, c, d, M_offset_13, 5, T[28]); + d = GG(d, a, b, c, M_offset_2, 9, T[29]); + c = GG(c, d, a, b, M_offset_7, 14, T[30]); + b = GG(b, c, d, a, M_offset_12, 20, T[31]); + + a = HH(a, b, c, d, M_offset_5, 4, T[32]); + d = HH(d, a, b, c, M_offset_8, 11, T[33]); + c = HH(c, d, a, b, M_offset_11, 16, T[34]); + b = HH(b, c, d, a, M_offset_14, 23, T[35]); + a = HH(a, b, c, d, M_offset_1, 4, T[36]); + d = HH(d, a, b, c, M_offset_4, 11, T[37]); + c = HH(c, d, a, b, M_offset_7, 16, T[38]); + b = HH(b, c, d, a, M_offset_10, 23, T[39]); + a = HH(a, b, c, d, M_offset_13, 4, T[40]); + d = HH(d, a, b, c, M_offset_0, 11, T[41]); + c = HH(c, d, a, b, M_offset_3, 16, T[42]); + b = HH(b, c, d, a, M_offset_6, 23, T[43]); + a = HH(a, b, c, d, M_offset_9, 4, T[44]); + d = HH(d, a, b, c, M_offset_12, 11, T[45]); + c = HH(c, d, a, b, M_offset_15, 16, T[46]); + b = HH(b, c, d, a, M_offset_2, 23, T[47]); + + a = II(a, b, c, d, M_offset_0, 6, T[48]); + d = II(d, a, b, c, M_offset_7, 10, T[49]); + c = II(c, d, a, b, M_offset_14, 15, T[50]); + b = II(b, c, d, a, M_offset_5, 21, T[51]); + a = II(a, b, c, d, M_offset_12, 6, T[52]); + d = II(d, a, b, c, M_offset_3, 10, T[53]); + c = II(c, d, a, b, M_offset_10, 15, T[54]); + b = II(b, c, d, a, M_offset_1, 21, T[55]); + a = II(a, b, c, d, M_offset_8, 6, T[56]); + d = II(d, a, b, c, M_offset_15, 10, T[57]); + c = II(c, d, a, b, M_offset_6, 15, T[58]); + b = II(b, c, d, a, M_offset_13, 21, T[59]); + a = II(a, b, c, d, M_offset_4, 6, T[60]); + d = II(d, a, b, c, M_offset_11, 10, T[61]); + c = II(c, d, a, b, M_offset_2, 15, T[62]); + b = II(b, c, d, a, M_offset_9, 21, T[63]); + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + + var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000); + var nBitsTotalL = nBitsTotal; + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ( + (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | + (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00) + ); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | + (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00) + ); + + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 4; i++) { + // Shortcut + var H_i = H[i]; + + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + function FF(a, b, c, d, x, s, t) { + var n = a + ((b & c) | (~b & d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function GG(a, b, c, d, x, s, t) { + var n = a + ((b & d) | (c & ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function HH(a, b, c, d, x, s, t) { + var n = a + (b ^ c ^ d) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function II(a, b, c, d, x, s, t) { + var n = a + (c ^ (b | ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.MD5('message'); + * var hash = CryptoJS.MD5(wordArray); + */ + C.MD5 = Hasher._createHelper(MD5); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacMD5(message, key); + */ + C.HmacMD5 = Hasher._createHmacHelper(MD5); + }(Math)); + + + return CryptoJS.MD5; + +})); +},{"./core":53}],62:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Cipher Feedback block mode. + */ + CryptoJS.mode.CFB = (function () { + var CFB = CryptoJS.lib.BlockCipherMode.extend(); + + CFB.Encryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + CFB.Decryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) { + // Shortcut + var iv = this._iv; + + // Generate keystream + if (iv) { + var keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var keystream = this._prevBlock; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + + return CFB; + }()); + + + return CryptoJS.mode.CFB; + +})); +},{"./cipher-core":52,"./core":53}],63:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** @preserve + * Counter block mode compatible with Dr Brian Gladman fileenc.c + * derived from CryptoJS.mode.CTR + * Jan Hruby jhruby.web@gmail.com + */ + CryptoJS.mode.CTRGladman = (function () { + var CTRGladman = CryptoJS.lib.BlockCipherMode.extend(); + + function incWord(word) + { + if (((word >> 24) & 0xff) === 0xff) { //overflow + var b1 = (word >> 16)&0xff; + var b2 = (word >> 8)&0xff; + var b3 = word & 0xff; + + if (b1 === 0xff) // overflow b1 + { + b1 = 0; + if (b2 === 0xff) + { + b2 = 0; + if (b3 === 0xff) + { + b3 = 0; + } + else + { + ++b3; + } + } + else + { + ++b2; + } + } + else + { + ++b1; + } + + word = 0; + word += (b1 << 16); + word += (b2 << 8); + word += b3; + } + else + { + word += (0x01 << 24); + } + return word; + } + + function incCounter(counter) + { + if ((counter[0] = incWord(counter[0])) === 0) + { + // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 + counter[1] = incWord(counter[1]); + } + return counter; + } + + var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + + incCounter(counter); + + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTRGladman.Decryptor = Encryptor; + + return CTRGladman; + }()); + + + + + return CryptoJS.mode.CTRGladman; + +})); +},{"./cipher-core":52,"./core":53}],64:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Counter block mode. + */ + CryptoJS.mode.CTR = (function () { + var CTR = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = CTR.Encryptor = CTR.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Increment counter + counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTR.Decryptor = Encryptor; + + return CTR; + }()); + + + return CryptoJS.mode.CTR; + +})); +},{"./cipher-core":52,"./core":53}],65:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Electronic Codebook block mode. + */ + CryptoJS.mode.ECB = (function () { + var ECB = CryptoJS.lib.BlockCipherMode.extend(); + + ECB.Encryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.encryptBlock(words, offset); + } + }); + + ECB.Decryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.decryptBlock(words, offset); + } + }); + + return ECB; + }()); + + + return CryptoJS.mode.ECB; + +})); +},{"./cipher-core":52,"./core":53}],66:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Output Feedback block mode. + */ + CryptoJS.mode.OFB = (function () { + var OFB = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = OFB.Encryptor = OFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var keystream = this._keystream; + + // Generate keystream + if (iv) { + keystream = this._keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + OFB.Decryptor = Encryptor; + + return OFB; + }()); + + + return CryptoJS.mode.OFB; + +})); +},{"./cipher-core":52,"./core":53}],67:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * ANSI X.923 padding strategy. + */ + CryptoJS.pad.AnsiX923 = { + pad: function (data, blockSize) { + // Shortcuts + var dataSigBytes = data.sigBytes; + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes; + + // Compute last byte position + var lastBytePos = dataSigBytes + nPaddingBytes - 1; + + // Pad + data.clamp(); + data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8); + data.sigBytes += nPaddingBytes; + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + return CryptoJS.pad.Ansix923; + +})); +},{"./cipher-core":52,"./core":53}],68:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * ISO 10126 padding strategy. + */ + CryptoJS.pad.Iso10126 = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Pad + data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)). + concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1)); + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + return CryptoJS.pad.Iso10126; + +})); +},{"./cipher-core":52,"./core":53}],69:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * ISO/IEC 9797-1 Padding Method 2. + */ + CryptoJS.pad.Iso97971 = { + pad: function (data, blockSize) { + // Add 0x80 byte + data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); + + // Zero pad the rest + CryptoJS.pad.ZeroPadding.pad(data, blockSize); + }, + + unpad: function (data) { + // Remove zero padding + CryptoJS.pad.ZeroPadding.unpad(data); + + // Remove one more byte -- the 0x80 byte + data.sigBytes--; + } + }; + + + return CryptoJS.pad.Iso97971; + +})); +},{"./cipher-core":52,"./core":53}],70:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * A noop padding strategy. + */ + CryptoJS.pad.NoPadding = { + pad: function () { + }, + + unpad: function () { + } + }; + + + return CryptoJS.pad.NoPadding; + +})); +},{"./cipher-core":52,"./core":53}],71:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** + * Zero padding strategy. + */ + CryptoJS.pad.ZeroPadding = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Pad + data.clamp(); + data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes); + }, + + unpad: function (data) { + // Shortcut + var dataWords = data.words; + + // Unpad + var i = data.sigBytes - 1; + while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) { + i--; + } + data.sigBytes = i + 1; + } + }; + + + return CryptoJS.pad.ZeroPadding; + +})); +},{"./cipher-core":52,"./core":53}],72:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./sha1"), require("./hmac")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./sha1", "./hmac"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA1 = C_algo.SHA1; + var HMAC = C_algo.HMAC; + + /** + * Password-Based Key Derivation Function 2 algorithm. + */ + var PBKDF2 = C_algo.PBKDF2 = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hasher to use. Default: SHA1 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: SHA1, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.PBKDF2.create(); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init HMAC + var hmac = HMAC.create(cfg.hasher, password); + + // Initial values + var derivedKey = WordArray.create(); + var blockIndex = WordArray.create([0x00000001]); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var blockIndexWords = blockIndex.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + var block = hmac.update(salt).finalize(blockIndex); + hmac.reset(); + + // Shortcuts + var blockWords = block.words; + var blockWordsLength = blockWords.length; + + // Iterations + var intermediate = block; + for (var i = 1; i < iterations; i++) { + intermediate = hmac.finalize(intermediate); + hmac.reset(); + + // Shortcut + var intermediateWords = intermediate.words; + + // XOR intermediate with block + for (var j = 0; j < blockWordsLength; j++) { + blockWords[j] ^= intermediateWords[j]; + } + } + + derivedKey.concat(block); + blockIndexWords[0]++; + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.PBKDF2(password, salt); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.PBKDF2 = function (password, salt, cfg) { + return PBKDF2.create(cfg).compute(password, salt); + }; + }()); + + + return CryptoJS.PBKDF2; + +})); +},{"./core":53,"./hmac":58,"./sha1":77}],73:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm. + * + * This is a legacy version that neglected to convert the key to little-endian. + * This error doesn't affect the cipher's security, + * but it does affect its compatibility with other implementations. + */ + var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg); + */ + C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy); + }()); + + + return CryptoJS.RabbitLegacy; + +})); +},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],74:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm + */ + var Rabbit = C_algo.Rabbit = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Swap endian + for (var i = 0; i < 4; i++) { + K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) | + (((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00); + } + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg); + * var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg); + */ + C.Rabbit = StreamCipher._createHelper(Rabbit); + }()); + + + return CryptoJS.Rabbit; + +})); +},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],75:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + /** + * RC4 stream cipher algorithm. + */ + var RC4 = C_algo.RC4 = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + var keySigBytes = key.sigBytes; + + // Init sbox + var S = this._S = []; + for (var i = 0; i < 256; i++) { + S[i] = i; + } + + // Key setup + for (var i = 0, j = 0; i < 256; i++) { + var keyByteIndex = i % keySigBytes; + var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff; + + j = (j + S[i] + keyByte) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + } + + // Counters + this._i = this._j = 0; + }, + + _doProcessBlock: function (M, offset) { + M[offset] ^= generateKeystreamWord.call(this); + }, + + keySize: 256/32, + + ivSize: 0 + }); + + function generateKeystreamWord() { + // Shortcuts + var S = this._S; + var i = this._i; + var j = this._j; + + // Generate keystream word + var keystreamWord = 0; + for (var n = 0; n < 4; n++) { + i = (i + 1) % 256; + j = (j + S[i]) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + + keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8); + } + + // Update counters + this._i = i; + this._j = j; + + return keystreamWord; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg); + */ + C.RC4 = StreamCipher._createHelper(RC4); + + /** + * Modified RC4 stream cipher algorithm. + */ + var RC4Drop = C_algo.RC4Drop = RC4.extend({ + /** + * Configuration options. + * + * @property {number} drop The number of keystream words to drop. Default 192 + */ + cfg: RC4.cfg.extend({ + drop: 192 + }), + + _doReset: function () { + RC4._doReset.call(this); + + // Drop + for (var i = this.cfg.drop; i > 0; i--) { + generateKeystreamWord.call(this); + } + } + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg); + */ + C.RC4Drop = StreamCipher._createHelper(RC4Drop); + }()); + + + return CryptoJS.RC4; + +})); +},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],76:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + /** @preserve + (c) 2012 by Cédric Mesnil. 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. + + 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 THE COPYRIGHT HOLDER 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. + */ + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var _zl = WordArray.create([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]); + var _zr = WordArray.create([ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]); + var _sl = WordArray.create([ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]); + var _sr = WordArray.create([ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]); + + var _hl = WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]); + var _hr = WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]); + + /** + * RIPEMD160 hash algorithm. + */ + var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({ + _doReset: function () { + this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]); + }, + + _doProcessBlock: function (M, offset) { + + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + // Swap + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + // Shortcut + var H = this._hash.words; + var hl = _hl.words; + var hr = _hr.words; + var zl = _zl.words; + var zr = _zr.words; + var sl = _sl.words; + var sr = _sr.words; + + // Working variables + var al, bl, cl, dl, el; + var ar, br, cr, dr, er; + + ar = al = H[0]; + br = bl = H[1]; + cr = cl = H[2]; + dr = dl = H[3]; + er = el = H[4]; + // Computation + var t; + for (var i = 0; i < 80; i += 1) { + t = (al + M[offset+zl[i]])|0; + if (i<16){ + t += f1(bl,cl,dl) + hl[0]; + } else if (i<32) { + t += f2(bl,cl,dl) + hl[1]; + } else if (i<48) { + t += f3(bl,cl,dl) + hl[2]; + } else if (i<64) { + t += f4(bl,cl,dl) + hl[3]; + } else {// if (i<80) { + t += f5(bl,cl,dl) + hl[4]; + } + t = t|0; + t = rotl(t,sl[i]); + t = (t+el)|0; + al = el; + el = dl; + dl = rotl(cl, 10); + cl = bl; + bl = t; + + t = (ar + M[offset+zr[i]])|0; + if (i<16){ + t += f5(br,cr,dr) + hr[0]; + } else if (i<32) { + t += f4(br,cr,dr) + hr[1]; + } else if (i<48) { + t += f3(br,cr,dr) + hr[2]; + } else if (i<64) { + t += f2(br,cr,dr) + hr[3]; + } else {// if (i<80) { + t += f1(br,cr,dr) + hr[4]; + } + t = t|0; + t = rotl(t,sr[i]) ; + t = (t+er)|0; + ar = er; + er = dr; + dr = rotl(cr, 10); + cr = br; + br = t; + } + // Intermediate hash value + t = (H[1] + cl + dr)|0; + H[1] = (H[2] + dl + er)|0; + H[2] = (H[3] + el + ar)|0; + H[3] = (H[4] + al + br)|0; + H[4] = (H[0] + bl + cr)|0; + H[0] = t; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | + (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00) + ); + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 5; i++) { + // Shortcut + var H_i = H[i]; + + // Swap + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + + function f1(x, y, z) { + return ((x) ^ (y) ^ (z)); + + } + + function f2(x, y, z) { + return (((x)&(y)) | ((~x)&(z))); + } + + function f3(x, y, z) { + return (((x) | (~(y))) ^ (z)); + } + + function f4(x, y, z) { + return (((x) & (z)) | ((y)&(~(z)))); + } + + function f5(x, y, z) { + return ((x) ^ ((y) |(~(z)))); + + } + + function rotl(x,n) { + return (x<>>(32-n)); + } + + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.RIPEMD160('message'); + * var hash = CryptoJS.RIPEMD160(wordArray); + */ + C.RIPEMD160 = Hasher._createHelper(RIPEMD160); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacRIPEMD160(message, key); + */ + C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160); + }(Math)); + + + return CryptoJS.RIPEMD160; + +})); +},{"./core":53}],77:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Reusable object + var W = []; + + /** + * SHA-1 hash algorithm. + */ + var SHA1 = C_algo.SHA1 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476, + 0xc3d2e1f0 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + + // Computation + for (var i = 0; i < 80; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; + W[i] = (n << 1) | (n >>> 31); + } + + var t = ((a << 5) | (a >>> 27)) + e + W[i]; + if (i < 20) { + t += ((b & c) | (~b & d)) + 0x5a827999; + } else if (i < 40) { + t += (b ^ c ^ d) + 0x6ed9eba1; + } else if (i < 60) { + t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; + } else /* if (i < 80) */ { + t += (b ^ c ^ d) - 0x359d3e2a; + } + + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA1('message'); + * var hash = CryptoJS.SHA1(wordArray); + */ + C.SHA1 = Hasher._createHelper(SHA1); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA1(message, key); + */ + C.HmacSHA1 = Hasher._createHmacHelper(SHA1); + }()); + + + return CryptoJS.SHA1; + +})); +},{"./core":53}],78:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./sha256")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./sha256"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA256 = C_algo.SHA256; + + /** + * SHA-224 hash algorithm. + */ + var SHA224 = C_algo.SHA224 = SHA256.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + ]); + }, + + _doFinalize: function () { + var hash = SHA256._doFinalize.call(this); + + hash.sigBytes -= 4; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA224('message'); + * var hash = CryptoJS.SHA224(wordArray); + */ + C.SHA224 = SHA256._createHelper(SHA224); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA224(message, key); + */ + C.HmacSHA224 = SHA256._createHmacHelper(SHA224); + }()); + + + return CryptoJS.SHA224; + +})); +},{"./core":53,"./sha256":79}],79:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Initialization and round constants tables + var H = []; + var K = []; + + // Compute constants + (function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + if (nPrime < 8) { + H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); + } + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + + nPrime++; + } + + n++; + } + }()); + + // Reusable object + var W = []; + + /** + * SHA-256 hash algorithm. + */ + var SHA256 = C_algo.SHA256 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init(H.slice(0)); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA256('message'); + * var hash = CryptoJS.SHA256(wordArray); + */ + C.SHA256 = Hasher._createHelper(SHA256); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA256(message, key); + */ + C.HmacSHA256 = Hasher._createHmacHelper(SHA256); + }(Math)); + + + return CryptoJS.SHA256; + +})); +},{"./core":53}],80:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./x64-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./x64-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var C_algo = C.algo; + + // Constants tables + var RHO_OFFSETS = []; + var PI_INDEXES = []; + var ROUND_CONSTANTS = []; + + // Compute Constants + (function () { + // Compute rho offset constants + var x = 1, y = 0; + for (var t = 0; t < 24; t++) { + RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; + + var newX = y % 5; + var newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + // Compute pi index constants + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; + } + } + + // Compute round constants + var LFSR = 0x01; + for (var i = 0; i < 24; i++) { + var roundConstantMsw = 0; + var roundConstantLsw = 0; + + for (var j = 0; j < 7; j++) { + if (LFSR & 0x01) { + var bitPosition = (1 << j) - 1; + if (bitPosition < 32) { + roundConstantLsw ^= 1 << bitPosition; + } else /* if (bitPosition >= 32) */ { + roundConstantMsw ^= 1 << (bitPosition - 32); + } + } + + // Compute next LFSR + if (LFSR & 0x80) { + // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 + LFSR = (LFSR << 1) ^ 0x71; + } else { + LFSR <<= 1; + } + } + + ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); + } + }()); + + // Reusable objects for temporary values + var T = []; + (function () { + for (var i = 0; i < 25; i++) { + T[i] = X64Word.create(); + } + }()); + + /** + * SHA-3 hash algorithm. + */ + var SHA3 = C_algo.SHA3 = Hasher.extend({ + /** + * Configuration options. + * + * @property {number} outputLength + * The desired number of bits in the output hash. + * Only values permitted are: 224, 256, 384, 512. + * Default: 512 + */ + cfg: Hasher.cfg.extend({ + outputLength: 512 + }), + + _doReset: function () { + var state = this._state = [] + for (var i = 0; i < 25; i++) { + state[i] = new X64Word.init(); + } + + this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var state = this._state; + var nBlockSizeLanes = this.blockSize / 2; + + // Absorb + for (var i = 0; i < nBlockSizeLanes; i++) { + // Shortcuts + var M2i = M[offset + 2 * i]; + var M2i1 = M[offset + 2 * i + 1]; + + // Swap endian + M2i = ( + (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | + (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) + ); + M2i1 = ( + (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | + (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) + ); + + // Absorb message into state + var lane = state[i]; + lane.high ^= M2i1; + lane.low ^= M2i; + } + + // Rounds + for (var round = 0; round < 24; round++) { + // Theta + for (var x = 0; x < 5; x++) { + // Mix column lanes + var tMsw = 0, tLsw = 0; + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + tMsw ^= lane.high; + tLsw ^= lane.low; + } + + // Temporary values + var Tx = T[x]; + Tx.high = tMsw; + Tx.low = tLsw; + } + for (var x = 0; x < 5; x++) { + // Shortcuts + var Tx4 = T[(x + 4) % 5]; + var Tx1 = T[(x + 1) % 5]; + var Tx1Msw = Tx1.high; + var Tx1Lsw = Tx1.low; + + // Mix surrounding columns + var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); + var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + lane.high ^= tMsw; + lane.low ^= tLsw; + } + } + + // Rho Pi + for (var laneIndex = 1; laneIndex < 25; laneIndex++) { + // Shortcuts + var lane = state[laneIndex]; + var laneMsw = lane.high; + var laneLsw = lane.low; + var rhoOffset = RHO_OFFSETS[laneIndex]; + + // Rotate lanes + if (rhoOffset < 32) { + var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); + var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); + } else /* if (rhoOffset >= 32) */ { + var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); + var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); + } + + // Transpose lanes + var TPiLane = T[PI_INDEXES[laneIndex]]; + TPiLane.high = tMsw; + TPiLane.low = tLsw; + } + + // Rho pi at x = y = 0 + var T0 = T[0]; + var state0 = state[0]; + T0.high = state0.high; + T0.low = state0.low; + + // Chi + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + // Shortcuts + var laneIndex = x + 5 * y; + var lane = state[laneIndex]; + var TLane = T[laneIndex]; + var Tx1Lane = T[((x + 1) % 5) + 5 * y]; + var Tx2Lane = T[((x + 2) % 5) + 5 * y]; + + // Mix rows + lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); + lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); + } + } + + // Iota + var lane = state[0]; + var roundConstant = ROUND_CONSTANTS[round]; + lane.high ^= roundConstant.high; + lane.low ^= roundConstant.low;; + } + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + var blockSizeBits = this.blockSize * 32; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); + dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var state = this._state; + var outputLengthBytes = this.cfg.outputLength / 8; + var outputLengthLanes = outputLengthBytes / 8; + + // Squeeze + var hashWords = []; + for (var i = 0; i < outputLengthLanes; i++) { + // Shortcuts + var lane = state[i]; + var laneMsw = lane.high; + var laneLsw = lane.low; + + // Swap endian + laneMsw = ( + (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | + (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) + ); + laneLsw = ( + (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | + (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) + ); + + // Squeeze state to retrieve hash + hashWords.push(laneLsw); + hashWords.push(laneMsw); + } + + // Return final computed hash + return new WordArray.init(hashWords, outputLengthBytes); + }, + + clone: function () { + var clone = Hasher.clone.call(this); + + var state = clone._state = this._state.slice(0); + for (var i = 0; i < 25; i++) { + state[i] = state[i].clone(); + } + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA3('message'); + * var hash = CryptoJS.SHA3(wordArray); + */ + C.SHA3 = Hasher._createHelper(SHA3); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA3(message, key); + */ + C.HmacSHA3 = Hasher._createHmacHelper(SHA3); + }(Math)); + + + return CryptoJS.SHA3; + +})); +},{"./core":53,"./x64-core":84}],81:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./x64-core"), require("./sha512")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./x64-core", "./sha512"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + var SHA512 = C_algo.SHA512; + + /** + * SHA-384 hash algorithm. + */ + var SHA384 = C_algo.SHA384 = SHA512.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), + new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), + new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), + new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4) + ]); + }, + + _doFinalize: function () { + var hash = SHA512._doFinalize.call(this); + + hash.sigBytes -= 16; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA384('message'); + * var hash = CryptoJS.SHA384(wordArray); + */ + C.SHA384 = SHA512._createHelper(SHA384); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA384(message, key); + */ + C.HmacSHA384 = SHA512._createHmacHelper(SHA384); + }()); + + + return CryptoJS.SHA384; + +})); +},{"./core":53,"./sha512":82,"./x64-core":84}],82:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./x64-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./x64-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + + function X64Word_create() { + return X64Word.create.apply(X64Word, arguments); + } + + // Constants + var K = [ + X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), + X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), + X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), + X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), + X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), + X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), + X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), + X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), + X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), + X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), + X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), + X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), + X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), + X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), + X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), + X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), + X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), + X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), + X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), + X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), + X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), + X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), + X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), + X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), + X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), + X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), + X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), + X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), + X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), + X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), + X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), + X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), + X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), + X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), + X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), + X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), + X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), + X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), + X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), + X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817) + ]; + + // Reusable objects + var W = []; + (function () { + for (var i = 0; i < 80; i++) { + W[i] = X64Word_create(); + } + }()); + + /** + * SHA-512 hash algorithm. + */ + var SHA512 = C_algo.SHA512 = Hasher.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), + new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), + new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), + new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179) + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var H = this._hash.words; + + var H0 = H[0]; + var H1 = H[1]; + var H2 = H[2]; + var H3 = H[3]; + var H4 = H[4]; + var H5 = H[5]; + var H6 = H[6]; + var H7 = H[7]; + + var H0h = H0.high; + var H0l = H0.low; + var H1h = H1.high; + var H1l = H1.low; + var H2h = H2.high; + var H2l = H2.low; + var H3h = H3.high; + var H3l = H3.low; + var H4h = H4.high; + var H4l = H4.low; + var H5h = H5.high; + var H5l = H5.low; + var H6h = H6.high; + var H6l = H6.low; + var H7h = H7.high; + var H7l = H7.low; + + // Working variables + var ah = H0h; + var al = H0l; + var bh = H1h; + var bl = H1l; + var ch = H2h; + var cl = H2l; + var dh = H3h; + var dl = H3l; + var eh = H4h; + var el = H4l; + var fh = H5h; + var fl = H5l; + var gh = H6h; + var gl = H6l; + var hh = H7h; + var hl = H7l; + + // Rounds + for (var i = 0; i < 80; i++) { + // Shortcut + var Wi = W[i]; + + // Extend message + if (i < 16) { + var Wih = Wi.high = M[offset + i * 2] | 0; + var Wil = Wi.low = M[offset + i * 2 + 1] | 0; + } else { + // Gamma0 + var gamma0x = W[i - 15]; + var gamma0xh = gamma0x.high; + var gamma0xl = gamma0x.low; + var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); + var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); + + // Gamma1 + var gamma1x = W[i - 2]; + var gamma1xh = gamma1x.high; + var gamma1xl = gamma1x.low; + var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); + var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); + + // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] + var Wi7 = W[i - 7]; + var Wi7h = Wi7.high; + var Wi7l = Wi7.low; + + var Wi16 = W[i - 16]; + var Wi16h = Wi16.high; + var Wi16l = Wi16.low; + + var Wil = gamma0l + Wi7l; + var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); + var Wil = Wil + gamma1l; + var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); + var Wil = Wil + Wi16l; + var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); + + Wi.high = Wih; + Wi.low = Wil; + } + + var chh = (eh & fh) ^ (~eh & gh); + var chl = (el & fl) ^ (~el & gl); + var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); + var majl = (al & bl) ^ (al & cl) ^ (bl & cl); + + var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); + var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); + var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); + var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); + + // t1 = h + sigma1 + ch + K[i] + W[i] + var Ki = K[i]; + var Kih = Ki.high; + var Kil = Ki.low; + + var t1l = hl + sigma1l; + var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); + var t1l = t1l + chl; + var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); + var t1l = t1l + Kil; + var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); + var t1l = t1l + Wil; + var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); + + // t2 = sigma0 + maj + var t2l = sigma0l + majl; + var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); + + // Update working variables + hh = gh; + hl = gl; + gh = fh; + gl = fl; + fh = eh; + fl = el; + el = (dl + t1l) | 0; + eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; + dh = ch; + dl = cl; + ch = bh; + cl = bl; + bh = ah; + bl = al; + al = (t1l + t2l) | 0; + ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; + } + + // Intermediate hash value + H0l = H0.low = (H0l + al); + H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); + H1l = H1.low = (H1l + bl); + H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); + H2l = H2.low = (H2l + cl); + H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); + H3l = H3.low = (H3l + dl); + H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); + H4l = H4.low = (H4l + el); + H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); + H5l = H5.low = (H5l + fl); + H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); + H6l = H6.low = (H6l + gl); + H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); + H7l = H7.low = (H7l + hl); + H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Convert hash to 32-bit word array before returning + var hash = this._hash.toX32(); + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + }, + + blockSize: 1024/32 + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA512('message'); + * var hash = CryptoJS.SHA512(wordArray); + */ + C.SHA512 = Hasher._createHelper(SHA512); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA512(message, key); + */ + C.HmacSHA512 = Hasher._createHmacHelper(SHA512); + }()); + + + return CryptoJS.SHA512; + +})); +},{"./core":53,"./x64-core":84}],83:[function(require,module,exports){ +;(function (root, factory, undef) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core"), require("./enc-base64"), require("./md5"), require("./evpkdf"), require("./cipher-core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core", "./enc-base64", "./md5", "./evpkdf", "./cipher-core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Permuted Choice 1 constants + var PC1 = [ + 57, 49, 41, 33, 25, 17, 9, 1, + 58, 50, 42, 34, 26, 18, 10, 2, + 59, 51, 43, 35, 27, 19, 11, 3, + 60, 52, 44, 36, 63, 55, 47, 39, + 31, 23, 15, 7, 62, 54, 46, 38, + 30, 22, 14, 6, 61, 53, 45, 37, + 29, 21, 13, 5, 28, 20, 12, 4 + ]; + + // Permuted Choice 2 constants + var PC2 = [ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 + ]; + + // Cumulative bit shift constants + var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]; + + // SBOXes and round permutation constants + var SBOX_P = [ + { + 0x0: 0x808200, + 0x10000000: 0x8000, + 0x20000000: 0x808002, + 0x30000000: 0x2, + 0x40000000: 0x200, + 0x50000000: 0x808202, + 0x60000000: 0x800202, + 0x70000000: 0x800000, + 0x80000000: 0x202, + 0x90000000: 0x800200, + 0xa0000000: 0x8200, + 0xb0000000: 0x808000, + 0xc0000000: 0x8002, + 0xd0000000: 0x800002, + 0xe0000000: 0x0, + 0xf0000000: 0x8202, + 0x8000000: 0x0, + 0x18000000: 0x808202, + 0x28000000: 0x8202, + 0x38000000: 0x8000, + 0x48000000: 0x808200, + 0x58000000: 0x200, + 0x68000000: 0x808002, + 0x78000000: 0x2, + 0x88000000: 0x800200, + 0x98000000: 0x8200, + 0xa8000000: 0x808000, + 0xb8000000: 0x800202, + 0xc8000000: 0x800002, + 0xd8000000: 0x8002, + 0xe8000000: 0x202, + 0xf8000000: 0x800000, + 0x1: 0x8000, + 0x10000001: 0x2, + 0x20000001: 0x808200, + 0x30000001: 0x800000, + 0x40000001: 0x808002, + 0x50000001: 0x8200, + 0x60000001: 0x200, + 0x70000001: 0x800202, + 0x80000001: 0x808202, + 0x90000001: 0x808000, + 0xa0000001: 0x800002, + 0xb0000001: 0x8202, + 0xc0000001: 0x202, + 0xd0000001: 0x800200, + 0xe0000001: 0x8002, + 0xf0000001: 0x0, + 0x8000001: 0x808202, + 0x18000001: 0x808000, + 0x28000001: 0x800000, + 0x38000001: 0x200, + 0x48000001: 0x8000, + 0x58000001: 0x800002, + 0x68000001: 0x2, + 0x78000001: 0x8202, + 0x88000001: 0x8002, + 0x98000001: 0x800202, + 0xa8000001: 0x202, + 0xb8000001: 0x808200, + 0xc8000001: 0x800200, + 0xd8000001: 0x0, + 0xe8000001: 0x8200, + 0xf8000001: 0x808002 + }, + { + 0x0: 0x40084010, + 0x1000000: 0x4000, + 0x2000000: 0x80000, + 0x3000000: 0x40080010, + 0x4000000: 0x40000010, + 0x5000000: 0x40084000, + 0x6000000: 0x40004000, + 0x7000000: 0x10, + 0x8000000: 0x84000, + 0x9000000: 0x40004010, + 0xa000000: 0x40000000, + 0xb000000: 0x84010, + 0xc000000: 0x80010, + 0xd000000: 0x0, + 0xe000000: 0x4010, + 0xf000000: 0x40080000, + 0x800000: 0x40004000, + 0x1800000: 0x84010, + 0x2800000: 0x10, + 0x3800000: 0x40004010, + 0x4800000: 0x40084010, + 0x5800000: 0x40000000, + 0x6800000: 0x80000, + 0x7800000: 0x40080010, + 0x8800000: 0x80010, + 0x9800000: 0x0, + 0xa800000: 0x4000, + 0xb800000: 0x40080000, + 0xc800000: 0x40000010, + 0xd800000: 0x84000, + 0xe800000: 0x40084000, + 0xf800000: 0x4010, + 0x10000000: 0x0, + 0x11000000: 0x40080010, + 0x12000000: 0x40004010, + 0x13000000: 0x40084000, + 0x14000000: 0x40080000, + 0x15000000: 0x10, + 0x16000000: 0x84010, + 0x17000000: 0x4000, + 0x18000000: 0x4010, + 0x19000000: 0x80000, + 0x1a000000: 0x80010, + 0x1b000000: 0x40000010, + 0x1c000000: 0x84000, + 0x1d000000: 0x40004000, + 0x1e000000: 0x40000000, + 0x1f000000: 0x40084010, + 0x10800000: 0x84010, + 0x11800000: 0x80000, + 0x12800000: 0x40080000, + 0x13800000: 0x4000, + 0x14800000: 0x40004000, + 0x15800000: 0x40084010, + 0x16800000: 0x10, + 0x17800000: 0x40000000, + 0x18800000: 0x40084000, + 0x19800000: 0x40000010, + 0x1a800000: 0x40004010, + 0x1b800000: 0x80010, + 0x1c800000: 0x0, + 0x1d800000: 0x4010, + 0x1e800000: 0x40080010, + 0x1f800000: 0x84000 + }, + { + 0x0: 0x104, + 0x100000: 0x0, + 0x200000: 0x4000100, + 0x300000: 0x10104, + 0x400000: 0x10004, + 0x500000: 0x4000004, + 0x600000: 0x4010104, + 0x700000: 0x4010000, + 0x800000: 0x4000000, + 0x900000: 0x4010100, + 0xa00000: 0x10100, + 0xb00000: 0x4010004, + 0xc00000: 0x4000104, + 0xd00000: 0x10000, + 0xe00000: 0x4, + 0xf00000: 0x100, + 0x80000: 0x4010100, + 0x180000: 0x4010004, + 0x280000: 0x0, + 0x380000: 0x4000100, + 0x480000: 0x4000004, + 0x580000: 0x10000, + 0x680000: 0x10004, + 0x780000: 0x104, + 0x880000: 0x4, + 0x980000: 0x100, + 0xa80000: 0x4010000, + 0xb80000: 0x10104, + 0xc80000: 0x10100, + 0xd80000: 0x4000104, + 0xe80000: 0x4010104, + 0xf80000: 0x4000000, + 0x1000000: 0x4010100, + 0x1100000: 0x10004, + 0x1200000: 0x10000, + 0x1300000: 0x4000100, + 0x1400000: 0x100, + 0x1500000: 0x4010104, + 0x1600000: 0x4000004, + 0x1700000: 0x0, + 0x1800000: 0x4000104, + 0x1900000: 0x4000000, + 0x1a00000: 0x4, + 0x1b00000: 0x10100, + 0x1c00000: 0x4010000, + 0x1d00000: 0x104, + 0x1e00000: 0x10104, + 0x1f00000: 0x4010004, + 0x1080000: 0x4000000, + 0x1180000: 0x104, + 0x1280000: 0x4010100, + 0x1380000: 0x0, + 0x1480000: 0x10004, + 0x1580000: 0x4000100, + 0x1680000: 0x100, + 0x1780000: 0x4010004, + 0x1880000: 0x10000, + 0x1980000: 0x4010104, + 0x1a80000: 0x10104, + 0x1b80000: 0x4000004, + 0x1c80000: 0x4000104, + 0x1d80000: 0x4010000, + 0x1e80000: 0x4, + 0x1f80000: 0x10100 + }, + { + 0x0: 0x80401000, + 0x10000: 0x80001040, + 0x20000: 0x401040, + 0x30000: 0x80400000, + 0x40000: 0x0, + 0x50000: 0x401000, + 0x60000: 0x80000040, + 0x70000: 0x400040, + 0x80000: 0x80000000, + 0x90000: 0x400000, + 0xa0000: 0x40, + 0xb0000: 0x80001000, + 0xc0000: 0x80400040, + 0xd0000: 0x1040, + 0xe0000: 0x1000, + 0xf0000: 0x80401040, + 0x8000: 0x80001040, + 0x18000: 0x40, + 0x28000: 0x80400040, + 0x38000: 0x80001000, + 0x48000: 0x401000, + 0x58000: 0x80401040, + 0x68000: 0x0, + 0x78000: 0x80400000, + 0x88000: 0x1000, + 0x98000: 0x80401000, + 0xa8000: 0x400000, + 0xb8000: 0x1040, + 0xc8000: 0x80000000, + 0xd8000: 0x400040, + 0xe8000: 0x401040, + 0xf8000: 0x80000040, + 0x100000: 0x400040, + 0x110000: 0x401000, + 0x120000: 0x80000040, + 0x130000: 0x0, + 0x140000: 0x1040, + 0x150000: 0x80400040, + 0x160000: 0x80401000, + 0x170000: 0x80001040, + 0x180000: 0x80401040, + 0x190000: 0x80000000, + 0x1a0000: 0x80400000, + 0x1b0000: 0x401040, + 0x1c0000: 0x80001000, + 0x1d0000: 0x400000, + 0x1e0000: 0x40, + 0x1f0000: 0x1000, + 0x108000: 0x80400000, + 0x118000: 0x80401040, + 0x128000: 0x0, + 0x138000: 0x401000, + 0x148000: 0x400040, + 0x158000: 0x80000000, + 0x168000: 0x80001040, + 0x178000: 0x40, + 0x188000: 0x80000040, + 0x198000: 0x1000, + 0x1a8000: 0x80001000, + 0x1b8000: 0x80400040, + 0x1c8000: 0x1040, + 0x1d8000: 0x80401000, + 0x1e8000: 0x400000, + 0x1f8000: 0x401040 + }, + { + 0x0: 0x80, + 0x1000: 0x1040000, + 0x2000: 0x40000, + 0x3000: 0x20000000, + 0x4000: 0x20040080, + 0x5000: 0x1000080, + 0x6000: 0x21000080, + 0x7000: 0x40080, + 0x8000: 0x1000000, + 0x9000: 0x20040000, + 0xa000: 0x20000080, + 0xb000: 0x21040080, + 0xc000: 0x21040000, + 0xd000: 0x0, + 0xe000: 0x1040080, + 0xf000: 0x21000000, + 0x800: 0x1040080, + 0x1800: 0x21000080, + 0x2800: 0x80, + 0x3800: 0x1040000, + 0x4800: 0x40000, + 0x5800: 0x20040080, + 0x6800: 0x21040000, + 0x7800: 0x20000000, + 0x8800: 0x20040000, + 0x9800: 0x0, + 0xa800: 0x21040080, + 0xb800: 0x1000080, + 0xc800: 0x20000080, + 0xd800: 0x21000000, + 0xe800: 0x1000000, + 0xf800: 0x40080, + 0x10000: 0x40000, + 0x11000: 0x80, + 0x12000: 0x20000000, + 0x13000: 0x21000080, + 0x14000: 0x1000080, + 0x15000: 0x21040000, + 0x16000: 0x20040080, + 0x17000: 0x1000000, + 0x18000: 0x21040080, + 0x19000: 0x21000000, + 0x1a000: 0x1040000, + 0x1b000: 0x20040000, + 0x1c000: 0x40080, + 0x1d000: 0x20000080, + 0x1e000: 0x0, + 0x1f000: 0x1040080, + 0x10800: 0x21000080, + 0x11800: 0x1000000, + 0x12800: 0x1040000, + 0x13800: 0x20040080, + 0x14800: 0x20000000, + 0x15800: 0x1040080, + 0x16800: 0x80, + 0x17800: 0x21040000, + 0x18800: 0x40080, + 0x19800: 0x21040080, + 0x1a800: 0x0, + 0x1b800: 0x21000000, + 0x1c800: 0x1000080, + 0x1d800: 0x40000, + 0x1e800: 0x20040000, + 0x1f800: 0x20000080 + }, + { + 0x0: 0x10000008, + 0x100: 0x2000, + 0x200: 0x10200000, + 0x300: 0x10202008, + 0x400: 0x10002000, + 0x500: 0x200000, + 0x600: 0x200008, + 0x700: 0x10000000, + 0x800: 0x0, + 0x900: 0x10002008, + 0xa00: 0x202000, + 0xb00: 0x8, + 0xc00: 0x10200008, + 0xd00: 0x202008, + 0xe00: 0x2008, + 0xf00: 0x10202000, + 0x80: 0x10200000, + 0x180: 0x10202008, + 0x280: 0x8, + 0x380: 0x200000, + 0x480: 0x202008, + 0x580: 0x10000008, + 0x680: 0x10002000, + 0x780: 0x2008, + 0x880: 0x200008, + 0x980: 0x2000, + 0xa80: 0x10002008, + 0xb80: 0x10200008, + 0xc80: 0x0, + 0xd80: 0x10202000, + 0xe80: 0x202000, + 0xf80: 0x10000000, + 0x1000: 0x10002000, + 0x1100: 0x10200008, + 0x1200: 0x10202008, + 0x1300: 0x2008, + 0x1400: 0x200000, + 0x1500: 0x10000000, + 0x1600: 0x10000008, + 0x1700: 0x202000, + 0x1800: 0x202008, + 0x1900: 0x0, + 0x1a00: 0x8, + 0x1b00: 0x10200000, + 0x1c00: 0x2000, + 0x1d00: 0x10002008, + 0x1e00: 0x10202000, + 0x1f00: 0x200008, + 0x1080: 0x8, + 0x1180: 0x202000, + 0x1280: 0x200000, + 0x1380: 0x10000008, + 0x1480: 0x10002000, + 0x1580: 0x2008, + 0x1680: 0x10202008, + 0x1780: 0x10200000, + 0x1880: 0x10202000, + 0x1980: 0x10200008, + 0x1a80: 0x2000, + 0x1b80: 0x202008, + 0x1c80: 0x200008, + 0x1d80: 0x0, + 0x1e80: 0x10000000, + 0x1f80: 0x10002008 + }, + { + 0x0: 0x100000, + 0x10: 0x2000401, + 0x20: 0x400, + 0x30: 0x100401, + 0x40: 0x2100401, + 0x50: 0x0, + 0x60: 0x1, + 0x70: 0x2100001, + 0x80: 0x2000400, + 0x90: 0x100001, + 0xa0: 0x2000001, + 0xb0: 0x2100400, + 0xc0: 0x2100000, + 0xd0: 0x401, + 0xe0: 0x100400, + 0xf0: 0x2000000, + 0x8: 0x2100001, + 0x18: 0x0, + 0x28: 0x2000401, + 0x38: 0x2100400, + 0x48: 0x100000, + 0x58: 0x2000001, + 0x68: 0x2000000, + 0x78: 0x401, + 0x88: 0x100401, + 0x98: 0x2000400, + 0xa8: 0x2100000, + 0xb8: 0x100001, + 0xc8: 0x400, + 0xd8: 0x2100401, + 0xe8: 0x1, + 0xf8: 0x100400, + 0x100: 0x2000000, + 0x110: 0x100000, + 0x120: 0x2000401, + 0x130: 0x2100001, + 0x140: 0x100001, + 0x150: 0x2000400, + 0x160: 0x2100400, + 0x170: 0x100401, + 0x180: 0x401, + 0x190: 0x2100401, + 0x1a0: 0x100400, + 0x1b0: 0x1, + 0x1c0: 0x0, + 0x1d0: 0x2100000, + 0x1e0: 0x2000001, + 0x1f0: 0x400, + 0x108: 0x100400, + 0x118: 0x2000401, + 0x128: 0x2100001, + 0x138: 0x1, + 0x148: 0x2000000, + 0x158: 0x100000, + 0x168: 0x401, + 0x178: 0x2100400, + 0x188: 0x2000001, + 0x198: 0x2100000, + 0x1a8: 0x0, + 0x1b8: 0x2100401, + 0x1c8: 0x100401, + 0x1d8: 0x400, + 0x1e8: 0x2000400, + 0x1f8: 0x100001 + }, + { + 0x0: 0x8000820, + 0x1: 0x20000, + 0x2: 0x8000000, + 0x3: 0x20, + 0x4: 0x20020, + 0x5: 0x8020820, + 0x6: 0x8020800, + 0x7: 0x800, + 0x8: 0x8020000, + 0x9: 0x8000800, + 0xa: 0x20800, + 0xb: 0x8020020, + 0xc: 0x820, + 0xd: 0x0, + 0xe: 0x8000020, + 0xf: 0x20820, + 0x80000000: 0x800, + 0x80000001: 0x8020820, + 0x80000002: 0x8000820, + 0x80000003: 0x8000000, + 0x80000004: 0x8020000, + 0x80000005: 0x20800, + 0x80000006: 0x20820, + 0x80000007: 0x20, + 0x80000008: 0x8000020, + 0x80000009: 0x820, + 0x8000000a: 0x20020, + 0x8000000b: 0x8020800, + 0x8000000c: 0x0, + 0x8000000d: 0x8020020, + 0x8000000e: 0x8000800, + 0x8000000f: 0x20000, + 0x10: 0x20820, + 0x11: 0x8020800, + 0x12: 0x20, + 0x13: 0x800, + 0x14: 0x8000800, + 0x15: 0x8000020, + 0x16: 0x8020020, + 0x17: 0x20000, + 0x18: 0x0, + 0x19: 0x20020, + 0x1a: 0x8020000, + 0x1b: 0x8000820, + 0x1c: 0x8020820, + 0x1d: 0x20800, + 0x1e: 0x820, + 0x1f: 0x8000000, + 0x80000010: 0x20000, + 0x80000011: 0x800, + 0x80000012: 0x8020020, + 0x80000013: 0x20820, + 0x80000014: 0x20, + 0x80000015: 0x8020000, + 0x80000016: 0x8000000, + 0x80000017: 0x8000820, + 0x80000018: 0x8020820, + 0x80000019: 0x8000020, + 0x8000001a: 0x8000800, + 0x8000001b: 0x0, + 0x8000001c: 0x20800, + 0x8000001d: 0x820, + 0x8000001e: 0x20020, + 0x8000001f: 0x8020800 + } + ]; + + // Masks that select the SBOX input + var SBOX_MASK = [ + 0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, + 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f + ]; + + /** + * DES block cipher algorithm. + */ + var DES = C_algo.DES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Select 56 bits according to PC1 + var keyBits = []; + for (var i = 0; i < 56; i++) { + var keyBitPos = PC1[i] - 1; + keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1; + } + + // Assemble 16 subkeys + var subKeys = this._subKeys = []; + for (var nSubKey = 0; nSubKey < 16; nSubKey++) { + // Create subkey + var subKey = subKeys[nSubKey] = []; + + // Shortcut + var bitShift = BIT_SHIFTS[nSubKey]; + + // Select 48 bits according to PC2 + for (var i = 0; i < 24; i++) { + // Select from the left 28 key bits + subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6); + + // Select from the right 28 key bits + subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6); + } + + // Since each subkey is applied to an expanded 32-bit input, + // the subkey can be broken into 8 values scaled to 32-bits, + // which allows the key to be used without expansion + subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31); + for (var i = 1; i < 7; i++) { + subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3); + } + subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27); + } + + // Compute inverse subkeys + var invSubKeys = this._invSubKeys = []; + for (var i = 0; i < 16; i++) { + invSubKeys[i] = subKeys[15 - i]; + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._subKeys); + }, + + decryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._invSubKeys); + }, + + _doCryptBlock: function (M, offset, subKeys) { + // Get input + this._lBlock = M[offset]; + this._rBlock = M[offset + 1]; + + // Initial permutation + exchangeLR.call(this, 4, 0x0f0f0f0f); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeRL.call(this, 2, 0x33333333); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeLR.call(this, 1, 0x55555555); + + // Rounds + for (var round = 0; round < 16; round++) { + // Shortcuts + var subKey = subKeys[round]; + var lBlock = this._lBlock; + var rBlock = this._rBlock; + + // Feistel function + var f = 0; + for (var i = 0; i < 8; i++) { + f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0]; + } + this._lBlock = rBlock; + this._rBlock = lBlock ^ f; + } + + // Undo swap from last round + var t = this._lBlock; + this._lBlock = this._rBlock; + this._rBlock = t; + + // Final permutation + exchangeLR.call(this, 1, 0x55555555); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeRL.call(this, 2, 0x33333333); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeLR.call(this, 4, 0x0f0f0f0f); + + // Set output + M[offset] = this._lBlock; + M[offset + 1] = this._rBlock; + }, + + keySize: 64/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + // Swap bits across the left and right words + function exchangeLR(offset, mask) { + var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask; + this._rBlock ^= t; + this._lBlock ^= t << offset; + } + + function exchangeRL(offset, mask) { + var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask; + this._lBlock ^= t; + this._rBlock ^= t << offset; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.DES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg); + */ + C.DES = BlockCipher._createHelper(DES); + + /** + * Triple-DES block cipher algorithm. + */ + var TripleDES = C_algo.TripleDES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Create DES instances + this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2))); + this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4))); + this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6))); + }, + + encryptBlock: function (M, offset) { + this._des1.encryptBlock(M, offset); + this._des2.decryptBlock(M, offset); + this._des3.encryptBlock(M, offset); + }, + + decryptBlock: function (M, offset) { + this._des3.decryptBlock(M, offset); + this._des2.encryptBlock(M, offset); + this._des1.decryptBlock(M, offset); + }, + + keySize: 192/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg); + */ + C.TripleDES = BlockCipher._createHelper(TripleDES); + }()); + + + return CryptoJS.TripleDES; + +})); +},{"./cipher-core":52,"./core":53,"./enc-base64":54,"./evpkdf":56,"./md5":61}],84:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var X32WordArray = C_lib.WordArray; + + /** + * x64 namespace. + */ + var C_x64 = C.x64 = {}; + + /** + * A 64-bit word. + */ + var X64Word = C_x64.Word = Base.extend({ + /** + * Initializes a newly created 64-bit word. + * + * @param {number} high The high 32 bits. + * @param {number} low The low 32 bits. + * + * @example + * + * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); + */ + init: function (high, low) { + this.high = high; + this.low = low; + } + + /** + * Bitwise NOTs this word. + * + * @return {X64Word} A new x64-Word object after negating. + * + * @example + * + * var negated = x64Word.not(); + */ + // not: function () { + // var high = ~this.high; + // var low = ~this.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ANDs this word with the passed word. + * + * @param {X64Word} word The x64-Word to AND with this word. + * + * @return {X64Word} A new x64-Word object after ANDing. + * + * @example + * + * var anded = x64Word.and(anotherX64Word); + */ + // and: function (word) { + // var high = this.high & word.high; + // var low = this.low & word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to OR with this word. + * + * @return {X64Word} A new x64-Word object after ORing. + * + * @example + * + * var ored = x64Word.or(anotherX64Word); + */ + // or: function (word) { + // var high = this.high | word.high; + // var low = this.low | word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise XORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to XOR with this word. + * + * @return {X64Word} A new x64-Word object after XORing. + * + * @example + * + * var xored = x64Word.xor(anotherX64Word); + */ + // xor: function (word) { + // var high = this.high ^ word.high; + // var low = this.low ^ word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the left. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftL(25); + */ + // shiftL: function (n) { + // if (n < 32) { + // var high = (this.high << n) | (this.low >>> (32 - n)); + // var low = this.low << n; + // } else { + // var high = this.low << (n - 32); + // var low = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the right. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftR(7); + */ + // shiftR: function (n) { + // if (n < 32) { + // var low = (this.low >>> n) | (this.high << (32 - n)); + // var high = this.high >>> n; + // } else { + // var low = this.high >>> (n - 32); + // var high = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Rotates this word n bits to the left. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotL(25); + */ + // rotL: function (n) { + // return this.shiftL(n).or(this.shiftR(64 - n)); + // }, + + /** + * Rotates this word n bits to the right. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotR(7); + */ + // rotR: function (n) { + // return this.shiftR(n).or(this.shiftL(64 - n)); + // }, + + /** + * Adds this word with the passed word. + * + * @param {X64Word} word The x64-Word to add with this word. + * + * @return {X64Word} A new x64-Word object after adding. + * + * @example + * + * var added = x64Word.add(anotherX64Word); + */ + // add: function (word) { + // var low = (this.low + word.low) | 0; + // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; + // var high = (this.high + word.high + carry) | 0; + + // return X64Word.create(high, low); + // } + }); + + /** + * An array of 64-bit words. + * + * @property {Array} words The array of CryptoJS.x64.Word objects. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var X64WordArray = C_x64.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.x64.WordArray.create(); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ]); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ], 10); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 8; + } + }, + + /** + * Converts this 64-bit word array to a 32-bit word array. + * + * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. + * + * @example + * + * var x32WordArray = x64WordArray.toX32(); + */ + toX32: function () { + // Shortcuts + var x64Words = this.words; + var x64WordsLength = x64Words.length; + + // Convert + var x32Words = []; + for (var i = 0; i < x64WordsLength; i++) { + var x64Word = x64Words[i]; + x32Words.push(x64Word.high); + x32Words.push(x64Word.low); + } + + return X32WordArray.create(x32Words, this.sigBytes); + }, + + /** + * Creates a copy of this word array. + * + * @return {X64WordArray} The clone. + * + * @example + * + * var clone = x64WordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + + // Clone "words" array + var words = clone.words = this.words.slice(0); + + // Clone each X64Word object + var wordsLength = words.length; + for (var i = 0; i < wordsLength; i++) { + words[i] = words[i].clone(); + } + + return clone; + } + }); + }()); + + + return CryptoJS; + +})); +},{"./core":53}],85:[function(require,module,exports){ +/*! https://mths.be/utf8js v2.1.2 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + // Taken from https://mths.be/punycode + function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + // Taken from https://mths.be/punycode + function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; + } + + function checkScalarValue(codePoint) { + if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { + throw Error( + 'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + + ' is not a scalar value' + ); + } + } + /*--------------------------------------------------------------------------*/ + + function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); + } + + function encodeCodePoint(codePoint) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + checkScalarValue(codePoint); + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; + } + + function utf8encode(string) { + var codePoints = ucs2decode(string); + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint); + } + return byteString; + } + + /*--------------------------------------------------------------------------*/ + + function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte + throw Error('Invalid continuation byte'); + } + + function decodeSymbol() { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read first byte + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + checkScalarValue(codePoint); + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid UTF-8 detected'); + } + + var byteArray; + var byteCount; + var byteIndex; + function utf8decode(byteString) { + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol()) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); + } + + /*--------------------------------------------------------------------------*/ + + var utf8 = { + 'version': '2.1.2', + 'encode': utf8encode, + 'decode': utf8decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return utf8; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = utf8; + } else { // in Narwhal or RingoJS v0.7.0- + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + for (var key in utf8) { + hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); + } + } + } else { // in Rhino or a web browser + root.utf8 = utf8; + } + +}(this)); + +},{}],86:[function(require,module,exports){ +module.exports = XMLHttpRequest; + +},{}],"bignumber.js":[function(require,module,exports){ +/*! bignumber.js v2.0.7 https://github.com/MikeMcl/bignumber.js/LICENCE */ + +;(function (global) { + 'use strict'; + + /* + bignumber.js v2.0.7 + A JavaScript library for arbitrary-precision arithmetic. + https://github.com/MikeMcl/bignumber.js + Copyright (c) 2015 Michael Mclaughlin + MIT Expat Licence + */ + + + var BigNumber, crypto, parseNumeric, + isNumeric = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i, + mathceil = Math.ceil, + mathfloor = Math.floor, + notBool = ' not a boolean or binary digit', + roundingMode = 'rounding mode', + tooManyDigits = 'number type has more than 15 significant digits', + ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_', + BASE = 1e14, + LOG_BASE = 14, + MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 + // MAX_INT32 = 0x7fffffff, // 2^31 - 1 + POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], + SQRT_BASE = 1e7, + + /* + * The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and + * the arguments to toExponential, toFixed, toFormat, and toPrecision, beyond which an + * exception is thrown (if ERRORS is true). + */ + MAX = 1E9; // 0 to MAX_INT32 + + + /* + * Create and return a BigNumber constructor. + */ + function another(configObj) { + var div, + + // id tracks the caller function, so its name can be included in error messages. + id = 0, + P = BigNumber.prototype, + ONE = new BigNumber(1), + + + /********************************* EDITABLE DEFAULTS **********************************/ + + + /* + * The default values below must be integers within the inclusive ranges stated. + * The values can also be changed at run-time using BigNumber.config. + */ + + // The maximum number of decimal places for operations involving division. + DECIMAL_PLACES = 20, // 0 to MAX + + /* + * The rounding mode used when rounding to the above decimal places, and when using + * toExponential, toFixed, toFormat and toPrecision, and round (default value). + * UP 0 Away from zero. + * DOWN 1 Towards zero. + * CEIL 2 Towards +Infinity. + * FLOOR 3 Towards -Infinity. + * HALF_UP 4 Towards nearest neighbour. If equidistant, up. + * HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. + * HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. + * HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. + * HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. + */ + ROUNDING_MODE = 4, // 0 to 8 + + // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] + + // The exponent value at and beneath which toString returns exponential notation. + // Number type: -7 + TO_EXP_NEG = -7, // 0 to -MAX + + // The exponent value at and above which toString returns exponential notation. + // Number type: 21 + TO_EXP_POS = 21, // 0 to MAX + + // RANGE : [MIN_EXP, MAX_EXP] + + // The minimum exponent value, beneath which underflow to zero occurs. + // Number type: -324 (5e-324) + MIN_EXP = -1e7, // -1 to -MAX + + // The maximum exponent value, above which overflow to Infinity occurs. + // Number type: 308 (1.7976931348623157e+308) + // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. + MAX_EXP = 1e7, // 1 to MAX + + // Whether BigNumber Errors are ever thrown. + ERRORS = true, // true or false + + // Change to intValidatorNoErrors if ERRORS is false. + isValidInt = intValidatorWithErrors, // intValidatorWithErrors/intValidatorNoErrors + + // Whether to use cryptographically-secure random number generation, if available. + CRYPTO = false, // true or false + + /* + * The modulo mode used when calculating the modulus: a mod n. + * The quotient (q = a / n) is calculated according to the corresponding rounding mode. + * The remainder (r) is calculated as: r = a - n * q. + * + * UP 0 The remainder is positive if the dividend is negative, else is negative. + * DOWN 1 The remainder has the same sign as the dividend. + * This modulo mode is commonly known as 'truncated division' and is + * equivalent to (a % n) in JavaScript. + * FLOOR 3 The remainder has the same sign as the divisor (Python %). + * HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. + * EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). + * The remainder is always positive. + * + * The truncated division, floored division, Euclidian division and IEEE 754 remainder + * modes are commonly used for the modulus operation. + * Although the other rounding modes can also be used, they may not give useful results. + */ + MODULO_MODE = 1, // 0 to 9 + + // The maximum number of significant digits of the result of the toPower operation. + // If POW_PRECISION is 0, there will be unlimited significant digits. + POW_PRECISION = 100, // 0 to MAX + + // The format specification used by the BigNumber.prototype.toFormat method. + FORMAT = { + decimalSeparator: '.', + groupSeparator: ',', + groupSize: 3, + secondaryGroupSize: 0, + fractionGroupSeparator: '\xA0', // non-breaking space + fractionGroupSize: 0 + }; + + + /******************************************************************************************/ + + + // CONSTRUCTOR + + + /* + * The BigNumber constructor and exported function. + * Create and return a new instance of a BigNumber object. + * + * n {number|string|BigNumber} A numeric value. + * [b] {number} The base of n. Integer, 2 to 64 inclusive. + */ + function BigNumber( n, b ) { + var c, e, i, num, len, str, + x = this; + + // Enable constructor usage without new. + if ( !( x instanceof BigNumber ) ) { + + // 'BigNumber() constructor call without new: {n}' + if (ERRORS) raise( 26, 'constructor call without new', n ); + return new BigNumber( n, b ); + } + + // 'new BigNumber() base not an integer: {b}' + // 'new BigNumber() base out of range: {b}' + if ( b == null || !isValidInt( b, 2, 64, id, 'base' ) ) { + + // Duplicate. + if ( n instanceof BigNumber ) { + x.s = n.s; + x.e = n.e; + x.c = ( n = n.c ) ? n.slice() : n; + id = 0; + return; + } + + if ( ( num = typeof n == 'number' ) && n * 0 == 0 ) { + x.s = 1 / n < 0 ? ( n = -n, -1 ) : 1; + + // Fast path for integers. + if ( n === ~~n ) { + for ( e = 0, i = n; i >= 10; i /= 10, e++ ); + x.e = e; + x.c = [n]; + id = 0; + return; + } + + str = n + ''; + } else { + if ( !isNumeric.test( str = n + '' ) ) return parseNumeric( x, str, num ); + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + } else { + b = b | 0; + str = n + ''; + + // Ensure return value is rounded to DECIMAL_PLACES as with other bases. + // Allow exponential notation to be used with base 10 argument. + if ( b == 10 ) { + x = new BigNumber( n instanceof BigNumber ? n : str ); + return round( x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE ); + } + + // Avoid potential interpretation of Infinity and NaN as base 44+ values. + // Any number in exponential form will fail due to the [Ee][+-]. + if ( ( num = typeof n == 'number' ) && n * 0 != 0 || + !( new RegExp( '^-?' + ( c = '[' + ALPHABET.slice( 0, b ) + ']+' ) + + '(?:\\.' + c + ')?$',b < 37 ? 'i' : '' ) ).test(str) ) { + return parseNumeric( x, str, num, b ); + } + + if (num) { + x.s = 1 / n < 0 ? ( str = str.slice(1), -1 ) : 1; + + if ( ERRORS && str.replace( /^0\.0*|\./, '' ).length > 15 ) { + + // 'new BigNumber() number type has more than 15 significant digits: {n}' + raise( id, tooManyDigits, n ); + } + + // Prevent later check for length on converted number. + num = false; + } else { + x.s = str.charCodeAt(0) === 45 ? ( str = str.slice(1), -1 ) : 1; + } + + str = convertBase( str, 10, b, x.s ); + } + + // Decimal point? + if ( ( e = str.indexOf('.') ) > -1 ) str = str.replace( '.', '' ); + + // Exponential form? + if ( ( i = str.search( /e/i ) ) > 0 ) { + + // Determine exponent. + if ( e < 0 ) e = i; + e += +str.slice( i + 1 ); + str = str.substring( 0, i ); + } else if ( e < 0 ) { + + // Integer. + e = str.length; + } + + // Determine leading zeros. + for ( i = 0; str.charCodeAt(i) === 48; i++ ); + + // Determine trailing zeros. + for ( len = str.length; str.charCodeAt(--len) === 48; ); + str = str.slice( i, len + 1 ); + + if (str) { + len = str.length; + + // Disallow numbers with over 15 significant digits if number type. + // 'new BigNumber() number type has more than 15 significant digits: {n}' + if ( num && ERRORS && len > 15 ) raise( id, tooManyDigits, x.s * n ); + + e = e - i - 1; + + // Overflow? + if ( e > MAX_EXP ) { + + // Infinity. + x.c = x.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + x.c = [ x.e = 0 ]; + } else { + x.e = e; + x.c = []; + + // Transform base + + // e is the base 10 exponent. + // i is where to slice str to get the first element of the coefficient array. + i = ( e + 1 ) % LOG_BASE; + if ( e < 0 ) i += LOG_BASE; + + if ( i < len ) { + if (i) x.c.push( +str.slice( 0, i ) ); + + for ( len -= LOG_BASE; i < len; ) { + x.c.push( +str.slice( i, i += LOG_BASE ) ); + } + + str = str.slice(i); + i = LOG_BASE - str.length; + } else { + i -= len; + } + + for ( ; i--; str += '0' ); + x.c.push( +str ); + } + } else { + + // Zero. + x.c = [ x.e = 0 ]; + } + + id = 0; + } + + + // CONSTRUCTOR PROPERTIES + + + BigNumber.another = another; + + BigNumber.ROUND_UP = 0; + BigNumber.ROUND_DOWN = 1; + BigNumber.ROUND_CEIL = 2; + BigNumber.ROUND_FLOOR = 3; + BigNumber.ROUND_HALF_UP = 4; + BigNumber.ROUND_HALF_DOWN = 5; + BigNumber.ROUND_HALF_EVEN = 6; + BigNumber.ROUND_HALF_CEIL = 7; + BigNumber.ROUND_HALF_FLOOR = 8; + BigNumber.EUCLID = 9; + + + /* + * Configure infrequently-changing library-wide settings. + * + * Accept an object or an argument list, with one or many of the following properties or + * parameters respectively: + * + * DECIMAL_PLACES {number} Integer, 0 to MAX inclusive + * ROUNDING_MODE {number} Integer, 0 to 8 inclusive + * EXPONENTIAL_AT {number|number[]} Integer, -MAX to MAX inclusive or + * [integer -MAX to 0 incl., 0 to MAX incl.] + * RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + * [integer -MAX to -1 incl., integer 1 to MAX incl.] + * ERRORS {boolean|number} true, false, 1 or 0 + * CRYPTO {boolean|number} true, false, 1 or 0 + * MODULO_MODE {number} 0 to 9 inclusive + * POW_PRECISION {number} 0 to MAX inclusive + * FORMAT {object} See BigNumber.prototype.toFormat + * decimalSeparator {string} + * groupSeparator {string} + * groupSize {number} + * secondaryGroupSize {number} + * fractionGroupSeparator {string} + * fractionGroupSize {number} + * + * (The values assigned to the above FORMAT object properties are not checked for validity.) + * + * E.g. + * BigNumber.config(20, 4) is equivalent to + * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) + * + * Ignore properties/parameters set to null or undefined. + * Return an object with the properties current values. + */ + BigNumber.config = function () { + var v, p, + i = 0, + r = {}, + a = arguments, + o = a[0], + has = o && typeof o == 'object' + ? function () { if ( o.hasOwnProperty(p) ) return ( v = o[p] ) != null; } + : function () { if ( a.length > i ) return ( v = a[i++] ) != null; }; + + // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. + // 'config() DECIMAL_PLACES not an integer: {v}' + // 'config() DECIMAL_PLACES out of range: {v}' + if ( has( p = 'DECIMAL_PLACES' ) && isValidInt( v, 0, MAX, 2, p ) ) { + DECIMAL_PLACES = v | 0; + } + r[p] = DECIMAL_PLACES; + + // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. + // 'config() ROUNDING_MODE not an integer: {v}' + // 'config() ROUNDING_MODE out of range: {v}' + if ( has( p = 'ROUNDING_MODE' ) && isValidInt( v, 0, 8, 2, p ) ) { + ROUNDING_MODE = v | 0; + } + r[p] = ROUNDING_MODE; + + // EXPONENTIAL_AT {number|number[]} + // Integer, -MAX to MAX inclusive or [integer -MAX to 0 inclusive, 0 to MAX inclusive]. + // 'config() EXPONENTIAL_AT not an integer: {v}' + // 'config() EXPONENTIAL_AT out of range: {v}' + if ( has( p = 'EXPONENTIAL_AT' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, 0, 2, p ) && isValidInt( v[1], 0, MAX, 2, p ) ) { + TO_EXP_NEG = v[0] | 0; + TO_EXP_POS = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + TO_EXP_NEG = -( TO_EXP_POS = ( v < 0 ? -v : v ) | 0 ); + } + } + r[p] = [ TO_EXP_NEG, TO_EXP_POS ]; + + // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. + // 'config() RANGE not an integer: {v}' + // 'config() RANGE cannot be zero: {v}' + // 'config() RANGE out of range: {v}' + if ( has( p = 'RANGE' ) ) { + + if ( isArray(v) ) { + if ( isValidInt( v[0], -MAX, -1, 2, p ) && isValidInt( v[1], 1, MAX, 2, p ) ) { + MIN_EXP = v[0] | 0; + MAX_EXP = v[1] | 0; + } + } else if ( isValidInt( v, -MAX, MAX, 2, p ) ) { + if ( v | 0 ) MIN_EXP = -( MAX_EXP = ( v < 0 ? -v : v ) | 0 ); + else if (ERRORS) raise( 2, p + ' cannot be zero', v ); + } + } + r[p] = [ MIN_EXP, MAX_EXP ]; + + // ERRORS {boolean|number} true, false, 1 or 0. + // 'config() ERRORS not a boolean or binary digit: {v}' + if ( has( p = 'ERRORS' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + id = 0; + isValidInt = ( ERRORS = !!v ) ? intValidatorWithErrors : intValidatorNoErrors; + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = ERRORS; + + // CRYPTO {boolean|number} true, false, 1 or 0. + // 'config() CRYPTO not a boolean or binary digit: {v}' + // 'config() crypto unavailable: {crypto}' + if ( has( p = 'CRYPTO' ) ) { + + if ( v === !!v || v === 1 || v === 0 ) { + CRYPTO = !!( v && crypto && typeof crypto == 'object' ); + if ( v && !CRYPTO && ERRORS ) raise( 2, 'crypto unavailable', crypto ); + } else if (ERRORS) { + raise( 2, p + notBool, v ); + } + } + r[p] = CRYPTO; + + // MODULO_MODE {number} Integer, 0 to 9 inclusive. + // 'config() MODULO_MODE not an integer: {v}' + // 'config() MODULO_MODE out of range: {v}' + if ( has( p = 'MODULO_MODE' ) && isValidInt( v, 0, 9, 2, p ) ) { + MODULO_MODE = v | 0; + } + r[p] = MODULO_MODE; + + // POW_PRECISION {number} Integer, 0 to MAX inclusive. + // 'config() POW_PRECISION not an integer: {v}' + // 'config() POW_PRECISION out of range: {v}' + if ( has( p = 'POW_PRECISION' ) && isValidInt( v, 0, MAX, 2, p ) ) { + POW_PRECISION = v | 0; + } + r[p] = POW_PRECISION; + + // FORMAT {object} + // 'config() FORMAT not an object: {v}' + if ( has( p = 'FORMAT' ) ) { + + if ( typeof v == 'object' ) { + FORMAT = v; + } else if (ERRORS) { + raise( 2, p + ' not an object', v ); + } + } + r[p] = FORMAT; + + return r; + }; + + + /* + * Return a new BigNumber whose value is the maximum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.max = function () { return maxOrMin( arguments, P.lt ); }; + + + /* + * Return a new BigNumber whose value is the minimum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.min = function () { return maxOrMin( arguments, P.gt ); }; + + + /* + * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, + * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing + * zeros are produced). + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * + * 'random() decimal places not an integer: {dp}' + * 'random() decimal places out of range: {dp}' + * 'random() crypto unavailable: {crypto}' + */ + BigNumber.random = (function () { + var pow2_53 = 0x20000000000000; + + // Return a 53 bit integer n, where 0 <= n < 9007199254740992. + // Check if Math.random() produces more than 32 bits of randomness. + // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. + // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. + var random53bitInt = (Math.random() * pow2_53) & 0x1fffff + ? function () { return mathfloor( Math.random() * pow2_53 ); } + : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + + (Math.random() * 0x800000 | 0); }; + + return function (dp) { + var a, b, e, k, v, + i = 0, + c = [], + rand = new BigNumber(ONE); + + dp = dp == null || !isValidInt( dp, 0, MAX, 14 ) ? DECIMAL_PLACES : dp | 0; + k = mathceil( dp / LOG_BASE ); + + if (CRYPTO) { + + // Browsers supporting crypto.getRandomValues. + if ( crypto && crypto.getRandomValues ) { + + a = crypto.getRandomValues( new Uint32Array( k *= 2 ) ); + + for ( ; i < k; ) { + + // 53 bits: + // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) + // 11111 11111111 11111111 11111111 11100000 00000000 00000000 + // ((Math.pow(2, 32) - 1) >>> 11).toString(2) + // 11111 11111111 11111111 + // 0x20000 is 2^21. + v = a[i] * 0x20000 + (a[i + 1] >>> 11); + + // Rejection sampling: + // 0 <= v < 9007199254740992 + // Probability that v >= 9e15, is + // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 + if ( v >= 9e15 ) { + b = crypto.getRandomValues( new Uint32Array(2) ); + a[i] = b[0]; + a[i + 1] = b[1]; + } else { + + // 0 <= v <= 8999999999999999 + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 2; + } + } + i = k / 2; + + // Node.js supporting crypto.randomBytes. + } else if ( crypto && crypto.randomBytes ) { + + // buffer + a = crypto.randomBytes( k *= 7 ); + + for ( ; i < k; ) { + + // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 + // 0x100000000 is 2^32, 0x1000000 is 2^24 + // 11111 11111111 11111111 11111111 11111111 11111111 11111111 + // 0 <= v < 9007199254740992 + v = ( ( a[i] & 31 ) * 0x1000000000000 ) + ( a[i + 1] * 0x10000000000 ) + + ( a[i + 2] * 0x100000000 ) + ( a[i + 3] * 0x1000000 ) + + ( a[i + 4] << 16 ) + ( a[i + 5] << 8 ) + a[i + 6]; + + if ( v >= 9e15 ) { + crypto.randomBytes(7).copy( a, i ); + } else { + + // 0 <= (v % 1e14) <= 99999999999999 + c.push( v % 1e14 ); + i += 7; + } + } + i = k / 7; + } else if (ERRORS) { + raise( 14, 'crypto unavailable', crypto ); + } + } + + // Use Math.random: CRYPTO is false or crypto is unavailable and ERRORS is false. + if (!i) { + + for ( ; i < k; ) { + v = random53bitInt(); + if ( v < 9e15 ) c[i++] = v % 1e14; + } + } + + k = c[--i]; + dp %= LOG_BASE; + + // Convert trailing digits to zeros according to dp. + if ( k && dp ) { + v = POWS_TEN[LOG_BASE - dp]; + c[i] = mathfloor( k / v ) * v; + } + + // Remove trailing elements which are zero. + for ( ; c[i] === 0; c.pop(), i-- ); + + // Zero? + if ( i < 0 ) { + c = [ e = 0 ]; + } else { + + // Remove leading elements which are zero and adjust exponent accordingly. + for ( e = -1 ; c[0] === 0; c.shift(), e -= LOG_BASE); + + // Count the digits of the first element of c to determine leading zeros, and... + for ( i = 1, v = c[0]; v >= 10; v /= 10, i++); + + // adjust the exponent accordingly. + if ( i < LOG_BASE ) e -= LOG_BASE - i; + } + + rand.e = e; + rand.c = c; + return rand; + }; + })(); + + + // PRIVATE FUNCTIONS + + + // Convert a numeric string of baseIn to a numeric string of baseOut. + function convertBase( str, baseOut, baseIn, sign ) { + var d, e, k, r, x, xc, y, + i = str.indexOf( '.' ), + dp = DECIMAL_PLACES, + rm = ROUNDING_MODE; + + if ( baseIn < 37 ) str = str.toLowerCase(); + + // Non-integer. + if ( i >= 0 ) { + k = POW_PRECISION; + + // Unlimited precision. + POW_PRECISION = 0; + str = str.replace( '.', '' ); + y = new BigNumber(baseIn); + x = y.pow( str.length - i ); + POW_PRECISION = k; + + // Convert str as if an integer, then restore the fraction part by dividing the + // result by its base raised to a power. + y.c = toBaseOut( toFixedPoint( coeffToString( x.c ), x.e ), 10, baseOut ); + y.e = y.c.length; + } + + // Convert the number as integer. + xc = toBaseOut( str, baseIn, baseOut ); + e = k = xc.length; + + // Remove trailing zeros. + for ( ; xc[--k] == 0; xc.pop() ); + if ( !xc[0] ) return '0'; + + if ( i < 0 ) { + --e; + } else { + x.c = xc; + x.e = e; + + // sign is needed for correct rounding. + x.s = sign; + x = div( x, y, dp, rm, baseOut ); + xc = x.c; + r = x.r; + e = x.e; + } + + d = e + dp + 1; + + // The rounding digit, i.e. the digit to the right of the digit that may be rounded up. + i = xc[d]; + k = baseOut / 2; + r = r || d < 0 || xc[d + 1] != null; + + r = rm < 4 ? ( i != null || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : i > k || i == k &&( rm == 4 || r || rm == 6 && xc[d - 1] & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( d < 1 || !xc[0] ) { + + // 1^-dp or 0. + str = r ? toFixedPoint( '1', -dp ) : '0'; + } else { + xc.length = d; + + if (r) { + + // Rounding up may mean the previous digit has to be rounded up and so on. + for ( --baseOut; ++xc[--d] > baseOut; ) { + xc[d] = 0; + + if ( !d ) { + ++e; + xc.unshift(1); + } + } + } + + // Determine trailing zeros. + for ( k = xc.length; !xc[--k]; ); + + // E.g. [4, 11, 15] becomes 4bf. + for ( i = 0, str = ''; i <= k; str += ALPHABET.charAt( xc[i++] ) ); + str = toFixedPoint( str, e ); + } + + // The caller will add the sign. + return str; + } + + + // Perform division in the specified base. Called by div and convertBase. + div = (function () { + + // Assume non-zero x and k. + function multiply( x, k, base ) { + var m, temp, xlo, xhi, + carry = 0, + i = x.length, + klo = k % SQRT_BASE, + khi = k / SQRT_BASE | 0; + + for ( x = x.slice(); i--; ) { + xlo = x[i] % SQRT_BASE; + xhi = x[i] / SQRT_BASE | 0; + m = khi * xlo + xhi * klo; + temp = klo * xlo + ( ( m % SQRT_BASE ) * SQRT_BASE ) + carry; + carry = ( temp / base | 0 ) + ( m / SQRT_BASE | 0 ) + khi * xhi; + x[i] = temp % base; + } + + if (carry) x.unshift(carry); + + return x; + } + + function compare( a, b, aL, bL ) { + var i, cmp; + + if ( aL != bL ) { + cmp = aL > bL ? 1 : -1; + } else { + + for ( i = cmp = 0; i < aL; i++ ) { + + if ( a[i] != b[i] ) { + cmp = a[i] > b[i] ? 1 : -1; + break; + } + } + } + return cmp; + } + + function subtract( a, b, aL, base ) { + var i = 0; + + // Subtract b from a. + for ( ; aL--; ) { + a[aL] -= i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + + // Remove leading zeros. + for ( ; !a[0] && a.length > 1; a.shift() ); + } + + // x: dividend, y: divisor. + return function ( x, y, dp, rm, base ) { + var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, + yL, yz, + s = x.s == y.s ? 1 : -1, + xc = x.c, + yc = y.c; + + // Either NaN, Infinity or 0? + if ( !xc || !xc[0] || !yc || !yc[0] ) { + + return new BigNumber( + + // Return NaN if either NaN, or both Infinity or 0. + !x.s || !y.s || ( xc ? yc && xc[0] == yc[0] : !yc ) ? NaN : + + // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. + xc && xc[0] == 0 || !yc ? s * 0 : s / 0 + ); + } + + q = new BigNumber(s); + qc = q.c = []; + e = x.e - y.e; + s = dp + e + 1; + + if ( !base ) { + base = BASE; + e = bitFloor( x.e / LOG_BASE ) - bitFloor( y.e / LOG_BASE ); + s = s / LOG_BASE | 0; + } + + // Result exponent may be one less then the current value of e. + // The coefficients of the BigNumbers from convertBase may have trailing zeros. + for ( i = 0; yc[i] == ( xc[i] || 0 ); i++ ); + if ( yc[i] > ( xc[i] || 0 ) ) e--; + + if ( s < 0 ) { + qc.push(1); + more = true; + } else { + xL = xc.length; + yL = yc.length; + i = 0; + s += 2; + + // Normalise xc and yc so highest order digit of yc is >= base / 2. + + n = mathfloor( base / ( yc[0] + 1 ) ); + + // Not necessary, but to handle odd bases where yc[0] == ( base / 2 ) - 1. + // if ( n > 1 || n++ == 1 && yc[0] < base / 2 ) { + if ( n > 1 ) { + yc = multiply( yc, n, base ); + xc = multiply( xc, n, base ); + yL = yc.length; + xL = xc.length; + } + + xi = yL; + rem = xc.slice( 0, yL ); + remL = rem.length; + + // Add zeros to make remainder as long as divisor. + for ( ; remL < yL; rem[remL++] = 0 ); + yz = yc.slice(); + yz.unshift(0); + yc0 = yc[0]; + if ( yc[1] >= base / 2 ) yc0++; + // Not necessary, but to prevent trial digit n > base, when using base 3. + // else if ( base == 3 && yc0 == 1 ) yc0 = 1 + 1e-15; + + do { + n = 0; + + // Compare divisor and remainder. + cmp = compare( yc, rem, yL, remL ); + + // If divisor < remainder. + if ( cmp < 0 ) { + + // Calculate trial digit, n. + + rem0 = rem[0]; + if ( yL != remL ) rem0 = rem0 * base + ( rem[1] || 0 ); + + // n is how many times the divisor goes into the current remainder. + n = mathfloor( rem0 / yc0 ); + + // Algorithm: + // 1. product = divisor * trial digit (n) + // 2. if product > remainder: product -= divisor, n-- + // 3. remainder -= product + // 4. if product was < remainder at 2: + // 5. compare new remainder and divisor + // 6. If remainder > divisor: remainder -= divisor, n++ + + if ( n > 1 ) { + + // n may be > base only when base is 3. + if (n >= base) n = base - 1; + + // product = divisor * trial digit. + prod = multiply( yc, n, base ); + prodL = prod.length; + remL = rem.length; + + // Compare product and remainder. + // If product > remainder. + // Trial digit n too high. + // n is 1 too high about 5% of the time, and is not known to have + // ever been more than 1 too high. + while ( compare( prod, rem, prodL, remL ) == 1 ) { + n--; + + // Subtract divisor from product. + subtract( prod, yL < prodL ? yz : yc, prodL, base ); + prodL = prod.length; + cmp = 1; + } + } else { + + // n is 0 or 1, cmp is -1. + // If n is 0, there is no need to compare yc and rem again below, + // so change cmp to 1 to avoid it. + // If n is 1, leave cmp as -1, so yc and rem are compared again. + if ( n == 0 ) { + + // divisor < remainder, so n must be at least 1. + cmp = n = 1; + } + + // product = divisor + prod = yc.slice(); + prodL = prod.length; + } + + if ( prodL < remL ) prod.unshift(0); + + // Subtract product from remainder. + subtract( rem, prod, remL, base ); + remL = rem.length; + + // If product was < remainder. + if ( cmp == -1 ) { + + // Compare divisor and new remainder. + // If divisor < new remainder, subtract divisor from remainder. + // Trial digit n too low. + // n is 1 too low about 5% of the time, and very rarely 2 too low. + while ( compare( yc, rem, yL, remL ) < 1 ) { + n++; + + // Subtract divisor from remainder. + subtract( rem, yL < remL ? yz : yc, remL, base ); + remL = rem.length; + } + } + } else if ( cmp === 0 ) { + n++; + rem = [0]; + } // else cmp === 1 and n will be 0 + + // Add the next digit, n, to the result array. + qc[i++] = n; + + // Update the remainder. + if ( rem[0] ) { + rem[remL++] = xc[xi] || 0; + } else { + rem = [ xc[xi] ]; + remL = 1; + } + } while ( ( xi++ < xL || rem[0] != null ) && s-- ); + + more = rem[0] != null; + + // Leading zero? + if ( !qc[0] ) qc.shift(); + } + + if ( base == BASE ) { + + // To calculate q.e, first get the number of digits of qc[0]. + for ( i = 1, s = qc[0]; s >= 10; s /= 10, i++ ); + round( q, dp + ( q.e = i + e * LOG_BASE - 1 ) + 1, rm, more ); + + // Caller is convertBase. + } else { + q.e = e; + q.r = +more; + } + + return q; + }; + })(); + + + /* + * Return a string representing the value of BigNumber n in fixed-point or exponential + * notation rounded to the specified decimal places or significant digits. + * + * n is a BigNumber. + * i is the index of the last digit required (i.e. the digit that may be rounded up). + * rm is the rounding mode. + * caller is caller id: toExponential 19, toFixed 20, toFormat 21, toPrecision 24. + */ + function format( n, i, rm, caller ) { + var c0, e, ne, len, str; + + rm = rm != null && isValidInt( rm, 0, 8, caller, roundingMode ) + ? rm | 0 : ROUNDING_MODE; + + if ( !n.c ) return n.toString(); + c0 = n.c[0]; + ne = n.e; + + if ( i == null ) { + str = coeffToString( n.c ); + str = caller == 19 || caller == 24 && ne <= TO_EXP_NEG + ? toExponential( str, ne ) + : toFixedPoint( str, ne ); + } else { + n = round( new BigNumber(n), i, rm ); + + // n.e may have changed if the value was rounded up. + e = n.e; + + str = coeffToString( n.c ); + len = str.length; + + // toPrecision returns exponential notation if the number of significant digits + // specified is less than the number of digits necessary to represent the integer + // part of the value in fixed-point notation. + + // Exponential notation. + if ( caller == 19 || caller == 24 && ( i <= e || e <= TO_EXP_NEG ) ) { + + // Append zeros? + for ( ; len < i; str += '0', len++ ); + str = toExponential( str, e ); + + // Fixed-point notation. + } else { + i -= ne; + str = toFixedPoint( str, e ); + + // Append zeros? + if ( e + 1 > len ) { + if ( --i > 0 ) for ( str += '.'; i--; str += '0' ); + } else { + i += e - len; + if ( i > 0 ) { + if ( e + 1 == len ) str += '.'; + for ( ; i--; str += '0' ); + } + } + } + } + + return n.s < 0 && c0 ? '-' + str : str; + } + + + // Handle BigNumber.max and BigNumber.min. + function maxOrMin( args, method ) { + var m, n, + i = 0; + + if ( isArray( args[0] ) ) args = args[0]; + m = new BigNumber( args[0] ); + + for ( ; ++i < args.length; ) { + n = new BigNumber( args[i] ); + + // If any number is NaN, return NaN. + if ( !n.s ) { + m = n; + break; + } else if ( method.call( m, n ) ) { + m = n; + } + } + + return m; + } + + + /* + * Return true if n is an integer in range, otherwise throw. + * Use for argument validation when ERRORS is true. + */ + function intValidatorWithErrors( n, min, max, caller, name ) { + if ( n < min || n > max || n != truncate(n) ) { + raise( caller, ( name || 'decimal places' ) + + ( n < min || n > max ? ' out of range' : ' not an integer' ), n ); + } + + return true; + } + + + /* + * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. + * Called by minus, plus and times. + */ + function normalise( n, c, e ) { + var i = 1, + j = c.length; + + // Remove trailing zeros. + for ( ; !c[--j]; c.pop() ); + + // Calculate the base 10 exponent. First get the number of digits of c[0]. + for ( j = c[0]; j >= 10; j /= 10, i++ ); + + // Overflow? + if ( ( e = i + e * LOG_BASE - 1 ) > MAX_EXP ) { + + // Infinity. + n.c = n.e = null; + + // Underflow? + } else if ( e < MIN_EXP ) { + + // Zero. + n.c = [ n.e = 0 ]; + } else { + n.e = e; + n.c = c; + } + + return n; + } + + + // Handle values that fail the validity test in BigNumber. + parseNumeric = (function () { + var basePrefix = /^(-?)0([xbo])/i, + dotAfter = /^([^.]+)\.$/, + dotBefore = /^\.([^.]+)$/, + isInfinityOrNaN = /^-?(Infinity|NaN)$/, + whitespaceOrPlus = /^\s*\+|^\s+|\s+$/g; + + return function ( x, str, num, b ) { + var base, + s = num ? str : str.replace( whitespaceOrPlus, '' ); + + // No exception on ±Infinity or NaN. + if ( isInfinityOrNaN.test(s) ) { + x.s = isNaN(s) ? null : s < 0 ? -1 : 1; + } else { + if ( !num ) { + + // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i + s = s.replace( basePrefix, function ( m, p1, p2 ) { + base = ( p2 = p2.toLowerCase() ) == 'x' ? 16 : p2 == 'b' ? 2 : 8; + return !b || b == base ? p1 : m; + }); + + if (b) { + base = b; + + // E.g. '1.' to '1', '.1' to '0.1' + s = s.replace( dotAfter, '$1' ).replace( dotBefore, '0.$1' ); + } + + if ( str != s ) return new BigNumber( s, base ); + } + + // 'new BigNumber() not a number: {n}' + // 'new BigNumber() not a base {b} number: {n}' + if (ERRORS) raise( id, 'not a' + ( b ? ' base ' + b : '' ) + ' number', str ); + x.s = null; + } + + x.c = x.e = null; + id = 0; + } + })(); + + + // Throw a BigNumber Error. + function raise( caller, msg, val ) { + var error = new Error( [ + 'new BigNumber', // 0 + 'cmp', // 1 + 'config', // 2 + 'div', // 3 + 'divToInt', // 4 + 'eq', // 5 + 'gt', // 6 + 'gte', // 7 + 'lt', // 8 + 'lte', // 9 + 'minus', // 10 + 'mod', // 11 + 'plus', // 12 + 'precision', // 13 + 'random', // 14 + 'round', // 15 + 'shift', // 16 + 'times', // 17 + 'toDigits', // 18 + 'toExponential', // 19 + 'toFixed', // 20 + 'toFormat', // 21 + 'toFraction', // 22 + 'pow', // 23 + 'toPrecision', // 24 + 'toString', // 25 + 'BigNumber' // 26 + ][caller] + '() ' + msg + ': ' + val ); + + error.name = 'BigNumber Error'; + id = 0; + throw error; + } + + + /* + * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. + * If r is truthy, it is known that there are more digits after the rounding digit. + */ + function round( x, sd, rm, r ) { + var d, i, j, k, n, ni, rd, + xc = x.c, + pows10 = POWS_TEN; + + // if x is not Infinity or NaN... + if (xc) { + + // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. + // n is a base 1e14 number, the value of the element of array x.c containing rd. + // ni is the index of n within x.c. + // d is the number of digits of n. + // i is the index of rd within n including leading zeros. + // j is the actual index of rd within n (if < 0, rd is a leading zero). + out: { + + // Get the number of digits of the first element of xc. + for ( d = 1, k = xc[0]; k >= 10; k /= 10, d++ ); + i = sd - d; + + // If the rounding digit is in the first element of xc... + if ( i < 0 ) { + i += LOG_BASE; + j = sd; + n = xc[ ni = 0 ]; + + // Get the rounding digit at index j of n. + rd = n / pows10[ d - j - 1 ] % 10 | 0; + } else { + ni = mathceil( ( i + 1 ) / LOG_BASE ); + + if ( ni >= xc.length ) { + + if (r) { + + // Needed by sqrt. + for ( ; xc.length <= ni; xc.push(0) ); + n = rd = 0; + d = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + break out; + } + } else { + n = k = xc[ni]; + + // Get the number of digits of n. + for ( d = 1; k >= 10; k /= 10, d++ ); + + // Get the index of rd within n. + i %= LOG_BASE; + + // Get the index of rd within n, adjusted for leading zeros. + // The number of leading zeros of n is given by LOG_BASE - d. + j = i - LOG_BASE + d; + + // Get the rounding digit at index j of n. + rd = j < 0 ? 0 : n / pows10[ d - j - 1 ] % 10 | 0; + } + } + + r = r || sd < 0 || + + // Are there any non-zero digits after the rounding digit? + // The expression n % pows10[ d - j - 1 ] returns all digits of n to the right + // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. + xc[ni + 1] != null || ( j < 0 ? n : n % pows10[ d - j - 1 ] ); + + r = rm < 4 + ? ( rd || r ) && ( rm == 0 || rm == ( x.s < 0 ? 3 : 2 ) ) + : rd > 5 || rd == 5 && ( rm == 4 || r || rm == 6 && + + // Check whether the digit to the left of the rounding digit is odd. + ( ( i > 0 ? j > 0 ? n / pows10[ d - j ] : 0 : xc[ni - 1] ) % 10 ) & 1 || + rm == ( x.s < 0 ? 8 : 7 ) ); + + if ( sd < 1 || !xc[0] ) { + xc.length = 0; + + if (r) { + + // Convert sd to decimal places. + sd -= x.e + 1; + + // 1, 0.1, 0.01, 0.001, 0.0001 etc. + xc[0] = pows10[ sd % LOG_BASE ]; + x.e = -sd || 0; + } else { + + // Zero. + xc[0] = x.e = 0; + } + + return x; + } + + // Remove excess digits. + if ( i == 0 ) { + xc.length = ni; + k = 1; + ni--; + } else { + xc.length = ni + 1; + k = pows10[ LOG_BASE - i ]; + + // E.g. 56700 becomes 56000 if 7 is the rounding digit. + // j > 0 means i > number of leading zeros of n. + xc[ni] = j > 0 ? mathfloor( n / pows10[ d - j ] % pows10[j] ) * k : 0; + } + + // Round up? + if (r) { + + for ( ; ; ) { + + // If the digit to be rounded up is in the first element of xc... + if ( ni == 0 ) { + + // i will be the length of xc[0] before k is added. + for ( i = 1, j = xc[0]; j >= 10; j /= 10, i++ ); + j = xc[0] += k; + for ( k = 1; j >= 10; j /= 10, k++ ); + + // if i != k the length has increased. + if ( i != k ) { + x.e++; + if ( xc[0] == BASE ) xc[0] = 1; + } + + break; + } else { + xc[ni] += k; + if ( xc[ni] != BASE ) break; + xc[ni--] = 0; + k = 1; + } + } + } + + // Remove trailing zeros. + for ( i = xc.length; xc[--i] === 0; xc.pop() ); + } + + // Overflow? Infinity. + if ( x.e > MAX_EXP ) { + x.c = x.e = null; + + // Underflow? Zero. + } else if ( x.e < MIN_EXP ) { + x.c = [ x.e = 0 ]; + } + } + + return x; + } + + + // PROTOTYPE/INSTANCE METHODS + + + /* + * Return a new BigNumber whose value is the absolute value of this BigNumber. + */ + P.absoluteValue = P.abs = function () { + var x = new BigNumber(this); + if ( x.s < 0 ) x.s = 1; + return x; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of Infinity. + */ + P.ceil = function () { + return round( new BigNumber(this), this.e + 1, 2 ); + }; + + + /* + * Return + * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), + * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), + * 0 if they have the same value, + * or null if the value of either is NaN. + */ + P.comparedTo = P.cmp = function ( y, b ) { + id = 1; + return compare( this, new BigNumber( y, b ) ); + }; + + + /* + * Return the number of decimal places of the value of this BigNumber, or null if the value + * of this BigNumber is ±Infinity or NaN. + */ + P.decimalPlaces = P.dp = function () { + var n, v, + c = this.c; + + if ( !c ) return null; + n = ( ( v = c.length - 1 ) - bitFloor( this.e / LOG_BASE ) ) * LOG_BASE; + + // Subtract the number of trailing zeros of the last number. + if ( v = c[v] ) for ( ; v % 10 == 0; v /= 10, n-- ); + if ( n < 0 ) n = 0; + + return n; + }; + + + /* + * n / 0 = I + * n / N = N + * n / I = 0 + * 0 / n = 0 + * 0 / 0 = N + * 0 / N = N + * 0 / I = 0 + * N / n = N + * N / 0 = N + * N / N = N + * N / I = N + * I / n = I + * I / 0 = I + * I / N = N + * I / I = N + * + * Return a new BigNumber whose value is the value of this BigNumber divided by the value of + * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.dividedBy = P.div = function ( y, b ) { + id = 3; + return div( this, new BigNumber( y, b ), DECIMAL_PLACES, ROUNDING_MODE ); + }; + + + /* + * Return a new BigNumber whose value is the integer part of dividing the value of this + * BigNumber by the value of BigNumber(y, b). + */ + P.dividedToIntegerBy = P.divToInt = function ( y, b ) { + id = 4; + return div( this, new BigNumber( y, b ), 0, 1 ); + }; + + + /* + * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), + * otherwise returns false. + */ + P.equals = P.eq = function ( y, b ) { + id = 5; + return compare( this, new BigNumber( y, b ) ) === 0; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a whole + * number in the direction of -Infinity. + */ + P.floor = function () { + return round( new BigNumber(this), this.e + 1, 3 ); + }; + + + /* + * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.greaterThan = P.gt = function ( y, b ) { + id = 6; + return compare( this, new BigNumber( y, b ) ) > 0; + }; + + + /* + * Return true if the value of this BigNumber is greater than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.greaterThanOrEqualTo = P.gte = function ( y, b ) { + id = 7; + return ( b = compare( this, new BigNumber( y, b ) ) ) === 1 || b === 0; + + }; + + + /* + * Return true if the value of this BigNumber is a finite number, otherwise returns false. + */ + P.isFinite = function () { + return !!this.c; + }; + + + /* + * Return true if the value of this BigNumber is an integer, otherwise return false. + */ + P.isInteger = P.isInt = function () { + return !!this.c && bitFloor( this.e / LOG_BASE ) > this.c.length - 2; + }; + + + /* + * Return true if the value of this BigNumber is NaN, otherwise returns false. + */ + P.isNaN = function () { + return !this.s; + }; + + + /* + * Return true if the value of this BigNumber is negative, otherwise returns false. + */ + P.isNegative = P.isNeg = function () { + return this.s < 0; + }; + + + /* + * Return true if the value of this BigNumber is 0 or -0, otherwise returns false. + */ + P.isZero = function () { + return !!this.c && this.c[0] == 0; + }; + + + /* + * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), + * otherwise returns false. + */ + P.lessThan = P.lt = function ( y, b ) { + id = 8; + return compare( this, new BigNumber( y, b ) ) < 0; + }; + + + /* + * Return true if the value of this BigNumber is less than or equal to the value of + * BigNumber(y, b), otherwise returns false. + */ + P.lessThanOrEqualTo = P.lte = function ( y, b ) { + id = 9; + return ( b = compare( this, new BigNumber( y, b ) ) ) === -1 || b === 0; + }; + + + /* + * n - 0 = n + * n - N = N + * n - I = -I + * 0 - n = -n + * 0 - 0 = 0 + * 0 - N = N + * 0 - I = -I + * N - n = N + * N - 0 = N + * N - N = N + * N - I = N + * I - n = I + * I - 0 = I + * I - N = N + * I - I = N + * + * Return a new BigNumber whose value is the value of this BigNumber minus the value of + * BigNumber(y, b). + */ + P.minus = P.sub = function ( y, b ) { + var i, j, t, xLTy, + x = this, + a = x.s; + + id = 10; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.plus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Either Infinity? + if ( !xc || !yc ) return xc ? ( y.s = -b, y ) : new BigNumber( yc ? x : NaN ); + + // Either zero? + if ( !xc[0] || !yc[0] ) { + + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + return yc[0] ? ( y.s = -b, y ) : new BigNumber( xc[0] ? x : + + // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity + ROUNDING_MODE == 3 ? -0 : 0 ); + } + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Determine which is the bigger number. + if ( a = xe - ye ) { + + if ( xLTy = a < 0 ) { + a = -a; + t = xc; + } else { + ye = xe; + t = yc; + } + + t.reverse(); + + // Prepend zeros to equalise exponents. + for ( b = a; b--; t.push(0) ); + t.reverse(); + } else { + + // Exponents equal. Check digit by digit. + j = ( xLTy = ( a = xc.length ) < ( b = yc.length ) ) ? a : b; + + for ( a = b = 0; b < j; b++ ) { + + if ( xc[b] != yc[b] ) { + xLTy = xc[b] < yc[b]; + break; + } + } + } + + // x < y? Point xc to the array of the bigger number. + if (xLTy) t = xc, xc = yc, yc = t, y.s = -y.s; + + b = ( j = yc.length ) - ( i = xc.length ); + + // Append zeros to xc if shorter. + // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. + if ( b > 0 ) for ( ; b--; xc[i++] = 0 ); + b = BASE - 1; + + // Subtract yc from xc. + for ( ; j > a; ) { + + if ( xc[--j] < yc[j] ) { + for ( i = j; i && !xc[--i]; xc[i] = b ); + --xc[i]; + xc[j] += BASE; + } + + xc[j] -= yc[j]; + } + + // Remove leading zeros and adjust exponent accordingly. + for ( ; xc[0] == 0; xc.shift(), --ye ); + + // Zero? + if ( !xc[0] ) { + + // Following IEEE 754 (2008) 6.3, + // n - n = +0 but n - n = -0 when rounding towards -Infinity. + y.s = ROUNDING_MODE == 3 ? -1 : 1; + y.c = [ y.e = 0 ]; + return y; + } + + // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity + // for finite x and y. + return normalise( y, xc, ye ); + }; + + + /* + * n % 0 = N + * n % N = N + * n % I = n + * 0 % n = 0 + * -0 % n = -0 + * 0 % 0 = N + * 0 % N = N + * 0 % I = 0 + * N % n = N + * N % 0 = N + * N % N = N + * N % I = N + * I % n = N + * I % 0 = N + * I % N = N + * I % I = N + * + * Return a new BigNumber whose value is the value of this BigNumber modulo the value of + * BigNumber(y, b). The result depends on the value of MODULO_MODE. + */ + P.modulo = P.mod = function ( y, b ) { + var q, s, + x = this; + + id = 11; + y = new BigNumber( y, b ); + + // Return NaN if x is Infinity or NaN, or y is NaN or zero. + if ( !x.c || !y.s || y.c && !y.c[0] ) { + return new BigNumber(NaN); + + // Return x if y is Infinity or x is zero. + } else if ( !y.c || x.c && !x.c[0] ) { + return new BigNumber(x); + } + + if ( MODULO_MODE == 9 ) { + + // Euclidian division: q = sign(y) * floor(x / abs(y)) + // r = x - qy where 0 <= r < abs(y) + s = y.s; + y.s = 1; + q = div( x, y, 0, 3 ); + y.s = s; + q.s *= s; + } else { + q = div( x, y, 0, MODULO_MODE ); + } + + return x.minus( q.times(y) ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber negated, + * i.e. multiplied by -1. + */ + P.negated = P.neg = function () { + var x = new BigNumber(this); + x.s = -x.s || null; + return x; + }; + + + /* + * n + 0 = n + * n + N = N + * n + I = I + * 0 + n = n + * 0 + 0 = 0 + * 0 + N = N + * 0 + I = I + * N + n = N + * N + 0 = N + * N + N = N + * N + I = N + * I + n = I + * I + 0 = I + * I + N = N + * I + I = I + * + * Return a new BigNumber whose value is the value of this BigNumber plus the value of + * BigNumber(y, b). + */ + P.plus = P.add = function ( y, b ) { + var t, + x = this, + a = x.s; + + id = 12; + y = new BigNumber( y, b ); + b = y.s; + + // Either NaN? + if ( !a || !b ) return new BigNumber(NaN); + + // Signs differ? + if ( a != b ) { + y.s = -b; + return x.minus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if ( !xe || !ye ) { + + // Return ±Infinity if either ±Infinity. + if ( !xc || !yc ) return new BigNumber( a / 0 ); + + // Either zero? + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + if ( !xc[0] || !yc[0] ) return yc[0] ? y : new BigNumber( xc[0] ? x : a * 0 ); + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. + if ( a = xe - ye ) { + if ( a > 0 ) { + ye = xe; + t = yc; + } else { + a = -a; + t = xc; + } + + t.reverse(); + for ( ; a--; t.push(0) ); + t.reverse(); + } + + a = xc.length; + b = yc.length; + + // Point xc to the longer array, and b to the shorter length. + if ( a - b < 0 ) t = yc, yc = xc, xc = t, b = a; + + // Only start adding at yc.length - 1 as the further digits of xc can be ignored. + for ( a = 0; b; ) { + a = ( xc[--b] = xc[b] + yc[b] + a ) / BASE | 0; + xc[b] %= BASE; + } + + if (a) { + xc.unshift(a); + ++ye; + } + + // No need to check for zero, as +x + +y != 0 && -x + -y != 0 + // ye = MAX_EXP + 1 possible + return normalise( y, xc, ye ); + }; + + + /* + * Return the number of significant digits of the value of this BigNumber. + * + * [z] {boolean|number} Whether to count integer-part trailing zeros: true, false, 1 or 0. + */ + P.precision = P.sd = function (z) { + var n, v, + x = this, + c = x.c; + + // 'precision() argument not a boolean or binary digit: {z}' + if ( z != null && z !== !!z && z !== 1 && z !== 0 ) { + if (ERRORS) raise( 13, 'argument' + notBool, z ); + if ( z != !!z ) z = null; + } + + if ( !c ) return null; + v = c.length - 1; + n = v * LOG_BASE + 1; + + if ( v = c[v] ) { + + // Subtract the number of trailing zeros of the last element. + for ( ; v % 10 == 0; v /= 10, n-- ); + + // Add the number of digits of the first element. + for ( v = c[0]; v >= 10; v /= 10, n++ ); + } + + if ( z && x.e + 1 > n ) n = x.e + 1; + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * dp decimal places using rounding mode rm, or to 0 and ROUNDING_MODE respectively if + * omitted. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'round() decimal places out of range: {dp}' + * 'round() decimal places not an integer: {dp}' + * 'round() rounding mode not an integer: {rm}' + * 'round() rounding mode out of range: {rm}' + */ + P.round = function ( dp, rm ) { + var n = new BigNumber(this); + + if ( dp == null || isValidInt( dp, 0, MAX, 15 ) ) { + round( n, ~~dp + this.e + 1, rm == null || + !isValidInt( rm, 0, 8, 15, roundingMode ) ? ROUNDING_MODE : rm | 0 ); + } + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber shifted by k places + * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. + * + * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * + * If k is out of range and ERRORS is false, the result will be ±0 if k < 0, or ±Infinity + * otherwise. + * + * 'shift() argument not an integer: {k}' + * 'shift() argument out of range: {k}' + */ + P.shift = function (k) { + var n = this; + return isValidInt( k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 16, 'argument' ) + + // k < 1e+21, or truncate(k) will produce exponential notation. + ? n.times( '1e' + truncate(k) ) + : new BigNumber( n.c && n.c[0] && ( k < -MAX_SAFE_INTEGER || k > MAX_SAFE_INTEGER ) + ? n.s * ( k < 0 ? 0 : 1 / 0 ) + : n ); + }; + + + /* + * sqrt(-n) = N + * sqrt( N) = N + * sqrt(-I) = N + * sqrt( I) = I + * sqrt( 0) = 0 + * sqrt(-0) = -0 + * + * Return a new BigNumber whose value is the square root of the value of this BigNumber, + * rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.squareRoot = P.sqrt = function () { + var m, n, r, rep, t, + x = this, + c = x.c, + s = x.s, + e = x.e, + dp = DECIMAL_PLACES + 4, + half = new BigNumber('0.5'); + + // Negative/NaN/Infinity/zero? + if ( s !== 1 || !c || !c[0] ) { + return new BigNumber( !s || s < 0 && ( !c || c[0] ) ? NaN : c ? x : 1 / 0 ); + } + + // Initial estimate. + s = Math.sqrt( +x ); + + // Math.sqrt underflow/overflow? + // Pass x to Math.sqrt as integer, then adjust the exponent of the result. + if ( s == 0 || s == 1 / 0 ) { + n = coeffToString(c); + if ( ( n.length + e ) % 2 == 0 ) n += '0'; + s = Math.sqrt(n); + e = bitFloor( ( e + 1 ) / 2 ) - ( e < 0 || e % 2 ); + + if ( s == 1 / 0 ) { + n = '1e' + e; + } else { + n = s.toExponential(); + n = n.slice( 0, n.indexOf('e') + 1 ) + e; + } + + r = new BigNumber(n); + } else { + r = new BigNumber( s + '' ); + } + + // Check for zero. + // r could be zero if MIN_EXP is changed after the this value was created. + // This would cause a division by zero (x/t) and hence Infinity below, which would cause + // coeffToString to throw. + if ( r.c[0] ) { + e = r.e; + s = e + dp; + if ( s < 3 ) s = 0; + + // Newton-Raphson iteration. + for ( ; ; ) { + t = r; + r = half.times( t.plus( div( x, t, dp, 1 ) ) ); + + if ( coeffToString( t.c ).slice( 0, s ) === ( n = + coeffToString( r.c ) ).slice( 0, s ) ) { + + // The exponent of r may here be one less than the final result exponent, + // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits + // are indexed correctly. + if ( r.e < e ) --s; + n = n.slice( s - 3, s + 1 ); + + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits + // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the + // iteration. + if ( n == '9999' || !rep && n == '4999' ) { + + // On the first iteration only, check to see if rounding up gives the + // exact result as the nines may infinitely repeat. + if ( !rep ) { + round( t, t.e + DECIMAL_PLACES + 2, 0 ); + + if ( t.times(t).eq(x) ) { + r = t; + break; + } + } + + dp += 4; + s += 4; + rep = 1; + } else { + + // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact + // result. If not, then there are further digits and m will be truthy. + if ( !+n || !+n.slice(1) && n.charAt(0) == '5' ) { + + // Truncate to the first rounding digit. + round( r, r.e + DECIMAL_PLACES + 2, 1 ); + m = !r.times(r).eq(x); + } + + break; + } + } + } + } + + return round( r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m ); + }; + + + /* + * n * 0 = 0 + * n * N = N + * n * I = I + * 0 * n = 0 + * 0 * 0 = 0 + * 0 * N = N + * 0 * I = N + * N * n = N + * N * 0 = N + * N * N = N + * N * I = N + * I * n = I + * I * 0 = N + * I * N = N + * I * I = I + * + * Return a new BigNumber whose value is the value of this BigNumber times the value of + * BigNumber(y, b). + */ + P.times = P.mul = function ( y, b ) { + var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, + base, sqrtBase, + x = this, + xc = x.c, + yc = ( id = 17, y = new BigNumber( y, b ) ).c; + + // Either NaN, ±Infinity or ±0? + if ( !xc || !yc || !xc[0] || !yc[0] ) { + + // Return NaN if either is NaN, or one is 0 and the other is Infinity. + if ( !x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc ) { + y.c = y.e = y.s = null; + } else { + y.s *= x.s; + + // Return ±Infinity if either is ±Infinity. + if ( !xc || !yc ) { + y.c = y.e = null; + + // Return ±0 if either is ±0. + } else { + y.c = [0]; + y.e = 0; + } + } + + return y; + } + + e = bitFloor( x.e / LOG_BASE ) + bitFloor( y.e / LOG_BASE ); + y.s *= x.s; + xcL = xc.length; + ycL = yc.length; + + // Ensure xc points to longer array and xcL to its length. + if ( xcL < ycL ) zc = xc, xc = yc, yc = zc, i = xcL, xcL = ycL, ycL = i; + + // Initialise the result array with zeros. + for ( i = xcL + ycL, zc = []; i--; zc.push(0) ); + + base = BASE; + sqrtBase = SQRT_BASE; + + for ( i = ycL; --i >= 0; ) { + c = 0; + ylo = yc[i] % sqrtBase; + yhi = yc[i] / sqrtBase | 0; + + for ( k = xcL, j = i + k; j > i; ) { + xlo = xc[--k] % sqrtBase; + xhi = xc[k] / sqrtBase | 0; + m = yhi * xlo + xhi * ylo; + xlo = ylo * xlo + ( ( m % sqrtBase ) * sqrtBase ) + zc[j] + c; + c = ( xlo / base | 0 ) + ( m / sqrtBase | 0 ) + yhi * xhi; + zc[j--] = xlo % base; + } + + zc[j] = c; + } + + if (c) { + ++e; + } else { + zc.shift(); + } + + return normalise( y, zc, e ); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to a maximum of + * sd significant digits using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toDigits() precision out of range: {sd}' + * 'toDigits() precision not an integer: {sd}' + * 'toDigits() rounding mode not an integer: {rm}' + * 'toDigits() rounding mode out of range: {rm}' + */ + P.toDigits = function ( sd, rm ) { + var n = new BigNumber(this); + sd = sd == null || !isValidInt( sd, 1, MAX, 18, 'precision' ) ? null : sd | 0; + rm = rm == null || !isValidInt( rm, 0, 8, 18, roundingMode ) ? ROUNDING_MODE : rm | 0; + return sd ? round( n, sd, rm ) : n; + }; + + + /* + * Return a string representing the value of this BigNumber in exponential notation and + * rounded using ROUNDING_MODE to dp fixed decimal places. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toExponential() decimal places not an integer: {dp}' + * 'toExponential() decimal places out of range: {dp}' + * 'toExponential() rounding mode not an integer: {rm}' + * 'toExponential() rounding mode out of range: {rm}' + */ + P.toExponential = function ( dp, rm ) { + return format( this, + dp != null && isValidInt( dp, 0, MAX, 19 ) ? ~~dp + 1 : null, rm, 19 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounding + * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', + * but e.g. (-0.00001).toFixed(0) is '-0'. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFixed() decimal places not an integer: {dp}' + * 'toFixed() decimal places out of range: {dp}' + * 'toFixed() rounding mode not an integer: {rm}' + * 'toFixed() rounding mode out of range: {rm}' + */ + P.toFixed = function ( dp, rm ) { + return format( this, dp != null && isValidInt( dp, 0, MAX, 20 ) + ? ~~dp + this.e + 1 : null, rm, 20 ); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounded + * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties + * of the FORMAT object (see BigNumber.config). + * + * FORMAT = { + * decimalSeparator : '.', + * groupSeparator : ',', + * groupSize : 3, + * secondaryGroupSize : 0, + * fractionGroupSeparator : '\xA0', // non-breaking space + * fractionGroupSize : 0 + * }; + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toFormat() decimal places not an integer: {dp}' + * 'toFormat() decimal places out of range: {dp}' + * 'toFormat() rounding mode not an integer: {rm}' + * 'toFormat() rounding mode out of range: {rm}' + */ + P.toFormat = function ( dp, rm ) { + var str = format( this, dp != null && isValidInt( dp, 0, MAX, 21 ) + ? ~~dp + this.e + 1 : null, rm, 21 ); + + if ( this.c ) { + var i, + arr = str.split('.'), + g1 = +FORMAT.groupSize, + g2 = +FORMAT.secondaryGroupSize, + groupSeparator = FORMAT.groupSeparator, + intPart = arr[0], + fractionPart = arr[1], + isNeg = this.s < 0, + intDigits = isNeg ? intPart.slice(1) : intPart, + len = intDigits.length; + + if (g2) i = g1, g1 = g2, g2 = i, len -= i; + + if ( g1 > 0 && len > 0 ) { + i = len % g1 || g1; + intPart = intDigits.substr( 0, i ); + + for ( ; i < len; i += g1 ) { + intPart += groupSeparator + intDigits.substr( i, g1 ); + } + + if ( g2 > 0 ) intPart += groupSeparator + intDigits.slice(i); + if (isNeg) intPart = '-' + intPart; + } + + str = fractionPart + ? intPart + FORMAT.decimalSeparator + ( ( g2 = +FORMAT.fractionGroupSize ) + ? fractionPart.replace( new RegExp( '\\d{' + g2 + '}\\B', 'g' ), + '$&' + FORMAT.fractionGroupSeparator ) + : fractionPart ) + : intPart; + } + + return str; + }; + + + /* + * Return a string array representing the value of this BigNumber as a simple fraction with + * an integer numerator and an integer denominator. The denominator will be a positive + * non-zero value less than or equal to the specified maximum denominator. If a maximum + * denominator is not specified, the denominator will be the lowest value necessary to + * represent the number exactly. + * + * [md] {number|string|BigNumber} Integer >= 1 and < Infinity. The maximum denominator. + * + * 'toFraction() max denominator not an integer: {md}' + * 'toFraction() max denominator out of range: {md}' + */ + P.toFraction = function (md) { + var arr, d0, d2, e, exp, n, n0, q, s, + k = ERRORS, + x = this, + xc = x.c, + d = new BigNumber(ONE), + n1 = d0 = new BigNumber(ONE), + d1 = n0 = new BigNumber(ONE); + + if ( md != null ) { + ERRORS = false; + n = new BigNumber(md); + ERRORS = k; + + if ( !( k = n.isInt() ) || n.lt(ONE) ) { + + if (ERRORS) { + raise( 22, + 'max denominator ' + ( k ? 'out of range' : 'not an integer' ), md ); + } + + // ERRORS is false: + // If md is a finite non-integer >= 1, round it to an integer and use it. + md = !k && n.c && round( n, n.e + 1, 1 ).gte(ONE) ? n : null; + } + } + + if ( !xc ) return x.toString(); + s = coeffToString(xc); + + // Determine initial denominator. + // d is a power of 10 and the minimum max denominator that specifies the value exactly. + e = d.e = s.length - x.e - 1; + d.c[0] = POWS_TEN[ ( exp = e % LOG_BASE ) < 0 ? LOG_BASE + exp : exp ]; + md = !md || n.cmp(d) > 0 ? ( e > 0 ? d : n1 ) : n; + + exp = MAX_EXP; + MAX_EXP = 1 / 0; + n = new BigNumber(s); + + // n0 = d1 = 0 + n0.c[0] = 0; + + for ( ; ; ) { + q = div( n, d, 0, 1 ); + d2 = d0.plus( q.times(d1) ); + if ( d2.cmp(md) == 1 ) break; + d0 = d1; + d1 = d2; + n1 = n0.plus( q.times( d2 = n1 ) ); + n0 = d2; + d = n.minus( q.times( d2 = d ) ); + n = d2; + } + + d2 = div( md.minus(d0), d1, 0, 1 ); + n0 = n0.plus( d2.times(n1) ); + d0 = d0.plus( d2.times(d1) ); + n0.s = n1.s = x.s; + e *= 2; + + // Determine which fraction is closer to x, n0/d0 or n1/d1 + arr = div( n1, d1, e, ROUNDING_MODE ).minus(x).abs().cmp( + div( n0, d0, e, ROUNDING_MODE ).minus(x).abs() ) < 1 + ? [ n1.toString(), d1.toString() ] + : [ n0.toString(), d0.toString() ]; + + MAX_EXP = exp; + return arr; + }; + + + /* + * Return the value of this BigNumber converted to a number primitive. + */ + P.toNumber = function () { + var x = this; + + // Ensure zero has correct sign. + return +x || ( x.s ? x.s * 0 : NaN ); + }; + + + /* + * Return a BigNumber whose value is the value of this BigNumber raised to the power n. + * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. + * If POW_PRECISION is not 0, round to POW_PRECISION using ROUNDING_MODE. + * + * n {number} Integer, -9007199254740992 to 9007199254740992 inclusive. + * (Performs 54 loop iterations for n of 9007199254740992.) + * + * 'pow() exponent not an integer: {n}' + * 'pow() exponent out of range: {n}' + */ + P.toPower = P.pow = function (n) { + var k, y, + i = mathfloor( n < 0 ? -n : +n ), + x = this; + + // Pass ±Infinity to Math.pow if exponent is out of range. + if ( !isValidInt( n, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER, 23, 'exponent' ) && + ( !isFinite(n) || i > MAX_SAFE_INTEGER && ( n /= 0 ) || + parseFloat(n) != n && !( n = NaN ) ) ) { + return new BigNumber( Math.pow( +x, n ) ); + } + + // Truncating each coefficient array to a length of k after each multiplication equates + // to truncating significant digits to POW_PRECISION + [28, 41], i.e. there will be a + // minimum of 28 guard digits retained. (Using + 1.5 would give [9, 21] guard digits.) + k = POW_PRECISION ? mathceil( POW_PRECISION / LOG_BASE + 2 ) : 0; + y = new BigNumber(ONE); + + for ( ; ; ) { + + if ( i % 2 ) { + y = y.times(x); + if ( !y.c ) break; + if ( k && y.c.length > k ) y.c.length = k; + } + + i = mathfloor( i / 2 ); + if ( !i ) break; + + x = x.times(x); + if ( k && x.c && x.c.length > k ) x.c.length = k; + } + + if ( n < 0 ) y = ONE.div(y); + return k ? round( y, POW_PRECISION, ROUNDING_MODE ) : y; + }; + + + /* + * Return a string representing the value of this BigNumber rounded to sd significant digits + * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits + * necessary to represent the integer part of the value in fixed-point notation, then use + * exponential notation. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * 'toPrecision() precision not an integer: {sd}' + * 'toPrecision() precision out of range: {sd}' + * 'toPrecision() rounding mode not an integer: {rm}' + * 'toPrecision() rounding mode out of range: {rm}' + */ + P.toPrecision = function ( sd, rm ) { + return format( this, sd != null && isValidInt( sd, 1, MAX, 24, 'precision' ) + ? sd | 0 : null, rm, 24 ); + }; + + + /* + * Return a string representing the value of this BigNumber in base b, or base 10 if b is + * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and + * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent + * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than + * TO_EXP_NEG, return exponential notation. + * + * [b] {number} Integer, 2 to 64 inclusive. + * + * 'toString() base not an integer: {b}' + * 'toString() base out of range: {b}' + */ + P.toString = function (b) { + var str, + n = this, + s = n.s, + e = n.e; + + // Infinity or NaN? + if ( e === null ) { + + if (s) { + str = 'Infinity'; + if ( s < 0 ) str = '-' + str; + } else { + str = 'NaN'; + } + } else { + str = coeffToString( n.c ); + + if ( b == null || !isValidInt( b, 2, 64, 25, 'base' ) ) { + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential( str, e ) + : toFixedPoint( str, e ); + } else { + str = convertBase( toFixedPoint( str, e ), b | 0, 10, s ); + } + + if ( s < 0 && n.c[0] ) str = '-' + str; + } + + return str; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber truncated to a whole + * number. + */ + P.truncated = P.trunc = function () { + return round( new BigNumber(this), this.e + 1, 1 ); + }; + + + + /* + * Return as toString, but do not accept a base argument. + */ + P.valueOf = P.toJSON = function () { + return this.toString(); + }; + + + // Aliases for BigDecimal methods. + //P.add = P.plus; // P.add included above + //P.subtract = P.minus; // P.sub included above + //P.multiply = P.times; // P.mul included above + //P.divide = P.div; + //P.remainder = P.mod; + //P.compareTo = P.cmp; + //P.negate = P.neg; + + + if ( configObj != null ) BigNumber.config(configObj); + + return BigNumber; + } + + + // PRIVATE HELPER FUNCTIONS + + + function bitFloor(n) { + var i = n | 0; + return n > 0 || n === i ? i : i - 1; + } + + + // Return a coefficient array as a string of base 10 digits. + function coeffToString(a) { + var s, z, + i = 1, + j = a.length, + r = a[0] + ''; + + for ( ; i < j; ) { + s = a[i++] + ''; + z = LOG_BASE - s.length; + for ( ; z--; s = '0' + s ); + r += s; + } + + // Determine trailing zeros. + for ( j = r.length; r.charCodeAt(--j) === 48; ); + return r.slice( 0, j + 1 || 1 ); + } + + + // Compare the value of BigNumbers x and y. + function compare( x, y ) { + var a, b, + xc = x.c, + yc = y.c, + i = x.s, + j = y.s, + k = x.e, + l = y.e; + + // Either NaN? + if ( !i || !j ) return null; + + a = xc && !xc[0]; + b = yc && !yc[0]; + + // Either zero? + if ( a || b ) return a ? b ? 0 : -j : i; + + // Signs differ? + if ( i != j ) return i; + + a = i < 0; + b = k == l; + + // Either Infinity? + if ( !xc || !yc ) return b ? 0 : !xc ^ a ? 1 : -1; + + // Compare exponents. + if ( !b ) return k > l ^ a ? 1 : -1; + + j = ( k = xc.length ) < ( l = yc.length ) ? k : l; + + // Compare digit by digit. + for ( i = 0; i < j; i++ ) if ( xc[i] != yc[i] ) return xc[i] > yc[i] ^ a ? 1 : -1; + + // Compare lengths. + return k == l ? 0 : k > l ^ a ? 1 : -1; + } + + + /* + * Return true if n is a valid number in range, otherwise false. + * Use for argument validation when ERRORS is false. + * Note: parseInt('1e+1') == 1 but parseFloat('1e+1') == 10. + */ + function intValidatorNoErrors( n, min, max ) { + return ( n = truncate(n) ) >= min && n <= max; + } + + + function isArray(obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; + } + + + /* + * Convert string of baseIn to an array of numbers of baseOut. + * Eg. convertBase('255', 10, 16) returns [15, 15]. + * Eg. convertBase('ff', 16, 10) returns [2, 5, 5]. + */ + function toBaseOut( str, baseIn, baseOut ) { + var j, + arr = [0], + arrL, + i = 0, + len = str.length; + + for ( ; i < len; ) { + for ( arrL = arr.length; arrL--; arr[arrL] *= baseIn ); + arr[ j = 0 ] += ALPHABET.indexOf( str.charAt( i++ ) ); + + for ( ; j < arr.length; j++ ) { + + if ( arr[j] > baseOut - 1 ) { + if ( arr[j + 1] == null ) arr[j + 1] = 0; + arr[j + 1] += arr[j] / baseOut | 0; + arr[j] %= baseOut; + } + } + } + + return arr.reverse(); + } + + + function toExponential( str, e ) { + return ( str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str ) + + ( e < 0 ? 'e' : 'e+' ) + e; + } + + + function toFixedPoint( str, e ) { + var len, z; + + // Negative exponent? + if ( e < 0 ) { + + // Prepend zeros. + for ( z = '0.'; ++e; z += '0' ); + str = z + str; + + // Positive exponent + } else { + len = str.length; + + // Append zeros. + if ( ++e > len ) { + for ( z = '0', e -= len; --e; z += '0' ); + str += z; + } else if ( e < len ) { + str = str.slice( 0, e ) + '.' + str.slice(e); + } + } + + return str; + } + + + function truncate(n) { + n = parseFloat(n); + return n < 0 ? mathceil(n) : mathfloor(n); + } + + + // EXPORT + + + BigNumber = another(); + + // AMD. + if ( typeof define == 'function' && define.amd ) { + define( function () { return BigNumber; } ); + + // Node and other environments that support module.exports. + } else if ( typeof module != 'undefined' && module.exports ) { + module.exports = BigNumber; + if ( !crypto ) try { crypto = require('crypto'); } catch (e) {} + + // Browser. + } else { + global.BigNumber = BigNumber; + } +})(this); + +},{"crypto":50}],"web3":[function(require,module,exports){ +var Web3 = require('./lib/web3'); + +// dont override global variable +if (typeof window !== 'undefined' && typeof window.Web3 === 'undefined') { + window.Web3 = Web3; +} + +module.exports = Web3; + +},{"./lib/web3":22}]},{},["web3"]) +//# sourceMappingURL=web3.js.map diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre.go new file mode 100644 index 0000000..f05865e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre.go @@ -0,0 +1,335 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package jsre provides execution environment for JavaScript. +package jsre + +import ( + crand "crypto/rand" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/jsre/deps" + "github.com/robertkrimen/otto" +) + +var ( + BigNumber_JS = deps.MustAsset("bignumber.js") + Web3_JS = deps.MustAsset("web3.js") +) + +/* +JSRE is a generic JS runtime environment embedding the otto JS interpreter. +It provides some helper functions to +- load code from files +- run code snippets +- require libraries +- bind native go objects +*/ +type JSRE struct { + assetPath string + output io.Writer + evalQueue chan *evalReq + stopEventLoop chan bool + closed chan struct{} +} + +// jsTimer is a single timer instance with a callback function +type jsTimer struct { + timer *time.Timer + duration time.Duration + interval bool + call otto.FunctionCall +} + +// evalReq is a serialized vm execution request processed by runEventLoop. +type evalReq struct { + fn func(vm *otto.Otto) + done chan bool +} + +// runtime must be stopped with Stop() after use and cannot be used after stopping +func New(assetPath string, output io.Writer) *JSRE { + re := &JSRE{ + assetPath: assetPath, + output: output, + closed: make(chan struct{}), + evalQueue: make(chan *evalReq), + stopEventLoop: make(chan bool), + } + go re.runEventLoop() + re.Set("loadScript", re.loadScript) + re.Set("inspect", re.prettyPrintJS) + return re +} + +// randomSource returns a pseudo random value generator. +func randomSource() *rand.Rand { + bytes := make([]byte, 8) + seed := time.Now().UnixNano() + if _, err := crand.Read(bytes); err == nil { + seed = int64(binary.LittleEndian.Uint64(bytes)) + } + + src := rand.NewSource(seed) + return rand.New(src) +} + +// This function runs the main event loop from a goroutine that is started +// when JSRE is created. Use Stop() before exiting to properly stop it. +// The event loop processes vm access requests from the evalQueue in a +// serialized way and calls timer callback functions at the appropriate time. + +// Exported functions always access the vm through the event queue. You can +// call the functions of the otto vm directly to circumvent the queue. These +// functions should be used if and only if running a routine that was already +// called from JS through an RPC call. +func (self *JSRE) runEventLoop() { + defer close(self.closed) + + vm := otto.New() + r := randomSource() + vm.SetRandomSource(r.Float64) + + registry := map[*jsTimer]*jsTimer{} + ready := make(chan *jsTimer) + + newTimer := func(call otto.FunctionCall, interval bool) (*jsTimer, otto.Value) { + delay, _ := call.Argument(1).ToInteger() + if 0 >= delay { + delay = 1 + } + timer := &jsTimer{ + duration: time.Duration(delay) * time.Millisecond, + call: call, + interval: interval, + } + registry[timer] = timer + + timer.timer = time.AfterFunc(timer.duration, func() { + ready <- timer + }) + + value, err := call.Otto.ToValue(timer) + if err != nil { + panic(err) + } + return timer, value + } + + setTimeout := func(call otto.FunctionCall) otto.Value { + _, value := newTimer(call, false) + return value + } + + setInterval := func(call otto.FunctionCall) otto.Value { + _, value := newTimer(call, true) + return value + } + + clearTimeout := func(call otto.FunctionCall) otto.Value { + timer, _ := call.Argument(0).Export() + if timer, ok := timer.(*jsTimer); ok { + timer.timer.Stop() + delete(registry, timer) + } + return otto.UndefinedValue() + } + vm.Set("_setTimeout", setTimeout) + vm.Set("_setInterval", setInterval) + vm.Run(`var setTimeout = function(args) { + if (arguments.length < 1) { + throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present."); + } + return _setTimeout.apply(this, arguments); + }`) + vm.Run(`var setInterval = function(args) { + if (arguments.length < 1) { + throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present."); + } + return _setInterval.apply(this, arguments); + }`) + vm.Set("clearTimeout", clearTimeout) + vm.Set("clearInterval", clearTimeout) + + var waitForCallbacks bool + +loop: + for { + select { + case timer := <-ready: + // execute callback, remove/reschedule the timer + var arguments []interface{} + if len(timer.call.ArgumentList) > 2 { + tmp := timer.call.ArgumentList[2:] + arguments = make([]interface{}, 2+len(tmp)) + for i, value := range tmp { + arguments[i+2] = value + } + } else { + arguments = make([]interface{}, 1) + } + arguments[0] = timer.call.ArgumentList[0] + _, err := vm.Call(`Function.call.call`, nil, arguments...) + if err != nil { + fmt.Println("js error:", err, arguments) + } + + _, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it + if timer.interval && inreg { + timer.timer.Reset(timer.duration) + } else { + delete(registry, timer) + if waitForCallbacks && (len(registry) == 0) { + break loop + } + } + case req := <-self.evalQueue: + // run the code, send the result back + req.fn(vm) + close(req.done) + if waitForCallbacks && (len(registry) == 0) { + break loop + } + case waitForCallbacks = <-self.stopEventLoop: + if !waitForCallbacks || (len(registry) == 0) { + break loop + } + } + } + + for _, timer := range registry { + timer.timer.Stop() + delete(registry, timer) + } +} + +// Do executes the given function on the JS event loop. +func (self *JSRE) Do(fn func(*otto.Otto)) { + done := make(chan bool) + req := &evalReq{fn, done} + self.evalQueue <- req + <-done +} + +// stops the event loop before exit, optionally waits for all timers to expire +func (self *JSRE) Stop(waitForCallbacks bool) { + select { + case <-self.closed: + case self.stopEventLoop <- waitForCallbacks: + <-self.closed + } +} + +// Exec(file) loads and runs the contents of a file +// if a relative path is given, the jsre's assetPath is used +func (self *JSRE) Exec(file string) error { + code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file)) + if err != nil { + return err + } + var script *otto.Script + self.Do(func(vm *otto.Otto) { + script, err = vm.Compile(file, code) + if err != nil { + return + } + _, err = vm.Run(script) + }) + return err +} + +// Bind assigns value v to a variable in the JS environment +// This method is deprecated, use Set. +func (self *JSRE) Bind(name string, v interface{}) error { + return self.Set(name, v) +} + +// Run runs a piece of JS code. +func (self *JSRE) Run(code string) (v otto.Value, err error) { + self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) }) + return v, err +} + +// Get returns the value of a variable in the JS environment. +func (self *JSRE) Get(ns string) (v otto.Value, err error) { + self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) }) + return v, err +} + +// Set assigns value v to a variable in the JS environment. +func (self *JSRE) Set(ns string, v interface{}) (err error) { + self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) }) + return err +} + +// loadScript executes a JS script from inside the currently executing JS code. +func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value { + file, err := call.Argument(0).ToString() + if err != nil { + // TODO: throw exception + return otto.FalseValue() + } + file = common.AbsolutePath(self.assetPath, file) + source, err := ioutil.ReadFile(file) + if err != nil { + // TODO: throw exception + return otto.FalseValue() + } + if _, err := compileAndRun(call.Otto, file, source); err != nil { + // TODO: throw exception + fmt.Println("err:", err) + return otto.FalseValue() + } + // TODO: return evaluation result + return otto.TrueValue() +} + +// Evaluate executes code and pretty prints the result to the specified output +// stream. +func (self *JSRE) Evaluate(code string, w io.Writer) error { + var fail error + + self.Do(func(vm *otto.Otto) { + val, err := vm.Run(code) + if err != nil { + prettyError(vm, err, w) + } else { + prettyPrint(vm, val, w) + } + fmt.Fprintln(w) + }) + return fail +} + +// Compile compiles and then runs a piece of JS code. +func (self *JSRE) Compile(filename string, src interface{}) (err error) { + self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) }) + return err +} + +func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) { + script, err := vm.Compile(filename, src) + if err != nil { + return otto.Value{}, err + } + return vm.Run(script) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre_test.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre_test.go new file mode 100644 index 0000000..bcb6e0d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/jsre_test.go @@ -0,0 +1,137 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package jsre + +import ( + "io/ioutil" + "os" + "path" + "testing" + "time" + + "github.com/robertkrimen/otto" +) + +type testNativeObjectBinding struct{} + +type msg struct { + Msg string +} + +func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value { + m, err := call.Argument(0).ToString() + if err != nil { + return otto.UndefinedValue() + } + v, _ := call.Otto.ToValue(&msg{m}) + return v +} + +func newWithTestJS(t *testing.T, testjs string) (*JSRE, string) { + dir, err := ioutil.TempDir("", "jsre-test") + if err != nil { + t.Fatal("cannot create temporary directory:", err) + } + if testjs != "" { + if err := ioutil.WriteFile(path.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil { + t.Fatal("cannot create test.js:", err) + } + } + return New(dir, os.Stdout), dir +} + +func TestExec(t *testing.T) { + jsre, dir := newWithTestJS(t, `msg = "testMsg"`) + defer os.RemoveAll(dir) + + err := jsre.Exec("test.js") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + val, err := jsre.Run("msg") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if !val.IsString() { + t.Errorf("expected string value, got %v", val) + } + exp := "testMsg" + got, _ := val.ToString() + if exp != got { + t.Errorf("expected '%v', got '%v'", exp, got) + } + jsre.Stop(false) +} + +func TestNatto(t *testing.T) { + jsre, dir := newWithTestJS(t, `setTimeout(function(){msg = "testMsg"}, 1);`) + defer os.RemoveAll(dir) + + err := jsre.Exec("test.js") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + time.Sleep(100 * time.Millisecond) + val, err := jsre.Run("msg") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if !val.IsString() { + t.Errorf("expected string value, got %v", val) + } + exp := "testMsg" + got, _ := val.ToString() + if exp != got { + t.Errorf("expected '%v', got '%v'", exp, got) + } + jsre.Stop(false) +} + +func TestBind(t *testing.T) { + jsre := New("", os.Stdout) + defer jsre.Stop(false) + + jsre.Bind("no", &testNativeObjectBinding{}) + + _, err := jsre.Run(`no.TestMethod("testMsg")`) + if err != nil { + t.Errorf("expected no error, got %v", err) + } +} + +func TestLoadScript(t *testing.T) { + jsre, dir := newWithTestJS(t, `msg = "testMsg"`) + defer os.RemoveAll(dir) + + _, err := jsre.Run(`loadScript("test.js")`) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + val, err := jsre.Run("msg") + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if !val.IsString() { + t.Errorf("expected string value, got %v", val) + } + exp := "testMsg" + got, _ := val.ToString() + if exp != got { + t.Errorf("expected '%v', got '%v'", exp, got) + } + jsre.Stop(false) +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go new file mode 100644 index 0000000..e096eec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/jsre/pretty.go @@ -0,0 +1,280 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package jsre + +import ( + "fmt" + "io" + "sort" + "strconv" + "strings" + + "github.com/fatih/color" + "github.com/robertkrimen/otto" +) + +const ( + maxPrettyPrintLevel = 3 + indentString = " " +) + +var ( + FunctionColor = color.New(color.FgMagenta).SprintfFunc() + SpecialColor = color.New(color.Bold).SprintfFunc() + NumberColor = color.New(color.FgRed).SprintfFunc() + StringColor = color.New(color.FgGreen).SprintfFunc() + ErrorColor = color.New(color.FgHiRed).SprintfFunc() +) + +// these fields are hidden when printing objects. +var boringKeys = map[string]bool{ + "valueOf": true, + "toString": true, + "toLocaleString": true, + "hasOwnProperty": true, + "isPrototypeOf": true, + "propertyIsEnumerable": true, + "constructor": true, +} + +// prettyPrint writes value to standard output. +func prettyPrint(vm *otto.Otto, value otto.Value, w io.Writer) { + ppctx{vm: vm, w: w}.printValue(value, 0, false) +} + +// prettyError writes err to standard output. +func prettyError(vm *otto.Otto, err error, w io.Writer) { + failure := err.Error() + if ottoErr, ok := err.(*otto.Error); ok { + failure = ottoErr.String() + } + fmt.Fprint(w, ErrorColor("%s", failure)) +} + +// jsErrorString adds a backtrace to errors generated by otto. +func jsErrorString(err error) string { + if ottoErr, ok := err.(*otto.Error); ok { + return ottoErr.String() + } + return err.Error() +} + +func (re *JSRE) prettyPrintJS(call otto.FunctionCall) otto.Value { + for _, v := range call.ArgumentList { + prettyPrint(call.Otto, v, re.output) + fmt.Fprintln(re.output) + } + return otto.UndefinedValue() +} + +type ppctx struct { + vm *otto.Otto + w io.Writer +} + +func (ctx ppctx) indent(level int) string { + return strings.Repeat(indentString, level) +} + +func (ctx ppctx) printValue(v otto.Value, level int, inArray bool) { + switch { + case v.IsObject(): + ctx.printObject(v.Object(), level, inArray) + case v.IsNull(): + fmt.Fprint(ctx.w, SpecialColor("null")) + case v.IsUndefined(): + fmt.Fprint(ctx.w, SpecialColor("undefined")) + case v.IsString(): + s, _ := v.ToString() + fmt.Fprint(ctx.w, StringColor("%q", s)) + case v.IsBoolean(): + b, _ := v.ToBoolean() + fmt.Fprint(ctx.w, SpecialColor("%t", b)) + case v.IsNaN(): + fmt.Fprint(ctx.w, NumberColor("NaN")) + case v.IsNumber(): + s, _ := v.ToString() + fmt.Fprint(ctx.w, NumberColor("%s", s)) + default: + fmt.Fprint(ctx.w, "") + } +} + +func (ctx ppctx) printObject(obj *otto.Object, level int, inArray bool) { + switch obj.Class() { + case "Array", "GoArray": + lv, _ := obj.Get("length") + len, _ := lv.ToInteger() + if len == 0 { + fmt.Fprintf(ctx.w, "[]") + return + } + if level > maxPrettyPrintLevel { + fmt.Fprint(ctx.w, "[...]") + return + } + fmt.Fprint(ctx.w, "[") + for i := int64(0); i < len; i++ { + el, err := obj.Get(strconv.FormatInt(i, 10)) + if err == nil { + ctx.printValue(el, level+1, true) + } + if i < len-1 { + fmt.Fprintf(ctx.w, ", ") + } + } + fmt.Fprint(ctx.w, "]") + + case "Object": + // Print values from bignumber.js as regular numbers. + if ctx.isBigNumber(obj) { + fmt.Fprint(ctx.w, NumberColor("%s", toString(obj))) + return + } + // Otherwise, print all fields indented, but stop if we're too deep. + keys := ctx.fields(obj) + if len(keys) == 0 { + fmt.Fprint(ctx.w, "{}") + return + } + if level > maxPrettyPrintLevel { + fmt.Fprint(ctx.w, "{...}") + return + } + fmt.Fprintln(ctx.w, "{") + for i, k := range keys { + v, _ := obj.Get(k) + fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k) + ctx.printValue(v, level+1, false) + if i < len(keys)-1 { + fmt.Fprintf(ctx.w, ",") + } + fmt.Fprintln(ctx.w) + } + if inArray { + level-- + } + fmt.Fprintf(ctx.w, "%s}", ctx.indent(level)) + + case "Function": + // Use toString() to display the argument list if possible. + if robj, err := obj.Call("toString"); err != nil { + fmt.Fprint(ctx.w, FunctionColor("function()")) + } else { + desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n") + desc = strings.Replace(desc, " (", "(", 1) + fmt.Fprint(ctx.w, FunctionColor("%s", desc)) + } + + case "RegExp": + fmt.Fprint(ctx.w, StringColor("%s", toString(obj))) + + default: + if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel { + s, _ := obj.Call("toString") + fmt.Fprintf(ctx.w, "<%s %s>", obj.Class(), s.String()) + } else { + fmt.Fprintf(ctx.w, "<%s>", obj.Class()) + } + } +} + +func (ctx ppctx) fields(obj *otto.Object) []string { + var ( + vals, methods []string + seen = make(map[string]bool) + ) + add := func(k string) { + if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") { + return + } + seen[k] = true + if v, _ := obj.Get(k); v.IsFunction() { + methods = append(methods, k) + } else { + vals = append(vals, k) + } + } + iterOwnAndConstructorKeys(ctx.vm, obj, add) + sort.Strings(vals) + sort.Strings(methods) + return append(vals, methods...) +} + +func iterOwnAndConstructorKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { + seen := make(map[string]bool) + iterOwnKeys(vm, obj, func(prop string) { + seen[prop] = true + f(prop) + }) + if cp := constructorPrototype(obj); cp != nil { + iterOwnKeys(vm, cp, func(prop string) { + if !seen[prop] { + f(prop) + } + }) + } +} + +func iterOwnKeys(vm *otto.Otto, obj *otto.Object, f func(string)) { + Object, _ := vm.Object("Object") + rv, _ := Object.Call("getOwnPropertyNames", obj.Value()) + gv, _ := rv.Export() + switch gv := gv.(type) { + case []interface{}: + for _, v := range gv { + f(v.(string)) + } + case []string: + for _, v := range gv { + f(v) + } + default: + panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv)) + } +} + +func (ctx ppctx) isBigNumber(v *otto.Object) bool { + // Handle numbers with custom constructor. + if v, _ := v.Get("constructor"); v.Object() != nil { + if strings.HasPrefix(toString(v.Object()), "function BigNumber") { + return true + } + } + // Handle default constructor. + BigNumber, _ := ctx.vm.Object("BigNumber.prototype") + if BigNumber == nil { + return false + } + bv, _ := BigNumber.Call("isPrototypeOf", v) + b, _ := bv.ToBoolean() + return b +} + +func toString(obj *otto.Object) string { + s, _ := obj.Call("toString") + return s.String() +} + +func constructorPrototype(obj *otto.Object) *otto.Object { + if v, _ := obj.Get("constructor"); v.Object() != nil { + if v, _ = v.Object().Get("prototype"); v.Object() != nil { + return v.Object() + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go new file mode 100644 index 0000000..e6bd181 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -0,0 +1,527 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// package web3ext contains geth specific web3.js extensions. +package web3ext + +var Modules = map[string]string{ + "admin": Admin_JS, + "chequebook": Chequebook_JS, + "debug": Debug_JS, + "eth": Eth_JS, + "miner": Miner_JS, + "net": Net_JS, + "personal": Personal_JS, + "rpc": RPC_JS, + "shh": Shh_JS, + "txpool": TxPool_JS, +} + +const Chequebook_JS = ` +web3._extend({ + property: 'chequebook', + methods: + [ + new web3._extend.Method({ + name: 'deposit', + call: 'chequebook_deposit', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Property({ + name: 'balance', + getter: 'chequebook_balance', + outputFormatter: web3._extend.utils.toDecimal + }), + new web3._extend.Method({ + name: 'cash', + call: 'chequebook_cash', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'issue', + call: 'chequebook_issue', + params: 2, + inputFormatter: [null, null] + }), + ] +}); +` + +const Admin_JS = ` +web3._extend({ + property: 'admin', + methods: + [ + new web3._extend.Method({ + name: 'addPeer', + call: 'admin_addPeer', + params: 1 + }), + new web3._extend.Method({ + name: 'removePeer', + call: 'admin_removePeer', + params: 1 + }), + new web3._extend.Method({ + name: 'exportChain', + call: 'admin_exportChain', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'importChain', + call: 'admin_importChain', + params: 1 + }), + new web3._extend.Method({ + name: 'sleepBlocks', + call: 'admin_sleepBlocks', + params: 2 + }), + new web3._extend.Method({ + name: 'setSolc', + call: 'admin_setSolc', + params: 1 + }), + new web3._extend.Method({ + name: 'startRPC', + call: 'admin_startRPC', + params: 4, + inputFormatter: [null, null, null, null] + }), + new web3._extend.Method({ + name: 'stopRPC', + call: 'admin_stopRPC' + }), + new web3._extend.Method({ + name: 'startWS', + call: 'admin_startWS', + params: 4, + inputFormatter: [null, null, null, null] + }), + new web3._extend.Method({ + name: 'stopWS', + call: 'admin_stopWS' + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'nodeInfo', + getter: 'admin_nodeInfo' + }), + new web3._extend.Property({ + name: 'peers', + getter: 'admin_peers' + }), + new web3._extend.Property({ + name: 'datadir', + getter: 'admin_datadir' + }) + ] +}); +` + +const Debug_JS = ` +web3._extend({ + property: 'debug', + methods: + [ + new web3._extend.Method({ + name: 'printBlock', + call: 'debug_printBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'getBlockRlp', + call: 'debug_getBlockRlp', + params: 1 + }), + new web3._extend.Method({ + name: 'setHead', + call: 'debug_setHead', + params: 1 + }), + new web3._extend.Method({ + name: 'traceBlock', + call: 'debug_traceBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'traceBlockByFile', + call: 'debug_traceBlockByFile', + params: 1 + }), + new web3._extend.Method({ + name: 'traceBlockByNumber', + call: 'debug_traceBlockByNumber', + params: 1 + }), + new web3._extend.Method({ + name: 'traceBlockByHash', + call: 'debug_traceBlockByHash', + params: 1 + }), + new web3._extend.Method({ + name: 'seedHash', + call: 'debug_seedHash', + params: 1 + }), + new web3._extend.Method({ + name: 'dumpBlock', + call: 'debug_dumpBlock', + params: 1 + }), + new web3._extend.Method({ + name: 'chaindbProperty', + call: 'debug_chaindbProperty', + params: 1, + outputFormatter: console.log + }), + new web3._extend.Method({ + name: 'chaindbCompact', + call: 'debug_chaindbCompact', + }), + new web3._extend.Method({ + name: 'metrics', + call: 'debug_metrics', + params: 1 + }), + new web3._extend.Method({ + name: 'verbosity', + call: 'debug_verbosity', + params: 1 + }), + new web3._extend.Method({ + name: 'vmodule', + call: 'debug_vmodule', + params: 1 + }), + new web3._extend.Method({ + name: 'backtraceAt', + call: 'debug_backtraceAt', + params: 1, + }), + new web3._extend.Method({ + name: 'stacks', + call: 'debug_stacks', + params: 0, + outputFormatter: console.log + }), + new web3._extend.Method({ + name: 'memStats', + call: 'debug_memStats', + params: 0, + }), + new web3._extend.Method({ + name: 'gcStats', + call: 'debug_gcStats', + params: 0, + }), + new web3._extend.Method({ + name: 'cpuProfile', + call: 'debug_cpuProfile', + params: 2 + }), + new web3._extend.Method({ + name: 'startCPUProfile', + call: 'debug_startCPUProfile', + params: 1 + }), + new web3._extend.Method({ + name: 'stopCPUProfile', + call: 'debug_stopCPUProfile', + params: 0 + }), + new web3._extend.Method({ + name: 'goTrace', + call: 'debug_goTrace', + params: 2 + }), + new web3._extend.Method({ + name: 'startGoTrace', + call: 'debug_startGoTrace', + params: 1 + }), + new web3._extend.Method({ + name: 'stopGoTrace', + call: 'debug_stopGoTrace', + params: 0 + }), + new web3._extend.Method({ + name: 'blockProfile', + call: 'debug_blockProfile', + params: 2 + }), + new web3._extend.Method({ + name: 'setBlockProfileRate', + call: 'debug_setBlockProfileRate', + params: 1 + }), + new web3._extend.Method({ + name: 'writeBlockProfile', + call: 'debug_writeBlockProfile', + params: 1 + }), + new web3._extend.Method({ + name: 'writeMemProfile', + call: 'debug_writeMemProfile', + params: 1 + }), + new web3._extend.Method({ + name: 'traceTransaction', + call: 'debug_traceTransaction', + params: 2, + inputFormatter: [null, null] + }), + new web3._extend.Method({ + name: 'preimage', + call: 'debug_preimage', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'getBadBlocks', + call: 'debug_getBadBlocks', + params: 0, + }), + ], + properties: [] +}); +` + +const Eth_JS = ` +web3._extend({ + property: 'eth', + methods: + [ + new web3._extend.Method({ + name: 'sign', + call: 'eth_sign', + params: 2, + inputFormatter: [web3._extend.formatters.inputAddressFormatter, null] + }), + new web3._extend.Method({ + name: 'resend', + call: 'eth_resend', + params: 3, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] + }), + new web3._extend.Method({ + name: 'signTransaction', + call: 'eth_signTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'submitTransaction', + call: 'eth_submitTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'getRawTransaction', + call: 'eth_getRawTransactionByHash', + params: 1 + }), + new web3._extend.Method({ + name: 'getRawTransactionFromBlock', + call: function(args) { + return (web3._extend.utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getRawTransactionByBlockHashAndIndex' : 'eth_getRawTransactionByBlockNumberAndIndex'; + }, + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex] + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'pendingTransactions', + getter: 'eth_pendingTransactions', + outputFormatter: function(txs) { + var formatted = []; + for (var i = 0; i < txs.length; i++) { + formatted.push(web3._extend.formatters.outputTransactionFormatter(txs[i])); + formatted[i].blockHash = null; + } + return formatted; + } + }) + ] +}); +` + +const Miner_JS = ` +web3._extend({ + property: 'miner', + methods: + [ + new web3._extend.Method({ + name: 'start', + call: 'miner_start', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'stop', + call: 'miner_stop' + }), + new web3._extend.Method({ + name: 'setEtherbase', + call: 'miner_setEtherbase', + params: 1, + inputFormatter: [web3._extend.formatters.inputAddressFormatter] + }), + new web3._extend.Method({ + name: 'setExtra', + call: 'miner_setExtra', + params: 1 + }), + new web3._extend.Method({ + name: 'setGasPrice', + call: 'miner_setGasPrice', + params: 1, + inputFormatter: [web3._extend.utils.fromDecimal] + }), + new web3._extend.Method({ + name: 'startAutoDAG', + call: 'miner_startAutoDAG', + params: 0 + }), + new web3._extend.Method({ + name: 'stopAutoDAG', + call: 'miner_stopAutoDAG', + params: 0 + }), + new web3._extend.Method({ + name: 'makeDAG', + call: 'miner_makeDAG', + params: 1, + inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter] + }) + ], + properties: [] +}); +` + +const Net_JS = ` +web3._extend({ + property: 'net', + methods: [], + properties: + [ + new web3._extend.Property({ + name: 'version', + getter: 'net_version' + }) + ] +}); +` + +const Personal_JS = ` +web3._extend({ + property: 'personal', + methods: + [ + new web3._extend.Method({ + name: 'importRawKey', + call: 'personal_importRawKey', + params: 2 + }), + new web3._extend.Method({ + name: 'sign', + call: 'personal_sign', + params: 3, + inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null] + }), + new web3._extend.Method({ + name: 'ecRecover', + call: 'personal_ecRecover', + params: 2 + }), + new web3._extend.Method({ + name: 'deriveAccount', + call: 'personal_deriveAccount', + params: 3 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'listWallets', + getter: 'personal_listWallets' + }) + ] +}) +` + +const RPC_JS = ` +web3._extend({ + property: 'rpc', + methods: [], + properties: + [ + new web3._extend.Property({ + name: 'modules', + getter: 'rpc_modules' + }) + ] +}); +` + +const Shh_JS = ` +web3._extend({ + property: 'shh', + methods: [], + properties: + [ + new web3._extend.Property({ + name: 'version', + getter: 'shh_version', + outputFormatter: web3._extend.utils.toDecimal + }) + ] +}); +` + +const TxPool_JS = ` +web3._extend({ + property: 'txpool', + methods: [], + properties: + [ + new web3._extend.Property({ + name: 'content', + getter: 'txpool_content' + }), + new web3._extend.Property({ + name: 'inspect', + getter: 'txpool_inspect' + }), + new web3._extend.Property({ + name: 'status', + getter: 'txpool_status', + outputFormatter: function(status) { + status.pending = web3._extend.utils.toDecimal(status.pending); + status.queued = web3._extend.utils.toDecimal(status.queued); + return status; + } + }) + ] +}); +` diff --git a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go new file mode 100644 index 0000000..ed2a7cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -0,0 +1,155 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +type LesApiBackend struct { + eth *LightEthereum + gpo *gasprice.LightPriceOracle +} + +func (b *LesApiBackend) ChainConfig() *params.ChainConfig { + return b.eth.chainConfig +} + +func (b *LesApiBackend) CurrentBlock() *types.Block { + return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) +} + +func (b *LesApiBackend) SetHead(number uint64) { + b.eth.blockchain.SetHead(number) +} + +func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { + if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { + return b.eth.blockchain.CurrentHeader(), nil + } + + return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) +} + +func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { + header, err := b.HeaderByNumber(ctx, blockNr) + if header == nil || err != nil { + return nil, err + } + return b.GetBlock(ctx, header.Hash()) +} + +func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { + header, err := b.HeaderByNumber(ctx, blockNr) + if header == nil || err != nil { + return nil, nil, err + } + return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil +} + +func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { + return b.eth.blockchain.GetBlockByHash(ctx, blockHash) +} + +func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { + return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)) +} + +func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { + return b.eth.blockchain.GetTdByHash(blockHash) +} + +func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) { + stateDb := state.(*light.LightState).Copy() + addr := msg.From() + from, err := stateDb.GetOrNewStateObject(ctx, addr) + if err != nil { + return nil, nil, err + } + from.SetBalance(common.MaxBig) + + vmstate := light.NewVMState(ctx, stateDb) + context := core.NewEVMContext(msg, header, b.eth.blockchain) + return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil +} + +func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { + return b.eth.txPool.Add(ctx, signedTx) +} + +func (b *LesApiBackend) RemoveTx(txHash common.Hash) { + b.eth.txPool.RemoveTx(txHash) +} + +func (b *LesApiBackend) GetPoolTransactions() (types.Transactions, error) { + return b.eth.txPool.GetTransactions() +} + +func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { + return b.eth.txPool.GetTransaction(txHash) +} + +func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + return b.eth.txPool.GetNonce(ctx, addr) +} + +func (b *LesApiBackend) Stats() (pending int, queued int) { + return b.eth.txPool.Stats(), 0 +} + +func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + return b.eth.txPool.Content() +} + +func (b *LesApiBackend) Downloader() *downloader.Downloader { + return b.eth.Downloader() +} + +func (b *LesApiBackend) ProtocolVersion() int { + return b.eth.LesVersion() + 10000 +} + +func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { + return b.gpo.SuggestPrice(ctx) +} + +func (b *LesApiBackend) ChainDb() ethdb.Database { + return b.eth.chainDb +} + +func (b *LesApiBackend) EventMux() *event.TypeMux { + return b.eth.eventMux +} + +func (b *LesApiBackend) AccountManager() *accounts.Manager { + return b.eth.accountManager +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/backend.go b/vendor/github.com/ethereum/go-ethereum/les/backend.go new file mode 100644 index 0000000..21ee084 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/backend.go @@ -0,0 +1,212 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + rpc "github.com/ethereum/go-ethereum/rpc" +) + +type LightEthereum struct { + odr *LesOdr + relay *LesTxRelay + chainConfig *params.ChainConfig + // Channel for shutting down the service + shutdownChan chan bool + // Handlers + txPool *light.TxPool + blockchain *light.LightChain + protocolManager *ProtocolManager + // DB interfaces + chainDb ethdb.Database // Block chain database + + ApiBackend *LesApiBackend + + eventMux *event.TypeMux + pow pow.PoW + accountManager *accounts.Manager + solcPath string + solc *compiler.Solidity + + netVersionId int + netRPCService *ethapi.PublicNetAPI +} + +func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { + chainDb, err := eth.CreateDB(ctx, config, "lightchaindata") + if err != nil { + return nil, err + } + if err := eth.SetupGenesisBlock(&chainDb, config); err != nil { + return nil, err + } + pow, err := eth.CreatePoW(config) + if err != nil { + return nil, err + } + + odr := NewLesOdr(chainDb) + relay := NewLesTxRelay() + eth := &LightEthereum{ + odr: odr, + relay: relay, + chainDb: chainDb, + eventMux: ctx.EventMux, + accountManager: ctx.AccountManager, + pow: pow, + shutdownChan: make(chan bool), + netVersionId: config.NetworkId, + solcPath: config.SolcPath, + } + + if config.ChainConfig == nil { + return nil, errors.New("missing chain config") + } + eth.chainConfig = config.ChainConfig + eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.pow, eth.eventMux) + if err != nil { + if err == core.ErrNoGenesis { + return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) + } + return nil, err + } + + eth.txPool = light.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain, eth.relay) + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.LightMode, config.NetworkId, eth.eventMux, eth.pow, eth.blockchain, nil, chainDb, odr, relay); err != nil { + return nil, err + } + + eth.ApiBackend = &LesApiBackend{eth, nil} + eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend) + return eth, nil +} + +type LightDummyAPI struct{} + +// Etherbase is the address that mining rewards will be send to +func (s *LightDummyAPI) Etherbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Coinbase is the address that mining rewards will be send to (alias for Etherbase) +func (s *LightDummyAPI) Coinbase() (common.Address, error) { + return common.Address{}, fmt.Errorf("not supported") +} + +// Hashrate returns the POW hashrate +func (s *LightDummyAPI) Hashrate() hexutil.Uint { + return 0 +} + +// Mining returns an indication if this node is currently mining. +func (s *LightDummyAPI) Mining() bool { + return false +} + +// APIs returns the collection of RPC services the ethereum package offers. +// NOTE, some of these services probably need to be moved to somewhere else. +func (s *LightEthereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ + { + Namespace: "eth", + Version: "1.0", + Service: &LightDummyAPI{}, + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Public: true, + }, { + Namespace: "eth", + Version: "1.0", + Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Public: true, + }, { + Namespace: "net", + Version: "1.0", + Service: s.netRPCService, + Public: true, + }, + }...) +} + +func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { + s.blockchain.ResetWithGenesisBlock(gb) +} + +func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } +func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } +func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } +func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } + +// Protocols implements node.Service, returning all the currently configured +// network protocols to start. +func (s *LightEthereum) Protocols() []p2p.Protocol { + return s.protocolManager.SubProtocols +} + +// Start implements node.Service, starting all internal goroutines needed by the +// Ethereum protocol implementation. +func (s *LightEthereum) Start(srvr *p2p.Server) error { + glog.V(logger.Info).Infof("WARNING: light client mode is an experimental feature") + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId) + s.protocolManager.Start(srvr) + return nil +} + +// Stop implements node.Service, terminating all internal goroutines used by the +// Ethereum protocol. +func (s *LightEthereum) Stop() error { + s.odr.Stop() + s.blockchain.Stop() + s.protocolManager.Stop() + s.txPool.Stop() + + s.eventMux.Stop() + + time.Sleep(time.Millisecond * 200) + s.chainDb.Close() + close(s.shutdownChan) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go new file mode 100644 index 0000000..de706de --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go @@ -0,0 +1,719 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + blockDelayTimeout = time.Second * 10 // timeout for a peer to announce a head that has already been confirmed by others + maxNodeCount = 20 // maximum number of fetcherTreeNode entries remembered for each peer +) + +// lightFetcher +type lightFetcher struct { + pm *ProtocolManager + odr *LesOdr + chain *light.LightChain + + maxConfirmedTd *big.Int + peers map[*peer]*fetcherPeerInfo + lastUpdateStats *updateStatsEntry + + lock sync.Mutex // qwerqwerqwe + deliverChn chan fetchResponse + reqMu sync.RWMutex + requested map[uint64]fetchRequest + timeoutChn chan uint64 + requestChn chan bool // true if initiated from outside + syncing bool + syncDone chan *peer +} + +// fetcherPeerInfo holds fetcher-specific information about each active peer +type fetcherPeerInfo struct { + root, lastAnnounced *fetcherTreeNode + nodeCnt int + confirmedTd *big.Int + bestConfirmed *fetcherTreeNode + nodeByHash map[common.Hash]*fetcherTreeNode + firstUpdateStats *updateStatsEntry +} + +// fetcherTreeNode is a node of a tree that holds information about blocks recently +// announced and confirmed by a certain peer. Each new announce message from a peer +// adds nodes to the tree, based on the previous announced head and the reorg depth. +// There are three possible states for a tree node: +// - announced: not downloaded (known) yet, but we know its head, number and td +// - intermediate: not known, hash and td are empty, they are filled out when it becomes known +// - known: both announced by this peer and downloaded (from any peer). +// This structure makes it possible to always know which peer has a certain block, +// which is necessary for selecting a suitable peer for ODR requests and also for +// canonizing new heads. It also helps to always download the minimum necessary +// amount of headers with a single request. +type fetcherTreeNode struct { + hash common.Hash + number uint64 + td *big.Int + known, requested bool + parent *fetcherTreeNode + children []*fetcherTreeNode +} + +// fetchRequest represents a header download request +type fetchRequest struct { + hash common.Hash + amount uint64 + peer *peer + sent mclock.AbsTime + timeout bool +} + +// fetchResponse represents a header download response +type fetchResponse struct { + reqID uint64 + headers []*types.Header + peer *peer +} + +// newLightFetcher creates a new light fetcher +func newLightFetcher(pm *ProtocolManager) *lightFetcher { + f := &lightFetcher{ + pm: pm, + chain: pm.blockchain.(*light.LightChain), + odr: pm.odr, + peers: make(map[*peer]*fetcherPeerInfo), + deliverChn: make(chan fetchResponse, 100), + requested: make(map[uint64]fetchRequest), + timeoutChn: make(chan uint64), + requestChn: make(chan bool, 100), + syncDone: make(chan *peer), + maxConfirmedTd: big.NewInt(0), + } + go f.syncLoop() + return f +} + +// syncLoop is the main event loop of the light fetcher +func (f *lightFetcher) syncLoop() { + f.pm.wg.Add(1) + defer f.pm.wg.Done() + + requesting := false + for { + select { + case <-f.pm.quitSync: + return + // when a new announce is received, request loop keeps running until + // no further requests are necessary or possible + case newAnnounce := <-f.requestChn: + f.lock.Lock() + s := requesting + requesting = false + if !f.syncing && !(newAnnounce && s) { + reqID := getNextReqID() + if peer, node, amount, retry := f.nextRequest(reqID); node != nil { + requesting = true + if reqID, ok := f.request(peer, reqID, node, amount); ok { + go func() { + time.Sleep(softRequestTimeout) + f.reqMu.Lock() + req, ok := f.requested[reqID] + if ok { + req.timeout = true + f.requested[reqID] = req + } + f.reqMu.Unlock() + // keep starting new requests while possible + f.requestChn <- false + }() + } + } else { + if retry { + requesting = true + go func() { + time.Sleep(time.Millisecond * 100) + f.requestChn <- false + }() + } + } + } + f.lock.Unlock() + case reqID := <-f.timeoutChn: + f.reqMu.Lock() + req, ok := f.requested[reqID] + if ok { + delete(f.requested, reqID) + } + f.reqMu.Unlock() + if ok { + f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), true) + glog.V(logger.Debug).Infof("hard timeout by peer %v", req.peer.id) + go f.pm.removePeer(req.peer.id) + } + case resp := <-f.deliverChn: + f.reqMu.Lock() + req, ok := f.requested[resp.reqID] + if ok && req.peer != resp.peer { + ok = false + } + if ok { + delete(f.requested, resp.reqID) + } + f.reqMu.Unlock() + if ok { + f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), req.timeout) + } + f.lock.Lock() + if !ok || !(f.syncing || f.processResponse(req, resp)) { + glog.V(logger.Debug).Infof("failed processing response by peer %v", resp.peer.id) + go f.pm.removePeer(resp.peer.id) + } + f.lock.Unlock() + case p := <-f.syncDone: + f.lock.Lock() + glog.V(logger.Debug).Infof("done synchronising with peer %v", p.id) + f.checkSyncedHeaders(p) + f.syncing = false + f.lock.Unlock() + } + } +} + +// addPeer adds a new peer to the fetcher's peer set +func (f *lightFetcher) addPeer(p *peer) { + p.lock.Lock() + p.hasBlock = func(hash common.Hash, number uint64) bool { + return f.peerHasBlock(p, hash, number) + } + p.lock.Unlock() + + f.lock.Lock() + defer f.lock.Unlock() + + f.peers[p] = &fetcherPeerInfo{nodeByHash: make(map[common.Hash]*fetcherTreeNode)} +} + +// removePeer removes a new peer from the fetcher's peer set +func (f *lightFetcher) removePeer(p *peer) { + p.lock.Lock() + p.hasBlock = nil + p.lock.Unlock() + + f.lock.Lock() + defer f.lock.Unlock() + + // check for potential timed out block delay statistics + f.checkUpdateStats(p, nil) + delete(f.peers, p) +} + +// announce processes a new announcement message received from a peer, adding new +// nodes to the peer's block tree and removing old nodes if necessary +func (f *lightFetcher) announce(p *peer, head *announceData) { + f.lock.Lock() + defer f.lock.Unlock() + glog.V(logger.Debug).Infof("received announce from peer %v #%d %016x reorg: %d", p.id, head.Number, head.Hash[:8], head.ReorgDepth) + + fp := f.peers[p] + if fp == nil { + glog.V(logger.Debug).Infof("announce: unknown peer") + return + } + + if fp.lastAnnounced != nil && head.Td.Cmp(fp.lastAnnounced.td) <= 0 { + // announced tds should be strictly monotonic + glog.V(logger.Debug).Infof("non-monotonic Td from peer %v", p.id) + go f.pm.removePeer(p.id) + return + } + + n := fp.lastAnnounced + for i := uint64(0); i < head.ReorgDepth; i++ { + if n == nil { + break + } + n = n.parent + } + if n != nil { + // n is now the reorg common ancestor, add a new branch of nodes + // check if the node count is too high to add new nodes + locked := false + for uint64(fp.nodeCnt)+head.Number-n.number > maxNodeCount && fp.root != nil { + if !locked { + f.chain.LockChain() + defer f.chain.UnlockChain() + locked = true + } + // if one of root's children is canonical, keep it, delete other branches and root itself + var newRoot *fetcherTreeNode + for i, nn := range fp.root.children { + if core.GetCanonicalHash(f.pm.chainDb, nn.number) == nn.hash { + fp.root.children = append(fp.root.children[:i], fp.root.children[i+1:]...) + nn.parent = nil + newRoot = nn + break + } + } + fp.deleteNode(fp.root) + if n == fp.root { + n = newRoot + } + fp.root = newRoot + if newRoot == nil || !f.checkKnownNode(p, newRoot) { + fp.bestConfirmed = nil + fp.confirmedTd = nil + } + + if n == nil { + break + } + } + if n != nil { + for n.number < head.Number { + nn := &fetcherTreeNode{number: n.number + 1, parent: n} + n.children = append(n.children, nn) + n = nn + fp.nodeCnt++ + } + n.hash = head.Hash + n.td = head.Td + fp.nodeByHash[n.hash] = n + } + } + if n == nil { + // could not find reorg common ancestor or had to delete entire tree, a new root and a resync is needed + if fp.root != nil { + fp.deleteNode(fp.root) + } + n = &fetcherTreeNode{hash: head.Hash, number: head.Number, td: head.Td} + fp.root = n + fp.nodeCnt++ + fp.nodeByHash[n.hash] = n + fp.bestConfirmed = nil + fp.confirmedTd = nil + } + + f.checkKnownNode(p, n) + p.lock.Lock() + p.headInfo = head + fp.lastAnnounced = n + p.lock.Unlock() + f.checkUpdateStats(p, nil) + f.requestChn <- true +} + +// peerHasBlock returns true if we can assume the peer knows the given block +// based on its announcements +func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64) bool { + f.lock.Lock() + defer f.lock.Unlock() + + fp := f.peers[p] + if fp == nil || fp.root == nil { + return false + } + + if number >= fp.root.number { + // it is recent enough that if it is known, is should be in the peer's block tree + return fp.nodeByHash[hash] != nil + } + f.chain.LockChain() + defer f.chain.UnlockChain() + // if it's older than the peer's block tree root but it's in the same canonical chain + // than the root, we can still be sure the peer knows it + return core.GetCanonicalHash(f.pm.chainDb, fp.root.number) == fp.root.hash && core.GetCanonicalHash(f.pm.chainDb, number) == hash +} + +// request initiates a header download request from a certain peer +func (f *lightFetcher) request(p *peer, reqID uint64, n *fetcherTreeNode, amount uint64) (uint64, bool) { + fp := f.peers[p] + if fp == nil { + glog.V(logger.Debug).Infof("request: unknown peer") + p.fcServer.DeassignRequest(reqID) + return 0, false + } + if fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root) { + f.syncing = true + go func() { + glog.V(logger.Debug).Infof("synchronising with peer %v", p.id) + f.pm.synchronise(p) + f.syncDone <- p + }() + p.fcServer.DeassignRequest(reqID) + return 0, false + } + + n.requested = true + cost := p.GetRequestCost(GetBlockHeadersMsg, int(amount)) + p.fcServer.SendRequest(reqID, cost) + f.reqMu.Lock() + f.requested[reqID] = fetchRequest{hash: n.hash, amount: amount, peer: p, sent: mclock.Now()} + f.reqMu.Unlock() + go p.RequestHeadersByHash(reqID, cost, n.hash, int(amount), 0, true) + go func() { + time.Sleep(hardRequestTimeout) + f.timeoutChn <- reqID + }() + return reqID, true +} + +// requestAmount calculates the amount of headers to be downloaded starting +// from a certain head backwards +func (f *lightFetcher) requestAmount(p *peer, n *fetcherTreeNode) uint64 { + amount := uint64(0) + nn := n + for nn != nil && !f.checkKnownNode(p, nn) { + nn = nn.parent + amount++ + } + if nn == nil { + amount = n.number + } + return amount +} + +// requestedID tells if a certain reqID has been requested by the fetcher +func (f *lightFetcher) requestedID(reqID uint64) bool { + f.reqMu.RLock() + _, ok := f.requested[reqID] + f.reqMu.RUnlock() + return ok +} + +// nextRequest selects the peer and announced head to be requested next, amount +// to be downloaded starting from the head backwards is also returned +func (f *lightFetcher) nextRequest(reqID uint64) (*peer, *fetcherTreeNode, uint64, bool) { + var ( + bestHash common.Hash + bestAmount uint64 + ) + bestTd := f.maxConfirmedTd + + for p, fp := range f.peers { + for hash, n := range fp.nodeByHash { + if !f.checkKnownNode(p, n) && !n.requested && (bestTd == nil || n.td.Cmp(bestTd) >= 0) { + amount := f.requestAmount(p, n) + if bestTd == nil || n.td.Cmp(bestTd) > 0 || amount < bestAmount { + bestHash = hash + bestAmount = amount + bestTd = n.td + } + } + } + } + if bestTd == f.maxConfirmedTd { + return nil, nil, 0, false + } + + peer, _, locked := f.pm.serverPool.selectPeer(reqID, func(p *peer) (bool, time.Duration) { + fp := f.peers[p] + if fp == nil || fp.nodeByHash[bestHash] == nil { + return false, 0 + } + return true, p.fcServer.CanSend(p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))) + }) + if !locked { + return nil, nil, 0, true + } + var node *fetcherTreeNode + if peer != nil { + node = f.peers[peer].nodeByHash[bestHash] + } + return peer, node, bestAmount, false +} + +// deliverHeaders delivers header download request responses for processing +func (f *lightFetcher) deliverHeaders(peer *peer, reqID uint64, headers []*types.Header) { + f.deliverChn <- fetchResponse{reqID: reqID, headers: headers, peer: peer} +} + +// processResponse processes header download request responses, returns true if successful +func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) bool { + if uint64(len(resp.headers)) != req.amount || resp.headers[0].Hash() != req.hash { + glog.V(logger.Debug).Infof("response mismatch %v %016x != %v %016x", len(resp.headers), resp.headers[0].Hash().Bytes()[:8], req.amount, req.hash[:8]) + return false + } + headers := make([]*types.Header, req.amount) + for i, header := range resp.headers { + headers[int(req.amount)-1-i] = header + } + if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil { + if err == core.BlockFutureErr { + return true + } + glog.V(logger.Debug).Infof("InsertHeaderChain error: %v", err) + return false + } + tds := make([]*big.Int, len(headers)) + for i, header := range headers { + td := f.chain.GetTd(header.Hash(), header.Number.Uint64()) + if td == nil { + glog.V(logger.Debug).Infof("TD not found for header %v of %v", i+1, len(headers)) + return false + } + tds[i] = td + } + f.newHeaders(headers, tds) + return true +} + +// newHeaders updates the block trees of all active peers according to a newly +// downloaded and validated batch or headers +func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) { + var maxTd *big.Int + for p, fp := range f.peers { + if !f.checkAnnouncedHeaders(fp, headers, tds) { + glog.V(logger.Debug).Infof("announce inconsistency by peer %v", p.id) + go f.pm.removePeer(p.id) + } + if fp.confirmedTd != nil && (maxTd == nil || maxTd.Cmp(fp.confirmedTd) > 0) { + maxTd = fp.confirmedTd + } + } + if maxTd != nil { + f.updateMaxConfirmedTd(maxTd) + } +} + +// checkAnnouncedHeaders updates peer's block tree if necessary after validating +// a batch of headers. It searches for the latest header in the batch that has a +// matching tree node (if any), and if it has not been marked as known already, +// sets it and its parents to known (even those which are older than the currently +// validated ones). Return value shows if all hashes, numbers and Tds matched +// correctly to the announced values (otherwise the peer should be dropped). +func (f *lightFetcher) checkAnnouncedHeaders(fp *fetcherPeerInfo, headers []*types.Header, tds []*big.Int) bool { + var ( + n *fetcherTreeNode + header *types.Header + td *big.Int + ) + + for i := len(headers) - 1; ; i-- { + if i < 0 { + if n == nil { + // no more headers and nothing to match + return true + } + // we ran out of recently delivered headers but have not reached a node known by this peer yet, continue matching + td = f.chain.GetTd(header.ParentHash, header.Number.Uint64()-1) + header = f.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) + } else { + header = headers[i] + td = tds[i] + } + hash := header.Hash() + number := header.Number.Uint64() + if n == nil { + n = fp.nodeByHash[hash] + } + if n != nil { + if n.td == nil { + // node was unannounced + if nn := fp.nodeByHash[hash]; nn != nil { + // if there was already a node with the same hash, continue there and drop this one + nn.children = append(nn.children, n.children...) + n.children = nil + fp.deleteNode(n) + n = nn + } else { + n.hash = hash + n.td = td + fp.nodeByHash[hash] = n + } + } + // check if it matches the header + if n.hash != hash || n.number != number || n.td.Cmp(td) != 0 { + // peer has previously made an invalid announcement + return false + } + if n.known { + // we reached a known node that matched our expectations, return with success + return true + } + n.known = true + if fp.confirmedTd == nil || td.Cmp(fp.confirmedTd) > 0 { + fp.confirmedTd = td + fp.bestConfirmed = n + } + n = n.parent + if n == nil { + return true + } + } + } +} + +// checkSyncedHeaders updates peer's block tree after synchronisation by marking +// downloaded headers as known. If none of the announced headers are found after +// syncing, the peer is dropped. +func (f *lightFetcher) checkSyncedHeaders(p *peer) { + fp := f.peers[p] + if fp == nil { + glog.V(logger.Debug).Infof("checkSyncedHeaders: unknown peer") + return + } + n := fp.lastAnnounced + var td *big.Int + for n != nil { + if td = f.chain.GetTd(n.hash, n.number); td != nil { + break + } + n = n.parent + } + // now n is the latest downloaded header after syncing + if n == nil { + glog.V(logger.Debug).Infof("synchronisation failed with peer %v", p.id) + go f.pm.removePeer(p.id) + } else { + header := f.chain.GetHeader(n.hash, n.number) + f.newHeaders([]*types.Header{header}, []*big.Int{td}) + } +} + +// checkKnownNode checks if a block tree node is known (downloaded and validated) +// If it was not known previously but found in the database, sets its known flag +func (f *lightFetcher) checkKnownNode(p *peer, n *fetcherTreeNode) bool { + if n.known { + return true + } + td := f.chain.GetTd(n.hash, n.number) + if td == nil { + return false + } + + fp := f.peers[p] + if fp == nil { + glog.V(logger.Debug).Infof("checkKnownNode: unknown peer") + return false + } + header := f.chain.GetHeader(n.hash, n.number) + if !f.checkAnnouncedHeaders(fp, []*types.Header{header}, []*big.Int{td}) { + glog.V(logger.Debug).Infof("announce inconsistency by peer %v", p.id) + go f.pm.removePeer(p.id) + } + if fp.confirmedTd != nil { + f.updateMaxConfirmedTd(fp.confirmedTd) + } + return n.known +} + +// deleteNode deletes a node and its child subtrees from a peer's block tree +func (fp *fetcherPeerInfo) deleteNode(n *fetcherTreeNode) { + if n.parent != nil { + for i, nn := range n.parent.children { + if nn == n { + n.parent.children = append(n.parent.children[:i], n.parent.children[i+1:]...) + break + } + } + } + for { + if n.td != nil { + delete(fp.nodeByHash, n.hash) + } + fp.nodeCnt-- + if len(n.children) == 0 { + return + } + for i, nn := range n.children { + if i == 0 { + n = nn + } else { + fp.deleteNode(nn) + } + } + } +} + +// updateStatsEntry items form a linked list that is expanded with a new item every time a new head with a higher Td +// than the previous one has been downloaded and validated. The list contains a series of maximum confirmed Td values +// and the time these values have been confirmed, both increasing monotonically. A maximum confirmed Td is calculated +// both globally for all peers and also for each individual peer (meaning that the given peer has announced the head +// and it has also been downloaded from any peer, either before or after the given announcement). +// The linked list has a global tail where new confirmed Td entries are added and a separate head for each peer, +// pointing to the next Td entry that is higher than the peer's max confirmed Td (nil if it has already confirmed +// the current global head). +type updateStatsEntry struct { + time mclock.AbsTime + td *big.Int + next *updateStatsEntry +} + +// updateMaxConfirmedTd updates the block delay statistics of active peers. Whenever a new highest Td is confirmed, +// adds it to the end of a linked list together with the time it has been confirmed. Then checks which peers have +// already confirmed a head with the same or higher Td (which counts as zero block delay) and updates their statistics. +// Those who have not confirmed such a head by now will be updated by a subsequent checkUpdateStats call with a +// positive block delay value. +func (f *lightFetcher) updateMaxConfirmedTd(td *big.Int) { + if f.maxConfirmedTd == nil || td.Cmp(f.maxConfirmedTd) > 0 { + f.maxConfirmedTd = td + newEntry := &updateStatsEntry{ + time: mclock.Now(), + td: td, + } + if f.lastUpdateStats != nil { + f.lastUpdateStats.next = newEntry + } + f.lastUpdateStats = newEntry + for p := range f.peers { + f.checkUpdateStats(p, newEntry) + } + } +} + +// checkUpdateStats checks those peers who have not confirmed a certain highest Td (or a larger one) by the time it +// has been confirmed by another peer. If they have confirmed such a head by now, their stats are updated with the +// block delay which is (this peer's confirmation time)-(first confirmation time). After blockDelayTimeout has passed, +// the stats are updated with blockDelayTimeout value. In either case, the confirmed or timed out updateStatsEntry +// items are removed from the head of the linked list. +// If a new entry has been added to the global tail, it is passed as a parameter here even though this function +// assumes that it has already been added, so that if the peer's list is empty (all heads confirmed, head is nil), +// it can set the new head to newEntry. +func (f *lightFetcher) checkUpdateStats(p *peer, newEntry *updateStatsEntry) { + now := mclock.Now() + fp := f.peers[p] + if fp == nil { + glog.V(logger.Debug).Infof("checkUpdateStats: unknown peer") + return + } + if newEntry != nil && fp.firstUpdateStats == nil { + fp.firstUpdateStats = newEntry + } + for fp.firstUpdateStats != nil && fp.firstUpdateStats.time <= now-mclock.AbsTime(blockDelayTimeout) { + f.pm.serverPool.adjustBlockDelay(p.poolEntry, blockDelayTimeout) + fp.firstUpdateStats = fp.firstUpdateStats.next + } + if fp.confirmedTd != nil { + for fp.firstUpdateStats != nil && fp.firstUpdateStats.td.Cmp(fp.confirmedTd) <= 0 { + f.pm.serverPool.adjustBlockDelay(p.poolEntry, time.Duration(now-fp.firstUpdateStats.time)) + fp.firstUpdateStats = fp.firstUpdateStats.next + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go new file mode 100644 index 0000000..e45537c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/control.go @@ -0,0 +1,240 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package flowcontrol implements a client side flow control mechanism +package flowcontrol + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" +) + +const fcTimeConst = time.Millisecond + +type ServerParams struct { + BufLimit, MinRecharge uint64 +} + +type ClientNode struct { + params *ServerParams + bufValue uint64 + lastTime mclock.AbsTime + lock sync.Mutex + cm *ClientManager + cmNode *cmNode +} + +func NewClientNode(cm *ClientManager, params *ServerParams) *ClientNode { + node := &ClientNode{ + cm: cm, + params: params, + bufValue: params.BufLimit, + lastTime: mclock.Now(), + } + node.cmNode = cm.addNode(node) + return node +} + +func (peer *ClientNode) Remove(cm *ClientManager) { + cm.removeNode(peer.cmNode) +} + +func (peer *ClientNode) recalcBV(time mclock.AbsTime) { + dt := uint64(time - peer.lastTime) + if time < peer.lastTime { + dt = 0 + } + peer.bufValue += peer.params.MinRecharge * dt / uint64(fcTimeConst) + if peer.bufValue > peer.params.BufLimit { + peer.bufValue = peer.params.BufLimit + } + peer.lastTime = time +} + +func (peer *ClientNode) AcceptRequest() (uint64, bool) { + peer.lock.Lock() + defer peer.lock.Unlock() + + time := mclock.Now() + peer.recalcBV(time) + return peer.bufValue, peer.cm.accept(peer.cmNode, time) +} + +func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) { + peer.lock.Lock() + defer peer.lock.Unlock() + + time := mclock.Now() + peer.recalcBV(time) + peer.bufValue -= cost + peer.recalcBV(time) + rcValue, rcost := peer.cm.processed(peer.cmNode, time) + if rcValue < peer.params.BufLimit { + bv := peer.params.BufLimit - rcValue + if bv > peer.bufValue { + peer.bufValue = bv + } + } + return peer.bufValue, rcost +} + +type ServerNode struct { + bufEstimate uint64 + lastTime mclock.AbsTime + params *ServerParams + sumCost uint64 // sum of req costs sent to this server + pending map[uint64]uint64 // value = sumCost after sending the given req + assignedRequest uint64 // when != 0, only the request with the given ID can be sent to this peer + assignToken chan struct{} // send to this channel before assigning, read from it after deassigning + lock sync.RWMutex +} + +func NewServerNode(params *ServerParams) *ServerNode { + return &ServerNode{ + bufEstimate: params.BufLimit, + lastTime: mclock.Now(), + params: params, + pending: make(map[uint64]uint64), + assignToken: make(chan struct{}, 1), + } +} + +func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { + dt := uint64(time - peer.lastTime) + if time < peer.lastTime { + dt = 0 + } + peer.bufEstimate += peer.params.MinRecharge * dt / uint64(fcTimeConst) + if peer.bufEstimate > peer.params.BufLimit { + peer.bufEstimate = peer.params.BufLimit + } + peer.lastTime = time +} + +// safetyMargin is added to the flow control waiting time when estimated buffer value is low +const safetyMargin = time.Millisecond * 200 + +func (peer *ServerNode) canSend(maxCost uint64) time.Duration { + maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) + if maxCost > peer.params.BufLimit { + maxCost = peer.params.BufLimit + } + if peer.bufEstimate >= maxCost { + return 0 + } + return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge) +} + +// CanSend returns the minimum waiting time required before sending a request +// with the given maximum estimated cost +func (peer *ServerNode) CanSend(maxCost uint64) time.Duration { + peer.lock.RLock() + defer peer.lock.RUnlock() + + return peer.canSend(maxCost) +} + +// AssignRequest tries to assign the server node to the given request, guaranteeing +// that once it returns true, no request will be sent to the node before this one +func (peer *ServerNode) AssignRequest(reqID uint64) bool { + select { + case peer.assignToken <- struct{}{}: + default: + return false + } + peer.lock.Lock() + peer.assignedRequest = reqID + peer.lock.Unlock() + return true +} + +// MustAssignRequest waits until the node can be assigned to the given request. +// It is always guaranteed that assignments are released in a short amount of time. +func (peer *ServerNode) MustAssignRequest(reqID uint64) { + peer.assignToken <- struct{}{} + peer.lock.Lock() + peer.assignedRequest = reqID + peer.lock.Unlock() +} + +// DeassignRequest releases a request assignment in case the planned request +// is not being sent. +func (peer *ServerNode) DeassignRequest(reqID uint64) { + peer.lock.Lock() + if peer.assignedRequest == reqID { + peer.assignedRequest = 0 + <-peer.assignToken + } + peer.lock.Unlock() +} + +// IsAssigned returns true if the server node has already been assigned to a request +// (note that this function returning false does not guarantee that you can assign a request +// immediately afterwards, its only purpose is to help peer selection) +func (peer *ServerNode) IsAssigned() bool { + peer.lock.RLock() + locked := peer.assignedRequest != 0 + peer.lock.RUnlock() + return locked +} + +// blocks until request can be sent +func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { + peer.lock.Lock() + defer peer.lock.Unlock() + + if peer.assignedRequest != reqID { + peer.lock.Unlock() + peer.MustAssignRequest(reqID) + peer.lock.Lock() + } + + peer.recalcBLE(mclock.Now()) + wait := peer.canSend(maxCost) + for wait > 0 { + peer.lock.Unlock() + time.Sleep(wait) + peer.lock.Lock() + peer.recalcBLE(mclock.Now()) + wait = peer.canSend(maxCost) + } + peer.assignedRequest = 0 + <-peer.assignToken + peer.bufEstimate -= maxCost + peer.sumCost += maxCost + if reqID >= 0 { + peer.pending[reqID] = peer.sumCost + } +} + +func (peer *ServerNode) GotReply(reqID, bv uint64) { + + peer.lock.Lock() + defer peer.lock.Unlock() + + if bv > peer.params.BufLimit { + bv = peer.params.BufLimit + } + sc, ok := peer.pending[reqID] + if !ok { + return + } + delete(peer.pending, reqID) + peer.bufEstimate = bv - (peer.sumCost - sc) + peer.lastTime = mclock.Now() +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go new file mode 100644 index 0000000..28cc6f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/flowcontrol/manager.go @@ -0,0 +1,224 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package flowcontrol implements a client side flow control mechanism +package flowcontrol + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" +) + +const rcConst = 1000000 + +type cmNode struct { + node *ClientNode + lastUpdate mclock.AbsTime + serving, recharging bool + rcWeight uint64 + rcValue, rcDelta, startValue int64 + finishRecharge mclock.AbsTime +} + +func (node *cmNode) update(time mclock.AbsTime) { + dt := int64(time - node.lastUpdate) + node.rcValue += node.rcDelta * dt / rcConst + node.lastUpdate = time + if node.recharging && time >= node.finishRecharge { + node.recharging = false + node.rcDelta = 0 + node.rcValue = 0 + } +} + +func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) { + if node.serving && !serving { + node.recharging = true + sumWeight += node.rcWeight + } + node.serving = serving + if node.recharging && serving { + node.recharging = false + sumWeight -= node.rcWeight + } + + node.rcDelta = 0 + if serving { + node.rcDelta = int64(rcConst / simReqCnt) + } + if node.recharging { + node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight) + node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta)) + } +} + +type ClientManager struct { + lock sync.Mutex + nodes map[*cmNode]struct{} + simReqCnt, sumWeight, rcSumValue uint64 + maxSimReq, maxRcSum uint64 + rcRecharge uint64 + resumeQueue chan chan bool + time mclock.AbsTime +} + +func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager { + cm := &ClientManager{ + nodes: make(map[*cmNode]struct{}), + resumeQueue: make(chan chan bool), + rcRecharge: rcConst * rcConst / (100*rcConst/rcTarget - rcConst), + maxSimReq: maxSimReq, + maxRcSum: maxRcSum, + } + go cm.queueProc() + return cm +} + +func (self *ClientManager) Stop() { + self.lock.Lock() + defer self.lock.Unlock() + + // signal any waiting accept routines to return false + self.nodes = make(map[*cmNode]struct{}) + close(self.resumeQueue) +} + +func (self *ClientManager) addNode(cnode *ClientNode) *cmNode { + time := mclock.Now() + node := &cmNode{ + node: cnode, + lastUpdate: time, + finishRecharge: time, + rcWeight: 1, + } + self.lock.Lock() + defer self.lock.Unlock() + + self.nodes[node] = struct{}{} + self.update(mclock.Now()) + return node +} + +func (self *ClientManager) removeNode(node *cmNode) { + self.lock.Lock() + defer self.lock.Unlock() + + time := mclock.Now() + self.stop(node, time) + delete(self.nodes, node) + self.update(time) +} + +// recalc sumWeight +func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { + var sumWeight, rcSum uint64 + for node := range self.nodes { + rc := node.recharging + node.update(time) + if rc && !node.recharging { + rce = true + } + if node.recharging { + sumWeight += node.rcWeight + } + rcSum += uint64(node.rcValue) + } + self.sumWeight = sumWeight + self.rcSumValue = rcSum + return +} + +func (self *ClientManager) update(time mclock.AbsTime) { + for { + firstTime := time + for node := range self.nodes { + if node.recharging && node.finishRecharge < firstTime { + firstTime = node.finishRecharge + } + } + if self.updateNodes(firstTime) { + for node := range self.nodes { + if node.recharging { + node.set(node.serving, self.simReqCnt, self.sumWeight) + } + } + } else { + self.time = time + return + } + } +} + +func (self *ClientManager) canStartReq() bool { + return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum +} + +func (self *ClientManager) queueProc() { + for rc := range self.resumeQueue { + for { + time.Sleep(time.Millisecond * 10) + self.lock.Lock() + self.update(mclock.Now()) + cs := self.canStartReq() + self.lock.Unlock() + if cs { + break + } + } + close(rc) + } +} + +func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool { + self.lock.Lock() + defer self.lock.Unlock() + + self.update(time) + if !self.canStartReq() { + resume := make(chan bool) + self.lock.Unlock() + self.resumeQueue <- resume + <-resume + self.lock.Lock() + if _, ok := self.nodes[node]; !ok { + return false // reject if node has been removed or manager has been stopped + } + } + self.simReqCnt++ + node.set(true, self.simReqCnt, self.sumWeight) + node.startValue = node.rcValue + self.update(self.time) + return true +} + +func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) { + if node.serving { + self.update(time) + self.simReqCnt-- + node.set(false, self.simReqCnt, self.sumWeight) + self.update(time) + } +} + +func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) { + self.lock.Lock() + defer self.lock.Unlock() + + self.stop(node, time) + return uint64(node.rcValue), uint64(node.rcValue - node.startValue) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler.go b/vendor/github.com/ethereum/go-ethereum/les/handler.go new file mode 100644 index 0000000..42a4584 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/handler.go @@ -0,0 +1,907 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + "net" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. + estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header + + ethVersion = 63 // equivalent eth version for the downloader + + MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request + MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request + MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request + MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request + MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request + MaxHeaderProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request + MaxTxSend = 64 // Amount of transactions to be send per request + + disableClientRemovePeer = false +) + +// errIncompatibleConfig is returned if the requested protocols and configs are +// not compatible (low protocol version restrictions and high requirements). +var errIncompatibleConfig = errors.New("incompatible configuration") + +func errResp(code errCode, format string, v ...interface{}) error { + return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) +} + +type hashFetcherFn func(common.Hash) error + +type BlockChain interface { + HasHeader(hash common.Hash) bool + GetHeader(hash common.Hash, number uint64) *types.Header + GetHeaderByHash(hash common.Hash) *types.Header + CurrentHeader() *types.Header + GetTdByHash(hash common.Hash) *big.Int + InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) + Rollback(chain []common.Hash) + Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) + GetHeaderByNumber(number uint64) *types.Header + GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash + LastBlockHash() common.Hash + Genesis() *types.Block +} + +type txPool interface { + // AddTransactions should add the given transactions to the pool. + AddBatch([]*types.Transaction) error +} + +type ProtocolManager struct { + lightSync bool + txpool txPool + txrelay *LesTxRelay + networkId int + chainConfig *params.ChainConfig + blockchain BlockChain + chainDb ethdb.Database + odr *LesOdr + server *LesServer + serverPool *serverPool + + downloader *downloader.Downloader + fetcher *lightFetcher + peers *peerSet + + SubProtocols []p2p.Protocol + + eventMux *event.TypeMux + + // channels for fetcher, syncer, txsyncLoop + newPeerCh chan *peer + quitSync chan struct{} + noMorePeers chan struct{} + + syncMu sync.Mutex + syncing bool + syncDone chan struct{} + + // wait group is used for graceful shutdowns during downloading + // and processing + wg sync.WaitGroup +} + +// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable +// with the ethereum network. +func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { + // Create the protocol manager with the base fields + manager := &ProtocolManager{ + lightSync: lightSync, + eventMux: mux, + blockchain: blockchain, + chainConfig: chainConfig, + chainDb: chainDb, + networkId: networkId, + txpool: txpool, + txrelay: txrelay, + odr: odr, + peers: newPeerSet(), + newPeerCh: make(chan *peer), + quitSync: make(chan struct{}), + noMorePeers: make(chan struct{}), + } + // Initiate a sub-protocol for every implemented version we can handle + manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) + for i, version := range ProtocolVersions { + // Compatible, initialize the sub-protocol + version := version // Closure for the run + manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{ + Name: "les", + Version: version, + Length: ProtocolLengths[i], + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + var entry *poolEntry + peer := manager.newPeer(int(version), networkId, p, rw) + if manager.serverPool != nil { + addr := p.RemoteAddr().(*net.TCPAddr) + entry = manager.serverPool.connect(peer, addr.IP, uint16(addr.Port)) + } + peer.poolEntry = entry + select { + case manager.newPeerCh <- peer: + manager.wg.Add(1) + defer manager.wg.Done() + err := manager.handle(peer) + if entry != nil { + manager.serverPool.disconnect(entry) + } + return err + case <-manager.quitSync: + if entry != nil { + manager.serverPool.disconnect(entry) + } + return p2p.DiscQuitting + } + }, + NodeInfo: func() interface{} { + return manager.NodeInfo() + }, + PeerInfo: func(id discover.NodeID) interface{} { + if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { + return p.Info() + } + return nil + }, + }) + } + if len(manager.SubProtocols) == 0 { + return nil, errIncompatibleConfig + } + + removePeer := manager.removePeer + if disableClientRemovePeer { + removePeer = func(id string) {} + } + + if lightSync { + glog.V(logger.Debug).Infof("LES: create downloader") + manager.downloader = downloader.New(downloader.LightSync, chainDb, manager.eventMux, blockchain.HasHeader, nil, blockchain.GetHeaderByHash, + nil, blockchain.CurrentHeader, nil, nil, nil, blockchain.GetTdByHash, + blockchain.InsertHeaderChain, nil, nil, blockchain.Rollback, removePeer) + } + + if odr != nil { + odr.removePeer = removePeer + } + + /*validator := func(block *types.Block, parent *types.Block) error { + return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false) + } + heighter := func() uint64 { + return chainman.LastBlockNumberU64() + } + manager.fetcher = fetcher.New(chainman.GetBlockNoOdr, validator, nil, heighter, chainman.InsertChain, manager.removePeer) + */ + return manager, nil +} + +func (pm *ProtocolManager) removePeer(id string) { + // Short circuit if the peer was already removed + peer := pm.peers.Peer(id) + if peer == nil { + return + } + if err := pm.peers.Unregister(id); err != nil { + if err == errNotRegistered { + return + } + glog.V(logger.Error).Infoln("Removal failed:", err) + } + glog.V(logger.Debug).Infoln("Removing peer", id) + + // Unregister the peer from the downloader and Ethereum peer set + glog.V(logger.Debug).Infof("LES: unregister peer %v", id) + if pm.lightSync { + pm.downloader.UnregisterPeer(id) + if pm.txrelay != nil { + pm.txrelay.removePeer(id) + } + if pm.fetcher != nil { + pm.fetcher.removePeer(peer) + } + } + // Hard disconnect at the networking layer + if peer != nil { + peer.Peer.Disconnect(p2p.DiscUselessPeer) + } +} + +func (pm *ProtocolManager) Start(srvr *p2p.Server) { + var topicDisc *discv5.Network + if srvr != nil { + topicDisc = srvr.DiscV5 + } + lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8])) + if pm.lightSync { + // start sync handler + if srvr != nil { // srvr is nil during testing + pm.serverPool = newServerPool(pm.chainDb, []byte("serverPool/"), srvr, lesTopic, pm.quitSync, &pm.wg) + pm.odr.serverPool = pm.serverPool + pm.fetcher = newLightFetcher(pm) + } + go pm.syncer() + } else { + if topicDisc != nil { + go func() { + glog.V(logger.Info).Infoln("Starting registering topic", string(lesTopic)) + topicDisc.RegisterTopic(lesTopic, pm.quitSync) + glog.V(logger.Info).Infoln("Stopped registering topic", string(lesTopic)) + }() + } + go func() { + for range pm.newPeerCh { + } + }() + } +} + +func (pm *ProtocolManager) Stop() { + // Showing a log message. During download / process this could actually + // take between 5 to 10 seconds and therefor feedback is required. + glog.V(logger.Info).Infoln("Stopping light ethereum protocol handler...") + + // Quit the sync loop. + // After this send has completed, no new peers will be accepted. + pm.noMorePeers <- struct{}{} + + close(pm.quitSync) // quits syncer, fetcher + + // Disconnect existing sessions. + // This also closes the gate for any new registrations on the peer set. + // sessions which are already established but not added to pm.peers yet + // will exit when they try to register. + pm.peers.Close() + + // Wait for any process action + pm.wg.Wait() + + glog.V(logger.Info).Infoln("Light ethereum protocol handler stopped") +} + +func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { + return newPeer(pv, nv, p, newMeteredMsgWriter(rw)) +} + +// handle is the callback invoked to manage the life cycle of a les peer. When +// this function terminates, the peer is disconnected. +func (pm *ProtocolManager) handle(p *peer) error { + glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name()) + + // Execute the LES handshake + td, head, genesis := pm.blockchain.Status() + headNum := core.GetBlockNumber(pm.chainDb, head) + if err := p.Handshake(td, head, headNum, genesis, pm.server); err != nil { + glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err) + return err + } + if rw, ok := p.rw.(*meteredMsgReadWriter); ok { + rw.Init(p.version) + } + // Register the peer locally + glog.V(logger.Detail).Infof("%v: adding peer", p) + if err := pm.peers.Register(p); err != nil { + glog.V(logger.Error).Infof("%v: addition failed: %v", p, err) + return err + } + defer func() { + if pm.server != nil && pm.server.fcManager != nil && p.fcClient != nil { + p.fcClient.Remove(pm.server.fcManager) + } + pm.removePeer(p.id) + }() + + // Register the peer in the downloader. If the downloader considers it banned, we disconnect + glog.V(logger.Debug).Infof("LES: register peer %v", p.id) + if pm.lightSync { + requestHeadersByHash := func(origin common.Hash, amount int, skip int, reverse bool) error { + reqID := getNextReqID() + cost := p.GetRequestCost(GetBlockHeadersMsg, amount) + p.fcServer.MustAssignRequest(reqID) + p.fcServer.SendRequest(reqID, cost) + return p.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) + } + requestHeadersByNumber := func(origin uint64, amount int, skip int, reverse bool) error { + reqID := getNextReqID() + cost := p.GetRequestCost(GetBlockHeadersMsg, amount) + p.fcServer.MustAssignRequest(reqID) + p.fcServer.SendRequest(reqID, cost) + return p.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) + } + if err := pm.downloader.RegisterPeer(p.id, ethVersion, p.HeadAndTd, + requestHeadersByHash, requestHeadersByNumber, nil, nil, nil); err != nil { + return err + } + if pm.txrelay != nil { + pm.txrelay.addPeer(p) + } + + p.lock.Lock() + head := p.headInfo + p.lock.Unlock() + if pm.fetcher != nil { + pm.fetcher.addPeer(p) + pm.fetcher.announce(p, head) + } + + if p.poolEntry != nil { + pm.serverPool.registered(p.poolEntry) + } + } + + stop := make(chan struct{}) + defer close(stop) + go func() { + // new block announce loop + for { + select { + case announce := <-p.announceChn: + p.SendAnnounce(announce) + case <-stop: + return + } + } + }() + + // main loop. handle incoming messages. + for { + if err := pm.handleMsg(p); err != nil { + glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err) + return err + } + } +} + +var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg} + +// handleMsg is invoked whenever an inbound message is received from a remote +// peer. The remote connection is torn down upon returning any error. +func (pm *ProtocolManager) handleMsg(p *peer) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + + glog.V(logger.Debug).Infoln("msg:", msg.Code, msg.Size) + + costs := p.fcCosts[msg.Code] + reject := func(reqCnt, maxCnt uint64) bool { + if p.fcClient == nil || reqCnt > maxCnt { + return true + } + bufValue, _ := p.fcClient.AcceptRequest() + cost := costs.baseCost + reqCnt*costs.reqCost + if cost > pm.server.defParams.BufLimit { + cost = pm.server.defParams.BufLimit + } + if cost > bufValue { + glog.V(logger.Error).Infof("Request from %v came %v too early", p.id, time.Duration((cost-bufValue)*1000000/pm.server.defParams.MinRecharge)) + return true + } + return false + } + + if msg.Size > ProtocolMaxMsgSize { + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + defer msg.Discard() + + var deliverMsg *Msg + + // Handle the message depending on its contents + switch msg.Code { + case StatusMsg: + glog.V(logger.Debug).Infof("<=== StatusMsg from peer %v", p.id) + // Status messages should never arrive after the handshake + return errResp(ErrExtraStatusMsg, "uncontrolled status message") + + // Block header query, collect the requested headers and reply + case AnnounceMsg: + glog.V(logger.Debug).Infoln("<=== AnnounceMsg from peer %v:", p.id) + + var req announceData + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + glog.V(logger.Detail).Infoln("AnnounceMsg:", req.Number, req.Hash, req.Td, req.ReorgDepth) + if pm.fetcher != nil { + pm.fetcher.announce(p, &req) + } + + case GetBlockHeadersMsg: + glog.V(logger.Debug).Infof("<=== GetBlockHeadersMsg from peer %v", p.id) + // Decode the complex header query + var req struct { + ReqID uint64 + Query getBlockHeadersData + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + + query := req.Query + if reject(query.Amount, MaxHeaderFetch) { + return errResp(ErrRequestRejected, "") + } + + hashMode := query.Origin.Hash != (common.Hash{}) + + // Gather headers until the fetch or network limits is reached + var ( + bytes common.StorageSize + headers []*types.Header + unknown bool + ) + for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit { + // Retrieve the next header satisfying the query + var origin *types.Header + if hashMode { + origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash) + } else { + origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) + } + if origin == nil { + break + } + number := origin.Number.Uint64() + headers = append(headers, origin) + bytes += estHeaderRlpSize + + // Advance to the next header of the query + switch { + case query.Origin.Hash != (common.Hash{}) && query.Reverse: + // Hash based traversal towards the genesis block + for i := 0; i < int(query.Skip)+1; i++ { + if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil { + query.Origin.Hash = header.ParentHash + number-- + } else { + unknown = true + break + } + } + case query.Origin.Hash != (common.Hash{}) && !query.Reverse: + // Hash based traversal towards the leaf block + if header := pm.blockchain.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil { + if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash { + query.Origin.Hash = header.Hash() + } else { + unknown = true + } + } else { + unknown = true + } + case query.Reverse: + // Number based traversal towards the genesis block + if query.Origin.Number >= query.Skip+1 { + query.Origin.Number -= (query.Skip + 1) + } else { + unknown = true + } + + case !query.Reverse: + // Number based traversal towards the leaf block + query.Origin.Number += (query.Skip + 1) + } + } + + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + query.Amount*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, query.Amount, rcost) + return p.SendBlockHeaders(req.ReqID, bv, headers) + + case BlockHeadersMsg: + if pm.downloader == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== BlockHeadersMsg from peer %v", p.id) + // A batch of headers arrived to one of our previous requests + var resp struct { + ReqID, BV uint64 + Headers []*types.Header + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + if pm.fetcher != nil && pm.fetcher.requestedID(resp.ReqID) { + pm.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) + } else { + err := pm.downloader.DeliverHeaders(p.id, resp.Headers) + if err != nil { + glog.V(logger.Debug).Infoln(err) + } + } + + case GetBlockBodiesMsg: + glog.V(logger.Debug).Infof("<=== GetBlockBodiesMsg from peer %v", p.id) + // Decode the retrieval message + var req struct { + ReqID uint64 + Hashes []common.Hash + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather blocks until the fetch or network limits is reached + var ( + bytes int + bodies []rlp.RawValue + ) + reqCnt := len(req.Hashes) + if reject(uint64(reqCnt), MaxBodyFetch) { + return errResp(ErrRequestRejected, "") + } + for _, hash := range req.Hashes { + if bytes >= softResponseLimit { + break + } + // Retrieve the requested block body, stopping if enough was found + if data := core.GetBodyRLP(pm.chainDb, hash, core.GetBlockNumber(pm.chainDb, hash)); len(data) != 0 { + bodies = append(bodies, data) + bytes += len(data) + } + } + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + return p.SendBlockBodiesRLP(req.ReqID, bv, bodies) + + case BlockBodiesMsg: + if pm.odr == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== BlockBodiesMsg from peer %v", p.id) + // A batch of block bodies arrived to one of our previous requests + var resp struct { + ReqID, BV uint64 + Data []*types.Body + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgBlockBodies, + ReqID: resp.ReqID, + Obj: resp.Data, + } + + case GetCodeMsg: + glog.V(logger.Debug).Infof("<=== GetCodeMsg from peer %v", p.id) + // Decode the retrieval message + var req struct { + ReqID uint64 + Reqs []CodeReq + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + bytes int + data [][]byte + ) + reqCnt := len(req.Reqs) + if reject(uint64(reqCnt), MaxCodeFetch) { + return errResp(ErrRequestRejected, "") + } + for _, req := range req.Reqs { + // Retrieve the requested state entry, stopping if enough was found + if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { + if trie, _ := trie.New(header.Root, pm.chainDb); trie != nil { + sdata := trie.Get(req.AccKey) + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + entry, _ := pm.chainDb.Get(acc.CodeHash) + if bytes+len(entry) >= softResponseLimit { + break + } + data = append(data, entry) + bytes += len(entry) + } + } + } + } + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + return p.SendCode(req.ReqID, bv, data) + + case CodeMsg: + if pm.odr == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== CodeMsg from peer %v", p.id) + // A batch of node state data arrived to one of our previous requests + var resp struct { + ReqID, BV uint64 + Data [][]byte + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgCode, + ReqID: resp.ReqID, + Obj: resp.Data, + } + + case GetReceiptsMsg: + glog.V(logger.Debug).Infof("<=== GetReceiptsMsg from peer %v", p.id) + // Decode the retrieval message + var req struct { + ReqID uint64 + Hashes []common.Hash + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + bytes int + receipts []rlp.RawValue + ) + reqCnt := len(req.Hashes) + if reject(uint64(reqCnt), MaxReceiptFetch) { + return errResp(ErrRequestRejected, "") + } + for _, hash := range req.Hashes { + if bytes >= softResponseLimit { + break + } + // Retrieve the requested block's receipts, skipping if unknown to us + results := core.GetBlockReceipts(pm.chainDb, hash, core.GetBlockNumber(pm.chainDb, hash)) + if results == nil { + if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + continue + } + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(results); err != nil { + glog.V(logger.Error).Infof("failed to encode receipt: %v", err) + } else { + receipts = append(receipts, encoded) + bytes += len(encoded) + } + } + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + return p.SendReceiptsRLP(req.ReqID, bv, receipts) + + case ReceiptsMsg: + if pm.odr == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== ReceiptsMsg from peer %v", p.id) + // A batch of receipts arrived to one of our previous requests + var resp struct { + ReqID, BV uint64 + Receipts []types.Receipts + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgReceipts, + ReqID: resp.ReqID, + Obj: resp.Receipts, + } + + case GetProofsMsg: + glog.V(logger.Debug).Infof("<=== GetProofsMsg from peer %v", p.id) + // Decode the retrieval message + var req struct { + ReqID uint64 + Reqs []ProofReq + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + bytes int + proofs proofsData + ) + reqCnt := len(req.Reqs) + if reject(uint64(reqCnt), MaxProofsFetch) { + return errResp(ErrRequestRejected, "") + } + for _, req := range req.Reqs { + if bytes >= softResponseLimit { + break + } + // Retrieve the requested state entry, stopping if enough was found + if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil { + if tr, _ := trie.New(header.Root, pm.chainDb); tr != nil { + if len(req.AccKey) > 0 { + sdata := tr.Get(req.AccKey) + tr = nil + var acc state.Account + if err := rlp.DecodeBytes(sdata, &acc); err == nil { + tr, _ = trie.New(acc.Root, pm.chainDb) + } + } + if tr != nil { + proof := tr.Prove(req.Key) + proofs = append(proofs, proof) + bytes += len(proof) + } + } + } + } + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + return p.SendProofs(req.ReqID, bv, proofs) + + case ProofsMsg: + if pm.odr == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== ProofsMsg from peer %v", p.id) + // A batch of merkle proofs arrived to one of our previous requests + var resp struct { + ReqID, BV uint64 + Data [][]rlp.RawValue + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgProofs, + ReqID: resp.ReqID, + Obj: resp.Data, + } + + case GetHeaderProofsMsg: + glog.V(logger.Debug).Infof("<=== GetHeaderProofsMsg from peer %v", p.id) + // Decode the retrieval message + var req struct { + ReqID uint64 + Reqs []ChtReq + } + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + bytes int + proofs []ChtResp + ) + reqCnt := len(req.Reqs) + if reject(uint64(reqCnt), MaxHeaderProofsFetch) { + return errResp(ErrRequestRejected, "") + } + for _, req := range req.Reqs { + if bytes >= softResponseLimit { + break + } + + if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil { + if root := getChtRoot(pm.chainDb, req.ChtNum); root != (common.Hash{}) { + if tr, _ := trie.New(root, pm.chainDb); tr != nil { + var encNumber [8]byte + binary.BigEndian.PutUint64(encNumber[:], req.BlockNum) + proof := tr.Prove(encNumber[:]) + proofs = append(proofs, ChtResp{Header: header, Proof: proof}) + bytes += len(proof) + estHeaderRlpSize + } + } + } + } + bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + return p.SendHeaderProofs(req.ReqID, bv, proofs) + + case HeaderProofsMsg: + if pm.odr == nil { + return errResp(ErrUnexpectedResponse, "") + } + + glog.V(logger.Debug).Infof("<=== HeaderProofsMsg from peer %v", p.id) + var resp struct { + ReqID, BV uint64 + Data []ChtResp + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.GotReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgHeaderProofs, + ReqID: resp.ReqID, + Obj: resp.Data, + } + + case SendTxMsg: + if pm.txpool == nil { + return errResp(ErrUnexpectedResponse, "") + } + // Transactions arrived, parse all of them and deliver to the pool + var txs []*types.Transaction + if err := msg.Decode(&txs); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + reqCnt := len(txs) + if reject(uint64(reqCnt), MaxTxSend) { + return errResp(ErrRequestRejected, "") + } + + if err := pm.txpool.AddBatch(txs); err != nil { + return errResp(ErrUnexpectedResponse, "msg: %v", err) + } + + _, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) + pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) + + default: + glog.V(logger.Debug).Infof("<=== unknown message with code %d from peer %v", msg.Code, p.id) + return errResp(ErrInvalidMsgCode, "%v", msg.Code) + } + + if deliverMsg != nil { + return pm.odr.Deliver(p, deliverMsg) + } + + return nil +} + +// NodeInfo retrieves some protocol metadata about the running host node. +func (self *ProtocolManager) NodeInfo() *eth.EthNodeInfo { + return ð.EthNodeInfo{ + Network: self.networkId, + Difficulty: self.blockchain.GetTdByHash(self.blockchain.LastBlockHash()), + Genesis: self.blockchain.Genesis().Hash(), + Head: self.blockchain.LastBlockHash(), + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler_test.go b/vendor/github.com/ethereum/go-ethereum/les/handler_test.go new file mode 100644 index 0000000..0b94d0d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/handler_test.go @@ -0,0 +1,338 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error { + type resp struct { + ReqID, BV uint64 + Data interface{} + } + return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data}) +} + +// Tests that block headers can be retrieved from a remote chain based on user queries. +func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) } + +func testGetBlockHeaders(t *testing.T, protocol int) { + pm, _, _ := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil) + bc := pm.blockchain.(*core.BlockChain) + peer, _ := newTestPeer(t, "peer", protocol, pm, true) + defer peer.close() + + // Create a "random" unknown hash for testing + var unknown common.Hash + for i := range unknown { + unknown[i] = byte(i) + } + // Create a batch of tests for various scenarios + limit := uint64(MaxHeaderFetch) + tests := []struct { + query *getBlockHeadersData // The query to execute for header retrieval + expect []common.Hash // The hashes of the block whose headers are expected + }{ + // A single random block should be retrievable by hash and number too + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, + []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, + []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, + }, + // Multiple headers should be retrievable in both directions + { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, + []common.Hash{ + bc.GetBlockByNumber(limit / 2).Hash(), + bc.GetBlockByNumber(limit/2 + 1).Hash(), + bc.GetBlockByNumber(limit/2 + 2).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, + []common.Hash{ + bc.GetBlockByNumber(limit / 2).Hash(), + bc.GetBlockByNumber(limit/2 - 1).Hash(), + bc.GetBlockByNumber(limit/2 - 2).Hash(), + }, + }, + // Multiple headers with skip lists should be retrievable + { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, + []common.Hash{ + bc.GetBlockByNumber(limit / 2).Hash(), + bc.GetBlockByNumber(limit/2 + 4).Hash(), + bc.GetBlockByNumber(limit/2 + 8).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + bc.GetBlockByNumber(limit / 2).Hash(), + bc.GetBlockByNumber(limit/2 - 4).Hash(), + bc.GetBlockByNumber(limit/2 - 8).Hash(), + }, + }, + // The chain endpoints should be retrievable + { + &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, + []common.Hash{bc.GetBlockByNumber(0).Hash()}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, + []common.Hash{bc.CurrentBlock().Hash()}, + }, + // Ensure protocol limits are honored + /*{ + &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, + bc.GetBlockHashesFromHash(bc.CurrentBlock().Hash(), limit), + },*/ + // Check that requesting more than available is handled gracefully + { + &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, + []common.Hash{ + bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), + bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, + []common.Hash{ + bc.GetBlockByNumber(4).Hash(), + bc.GetBlockByNumber(0).Hash(), + }, + }, + // Check that requesting more than available is handled gracefully, even if mid skip + { + &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, + []common.Hash{ + bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), + bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(), + }, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, + []common.Hash{ + bc.GetBlockByNumber(4).Hash(), + bc.GetBlockByNumber(1).Hash(), + }, + }, + // Check that non existing headers aren't returned + { + &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, + []common.Hash{}, + }, { + &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, + []common.Hash{}, + }, + } + // Run each of the tests and verify the results against the chain + var reqID uint64 + for i, tt := range tests { + // Collect the headers to expect in the response + headers := []*types.Header{} + for _, hash := range tt.expect { + headers = append(headers, bc.GetHeaderByHash(hash)) + } + // Send the hash request and verify the response + reqID++ + cost := peer.GetRequestCost(GetBlockHeadersMsg, int(tt.query.Amount)) + sendRequest(peer.app, GetBlockHeadersMsg, reqID, cost, tt.query) + if err := expectResponse(peer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { + t.Errorf("test %d: headers mismatch: %v", i, err) + } + } +} + +// Tests that block contents can be retrieved from a remote chain based on their hashes. +func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) } + +func testGetBlockBodies(t *testing.T, protocol int) { + pm, _, _ := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil) + bc := pm.blockchain.(*core.BlockChain) + peer, _ := newTestPeer(t, "peer", protocol, pm, true) + defer peer.close() + + // Create a batch of tests for various scenarios + limit := MaxBodyFetch + tests := []struct { + random int // Number of blocks to fetch randomly from the chain + explicit []common.Hash // Explicitly requested blocks + available []bool // Availability of explicitly requested blocks + expected int // Total number of existing blocks to expect + }{ + {1, nil, nil, 1}, // A single random block should be retrievable + {10, nil, nil, 10}, // Multiple random blocks should be retrievable + {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable + //{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned + {0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable + {0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable + {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned + + // Existing and non-existing blocks interleaved should not cause problems + {0, []common.Hash{ + {}, + bc.GetBlockByNumber(1).Hash(), + {}, + bc.GetBlockByNumber(10).Hash(), + {}, + bc.GetBlockByNumber(100).Hash(), + {}, + }, []bool{false, true, false, true, false, true, false}, 3}, + } + // Run each of the tests and verify the results against the chain + var reqID uint64 + for i, tt := range tests { + // Collect the hashes to request, and the response to expect + hashes, seen := []common.Hash{}, make(map[int64]bool) + bodies := []*types.Body{} + + for j := 0; j < tt.random; j++ { + for { + num := rand.Int63n(int64(bc.CurrentBlock().NumberU64())) + if !seen[num] { + seen[num] = true + + block := bc.GetBlockByNumber(uint64(num)) + hashes = append(hashes, block.Hash()) + if len(bodies) < tt.expected { + bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + break + } + } + } + for j, hash := range tt.explicit { + hashes = append(hashes, hash) + if tt.available[j] && len(bodies) < tt.expected { + block := bc.GetBlockByHash(hash) + bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) + } + } + reqID++ + // Send the hash request and verify the response + cost := peer.GetRequestCost(GetBlockBodiesMsg, len(hashes)) + sendRequest(peer.app, GetBlockBodiesMsg, reqID, cost, hashes) + if err := expectResponse(peer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { + t.Errorf("test %d: bodies mismatch: %v", i, err) + } + } +} + +// Tests that the contract codes can be retrieved based on account addresses. +func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) } + +func testGetCode(t *testing.T, protocol int) { + // Assemble the test environment + pm, _, _ := newTestProtocolManagerMust(t, false, 4, testChainGen) + bc := pm.blockchain.(*core.BlockChain) + peer, _ := newTestPeer(t, "peer", protocol, pm, true) + defer peer.close() + + var codereqs []*CodeReq + var codes [][]byte + + for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { + header := bc.GetHeaderByNumber(i) + req := &CodeReq{ + BHash: header.Hash(), + AccKey: crypto.Keccak256(testContractAddr[:]), + } + codereqs = append(codereqs, req) + if i >= testContractDeployed { + codes = append(codes, testContractCodeDeployed) + } + } + + cost := peer.GetRequestCost(GetCodeMsg, len(codereqs)) + sendRequest(peer.app, GetCodeMsg, 42, cost, codereqs) + if err := expectResponse(peer.app, CodeMsg, 42, testBufLimit, codes); err != nil { + t.Errorf("codes mismatch: %v", err) + } +} + +// Tests that the transaction receipts can be retrieved based on hashes. +func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) } + +func testGetReceipt(t *testing.T, protocol int) { + // Assemble the test environment + pm, db, _ := newTestProtocolManagerMust(t, false, 4, testChainGen) + bc := pm.blockchain.(*core.BlockChain) + peer, _ := newTestPeer(t, "peer", protocol, pm, true) + defer peer.close() + + // Collect the hashes to request, and the response to expect + hashes, receipts := []common.Hash{}, []types.Receipts{} + for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { + block := bc.GetBlockByNumber(i) + + hashes = append(hashes, block.Hash()) + receipts = append(receipts, core.GetBlockReceipts(db, block.Hash(), block.NumberU64())) + } + // Send the hash request and verify the response + cost := peer.GetRequestCost(GetReceiptsMsg, len(hashes)) + sendRequest(peer.app, GetReceiptsMsg, 42, cost, hashes) + if err := expectResponse(peer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { + t.Errorf("receipts mismatch: %v", err) + } +} + +// Tests that trie merkle proofs can be retrieved +func TestGetProofsLes1(t *testing.T) { testGetReceipt(t, 1) } + +func testGetProofs(t *testing.T, protocol int) { + // Assemble the test environment + pm, db, _ := newTestProtocolManagerMust(t, false, 4, testChainGen) + bc := pm.blockchain.(*core.BlockChain) + peer, _ := newTestPeer(t, "peer", protocol, pm, true) + defer peer.close() + + var proofreqs []ProofReq + var proofs [][]rlp.RawValue + + accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}} + for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { + header := bc.GetHeaderByNumber(i) + root := header.Root + trie, _ := trie.New(root, db) + + for _, acc := range accounts { + req := ProofReq{ + BHash: header.Hash(), + Key: acc[:], + } + proofreqs = append(proofreqs, req) + + proof := trie.Prove(crypto.Keccak256(acc[:])) + proofs = append(proofs, proof) + } + } + // Send the proof request and verify the response + cost := peer.GetRequestCost(GetProofsMsg, len(proofreqs)) + sendRequest(peer.app, GetProofsMsg, 42, cost, proofreqs) + if err := expectResponse(peer.app, ProofsMsg, 42, testBufLimit, proofs); err != nil { + t.Errorf("proofs mismatch: %v", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/helper_test.go b/vendor/github.com/ethereum/go-ethereum/les/helper_test.go new file mode 100644 index 0000000..2c6f34a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/helper_test.go @@ -0,0 +1,363 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains some shares testing functionality, common to multiple +// different files and modules being tested. + +package les + +import ( + "crypto/ecdsa" + "crypto/rand" + "math/big" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/les/flowcontrol" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" +) + +var ( + testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(1000000) + + acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) + + testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") + testContractAddr common.Address + testContractCodeDeployed = testContractCode[16:] + testContractDeployed = uint64(2) + + testBufLimit = uint64(100) + + bigTxGas = new(big.Int).SetUint64(params.TxGas) +) + +/* +contract test { + + uint256[100] data; + + function Put(uint256 addr, uint256 value) { + data[addr] = value; + } + + function Get(uint256 addr) constant returns (uint256 value) { + return data[addr]; + } +} +*/ + +func testChainGen(i int, block *core.BlockGen) { + signer := types.HomesteadSigner{} + + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // acc1Addr passes it on to account #2. + // acc1Addr creates a test contract. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + nonce := block.TxNonce(acc1Addr) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) + nonce++ + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(200000), big.NewInt(0), testContractCode), signer, acc1Key) + testContractAddr = crypto.CreateAddress(acc1Addr, nonce) + block.AddTx(tx1) + block.AddTx(tx2) + block.AddTx(tx3) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data), signer, testBankKey) + block.AddTx(tx) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data), signer, testBankKey) + block.AddTx(tx) + } +} + +func testRCL() RequestCostList { + cl := make(RequestCostList, len(reqList)) + for i, code := range reqList { + cl[i].MsgCode = code + cl[i].BaseCost = 0 + cl[i].ReqCost = 0 + } + return cl +} + +// newTestProtocolManager creates a new protocol manager for testing purposes, +// with the given number of blocks already known, and potential notification +// channels for different events. +func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, ethdb.Database, *LesOdr, error) { + var ( + evmux = new(event.TypeMux) + pow = new(core.FakePow) + db, _ = ethdb.NewMemDatabase() + genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker + odr *LesOdr + chain BlockChain + ) + + if lightSync { + odr = NewLesOdr(db) + chain, _ = light.NewLightChain(odr, chainConfig, pow, evmux) + } else { + blockchain, _ := core.NewBlockChain(db, chainConfig, pow, evmux, vm.Config{}) + gchain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) + if _, err := blockchain.InsertChain(gchain); err != nil { + panic(err) + } + chain = blockchain + } + + pm, err := NewProtocolManager(chainConfig, lightSync, NetworkId, evmux, pow, chain, nil, db, odr, nil) + if err != nil { + return nil, nil, nil, err + } + if !lightSync { + srv := &LesServer{protocolManager: pm} + pm.server = srv + + srv.defParams = &flowcontrol.ServerParams{ + BufLimit: testBufLimit, + MinRecharge: 1, + } + + srv.fcManager = flowcontrol.NewClientManager(50, 10, 1000000000) + srv.fcCostStats = newCostStats(nil) + } + pm.Start(nil) + return pm, db, odr, nil +} + +// newTestProtocolManagerMust creates a new protocol manager for testing purposes, +// with the given number of blocks already known, and potential notification +// channels for different events. In case of an error, the constructor force- +// fails the test. +func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, ethdb.Database, *LesOdr) { + pm, db, odr, err := newTestProtocolManager(lightSync, blocks, generator) + if err != nil { + t.Fatalf("Failed to create protocol manager: %v", err) + } + return pm, db, odr +} + +// testTxPool is a fake, helper transaction pool for testing purposes +type testTxPool struct { + pool []*types.Transaction // Collection of all transactions + added chan<- []*types.Transaction // Notification channel for new transactions + + lock sync.RWMutex // Protects the transaction pool +} + +// AddTransactions appends a batch of transactions to the pool, and notifies any +// listeners if the addition channel is non nil +func (p *testTxPool) AddBatch(txs []*types.Transaction) { + p.lock.Lock() + defer p.lock.Unlock() + + p.pool = append(p.pool, txs...) + if p.added != nil { + p.added <- txs + } +} + +// GetTransactions returns all the transactions known to the pool +func (p *testTxPool) GetTransactions() types.Transactions { + p.lock.RLock() + defer p.lock.RUnlock() + + txs := make([]*types.Transaction, len(p.pool)) + copy(txs, p.pool) + + return txs +} + +// newTestTransaction create a new dummy transaction. +func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction { + tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from) + + return tx +} + +// testPeer is a simulated peer to allow testing direct network calls. +type testPeer struct { + net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging + app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side + *peer +} + +// newTestPeer creates a new peer registered at the given protocol manager. +func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id discover.NodeID + rand.Read(id[:]) + + peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) + + // Start the peer on a new thread + errc := make(chan error, 1) + go func() { + select { + case pm.newPeerCh <- peer: + errc <- pm.handle(peer) + case <-pm.quitSync: + errc <- p2p.DiscQuitting + } + }() + tp := &testPeer{ + app: app, + net: net, + peer: peer, + } + // Execute any implicitly requested handshakes and return + if shake { + td, head, genesis := pm.blockchain.Status() + headNum := pm.blockchain.CurrentHeader().Number.Uint64() + tp.handshake(t, td, head, headNum, genesis) + } + return tp, errc +} + +func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer, <-chan error, *peer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id discover.NodeID + rand.Read(id[:]) + + peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) + peer2 := pm2.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), app) + + // Start the peer on a new thread + errc := make(chan error, 1) + errc2 := make(chan error, 1) + go func() { + select { + case pm.newPeerCh <- peer: + errc <- pm.handle(peer) + case <-pm.quitSync: + errc <- p2p.DiscQuitting + } + }() + go func() { + select { + case pm2.newPeerCh <- peer2: + errc2 <- pm2.handle(peer2) + case <-pm2.quitSync: + errc2 <- p2p.DiscQuitting + } + }() + return peer, errc, peer2, errc2 +} + +// handshake simulates a trivial handshake that expects the same state from the +// remote side as we are simulating locally. +func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash) { + var expList keyValueList + expList = expList.add("protocolVersion", uint64(p.version)) + expList = expList.add("networkId", uint64(NetworkId)) + expList = expList.add("headTd", td) + expList = expList.add("headHash", head) + expList = expList.add("headNum", headNum) + expList = expList.add("genesisHash", genesis) + sendList := make(keyValueList, len(expList)) + copy(sendList, expList) + expList = expList.add("serveHeaders", nil) + expList = expList.add("serveChainSince", uint64(0)) + expList = expList.add("serveStateSince", uint64(0)) + expList = expList.add("txRelay", nil) + expList = expList.add("flowControl/BL", testBufLimit) + expList = expList.add("flowControl/MRR", uint64(1)) + expList = expList.add("flowControl/MRC", testRCL()) + + if err := p2p.ExpectMsg(p.app, StatusMsg, expList); err != nil { + t.Fatalf("status recv: %v", err) + } + if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { + t.Fatalf("status send: %v", err) + } + + p.fcServerParams = &flowcontrol.ServerParams{ + BufLimit: testBufLimit, + MinRecharge: 1, + } +} + +// close terminates the local side of the peer, notifying the remote protocol +// manager of termination. +func (p *testPeer) close() { + p.app.Close() +} + +type testServerPool struct { + peer *peer + lock sync.RWMutex +} + +func (p *testServerPool) setPeer(peer *peer) { + p.lock.Lock() + defer p.lock.Unlock() + + p.peer = peer +} + +func (p *testServerPool) selectPeerWait(uint64, func(*peer) (bool, time.Duration), <-chan struct{}) *peer { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.peer +} + +func (p *testServerPool) adjustResponseTime(*poolEntry, time.Duration, bool) { + +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/metrics.go b/vendor/github.com/ethereum/go-ethereum/les/metrics.go new file mode 100644 index 0000000..0162a1d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/metrics.go @@ -0,0 +1,111 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" +) + +var ( + /* propTxnInPacketsMeter = metrics.NewMeter("eth/prop/txns/in/packets") + propTxnInTrafficMeter = metrics.NewMeter("eth/prop/txns/in/traffic") + propTxnOutPacketsMeter = metrics.NewMeter("eth/prop/txns/out/packets") + propTxnOutTrafficMeter = metrics.NewMeter("eth/prop/txns/out/traffic") + propHashInPacketsMeter = metrics.NewMeter("eth/prop/hashes/in/packets") + propHashInTrafficMeter = metrics.NewMeter("eth/prop/hashes/in/traffic") + propHashOutPacketsMeter = metrics.NewMeter("eth/prop/hashes/out/packets") + propHashOutTrafficMeter = metrics.NewMeter("eth/prop/hashes/out/traffic") + propBlockInPacketsMeter = metrics.NewMeter("eth/prop/blocks/in/packets") + propBlockInTrafficMeter = metrics.NewMeter("eth/prop/blocks/in/traffic") + propBlockOutPacketsMeter = metrics.NewMeter("eth/prop/blocks/out/packets") + propBlockOutTrafficMeter = metrics.NewMeter("eth/prop/blocks/out/traffic") + reqHashInPacketsMeter = metrics.NewMeter("eth/req/hashes/in/packets") + reqHashInTrafficMeter = metrics.NewMeter("eth/req/hashes/in/traffic") + reqHashOutPacketsMeter = metrics.NewMeter("eth/req/hashes/out/packets") + reqHashOutTrafficMeter = metrics.NewMeter("eth/req/hashes/out/traffic") + reqBlockInPacketsMeter = metrics.NewMeter("eth/req/blocks/in/packets") + reqBlockInTrafficMeter = metrics.NewMeter("eth/req/blocks/in/traffic") + reqBlockOutPacketsMeter = metrics.NewMeter("eth/req/blocks/out/packets") + reqBlockOutTrafficMeter = metrics.NewMeter("eth/req/blocks/out/traffic") + reqHeaderInPacketsMeter = metrics.NewMeter("eth/req/headers/in/packets") + reqHeaderInTrafficMeter = metrics.NewMeter("eth/req/headers/in/traffic") + reqHeaderOutPacketsMeter = metrics.NewMeter("eth/req/headers/out/packets") + reqHeaderOutTrafficMeter = metrics.NewMeter("eth/req/headers/out/traffic") + reqBodyInPacketsMeter = metrics.NewMeter("eth/req/bodies/in/packets") + reqBodyInTrafficMeter = metrics.NewMeter("eth/req/bodies/in/traffic") + reqBodyOutPacketsMeter = metrics.NewMeter("eth/req/bodies/out/packets") + reqBodyOutTrafficMeter = metrics.NewMeter("eth/req/bodies/out/traffic") + reqStateInPacketsMeter = metrics.NewMeter("eth/req/states/in/packets") + reqStateInTrafficMeter = metrics.NewMeter("eth/req/states/in/traffic") + reqStateOutPacketsMeter = metrics.NewMeter("eth/req/states/out/packets") + reqStateOutTrafficMeter = metrics.NewMeter("eth/req/states/out/traffic") + reqReceiptInPacketsMeter = metrics.NewMeter("eth/req/receipts/in/packets") + reqReceiptInTrafficMeter = metrics.NewMeter("eth/req/receipts/in/traffic") + reqReceiptOutPacketsMeter = metrics.NewMeter("eth/req/receipts/out/packets") + reqReceiptOutTrafficMeter = metrics.NewMeter("eth/req/receipts/out/traffic")*/ + miscInPacketsMeter = metrics.NewMeter("les/misc/in/packets") + miscInTrafficMeter = metrics.NewMeter("les/misc/in/traffic") + miscOutPacketsMeter = metrics.NewMeter("les/misc/out/packets") + miscOutTrafficMeter = metrics.NewMeter("les/misc/out/traffic") +) + +// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of +// accumulating the above defined metrics based on the data stream contents. +type meteredMsgReadWriter struct { + p2p.MsgReadWriter // Wrapped message stream to meter + version int // Protocol version to select correct meters +} + +// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the +// metrics system is disabled, this function returns the original object. +func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { + if !metrics.Enabled { + return rw + } + return &meteredMsgReadWriter{MsgReadWriter: rw} +} + +// Init sets the protocol version used by the stream to know which meters to +// increment in case of overlapping message ids between protocol versions. +func (rw *meteredMsgReadWriter) Init(version int) { + rw.version = version +} + +func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) { + // Read the message and short circuit in case of an error + msg, err := rw.MsgReadWriter.ReadMsg() + if err != nil { + return msg, err + } + // Account for the data traffic + packets, traffic := miscInPacketsMeter, miscInTrafficMeter + packets.Mark(1) + traffic.Mark(int64(msg.Size)) + + return msg, err +} + +func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error { + // Account for the data traffic + packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter + packets.Mark(1) + traffic.Mark(int64(msg.Size)) + + // Send the packet to the p2p layer + return rw.MsgReadWriter.WriteMsg(msg) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr.go b/vendor/github.com/ethereum/go-ethereum/les/odr.go new file mode 100644 index 0000000..88c7d85 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/odr.go @@ -0,0 +1,249 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "crypto/rand" + "encoding/binary" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +var ( + softRequestTimeout = time.Millisecond * 500 + hardRequestTimeout = time.Second * 10 + retryPeers = time.Second * 1 +) + +// peerDropFn is a callback type for dropping a peer detected as malicious. +type peerDropFn func(id string) + +type odrPeerSelector interface { + selectPeerWait(uint64, func(*peer) (bool, time.Duration), <-chan struct{}) *peer + adjustResponseTime(*poolEntry, time.Duration, bool) +} + +type LesOdr struct { + light.OdrBackend + db ethdb.Database + stop chan struct{} + removePeer peerDropFn + mlock, clock sync.Mutex + sentReqs map[uint64]*sentReq + serverPool odrPeerSelector +} + +func NewLesOdr(db ethdb.Database) *LesOdr { + return &LesOdr{ + db: db, + stop: make(chan struct{}), + sentReqs: make(map[uint64]*sentReq), + } +} + +func (odr *LesOdr) Stop() { + close(odr.stop) +} + +func (odr *LesOdr) Database() ethdb.Database { + return odr.db +} + +// validatorFunc is a function that processes a message and returns true if +// it was a meaningful answer to a given request +type validatorFunc func(ethdb.Database, *Msg) bool + +// sentReq is a request waiting for an answer that satisfies its valFunc +type sentReq struct { + valFunc validatorFunc + sentTo map[*peer]chan struct{} + lock sync.RWMutex // protects acces to sentTo + answered chan struct{} // closed and set to nil when any peer answers it +} + +const ( + MsgBlockBodies = iota + MsgCode + MsgReceipts + MsgProofs + MsgHeaderProofs +) + +// Msg encodes a LES message that delivers reply data for a request +type Msg struct { + MsgType int + ReqID uint64 + Obj interface{} +} + +// Deliver is called by the LES protocol manager to deliver ODR reply messages to waiting requests +func (self *LesOdr) Deliver(peer *peer, msg *Msg) error { + var delivered chan struct{} + self.mlock.Lock() + req, ok := self.sentReqs[msg.ReqID] + self.mlock.Unlock() + if ok { + req.lock.Lock() + delivered, ok = req.sentTo[peer] + req.lock.Unlock() + } + + if !ok { + return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) + } + + if req.valFunc(self.db, msg) { + close(delivered) + req.lock.Lock() + delete(req.sentTo, peer) + if req.answered != nil { + close(req.answered) + req.answered = nil + } + req.lock.Unlock() + return nil + } + return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID) +} + +func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout chan struct{}, reqWg *sync.WaitGroup) { + stime := mclock.Now() + defer func() { + req.lock.Lock() + delete(req.sentTo, peer) + req.lock.Unlock() + reqWg.Done() + }() + + select { + case <-delivered: + if self.serverPool != nil { + self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), false) + } + return + case <-time.After(softRequestTimeout): + close(timeout) + case <-self.stop: + return + } + + select { + case <-delivered: + case <-time.After(hardRequestTimeout): + glog.V(logger.Debug).Infof("ODR hard request timeout from peer %v", peer.id) + go self.removePeer(peer.id) + case <-self.stop: + return + } + if self.serverPool != nil { + self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), true) + } +} + +// networkRequest sends a request to known peers until an answer is received +// or the context is cancelled +func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) error { + answered := make(chan struct{}) + req := &sentReq{ + valFunc: lreq.Valid, + sentTo: make(map[*peer]chan struct{}), + answered: answered, // reply delivered by any peer + } + reqID := getNextReqID() + self.mlock.Lock() + self.sentReqs[reqID] = req + self.mlock.Unlock() + + reqWg := new(sync.WaitGroup) + reqWg.Add(1) + defer reqWg.Done() + go func() { + reqWg.Wait() + self.mlock.Lock() + delete(self.sentReqs, reqID) + self.mlock.Unlock() + }() + + exclude := make(map[*peer]struct{}) + for { + var p *peer + if self.serverPool != nil { + p = self.serverPool.selectPeerWait(reqID, func(p *peer) (bool, time.Duration) { + if _, ok := exclude[p]; ok || !lreq.CanSend(p) { + return false, 0 + } + return true, p.fcServer.CanSend(lreq.GetCost(p)) + }, ctx.Done()) + } + if p == nil { + select { + case <-ctx.Done(): + return ctx.Err() + case <-req.answered: + return nil + case <-time.After(retryPeers): + } + } else { + exclude[p] = struct{}{} + delivered := make(chan struct{}) + timeout := make(chan struct{}) + req.lock.Lock() + req.sentTo[p] = delivered + req.lock.Unlock() + reqWg.Add(1) + cost := lreq.GetCost(p) + p.fcServer.SendRequest(reqID, cost) + go self.requestPeer(req, p, delivered, timeout, reqWg) + lreq.Request(reqID, p) + + select { + case <-ctx.Done(): + return ctx.Err() + case <-answered: + return nil + case <-timeout: + } + } + } +} + +// Retrieve tries to fetch an object from the local db, then from the LES network. +// If the network retrieval was successful, it stores the object in local db. +func (self *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { + lreq := LesRequest(req) + err = self.networkRequest(ctx, lreq) + if err == nil { + // retrieved from network, store in db + req.StoreResult(self.db) + } else { + glog.V(logger.Debug).Infof("networkRequest err = %v", err) + } + return +} + +func getNextReqID() uint64 { + var rnd [8]byte + rand.Read(rnd[:]) + return binary.BigEndian.Uint64(rnd[:]) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go new file mode 100644 index 0000000..2987eb2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_requests.go @@ -0,0 +1,353 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package light implements on-demand retrieval capable state and chain objects +// for the Ethereum Light Client. +package les + +import ( + "bytes" + "encoding/binary" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +type LesOdrRequest interface { + GetCost(*peer) uint64 + CanSend(*peer) bool + Request(uint64, *peer) error + Valid(ethdb.Database, *Msg) bool // if true, keeps the retrieved object +} + +func LesRequest(req light.OdrRequest) LesOdrRequest { + switch r := req.(type) { + case *light.BlockRequest: + return (*BlockRequest)(r) + case *light.ReceiptsRequest: + return (*ReceiptsRequest)(r) + case *light.TrieRequest: + return (*TrieRequest)(r) + case *light.CodeRequest: + return (*CodeRequest)(r) + case *light.ChtRequest: + return (*ChtRequest)(r) + default: + return nil + } +} + +// BlockRequest is the ODR request type for block bodies +type BlockRequest light.BlockRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation of LesOdrRequest) +func (self *BlockRequest) GetCost(peer *peer) uint64 { + return peer.GetRequestCost(GetBlockBodiesMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (self *BlockRequest) CanSend(peer *peer) bool { + return peer.HasBlock(self.Hash, self.Number) +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (self *BlockRequest) Request(reqID uint64, peer *peer) error { + glog.V(logger.Debug).Infof("ODR: requesting body of block %08x from peer %v", self.Hash[:4], peer.id) + return peer.RequestBodies(reqID, self.GetCost(peer), []common.Hash{self.Hash}) +} + +// Valid processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (self *BlockRequest) Valid(db ethdb.Database, msg *Msg) bool { + glog.V(logger.Debug).Infof("ODR: validating body of block %08x", self.Hash[:4]) + if msg.MsgType != MsgBlockBodies { + glog.V(logger.Debug).Infof("ODR: invalid message type") + return false + } + bodies := msg.Obj.([]*types.Body) + if len(bodies) != 1 { + glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(bodies)) + return false + } + body := bodies[0] + header := core.GetHeader(db, self.Hash, self.Number) + if header == nil { + glog.V(logger.Debug).Infof("ODR: header not found for block %08x", self.Hash[:4]) + return false + } + txHash := types.DeriveSha(types.Transactions(body.Transactions)) + if header.TxHash != txHash { + glog.V(logger.Debug).Infof("ODR: header.TxHash %08x does not match received txHash %08x", header.TxHash[:4], txHash[:4]) + return false + } + uncleHash := types.CalcUncleHash(body.Uncles) + if header.UncleHash != uncleHash { + glog.V(logger.Debug).Infof("ODR: header.UncleHash %08x does not match received uncleHash %08x", header.UncleHash[:4], uncleHash[:4]) + return false + } + data, err := rlp.EncodeToBytes(body) + if err != nil { + glog.V(logger.Debug).Infof("ODR: body RLP encode error: %v", err) + return false + } + self.Rlp = data + glog.V(logger.Debug).Infof("ODR: validation successful") + return true +} + +// ReceiptsRequest is the ODR request type for block receipts by block hash +type ReceiptsRequest light.ReceiptsRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation of LesOdrRequest) +func (self *ReceiptsRequest) GetCost(peer *peer) uint64 { + return peer.GetRequestCost(GetReceiptsMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (self *ReceiptsRequest) CanSend(peer *peer) bool { + return peer.HasBlock(self.Hash, self.Number) +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (self *ReceiptsRequest) Request(reqID uint64, peer *peer) error { + glog.V(logger.Debug).Infof("ODR: requesting receipts for block %08x from peer %v", self.Hash[:4], peer.id) + return peer.RequestReceipts(reqID, self.GetCost(peer), []common.Hash{self.Hash}) +} + +// Valid processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (self *ReceiptsRequest) Valid(db ethdb.Database, msg *Msg) bool { + glog.V(logger.Debug).Infof("ODR: validating receipts for block %08x", self.Hash[:4]) + if msg.MsgType != MsgReceipts { + glog.V(logger.Debug).Infof("ODR: invalid message type") + return false + } + receipts := msg.Obj.([]types.Receipts) + if len(receipts) != 1 { + glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(receipts)) + return false + } + hash := types.DeriveSha(receipts[0]) + header := core.GetHeader(db, self.Hash, self.Number) + if header == nil { + glog.V(logger.Debug).Infof("ODR: header not found for block %08x", self.Hash[:4]) + return false + } + if !bytes.Equal(header.ReceiptHash[:], hash[:]) { + glog.V(logger.Debug).Infof("ODR: header receipts hash %08x does not match calculated RLP hash %08x", header.ReceiptHash[:4], hash[:4]) + return false + } + self.Receipts = receipts[0] + glog.V(logger.Debug).Infof("ODR: validation successful") + return true +} + +type ProofReq struct { + BHash common.Hash + AccKey, Key []byte + FromLevel uint +} + +// ODR request type for state/storage trie entries, see LesOdrRequest interface +type TrieRequest light.TrieRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation of LesOdrRequest) +func (self *TrieRequest) GetCost(peer *peer) uint64 { + return peer.GetRequestCost(GetProofsMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (self *TrieRequest) CanSend(peer *peer) bool { + return peer.HasBlock(self.Id.BlockHash, self.Id.BlockNumber) +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (self *TrieRequest) Request(reqID uint64, peer *peer) error { + glog.V(logger.Debug).Infof("ODR: requesting trie root %08x key %08x from peer %v", self.Id.Root[:4], self.Key[:4], peer.id) + req := &ProofReq{ + BHash: self.Id.BlockHash, + AccKey: self.Id.AccKey, + Key: self.Key, + } + return peer.RequestProofs(reqID, self.GetCost(peer), []*ProofReq{req}) +} + +// Valid processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (self *TrieRequest) Valid(db ethdb.Database, msg *Msg) bool { + glog.V(logger.Debug).Infof("ODR: validating trie root %08x key %08x", self.Id.Root[:4], self.Key[:4]) + + if msg.MsgType != MsgProofs { + glog.V(logger.Debug).Infof("ODR: invalid message type") + return false + } + proofs := msg.Obj.([][]rlp.RawValue) + if len(proofs) != 1 { + glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(proofs)) + return false + } + _, err := trie.VerifyProof(self.Id.Root, self.Key, proofs[0]) + if err != nil { + glog.V(logger.Debug).Infof("ODR: merkle proof verification error: %v", err) + return false + } + self.Proof = proofs[0] + glog.V(logger.Debug).Infof("ODR: validation successful") + return true +} + +type CodeReq struct { + BHash common.Hash + AccKey []byte +} + +// ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface +type CodeRequest light.CodeRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation of LesOdrRequest) +func (self *CodeRequest) GetCost(peer *peer) uint64 { + return peer.GetRequestCost(GetCodeMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (self *CodeRequest) CanSend(peer *peer) bool { + return peer.HasBlock(self.Id.BlockHash, self.Id.BlockNumber) +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (self *CodeRequest) Request(reqID uint64, peer *peer) error { + glog.V(logger.Debug).Infof("ODR: requesting node data for hash %08x from peer %v", self.Hash[:4], peer.id) + req := &CodeReq{ + BHash: self.Id.BlockHash, + AccKey: self.Id.AccKey, + } + return peer.RequestCode(reqID, self.GetCost(peer), []*CodeReq{req}) +} + +// Valid processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (self *CodeRequest) Valid(db ethdb.Database, msg *Msg) bool { + glog.V(logger.Debug).Infof("ODR: validating node data for hash %08x", self.Hash[:4]) + if msg.MsgType != MsgCode { + glog.V(logger.Debug).Infof("ODR: invalid message type") + return false + } + reply := msg.Obj.([][]byte) + if len(reply) != 1 { + glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(reply)) + return false + } + data := reply[0] + if hash := crypto.Keccak256Hash(data); self.Hash != hash { + glog.V(logger.Debug).Infof("ODR: requested hash %08x does not match received data hash %08x", self.Hash[:4], hash[:4]) + return false + } + self.Data = data + glog.V(logger.Debug).Infof("ODR: validation successful") + return true +} + +type ChtReq struct { + ChtNum, BlockNum, FromLevel uint64 +} + +type ChtResp struct { + Header *types.Header + Proof []rlp.RawValue +} + +// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface +type ChtRequest light.ChtRequest + +// GetCost returns the cost of the given ODR request according to the serving +// peer's cost table (implementation of LesOdrRequest) +func (self *ChtRequest) GetCost(peer *peer) uint64 { + return peer.GetRequestCost(GetHeaderProofsMsg, 1) +} + +// CanSend tells if a certain peer is suitable for serving the given request +func (self *ChtRequest) CanSend(peer *peer) bool { + peer.lock.RLock() + defer peer.lock.RUnlock() + + return self.ChtNum <= (peer.headInfo.Number-light.ChtConfirmations)/light.ChtFrequency +} + +// Request sends an ODR request to the LES network (implementation of LesOdrRequest) +func (self *ChtRequest) Request(reqID uint64, peer *peer) error { + glog.V(logger.Debug).Infof("ODR: requesting CHT #%d block #%d from peer %v", self.ChtNum, self.BlockNum, peer.id) + req := &ChtReq{ + ChtNum: self.ChtNum, + BlockNum: self.BlockNum, + } + return peer.RequestHeaderProofs(reqID, self.GetCost(peer), []*ChtReq{req}) +} + +// Valid processes an ODR request reply message from the LES network +// returns true and stores results in memory if the message was a valid reply +// to the request (implementation of LesOdrRequest) +func (self *ChtRequest) Valid(db ethdb.Database, msg *Msg) bool { + glog.V(logger.Debug).Infof("ODR: validating CHT #%d block #%d", self.ChtNum, self.BlockNum) + + if msg.MsgType != MsgHeaderProofs { + glog.V(logger.Debug).Infof("ODR: invalid message type") + return false + } + proofs := msg.Obj.([]ChtResp) + if len(proofs) != 1 { + glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(proofs)) + return false + } + proof := proofs[0] + var encNumber [8]byte + binary.BigEndian.PutUint64(encNumber[:], self.BlockNum) + value, err := trie.VerifyProof(self.ChtRoot, encNumber[:], proof.Proof) + if err != nil { + glog.V(logger.Debug).Infof("ODR: CHT merkle proof verification error: %v", err) + return false + } + var node light.ChtNode + if err := rlp.DecodeBytes(value, &node); err != nil { + glog.V(logger.Debug).Infof("ODR: error decoding CHT node: %v", err) + return false + } + if node.Hash != proof.Header.Hash() { + glog.V(logger.Debug).Infof("ODR: CHT header hash does not match") + return false + } + + self.Proof = proof.Proof + self.Header = proof.Header + self.Td = node.Td + glog.V(logger.Debug).Infof("ODR: validation successful") + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr_test.go b/vendor/github.com/ethereum/go-ethereum/les/odr_test.go new file mode 100644 index 0000000..622d89e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/odr_test.go @@ -0,0 +1,203 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "bytes" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/net/context" +) + +type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte + +func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) } + +func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { + var block *types.Block + if bc != nil { + block = bc.GetBlockByHash(bhash) + } else { + block, _ = lc.GetBlockByHash(ctx, bhash) + } + if block == nil { + return nil + } + rlp, _ := rlp.EncodeToBytes(block) + return rlp +} + +func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, 1, odrGetReceipts) } + +func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { + var receipts types.Receipts + if bc != nil { + receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash)) + } else { + receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash)) + } + if receipts == nil { + return nil + } + rlp, _ := rlp.EncodeToBytes(receipts) + return rlp +} + +func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, 1, odrAccounts) } + +func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { + dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") + acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} + + var res []byte + for _, addr := range acc { + if bc != nil { + header := bc.GetHeaderByHash(bhash) + st, err := state.New(header.Root, db) + if err == nil { + bal := st.GetBalance(addr) + rlp, _ := rlp.EncodeToBytes(bal) + res = append(res, rlp...) + } + } else { + header := lc.GetHeaderByHash(bhash) + st := light.NewLightState(light.StateTrieID(header), lc.Odr()) + bal, err := st.GetBalance(ctx, addr) + if err == nil { + rlp, _ := rlp.EncodeToBytes(bal) + res = append(res, rlp...) + } + } + } + + return res +} + +func TestOdrContractCallLes1(t *testing.T) { testOdr(t, 1, 2, odrContractCall) } + +type callmsg struct { + types.Message +} + +func (callmsg) CheckNonce() bool { return false } + +func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { + data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") + + var res []byte + for i := 0; i < 3; i++ { + data[35] = byte(i) + if bc != nil { + header := bc.GetHeaderByHash(bhash) + statedb, err := state.New(header.Root, db) + + if err == nil { + from := statedb.GetOrNewStateObject(testBankAddress) + from.SetBalance(common.MaxBig) + + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} + + context := core.NewEVMContext(msg, header, bc) + vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) + + //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) + gp := new(core.GasPool).AddGas(common.MaxBig) + ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + res = append(res, ret...) + } + } else { + header := lc.GetHeaderByHash(bhash) + state := light.NewLightState(light.StateTrieID(header), lc.Odr()) + vmstate := light.NewVMState(ctx, state) + from, err := state.GetOrNewStateObject(ctx, testBankAddress) + if err == nil { + from.SetBalance(common.MaxBig) + + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} + + context := core.NewEVMContext(msg, header, lc) + vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) + + //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) + gp := new(core.GasPool).AddGas(common.MaxBig) + ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + if vmstate.Error() == nil { + res = append(res, ret...) + } + } + } + } + return res +} + +func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { + // Assemble the test environment + pm, db, odr := newTestProtocolManagerMust(t, false, 4, testChainGen) + lpm, ldb, odr := newTestProtocolManagerMust(t, true, 0, nil) + _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) + pool := &testServerPool{} + pool.setPeer(lpeer) + odr.serverPool = pool + select { + case <-time.After(time.Millisecond * 100): + case err := <-err1: + t.Fatalf("peer 1 handshake error: %v", err) + case err := <-err2: + t.Fatalf("peer 1 handshake error: %v", err) + } + + lpm.synchronise(lpeer) + + test := func(expFail uint64) { + for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { + bhash := core.GetCanonicalHash(db, i) + b1 := fn(light.NoOdr, db, pm.chainConfig, pm.blockchain.(*core.BlockChain), nil, bhash) + ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + b2 := fn(ctx, ldb, lpm.chainConfig, nil, lpm.blockchain.(*light.LightChain), bhash) + eq := bytes.Equal(b1, b2) + exp := i < expFail + if exp && !eq { + t.Errorf("odr mismatch") + } + if !exp && eq { + t.Errorf("unexpected odr match") + } + } + } + + // temporarily remove peer to test odr fails + pool.setPeer(nil) + // expect retrievals to fail (except genesis block) without a les peer + test(expFail) + pool.setPeer(lpeer) + // expect all retrievals to pass + test(5) + pool.setPeer(nil) + // still expect all retrievals to pass, now data should be cached locally + test(5) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/peer.go b/vendor/github.com/ethereum/go-ethereum/les/peer.go new file mode 100644 index 0000000..d5008de --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/peer.go @@ -0,0 +1,536 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "errors" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/les/flowcontrol" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + errClosed = errors.New("peer set is closed") + errAlreadyRegistered = errors.New("peer is already registered") + errNotRegistered = errors.New("peer is not registered") +) + +const maxHeadInfoLen = 20 + +type peer struct { + *p2p.Peer + + rw p2p.MsgReadWriter + + version int // Protocol version negotiated + network int // Network ID being on + + id string + + headInfo *announceData + lock sync.RWMutex + + announceChn chan announceData + + poolEntry *poolEntry + hasBlock func(common.Hash, uint64) bool + + fcClient *flowcontrol.ClientNode // nil if the peer is server only + fcServer *flowcontrol.ServerNode // nil if the peer is client only + fcServerParams *flowcontrol.ServerParams + fcCosts requestCostTable +} + +func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { + id := p.ID() + + return &peer{ + Peer: p, + rw: rw, + version: version, + network: network, + id: fmt.Sprintf("%x", id[:8]), + announceChn: make(chan announceData, 20), + } +} + +// Info gathers and returns a collection of metadata known about a peer. +func (p *peer) Info() *eth.PeerInfo { + return ð.PeerInfo{ + Version: p.version, + Difficulty: p.Td(), + Head: fmt.Sprintf("%x", p.Head()), + } +} + +// Head retrieves a copy of the current head (most recent) hash of the peer. +func (p *peer) Head() (hash common.Hash) { + p.lock.RLock() + defer p.lock.RUnlock() + + copy(hash[:], p.headInfo.Hash[:]) + return hash +} + +func (p *peer) HeadAndTd() (hash common.Hash, td *big.Int) { + p.lock.RLock() + defer p.lock.RUnlock() + + copy(hash[:], p.headInfo.Hash[:]) + return hash, p.headInfo.Td +} + +func (p *peer) headBlockInfo() blockInfo { + p.lock.RLock() + defer p.lock.RUnlock() + + return blockInfo{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td} +} + +// Td retrieves the current total difficulty of a peer. +func (p *peer) Td() *big.Int { + p.lock.RLock() + defer p.lock.RUnlock() + + return new(big.Int).Set(p.headInfo.Td) +} + +func sendRequest(w p2p.MsgWriter, msgcode, reqID, cost uint64, data interface{}) error { + type req struct { + ReqID uint64 + Data interface{} + } + return p2p.Send(w, msgcode, req{reqID, data}) +} + +func sendResponse(w p2p.MsgWriter, msgcode, reqID, bv uint64, data interface{}) error { + type resp struct { + ReqID, BV uint64 + Data interface{} + } + return p2p.Send(w, msgcode, resp{reqID, bv, data}) +} + +func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + cost := p.fcCosts[msgcode].baseCost + p.fcCosts[msgcode].reqCost*uint64(amount) + if cost > p.fcServerParams.BufLimit { + cost = p.fcServerParams.BufLimit + } + return cost +} + +// HasBlock checks if the peer has a given block +func (p *peer) HasBlock(hash common.Hash, number uint64) bool { + p.lock.RLock() + hashBlock := p.hasBlock + p.lock.RUnlock() + return hashBlock != nil && hashBlock(hash, number) +} + +// SendAnnounce announces the availability of a number of blocks through +// a hash notification. +func (p *peer) SendAnnounce(request announceData) error { + return p2p.Send(p.rw, AnnounceMsg, request) +} + +// SendBlockHeaders sends a batch of block headers to the remote peer. +func (p *peer) SendBlockHeaders(reqID, bv uint64, headers []*types.Header) error { + return sendResponse(p.rw, BlockHeadersMsg, reqID, bv, headers) +} + +// SendBlockBodiesRLP sends a batch of block contents to the remote peer from +// an already RLP encoded format. +func (p *peer) SendBlockBodiesRLP(reqID, bv uint64, bodies []rlp.RawValue) error { + return sendResponse(p.rw, BlockBodiesMsg, reqID, bv, bodies) +} + +// SendCodeRLP sends a batch of arbitrary internal data, corresponding to the +// hashes requested. +func (p *peer) SendCode(reqID, bv uint64, data [][]byte) error { + return sendResponse(p.rw, CodeMsg, reqID, bv, data) +} + +// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the +// ones requested from an already RLP encoded format. +func (p *peer) SendReceiptsRLP(reqID, bv uint64, receipts []rlp.RawValue) error { + return sendResponse(p.rw, ReceiptsMsg, reqID, bv, receipts) +} + +// SendProofs sends a batch of merkle proofs, corresponding to the ones requested. +func (p *peer) SendProofs(reqID, bv uint64, proofs proofsData) error { + return sendResponse(p.rw, ProofsMsg, reqID, bv, proofs) +} + +// SendHeaderProofs sends a batch of header proofs, corresponding to the ones requested. +func (p *peer) SendHeaderProofs(reqID, bv uint64, proofs []ChtResp) error { + return sendResponse(p.rw, HeaderProofsMsg, reqID, bv, proofs) +} + +// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the +// specified header query, based on the hash of an origin block. +func (p *peer) RequestHeadersByHash(reqID, cost uint64, origin common.Hash, amount int, skip int, reverse bool) error { + glog.V(logger.Debug).Infof("%v fetching %d headers from %x, skipping %d (reverse = %v)", p, amount, origin[:4], skip, reverse) + return sendRequest(p.rw, GetBlockHeadersMsg, reqID, cost, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +} + +// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the +// specified header query, based on the number of an origin block. +func (p *peer) RequestHeadersByNumber(reqID, cost, origin uint64, amount int, skip int, reverse bool) error { + glog.V(logger.Debug).Infof("%v fetching %d headers from #%d, skipping %d (reverse = %v)", p, amount, origin, skip, reverse) + return sendRequest(p.rw, GetBlockHeadersMsg, reqID, cost, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) +} + +// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes +// specified. +func (p *peer) RequestBodies(reqID, cost uint64, hashes []common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching %d block bodies", p, len(hashes)) + return sendRequest(p.rw, GetBlockBodiesMsg, reqID, cost, hashes) +} + +// RequestCode fetches a batch of arbitrary data from a node's known state +// data, corresponding to the specified hashes. +func (p *peer) RequestCode(reqID, cost uint64, reqs []*CodeReq) error { + glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(reqs)) + return sendRequest(p.rw, GetCodeMsg, reqID, cost, reqs) +} + +// RequestReceipts fetches a batch of transaction receipts from a remote node. +func (p *peer) RequestReceipts(reqID, cost uint64, hashes []common.Hash) error { + glog.V(logger.Debug).Infof("%v fetching %v receipts", p, len(hashes)) + return sendRequest(p.rw, GetReceiptsMsg, reqID, cost, hashes) +} + +// RequestProofs fetches a batch of merkle proofs from a remote node. +func (p *peer) RequestProofs(reqID, cost uint64, reqs []*ProofReq) error { + glog.V(logger.Debug).Infof("%v fetching %v proofs", p, len(reqs)) + return sendRequest(p.rw, GetProofsMsg, reqID, cost, reqs) +} + +// RequestHeaderProofs fetches a batch of header merkle proofs from a remote node. +func (p *peer) RequestHeaderProofs(reqID, cost uint64, reqs []*ChtReq) error { + glog.V(logger.Debug).Infof("%v fetching %v header proofs", p, len(reqs)) + return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqs) +} + +func (p *peer) SendTxs(cost uint64, txs types.Transactions) error { + glog.V(logger.Debug).Infof("%v relaying %v txs", p, len(txs)) + reqID := getNextReqID() + p.fcServer.MustAssignRequest(reqID) + p.fcServer.SendRequest(reqID, cost) + return p2p.Send(p.rw, SendTxMsg, txs) +} + +type keyValueEntry struct { + Key string + Value rlp.RawValue +} +type keyValueList []keyValueEntry +type keyValueMap map[string]rlp.RawValue + +func (l keyValueList) add(key string, val interface{}) keyValueList { + var entry keyValueEntry + entry.Key = key + if val == nil { + val = uint64(0) + } + enc, err := rlp.EncodeToBytes(val) + if err == nil { + entry.Value = enc + } + return append(l, entry) +} + +func (l keyValueList) decode() keyValueMap { + m := make(keyValueMap) + for _, entry := range l { + m[entry.Key] = entry.Value + } + return m +} + +func (m keyValueMap) get(key string, val interface{}) error { + enc, ok := m[key] + if !ok { + return errResp(ErrHandshakeMissingKey, "%s", key) + } + if val == nil { + return nil + } + return rlp.DecodeBytes(enc, val) +} + +func (p *peer) sendReceiveHandshake(sendList keyValueList) (keyValueList, error) { + // Send out own handshake in a new thread + errc := make(chan error, 1) + go func() { + errc <- p2p.Send(p.rw, StatusMsg, sendList) + }() + // In the mean time retrieve the remote status message + msg, err := p.rw.ReadMsg() + if err != nil { + return nil, err + } + if msg.Code != StatusMsg { + return nil, errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) + } + if msg.Size > ProtocolMaxMsgSize { + return nil, errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + // Decode the handshake + var recvList keyValueList + if err := msg.Decode(&recvList); err != nil { + return nil, errResp(ErrDecode, "msg %v: %v", msg, err) + } + if err := <-errc; err != nil { + return nil, err + } + return recvList, nil +} + +// Handshake executes the les protocol handshake, negotiating version number, +// network IDs, difficulties, head and genesis blocks. +func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error { + p.lock.Lock() + defer p.lock.Unlock() + + var send keyValueList + send = send.add("protocolVersion", uint64(p.version)) + send = send.add("networkId", uint64(p.network)) + send = send.add("headTd", td) + send = send.add("headHash", head) + send = send.add("headNum", headNum) + send = send.add("genesisHash", genesis) + if server != nil { + send = send.add("serveHeaders", nil) + send = send.add("serveChainSince", uint64(0)) + send = send.add("serveStateSince", uint64(0)) + send = send.add("txRelay", nil) + send = send.add("flowControl/BL", server.defParams.BufLimit) + send = send.add("flowControl/MRR", server.defParams.MinRecharge) + list := server.fcCostStats.getCurrentList() + send = send.add("flowControl/MRC", list) + p.fcCosts = list.decode() + } + recvList, err := p.sendReceiveHandshake(send) + if err != nil { + return err + } + recv := recvList.decode() + + var rGenesis, rHash common.Hash + var rVersion, rNetwork, rNum uint64 + var rTd *big.Int + + if err := recv.get("protocolVersion", &rVersion); err != nil { + return err + } + if err := recv.get("networkId", &rNetwork); err != nil { + return err + } + if err := recv.get("headTd", &rTd); err != nil { + return err + } + if err := recv.get("headHash", &rHash); err != nil { + return err + } + if err := recv.get("headNum", &rNum); err != nil { + return err + } + if err := recv.get("genesisHash", &rGenesis); err != nil { + return err + } + + if rGenesis != genesis { + return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", rGenesis, genesis) + } + if int(rNetwork) != p.network { + return errResp(ErrNetworkIdMismatch, "%d (!= %d)", rNetwork, p.network) + } + if int(rVersion) != p.version { + return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version) + } + if server != nil { + if recv.get("serveStateSince", nil) == nil { + return errResp(ErrUselessPeer, "wanted client, got server") + } + p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams) + } else { + if recv.get("serveChainSince", nil) != nil { + return errResp(ErrUselessPeer, "peer cannot serve chain") + } + if recv.get("serveStateSince", nil) != nil { + return errResp(ErrUselessPeer, "peer cannot serve state") + } + if recv.get("txRelay", nil) != nil { + return errResp(ErrUselessPeer, "peer cannot relay transactions") + } + params := &flowcontrol.ServerParams{} + if err := recv.get("flowControl/BL", ¶ms.BufLimit); err != nil { + return err + } + if err := recv.get("flowControl/MRR", ¶ms.MinRecharge); err != nil { + return err + } + var MRC RequestCostList + if err := recv.get("flowControl/MRC", &MRC); err != nil { + return err + } + p.fcServerParams = params + p.fcServer = flowcontrol.NewServerNode(params) + p.fcCosts = MRC.decode() + } + + p.headInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum} + return nil +} + +// String implements fmt.Stringer. +func (p *peer) String() string { + return fmt.Sprintf("Peer %s [%s]", p.id, + fmt.Sprintf("les/%d", p.version), + ) +} + +// peerSet represents the collection of active peers currently participating in +// the Light Ethereum sub-protocol. +type peerSet struct { + peers map[string]*peer + lock sync.RWMutex + closed bool +} + +// newPeerSet creates a new peer set to track the active participants. +func newPeerSet() *peerSet { + return &peerSet{ + peers: make(map[string]*peer), + } +} + +// Register injects a new peer into the working set, or returns an error if the +// peer is already known. +func (ps *peerSet) Register(p *peer) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if ps.closed { + return errClosed + } + if _, ok := ps.peers[p.id]; ok { + return errAlreadyRegistered + } + ps.peers[p.id] = p + return nil +} + +// Unregister removes a remote peer from the active set, disabling any further +// actions to/from that particular entity. +func (ps *peerSet) Unregister(id string) error { + ps.lock.Lock() + defer ps.lock.Unlock() + + if _, ok := ps.peers[id]; !ok { + return errNotRegistered + } + delete(ps.peers, id) + return nil +} + +// AllPeerIDs returns a list of all registered peer IDs +func (ps *peerSet) AllPeerIDs() []string { + ps.lock.RLock() + defer ps.lock.RUnlock() + + res := make([]string, len(ps.peers)) + idx := 0 + for id := range ps.peers { + res[idx] = id + idx++ + } + return res +} + +// Peer retrieves the registered peer with the given id. +func (ps *peerSet) Peer(id string) *peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return ps.peers[id] +} + +// Len returns if the current number of peers in the set. +func (ps *peerSet) Len() int { + ps.lock.RLock() + defer ps.lock.RUnlock() + + return len(ps.peers) +} + +// BestPeer retrieves the known peer with the currently highest total difficulty. +func (ps *peerSet) BestPeer() *peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + var ( + bestPeer *peer + bestTd *big.Int + ) + for _, p := range ps.peers { + if td := p.Td(); bestPeer == nil || td.Cmp(bestTd) > 0 { + bestPeer, bestTd = p, td + } + } + return bestPeer +} + +// AllPeers returns all peers in a list +func (ps *peerSet) AllPeers() []*peer { + ps.lock.RLock() + defer ps.lock.RUnlock() + + list := make([]*peer, len(ps.peers)) + i := 0 + for _, peer := range ps.peers { + list[i] = peer + i++ + } + return list +} + +// Close disconnects all peers. +// No new peers can be registered after Close has returned. +func (ps *peerSet) Close() { + ps.lock.Lock() + defer ps.lock.Unlock() + + for _, p := range ps.peers { + p.Disconnect(p2p.DiscQuitting) + } + ps.closed = true +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/protocol.go b/vendor/github.com/ethereum/go-ethereum/les/protocol.go new file mode 100644 index 0000000..46da2b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/protocol.go @@ -0,0 +1,198 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// Constants to match up protocol versions and messages +const ( + lpv1 = 1 +) + +// Supported versions of the les protocol (first is primary). +var ProtocolVersions = []uint{lpv1} + +// Number of implemented message corresponding to different protocol versions. +var ProtocolLengths = []uint64{15} + +const ( + NetworkId = 1 + ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message +) + +// les protocol message codes +const ( + // Protocol messages belonging to LPV1 + StatusMsg = 0x00 + AnnounceMsg = 0x01 + GetBlockHeadersMsg = 0x02 + BlockHeadersMsg = 0x03 + GetBlockBodiesMsg = 0x04 + BlockBodiesMsg = 0x05 + GetReceiptsMsg = 0x06 + ReceiptsMsg = 0x07 + GetProofsMsg = 0x08 + ProofsMsg = 0x09 + GetCodeMsg = 0x0a + CodeMsg = 0x0b + SendTxMsg = 0x0c + GetHeaderProofsMsg = 0x0d + HeaderProofsMsg = 0x0e +) + +type errCode int + +const ( + ErrMsgTooLarge = iota + ErrDecode + ErrInvalidMsgCode + ErrProtocolVersionMismatch + ErrNetworkIdMismatch + ErrGenesisBlockMismatch + ErrNoStatusMsg + ErrExtraStatusMsg + ErrSuspendedPeer + ErrUselessPeer + ErrRequestRejected + ErrUnexpectedResponse + ErrInvalidResponse + ErrTooManyTimeouts + ErrHandshakeMissingKey +) + +func (e errCode) String() string { + return errorToString[int(e)] +} + +// XXX change once legacy code is out +var errorToString = map[int]string{ + ErrMsgTooLarge: "Message too long", + ErrDecode: "Invalid message", + ErrInvalidMsgCode: "Invalid message code", + ErrProtocolVersionMismatch: "Protocol version mismatch", + ErrNetworkIdMismatch: "NetworkId mismatch", + ErrGenesisBlockMismatch: "Genesis block mismatch", + ErrNoStatusMsg: "No status message", + ErrExtraStatusMsg: "Extra status message", + ErrSuspendedPeer: "Suspended peer", + ErrRequestRejected: "Request rejected", + ErrUnexpectedResponse: "Unexpected response", + ErrInvalidResponse: "Invalid response", + ErrTooManyTimeouts: "Too many request timeouts", + ErrHandshakeMissingKey: "Key missing from handshake message", +} + +type chainManager interface { + GetBlockHashesFromHash(hash common.Hash, amount uint64) (hashes []common.Hash) + GetBlock(hash common.Hash) (block *types.Block) + Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) +} + +// announceData is the network packet for the block announcements. +type announceData struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced + Td *big.Int // Total difficulty of one particular block being announced + ReorgDepth uint64 + Update keyValueList + + haveHeaders uint64 // we have the headers of the remote peer's chain up to this number + headKnown bool + requested bool + next *announceData +} + +type blockInfo struct { + Hash common.Hash // Hash of one particular block being announced + Number uint64 // Number of one particular block being announced + Td *big.Int // Total difficulty of one particular block being announced +} + +// getBlockHashesData is the network packet for the hash based hash retrieval. +type getBlockHashesData struct { + Hash common.Hash + Amount uint64 +} + +// getBlockHeadersData represents a block header query. +type getBlockHeadersData struct { + Origin hashOrNumber // Block from which to retrieve headers + Amount uint64 // Maximum number of headers to retrieve + Skip uint64 // Blocks to skip between consecutive headers + Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) +} + +// hashOrNumber is a combined field for specifying an origin block. +type hashOrNumber struct { + Hash common.Hash // Block hash from which to retrieve headers (excludes Number) + Number uint64 // Block hash from which to retrieve headers (excludes Hash) +} + +// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the +// two contained union fields. +func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { + if hn.Hash == (common.Hash{}) { + return rlp.Encode(w, hn.Number) + } + if hn.Number != 0 { + return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) + } + return rlp.Encode(w, hn.Hash) +} + +// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents +// into either a block hash or a block number. +func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + origin, err := s.Raw() + if err == nil { + switch { + case size == 32: + err = rlp.DecodeBytes(origin, &hn.Hash) + case size <= 8: + err = rlp.DecodeBytes(origin, &hn.Number) + default: + err = fmt.Errorf("invalid input size %d for origin", size) + } + } + return err +} + +// newBlockData is the network packet for the block propagation message. +type newBlockData struct { + Block *types.Block + TD *big.Int +} + +// blockBodiesData is the network packet for block content distribution. +type blockBodiesData []*types.Body + +// CodeData is the network response packet for a node data retrieval. +type CodeData []struct { + Value []byte +} + +type proofsData [][]rlp.RawValue diff --git a/vendor/github.com/ethereum/go-ethereum/les/randselect.go b/vendor/github.com/ethereum/go-ethereum/les/randselect.go new file mode 100644 index 0000000..1a9d069 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/randselect.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "math/rand" +) + +// wrsItem interface should be implemented by any entries that are to be selected from +// a weightedRandomSelect set. Note that recalculating monotonously decreasing item +// weights on-demand (without constantly calling update) is allowed +type wrsItem interface { + Weight() int64 +} + +// weightedRandomSelect is capable of weighted random selection from a set of items +type weightedRandomSelect struct { + root *wrsNode + idx map[wrsItem]int +} + +// newWeightedRandomSelect returns a new weightedRandomSelect structure +func newWeightedRandomSelect() *weightedRandomSelect { + return &weightedRandomSelect{root: &wrsNode{maxItems: wrsBranches}, idx: make(map[wrsItem]int)} +} + +// update updates an item's weight, adds it if it was non-existent or removes it if +// the new weight is zero. Note that explicitly updating decreasing weights is not necessary. +func (w *weightedRandomSelect) update(item wrsItem) { + w.setWeight(item, item.Weight()) +} + +// remove removes an item from the set +func (w *weightedRandomSelect) remove(item wrsItem) { + w.setWeight(item, 0) +} + +// setWeight sets an item's weight to a specific value (removes it if zero) +func (w *weightedRandomSelect) setWeight(item wrsItem, weight int64) { + idx, ok := w.idx[item] + if ok { + w.root.setWeight(idx, weight) + if weight == 0 { + delete(w.idx, item) + } + } else { + if weight != 0 { + if w.root.itemCnt == w.root.maxItems { + // add a new level + newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches} + newRoot.items[0] = w.root + newRoot.weights[0] = w.root.sumWeight + w.root = newRoot + } + w.idx[item] = w.root.insert(item, weight) + } + } +} + +// choose randomly selects an item from the set, with a chance proportional to its +// current weight. If the weight of the chosen element has been decreased since the +// last stored value, returns it with a newWeight/oldWeight chance, otherwise just +// updates its weight and selects another one +func (w *weightedRandomSelect) choose() wrsItem { + for { + if w.root.sumWeight == 0 { + return nil + } + val := rand.Int63n(w.root.sumWeight) + choice, lastWeight := w.root.choose(val) + weight := choice.Weight() + if weight != lastWeight { + w.setWeight(choice, weight) + } + if weight >= lastWeight || rand.Int63n(lastWeight) < weight { + return choice + } + } +} + +const wrsBranches = 8 // max number of branches in the wrsNode tree + +// wrsNode is a node of a tree structure that can store wrsItems or further wrsNodes. +type wrsNode struct { + items [wrsBranches]interface{} + weights [wrsBranches]int64 + sumWeight int64 + level, itemCnt, maxItems int +} + +// insert recursively inserts a new item to the tree and returns the item index +func (n *wrsNode) insert(item wrsItem, weight int64) int { + branch := 0 + for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) { + branch++ + if branch == wrsBranches { + panic(nil) + } + } + n.itemCnt++ + n.sumWeight += weight + n.weights[branch] += weight + if n.level == 0 { + n.items[branch] = item + return branch + } else { + var subNode *wrsNode + if n.items[branch] == nil { + subNode = &wrsNode{maxItems: n.maxItems / wrsBranches, level: n.level - 1} + n.items[branch] = subNode + } else { + subNode = n.items[branch].(*wrsNode) + } + subIdx := subNode.insert(item, weight) + return subNode.maxItems*branch + subIdx + } +} + +// setWeight updates the weight of a certain item (which should exist) and returns +// the change of the last weight value stored in the tree +func (n *wrsNode) setWeight(idx int, weight int64) int64 { + if n.level == 0 { + oldWeight := n.weights[idx] + n.weights[idx] = weight + diff := weight - oldWeight + n.sumWeight += diff + if weight == 0 { + n.items[idx] = nil + n.itemCnt-- + } + return diff + } + branchItems := n.maxItems / wrsBranches + branch := idx / branchItems + diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight) + n.weights[branch] += diff + n.sumWeight += diff + if weight == 0 { + n.itemCnt-- + } + return diff +} + +// choose recursively selects an item from the tree and returns it along with its weight +func (n *wrsNode) choose(val int64) (wrsItem, int64) { + for i, w := range n.weights { + if val < w { + if n.level == 0 { + return n.items[i].(wrsItem), n.weights[i] + } else { + return n.items[i].(*wrsNode).choose(val) + } + } else { + val -= w + } + } + panic(nil) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/randselect_test.go b/vendor/github.com/ethereum/go-ethereum/les/randselect_test.go new file mode 100644 index 0000000..9ae7726 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/randselect_test.go @@ -0,0 +1,67 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "math/rand" + "testing" +) + +type testWrsItem struct { + idx int + widx *int +} + +func (t *testWrsItem) Weight() int64 { + w := *t.widx + if w == -1 || w == t.idx { + return int64(t.idx + 1) + } + return 0 +} + +func TestWeightedRandomSelect(t *testing.T) { + testFn := func(cnt int) { + s := newWeightedRandomSelect() + w := -1 + list := make([]testWrsItem, cnt) + for i := range list { + list[i] = testWrsItem{idx: i, widx: &w} + s.update(&list[i]) + } + w = rand.Intn(cnt) + c := s.choose() + if c == nil { + t.Errorf("expected item, got nil") + } else { + if c.(*testWrsItem).idx != w { + t.Errorf("expected another item") + } + } + w = -2 + if s.choose() != nil { + t.Errorf("expected nil, got item") + } + } + testFn(1) + testFn(10) + testFn(100) + testFn(1000) + testFn(10000) + testFn(100000) + testFn(1000000) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/request_test.go b/vendor/github.com/ethereum/go-ethereum/les/request_test.go new file mode 100644 index 0000000..10e9edf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/request_test.go @@ -0,0 +1,112 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "golang.org/x/net/context" +) + +var testBankSecureTrieKey = secAddr(testBankAddress) + +func secAddr(addr common.Address) []byte { + return crypto.Keccak256(addr[:]) +} + +type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest + +func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, tfBlockAccess) } + +func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { + return &light.BlockRequest{Hash: bhash, Number: number} +} + +func TestReceiptsAccessLes1(t *testing.T) { testAccess(t, 1, tfReceiptsAccess) } + +func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { + return &light.ReceiptsRequest{Hash: bhash, Number: number} +} + +func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, tfTrieEntryAccess) } + +func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { + return &light.TrieRequest{Id: light.StateTrieID(core.GetHeader(db, bhash, core.GetBlockNumber(db, bhash))), Key: testBankSecureTrieKey} +} + +func TestCodeAccessLes1(t *testing.T) { testAccess(t, 1, tfCodeAccess) } + +func tfCodeAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { + header := core.GetHeader(db, bhash, core.GetBlockNumber(db, bhash)) + if header.Number.Uint64() < testContractDeployed { + return nil + } + sti := light.StateTrieID(header) + ci := light.StorageTrieID(sti, testContractAddr, common.Hash{}) + return &light.CodeRequest{Id: ci, Hash: crypto.Keccak256Hash(testContractCodeDeployed)} +} + +func testAccess(t *testing.T, protocol int, fn accessTestFn) { + // Assemble the test environment + pm, db, _ := newTestProtocolManagerMust(t, false, 4, testChainGen) + lpm, ldb, odr := newTestProtocolManagerMust(t, true, 0, nil) + _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) + pool := &testServerPool{} + pool.setPeer(lpeer) + odr.serverPool = pool + select { + case <-time.After(time.Millisecond * 100): + case err := <-err1: + t.Fatalf("peer 1 handshake error: %v", err) + case err := <-err2: + t.Fatalf("peer 1 handshake error: %v", err) + } + + lpm.synchronise(lpeer) + + test := func(expFail uint64) { + for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { + bhash := core.GetCanonicalHash(db, i) + if req := fn(ldb, bhash, i); req != nil { + ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + err := odr.Retrieve(ctx, req) + got := err == nil + exp := i < expFail + if exp && !got { + t.Errorf("object retrieval failed") + } + if !exp && got { + t.Errorf("unexpected object retrieval success") + } + } + } + } + + // temporarily remove peer to test odr fails + pool.setPeer(nil) + // expect retrievals to fail (except genesis block) without a les peer + test(0) + pool.setPeer(lpeer) + // expect all retrievals to pass + test(5) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/server.go b/vendor/github.com/ethereum/go-ethereum/les/server.go new file mode 100644 index 0000000..c4c6fca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/server.go @@ -0,0 +1,408 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "encoding/binary" + "math" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/les/flowcontrol" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +type LesServer struct { + protocolManager *ProtocolManager + fcManager *flowcontrol.ClientManager // nil if our node is client only + fcCostStats *requestCostStats + defParams *flowcontrol.ServerParams + stopped bool +} + +func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { + pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) + if err != nil { + return nil, err + } + pm.blockLoop() + + srv := &LesServer{protocolManager: pm} + pm.server = srv + + srv.defParams = &flowcontrol.ServerParams{ + BufLimit: 300000000, + MinRecharge: 50000, + } + srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000) + srv.fcCostStats = newCostStats(eth.ChainDb()) + return srv, nil +} + +func (s *LesServer) Protocols() []p2p.Protocol { + return s.protocolManager.SubProtocols +} + +// Start starts the LES server +func (s *LesServer) Start(srvr *p2p.Server) { + s.protocolManager.Start(srvr) +} + +// Stop stops the LES service +func (s *LesServer) Stop() { + s.fcCostStats.store() + s.fcManager.Stop() + go func() { + <-s.protocolManager.noMorePeers + }() + s.protocolManager.Stop() +} + +type requestCosts struct { + baseCost, reqCost uint64 +} + +type requestCostTable map[uint64]*requestCosts + +type RequestCostList []struct { + MsgCode, BaseCost, ReqCost uint64 +} + +func (list RequestCostList) decode() requestCostTable { + table := make(requestCostTable) + for _, e := range list { + table[e.MsgCode] = &requestCosts{ + baseCost: e.BaseCost, + reqCost: e.ReqCost, + } + } + return table +} + +func (table requestCostTable) encode() RequestCostList { + list := make(RequestCostList, len(table)) + for idx, code := range reqList { + list[idx].MsgCode = code + list[idx].BaseCost = table[code].baseCost + list[idx].ReqCost = table[code].reqCost + } + return list +} + +type linReg struct { + sumX, sumY, sumXX, sumXY float64 + cnt uint64 +} + +const linRegMaxCnt = 100000 + +func (l *linReg) add(x, y float64) { + if l.cnt >= linRegMaxCnt { + sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt + l.sumX -= l.sumX * sub + l.sumY -= l.sumY * sub + l.sumXX -= l.sumXX * sub + l.sumXY -= l.sumXY * sub + l.cnt = linRegMaxCnt - 1 + } + l.cnt++ + l.sumX += x + l.sumY += y + l.sumXX += x * x + l.sumXY += x * y +} + +func (l *linReg) calc() (b, m float64) { + if l.cnt == 0 { + return 0, 0 + } + cnt := float64(l.cnt) + d := cnt*l.sumXX - l.sumX*l.sumX + if d < 0.001 { + return l.sumY / cnt, 0 + } + m = (cnt*l.sumXY - l.sumX*l.sumY) / d + b = (l.sumY / cnt) - (m * l.sumX / cnt) + return b, m +} + +func (l *linReg) toBytes() []byte { + var arr [40]byte + binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX)) + binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY)) + binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX)) + binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY)) + binary.BigEndian.PutUint64(arr[32:40], l.cnt) + return arr[:] +} + +func linRegFromBytes(data []byte) *linReg { + if len(data) != 40 { + return nil + } + l := &linReg{} + l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8])) + l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16])) + l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24])) + l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32])) + l.cnt = binary.BigEndian.Uint64(data[32:40]) + return l +} + +type requestCostStats struct { + lock sync.RWMutex + db ethdb.Database + stats map[uint64]*linReg +} + +type requestCostStatsRlp []struct { + MsgCode uint64 + Data []byte +} + +var rcStatsKey = []byte("_requestCostStats") + +func newCostStats(db ethdb.Database) *requestCostStats { + stats := make(map[uint64]*linReg) + for _, code := range reqList { + stats[code] = &linReg{cnt: 100} + } + + if db != nil { + data, err := db.Get(rcStatsKey) + var statsRlp requestCostStatsRlp + if err == nil { + err = rlp.DecodeBytes(data, &statsRlp) + } + if err == nil { + for _, r := range statsRlp { + if stats[r.MsgCode] != nil { + if l := linRegFromBytes(r.Data); l != nil { + stats[r.MsgCode] = l + } + } + } + } + } + + return &requestCostStats{ + db: db, + stats: stats, + } +} + +func (s *requestCostStats) store() { + s.lock.Lock() + defer s.lock.Unlock() + + statsRlp := make(requestCostStatsRlp, len(reqList)) + for i, code := range reqList { + statsRlp[i].MsgCode = code + statsRlp[i].Data = s.stats[code].toBytes() + } + + if data, err := rlp.EncodeToBytes(statsRlp); err == nil { + s.db.Put(rcStatsKey, data) + } +} + +func (s *requestCostStats) getCurrentList() RequestCostList { + s.lock.Lock() + defer s.lock.Unlock() + + list := make(RequestCostList, len(reqList)) + //fmt.Println("RequestCostList") + for idx, code := range reqList { + b, m := s.stats[code].calc() + //fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000) + if m < 0 { + b += m + m = 0 + } + if b < 0 { + b = 0 + } + + list[idx].MsgCode = code + list[idx].BaseCost = uint64(b * 2) + list[idx].ReqCost = uint64(m * 2) + } + return list +} + +func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) { + s.lock.Lock() + defer s.lock.Unlock() + + c, ok := s.stats[msgCode] + if !ok || reqCnt == 0 { + return + } + c.add(float64(reqCnt), float64(cost)) +} + +func (pm *ProtocolManager) blockLoop() { + pm.wg.Add(1) + sub := pm.eventMux.Subscribe(core.ChainHeadEvent{}) + newCht := make(chan struct{}, 10) + newCht <- struct{}{} + go func() { + var mu sync.Mutex + var lastHead *types.Header + lastBroadcastTd := common.Big0 + for { + select { + case ev := <-sub.Chan(): + peers := pm.peers.AllPeers() + if len(peers) > 0 { + header := ev.Data.(core.ChainHeadEvent).Block.Header() + hash := header.Hash() + number := header.Number.Uint64() + td := core.GetTd(pm.chainDb, hash, number) + if td != nil && td.Cmp(lastBroadcastTd) > 0 { + var reorg uint64 + if lastHead != nil { + reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() + } + lastHead = header + lastBroadcastTd = td + + glog.V(logger.Debug).Infoln("===> ", number, hash, td, reorg) + + announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} + for _, p := range peers { + select { + case p.announceChn <- announce: + default: + pm.removePeer(p.id) + } + } + } + } + newCht <- struct{}{} + case <-newCht: + go func() { + mu.Lock() + more := makeCht(pm.chainDb) + mu.Unlock() + if more { + time.Sleep(time.Millisecond * 10) + newCht <- struct{}{} + } + }() + case <-pm.quitSync: + sub.Unsubscribe() + pm.wg.Done() + return + } + } + }() +} + +var ( + lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian) + chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash +) + +func getChtRoot(db ethdb.Database, num uint64) common.Hash { + var encNumber [8]byte + binary.BigEndian.PutUint64(encNumber[:], num) + data, _ := db.Get(append(chtPrefix, encNumber[:]...)) + return common.BytesToHash(data) +} + +func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) { + var encNumber [8]byte + binary.BigEndian.PutUint64(encNumber[:], num) + db.Put(append(chtPrefix, encNumber[:]...), root[:]) +} + +func makeCht(db ethdb.Database) bool { + headHash := core.GetHeadBlockHash(db) + headNum := core.GetBlockNumber(db, headHash) + + var newChtNum uint64 + if headNum > light.ChtConfirmations { + newChtNum = (headNum - light.ChtConfirmations) / light.ChtFrequency + } + + var lastChtNum uint64 + data, _ := db.Get(lastChtKey) + if len(data) == 8 { + lastChtNum = binary.BigEndian.Uint64(data[:]) + } + if newChtNum <= lastChtNum { + return false + } + + var t *trie.Trie + if lastChtNum > 0 { + var err error + t, err = trie.New(getChtRoot(db, lastChtNum), db) + if err != nil { + lastChtNum = 0 + } + } + if lastChtNum == 0 { + t, _ = trie.New(common.Hash{}, db) + } + + for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ { + hash := core.GetCanonicalHash(db, num) + if hash == (common.Hash{}) { + panic("Canonical hash not found") + } + td := core.GetTd(db, hash, num) + if td == nil { + panic("TD not found") + } + var encNumber [8]byte + binary.BigEndian.PutUint64(encNumber[:], num) + var node light.ChtNode + node.Hash = hash + node.Td = td + data, _ := rlp.EncodeToBytes(node) + t.Update(encNumber[:], data) + } + + root, err := t.Commit() + if err != nil { + lastChtNum = 0 + } else { + lastChtNum++ + + glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root) + + storeChtRoot(db, lastChtNum, root) + var data [8]byte + binary.BigEndian.PutUint64(data[:], lastChtNum) + db.Put(lastChtKey, data[:]) + } + + return newChtNum > lastChtNum +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/serverpool.go b/vendor/github.com/ethereum/go-ethereum/les/serverpool.go new file mode 100644 index 0000000..9735a71 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/serverpool.go @@ -0,0 +1,826 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package les implements the Light Ethereum Subprotocol. +package les + +import ( + "io" + "math" + "math/rand" + "net" + "strconv" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + // After a connection has been ended or timed out, there is a waiting period + // before it can be selected for connection again. + // waiting period = base delay * (1 + random(1)) + // base delay = shortRetryDelay for the first shortRetryCnt times after a + // successful connection, after that longRetryDelay is applied + shortRetryCnt = 5 + shortRetryDelay = time.Second * 5 + longRetryDelay = time.Minute * 10 + // maxNewEntries is the maximum number of newly discovered (never connected) nodes. + // If the limit is reached, the least recently discovered one is thrown out. + maxNewEntries = 1000 + // maxKnownEntries is the maximum number of known (already connected) nodes. + // If the limit is reached, the least recently connected one is thrown out. + // (not that unlike new entries, known entries are persistent) + maxKnownEntries = 1000 + // target for simultaneously connected servers + targetServerCount = 5 + // target for servers selected from the known table + // (we leave room for trying new ones if there is any) + targetKnownSelect = 3 + // after dialTimeout, consider the server unavailable and adjust statistics + dialTimeout = time.Second * 30 + // targetConnTime is the minimum expected connection duration before a server + // drops a client without any specific reason + targetConnTime = time.Minute * 10 + // new entry selection weight calculation based on most recent discovery time: + // unity until discoverExpireStart, then exponential decay with discoverExpireConst + discoverExpireStart = time.Minute * 20 + discoverExpireConst = time.Minute * 20 + // known entry selection weight is dropped by a factor of exp(-failDropLn) after + // each unsuccessful connection (restored after a successful one) + failDropLn = 0.1 + // known node connection success and quality statistics have a long term average + // and a short term value which is adjusted exponentially with a factor of + // pstatRecentAdjust with each dial/connection and also returned exponentially + // to the average with the time constant pstatReturnToMeanTC + pstatRecentAdjust = 0.1 + pstatReturnToMeanTC = time.Hour + // node address selection weight is dropped by a factor of exp(-addrFailDropLn) after + // each unsuccessful connection (restored after a successful one) + addrFailDropLn = math.Ln2 + // responseScoreTC and delayScoreTC are exponential decay time constants for + // calculating selection chances from response times and block delay times + responseScoreTC = time.Millisecond * 100 + delayScoreTC = time.Second * 5 + timeoutPow = 10 + // peerSelectMinWeight is added to calculated weights at request peer selection + // to give poorly performing peers a little chance of coming back + peerSelectMinWeight = 0.005 + // initStatsWeight is used to initialize previously unknown peers with good + // statistics to give a chance to prove themselves + initStatsWeight = 1 +) + +// serverPool implements a pool for storing and selecting newly discovered and already +// known light server nodes. It received discovered nodes, stores statistics about +// known nodes and takes care of always having enough good quality servers connected. +type serverPool struct { + db ethdb.Database + dbKey []byte + server *p2p.Server + quit chan struct{} + wg *sync.WaitGroup + connWg sync.WaitGroup + + discSetPeriod chan time.Duration + discNodes chan *discv5.Node + discLookups chan bool + + entries map[discover.NodeID]*poolEntry + lock sync.Mutex + timeout, enableRetry chan *poolEntry + adjustStats chan poolStatAdjust + + knownQueue, newQueue poolEntryQueue + knownSelect, newSelect *weightedRandomSelect + knownSelected, newSelected int + fastDiscover bool +} + +// newServerPool creates a new serverPool instance +func newServerPool(db ethdb.Database, dbPrefix []byte, server *p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool { + pool := &serverPool{ + db: db, + dbKey: append(dbPrefix, []byte(topic)...), + server: server, + quit: quit, + wg: wg, + entries: make(map[discover.NodeID]*poolEntry), + timeout: make(chan *poolEntry, 1), + adjustStats: make(chan poolStatAdjust, 100), + enableRetry: make(chan *poolEntry, 1), + knownSelect: newWeightedRandomSelect(), + newSelect: newWeightedRandomSelect(), + fastDiscover: true, + } + pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry) + pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry) + wg.Add(1) + pool.loadNodes() + pool.checkDial() + + if pool.server.DiscV5 != nil { + pool.discSetPeriod = make(chan time.Duration, 1) + pool.discNodes = make(chan *discv5.Node, 100) + pool.discLookups = make(chan bool, 100) + go pool.server.DiscV5.SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups) + } + + go pool.eventLoop() + return pool +} + +// connect should be called upon any incoming connection. If the connection has been +// dialed by the server pool recently, the appropriate pool entry is returned. +// Otherwise, the connection should be rejected. +// Note that whenever a connection has been accepted and a pool entry has been returned, +// disconnect should also always be called. +func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry { + pool.lock.Lock() + defer pool.lock.Unlock() + entry := pool.entries[p.ID()] + if entry == nil { + entry = pool.findOrNewNode(p.ID(), ip, port) + } + glog.V(logger.Debug).Infof("connecting to %v, state: %v", p.id, entry.state) + if entry.state == psConnected || entry.state == psRegistered { + return nil + } + pool.connWg.Add(1) + entry.peer = p + entry.state = psConnected + addr := &poolEntryAddress{ + ip: ip, + port: port, + lastSeen: mclock.Now(), + } + entry.lastConnected = addr + entry.addr = make(map[string]*poolEntryAddress) + entry.addr[addr.strKey()] = addr + entry.addrSelect = *newWeightedRandomSelect() + entry.addrSelect.update(addr) + return entry +} + +// registered should be called after a successful handshake +func (pool *serverPool) registered(entry *poolEntry) { + glog.V(logger.Debug).Infof("registered %v", entry.id.String()) + pool.lock.Lock() + defer pool.lock.Unlock() + + entry.state = psRegistered + entry.regTime = mclock.Now() + if !entry.known { + pool.newQueue.remove(entry) + entry.known = true + } + pool.knownQueue.setLatest(entry) + entry.shortRetry = shortRetryCnt +} + +// disconnect should be called when ending a connection. Service quality statistics +// can be updated optionally (not updated if no registration happened, in this case +// only connection statistics are updated, just like in case of timeout) +func (pool *serverPool) disconnect(entry *poolEntry) { + glog.V(logger.Debug).Infof("disconnected %v", entry.id.String()) + pool.lock.Lock() + defer pool.lock.Unlock() + + if entry.state == psRegistered { + connTime := mclock.Now() - entry.regTime + connAdjust := float64(connTime) / float64(targetConnTime) + if connAdjust > 1 { + connAdjust = 1 + } + stopped := false + select { + case <-pool.quit: + stopped = true + default: + } + if stopped { + entry.connectStats.add(1, connAdjust) + } else { + entry.connectStats.add(connAdjust, 1) + } + } + + entry.state = psNotConnected + if entry.knownSelected { + pool.knownSelected-- + } else { + pool.newSelected-- + } + pool.setRetryDial(entry) + pool.connWg.Done() +} + +const ( + pseBlockDelay = iota + pseResponseTime + pseResponseTimeout +) + +// poolStatAdjust records are sent to adjust peer block delay/response time statistics +type poolStatAdjust struct { + adjustType int + entry *poolEntry + time time.Duration +} + +// adjustBlockDelay adjusts the block announce delay statistics of a node +func (pool *serverPool) adjustBlockDelay(entry *poolEntry, time time.Duration) { + if entry == nil { + return + } + pool.adjustStats <- poolStatAdjust{pseBlockDelay, entry, time} +} + +// adjustResponseTime adjusts the request response time statistics of a node +func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, timeout bool) { + if entry == nil { + return + } + if timeout { + pool.adjustStats <- poolStatAdjust{pseResponseTimeout, entry, time} + } else { + pool.adjustStats <- poolStatAdjust{pseResponseTime, entry, time} + } +} + +type selectPeerItem struct { + peer *peer + weight int64 + wait time.Duration +} + +func (sp selectPeerItem) Weight() int64 { + return sp.weight +} + +// selectPeer selects a suitable peer for a request, also returning a necessary waiting time to perform the request +// and a "locked" flag meaning that the request has been assigned to the given peer and its execution is guaranteed +// after the given waiting time. If locked flag is false, selectPeer should be called again after the waiting time. +func (pool *serverPool) selectPeer(reqID uint64, canSend func(*peer) (bool, time.Duration)) (*peer, time.Duration, bool) { + pool.lock.Lock() + type selectPeer struct { + peer *peer + rstat, tstat float64 + } + var list []selectPeer + sel := newWeightedRandomSelect() + for _, entry := range pool.entries { + if entry.state == psRegistered { + if !entry.peer.fcServer.IsAssigned() { + list = append(list, selectPeer{entry.peer, entry.responseStats.recentAvg(), entry.timeoutStats.recentAvg()}) + } + } + } + pool.lock.Unlock() + + for _, sp := range list { + ok, wait := canSend(sp.peer) + if ok { + w := int64(1000000000 * (peerSelectMinWeight + math.Exp(-(sp.rstat+float64(wait))/float64(responseScoreTC))*math.Pow((1-sp.tstat), timeoutPow))) + sel.update(selectPeerItem{peer: sp.peer, weight: w, wait: wait}) + } + } + choice := sel.choose() + if choice == nil { + return nil, 0, false + } + peer, wait := choice.(selectPeerItem).peer, choice.(selectPeerItem).wait + locked := false + if wait < time.Millisecond*100 { + if peer.fcServer.AssignRequest(reqID) { + ok, w := canSend(peer) + wait = time.Duration(w) + if ok && wait < time.Millisecond*100 { + locked = true + } else { + peer.fcServer.DeassignRequest(reqID) + wait = time.Millisecond * 100 + } + } + } else { + wait = time.Millisecond * 100 + } + return peer, wait, locked +} + +// selectPeer selects a suitable peer for a request, waiting until an assignment to +// the request is guaranteed or the process is aborted. +func (pool *serverPool) selectPeerWait(reqID uint64, canSend func(*peer) (bool, time.Duration), abort <-chan struct{}) *peer { + for { + peer, wait, locked := pool.selectPeer(reqID, canSend) + if locked { + return peer + } + select { + case <-abort: + return nil + case <-time.After(wait): + } + } +} + +// eventLoop handles pool events and mutex locking for all internal functions +func (pool *serverPool) eventLoop() { + lookupCnt := 0 + var convTime mclock.AbsTime + if pool.discSetPeriod != nil { + pool.discSetPeriod <- time.Millisecond * 100 + } + for { + select { + case entry := <-pool.timeout: + pool.lock.Lock() + if !entry.removed { + pool.checkDialTimeout(entry) + } + pool.lock.Unlock() + + case entry := <-pool.enableRetry: + pool.lock.Lock() + if !entry.removed { + entry.delayedRetry = false + pool.updateCheckDial(entry) + } + pool.lock.Unlock() + + case adj := <-pool.adjustStats: + pool.lock.Lock() + switch adj.adjustType { + case pseBlockDelay: + adj.entry.delayStats.add(float64(adj.time), 1) + case pseResponseTime: + adj.entry.responseStats.add(float64(adj.time), 1) + adj.entry.timeoutStats.add(0, 1) + case pseResponseTimeout: + adj.entry.timeoutStats.add(1, 1) + } + pool.lock.Unlock() + + case node := <-pool.discNodes: + pool.lock.Lock() + entry := pool.findOrNewNode(discover.NodeID(node.ID), node.IP, node.TCP) + pool.updateCheckDial(entry) + pool.lock.Unlock() + + case conv := <-pool.discLookups: + if conv { + if lookupCnt == 0 { + convTime = mclock.Now() + } + lookupCnt++ + if pool.fastDiscover && (lookupCnt == 50 || time.Duration(mclock.Now()-convTime) > time.Minute) { + pool.fastDiscover = false + if pool.discSetPeriod != nil { + pool.discSetPeriod <- time.Minute + } + } + } + + case <-pool.quit: + if pool.discSetPeriod != nil { + close(pool.discSetPeriod) + } + pool.connWg.Wait() + pool.saveNodes() + pool.wg.Done() + return + + } + } +} + +func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16) *poolEntry { + now := mclock.Now() + entry := pool.entries[id] + if entry == nil { + glog.V(logger.Debug).Infof("discovered %v", id.String()) + entry = &poolEntry{ + id: id, + addr: make(map[string]*poolEntryAddress), + addrSelect: *newWeightedRandomSelect(), + shortRetry: shortRetryCnt, + } + pool.entries[id] = entry + // initialize previously unknown peers with good statistics to give a chance to prove themselves + entry.connectStats.add(1, initStatsWeight) + entry.delayStats.add(0, initStatsWeight) + entry.responseStats.add(0, initStatsWeight) + entry.timeoutStats.add(0, initStatsWeight) + } + entry.lastDiscovered = now + addr := &poolEntryAddress{ + ip: ip, + port: port, + } + if a, ok := entry.addr[addr.strKey()]; ok { + addr = a + } else { + entry.addr[addr.strKey()] = addr + } + addr.lastSeen = now + entry.addrSelect.update(addr) + if !entry.known { + pool.newQueue.setLatest(entry) + } + return entry +} + +// loadNodes loads known nodes and their statistics from the database +func (pool *serverPool) loadNodes() { + enc, err := pool.db.Get(pool.dbKey) + if err != nil { + return + } + var list []*poolEntry + err = rlp.DecodeBytes(enc, &list) + if err != nil { + glog.V(logger.Debug).Infof("node list decode error: %v", err) + return + } + for _, e := range list { + glog.V(logger.Debug).Infof("loaded server stats %016x fails: %v connStats: %v / %v delayStats: %v / %v responseStats: %v / %v timeoutStats: %v / %v", e.id[0:8], e.lastConnected.fails, e.connectStats.avg, e.connectStats.weight, time.Duration(e.delayStats.avg), e.delayStats.weight, time.Duration(e.responseStats.avg), e.responseStats.weight, e.timeoutStats.avg, e.timeoutStats.weight) + pool.entries[e.id] = e + pool.knownQueue.setLatest(e) + pool.knownSelect.update((*knownEntry)(e)) + } +} + +// saveNodes saves known nodes and their statistics into the database. Nodes are +// ordered from least to most recently connected. +func (pool *serverPool) saveNodes() { + list := make([]*poolEntry, len(pool.knownQueue.queue)) + for i := range list { + list[i] = pool.knownQueue.fetchOldest() + } + enc, err := rlp.EncodeToBytes(list) + if err == nil { + pool.db.Put(pool.dbKey, enc) + } +} + +// removeEntry removes a pool entry when the entry count limit is reached. +// Note that it is called by the new/known queues from which the entry has already +// been removed so removing it from the queues is not necessary. +func (pool *serverPool) removeEntry(entry *poolEntry) { + pool.newSelect.remove((*discoveredEntry)(entry)) + pool.knownSelect.remove((*knownEntry)(entry)) + entry.removed = true + delete(pool.entries, entry.id) +} + +// setRetryDial starts the timer which will enable dialing a certain node again +func (pool *serverPool) setRetryDial(entry *poolEntry) { + delay := longRetryDelay + if entry.shortRetry > 0 { + entry.shortRetry-- + delay = shortRetryDelay + } + delay += time.Duration(rand.Int63n(int64(delay) + 1)) + entry.delayedRetry = true + go func() { + select { + case <-pool.quit: + case <-time.After(delay): + select { + case <-pool.quit: + case pool.enableRetry <- entry: + } + } + }() +} + +// updateCheckDial is called when an entry can potentially be dialed again. It updates +// its selection weights and checks if new dials can/should be made. +func (pool *serverPool) updateCheckDial(entry *poolEntry) { + pool.newSelect.update((*discoveredEntry)(entry)) + pool.knownSelect.update((*knownEntry)(entry)) + pool.checkDial() +} + +// checkDial checks if new dials can/should be made. It tries to select servers both +// based on good statistics and recent discovery. +func (pool *serverPool) checkDial() { + fillWithKnownSelects := !pool.fastDiscover + for pool.knownSelected < targetKnownSelect { + entry := pool.knownSelect.choose() + if entry == nil { + fillWithKnownSelects = false + break + } + pool.dial((*poolEntry)(entry.(*knownEntry)), true) + } + for pool.knownSelected+pool.newSelected < targetServerCount { + entry := pool.newSelect.choose() + if entry == nil { + break + } + pool.dial((*poolEntry)(entry.(*discoveredEntry)), false) + } + if fillWithKnownSelects { + // no more newly discovered nodes to select and since fast discover period + // is over, we probably won't find more in the near future so select more + // known entries if possible + for pool.knownSelected < targetServerCount { + entry := pool.knownSelect.choose() + if entry == nil { + break + } + pool.dial((*poolEntry)(entry.(*knownEntry)), true) + } + } +} + +// dial initiates a new connection +func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) { + if entry.state != psNotConnected { + return + } + entry.state = psDialed + entry.knownSelected = knownSelected + if knownSelected { + pool.knownSelected++ + } else { + pool.newSelected++ + } + addr := entry.addrSelect.choose().(*poolEntryAddress) + glog.V(logger.Debug).Infof("dialing %v out of %v, known: %v", entry.id.String()+"@"+addr.strKey(), len(entry.addr), knownSelected) + entry.dialed = addr + go func() { + pool.server.AddPeer(discover.NewNode(entry.id, addr.ip, addr.port, addr.port)) + select { + case <-pool.quit: + case <-time.After(dialTimeout): + select { + case <-pool.quit: + case pool.timeout <- entry: + } + } + }() +} + +// checkDialTimeout checks if the node is still in dialed state and if so, resets it +// and adjusts connection statistics accordingly. +func (pool *serverPool) checkDialTimeout(entry *poolEntry) { + if entry.state != psDialed { + return + } + glog.V(logger.Debug).Infof("timeout %v", entry.id.String()+"@"+entry.dialed.strKey()) + entry.state = psNotConnected + if entry.knownSelected { + pool.knownSelected-- + } else { + pool.newSelected-- + } + entry.connectStats.add(0, 1) + entry.dialed.fails++ + pool.setRetryDial(entry) +} + +const ( + psNotConnected = iota + psDialed + psConnected + psRegistered +) + +// poolEntry represents a server node and stores its current state and statistics. +type poolEntry struct { + peer *peer + id discover.NodeID + addr map[string]*poolEntryAddress + lastConnected, dialed *poolEntryAddress + addrSelect weightedRandomSelect + + lastDiscovered mclock.AbsTime + known, knownSelected bool + connectStats, delayStats poolStats + responseStats, timeoutStats poolStats + state int + regTime mclock.AbsTime + queueIdx int + removed bool + + delayedRetry bool + shortRetry int +} + +func (e *poolEntry) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{e.id, e.lastConnected.ip, e.lastConnected.port, e.lastConnected.fails, &e.connectStats, &e.delayStats, &e.responseStats, &e.timeoutStats}) +} + +func (e *poolEntry) DecodeRLP(s *rlp.Stream) error { + var entry struct { + ID discover.NodeID + IP net.IP + Port uint16 + Fails uint + CStat, DStat, RStat, TStat poolStats + } + if err := s.Decode(&entry); err != nil { + return err + } + addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()} + e.id = entry.ID + e.addr = make(map[string]*poolEntryAddress) + e.addr[addr.strKey()] = addr + e.addrSelect = *newWeightedRandomSelect() + e.addrSelect.update(addr) + e.lastConnected = addr + e.connectStats = entry.CStat + e.delayStats = entry.DStat + e.responseStats = entry.RStat + e.timeoutStats = entry.TStat + e.shortRetry = shortRetryCnt + e.known = true + return nil +} + +// discoveredEntry implements wrsItem +type discoveredEntry poolEntry + +// Weight calculates random selection weight for newly discovered entries +func (e *discoveredEntry) Weight() int64 { + if e.state != psNotConnected || e.delayedRetry { + return 0 + } + t := time.Duration(mclock.Now() - e.lastDiscovered) + if t <= discoverExpireStart { + return 1000000000 + } else { + return int64(1000000000 * math.Exp(-float64(t-discoverExpireStart)/float64(discoverExpireConst))) + } +} + +// knownEntry implements wrsItem +type knownEntry poolEntry + +// Weight calculates random selection weight for known entries +func (e *knownEntry) Weight() int64 { + if e.state != psNotConnected || !e.known || e.delayedRetry { + return 0 + } + return int64(1000000000 * e.connectStats.recentAvg() * math.Exp(-float64(e.lastConnected.fails)*failDropLn-e.responseStats.recentAvg()/float64(responseScoreTC)-e.delayStats.recentAvg()/float64(delayScoreTC)) * math.Pow((1-e.timeoutStats.recentAvg()), timeoutPow)) +} + +// poolEntryAddress is a separate object because currently it is necessary to remember +// multiple potential network addresses for a pool entry. This will be removed after +// the final implementation of v5 discovery which will retrieve signed and serial +// numbered advertisements, making it clear which IP/port is the latest one. +type poolEntryAddress struct { + ip net.IP + port uint16 + lastSeen mclock.AbsTime // last time it was discovered, connected or loaded from db + fails uint // connection failures since last successful connection (persistent) +} + +func (a *poolEntryAddress) Weight() int64 { + t := time.Duration(mclock.Now() - a.lastSeen) + return int64(1000000*math.Exp(-float64(t)/float64(discoverExpireConst)-float64(a.fails)*addrFailDropLn)) + 1 +} + +func (a *poolEntryAddress) strKey() string { + return a.ip.String() + ":" + strconv.Itoa(int(a.port)) +} + +// poolStats implement statistics for a certain quantity with a long term average +// and a short term value which is adjusted exponentially with a factor of +// pstatRecentAdjust with each update and also returned exponentially to the +// average with the time constant pstatReturnToMeanTC +type poolStats struct { + sum, weight, avg, recent float64 + lastRecalc mclock.AbsTime +} + +// init initializes stats with a long term sum/update count pair retrieved from the database +func (s *poolStats) init(sum, weight float64) { + s.sum = sum + s.weight = weight + var avg float64 + if weight > 0 { + avg = s.sum / weight + } + s.avg = avg + s.recent = avg + s.lastRecalc = mclock.Now() +} + +// recalc recalculates recent value return-to-mean and long term average +func (s *poolStats) recalc() { + now := mclock.Now() + s.recent = s.avg + (s.recent-s.avg)*math.Exp(-float64(now-s.lastRecalc)/float64(pstatReturnToMeanTC)) + if s.sum == 0 { + s.avg = 0 + } else { + if s.sum > s.weight*1e30 { + s.avg = 1e30 + } else { + s.avg = s.sum / s.weight + } + } + s.lastRecalc = now +} + +// add updates the stats with a new value +func (s *poolStats) add(value, weight float64) { + s.weight += weight + s.sum += value * weight + s.recalc() +} + +// recentAvg returns the short-term adjusted average +func (s *poolStats) recentAvg() float64 { + s.recalc() + return s.recent +} + +func (s *poolStats) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{math.Float64bits(s.sum), math.Float64bits(s.weight)}) +} + +func (s *poolStats) DecodeRLP(st *rlp.Stream) error { + var stats struct { + SumUint, WeightUint uint64 + } + if err := st.Decode(&stats); err != nil { + return err + } + s.init(math.Float64frombits(stats.SumUint), math.Float64frombits(stats.WeightUint)) + return nil +} + +// poolEntryQueue keeps track of its least recently accessed entries and removes +// them when the number of entries reaches the limit +type poolEntryQueue struct { + queue map[int]*poolEntry // known nodes indexed by their latest lastConnCnt value + newPtr, oldPtr, maxCnt int + removeFromPool func(*poolEntry) +} + +// newPoolEntryQueue returns a new poolEntryQueue +func newPoolEntryQueue(maxCnt int, removeFromPool func(*poolEntry)) poolEntryQueue { + return poolEntryQueue{queue: make(map[int]*poolEntry), maxCnt: maxCnt, removeFromPool: removeFromPool} +} + +// fetchOldest returns and removes the least recently accessed entry +func (q *poolEntryQueue) fetchOldest() *poolEntry { + if len(q.queue) == 0 { + return nil + } + for { + if e := q.queue[q.oldPtr]; e != nil { + delete(q.queue, q.oldPtr) + q.oldPtr++ + return e + } + q.oldPtr++ + } +} + +// remove removes an entry from the queue +func (q *poolEntryQueue) remove(entry *poolEntry) { + if q.queue[entry.queueIdx] == entry { + delete(q.queue, entry.queueIdx) + } +} + +// setLatest adds or updates a recently accessed entry. It also checks if an old entry +// needs to be removed and removes it from the parent pool too with a callback function. +func (q *poolEntryQueue) setLatest(entry *poolEntry) { + if q.queue[entry.queueIdx] == entry { + delete(q.queue, entry.queueIdx) + } else { + if len(q.queue) == q.maxCnt { + e := q.fetchOldest() + q.remove(e) + q.removeFromPool(e) + } + } + entry.queueIdx = q.newPtr + q.queue[entry.queueIdx] = entry + q.newPtr++ +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/sync.go b/vendor/github.com/ethereum/go-ethereum/les/sync.go new file mode 100644 index 0000000..c143cb1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/sync.go @@ -0,0 +1,84 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "time" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/light" + "golang.org/x/net/context" +) + +const ( + //forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available + minDesiredPeerCount = 5 // Amount of peers desired to start syncing +) + +// syncer is responsible for periodically synchronising with the network, both +// downloading hashes and blocks as well as handling the announcement handler. +func (pm *ProtocolManager) syncer() { + // Start and ensure cleanup of sync mechanisms + //pm.fetcher.Start() + //defer pm.fetcher.Stop() + defer pm.downloader.Terminate() + + // Wait for different events to fire synchronisation operations + //forceSync := time.Tick(forceSyncCycle) + for { + select { + case <-pm.newPeerCh: + /* // Make sure we have peers to select from, then sync + if pm.peers.Len() < minDesiredPeerCount { + break + } + go pm.synchronise(pm.peers.BestPeer()) + */ + /*case <-forceSync: + // Force a sync even if not enough peers are present + go pm.synchronise(pm.peers.BestPeer()) + */ + case <-pm.noMorePeers: + return + } + } +} + +func (pm *ProtocolManager) needToSync(peerHead blockInfo) bool { + head := pm.blockchain.CurrentHeader() + currentTd := core.GetTd(pm.chainDb, head.Hash(), head.Number.Uint64()) + return currentTd != nil && peerHead.Td.Cmp(currentTd) > 0 +} + +// synchronise tries to sync up our local block chain with a remote peer. +func (pm *ProtocolManager) synchronise(peer *peer) { + // Short circuit if no peers are available + if peer == nil { + return + } + + // Make sure the peer's TD is higher than our own. + if !pm.needToSync(peer.headBlockInfo()) { + return + } + + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + pm.blockchain.(*light.LightChain).SyncCht(ctx) + + pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/txrelay.go b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go new file mode 100644 index 0000000..76d416c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/txrelay.go @@ -0,0 +1,156 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type ltrInfo struct { + tx *types.Transaction + sentTo map[*peer]struct{} +} + +type LesTxRelay struct { + txSent map[common.Hash]*ltrInfo + txPending map[common.Hash]struct{} + ps *peerSet + peerList []*peer + peerStartPos int + lock sync.RWMutex +} + +func NewLesTxRelay() *LesTxRelay { + return &LesTxRelay{ + txSent: make(map[common.Hash]*ltrInfo), + txPending: make(map[common.Hash]struct{}), + ps: newPeerSet(), + } +} + +func (self *LesTxRelay) addPeer(p *peer) { + self.lock.Lock() + defer self.lock.Unlock() + + self.ps.Register(p) + self.peerList = self.ps.AllPeers() +} + +func (self *LesTxRelay) removePeer(id string) { + self.lock.Lock() + defer self.lock.Unlock() + + self.ps.Unregister(id) + self.peerList = self.ps.AllPeers() +} + +// send sends a list of transactions to at most a given number of peers at +// once, never resending any particular transaction to the same peer twice +func (self *LesTxRelay) send(txs types.Transactions, count int) { + sendTo := make(map[*peer]types.Transactions) + + self.peerStartPos++ // rotate the starting position of the peer list + if self.peerStartPos >= len(self.peerList) { + self.peerStartPos = 0 + } + + for _, tx := range txs { + hash := tx.Hash() + ltr, ok := self.txSent[hash] + if !ok { + ltr = <rInfo{ + tx: tx, + sentTo: make(map[*peer]struct{}), + } + self.txSent[hash] = ltr + self.txPending[hash] = struct{}{} + } + + if len(self.peerList) > 0 { + cnt := count + pos := self.peerStartPos + for { + peer := self.peerList[pos] + if _, ok := ltr.sentTo[peer]; !ok { + sendTo[peer] = append(sendTo[peer], tx) + ltr.sentTo[peer] = struct{}{} + cnt-- + } + if cnt == 0 { + break // sent it to the desired number of peers + } + pos++ + if pos == len(self.peerList) { + pos = 0 + } + if pos == self.peerStartPos { + break // tried all available peers + } + } + } + } + + for p, list := range sendTo { + cost := p.GetRequestCost(SendTxMsg, len(list)) + go func(p *peer, list types.Transactions, cost uint64) { + p.SendTxs(cost, list) + }(p, list, cost) + } +} + +func (self *LesTxRelay) Send(txs types.Transactions) { + self.lock.Lock() + defer self.lock.Unlock() + + self.send(txs, 3) +} + +func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { + self.lock.Lock() + defer self.lock.Unlock() + + for _, hash := range mined { + delete(self.txPending, hash) + } + + for _, hash := range rollback { + self.txPending[hash] = struct{}{} + } + + if len(self.txPending) > 0 { + txs := make(types.Transactions, len(self.txPending)) + i := 0 + for hash := range self.txPending { + txs[i] = self.txSent[hash].tx + i++ + } + self.send(txs, 1) + } +} + +func (self *LesTxRelay) Discard(hashes []common.Hash) { + self.lock.Lock() + defer self.lock.Unlock() + + for _, hash := range hashes { + delete(self.txSent, hash) + delete(self.txPending, hash) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go new file mode 100644 index 0000000..0d28ad2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go @@ -0,0 +1,518 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" + "github.com/hashicorp/golang-lru" + "golang.org/x/net/context" +) + +var ( + bodyCacheLimit = 256 + blockCacheLimit = 256 +) + +// LightChain represents a canonical chain that by default only handles block +// headers, downloading block bodies and receipts on demand through an ODR +// interface. It only does header validation during chain insertion. +type LightChain struct { + hc *core.HeaderChain + chainDb ethdb.Database + odr OdrBackend + eventMux *event.TypeMux + genesisBlock *types.Block + + mu sync.RWMutex + chainmu sync.RWMutex + procmu sync.RWMutex + + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + + quit chan struct{} + running int32 // running must be called automically + // procInterrupt must be atomically called + procInterrupt int32 // interrupt signaler for block processing + wg sync.WaitGroup + + pow pow.PoW + validator core.HeaderValidator +} + +// NewLightChain returns a fully initialised light chain using information +// available in the database. It initialises the default Ethereum header +// validator. +func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { + bodyCache, _ := lru.New(bodyCacheLimit) + bodyRLPCache, _ := lru.New(bodyCacheLimit) + blockCache, _ := lru.New(blockCacheLimit) + + bc := &LightChain{ + chainDb: odr.Database(), + odr: odr, + eventMux: mux, + quit: make(chan struct{}), + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + blockCache: blockCache, + pow: pow, + } + + var err error + bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.Validator, bc.getProcInterrupt) + bc.SetValidator(core.NewHeaderValidator(config, bc.hc, pow)) + if err != nil { + return nil, err + } + + bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0) + if bc.genesisBlock == nil { + bc.genesisBlock, err = core.WriteDefaultGenesisBlock(odr.Database()) + if err != nil { + return nil, err + } + glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") + } + + if bc.genesisBlock.Hash() == (common.Hash{212, 229, 103, 64, 248, 118, 174, 248, 192, 16, 184, 106, 64, 213, 245, 103, 69, 161, 24, 208, 144, 106, 52, 230, 154, 236, 140, 13, 177, 203, 143, 163}) { + // add trusted CHT + if config.DAOForkSupport { + WriteTrustedCht(bc.chainDb, TrustedCht{ + Number: 637, + Root: common.HexToHash("01e408d9b1942f05dba1a879f3eaafe34d219edaeb8223fecf1244cc023d3e23"), + }) + } else { + WriteTrustedCht(bc.chainDb, TrustedCht{ + Number: 523, + Root: common.HexToHash("c035076523faf514038f619715de404a65398c51899b5dccca9c05b00bc79315"), + }) + } + glog.V(logger.Info).Infoln("Added trusted CHT for mainnet") + } else { + if bc.genesisBlock.Hash() == (common.Hash{12, 215, 134, 162, 66, 93, 22, 241, 82, 198, 88, 49, 108, 66, 62, 108, 225, 24, 30, 21, 195, 41, 88, 38, 215, 201, 144, 76, 186, 156, 227, 3}) { + // add trusted CHT for testnet + WriteTrustedCht(bc.chainDb, TrustedCht{ + Number: 452, + Root: common.HexToHash("511da2c88e32b14cf4a4e62f7fcbb297139faebc260a4ab5eb43cce6edcba324"), + }) + glog.V(logger.Info).Infoln("Added trusted CHT for testnet") + } else { + DeleteTrustedCht(bc.chainDb) + } + } + + if err := bc.loadLastState(); err != nil { + return nil, err + } + // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain + for hash := range core.BadHashes { + if header := bc.GetHeaderByHash(hash); header != nil { + glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4]) + bc.SetHead(header.Number.Uint64() - 1) + glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation") + } + } + return bc, nil +} + +func (self *LightChain) getProcInterrupt() bool { + return atomic.LoadInt32(&self.procInterrupt) == 1 +} + +// Odr returns the ODR backend of the chain +func (self *LightChain) Odr() OdrBackend { + return self.odr +} + +// loadLastState loads the last known chain state from the database. This method +// assumes that the chain manager mutex is held. +func (self *LightChain) loadLastState() error { + if head := core.GetHeadHeaderHash(self.chainDb); head == (common.Hash{}) { + // Corrupt or empty database, init from scratch + self.Reset() + } else { + if header := self.GetHeaderByHash(head); header != nil { + self.hc.SetCurrentHeader(header) + } + } + + // Issue a status log and return + header := self.hc.CurrentHeader() + headerTd := self.GetTd(header.Hash(), header.Number.Uint64()) + glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd) + + return nil +} + +// SetHead rewinds the local chain to a new head. Everything above the new +// head will be deleted and the new one set. +func (bc *LightChain) SetHead(head uint64) { + bc.mu.Lock() + defer bc.mu.Unlock() + + bc.hc.SetHead(head, nil) + bc.loadLastState() +} + +// GasLimit returns the gas limit of the current HEAD block. +func (self *LightChain) GasLimit() *big.Int { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.hc.CurrentHeader().GasLimit +} + +// LastBlockHash return the hash of the HEAD block. +func (self *LightChain) LastBlockHash() common.Hash { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.hc.CurrentHeader().Hash() +} + +// Status returns status information about the current chain such as the HEAD Td, +// the HEAD hash and the hash of the genesis block. +func (self *LightChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { + self.mu.RLock() + defer self.mu.RUnlock() + + header := self.hc.CurrentHeader() + hash := header.Hash() + return self.GetTd(hash, header.Number.Uint64()), hash, self.genesisBlock.Hash() +} + +// SetValidator sets the validator which is used to validate incoming headers. +func (self *LightChain) SetValidator(validator core.HeaderValidator) { + self.procmu.Lock() + defer self.procmu.Unlock() + self.validator = validator +} + +// Validator returns the current header validator. +func (self *LightChain) Validator() core.HeaderValidator { + self.procmu.RLock() + defer self.procmu.RUnlock() + return self.validator +} + +// State returns a new mutable state based on the current HEAD block. +func (self *LightChain) State() *LightState { + return NewLightState(StateTrieID(self.hc.CurrentHeader()), self.odr) +} + +// Reset purges the entire blockchain, restoring it to its genesis state. +func (bc *LightChain) Reset() { + bc.ResetWithGenesisBlock(bc.genesisBlock) +} + +// ResetWithGenesisBlock purges the entire blockchain, restoring it to the +// specified genesis state. +func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { + // Dump the entire block chain and purge the caches + bc.SetHead(0) + + bc.mu.Lock() + defer bc.mu.Unlock() + + // Prepare the genesis block and reinitialise the chain + if err := core.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { + glog.Fatalf("failed to write genesis block TD: %v", err) + } + if err := core.WriteBlock(bc.chainDb, genesis); err != nil { + glog.Fatalf("failed to write genesis block: %v", err) + } + bc.genesisBlock = genesis + bc.hc.SetGenesis(bc.genesisBlock.Header()) + bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) +} + +// Accessors + +// Genesis returns the genesis block +func (bc *LightChain) Genesis() *types.Block { + return bc.genesisBlock +} + +// GetBody retrieves a block body (transactions and uncles) from the database +// or ODR service by hash, caching it if found. +func (self *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyCache.Get(hash); ok { + body := cached.(*types.Body) + return body, nil + } + body, err := GetBody(ctx, self.odr, hash, self.hc.GetBlockNumber(hash)) + if err != nil { + return nil, err + } + // Cache the found body for next time and return + self.bodyCache.Add(hash, body) + return body, nil +} + +// GetBodyRLP retrieves a block body in RLP encoding from the database or +// ODR service by hash, caching it if found. +func (self *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := self.bodyRLPCache.Get(hash); ok { + return cached.(rlp.RawValue), nil + } + body, err := GetBodyRLP(ctx, self.odr, hash, self.hc.GetBlockNumber(hash)) + if err != nil { + return nil, err + } + // Cache the found body for next time and return + self.bodyRLPCache.Add(hash, body) + return body, nil +} + +// HasBlock checks if a block is fully present in the database or not, caching +// it if present. +func (bc *LightChain) HasBlock(hash common.Hash) bool { + blk, _ := bc.GetBlockByHash(NoOdr, hash) + return blk != nil +} + +// GetBlock retrieves a block from the database or ODR service by hash and number, +// caching it if found. +func (self *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { + // Short circuit if the block's already in the cache, retrieve otherwise + if block, ok := self.blockCache.Get(hash); ok { + return block.(*types.Block), nil + } + block, err := GetBlock(ctx, self.odr, hash, number) + if err != nil { + return nil, err + } + // Cache the found block for next time and return + self.blockCache.Add(block.Hash(), block) + return block, nil +} + +// GetBlockByHash retrieves a block from the database or ODR service by hash, +// caching it if found. +func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + return self.GetBlock(ctx, hash, self.hc.GetBlockNumber(hash)) +} + +// GetBlockByNumber retrieves a block from the database or ODR service by +// number, caching it (associated with its hash) if found. +func (self *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) { + hash, err := GetCanonicalHash(ctx, self.odr, number) + if hash == (common.Hash{}) || err != nil { + return nil, err + } + return self.GetBlock(ctx, hash, number) +} + +// Stop stops the blockchain service. If any imports are currently in progress +// it will abort them using the procInterrupt. +func (bc *LightChain) Stop() { + if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { + return + } + close(bc.quit) + atomic.StoreInt32(&bc.procInterrupt, 1) + + bc.wg.Wait() + + glog.V(logger.Info).Infoln("Chain manager stopped") +} + +// Rollback is designed to remove a chain of links from the database that aren't +// certain enough to be valid. +func (self *LightChain) Rollback(chain []common.Hash) { + self.mu.Lock() + defer self.mu.Unlock() + + for i := len(chain) - 1; i >= 0; i-- { + hash := chain[i] + + if head := self.hc.CurrentHeader(); head.Hash() == hash { + self.hc.SetCurrentHeader(self.GetHeader(head.ParentHash, head.Number.Uint64()-1)) + } + } +} + +// postChainEvents iterates over the events generated by a chain insertion and +// posts them into the event mux. +func (self *LightChain) postChainEvents(events []interface{}) { + for _, event := range events { + if event, ok := event.(core.ChainEvent); ok { + if self.LastBlockHash() == event.Hash { + self.eventMux.Post(core.ChainHeadEvent{Block: event.Block}) + } + } + // Fire the insertion events individually too + self.eventMux.Post(event) + } +} + +// InsertHeaderChain attempts to insert the given header chain in to the local +// chain, possibly creating a reorg. If an error is returned, it will return the +// index number of the failing header as well an error describing what went wrong. +// +// The verify parameter can be used to fine tune whether nonce verification +// should be done or not. The reason behind the optional check is because some +// of the header retrieval mechanisms already need to verfy nonces, as well as +// because nonces can be verified sparsely, not needing to check each. +// +// In the case of a light chain, InsertHeaderChain also creates and posts light +// chain events when necessary. +func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { + // Make sure only one thread manipulates the chain at once + self.chainmu.Lock() + defer self.chainmu.Unlock() + + self.wg.Add(1) + defer self.wg.Done() + + var events []interface{} + whFunc := func(header *types.Header) error { + self.mu.Lock() + defer self.mu.Unlock() + + status, err := self.hc.WriteHeader(header) + + switch status { + case core.CanonStatTy: + if glog.V(logger.Debug) { + glog.Infof("[%v] inserted header #%d (%x...).\n", time.Now().UnixNano(), header.Number, header.Hash().Bytes()[0:4]) + } + events = append(events, core.ChainEvent{Block: types.NewBlockWithHeader(header), Hash: header.Hash()}) + + case core.SideStatTy: + if glog.V(logger.Detail) { + glog.Infof("inserted forked header #%d (TD=%v) (%x...).\n", header.Number, header.Difficulty, header.Hash().Bytes()[0:4]) + } + events = append(events, core.ChainSideEvent{Block: types.NewBlockWithHeader(header)}) + + case core.SplitStatTy: + events = append(events, core.ChainSplitEvent{Block: types.NewBlockWithHeader(header)}) + } + + return err + } + i, err := self.hc.InsertHeaderChain(chain, checkFreq, whFunc) + go self.postChainEvents(events) + return i, err +} + +// CurrentHeader retrieves the current head header of the canonical chain. The +// header is retrieved from the HeaderChain's internal cache. +func (self *LightChain) CurrentHeader() *types.Header { + self.mu.RLock() + defer self.mu.RUnlock() + + return self.hc.CurrentHeader() +} + +// GetTd retrieves a block's total difficulty in the canonical chain from the +// database by hash and number, caching it if found. +func (self *LightChain) GetTd(hash common.Hash, number uint64) *big.Int { + return self.hc.GetTd(hash, number) +} + +// GetTdByHash retrieves a block's total difficulty in the canonical chain from the +// database by hash, caching it if found. +func (self *LightChain) GetTdByHash(hash common.Hash) *big.Int { + return self.hc.GetTdByHash(hash) +} + +// GetHeader retrieves a block header from the database by hash and number, +// caching it if found. +func (self *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header { + return self.hc.GetHeader(hash, number) +} + +// GetHeaderByHash retrieves a block header from the database by hash, caching it if +// found. +func (self *LightChain) GetHeaderByHash(hash common.Hash) *types.Header { + return self.hc.GetHeaderByHash(hash) +} + +// HasHeader checks if a block header is present in the database or not, caching +// it if present. +func (bc *LightChain) HasHeader(hash common.Hash) bool { + return bc.hc.HasHeader(hash) +} + +// GetBlockHashesFromHash retrieves a number of block hashes starting at a given +// hash, fetching towards the genesis block. +func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + return self.hc.GetBlockHashesFromHash(hash, max) +} + +// GetHeaderByNumber retrieves a block header from the database by number, +// caching it (associated with its hash) if found. +func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header { + return self.hc.GetHeaderByNumber(number) +} + +// GetHeaderByNumberOdr retrieves a block header from the database or network +// by number, caching it (associated with its hash) if found. +func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) { + if header := self.hc.GetHeaderByNumber(number); header != nil { + return header, nil + } + return GetHeaderByNumber(ctx, self.odr, number) +} + +func (self *LightChain) SyncCht(ctx context.Context) bool { + headNum := self.CurrentHeader().Number.Uint64() + cht := GetTrustedCht(self.chainDb) + if headNum+1 < cht.Number*ChtFrequency { + num := cht.Number*ChtFrequency - 1 + header, err := GetHeaderByNumber(ctx, self.odr, num) + if header != nil && err == nil { + self.mu.Lock() + if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { + self.hc.SetCurrentHeader(header) + } + self.mu.Unlock() + return true + } + } + return false +} + +// LockChain locks the chain mutex for reading so that multiple canonical hashes can be +// retrieved while it is guaranteed that they belong to the same version of the chain +func (self *LightChain) LockChain() { + self.chainmu.RLock() +} + +// UnlockChain unlocks the chain mutex +func (self *LightChain) UnlockChain() { + self.chainmu.RUnlock() +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain_test.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain_test.go new file mode 100644 index 0000000..0ba9258 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain_test.go @@ -0,0 +1,404 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "fmt" + "math/big" + "runtime" + "testing" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "github.com/hashicorp/golang-lru" + "golang.org/x/net/context" +) + +// So we can deterministically seed different blockchains +var ( + canonicalSeed = 1 + forkSeed = 2 +) + +// makeHeaderChain creates a deterministic chain of headers rooted at parent. +func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { + blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), db, n, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) + }) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + return headers +} + +func testChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} +} + +// newCanonical creates a chain database, and injects a deterministic canonical +// chain. Depending on the full flag, if creates either a full block chain or a +// header only chain. +func newCanonical(n int) (ethdb.Database, *LightChain, error) { + // Create te new chain database + db, _ := ethdb.NewMemDatabase() + evmux := &event.TypeMux{} + + // Initialize a fresh chain with only a genesis block + genesis, _ := core.WriteTestNetGenesisBlock(db) + + blockchain, _ := NewLightChain(&dummyOdr{db: db}, testChainConfig(), core.FakePow{}, evmux) + // Create and inject the requested chain + if n == 0 { + return db, blockchain, nil + } + // Header-only chain requested + headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) + _, err := blockchain.InsertHeaderChain(headers, 1) + return db, blockchain, err +} + +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) +} + +func thePow() pow.PoW { + pow, _ := ethash.NewForTesting() + return pow +} + +func theLightChain(db ethdb.Database, t *testing.T) *LightChain { + var eventMux event.TypeMux + core.WriteTestNetGenesisBlock(db) + LightChain, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), thePow(), &eventMux) + if err != nil { + t.Error("failed creating LightChain:", err) + t.FailNow() + return nil + } + + return LightChain +} + +// Test fork of length N starting from block i +func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) { + // Copy old chain up to #i into a new db + db, LightChain2, err := newCanonical(i) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + // Assert the chains have the same header/block at #i + var hash1, hash2 common.Hash + hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash() + hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash() + if hash1 != hash2 { + t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) + } + // Extend the newly created chain + var ( + headerChainB []*types.Header + ) + headerChainB = makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed) + if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + // Sanity check that the forked chain can be imported into the original + var tdPre, tdPost *big.Int + + tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash()) + if err := testHeaderChainImport(headerChainB, LightChain); err != nil { + t.Fatalf("failed to import forked header chain: %v", err) + } + tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) + // Compare the total difficulties of the chains + comparator(tdPre, tdPost) +} + +func printChain(bc *LightChain) { + for i := bc.CurrentHeader().Number.Uint64(); i > 0; i-- { + b := bc.GetHeaderByNumber(uint64(i)) + fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty) + } +} + +// testHeaderChainImport tries to process a chain of header, writing them into +// the database if successful. +func testHeaderChainImport(chain []*types.Header, LightChain *LightChain) error { + for _, header := range chain { + // Try and validate the header + if err := LightChain.Validator().ValidateHeader(header, LightChain.GetHeaderByHash(header.ParentHash), false); err != nil { + return err + } + // Manually insert the header into the database, but don't reorganize (allows subsequent testing) + LightChain.mu.Lock() + core.WriteTd(LightChain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, LightChain.GetTdByHash(header.ParentHash))) + core.WriteHeader(LightChain.chainDb, header) + LightChain.mu.Unlock() + } + return nil +} + +// Tests that given a starting canonical chain of a given size, it can be extended +// with various length chains. +func TestExtendCanonicalHeaders(t *testing.T) { + length := 5 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) + } + } + // Start fork from current height + testFork(t, processor, length, 1, better) + testFork(t, processor, length, 2, better) + testFork(t, processor, length, 5, better) + testFork(t, processor, length, 10, better) +} + +// Tests that given a starting canonical chain of a given size, creating shorter +// forks do not take canonical ownership. +func TestShorterForkHeaders(t *testing.T) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + worse := func(td1, td2 *big.Int) { + if td2.Cmp(td1) >= 0 { + t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) + } + } + // Sum of numbers must be less than `length` for this to be a shorter fork + testFork(t, processor, 0, 3, worse) + testFork(t, processor, 0, 7, worse) + testFork(t, processor, 1, 1, worse) + testFork(t, processor, 1, 7, worse) + testFork(t, processor, 5, 3, worse) + testFork(t, processor, 5, 4, worse) +} + +// Tests that given a starting canonical chain of a given size, creating longer +// forks do take canonical ownership. +func TestLongerForkHeaders(t *testing.T) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { + if td2.Cmp(td1) <= 0 { + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) + } + } + // Sum of numbers must be greater than `length` for this to be a longer fork + testFork(t, processor, 0, 11, better) + testFork(t, processor, 0, 15, better) + testFork(t, processor, 1, 10, better) + testFork(t, processor, 1, 12, better) + testFork(t, processor, 5, 6, better) + testFork(t, processor, 5, 8, better) +} + +// Tests that given a starting canonical chain of a given size, creating equal +// forks do take canonical ownership. +func TestEqualForkHeaders(t *testing.T) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Define the difficulty comparator + equal := func(td1, td2 *big.Int) { + if td2.Cmp(td1) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) + } + } + // Sum of numbers must be equal to `length` for this to be an equal fork + testFork(t, processor, 0, 10, equal) + testFork(t, processor, 1, 9, equal) + testFork(t, processor, 2, 8, equal) + testFork(t, processor, 5, 5, equal) + testFork(t, processor, 6, 4, equal) + testFork(t, processor, 9, 1, equal) +} + +// Tests that chains missing links do not get accepted by the processor. +func TestBrokenHeaderChain(t *testing.T) { + // Make chain starting from genesis + db, LightChain, err := newCanonical(10) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + // Create a forked chain, and try to insert with a missing link + chain := makeHeaderChain(LightChain.CurrentHeader(), 5, db, forkSeed)[1:] + if err := testHeaderChainImport(chain, LightChain); err == nil { + t.Errorf("broken header chain not reported") + } +} + +type bproc struct{} + +func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } + +func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { + var chain []*types.Header + for i, difficulty := range d { + header := &types.Header{ + Coinbase: common.Address{seed}, + Number: big.NewInt(int64(i + 1)), + Difficulty: big.NewInt(int64(difficulty)), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + } + if i == 0 { + header.ParentHash = genesis.Hash() + } else { + header.ParentHash = chain[i-1].Hash() + } + chain = append(chain, types.CopyHeader(header)) + } + return chain +} + +type dummyOdr struct { + OdrBackend + db ethdb.Database +} + +func (odr *dummyOdr) Database() ethdb.Database { + return odr.db +} + +func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { + return nil +} + +func chm(genesis *types.Block, db ethdb.Database) *LightChain { + odr := &dummyOdr{db: db} + var eventMux event.TypeMux + bc := &LightChain{odr: odr, chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: core.FakePow{}} + bc.hc, _ = core.NewHeaderChain(db, testChainConfig(), bc.Validator, bc.getProcInterrupt) + bc.bodyCache, _ = lru.New(100) + bc.bodyRLPCache, _ = lru.New(100) + bc.blockCache, _ = lru.New(100) + bc.SetValidator(bproc{}) + bc.ResetWithGenesisBlock(genesis) + + return bc +} + +// Tests that reorganizing a long difficult chain after a short easy one +// overwrites the canonical numbers and links in the database. +func TestReorgLongHeaders(t *testing.T) { + testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10) +} + +// Tests that reorganizing a short difficult chain after a long easy one +// overwrites the canonical numbers and links in the database. +func TestReorgShortHeaders(t *testing.T) { + testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11) +} + +func testReorg(t *testing.T, first, second []int, td int64) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := core.WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Insert an easy and a difficult chain afterwards + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1) + // Check that the chain is valid number and link wise + prev := bc.CurrentHeader() + for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { + if prev.ParentHash != header.Hash() { + t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) + } + } + // Make sure the chain total difficulty is the correct one + want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td)) + if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", have, want) + } +} + +// Tests that the insertion functions detect banned hashes. +func TestBadHeaderHashes(t *testing.T) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := core.WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Create a chain, ban a hash and try to import + var err error + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10) + core.BadHashes[headers[2].Hash()] = true + _, err = bc.InsertHeaderChain(headers, 1) + if !core.IsBadHashError(err) { + t.Errorf("error mismatch: want: BadHashError, have: %v", err) + } +} + +// Tests that bad hashes are detected on boot, and the chan rolled back to a +// good state prior to the bad hash. +func TestReorgBadHeaderHashes(t *testing.T) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := core.WriteTestNetGenesisBlock(db) + bc := chm(genesis, db) + + // Create a chain, import and ban aferwards + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + + if _, err := bc.InsertHeaderChain(headers, 1); err != nil { + t.Fatalf("failed to import headers: %v", err) + } + if bc.CurrentHeader().Hash() != headers[3].Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) + } + core.BadHashes[headers[3].Hash()] = true + defer func() { delete(core.BadHashes, headers[3].Hash()) }() + // Create a new chain manager and check it rolled back the state + ncm, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), core.FakePow{}, new(event.TypeMux)) + if err != nil { + t.Fatalf("failed to create new chain manager: %v", err) + } + if ncm.CurrentHeader().Hash() != headers[2].Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr.go b/vendor/github.com/ethereum/go-ethereum/light/odr.go new file mode 100644 index 0000000..4f6ef6b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/odr.go @@ -0,0 +1,159 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package light implements on-demand retrieval capable state and chain objects +// for the Ethereum Light Client. +package light + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/net/context" +) + +// NoOdr is the default context passed to an ODR capable function when the ODR +// service is not required. +var NoOdr = context.Background() + +// OdrBackend is an interface to a backend service that handles ODR retrievals +type OdrBackend interface { + Database() ethdb.Database + Retrieve(ctx context.Context, req OdrRequest) error +} + +// OdrRequest is an interface for retrieval requests +type OdrRequest interface { + StoreResult(db ethdb.Database) +} + +// TrieID identifies a state or account storage trie +type TrieID struct { + BlockHash, Root common.Hash + BlockNumber uint64 + AccKey []byte +} + +// StateTrieID returns a TrieID for a state trie belonging to a certain block +// header. +func StateTrieID(header *types.Header) *TrieID { + return &TrieID{ + BlockHash: header.Hash(), + BlockNumber: header.Number.Uint64(), + AccKey: nil, + Root: header.Root, + } +} + +// StorageTrieID returns a TrieID for a contract storage trie at a given account +// of a given state trie. It also requires the root hash of the trie for +// checking Merkle proofs. +func StorageTrieID(state *TrieID, addr common.Address, root common.Hash) *TrieID { + return &TrieID{ + BlockHash: state.BlockHash, + BlockNumber: state.BlockNumber, + AccKey: crypto.Keccak256(addr[:]), + Root: root, + } +} + +// TrieRequest is the ODR request type for state/storage trie entries +type TrieRequest struct { + OdrRequest + Id *TrieID + Key []byte + Proof []rlp.RawValue +} + +// StoreResult stores the retrieved data in local database +func (req *TrieRequest) StoreResult(db ethdb.Database) { + storeProof(db, req.Proof) +} + +// storeProof stores the new trie nodes obtained from a merkle proof in the database +func storeProof(db ethdb.Database, proof []rlp.RawValue) { + for _, buf := range proof { + hash := crypto.Keccak256(buf) + val, _ := db.Get(hash) + if val == nil { + db.Put(hash, buf) + } + } +} + +// CodeRequest is the ODR request type for retrieving contract code +type CodeRequest struct { + OdrRequest + Id *TrieID + Hash common.Hash + Data []byte +} + +// StoreResult stores the retrieved data in local database +func (req *CodeRequest) StoreResult(db ethdb.Database) { + db.Put(req.Hash[:], req.Data) +} + +// BlockRequest is the ODR request type for retrieving block bodies +type BlockRequest struct { + OdrRequest + Hash common.Hash + Number uint64 + Rlp []byte +} + +// StoreResult stores the retrieved data in local database +func (req *BlockRequest) StoreResult(db ethdb.Database) { + core.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp) +} + +// ReceiptsRequest is the ODR request type for retrieving block bodies +type ReceiptsRequest struct { + OdrRequest + Hash common.Hash + Number uint64 + Receipts types.Receipts +} + +// StoreResult stores the retrieved data in local database +func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { + core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts) +} + +// TrieRequest is the ODR request type for state/storage trie entries +type ChtRequest struct { + OdrRequest + ChtNum, BlockNum uint64 + ChtRoot common.Hash + Header *types.Header + Td *big.Int + Proof []rlp.RawValue +} + +// StoreResult stores the retrieved data in local database +func (req *ChtRequest) StoreResult(db ethdb.Database) { + // if there is a canonical hash, there is a header too + core.WriteHeader(db, req.Header) + hash, num := req.Header.Hash(), req.Header.Number.Uint64() + core.WriteTd(db, hash, num, req.Td) + core.WriteCanonicalHash(db, hash, num) + //storeProof(db, req.Proof) +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr_test.go b/vendor/github.com/ethereum/go-ethereum/light/odr_test.go new file mode 100644 index 0000000..a76050a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/odr_test.go @@ -0,0 +1,300 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "bytes" + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "golang.org/x/net/context" +) + +var ( + testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(100000000) + + acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) + + testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") + testContractAddr common.Address + + bigTxGas = new(big.Int).SetUint64(params.TxGas) +) + +type testOdr struct { + OdrBackend + sdb, ldb ethdb.Database + disable bool +} + +func (odr *testOdr) Database() ethdb.Database { + return odr.ldb +} + +var ErrOdrDisabled = errors.New("ODR disabled") + +func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error { + if odr.disable { + return ErrOdrDisabled + } + switch req := req.(type) { + case *BlockRequest: + req.Rlp = core.GetBodyRLP(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash)) + case *ReceiptsRequest: + req.Receipts = core.GetBlockReceipts(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash)) + case *TrieRequest: + t, _ := trie.New(req.Id.Root, odr.sdb) + req.Proof = t.Prove(req.Key) + case *CodeRequest: + req.Data, _ = odr.sdb.Get(req.Hash[:]) + } + req.StoreResult(odr.ldb) + return nil +} + +type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte + +func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, 1, odrGetBlock) } + +func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte { + var block *types.Block + if bc != nil { + block = bc.GetBlockByHash(bhash) + } else { + block, _ = lc.GetBlockByHash(ctx, bhash) + } + if block == nil { + return nil + } + rlp, _ := rlp.EncodeToBytes(block) + return rlp +} + +func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, 1, odrGetReceipts) } + +func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte { + var receipts types.Receipts + if bc != nil { + receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash)) + } else { + receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash)) + } + if receipts == nil { + return nil + } + rlp, _ := rlp.EncodeToBytes(receipts) + return rlp +} + +func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, 1, odrAccounts) } + +func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte { + dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") + acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} + + var res []byte + for _, addr := range acc { + if bc != nil { + header := bc.GetHeaderByHash(bhash) + st, err := state.New(header.Root, db) + if err == nil { + bal := st.GetBalance(addr) + rlp, _ := rlp.EncodeToBytes(bal) + res = append(res, rlp...) + } + } else { + header := lc.GetHeaderByHash(bhash) + st := NewLightState(StateTrieID(header), lc.Odr()) + bal, err := st.GetBalance(ctx, addr) + if err == nil { + rlp, _ := rlp.EncodeToBytes(bal) + res = append(res, rlp...) + } + } + } + + return res +} + +func TestOdrContractCallLes1(t *testing.T) { testChainOdr(t, 1, 2, odrContractCall) } + +type callmsg struct { + types.Message +} + +func (callmsg) CheckNonce() bool { return false } + +func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte { + data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") + + config := params.TestChainConfig + + var res []byte + for i := 0; i < 3; i++ { + data[35] = byte(i) + if bc != nil { + header := bc.GetHeaderByHash(bhash) + statedb, err := state.New(header.Root, db) + if err == nil { + from := statedb.GetOrNewStateObject(testBankAddress) + from.SetBalance(common.MaxBig) + + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} + + context := core.NewEVMContext(msg, header, bc) + vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) + + gp := new(core.GasPool).AddGas(common.MaxBig) + ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + res = append(res, ret...) + } + } else { + header := lc.GetHeaderByHash(bhash) + state := NewLightState(StateTrieID(header), lc.Odr()) + vmstate := NewVMState(ctx, state) + from, err := state.GetOrNewStateObject(ctx, testBankAddress) + if err == nil { + from.SetBalance(common.MaxBig) + + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} + context := core.NewEVMContext(msg, header, lc) + vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) + gp := new(core.GasPool).AddGas(common.MaxBig) + ret, _, _ := core.ApplyMessage(vmenv, msg, gp) + if vmstate.Error() == nil { + res = append(res, ret...) + } + } + } + } + return res +} + +func testChainGen(i int, block *core.BlockGen) { + signer := types.HomesteadSigner{} + switch i { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // acc1Addr passes it on to account #2. + // acc1Addr creates a test contract. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + nonce := block.TxNonce(acc1Addr) + tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) + nonce++ + tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(1000000), big.NewInt(0), testContractCode), signer, acc1Key) + testContractAddr = crypto.CreateAddress(acc1Addr, nonce) + block.AddTx(tx1) + block.AddTx(tx2) + block.AddTx(tx3) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data), signer, testBankKey) + block.AddTx(tx) + case 3: + // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). + b2 := block.PrevBlock(1).Header() + b2.Extra = []byte("foo") + block.AddUncle(b2) + b3 := block.PrevBlock(2).Header() + b3.Extra = []byte("foo") + block.AddUncle(b3) + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data), signer, testBankKey) + block.AddTx(tx) + } +} + +func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { + var ( + evmux = new(event.TypeMux) + pow = new(core.FakePow) + sdb, _ = ethdb.NewMemDatabase() + ldb, _ = ethdb.NewMemDatabase() + genesis = core.WriteGenesisBlockForTesting(sdb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + ) + core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + // Assemble the test environment + blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux, vm.Config{}) + chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} + gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, 4, testChainGen) + if _, err := blockchain.InsertChain(gchain); err != nil { + panic(err) + } + + odr := &testOdr{sdb: sdb, ldb: ldb} + lightchain, _ := NewLightChain(odr, testChainConfig(), pow, evmux) + lightchain.SetValidator(bproc{}) + headers := make([]*types.Header, len(gchain)) + for i, block := range gchain { + headers[i] = block.Header() + } + if _, err := lightchain.InsertHeaderChain(headers, 1); err != nil { + panic(err) + } + + test := func(expFail uint64) { + for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ { + bhash := core.GetCanonicalHash(sdb, i) + b1 := fn(NoOdr, sdb, blockchain, nil, bhash) + ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + b2 := fn(ctx, ldb, nil, lightchain, bhash) + eq := bytes.Equal(b1, b2) + exp := i < expFail + if exp && !eq { + t.Errorf("odr mismatch") + } + if !exp && eq { + t.Errorf("unexpected odr match") + } + } + } + + odr.disable = true + // expect retrievals to fail (except genesis block) without a les peer + test(expFail) + odr.disable = false + // expect all retrievals to pass + test(5) + odr.disable = true + // still expect all retrievals to pass, now data should be cached locally + test(5) +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go new file mode 100644 index 0000000..7617116 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go @@ -0,0 +1,187 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/net/context" +) + +var sha3_nil = crypto.Keccak256Hash(nil) + +var ( + ErrNoTrustedCht = errors.New("No trusted canonical hash trie") + ErrNoHeader = errors.New("Header not found") + + ChtFrequency = uint64(4096) + ChtConfirmations = uint64(2048) + trustedChtKey = []byte("TrustedCHT") +) + +type ChtNode struct { + Hash common.Hash + Td *big.Int +} + +type TrustedCht struct { + Number uint64 + Root common.Hash +} + +func GetTrustedCht(db ethdb.Database) TrustedCht { + data, _ := db.Get(trustedChtKey) + var res TrustedCht + if err := rlp.DecodeBytes(data, &res); err != nil { + return TrustedCht{0, common.Hash{}} + } + return res +} + +func WriteTrustedCht(db ethdb.Database, cht TrustedCht) { + data, _ := rlp.EncodeToBytes(cht) + db.Put(trustedChtKey, data) +} + +func DeleteTrustedCht(db ethdb.Database) { + db.Delete(trustedChtKey) +} + +func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) { + db := odr.Database() + hash := core.GetCanonicalHash(db, number) + if (hash != common.Hash{}) { + // if there is a canonical hash, there is a header too + header := core.GetHeader(db, hash, number) + if header == nil { + panic("Canonical hash present but header not found") + } + return header, nil + } + + cht := GetTrustedCht(db) + if number >= cht.Number*ChtFrequency { + return nil, ErrNoTrustedCht + } + + r := &ChtRequest{ChtRoot: cht.Root, ChtNum: cht.Number, BlockNum: number} + if err := odr.Retrieve(ctx, r); err != nil { + return nil, err + } else { + return r.Header, nil + } +} + +func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) { + hash := core.GetCanonicalHash(odr.Database(), number) + if (hash != common.Hash{}) { + return hash, nil + } + header, err := GetHeaderByNumber(ctx, odr, number) + if header != nil { + return header.Hash(), nil + } + return common.Hash{}, err +} + +// retrieveContractCode tries to retrieve the contract code of the given account +// with the given hash from the network (id points to the storage trie belonging +// to the same account) +func retrieveContractCode(ctx context.Context, odr OdrBackend, id *TrieID, hash common.Hash) ([]byte, error) { + if hash == sha3_nil { + return nil, nil + } + res, _ := odr.Database().Get(hash[:]) + if res != nil { + return res, nil + } + r := &CodeRequest{Id: id, Hash: hash} + if err := odr.Retrieve(ctx, r); err != nil { + return nil, err + } else { + return r.Data, nil + } +} + +// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. +func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) { + if data := core.GetBodyRLP(odr.Database(), hash, number); data != nil { + return data, nil + } + r := &BlockRequest{Hash: hash, Number: number} + if err := odr.Retrieve(ctx, r); err != nil { + return nil, err + } else { + return r.Rlp, nil + } +} + +// GetBody retrieves the block body (transactons, uncles) corresponding to the +// hash. +func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) { + data, err := GetBodyRLP(ctx, odr, hash, number) + if err != nil { + return nil, err + } + body := new(types.Body) + if err := rlp.Decode(bytes.NewReader(data), body); err != nil { + glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err) + return nil, err + } + return body, nil +} + +// GetBlock retrieves an entire block corresponding to the hash, assembling it +// back from the stored header and body. +func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) { + // Retrieve the block header and body contents + header := core.GetHeader(odr.Database(), hash, number) + if header == nil { + return nil, ErrNoHeader + } + body, err := GetBody(ctx, odr, hash, number) + if err != nil { + return nil, err + } + // Reassemble the block and return + return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil +} + +// GetBlockReceipts retrieves the receipts generated by the transactions included +// in a block given by its hash. +func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) { + receipts := core.GetBlockReceipts(odr.Database(), hash, number) + if receipts != nil { + return receipts, nil + } + r := &ReceiptsRequest{Hash: hash, Number: number} + if err := odr.Retrieve(ctx, r); err != nil { + return nil, err + } else { + return r.Receipts, nil + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/state.go b/vendor/github.com/ethereum/go-ethereum/light/state.go new file mode 100644 index 0000000..f19748e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/state.go @@ -0,0 +1,322 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +// LightState is a memory representation of a state. +// This version is ODR capable, caching only the already accessed part of the +// state, retrieving unknown parts on-demand from the ODR backend. Changes are +// never stored in the local database, only in the memory objects. +type LightState struct { + odr OdrBackend + trie *LightTrie + id *TrieID + stateObjects map[string]*StateObject + refund *big.Int +} + +// NewLightState creates a new LightState with the specified root. +// Note that the creation of a light state is always successful, even if the +// root is non-existent. In that case, ODR retrieval will always be unsuccessful +// and every operation will return with an error or wait for the context to be +// cancelled. +func NewLightState(id *TrieID, odr OdrBackend) *LightState { + var tr *LightTrie + if id != nil { + tr = NewLightTrie(id, odr, true) + } + return &LightState{ + odr: odr, + trie: tr, + id: id, + stateObjects: make(map[string]*StateObject), + refund: new(big.Int), + } +} + +// AddRefund adds an amount to the refund value collected during a vm execution +func (self *LightState) AddRefund(gas *big.Int) { + self.refund.Add(self.refund, gas) +} + +// HasAccount returns true if an account exists at the given address +func (self *LightState) HasAccount(ctx context.Context, addr common.Address) (bool, error) { + so, err := self.GetStateObject(ctx, addr) + return so != nil, err +} + +// GetBalance retrieves the balance from the given address or 0 if the account does +// not exist +func (self *LightState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return common.Big0, err + } + if stateObject != nil { + return stateObject.balance, nil + } + + return common.Big0, nil +} + +// GetNonce returns the nonce at the given address or 0 if the account does +// not exist +func (self *LightState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return 0, err + } + if stateObject != nil { + return stateObject.nonce, nil + } + return 0, nil +} + +// GetCode returns the contract code at the given address or nil if the account +// does not exist +func (self *LightState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return nil, err + } + if stateObject != nil { + return stateObject.code, nil + } + return nil, nil +} + +// GetState returns the contract storage value at storage address b from the +// contract address a or common.Hash{} if the account does not exist +func (self *LightState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { + stateObject, err := self.GetStateObject(ctx, a) + if err == nil && stateObject != nil { + return stateObject.GetState(ctx, b) + } + return common.Hash{}, err +} + +// HasSuicided returns true if the given account has been marked for deletion +// or false if the account does not exist +func (self *LightState) HasSuicided(ctx context.Context, addr common.Address) (bool, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err == nil && stateObject != nil { + return stateObject.remove, nil + } + return false, err +} + +/* + * SETTERS + */ + +// AddBalance adds the given amount to the balance of the specified account +func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amount *big.Int) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.AddBalance(amount) + } + return err +} + +// SubBalance adds the given amount to the balance of the specified account +func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SubBalance(amount) + } + return err +} + +// SetNonce sets the nonce of the specified account +func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SetNonce(nonce) + } + return err +} + +// SetCode sets the contract code at the specified account +func (self *LightState) SetCode(ctx context.Context, addr common.Address, code []byte) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SetCode(crypto.Keccak256Hash(code), code) + } + return err +} + +// SetState sets the storage value at storage address key of the account addr +func (self *LightState) SetState(ctx context.Context, addr common.Address, key common.Hash, value common.Hash) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SetState(key, value) + } + return err +} + +// Delete marks an account to be removed and clears its balance +func (self *LightState) Suicide(ctx context.Context, addr common.Address) (bool, error) { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.MarkForDeletion() + stateObject.balance = new(big.Int) + + return true, nil + } + + return false, err +} + +// +// Get, set, new state object methods +// + +// GetStateObject returns the state object of the given account or nil if the +// account does not exist +func (self *LightState) GetStateObject(ctx context.Context, addr common.Address) (stateObject *StateObject, err error) { + stateObject = self.stateObjects[addr.Str()] + if stateObject != nil { + if stateObject.deleted { + stateObject = nil + } + return stateObject, nil + } + data, err := self.trie.Get(ctx, addr[:]) + if err != nil { + return nil, err + } + if len(data) == 0 { + return nil, nil + } + + stateObject, err = DecodeObject(ctx, self.id, addr, self.odr, []byte(data)) + if err != nil { + return nil, err + } + + self.SetStateObject(stateObject) + + return stateObject, nil +} + +// SetStateObject sets the state object of the given account +func (self *LightState) SetStateObject(object *StateObject) { + self.stateObjects[object.Address().Str()] = object +} + +// GetOrNewStateObject returns the state object of the given account or creates a +// new one if the account does not exist +func (self *LightState) GetOrNewStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err == nil && (stateObject == nil || stateObject.deleted) { + stateObject, err = self.CreateStateObject(ctx, addr) + } + return stateObject, err +} + +// newStateObject creates a state object whether it exists in the state or not +func (self *LightState) newStateObject(addr common.Address) *StateObject { + if glog.V(logger.Debug) { + glog.Infof("(+) %x\n", addr) + } + + stateObject := NewStateObject(addr, self.odr) + self.stateObjects[addr.Str()] = stateObject + + return stateObject +} + +// CreateStateObject creates creates a new state object and takes ownership. +// This is different from "NewStateObject" +func (self *LightState) CreateStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { + // Get previous (if any) + so, err := self.GetStateObject(ctx, addr) + if err != nil { + return nil, err + } + // Create a new one + newSo := self.newStateObject(addr) + + // If it existed set the balance to the new account + if so != nil { + newSo.balance = so.balance + } + + return newSo, nil +} + +// ForEachStorage calls a callback function for every key/value pair found +// in the local storage cache. Note that unlike core/state.StateObject, +// light.StateObject only returns cached values and doesn't download the +// entire storage tree. +func (self *LightState) ForEachStorage(ctx context.Context, addr common.Address, cb func(key, value common.Hash) bool) error { + so, err := self.GetStateObject(ctx, addr) + if err != nil { + return err + } + + if so == nil { + return nil + } + + for h, v := range so.storage { + cb(h, v) + } + return nil +} + +// +// Setting, copying of the state methods +// + +// Copy creates a copy of the state +func (self *LightState) Copy() *LightState { + // ignore error - we assume state-to-be-copied always exists + state := NewLightState(nil, self.odr) + state.trie = self.trie + state.id = self.id + for k, stateObject := range self.stateObjects { + if stateObject.dirty { + state.stateObjects[k] = stateObject.Copy() + } + } + + state.refund.Set(self.refund) + return state +} + +// Set copies the contents of the given state onto this state, overwriting +// its contents +func (self *LightState) Set(state *LightState) { + self.trie = state.trie + self.stateObjects = state.stateObjects + self.refund = state.refund +} + +// GetRefund returns the refund value collected during a vm execution +func (self *LightState) GetRefund() *big.Int { + return self.refund +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/state_object.go b/vendor/github.com/ethereum/go-ethereum/light/state_object.go new file mode 100644 index 0000000..e876c15 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/state_object.go @@ -0,0 +1,289 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/net/context" +) + +var emptyCodeHash = crypto.Keccak256(nil) + +// Code represents a contract code in binary form +type Code []byte + +// String returns a string representation of the code +func (self Code) String() string { + return string(self) //strings.Join(Disassemble(self), " ") +} + +// Storage is a memory map cache of a contract storage +type Storage map[common.Hash]common.Hash + +// String returns a string representation of the storage cache +func (self Storage) String() (str string) { + for key, value := range self { + str += fmt.Sprintf("%X : %X\n", key, value) + } + + return +} + +// Copy copies the contents of a storage cache +func (self Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range self { + cpy[key] = value + } + + return cpy +} + +// StateObject is a memory representation of an account or contract and its storage. +// This version is ODR capable, caching only the already accessed part of the +// storage, retrieving unknown parts on-demand from the ODR backend. Changes are +// never stored in the local database, only in the memory objects. +type StateObject struct { + odr OdrBackend + trie *LightTrie + + // Address belonging to this account + address common.Address + // The balance of the account + balance *big.Int + // The nonce of the account + nonce uint64 + // The code hash if code is present (i.e. a contract) + codeHash []byte + // The code for this account + code Code + // Cached storage (flushed when updated) + storage Storage + + // Mark for deletion + // When an object is marked for deletion it will be delete from the trie + // during the "update" phase of the state transition + remove bool + deleted bool + dirty bool +} + +// NewStateObject creates a new StateObject of the specified account address +func NewStateObject(address common.Address, odr OdrBackend) *StateObject { + object := &StateObject{ + odr: odr, + address: address, + balance: new(big.Int), + dirty: true, + codeHash: emptyCodeHash, + storage: make(Storage), + } + object.trie = NewLightTrie(&TrieID{}, odr, true) + return object +} + +// MarkForDeletion marks an account to be removed +func (self *StateObject) MarkForDeletion() { + self.remove = true + self.dirty = true + + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) + } +} + +// getAddr gets the storage value at the given address from the trie +func (c *StateObject) getAddr(ctx context.Context, addr common.Hash) (common.Hash, error) { + var ret []byte + val, err := c.trie.Get(ctx, addr[:]) + if err != nil { + return common.Hash{}, err + } + rlp.DecodeBytes(val, &ret) + return common.BytesToHash(ret), nil +} + +// Storage returns the storage cache object of the account +func (self *StateObject) Storage() Storage { + return self.storage +} + +// GetState returns the storage value at the given address from either the cache +// or the trie +func (self *StateObject) GetState(ctx context.Context, key common.Hash) (common.Hash, error) { + value, exists := self.storage[key] + if !exists { + var err error + value, err = self.getAddr(ctx, key) + if err != nil { + return common.Hash{}, err + } + if (value != common.Hash{}) { + self.storage[key] = value + } + } + + return value, nil +} + +// SetState sets the storage value at the given address +func (self *StateObject) SetState(k, value common.Hash) { + self.storage[k] = value + self.dirty = true +} + +// AddBalance adds the given amount to the account balance +func (c *StateObject) AddBalance(amount *big.Int) { + c.SetBalance(new(big.Int).Add(c.balance, amount)) + + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) + } +} + +// SubBalance subtracts the given amount from the account balance +func (c *StateObject) SubBalance(amount *big.Int) { + c.SetBalance(new(big.Int).Sub(c.balance, amount)) + + if glog.V(logger.Debug) { + glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) + } +} + +// SetBalance sets the account balance to the given amount +func (c *StateObject) SetBalance(amount *big.Int) { + c.balance = amount + c.dirty = true +} + +// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures +func (c *StateObject) ReturnGas(gas *big.Int) {} + +// Copy creates a copy of the state object +func (self *StateObject) Copy() *StateObject { + stateObject := NewStateObject(self.Address(), self.odr) + stateObject.balance.Set(self.balance) + stateObject.codeHash = common.CopyBytes(self.codeHash) + stateObject.nonce = self.nonce + stateObject.trie = self.trie + stateObject.code = self.code + stateObject.storage = self.storage.Copy() + stateObject.remove = self.remove + stateObject.dirty = self.dirty + stateObject.deleted = self.deleted + + return stateObject +} + +// +// Attribute accessors +// + +// empty returns whether the account is considered empty. +func (self *StateObject) empty() bool { + return self.nonce == 0 && self.balance.BitLen() == 0 && bytes.Equal(self.codeHash, emptyCodeHash) +} + +// Balance returns the account balance +func (self *StateObject) Balance() *big.Int { + return self.balance +} + +// Address returns the address of the contract/account +func (self *StateObject) Address() common.Address { + return self.address +} + +// Code returns the contract code +func (self *StateObject) Code() []byte { + return self.code +} + +// SetCode sets the contract code +func (self *StateObject) SetCode(hash common.Hash, code []byte) { + self.code = code + self.codeHash = hash[:] + self.dirty = true +} + +// SetNonce sets the account nonce +func (self *StateObject) SetNonce(nonce uint64) { + self.nonce = nonce + self.dirty = true +} + +// Nonce returns the account nonce +func (self *StateObject) Nonce() uint64 { + return self.nonce +} + +// ForEachStorage calls a callback function for every key/value pair found +// in the local storage cache. Note that unlike core/state.StateObject, +// light.StateObject only returns cached values and doesn't download the +// entire storage tree. +func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { + for h, v := range self.storage { + cb(h, v) + } +} + +// Never called, but must be present to allow StateObject to be used +// as a vm.Account interface that also satisfies the vm.ContractRef +// interface. Interfaces are awesome. +func (self *StateObject) Value() *big.Int { + panic("Value on StateObject should never be called") +} + +// Encoding + +type extStateObject struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte +} + +// DecodeObject decodes an RLP-encoded state object. +func DecodeObject(ctx context.Context, stateID *TrieID, address common.Address, odr OdrBackend, data []byte) (*StateObject, error) { + var ( + obj = &StateObject{address: address, odr: odr, storage: make(Storage)} + ext extStateObject + err error + ) + if err = rlp.DecodeBytes(data, &ext); err != nil { + return nil, err + } + trieID := StorageTrieID(stateID, address, ext.Root) + obj.trie = NewLightTrie(trieID, odr, true) + if !bytes.Equal(ext.CodeHash, emptyCodeHash) { + if obj.code, err = retrieveContractCode(ctx, obj.odr, trieID, common.BytesToHash(ext.CodeHash)); err != nil { + return nil, fmt.Errorf("can't find code for hash %x: %v", ext.CodeHash, err) + } + } + obj.nonce = ext.Nonce + obj.balance = ext.Balance + obj.codeHash = ext.CodeHash + return obj, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/state_test.go b/vendor/github.com/ethereum/go-ethereum/light/state_test.go new file mode 100644 index 0000000..d594ab9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/state_test.go @@ -0,0 +1,248 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "golang.org/x/net/context" +) + +func makeTestState() (common.Hash, ethdb.Database) { + sdb, _ := ethdb.NewMemDatabase() + st, _ := state.New(common.Hash{}, sdb) + for i := byte(0); i < 100; i++ { + addr := common.Address{i} + for j := byte(0); j < 100; j++ { + st.SetState(addr, common.Hash{j}, common.Hash{i, j}) + } + st.SetNonce(addr, 100) + st.AddBalance(addr, big.NewInt(int64(i))) + st.SetCode(addr, []byte{i, i, i}) + } + root, _ := st.Commit(false) + return root, sdb +} + +func TestLightStateOdr(t *testing.T) { + root, sdb := makeTestState() + header := &types.Header{Root: root, Number: big.NewInt(0)} + core.WriteHeader(sdb, header) + ldb, _ := ethdb.NewMemDatabase() + odr := &testOdr{sdb: sdb, ldb: ldb} + ls := NewLightState(StateTrieID(header), odr) + ctx := context.Background() + + for i := byte(0); i < 100; i++ { + addr := common.Address{i} + err := ls.AddBalance(ctx, addr, big.NewInt(1000)) + if err != nil { + t.Fatalf("Error adding balance to acc[%d]: %v", i, err) + } + err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100}) + if err != nil { + t.Fatalf("Error setting storage of acc[%d]: %v", i, err) + } + } + + addr := common.Address{100} + _, err := ls.CreateStateObject(ctx, addr) + if err != nil { + t.Fatalf("Error creating state object: %v", err) + } + err = ls.SetCode(ctx, addr, []byte{100, 100, 100}) + if err != nil { + t.Fatalf("Error setting code: %v", err) + } + err = ls.AddBalance(ctx, addr, big.NewInt(1100)) + if err != nil { + t.Fatalf("Error adding balance to acc[100]: %v", err) + } + for j := byte(0); j < 101; j++ { + err = ls.SetState(ctx, addr, common.Hash{j}, common.Hash{100, j}) + if err != nil { + t.Fatalf("Error setting storage of acc[100]: %v", err) + } + } + err = ls.SetNonce(ctx, addr, 100) + if err != nil { + t.Fatalf("Error setting nonce for acc[100]: %v", err) + } + + for i := byte(0); i < 101; i++ { + addr := common.Address{i} + + bal, err := ls.GetBalance(ctx, addr) + if err != nil { + t.Fatalf("Error getting balance of acc[%d]: %v", i, err) + } + if bal.Int64() != int64(i)+1000 { + t.Fatalf("Incorrect balance at acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) + } + + nonce, err := ls.GetNonce(ctx, addr) + if err != nil { + t.Fatalf("Error getting nonce of acc[%d]: %v", i, err) + } + if nonce != 100 { + t.Fatalf("Incorrect nonce at acc[%d]: expected %v, got %v", i, 100, nonce) + } + + code, err := ls.GetCode(ctx, addr) + exp := []byte{i, i, i} + if err != nil { + t.Fatalf("Error getting code of acc[%d]: %v", i, err) + } + if !bytes.Equal(code, exp) { + t.Fatalf("Incorrect code at acc[%d]: expected %v, got %v", i, exp, code) + } + + for j := byte(0); j < 101; j++ { + exp := common.Hash{i, j} + val, err := ls.GetState(ctx, addr, common.Hash{j}) + if err != nil { + t.Fatalf("Error retrieving acc[%d].storage[%d]: %v", i, j, err) + } + if val != exp { + t.Fatalf("Retrieved wrong value from acc[%d].storage[%d]: expected %04x, got %04x", i, j, exp, val) + } + } + } +} + +func TestLightStateSetCopy(t *testing.T) { + root, sdb := makeTestState() + header := &types.Header{Root: root, Number: big.NewInt(0)} + core.WriteHeader(sdb, header) + ldb, _ := ethdb.NewMemDatabase() + odr := &testOdr{sdb: sdb, ldb: ldb} + ls := NewLightState(StateTrieID(header), odr) + ctx := context.Background() + + for i := byte(0); i < 100; i++ { + addr := common.Address{i} + err := ls.AddBalance(ctx, addr, big.NewInt(1000)) + if err != nil { + t.Fatalf("Error adding balance to acc[%d]: %v", i, err) + } + err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100}) + if err != nil { + t.Fatalf("Error setting storage of acc[%d]: %v", i, err) + } + } + + ls2 := ls.Copy() + + for i := byte(0); i < 100; i++ { + addr := common.Address{i} + err := ls2.AddBalance(ctx, addr, big.NewInt(1000)) + if err != nil { + t.Fatalf("Error adding balance to acc[%d]: %v", i, err) + } + err = ls2.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 200}) + if err != nil { + t.Fatalf("Error setting storage of acc[%d]: %v", i, err) + } + } + + lsx := ls.Copy() + ls.Set(ls2) + ls2.Set(lsx) + + for i := byte(0); i < 100; i++ { + addr := common.Address{i} + // check balance in ls + bal, err := ls.GetBalance(ctx, addr) + if err != nil { + t.Fatalf("Error getting balance to acc[%d]: %v", i, err) + } + if bal.Int64() != int64(i)+2000 { + t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) + } + // check balance in ls2 + bal, err = ls2.GetBalance(ctx, addr) + if err != nil { + t.Fatalf("Error getting balance to acc[%d]: %v", i, err) + } + if bal.Int64() != int64(i)+1000 { + t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) + } + // check storage in ls + exp := common.Hash{i, 200} + val, err := ls.GetState(ctx, addr, common.Hash{100}) + if err != nil { + t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err) + } + if val != exp { + t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val) + } + // check storage in ls2 + exp = common.Hash{i, 100} + val, err = ls2.GetState(ctx, addr, common.Hash{100}) + if err != nil { + t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err) + } + if val != exp { + t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val) + } + } +} + +func TestLightStateDelete(t *testing.T) { + root, sdb := makeTestState() + header := &types.Header{Root: root, Number: big.NewInt(0)} + core.WriteHeader(sdb, header) + ldb, _ := ethdb.NewMemDatabase() + odr := &testOdr{sdb: sdb, ldb: ldb} + ls := NewLightState(StateTrieID(header), odr) + ctx := context.Background() + + addr := common.Address{42} + + b, err := ls.HasAccount(ctx, addr) + if err != nil { + t.Fatalf("HasAccount error: %v", err) + } + if !b { + t.Fatalf("HasAccount returned false, expected true") + } + + b, err = ls.HasSuicided(ctx, addr) + if err != nil { + t.Fatalf("HasSuicided error: %v", err) + } + if b { + t.Fatalf("HasSuicided returned true, expected false") + } + + ls.Suicide(ctx, addr) + + b, err = ls.HasSuicided(ctx, addr) + if err != nil { + t.Fatalf("HasSuicided error: %v", err) + } + if !b { + t.Fatalf("HasSuicided returned false, expected true") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/trie.go b/vendor/github.com/ethereum/go-ethereum/light/trie.go new file mode 100644 index 0000000..c552535 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/trie.go @@ -0,0 +1,122 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/trie" + "golang.org/x/net/context" +) + +// LightTrie is an ODR-capable wrapper around trie.SecureTrie +type LightTrie struct { + trie *trie.SecureTrie + id *TrieID + odr OdrBackend + db ethdb.Database +} + +// NewLightTrie creates a new LightTrie instance. It doesn't instantly try to +// access the db or network and retrieve the root node, it only initializes its +// encapsulated SecureTrie at the first actual operation. +func NewLightTrie(id *TrieID, odr OdrBackend, useFakeMap bool) *LightTrie { + return &LightTrie{ + // SecureTrie is initialized before first request + id: id, + odr: odr, + db: odr.Database(), + } +} + +// retrieveKey retrieves a single key, returns true and stores nodes in local +// database if successful +func (t *LightTrie) retrieveKey(ctx context.Context, key []byte) bool { + r := &TrieRequest{Id: t.id, Key: key} + return t.odr.Retrieve(ctx, r) == nil +} + +// do tries and retries to execute a function until it returns with no error or +// an error type other than MissingNodeError +func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error) error { + err := fn() + for err != nil { + mn, ok := err.(*trie.MissingNodeError) + if !ok { + return err + } + + var key []byte + if mn.PrefixLen+mn.SuffixLen > 0 { + key = mn.Key + } else { + key = fallbackKey + } + if !t.retrieveKey(ctx, key) { + break + } + err = fn() + } + return err +} + +// Get returns the value for key stored in the trie. +// The value bytes must not be modified by the caller. +func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) { + err = t.do(ctx, key, func() (err error) { + if t.trie == nil { + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) + } + if err == nil { + res, err = t.trie.TryGet(key) + } + return + }) + return +} + +// Update associates key with value in the trie. Subsequent calls to +// Get will return value. If value has length zero, any existing value +// is deleted from the trie and calls to Get will return nil. +// +// The value bytes must not be modified by the caller while they are +// stored in the trie. +func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { + err = t.do(ctx, key, func() (err error) { + if t.trie == nil { + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) + } + if err == nil { + err = t.trie.TryUpdate(key, value) + } + return + }) + return +} + +// Delete removes any existing value for key from the trie. +func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) { + err = t.do(ctx, key, func() (err error) { + if t.trie == nil { + t.trie, err = trie.NewSecure(t.id.Root, t.db, 0) + } + if err == nil { + err = t.trie.TryDelete(key) + } + return + }) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool.go b/vendor/github.com/ethereum/go-ethereum/light/txpool.go new file mode 100644 index 0000000..bcdb612 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool.go @@ -0,0 +1,545 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/net/context" +) + +// txPermanent is the number of mined blocks after a mined transaction is +// considered permanent and no rollback is expected +var txPermanent = uint64(500) + +// TxPool implements the transaction pool for light clients, which keeps track +// of the status of locally created transactions, detecting if they are included +// in a block (mined) or rolled back. There are no queued transactions since we +// always receive all locally signed transactions in the same order as they are +// created. +type TxPool struct { + config *params.ChainConfig + signer types.Signer + quit chan bool + eventMux *event.TypeMux + events *event.TypeMuxSubscription + mu sync.RWMutex + chain *LightChain + odr OdrBackend + chainDb ethdb.Database + relay TxRelayBackend + head common.Hash + nonce map[common.Address]uint64 // "pending" nonce + pending map[common.Hash]*types.Transaction // pending transactions by tx hash + mined map[common.Hash][]*types.Transaction // mined transactions by block hash + clearIdx uint64 // earliest block nr that can contain mined tx info + + homestead bool +} + +// TxRelayBackend provides an interface to the mechanism that forwards transacions +// to the ETH network. The implementations of the functions should be non-blocking. +// +// Send instructs backend to forward new transactions +// NewHead notifies backend about a new head after processed by the tx pool, +// including mined and rolled back transactions since the last event +// Discard notifies backend about transactions that should be discarded either +// because they have been replaced by a re-send or because they have been mined +// long ago and no rollback is expected +type TxRelayBackend interface { + Send(txs types.Transactions) + NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) + Discard(hashes []common.Hash) +} + +// NewTxPool creates a new light transaction pool +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { + pool := &TxPool{ + config: config, + signer: types.HomesteadSigner{}, + nonce: make(map[common.Address]uint64), + pending: make(map[common.Hash]*types.Transaction), + mined: make(map[common.Hash][]*types.Transaction), + quit: make(chan bool), + eventMux: eventMux, + events: eventMux.Subscribe(core.ChainHeadEvent{}), + chain: chain, + relay: relay, + odr: chain.Odr(), + chainDb: chain.Odr().Database(), + head: chain.CurrentHeader().Hash(), + clearIdx: chain.CurrentHeader().Number.Uint64(), + } + go pool.eventLoop() + + return pool +} + +// currentState returns the light state of the current head header +func (pool *TxPool) currentState() *LightState { + return NewLightState(StateTrieID(pool.chain.CurrentHeader()), pool.odr) +} + +// GetNonce returns the "pending" nonce of a given address. It always queries +// the nonce belonging to the latest header too in order to detect if another +// client using the same key sent a transaction. +func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { + nonce, err := pool.currentState().GetNonce(ctx, addr) + if err != nil { + return 0, err + } + sn, ok := pool.nonce[addr] + if ok && sn > nonce { + nonce = sn + } + if !ok || sn < nonce { + pool.nonce[addr] = nonce + } + return nonce, nil +} + +type txBlockData struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 +} + +// storeTxBlockData stores the block position of a mined tx in the local db +func (pool *TxPool) storeTxBlockData(txh common.Hash, tbd txBlockData) { + //fmt.Println("storeTxBlockData", txh, tbd) + data, _ := rlp.EncodeToBytes(tbd) + pool.chainDb.Put(append(txh[:], byte(1)), data) +} + +// removeTxBlockData removes the stored block position of a rolled back tx +func (pool *TxPool) removeTxBlockData(txh common.Hash) { + //fmt.Println("removeTxBlockData", txh) + pool.chainDb.Delete(append(txh[:], byte(1))) +} + +// txStateChanges stores the recent changes between pending/mined states of +// transactions. True means mined, false means rolled back, no entry means no change +type txStateChanges map[common.Hash]bool + +// setState sets the status of a tx to either recently mined or recently rolled back +func (txc txStateChanges) setState(txHash common.Hash, mined bool) { + val, ent := txc[txHash] + if ent && (val != mined) { + delete(txc, txHash) + } else { + txc[txHash] = mined + } +} + +// getLists creates lists of mined and rolled back tx hashes +func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Hash) { + for hash, val := range txc { + if val { + mined = append(mined, hash) + } else { + rollback = append(rollback, hash) + } + } + return +} + +// checkMinedTxs checks newly added blocks for the currently pending transactions +// and marks them as mined if necessary. It also stores block position in the db +// and adds them to the received txStateChanges map. +func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, idx uint64, txc txStateChanges) error { + //fmt.Println("checkMinedTxs") + if len(pool.pending) == 0 { + return nil + } + //fmt.Println("len(pool) =", len(pool.pending)) + + block, err := GetBlock(ctx, pool.odr, hash, idx) + var receipts types.Receipts + if err != nil { + //fmt.Println(err) + return err + } + //fmt.Println("len(block.Transactions()) =", len(block.Transactions())) + + list := pool.mined[hash] + for i, tx := range block.Transactions() { + txHash := tx.Hash() + //fmt.Println(" txHash:", txHash) + if tx, ok := pool.pending[txHash]; ok { + //fmt.Println("TX FOUND") + if receipts == nil { + receipts, err = GetBlockReceipts(ctx, pool.odr, hash, idx) + if err != nil { + return err + } + if len(receipts) != len(block.Transactions()) { + panic(nil) // should never happen if hashes did match + } + core.SetReceiptsData(pool.config, block, receipts) + } + //fmt.Println("WriteReceipt", receipts[i].TxHash) + core.WriteReceipt(pool.chainDb, receipts[i]) + pool.storeTxBlockData(txHash, txBlockData{hash, idx, uint64(i)}) + delete(pool.pending, txHash) + list = append(list, tx) + txc.setState(txHash, true) + } + } + if list != nil { + pool.mined[hash] = list + } + return nil +} + +// rollbackTxs marks the transactions contained in recently rolled back blocks +// as rolled back. It also removes block position info from the db and adds them +// to the received txStateChanges map. +func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { + if list, ok := pool.mined[hash]; ok { + for _, tx := range list { + txHash := tx.Hash() + pool.removeTxBlockData(txHash) + pool.pending[txHash] = tx + txc.setState(txHash, false) + } + delete(pool.mined, hash) + } +} + +// setNewHead sets a new head header, processing (and rolling back if necessary) +// the blocks since the last known head and returns a txStateChanges map containing +// the recently mined and rolled back transaction hashes. If an error (context +// timeout) occurs during checking new blocks, it leaves the locally known head +// at the latest checked block and still returns a valid txStateChanges, making it +// possible to continue checking the missing blocks at the next chain head event +func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { + txc := make(txStateChanges) + oldh := pool.chain.GetHeaderByHash(pool.head) + newh := newHeader + // find common ancestor, create list of rolled back and new block hashes + var oldHashes, newHashes []common.Hash + for oldh.Hash() != newh.Hash() { + if oldh.Number.Uint64() >= newh.Number.Uint64() { + oldHashes = append(oldHashes, oldh.Hash()) + oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) + } + if oldh.Number.Uint64() < newh.Number.Uint64() { + newHashes = append(newHashes, newh.Hash()) + newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) + if newh == nil { + // happens when CHT syncing, nothing to do + newh = oldh + } + } + } + if oldh.Number.Uint64() < pool.clearIdx { + pool.clearIdx = oldh.Number.Uint64() + } + // roll back old blocks + for _, hash := range oldHashes { + pool.rollbackTxs(hash, txc) + } + pool.head = oldh.Hash() + // check mined txs of new blocks (array is in reversed order) + for i := len(newHashes) - 1; i >= 0; i-- { + hash := newHashes[i] + if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { + return txc, err + } + pool.head = hash + } + + // clear old mined tx entries of old blocks + if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { + idx2 := idx - txPermanent + for i := pool.clearIdx; i < idx2; i++ { + hash := core.GetCanonicalHash(pool.chainDb, i) + if list, ok := pool.mined[hash]; ok { + hashes := make([]common.Hash, len(list)) + for i, tx := range list { + hashes[i] = tx.Hash() + } + pool.relay.Discard(hashes) + delete(pool.mined, hash) + } + } + pool.clearIdx = idx2 + } + + return txc, nil +} + +// blockCheckTimeout is the time limit for checking new blocks for mined +// transactions. Checking resumes at the next chain head event if timed out. +const blockCheckTimeout = time.Second * 3 + +// eventLoop processes chain head events and also notifies the tx relay backend +// about the new head hash and tx state changes +func (pool *TxPool) eventLoop() { + for ev := range pool.events.Chan() { + switch ev.Data.(type) { + case core.ChainHeadEvent: + pool.mu.Lock() + ctx, _ := context.WithTimeout(context.Background(), blockCheckTimeout) + head := pool.chain.CurrentHeader() + txc, _ := pool.setNewHead(ctx, head) + m, r := txc.getLists() + pool.relay.NewHead(pool.head, m, r) + pool.homestead = pool.config.IsHomestead(head.Number) + pool.signer = types.MakeSigner(pool.config, head.Number) + pool.mu.Unlock() + } + } +} + +// Stop stops the light transaction pool +func (pool *TxPool) Stop() { + close(pool.quit) + pool.events.Unsubscribe() + glog.V(logger.Info).Infoln("Transaction pool stopped") +} + +// Stats returns the number of currently pending (locally created) transactions +func (pool *TxPool) Stats() (pending int) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + pending = len(pool.pending) + return +} + +// validateTx checks whether a transaction is valid according to the consensus rules. +func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { + // Validate sender + var ( + from common.Address + err error + ) + + // Validate the transaction sender and it's sig. Throw + // if the from fields is invalid. + if from, err = types.Sender(pool.signer, tx); err != nil { + return core.ErrInvalidSender + } + // Last but not least check for nonce errors + currentState := pool.currentState() + if n, err := currentState.GetNonce(ctx, from); err == nil { + if n > tx.Nonce() { + return core.ErrNonce + } + } else { + return err + } + + // Check the transaction doesn't exceed the current + // block limit gas. + header := pool.chain.GetHeaderByHash(pool.head) + if header.GasLimit.Cmp(tx.Gas()) < 0 { + return core.ErrGasLimit + } + + // Transactions can't be negative. This may never happen + // using RLP decoded transactions but may occur if you create + // a transaction using the RPC for example. + if tx.Value().Cmp(common.Big0) < 0 { + return core.ErrNegativeValue + } + + // Transactor should have enough funds to cover the costs + // cost == V + GP * GL + if b, err := currentState.GetBalance(ctx, from); err == nil { + if b.Cmp(tx.Cost()) < 0 { + return core.ErrInsufficientFunds + } + } else { + return err + } + + // Should supply enough intrinsic gas + if tx.Gas().Cmp(core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)) < 0 { + return core.ErrIntrinsicGas + } + + return nil +} + +// add validates a new transaction and sets its state pending if processable. +// It also updates the locally stored nonce if necessary. +func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { + hash := tx.Hash() + + if self.pending[hash] != nil { + return fmt.Errorf("Known transaction (%x)", hash[:4]) + } + err := self.validateTx(ctx, tx) + if err != nil { + return err + } + + if _, ok := self.pending[hash]; !ok { + self.pending[hash] = tx + + nonce := tx.Nonce() + 1 + + addr, _ := types.Sender(self.signer, tx) + if nonce > self.nonce[addr] { + self.nonce[addr] = nonce + } + + // Notify the subscribers. This event is posted in a goroutine + // because it's possible that somewhere during the post "Remove transaction" + // gets called which will then wait for the global tx pool lock and deadlock. + go self.eventMux.Post(core.TxPreEvent{Tx: tx}) + } + + if glog.V(logger.Debug) { + var toname string + if to := tx.To(); to != nil { + toname = common.Bytes2Hex(to[:4]) + } else { + toname = "[NEW_CONTRACT]" + } + // we can ignore the error here because From is + // verified in ValidateTransaction. + f, _ := types.Sender(self.signer, tx) + from := common.Bytes2Hex(f[:4]) + glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) + } + + return nil +} + +// Add adds a transaction to the pool if valid and passes it to the tx relay +// backend +func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error { + self.mu.Lock() + defer self.mu.Unlock() + + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + + if err := self.add(ctx, tx); err != nil { + return err + } + //fmt.Println("Send", tx.Hash()) + self.relay.Send(types.Transactions{tx}) + + self.chainDb.Put(tx.Hash().Bytes(), data) + return nil +} + +// AddTransactions adds all valid transactions to the pool and passes them to +// the tx relay backend +func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { + self.mu.Lock() + defer self.mu.Unlock() + var sendTx types.Transactions + + for _, tx := range txs { + if err := self.add(ctx, tx); err != nil { + glog.V(logger.Debug).Infoln("tx error:", err) + } else { + sendTx = append(sendTx, tx) + h := tx.Hash() + glog.V(logger.Debug).Infof("tx %x\n", h[:4]) + } + } + + if len(sendTx) > 0 { + self.relay.Send(sendTx) + } +} + +// GetTransaction returns a transaction if it is contained in the pool +// and nil otherwise. +func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { + // check the txs first + if tx, ok := tp.pending[hash]; ok { + return tx + } + return nil +} + +// GetTransactions returns all currently processable transactions. +// The returned slice may be modified by the caller. +func (self *TxPool) GetTransactions() (txs types.Transactions, err error) { + self.mu.RLock() + defer self.mu.RUnlock() + + txs = make(types.Transactions, len(self.pending)) + i := 0 + for _, tx := range self.pending { + txs[i] = tx + i++ + } + return txs, nil +} + +// Content retrieves the data content of the transaction pool, returning all the +// pending as well as queued transactions, grouped by account and nonce. +func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { + self.mu.RLock() + defer self.mu.RUnlock() + + // Retrieve all the pending transactions and sort by account and by nonce + pending := make(map[common.Address]types.Transactions) + for _, tx := range self.pending { + account, _ := types.Sender(self.signer, tx) + pending[account] = append(pending[account], tx) + } + // There are no queued transactions in a light pool, just return an empty map + queued := make(map[common.Address]types.Transactions) + return pending, queued +} + +// RemoveTransactions removes all given transactions from the pool. +func (self *TxPool) RemoveTransactions(txs types.Transactions) { + self.mu.Lock() + defer self.mu.Unlock() + var hashes []common.Hash + for _, tx := range txs { + //self.RemoveTx(tx.Hash()) + hash := tx.Hash() + delete(self.pending, hash) + self.chainDb.Delete(hash[:]) + hashes = append(hashes, hash) + } + self.relay.Discard(hashes) +} + +// RemoveTx removes the transaction with the given hash from the pool. +func (pool *TxPool) RemoveTx(hash common.Hash) { + pool.mu.Lock() + defer pool.mu.Unlock() + // delete from pending pool + delete(pool.pending, hash) + pool.chainDb.Delete(hash[:]) + pool.relay.Discard([]common.Hash{hash}) +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool_test.go b/vendor/github.com/ethereum/go-ethereum/light/txpool_test.go new file mode 100644 index 0000000..af2dcbb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool_test.go @@ -0,0 +1,145 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "math" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "golang.org/x/net/context" +) + +type testTxRelay struct { + send, discard, mined chan int +} + +func (self *testTxRelay) Send(txs types.Transactions) { + self.send <- len(txs) +} + +func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { + m := len(mined) + if m != 0 { + self.mined <- m + } +} + +func (self *testTxRelay) Discard(hashes []common.Hash) { + self.discard <- len(hashes) +} + +const poolTestTxs = 1000 +const poolTestBlocks = 100 + +// test tx 0..n-1 +var testTx [poolTestTxs]*types.Transaction + +// txs sent before block i +func sentTx(i int) int { + return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs) +} + +// txs included in block i or before that (minedTx(i) <= sentTx(i)) +func minedTx(i int) int { + return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs) +} + +func txPoolTestChainGen(i int, block *core.BlockGen) { + s := minedTx(i) + e := minedTx(i + 1) + for i := s; i < e; i++ { + block.AddTx(testTx[i]) + } +} + +func TestTxPool(t *testing.T) { + for i := range testTx { + testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), types.HomesteadSigner{}, testBankKey) + } + + var ( + evmux = new(event.TypeMux) + pow = new(core.FakePow) + sdb, _ = ethdb.NewMemDatabase() + ldb, _ = ethdb.NewMemDatabase() + genesis = core.WriteGenesisBlockForTesting(sdb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + ) + core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + // Assemble the test environment + blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux, vm.Config{}) + chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} + gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, poolTestBlocks, txPoolTestChainGen) + if _, err := blockchain.InsertChain(gchain); err != nil { + panic(err) + } + + odr := &testOdr{sdb: sdb, ldb: ldb} + relay := &testTxRelay{ + send: make(chan int, 1), + discard: make(chan int, 1), + mined: make(chan int, 1), + } + lightchain, _ := NewLightChain(odr, testChainConfig(), pow, evmux) + lightchain.SetValidator(bproc{}) + txPermanent = 50 + pool := NewTxPool(testChainConfig(), evmux, lightchain, relay) + + for ii, block := range gchain { + i := ii + 1 + ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + s := sentTx(i - 1) + e := sentTx(i) + for i := s; i < e; i++ { + pool.Add(ctx, testTx[i]) + got := <-relay.send + exp := 1 + if got != exp { + t.Errorf("relay.Send expected len = %d, got %d", exp, got) + } + } + + if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil { + panic(err) + } + + got := <-relay.mined + exp := minedTx(i) - minedTx(i-1) + if got != exp { + t.Errorf("relay.NewHead expected len(mined) = %d, got %d", exp, got) + } + + exp = 0 + if i > int(txPermanent)+1 { + exp = minedTx(i-int(txPermanent)-1) - minedTx(i-int(txPermanent)-2) + } + if exp != 0 { + got = <-relay.discard + if got != exp { + t.Errorf("relay.Discard expected len = %d, got %d", exp, got) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/vm_env.go b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go new file mode 100644 index 0000000..ebd229d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/light/vm_env.go @@ -0,0 +1,194 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package light + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/net/context" +) + +// VMState is a wrapper for the light state that holds the actual context and +// passes it to any state operation that requires it. +type VMState struct { + ctx context.Context + state *LightState + snapshots []*LightState + err error +} + +func NewVMState(ctx context.Context, state *LightState) *VMState { + return &VMState{ctx: ctx, state: state} +} + +func (s *VMState) Error() error { + return s.err +} + +func (s *VMState) AddLog(log *types.Log) {} + +func (s *VMState) AddPreimage(hash common.Hash, preimage []byte) {} + +// errHandler handles and stores any state error that happens during execution. +func (s *VMState) errHandler(err error) { + if err != nil && s.err == nil { + s.err = err + } +} + +func (self *VMState) Snapshot() int { + self.snapshots = append(self.snapshots, self.state.Copy()) + return len(self.snapshots) - 1 +} + +func (self *VMState) RevertToSnapshot(idx int) { + self.state.Set(self.snapshots[idx]) + self.snapshots = self.snapshots[:idx] +} + +// CreateAccount creates creates a new account object and takes ownership. +func (s *VMState) CreateAccount(addr common.Address) { + _, err := s.state.CreateStateObject(s.ctx, addr) + s.errHandler(err) +} + +// AddBalance adds the given amount to the balance of the specified account +func (s *VMState) AddBalance(addr common.Address, amount *big.Int) { + err := s.state.AddBalance(s.ctx, addr, amount) + s.errHandler(err) +} + +// SubBalance adds the given amount to the balance of the specified account +func (s *VMState) SubBalance(addr common.Address, amount *big.Int) { + err := s.state.SubBalance(s.ctx, addr, amount) + s.errHandler(err) +} + +// ForEachStorage calls a callback function for every key/value pair found +// in the local storage cache. Note that unlike core/state.StateObject, +// light.StateObject only returns cached values and doesn't download the +// entire storage tree. +func (s *VMState) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { + err := s.state.ForEachStorage(s.ctx, addr, cb) + s.errHandler(err) +} + +// GetBalance retrieves the balance from the given address or 0 if the account does +// not exist +func (s *VMState) GetBalance(addr common.Address) *big.Int { + res, err := s.state.GetBalance(s.ctx, addr) + s.errHandler(err) + return res +} + +// GetNonce returns the nonce at the given address or 0 if the account does +// not exist +func (s *VMState) GetNonce(addr common.Address) uint64 { + res, err := s.state.GetNonce(s.ctx, addr) + s.errHandler(err) + return res +} + +// SetNonce sets the nonce of the specified account +func (s *VMState) SetNonce(addr common.Address, nonce uint64) { + err := s.state.SetNonce(s.ctx, addr, nonce) + s.errHandler(err) +} + +// GetCode returns the contract code at the given address or nil if the account +// does not exist +func (s *VMState) GetCode(addr common.Address) []byte { + res, err := s.state.GetCode(s.ctx, addr) + s.errHandler(err) + return res +} + +// GetCodeHash returns the contract code hash at the given address +func (s *VMState) GetCodeHash(addr common.Address) common.Hash { + res, err := s.state.GetCode(s.ctx, addr) + s.errHandler(err) + return crypto.Keccak256Hash(res) +} + +// GetCodeSize returns the contract code size at the given address +func (s *VMState) GetCodeSize(addr common.Address) int { + res, err := s.state.GetCode(s.ctx, addr) + s.errHandler(err) + return len(res) +} + +// SetCode sets the contract code at the specified account +func (s *VMState) SetCode(addr common.Address, code []byte) { + err := s.state.SetCode(s.ctx, addr, code) + s.errHandler(err) +} + +// AddRefund adds an amount to the refund value collected during a vm execution +func (s *VMState) AddRefund(gas *big.Int) { + s.state.AddRefund(gas) +} + +// GetRefund returns the refund value collected during a vm execution +func (s *VMState) GetRefund() *big.Int { + return s.state.GetRefund() +} + +// GetState returns the contract storage value at storage address b from the +// contract address a or common.Hash{} if the account does not exist +func (s *VMState) GetState(a common.Address, b common.Hash) common.Hash { + res, err := s.state.GetState(s.ctx, a, b) + s.errHandler(err) + return res +} + +// SetState sets the storage value at storage address key of the account addr +func (s *VMState) SetState(addr common.Address, key common.Hash, value common.Hash) { + err := s.state.SetState(s.ctx, addr, key, value) + s.errHandler(err) +} + +// Suicide marks an account to be removed and clears its balance +func (s *VMState) Suicide(addr common.Address) bool { + res, err := s.state.Suicide(s.ctx, addr) + s.errHandler(err) + return res +} + +// Exist returns true if an account exists at the given address +func (s *VMState) Exist(addr common.Address) bool { + res, err := s.state.HasAccount(s.ctx, addr) + s.errHandler(err) + return res +} + +// Empty returns true if the account at the given address is considered empty +func (s *VMState) Empty(addr common.Address) bool { + so, err := s.state.GetStateObject(s.ctx, addr) + s.errHandler(err) + return so == nil || so.empty() +} + +// HasSuicided returns true if the given account has been marked for deletion +// or false if the account does not exist +func (s *VMState) HasSuicided(addr common.Address) bool { + res, err := s.state.HasSuicided(s.ctx, addr) + s.errHandler(err) + return res +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/CONTRIBUTORS b/vendor/github.com/ethereum/go-ethereum/log/CONTRIBUTORS new file mode 100644 index 0000000..a086671 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/CONTRIBUTORS @@ -0,0 +1,11 @@ +Contributors to log15: + +- Aaron L +- Alan Shreve +- Chris Hines +- Ciaran Downey +- Dmitry Chestnykh +- Evan Shaw +- Péter Szilágyi +- Trevor Gattis +- Vincent Vanackere diff --git a/vendor/github.com/ethereum/go-ethereum/log/LICENSE b/vendor/github.com/ethereum/go-ethereum/log/LICENSE new file mode 100644 index 0000000..5f0d1fb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Alan Shreve + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/log/README.md b/vendor/github.com/ethereum/go-ethereum/log/README.md new file mode 100644 index 0000000..0951b21 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/README.md @@ -0,0 +1,77 @@ +![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) + +# log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) + +Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. + +## Features +- A simple, easy-to-understand API +- Promotes structured logging by encouraging use of key/value pairs +- Child loggers which inherit and add their own private context +- Lazy evaluation of expensive operations +- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API. +- Color terminal support +- Built-in support for logging to files, streams, syslog, and the network +- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more + +## Versioning +The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API, +you must vendor the library. + +## Importing + +```go +import log "github.com/inconshreveable/log15" +``` + +## Examples + +```go +// all loggers can have key/value context +srvlog := log.New("module", "app/server") + +// all log messages can have key/value context +srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) + +// child loggers with inherited context +connlog := srvlog.New("raddr", c.RemoteAddr()) +connlog.Info("connection open") + +// lazy evaluation +connlog.Debug("ping remote", "latency", log.Lazy{pingRemote}) + +// flexible configuration +srvlog.SetHandler(log.MultiHandler( + log.StreamHandler(os.Stderr, log.LogfmtFormat()), + log.LvlFilterHandler( + log.LvlError, + log.Must.FileHandler("errors.json", log.JsonFormat())))) +``` + +Will result in output that looks like this: + +``` +WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800 +INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1 +``` + +## Breaking API Changes +The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version +of log15. + +- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler +- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack` +- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors + +## FAQ + +### The varargs style is brittle and error prone! Can I have type safety please? +Yes. Use `log.Ctx`: + +```go +srvlog := log.New(log.Ctx{"module": "app/server"}) +srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate}) +``` + +## License +Apache diff --git a/vendor/github.com/ethereum/go-ethereum/log/README_ETHEREUM.md b/vendor/github.com/ethereum/go-ethereum/log/README_ETHEREUM.md new file mode 100644 index 0000000..6a0b014 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/README_ETHEREUM.md @@ -0,0 +1,4 @@ +This package is a fork of https://github.com/inconshreveable/log15, with some +minor modifications required by the go-ethereum codebase: + + * Support for log level `trace` diff --git a/vendor/github.com/ethereum/go-ethereum/log/doc.go b/vendor/github.com/ethereum/go-ethereum/log/doc.go new file mode 100644 index 0000000..83ad8c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/doc.go @@ -0,0 +1,333 @@ +/* +Package log15 provides an opinionated, simple toolkit for best-practice logging that is +both human and machine readable. It is modeled after the standard library's io and net/http +packages. + +This package enforces you to only log key/value pairs. Keys must be strings. Values may be +any type that you like. The default output format is logfmt, but you may also choose to use +JSON instead if that suits you. Here's how you log: + + log.Info("page accessed", "path", r.URL.Path, "user_id", user.id) + +This will output a line that looks like: + + lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9 + +Getting Started + +To get started, you'll want to import the library: + + import log "github.com/inconshreveable/log15" + + +Now you're ready to start logging: + + func main() { + log.Info("Program starting", "args", os.Args()) + } + + +Convention + +Because recording a human-meaningful message is common and good practice, the first argument to every +logging method is the value to the *implicit* key 'msg'. + +Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so +will the current timestamp with key 't'. + +You may supply any additional context as a set of key/value pairs to the logging function. log15 allows +you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for +logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate +in the variadic argument list: + + log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val) + +If you really do favor your type-safety, you may choose to pass a log.Ctx instead: + + log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val}) + + +Context loggers + +Frequently, you want to add context to a logger so that you can track actions associated with it. An http +request is a good example. You can easily create new loggers that have context that is automatically included +with each log line: + + requestlogger := log.New("path", r.URL.Path) + + // later + requestlogger.Debug("db txn commit", "duration", txnTimer.Finish()) + +This will output a log line that includes the path context that is attached to the logger: + + lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12 + + +Handlers + +The Handler interface defines where log lines are printed to and how they are formated. Handler is a +single interface that is inspired by net/http's handler interface: + + type Handler interface { + Log(r *Record) error + } + + +Handlers can filter records, format them, or dispatch to multiple other Handlers. +This package implements a number of Handlers for common logging patterns that are +easily composed to create flexible, custom logging structures. + +Here's an example handler that prints logfmt output to Stdout: + + handler := log.StreamHandler(os.Stdout, log.LogfmtFormat()) + +Here's an example handler that defers to two other handlers. One handler only prints records +from the rpc package in logfmt to standard out. The other prints records at Error level +or above in JSON formatted output to the file /var/log/service.json + + handler := log.MultiHandler( + log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())), + log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler()) + ) + +Logging File Names and Line Numbers + +This package implements three Handlers that add debugging information to the +context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's +an example that adds the source file and line number of each logging call to +the context. + + h := log.CallerFileHandler(log.StdoutHandler) + log.Root().SetHandler(h) + ... + log.Error("open file", "err", err) + +This will output a line that looks like: + + lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42 + +Here's an example that logs the call stack rather than just the call site. + + h := log.CallerStackHandler("%+v", log.StdoutHandler) + log.Root().SetHandler(h) + ... + log.Error("open file", "err", err) + +This will output a line that looks like: + + lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]" + +The "%+v" format instructs the handler to include the path of the source file +relative to the compile time GOPATH. The github.com/go-stack/stack package +documents the full list of formatting verbs and modifiers available. + +Custom Handlers + +The Handler interface is so simple that it's also trivial to write your own. Let's create an +example handler which tries to write to one handler, but if that fails it falls back to +writing to another handler and includes the error that it encountered when trying to write +to the primary. This might be useful when trying to log over a network socket, but if that +fails you want to log those records to a file on disk. + + type BackupHandler struct { + Primary Handler + Secondary Handler + } + + func (h *BackupHandler) Log (r *Record) error { + err := h.Primary.Log(r) + if err != nil { + r.Ctx = append(ctx, "primary_err", err) + return h.Secondary.Log(r) + } + return nil + } + +This pattern is so useful that a generic version that handles an arbitrary number of Handlers +is included as part of this library called FailoverHandler. + +Logging Expensive Operations + +Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay +the price of computing them if you haven't turned up your logging level to a high level of detail. + +This package provides a simple type to annotate a logging operation that you want to be evaluated +lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler +filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example: + + func factorRSAKey() (factors []int) { + // return the factors of a very large number + } + + log.Debug("factors", log.Lazy{factorRSAKey}) + +If this message is not logged for any reason (like logging at the Error level), then +factorRSAKey is never evaluated. + +Dynamic context values + +The same log.Lazy mechanism can be used to attach context to a logger which you want to be +evaluated when the message is logged, but not when the logger is created. For example, let's imagine +a game where you have Player objects: + + type Player struct { + name string + alive bool + log.Logger + } + +You always want to log a player's name and whether they're alive or dead, so when you create the player +object, you might do: + + p := &Player{name: name, alive: true} + p.Logger = log.New("name", p.name, "alive", p.alive) + +Only now, even after a player has died, the logger will still report they are alive because the logging +context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation +of whether the player is alive or not to each log message, so that the log records will reflect the player's +current state no matter when the log message is written: + + p := &Player{name: name, alive: true} + isAlive := func() bool { return p.alive } + player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive}) + +Terminal Format + +If log15 detects that stdout is a terminal, it will configure the default +handler for it (which is log.StdoutHandler) to use TerminalFormat. This format +logs records nicely for your terminal, including color-coded output based +on log level. + +Error Handling + +Becasuse log15 allows you to step around the type system, there are a few ways you can specify +invalid arguments to the logging functions. You could, for example, wrap something that is not +a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries +are typically the mechanism by which errors are reported, it would be onerous for the logging functions +to return errors. Instead, log15 handles errors by making these guarantees to you: + +- Any log record containing an error will still be printed with the error explained to you as part of the log record. + +- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily +(and if you like, automatically) detect if any of your logging calls are passing bad values. + +Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers +are encouraged to return errors only if they fail to write their log records out to an external source like if the +syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures +like the FailoverHandler. + +Library Use + +log15 is intended to be useful for library authors as a way to provide configurable logging to +users of their library. Best practice for use in a library is to always disable all output for your logger +by default and to provide a public Logger instance that consumers of your library can configure. Like so: + + package yourlib + + import "github.com/inconshreveable/log15" + + var Log = log.New() + + func init() { + Log.SetHandler(log.DiscardHandler()) + } + +Users of your library may then enable it if they like: + + import "github.com/inconshreveable/log15" + import "example.com/yourlib" + + func main() { + handler := // custom handler setup + yourlib.Log.SetHandler(handler) + } + +Best practices attaching logger context + +The ability to attach context to a logger is a powerful one. Where should you do it and why? +I favor embedding a Logger directly into any persistent object in my application and adding +unique, tracing context keys to it. For instance, imagine I am writing a web browser: + + type Tab struct { + url string + render *RenderingContext + // ... + + Logger + } + + func NewTab(url string) *Tab { + return &Tab { + // ... + url: url, + + Logger: log.New("url", url), + } + } + +When a new tab is created, I assign a logger to it with the url of +the tab as context so it can easily be traced through the logs. +Now, whenever we perform any operation with the tab, we'll log with its +embedded logger and it will include the tab title automatically: + + tab.Debug("moved position", "idx", tab.idx) + +There's only one problem. What if the tab url changes? We could +use log.Lazy to make sure the current url is always written, but that +would mean that we couldn't trace a tab's full lifetime through our +logs after the user navigate to a new URL. + +Instead, think about what values to attach to your loggers the +same way you think about what to use as a key in a SQL database schema. +If it's possible to use a natural key that is unique for the lifetime of the +object, do so. But otherwise, log15's ext package has a handy RandId +function to let you generate what you might call "surrogate keys" +They're just random hex identifiers to use for tracing. Back to our +Tab example, we would prefer to set up our Logger like so: + + import logext "github.com/inconshreveable/log15/ext" + + t := &Tab { + // ... + url: url, + } + + t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl}) + return t + +Now we'll have a unique traceable identifier even across loading new urls, but +we'll still be able to see the tab's current url in the log messages. + +Must + +For all Handler functions which can return an error, there is a version of that +function which will return no error but panics on failure. They are all available +on the Must object. For example: + + log.Must.FileHandler("/path", log.JsonFormat) + log.Must.NetHandler("tcp", ":1234", log.JsonFormat) + +Inspiration and Credit + +All of the following excellent projects inspired the design of this library: + +code.google.com/p/log4go + +github.com/op/go-logging + +github.com/technoweenie/grohl + +github.com/Sirupsen/logrus + +github.com/kr/logfmt + +github.com/spacemonkeygo/spacelog + +golang's stdlib, notably io and net/http + +The Name + +https://xkcd.com/927/ + +*/ +package log diff --git a/vendor/github.com/ethereum/go-ethereum/log/format.go b/vendor/github.com/ethereum/go-ethereum/log/format.go new file mode 100644 index 0000000..3763761 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/format.go @@ -0,0 +1,279 @@ +package log + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strconv" + "strings" + "sync" + "time" +) + +const ( + timeFormat = "2006-01-02T15:04:05-0700" + termTimeFormat = "01-02|15:04:05" + floatFormat = 'f' + termMsgJust = 40 +) + +type Format interface { + Format(r *Record) []byte +} + +// FormatFunc returns a new Format object which uses +// the given function to perform record formatting. +func FormatFunc(f func(*Record) []byte) Format { + return formatFunc(f) +} + +type formatFunc func(*Record) []byte + +func (f formatFunc) Format(r *Record) []byte { + return f(r) +} + +// TerminalFormat formats log records optimized for human readability on +// a terminal with color-coded level output and terser human friendly timestamp. +// This format should only be used for interactive programs or while developing. +// +// [TIME] [LEVEL] MESAGE key=value key=value ... +// +// Example: +// +// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 +// +func TerminalFormat() Format { + return FormatFunc(func(r *Record) []byte { + var color = 0 + switch r.Lvl { + case LvlCrit: + color = 35 + case LvlError: + color = 31 + case LvlWarn: + color = 33 + case LvlInfo: + color = 32 + case LvlDebug: + color = 36 + } + + b := &bytes.Buffer{} + lvl := strings.ToUpper(r.Lvl.String()) + if color > 0 { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) + } else { + fmt.Fprintf(b, "[%s] [%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) + } + + // try to justify the log output for short messages + if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { + b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) + } + + // print the keys logfmt style + logfmt(b, r.Ctx, color) + return b.Bytes() + }) +} + +// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable +// format for key/value pairs. +// +// For more details see: http://godoc.org/github.com/kr/logfmt +// +func LogfmtFormat() Format { + return FormatFunc(func(r *Record) []byte { + common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} + buf := &bytes.Buffer{} + logfmt(buf, append(common, r.Ctx...), 0) + return buf.Bytes() + }) +} + +func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { + for i := 0; i < len(ctx); i += 2 { + if i != 0 { + buf.WriteByte(' ') + } + + k, ok := ctx[i].(string) + v := formatLogfmtValue(ctx[i+1]) + if !ok { + k, v = errorKey, formatLogfmtValue(k) + } + + // XXX: we should probably check that all of your key bytes aren't invalid + if color > 0 { + fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=%s", color, k, v) + } else { + buf.WriteString(k) + buf.WriteByte('=') + buf.WriteString(v) + } + } + + buf.WriteByte('\n') +} + +// JsonFormat formats log records as JSON objects separated by newlines. +// It is the equivalent of JsonFormatEx(false, true). +func JsonFormat() Format { + return JsonFormatEx(false, true) +} + +// JsonFormatEx formats log records as JSON objects. If pretty is true, +// records will be pretty-printed. If lineSeparated is true, records +// will be logged with a new line between each record. +func JsonFormatEx(pretty, lineSeparated bool) Format { + jsonMarshal := json.Marshal + if pretty { + jsonMarshal = func(v interface{}) ([]byte, error) { + return json.MarshalIndent(v, "", " ") + } + } + + return FormatFunc(func(r *Record) []byte { + props := make(map[string]interface{}) + + props[r.KeyNames.Time] = r.Time + props[r.KeyNames.Lvl] = r.Lvl.String() + props[r.KeyNames.Msg] = r.Msg + + for i := 0; i < len(r.Ctx); i += 2 { + k, ok := r.Ctx[i].(string) + if !ok { + props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) + } + props[k] = formatJsonValue(r.Ctx[i+1]) + } + + b, err := jsonMarshal(props) + if err != nil { + b, _ = jsonMarshal(map[string]string{ + errorKey: err.Error(), + }) + return b + } + + if lineSeparated { + b = append(b, '\n') + } + + return b + }) +} + +func formatShared(value interface{}) (result interface{}) { + defer func() { + if err := recover(); err != nil { + if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { + result = "nil" + } else { + panic(err) + } + } + }() + + switch v := value.(type) { + case time.Time: + return v.Format(timeFormat) + + case error: + return v.Error() + + case fmt.Stringer: + return v.String() + + default: + return v + } +} + +func formatJsonValue(value interface{}) interface{} { + value = formatShared(value) + switch value.(type) { + case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: + return value + default: + return fmt.Sprintf("%+v", value) + } +} + +// formatValue formats a value for serialization +func formatLogfmtValue(value interface{}) string { + if value == nil { + return "nil" + } + + if t, ok := value.(time.Time); ok { + // Performance optimization: No need for escaping since the provided + // timeFormat doesn't have any escape characters, and escaping is + // expensive. + return t.Format(timeFormat) + } + value = formatShared(value) + switch v := value.(type) { + case bool: + return strconv.FormatBool(v) + case float32: + return strconv.FormatFloat(float64(v), floatFormat, 3, 64) + case float64: + return strconv.FormatFloat(v, floatFormat, 3, 64) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return fmt.Sprintf("%d", value) + case string: + return escapeString(v) + default: + return escapeString(fmt.Sprintf("%+v", value)) + } +} + +var stringBufPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + +func escapeString(s string) string { + needsQuotes := false + needsEscape := false + for _, r := range s { + if r <= ' ' || r == '=' || r == '"' { + needsQuotes = true + } + if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' { + needsEscape = true + } + } + if needsEscape == false && needsQuotes == false { + return s + } + e := stringBufPool.Get().(*bytes.Buffer) + e.WriteByte('"') + for _, r := range s { + switch r { + case '\\', '"': + e.WriteByte('\\') + e.WriteByte(byte(r)) + case '\n': + e.WriteString("\\n") + case '\r': + e.WriteString("\\r") + case '\t': + e.WriteString("\\t") + default: + e.WriteRune(r) + } + } + e.WriteByte('"') + var ret string + if needsQuotes { + ret = e.String() + } else { + ret = string(e.Bytes()[1 : e.Len()-1]) + } + e.Reset() + stringBufPool.Put(e) + return ret +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/handler.go b/vendor/github.com/ethereum/go-ethereum/log/handler.go new file mode 100644 index 0000000..abb17b4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/handler.go @@ -0,0 +1,356 @@ +package log + +import ( + "fmt" + "io" + "net" + "os" + "reflect" + "sync" + + "github.com/go-stack/stack" +) + +// A Logger prints its log records by writing to a Handler. +// The Handler interface defines where and how log records are written. +// Handlers are composable, providing you great flexibility in combining +// them to achieve the logging structure that suits your applications. +type Handler interface { + Log(r *Record) error +} + +// FuncHandler returns a Handler that logs records with the given +// function. +func FuncHandler(fn func(r *Record) error) Handler { + return funcHandler(fn) +} + +type funcHandler func(r *Record) error + +func (h funcHandler) Log(r *Record) error { + return h(r) +} + +// StreamHandler writes log records to an io.Writer +// with the given format. StreamHandler can be used +// to easily begin writing log records to other +// outputs. +// +// StreamHandler wraps itself with LazyHandler and SyncHandler +// to evaluate Lazy objects and perform safe concurrent writes. +func StreamHandler(wr io.Writer, fmtr Format) Handler { + h := FuncHandler(func(r *Record) error { + _, err := wr.Write(fmtr.Format(r)) + return err + }) + return LazyHandler(SyncHandler(h)) +} + +// SyncHandler can be wrapped around a handler to guarantee that +// only a single Log operation can proceed at a time. It's necessary +// for thread-safe concurrent writes. +func SyncHandler(h Handler) Handler { + var mu sync.Mutex + return FuncHandler(func(r *Record) error { + defer mu.Unlock() + mu.Lock() + return h.Log(r) + }) +} + +// FileHandler returns a handler which writes log records to the give file +// using the given format. If the path +// already exists, FileHandler will append to the given file. If it does not, +// FileHandler will create the file with mode 0644. +func FileHandler(path string, fmtr Format) (Handler, error) { + f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + return closingHandler{f, StreamHandler(f, fmtr)}, nil +} + +// NetHandler opens a socket to the given address and writes records +// over the connection. +func NetHandler(network, addr string, fmtr Format) (Handler, error) { + conn, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + + return closingHandler{conn, StreamHandler(conn, fmtr)}, nil +} + +// XXX: closingHandler is essentially unused at the moment +// it's meant for a future time when the Handler interface supports +// a possible Close() operation +type closingHandler struct { + io.WriteCloser + Handler +} + +func (h *closingHandler) Close() error { + return h.WriteCloser.Close() +} + +// CallerFileHandler returns a Handler that adds the line number and file of +// the calling function to the context with key "caller". +func CallerFileHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) + return h.Log(r) + }) +} + +// CallerFuncHandler returns a Handler that adds the calling function name to +// the context with key "fn". +func CallerFuncHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + r.Ctx = append(r.Ctx, "fn", fmt.Sprintf("%+n", r.Call)) + return h.Log(r) + }) +} + +// CallerStackHandler returns a Handler that adds a stack trace to the context +// with key "stack". The stack trace is formated as a space separated list of +// call sites inside matching []'s. The most recent call site is listed first. +// Each call site is formatted according to format. See the documentation of +// package github.com/go-stack/stack for the list of supported formats. +func CallerStackHandler(format string, h Handler) Handler { + return FuncHandler(func(r *Record) error { + s := stack.Trace().TrimBelow(r.Call).TrimRuntime() + if len(s) > 0 { + r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) + } + return h.Log(r) + }) +} + +// FilterHandler returns a Handler that only writes records to the +// wrapped Handler if the given function evaluates true. For example, +// to only log records where the 'err' key is not nil: +// +// logger.SetHandler(FilterHandler(func(r *Record) bool { +// for i := 0; i < len(r.Ctx); i += 2 { +// if r.Ctx[i] == "err" { +// return r.Ctx[i+1] != nil +// } +// } +// return false +// }, h)) +// +func FilterHandler(fn func(r *Record) bool, h Handler) Handler { + return FuncHandler(func(r *Record) error { + if fn(r) { + return h.Log(r) + } + return nil + }) +} + +// MatchFilterHandler returns a Handler that only writes records +// to the wrapped Handler if the given key in the logged +// context matches the value. For example, to only log records +// from your ui package: +// +// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) +// +func MatchFilterHandler(key string, value interface{}, h Handler) Handler { + return FilterHandler(func(r *Record) (pass bool) { + switch key { + case r.KeyNames.Lvl: + return r.Lvl == value + case r.KeyNames.Time: + return r.Time == value + case r.KeyNames.Msg: + return r.Msg == value + } + + for i := 0; i < len(r.Ctx); i += 2 { + if r.Ctx[i] == key { + return r.Ctx[i+1] == value + } + } + return false + }, h) +} + +// LvlFilterHandler returns a Handler that only writes +// records which are less than the given verbosity +// level to the wrapped Handler. For example, to only +// log Error/Crit records: +// +// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) +// +func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { + return FilterHandler(func(r *Record) (pass bool) { + return r.Lvl <= maxLvl + }, h) +} + +// A MultiHandler dispatches any write to each of its handlers. +// This is useful for writing different types of log information +// to different locations. For example, to log to a file and +// standard error: +// +// log.MultiHandler( +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StderrHandler) +// +func MultiHandler(hs ...Handler) Handler { + return FuncHandler(func(r *Record) error { + for _, h := range hs { + // what to do about failures? + h.Log(r) + } + return nil + }) +} + +// A FailoverHandler writes all log records to the first handler +// specified, but will failover and write to the second handler if +// the first handler has failed, and so on for all handlers specified. +// For example you might want to log to a network socket, but failover +// to writing to a file if the network fails, and then to +// standard out if the file write fails: +// +// log.FailoverHandler( +// log.Must.NetHandler("tcp", ":9090", log.JsonFormat()), +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StdoutHandler) +// +// All writes that do not go to the first handler will add context with keys of +// the form "failover_err_{idx}" which explain the error encountered while +// trying to write to the handlers before them in the list. +func FailoverHandler(hs ...Handler) Handler { + return FuncHandler(func(r *Record) error { + var err error + for i, h := range hs { + err = h.Log(r) + if err == nil { + return nil + } else { + r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) + } + } + + return err + }) +} + +// ChannelHandler writes all records to the given channel. +// It blocks if the channel is full. Useful for async processing +// of log messages, it's used by BufferedHandler. +func ChannelHandler(recs chan<- *Record) Handler { + return FuncHandler(func(r *Record) error { + recs <- r + return nil + }) +} + +// BufferedHandler writes all records to a buffered +// channel of the given size which flushes into the wrapped +// handler whenever it is available for writing. Since these +// writes happen asynchronously, all writes to a BufferedHandler +// never return an error and any errors from the wrapped handler are ignored. +func BufferedHandler(bufSize int, h Handler) Handler { + recs := make(chan *Record, bufSize) + go func() { + for m := range recs { + _ = h.Log(m) + } + }() + return ChannelHandler(recs) +} + +// LazyHandler writes all values to the wrapped handler after evaluating +// any lazy functions in the record's context. It is already wrapped +// around StreamHandler and SyslogHandler in this library, you'll only need +// it if you write your own Handler. +func LazyHandler(h Handler) Handler { + return FuncHandler(func(r *Record) error { + // go through the values (odd indices) and reassign + // the values of any lazy fn to the result of its execution + hadErr := false + for i := 1; i < len(r.Ctx); i += 2 { + lz, ok := r.Ctx[i].(Lazy) + if ok { + v, err := evaluateLazy(lz) + if err != nil { + hadErr = true + r.Ctx[i] = err + } else { + if cs, ok := v.(stack.CallStack); ok { + v = cs.TrimBelow(r.Call).TrimRuntime() + } + r.Ctx[i] = v + } + } + } + + if hadErr { + r.Ctx = append(r.Ctx, errorKey, "bad lazy") + } + + return h.Log(r) + }) +} + +func evaluateLazy(lz Lazy) (interface{}, error) { + t := reflect.TypeOf(lz.Fn) + + if t.Kind() != reflect.Func { + return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) + } + + if t.NumIn() > 0 { + return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) + } + + if t.NumOut() == 0 { + return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) + } + + value := reflect.ValueOf(lz.Fn) + results := value.Call([]reflect.Value{}) + if len(results) == 1 { + return results[0].Interface(), nil + } else { + values := make([]interface{}, len(results)) + for i, v := range results { + values[i] = v.Interface() + } + return values, nil + } +} + +// DiscardHandler reports success for all writes but does nothing. +// It is useful for dynamically disabling logging at runtime via +// a Logger's SetHandler method. +func DiscardHandler() Handler { + return FuncHandler(func(r *Record) error { + return nil + }) +} + +// The Must object provides the following Handler creation functions +// which instead of returning an error parameter only return a Handler +// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler +var Must muster + +func must(h Handler, err error) Handler { + if err != nil { + panic(err) + } + return h +} + +type muster struct{} + +func (m muster) FileHandler(path string, fmtr Format) Handler { + return must(FileHandler(path, fmtr)) +} + +func (m muster) NetHandler(network, addr string, fmtr Format) Handler { + return must(NetHandler(network, addr, fmtr)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/handler_go13.go b/vendor/github.com/ethereum/go-ethereum/log/handler_go13.go new file mode 100644 index 0000000..0843ed0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/handler_go13.go @@ -0,0 +1,26 @@ +// +build !go1.4 + +package log + +import ( + "sync/atomic" + "unsafe" +) + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler unsafe.Pointer +} + +func (h *swapHandler) Log(r *Record) error { + return h.Get().Log(r) +} + +func (h *swapHandler) Get() Handler { + return *(*Handler)(atomic.LoadPointer(&h.handler)) +} + +func (h *swapHandler) Swap(newHandler Handler) { + atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/handler_go14.go b/vendor/github.com/ethereum/go-ethereum/log/handler_go14.go new file mode 100644 index 0000000..05dedbf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/handler_go14.go @@ -0,0 +1,23 @@ +// +build go1.4 + +package log + +import "sync/atomic" + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler atomic.Value +} + +func (h *swapHandler) Log(r *Record) error { + return (*h.handler.Load().(*Handler)).Log(r) +} + +func (h *swapHandler) Swap(newHandler Handler) { + h.handler.Store(&newHandler) +} + +func (h *swapHandler) Get() Handler { + return *h.handler.Load().(*Handler) +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/logger.go b/vendor/github.com/ethereum/go-ethereum/log/logger.go new file mode 100644 index 0000000..a9d42b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/logger.go @@ -0,0 +1,208 @@ +package log + +import ( + "fmt" + "time" + + "github.com/go-stack/stack" +) + +const timeKey = "t" +const lvlKey = "lvl" +const msgKey = "msg" +const errorKey = "LOG15_ERROR" + +type Lvl int + +const ( + LvlCrit Lvl = iota + LvlError + LvlWarn + LvlInfo + LvlDebug +) + +// Returns the name of a Lvl +func (l Lvl) String() string { + switch l { + case LvlDebug: + return "dbug" + case LvlInfo: + return "info" + case LvlWarn: + return "warn" + case LvlError: + return "eror" + case LvlCrit: + return "crit" + default: + panic("bad level") + } +} + +// Returns the appropriate Lvl from a string name. +// Useful for parsing command line args and configuration files. +func LvlFromString(lvlString string) (Lvl, error) { + switch lvlString { + case "debug", "dbug": + return LvlDebug, nil + case "info": + return LvlInfo, nil + case "warn": + return LvlWarn, nil + case "error", "eror": + return LvlError, nil + case "crit": + return LvlCrit, nil + default: + return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) + } +} + +// A Record is what a Logger asks its handler to write +type Record struct { + Time time.Time + Lvl Lvl + Msg string + Ctx []interface{} + Call stack.Call + KeyNames RecordKeyNames +} + +type RecordKeyNames struct { + Time string + Msg string + Lvl string +} + +// A Logger writes key/value pairs to a Handler +type Logger interface { + // New returns a new Logger that has this logger's context plus the given context + New(ctx ...interface{}) Logger + + // GetHandler gets the handler associated with the logger. + GetHandler() Handler + + // SetHandler updates the logger to write records to the specified handler. + SetHandler(h Handler) + + // Log a message at the given level with context key/value pairs + Debug(msg string, ctx ...interface{}) + Info(msg string, ctx ...interface{}) + Warn(msg string, ctx ...interface{}) + Error(msg string, ctx ...interface{}) + Crit(msg string, ctx ...interface{}) +} + +type logger struct { + ctx []interface{} + h *swapHandler +} + +func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { + l.h.Log(&Record{ + Time: time.Now(), + Lvl: lvl, + Msg: msg, + Ctx: newContext(l.ctx, ctx), + Call: stack.Caller(2), + KeyNames: RecordKeyNames{ + Time: timeKey, + Msg: msgKey, + Lvl: lvlKey, + }, + }) +} + +func (l *logger) New(ctx ...interface{}) Logger { + child := &logger{newContext(l.ctx, ctx), new(swapHandler)} + child.SetHandler(l.h) + return child +} + +func newContext(prefix []interface{}, suffix []interface{}) []interface{} { + normalizedSuffix := normalize(suffix) + newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) + n := copy(newCtx, prefix) + copy(newCtx[n:], normalizedSuffix) + return newCtx +} + +func (l *logger) Debug(msg string, ctx ...interface{}) { + l.write(msg, LvlDebug, ctx) +} + +func (l *logger) Info(msg string, ctx ...interface{}) { + l.write(msg, LvlInfo, ctx) +} + +func (l *logger) Warn(msg string, ctx ...interface{}) { + l.write(msg, LvlWarn, ctx) +} + +func (l *logger) Error(msg string, ctx ...interface{}) { + l.write(msg, LvlError, ctx) +} + +func (l *logger) Crit(msg string, ctx ...interface{}) { + l.write(msg, LvlCrit, ctx) +} + +func (l *logger) GetHandler() Handler { + return l.h.Get() +} + +func (l *logger) SetHandler(h Handler) { + l.h.Swap(h) +} + +func normalize(ctx []interface{}) []interface{} { + // if the caller passed a Ctx object, then expand it + if len(ctx) == 1 { + if ctxMap, ok := ctx[0].(Ctx); ok { + ctx = ctxMap.toArray() + } + } + + // ctx needs to be even because it's a series of key/value pairs + // no one wants to check for errors on logging functions, + // so instead of erroring on bad input, we'll just make sure + // that things are the right length and users can fix bugs + // when they see the output looks wrong + if len(ctx)%2 != 0 { + ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") + } + + return ctx +} + +// Lazy allows you to defer calculation of a logged value that is expensive +// to compute until it is certain that it must be evaluated with the given filters. +// +// Lazy may also be used in conjunction with a Logger's New() function +// to generate a child logger which always reports the current value of changing +// state. +// +// You may wrap any function which takes no arguments to Lazy. It may return any +// number of values of any type. +type Lazy struct { + Fn interface{} +} + +// Ctx is a map of key/value pairs to pass as context to a log function +// Use this only if you really need greater safety around the arguments you pass +// to the logging functions. +type Ctx map[string]interface{} + +func (c Ctx) toArray() []interface{} { + arr := make([]interface{}, len(c)*2) + + i := 0 + for k, v := range c { + arr[i] = k + arr[i+1] = v + i += 2 + } + + return arr +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/root.go b/vendor/github.com/ethereum/go-ethereum/log/root.go new file mode 100644 index 0000000..39b4c94 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/root.go @@ -0,0 +1,67 @@ +package log + +import ( + "os" + + "github.com/ethereum/go-ethereum/log/term" + "github.com/mattn/go-colorable" +) + +var ( + root *logger + StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat()) + StderrHandler = StreamHandler(os.Stderr, LogfmtFormat()) +) + +func init() { + if term.IsTty(os.Stdout.Fd()) { + StdoutHandler = StreamHandler(colorable.NewColorableStdout(), TerminalFormat()) + } + + if term.IsTty(os.Stderr.Fd()) { + StderrHandler = StreamHandler(colorable.NewColorableStderr(), TerminalFormat()) + } + + root = &logger{[]interface{}{}, new(swapHandler)} + root.SetHandler(StdoutHandler) +} + +// New returns a new logger with the given context. +// New is a convenient alias for Root().New +func New(ctx ...interface{}) Logger { + return root.New(ctx...) +} + +// Root returns the root logger +func Root() Logger { + return root +} + +// The following functions bypass the exported logger methods (logger.Debug, +// etc.) to keep the call depth the same for all paths to logger.write so +// runtime.Caller(2) always refers to the call site in client code. + +// Debug is a convenient alias for Root().Debug +func Debug(msg string, ctx ...interface{}) { + root.write(msg, LvlDebug, ctx) +} + +// Info is a convenient alias for Root().Info +func Info(msg string, ctx ...interface{}) { + root.write(msg, LvlInfo, ctx) +} + +// Warn is a convenient alias for Root().Warn +func Warn(msg string, ctx ...interface{}) { + root.write(msg, LvlWarn, ctx) +} + +// Error is a convenient alias for Root().Error +func Error(msg string, ctx ...interface{}) { + root.write(msg, LvlError, ctx) +} + +// Crit is a convenient alias for Root().Crit +func Crit(msg string, ctx ...interface{}) { + root.write(msg, LvlCrit, ctx) +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/syslog.go b/vendor/github.com/ethereum/go-ethereum/log/syslog.go new file mode 100644 index 0000000..4f1097f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/syslog.go @@ -0,0 +1,55 @@ +// +build !windows,!plan9 + +package log + +import ( + "log/syslog" + "strings" +) + +// SyslogHandler opens a connection to the system syslog daemon by calling +// syslog.New and writes all records to it. +func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) { + wr, err := syslog.New(priority, tag) + return sharedSyslog(fmtr, wr, err) +} + +// SyslogNetHandler opens a connection to a log daemon over the network and writes +// all log records to it. +func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) { + wr, err := syslog.Dial(net, addr, priority, tag) + return sharedSyslog(fmtr, wr, err) +} + +func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) { + if err != nil { + return nil, err + } + h := FuncHandler(func(r *Record) error { + var syslogFn = sysWr.Info + switch r.Lvl { + case LvlCrit: + syslogFn = sysWr.Crit + case LvlError: + syslogFn = sysWr.Err + case LvlWarn: + syslogFn = sysWr.Warning + case LvlInfo: + syslogFn = sysWr.Info + case LvlDebug: + syslogFn = sysWr.Debug + } + + s := strings.TrimSpace(string(fmtr.Format(r))) + return syslogFn(s) + }) + return LazyHandler(&closingHandler{sysWr, h}), nil +} + +func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler { + return must(SyslogHandler(priority, tag, fmtr)) +} + +func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler { + return must(SyslogNetHandler(net, addr, priority, tag, fmtr)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/LICENSE b/vendor/github.com/ethereum/go-ethereum/log/term/LICENSE new file mode 100644 index 0000000..f090cb4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_appengine.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_appengine.go new file mode 100644 index 0000000..c1b5d2a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_appengine.go @@ -0,0 +1,13 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package term + +// IsTty always returns false on AppEngine. +func IsTty(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_darwin.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_darwin.go new file mode 100644 index 0000000..d8f351b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_darwin.go @@ -0,0 +1,13 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +build !appengine + +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_freebsd.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_freebsd.go new file mode 100644 index 0000000..cfaceab --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_freebsd.go @@ -0,0 +1,18 @@ +package term + +import ( + "syscall" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_linux.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_linux.go new file mode 100644 index 0000000..5290468 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_linux.go @@ -0,0 +1,14 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +package term + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_netbsd.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_netbsd.go new file mode 100644 index 0000000..f9bb9e1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_netbsd.go @@ -0,0 +1,7 @@ +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_notwindows.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_notwindows.go new file mode 100644 index 0000000..c9af534 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_notwindows.go @@ -0,0 +1,20 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,!appengine darwin freebsd openbsd netbsd + +package term + +import ( + "syscall" + "unsafe" +) + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + var termios Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_openbsd.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_openbsd.go new file mode 100644 index 0000000..f9bb9e1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_openbsd.go @@ -0,0 +1,7 @@ +package term + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_solaris.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_solaris.go new file mode 100644 index 0000000..033c163 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_solaris.go @@ -0,0 +1,9 @@ +package term + +import "golang.org/x/sys/unix" + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETA) + return err == nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/log/term/terminal_windows.go b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_windows.go new file mode 100644 index 0000000..df3c30c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/log/term/terminal_windows.go @@ -0,0 +1,26 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package term + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/logger/glog/LICENSE b/vendor/github.com/ethereum/go-ethereum/logger/glog/LICENSE new file mode 100644 index 0000000..37ec93a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/glog/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/logger/glog/README b/vendor/github.com/ethereum/go-ethereum/logger/glog/README new file mode 100644 index 0000000..c7b1e60 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/glog/README @@ -0,0 +1,44 @@ +glog +==== + +Leveled execution logs for Go. + +This is an efficient pure Go implementation of leveled logs in the +manner of the open source C++ package + http://code.google.com/p/google-glog + +By binding methods to booleans it is possible to use the log package +without paying the expense of evaluating the arguments to the log. +Through the -vmodule flag, the package also provides fine-grained +control over logging at the file level. + +The comment from glog.go introduces the ideas: + + Package glog implements logging analogous to the Google-internal + C++ INFO/ERROR/V setup. It provides functions Info, Warning, + Error, Fatal, plus formatting variants such as Infof. It + also provides V-style logging controlled by the -v and + -vmodule=file=2 flags. + + Basic examples: + + glog.Info("Prepare to repel boarders") + + glog.Fatalf("Initialization failed: %s", err) + + See the documentation for the V function for an explanation + of these examples: + + if glog.V(2) { + glog.Info("Starting transaction...") + } + + glog.V(2).Infoln("Processed", nItems, "elements") + + +The repository contains an open source version of the log package +used inside Google. The master copy of the source lives inside +Google, not here. The code in this repo is for export only and is not itself +under development. Feature requests will be ignored. + +Send bug reports to golang-nuts@googlegroups.com. diff --git a/vendor/github.com/ethereum/go-ethereum/logger/glog/glog.go b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog.go new file mode 100644 index 0000000..0b33527 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog.go @@ -0,0 +1,1223 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. +// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as +// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. +// +// Basic examples: +// +// glog.Info("Prepare to repel boarders") +// +// glog.Fatalf("Initialization failed: %s", err) +// +// See the documentation for the V function for an explanation of these examples: +// +// if glog.V(2) { +// glog.Info("Starting transaction...") +// } +// +// glog.V(2).Infoln("Processed", nItems, "elements") +// +// Log output is buffered and written periodically using Flush. Programs +// should call Flush before exiting to guarantee all log output is written. +// +// By default, all log statements write to files in a temporary directory. +// This package provides several flags that modify this behavior. +// As a result, flag.Parse must be called before any logging is done. +// +// -logtostderr=false +// Logs are written to standard error instead of to files. +// -alsologtostderr=false +// Logs are written to standard error as well as to files. +// -stderrthreshold=ERROR +// Log events at or above this severity are logged to standard +// error as well as to files. +// -log_dir="" +// Log files will be written to this directory instead of the +// default temporary directory. +// +// Other flags provide aids to debugging. +// +// -log_backtrace_at="" +// When set to a file and line number holding a logging statement, +// such as +// -log_backtrace_at=gopherflakes.go:234 +// a stack trace will be written to the Info log whenever execution +// hits that statement. (Unlike with -vmodule, the ".go" must be +// present.) +// -v=0 +// Enable V-leveled logging at the specified level. +// -vmodule="" +// The syntax of the argument is a comma-separated list of pattern=N, +// where pattern is a literal file name or "glob" pattern matching +// and N is a V level. For instance, +// +// -vmodule=gopher.go=3 +// sets the V level to 3 in all Go files named "gopher.go". +// +// -vmodule=foo=3 +// sets V to 3 in all files of any packages whose import path ends in "foo". +// +// -vmodule=foo/*=3 +// sets V to 3 in all files of any packages whose import path contains "foo". +package glog + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + stdLog "log" + "os" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// severity identifies the sort of log: info, warning etc. It also implements +// the flag.Value interface. The -stderrthreshold flag is of type severity and +// should be modified only through the flag.Value interface. The values match +// the corresponding constants in C++. +type severity int32 // sync/atomic int32 + +// These constants identify the log levels in order of increasing severity. +// A message written to a high-severity log file is also written to each +// lower-severity log file. +const ( + infoLog severity = iota + warningLog + errorLog + fatalLog + numSeverity = 4 +) + +const severityChar = "IWEF" + +var severityName = []string{ + infoLog: "INFO", + warningLog: "WARNING", + errorLog: "ERROR", + fatalLog: "FATAL", +} + +// these path prefixes are trimmed for display, but not when +// matching vmodule filters. +var trimPrefixes = []string{ + "/github.com/ethereum/go-ethereum", + "/github.com/ethereum/ethash", +} + +func trimToImportPath(file string) string { + if root := strings.LastIndex(file, "src/"); root != 0 { + file = file[root+3:] + } + return file +} + +// SetV sets the global verbosity level +func SetV(v int) { + logging.verbosity.set(Level(v)) +} + +// SetToStderr sets the global output style +func SetToStderr(toStderr bool) { + logging.mu.Lock() + logging.toStderr = toStderr + logging.mu.Unlock() +} + +// GetTraceLocation returns the global TraceLocation flag. +func GetTraceLocation() *TraceLocation { + return &logging.traceLocation +} + +// GetVModule returns the global verbosity pattern flag. +func GetVModule() *moduleSpec { + return &logging.vmodule +} + +// GetVerbosity returns the global verbosity level flag. +func GetVerbosity() *Level { + return &logging.verbosity +} + +// get returns the value of the severity. +func (s *severity) get() severity { + return severity(atomic.LoadInt32((*int32)(s))) +} + +// set sets the value of the severity. +func (s *severity) set(val severity) { + atomic.StoreInt32((*int32)(s), int32(val)) +} + +// String is part of the flag.Value interface. +func (s *severity) String() string { + return strconv.FormatInt(int64(*s), 10) +} + +// Get is part of the flag.Value interface. +func (s *severity) Get() interface{} { + return *s +} + +// Set is part of the flag.Value interface. +func (s *severity) Set(value string) error { + var threshold severity + // Is it a known name? + if v, ok := severityByName(value); ok { + threshold = v + } else { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + threshold = severity(v) + } + logging.stderrThreshold.set(threshold) + return nil +} + +func severityByName(s string) (severity, bool) { + s = strings.ToUpper(s) + for i, name := range severityName { + if name == s { + return severity(i), true + } + } + return 0, false +} + +// OutputStats tracks the number of output lines and bytes written. +type OutputStats struct { + lines int64 + bytes int64 +} + +// Lines returns the number of lines written. +func (s *OutputStats) Lines() int64 { + return atomic.LoadInt64(&s.lines) +} + +// Bytes returns the number of bytes written. +func (s *OutputStats) Bytes() int64 { + return atomic.LoadInt64(&s.bytes) +} + +// Stats tracks the number of lines of output and number of bytes +// per severity level. Values must be read with atomic.LoadInt64. +var Stats struct { + Info, Warning, Error OutputStats +} + +var severityStats = [numSeverity]*OutputStats{ + infoLog: &Stats.Info, + warningLog: &Stats.Warning, + errorLog: &Stats.Error, +} + +// Level is exported because it appears in the arguments to V and is +// the type of the v flag, which can be set programmatically. +// It's a distinct type because we want to discriminate it from logType. +// Variables of type level are only changed under logging.mu. +// The -v flag is read only with atomic ops, so the state of the logging +// module is consistent. + +// Level is treated as a sync/atomic int32. + +// Level specifies a level of verbosity for V logs. *Level implements +// flag.Value; the -v flag is of type Level and should be modified +// only through the flag.Value interface. +type Level int32 + +// get returns the value of the Level. +func (l *Level) get() Level { + return Level(atomic.LoadInt32((*int32)(l))) +} + +// set sets the value of the Level. +func (l *Level) set(val Level) { + atomic.StoreInt32((*int32)(l), int32(val)) +} + +// String is part of the flag.Value interface. +func (l *Level) String() string { + return strconv.FormatInt(int64(*l), 10) +} + +// Get is part of the flag.Value interface. +func (l *Level) Get() interface{} { + return *l +} + +// Set is part of the flag.Value interface. +func (l *Level) Set(value string) error { + v, err := strconv.Atoi(value) + if err != nil { + return err + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(Level(v), logging.vmodule.filter, false) + return nil +} + +// moduleSpec represents the setting of the -vmodule flag. +type moduleSpec struct { + filter []modulePat +} + +// modulePat contains a filter for the -vmodule flag. +// It holds a verbosity level and a file pattern to match. +type modulePat struct { + pattern *regexp.Regexp + level Level +} + +func (m *moduleSpec) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + var b bytes.Buffer + for i, f := range m.filter { + if i > 0 { + b.WriteRune(',') + } + fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) + } + return b.String() +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported. +func (m *moduleSpec) Get() interface{} { + return nil +} + +var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") + +// Syntax: -vmodule=recordio=2,file=1,gfs*=3 +func (m *moduleSpec) Set(value string) error { + var filter []modulePat + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return errVmoduleSyntax + } + pattern := patLev[0] + v, err := strconv.Atoi(patLev[1]) + if err != nil { + return errors.New("syntax error: expect comma-separated list of filename=N") + } + if v < 0 { + return errors.New("negative value for vmodule level") + } + if v == 0 { + continue // Ignore. It's harmless but no point in paying the overhead. + } + // TODO: check syntax of filter? + re, _ := compileModulePattern(pattern) + filter = append(filter, modulePat{re, Level(v)}) + } + logging.mu.Lock() + defer logging.mu.Unlock() + logging.setVState(logging.verbosity, filter, true) + return nil +} + +// compiles a vmodule pattern to a regular expression. +func compileModulePattern(pat string) (*regexp.Regexp, error) { + re := ".*" + for _, comp := range strings.Split(pat, "/") { + if comp == "*" { + re += "(/.*)?" + } else if comp != "" { + // TODO: maybe return error if comp contains * + re += "/" + regexp.QuoteMeta(comp) + } + } + if !strings.HasSuffix(pat, ".go") { + re += "/[^/]+\\.go" + } + return regexp.Compile(re + "$") +} + +// traceLocation represents the setting of the -log_backtrace_at flag. +type TraceLocation struct { + file string + line int +} + +// isSet reports whether the trace location has been specified. +// logging.mu is held. +func (t *TraceLocation) isSet() bool { + return t.line > 0 +} + +// match reports whether the specified file and line matches the trace location. +// The argument file name is the full path, not the basename specified in the flag. +// logging.mu is held. +func (t *TraceLocation) match(file string, line int) bool { + if t.line != line { + return false + } + if i := strings.LastIndex(file, "/"); i >= 0 { + file = file[i+1:] + } + return t.file == file +} + +func (t *TraceLocation) String() string { + // Lock because the type is not atomic. TODO: clean this up. + logging.mu.Lock() + defer logging.mu.Unlock() + return fmt.Sprintf("%s:%d", t.file, t.line) +} + +// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the +// struct is not exported +func (t *TraceLocation) Get() interface{} { + return nil +} + +var errTraceSyntax = errors.New("syntax error: expect file.go:234") + +// Syntax: -log_backtrace_at=gopherflakes.go:234 +// Note that unlike vmodule the file extension is included here. +func (t *TraceLocation) Set(value string) error { + if value == "" { + // Unset. + logging.mu.Lock() + t.line = 0 + t.file = "" + logging.mu.Unlock() + return nil + } + + fields := strings.Split(value, ":") + if len(fields) != 2 { + return errTraceSyntax + } + file, line := fields[0], fields[1] + if !strings.Contains(file, ".") { + return errTraceSyntax + } + v, err := strconv.Atoi(line) + if err != nil { + return errTraceSyntax + } + if v <= 0 { + return errors.New("negative or zero value for level") + } + logging.mu.Lock() + defer logging.mu.Unlock() + t.line = v + t.file = file + return nil +} + +// flushSyncWriter is the interface satisfied by logging destinations. +type flushSyncWriter interface { + Flush() error + Sync() error + io.Writer +} + +func init() { + //flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") + //flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") + //flag.Var(&logging.verbosity, "v", "log level for V logs") + //flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") + //flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") + //flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") + + // Default stderrThreshold is ERROR. + logging.stderrThreshold = errorLog + logging.setVState(3, nil, false) + go logging.flushDaemon() +} + +// Flush flushes all pending log I/O. +func Flush() { + logging.lockAndFlushAll() +} + +// loggingT collects all the global state of the logging setup. +type loggingT struct { + // Boolean flags. Not handled atomically because the flag.Value interface + // does not let us avoid the =true, and that shorthand is necessary for + // compatibility. TODO: does this matter enough to fix? Seems unlikely. + toStderr bool // The -logtostderr flag. + alsoToStderr bool // The -alsologtostderr flag. + + // Level flag. Handled atomically. + stderrThreshold severity // The -stderrthreshold flag. + + // freeList is a list of byte buffers, maintained under freeListMu. + freeList *buffer + // freeListMu maintains the free list. It is separate from the main mutex + // so buffers can be grabbed and printed to without holding the main lock, + // for better parallelization. + freeListMu sync.Mutex + + // mu protects the remaining elements of this structure and is + // used to synchronize logging. + mu sync.Mutex + // file holds writer for each of the log types. + file [numSeverity]flushSyncWriter + // pcs is used in V to avoid an allocation when computing the caller's PC. + pcs [1]uintptr + // vmap is a cache of the V Level for each V() call site, identified by PC. + // It is wiped whenever the vmodule flag changes state. + vmap map[uintptr]Level + // filterLength stores the length of the vmodule filter chain. If greater + // than zero, it means vmodule is enabled. It may be read safely + // using sync.LoadInt32, but is only modified under mu. + filterLength int32 + // traceLocation is the state of the -log_backtrace_at flag. + traceLocation TraceLocation + // These flags are modified only under lock, although verbosity may be fetched + // safely using atomic.LoadInt32. + vmodule moduleSpec // The state of the -vmodule flag. + verbosity Level // V logging level, the value of the -v flag/ +} + +// buffer holds a byte Buffer for reuse. The zero value is ready for use. +type buffer struct { + bytes.Buffer + tmp [64]byte // temporary byte array for creating headers. + next *buffer +} + +var logging loggingT + +// setVState sets a consistent state for V logging. +// l.mu is held. +func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { + // Turn verbosity off so V will not fire while we are in transition. + logging.verbosity.set(0) + // Ditto for filter length. + atomic.StoreInt32(&logging.filterLength, 0) + + // Set the new filters and wipe the pc->Level map if the filter has changed. + if setFilter { + logging.vmodule.filter = filter + logging.vmap = make(map[uintptr]Level) + } + + // Things are consistent now, so enable filtering and verbosity. + // They are enabled in order opposite to that in V. + atomic.StoreInt32(&logging.filterLength, int32(len(filter))) + logging.verbosity.set(verbosity) +} + +// getBuffer returns a new, ready-to-use buffer. +func (l *loggingT) getBuffer() *buffer { + l.freeListMu.Lock() + b := l.freeList + if b != nil { + l.freeList = b.next + } + l.freeListMu.Unlock() + if b == nil { + b = new(buffer) + } else { + b.next = nil + b.Reset() + } + return b +} + +// putBuffer returns a buffer to the free list. +func (l *loggingT) putBuffer(b *buffer) { + if b.Len() >= 256 { + // Let big buffers die a natural death. + return + } + l.freeListMu.Lock() + b.next = l.freeList + l.freeList = b + l.freeListMu.Unlock() +} + +var timeNow = time.Now // Stubbed out for testing. + +/* +header formats a log header as defined by the C++ implementation. +It returns a buffer containing the formatted header and the user's file and line number. +The depth specifies how many stack frames above lives the source line to be identified in the log message. + +Log lines have this form: + Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +where the fields are defined as follows: + L A single character, representing the log level (eg 'I' for INFO) + mm The month (zero padded; ie May is '05') + dd The day (zero padded) + hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds + threadid The space-padded thread ID as returned by GetTID() + file The file name + line The line number + msg The user-supplied message +*/ +func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { + _, file, line, ok := runtime.Caller(3 + depth) + if !ok { + file = "???" + line = 1 + } else { + file = trimToImportPath(file) + for _, p := range trimPrefixes { + if strings.HasPrefix(file, p) { + file = file[len(p):] + break + } + } + file = file[1:] // drop '/' + } + return l.formatHeader(s, file, line), file, line +} + +// formatHeader formats a log header using the provided file name and line number. +func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { + now := timeNow() + if line < 0 { + line = 0 // not a real line number, but acceptable to someDigits + } + if s > fatalLog { + s = infoLog // for safety. + } + buf := l.getBuffer() + + // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. + // It's worth about 3X. Fprintf is hard. + _, month, day := now.Date() + hour, minute, second := now.Clock() + // Lmmdd hh:mm:ss.uuuuuu threadid file:line] + buf.tmp[0] = severityChar[s] + buf.twoDigits(1, int(month)) + buf.twoDigits(3, day) + buf.tmp[5] = ' ' + buf.twoDigits(6, hour) + buf.tmp[8] = ':' + buf.twoDigits(9, minute) + buf.tmp[11] = ':' + buf.twoDigits(12, second) + buf.tmp[14] = '.' + buf.nDigits(6, 15, now.Nanosecond()/1000, '0') + buf.tmp[21] = ' ' + buf.Write(buf.tmp[:22]) + buf.WriteString(file) + buf.tmp[0] = ':' + n := buf.someDigits(1, line) + buf.tmp[n+1] = ']' + buf.tmp[n+2] = ' ' + buf.Write(buf.tmp[:n+3]) + return buf +} + +// Some custom tiny helper functions to print the log header efficiently. + +const digits = "0123456789" + +// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. +func (buf *buffer) twoDigits(i, d int) { + buf.tmp[i+1] = digits[d%10] + d /= 10 + buf.tmp[i] = digits[d%10] +} + +// nDigits formats an n-digit integer at buf.tmp[i], +// padding with pad on the left. +// It assumes d >= 0. +func (buf *buffer) nDigits(n, i, d int, pad byte) { + j := n - 1 + for ; j >= 0 && d > 0; j-- { + buf.tmp[i+j] = digits[d%10] + d /= 10 + } + for ; j >= 0; j-- { + buf.tmp[i+j] = pad + } +} + +// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. +func (buf *buffer) someDigits(i, d int) int { + // Print into the top, then copy down. We know there's space for at least + // a 10-digit number. + j := len(buf.tmp) + for { + j-- + buf.tmp[j] = digits[d%10] + d /= 10 + if d == 0 { + break + } + } + return copy(buf.tmp[i:], buf.tmp[j:]) +} + +func (l *loggingT) println(s severity, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintln(buf, args...) + l.output(s, buf, file, line, false) +} + +func (l *loggingT) print(s severity, args ...interface{}) { + l.printDepth(s, 1, args...) +} + +func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { + buf, file, line := l.header(s, depth) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +func (l *loggingT) printfmt(s severity, format string, args ...interface{}) { + buf, file, line := l.header(s, 0) + fmt.Fprintf(buf, format, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, false) +} + +// printWithFileLine behaves like print but uses the provided file and line number. If +// alsoLogToStderr is true, the log message always appears on standard error; it +// will also appear in the log file unless --logtostderr is set. +func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { + buf := l.formatHeader(s, file, line) + fmt.Fprint(buf, args...) + if buf.Bytes()[buf.Len()-1] != '\n' { + buf.WriteByte('\n') + } + l.output(s, buf, file, line, alsoToStderr) +} + +// output writes the data to the log files and releases the buffer. +func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { + l.mu.Lock() + if l.traceLocation.isSet() { + if l.traceLocation.match(file, line) { + buf.Write(stacks(false)) + } + } + data := buf.Bytes() + if l.toStderr { + os.Stderr.Write(data) + } else { + if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { + os.Stderr.Write(data) + } + if l.file[s] == nil { + if err := l.createFiles(s); err != nil { + os.Stderr.Write(data) // Make sure the message appears somewhere. + l.exit(err) + } + } + switch s { + case fatalLog: + l.file[fatalLog].Write(data) + fallthrough + case errorLog: + l.file[errorLog].Write(data) + fallthrough + case warningLog: + l.file[warningLog].Write(data) + fallthrough + case infoLog: + l.file[infoLog].Write(data) + } + } + if s == fatalLog { + // If we got here via Exit rather than Fatal, print no stacks. + if atomic.LoadUint32(&fatalNoStacks) > 0 { + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(1) + } + // Dump all goroutine stacks before exiting. + // First, make sure we see the trace for the current goroutine on standard error. + // If -logtostderr has been specified, the loop below will do that anyway + // as the first stack in the full dump. + if !l.toStderr { + os.Stderr.Write(stacks(false)) + } + // Write the stack trace for all goroutines to the files. + trace := stacks(true) + logExitFunc = func(error) {} // If we get a write error, we'll still exit below. + for log := fatalLog; log >= infoLog; log-- { + if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. + f.Write(trace) + } + } + l.mu.Unlock() + timeoutFlush(10 * time.Second) + os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. + } + l.putBuffer(buf) + l.mu.Unlock() + if stats := severityStats[s]; stats != nil { + atomic.AddInt64(&stats.lines, 1) + atomic.AddInt64(&stats.bytes, int64(len(data))) + } +} + +// timeoutFlush calls Flush and returns when it completes or after timeout +// elapses, whichever happens first. This is needed because the hooks invoked +// by Flush may deadlock when glog.Fatal is called from a hook that holds +// a lock. +func timeoutFlush(timeout time.Duration) { + done := make(chan bool, 1) + go func() { + Flush() // calls logging.lockAndFlushAll() + done <- true + }() + select { + case <-done: + case <-time.After(timeout): + fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) + } +} + +// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. +func stacks(all bool) []byte { + // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. + n := 10000 + if all { + n = 100000 + } + var trace []byte + for i := 0; i < 5; i++ { + trace = make([]byte, n) + nbytes := runtime.Stack(trace, all) + if nbytes < len(trace) { + return trace[:nbytes] + } + n *= 2 + } + return trace +} + +// logExitFunc provides a simple mechanism to override the default behavior +// of exiting on error. Used in testing and to guarantee we reach a required exit +// for fatal logs. Instead, exit could be a function rather than a method but that +// would make its use clumsier. +var logExitFunc func(error) + +// exit is called if there is trouble creating or writing log files. +// It flushes the logs and exits the program; there's no point in hanging around. +// l.mu is held. +func (l *loggingT) exit(err error) { + fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) + // If logExitFunc is set, we do that instead of exiting. + if logExitFunc != nil { + logExitFunc(err) + return + } + l.flushAll() + os.Exit(2) +} + +// syncBuffer joins a bufio.Writer to its underlying file, providing access to the +// file's Sync method and providing a wrapper for the Write method that provides log +// file rotation. There are conflicting methods, so the file cannot be embedded. +// l.mu is held for all its methods. +type syncBuffer struct { + logger *loggingT + *bufio.Writer + file *os.File + sev severity + nbytes uint64 // The number of bytes written to this file +} + +func (sb *syncBuffer) Sync() error { + return sb.file.Sync() +} + +func (sb *syncBuffer) Write(p []byte) (n int, err error) { + if sb.nbytes+uint64(len(p)) >= MaxSize { + if err := sb.rotateFile(time.Now()); err != nil { + sb.logger.exit(err) + } + } + n, err = sb.Writer.Write(p) + sb.nbytes += uint64(n) + if err != nil { + sb.logger.exit(err) + } + return +} + +// rotateFile closes the syncBuffer's file and starts a new one. +func (sb *syncBuffer) rotateFile(now time.Time) error { + if sb.file != nil { + sb.Flush() + sb.file.Close() + } + var err error + sb.file, _, err = create(severityName[sb.sev], now) + sb.nbytes = 0 + if err != nil { + return err + } + + sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) + + // Write header. + var buf bytes.Buffer + fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) + fmt.Fprintf(&buf, "Running on machine: %s\n", host) + fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) + fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") + n, err := sb.file.Write(buf.Bytes()) + sb.nbytes += uint64(n) + return err +} + +// bufferSize sizes the buffer associated with each log file. It's large +// so that log records can accumulate without the logging thread blocking +// on disk I/O. The flushDaemon will block instead. +const bufferSize = 256 * 1024 + +// createFiles creates all the log files for severity from sev down to infoLog. +// l.mu is held. +func (l *loggingT) createFiles(sev severity) error { + now := time.Now() + // Files are created in decreasing severity order, so as soon as we find one + // has already been created, we can stop. + for s := sev; s >= infoLog && l.file[s] == nil; s-- { + sb := &syncBuffer{ + logger: l, + sev: s, + } + if err := sb.rotateFile(now); err != nil { + return err + } + l.file[s] = sb + } + return nil +} + +const flushInterval = 30 * time.Second + +// flushDaemon periodically flushes the log file buffers. +func (l *loggingT) flushDaemon() { + for range time.NewTicker(flushInterval).C { + l.lockAndFlushAll() + } +} + +// lockAndFlushAll is like flushAll but locks l.mu first. +func (l *loggingT) lockAndFlushAll() { + l.mu.Lock() + l.flushAll() + l.mu.Unlock() +} + +// flushAll flushes all the logs and attempts to "sync" their data to disk. +// l.mu is held. +func (l *loggingT) flushAll() { + // Flush from fatal down, in case there's trouble flushing. + for s := fatalLog; s >= infoLog; s-- { + file := l.file[s] + if file != nil { + file.Flush() // ignore error + file.Sync() // ignore error + } + } +} + +// CopyStandardLogTo arranges for messages written to the Go "log" package's +// default logs to also appear in the Google logs for the named and lower +// severities. Subsequent changes to the standard log's default output location +// or format may break this behavior. +// +// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not +// recognized, CopyStandardLogTo panics. +func CopyStandardLogTo(name string) { + sev, ok := severityByName(name) + if !ok { + panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) + } + // Set a log format that captures the user's file and line: + // d.go:23: message + stdLog.SetFlags(stdLog.Lshortfile) + stdLog.SetOutput(logBridge(sev)) +} + +// logBridge provides the Write method that enables CopyStandardLogTo to connect +// Go's standard logs to the logs provided by this package. +type logBridge severity + +// Write parses the standard logging line and passes its components to the +// logger for severity(lb). +func (lb logBridge) Write(b []byte) (n int, err error) { + var ( + file = "???" + line = 1 + text string + ) + // Split "d.go:23: message" into "d.go", "23", and "message". + if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { + text = fmt.Sprintf("bad log format: %s", b) + } else { + file = string(parts[0]) + text = string(parts[2][1:]) // skip leading space + line, err = strconv.Atoi(string(parts[1])) + if err != nil { + text = fmt.Sprintf("bad line number: %s", b) + line = 1 + } + } + // printWithFileLine with alsoToStderr=true, so standard log messages + // always appear on standard error. + logging.printWithFileLine(severity(lb), file, line, true, text) + return len(b), nil +} + +// setV computes and remembers the V level for a given PC +// when vmodule is enabled. +// File pattern matching takes the basename of the file, stripped +// of its .go suffix, and uses filepath.Match, which is a little more +// general than the *? matching used in C++. +// l.mu is held. +func (l *loggingT) setV(pc uintptr) Level { + fn := runtime.FuncForPC(pc) + file, _ := fn.FileLine(pc) + file = trimToImportPath(file) + for _, filter := range l.vmodule.filter { + if filter.pattern.MatchString(file) { + l.vmap[pc] = filter.level + return filter.level + } + } + l.vmap[pc] = 0 + return 0 +} + +// Verbose is a boolean type that implements Infof (like Printf) etc. +// See the documentation of V for more information. +type Verbose bool + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a boolean of type Verbose, which implements Info, Infoln +// and Infof. These methods will write to the Info log if called. +// Thus, one may write either +// if glog.V(2) { glog.Info("log this") } +// or +// glog.V(2).Info("log this") +// The second form is shorter but the first is cheaper if logging is off because it does +// not evaluate its arguments. +// +// Whether an individual call to V generates a log record depends on the setting of +// the -v and --vmodule flags; both are off by default. If the level in the call to +// V is at least the value of -v, or of -vmodule for the source file containing the +// call, the V call will log. +func V(level Level) Verbose { + // This function tries hard to be cheap unless there's work to do. + // The fast path is two atomic loads and compares. + + // Here is a cheap but safe test to see if V logging is enabled globally. + if logging.verbosity.get() >= level { + return Verbose(true) + } + + // It's off globally but it vmodule may still be set. + // Here is another cheap but safe test to see if vmodule is enabled. + if atomic.LoadInt32(&logging.filterLength) > 0 { + // Now we need a proper lock to use the logging structure. The pcs field + // is shared so we must lock before accessing it. This is fairly expensive, + // but if V logging is enabled we're slow anyway. + logging.mu.Lock() + defer logging.mu.Unlock() + if runtime.Callers(2, logging.pcs[:]) == 0 { + return Verbose(false) + } + v, ok := logging.vmap[logging.pcs[0]] + if !ok { + v = logging.setV(logging.pcs[0]) + } + return Verbose(v >= level) + } + return Verbose(false) +} + +// Info is equivalent to the global Info function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Info(args ...interface{}) { + if v { + logging.print(infoLog, args...) + } +} + +// Infoln is equivalent to the global Infoln function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infoln(args ...interface{}) { + if v { + logging.println(infoLog, args...) + } +} + +// Infof is equivalent to the global Infof function, guarded by the value of v. +// See the documentation of V for usage. +func (v Verbose) Infof(format string, args ...interface{}) { + if v { + logging.printfmt(infoLog, format, args...) + } +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Info(args ...interface{}) { + logging.print(infoLog, args...) +} + +// InfoDepth acts as Info but uses depth to determine which call frame to log. +// InfoDepth(0, "msg") is the same as Info("msg"). +func InfoDepth(depth int, args ...interface{}) { + logging.printDepth(infoLog, depth, args...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Infoln(args ...interface{}) { + logging.print(infoLog, args...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Infof(format string, args ...interface{}) { + logging.printfmt(infoLog, format, args...) +} + +// Warning logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Warning(args ...interface{}) { + logging.print(warningLog, args...) +} + +// WarningDepth acts as Warning but uses depth to determine which call frame to log. +// WarningDepth(0, "msg") is the same as Warning("msg"). +func WarningDepth(depth int, args ...interface{}) { + logging.printDepth(warningLog, depth, args...) +} + +// Warningln logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Warningln(args ...interface{}) { + logging.println(warningLog, args...) +} + +// Warningf logs to the WARNING and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Warningf(format string, args ...interface{}) { + logging.printfmt(warningLog, format, args...) +} + +// Error logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Error(args ...interface{}) { + logging.print(errorLog, args...) +} + +// ErrorDepth acts as Error but uses depth to determine which call frame to log. +// ErrorDepth(0, "msg") is the same as Error("msg"). +func ErrorDepth(depth int, args ...interface{}) { + logging.printDepth(errorLog, depth, args...) +} + +// Errorln logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Errorln(args ...interface{}) { + logging.println(errorLog, args...) +} + +// Errorf logs to the ERROR, WARNING, and INFO logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Errorf(format string, args ...interface{}) { + logging.printfmt(errorLog, format, args...) +} + +// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Fatal(args ...interface{}) { + logging.print(fatalLog, args...) +} + +// FatalDepth acts as Fatal but uses depth to determine which call frame to log. +// FatalDepth(0, "msg") is the same as Fatal("msg"). +func FatalDepth(depth int, args ...interface{}) { + logging.printDepth(fatalLog, depth, args...) +} + +// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Fatalln(args ...interface{}) { + logging.println(fatalLog, args...) +} + +// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, +// including a stack trace of all running goroutines, then calls os.Exit(255). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Fatalf(format string, args ...interface{}) { + logging.printfmt(fatalLog, format, args...) +} + +// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. +// It allows Exit and relatives to use the Fatal logs. +var fatalNoStacks uint32 + +// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Exit(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.print(fatalLog, args...) +} + +// ExitDepth acts as Exit but uses depth to determine which call frame to log. +// ExitDepth(0, "msg") is the same as Exit("msg"). +func ExitDepth(depth int, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printDepth(fatalLog, depth, args...) +} + +// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +func Exitln(args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.println(fatalLog, args...) +} + +// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Exitf(format string, args ...interface{}) { + atomic.StoreUint32(&fatalNoStacks, 1) + logging.printfmt(fatalLog, format, args...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_file.go b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_file.go new file mode 100644 index 0000000..2fc96eb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_file.go @@ -0,0 +1,128 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// File I/O for logs. + +package glog + +import ( + "errors" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + "sync" + "time" +) + +// MaxSize is the maximum size of a log file in bytes. +var MaxSize uint64 = 1024 * 1024 * 1800 + +// logDirs lists the candidate directories for new log files. +var logDirs []string + +// If non-empty, overrides the choice of directory in which to write logs. +// See createLogDirs for the full list of possible destinations. +//var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") +var logDir *string = new(string) + +func SetLogDir(str string) { + *logDir = str +} + +func createLogDirs() { + if *logDir != "" { + logDirs = append(logDirs, *logDir) + } + logDirs = append(logDirs, os.TempDir()) +} + +var ( + pid = os.Getpid() + program = filepath.Base(os.Args[0]) + host = "unknownhost" + userName = "unknownuser" +) + +func init() { + h, err := os.Hostname() + if err == nil { + host = shortHostname(h) + } + + current, err := user.Current() + if err == nil { + userName = current.Username + } + + // Sanitize userName since it may contain filepath separators on Windows. + userName = strings.Replace(userName, `\`, "_", -1) +} + +// shortHostname returns its argument, truncating at the first period. +// For instance, given "www.google.com" it returns "www". +func shortHostname(hostname string) string { + if i := strings.Index(hostname, "."); i >= 0 { + return hostname[:i] + } + return hostname +} + +// logName returns a new log file name containing tag, with start time t, and +// the name for the symlink for tag. +func logName(tag string, t time.Time) (name, link string) { + name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", + program, + host, + userName, + tag, + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + pid) + return name, program + "." + tag +} + +var onceLogDirs sync.Once + +// create creates a new log file and returns the file and its filename, which +// contains tag ("INFO", "FATAL", etc.) and t. If the file is created +// successfully, create also attempts to update the symlink for that tag, ignoring +// errors. +func create(tag string, t time.Time) (f *os.File, filename string, err error) { + onceLogDirs.Do(createLogDirs) + if len(logDirs) == 0 { + return nil, "", errors.New("log: no log dirs") + } + name, link := logName(tag, t) + var lastErr error + for _, dir := range logDirs { + fname := filepath.Join(dir, name) + f, err := os.Create(fname) + if err == nil { + symlink := filepath.Join(dir, link) + os.Remove(symlink) // ignore err + os.Symlink(name, symlink) // ignore err + return f, fname, nil + } + lastErr = err + } + return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) +} diff --git a/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_test.go b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_test.go new file mode 100644 index 0000000..b58f3d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/glog/glog_test.go @@ -0,0 +1,436 @@ +// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package glog + +import ( + "bytes" + "fmt" + stdLog "log" + "path/filepath" + "runtime" + "strconv" + "strings" + "testing" + "time" +) + +// Test that shortHostname works as advertised. +func TestShortHostname(t *testing.T) { + for hostname, expect := range map[string]string{ + "": "", + "host": "host", + "host.google.com": "host", + } { + if got := shortHostname(hostname); expect != got { + t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) + } + } +} + +// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. +type flushBuffer struct { + bytes.Buffer +} + +func (f *flushBuffer) Flush() error { + return nil +} + +func (f *flushBuffer) Sync() error { + return nil +} + +// swap sets the log writers and returns the old array. +func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { + l.mu.Lock() + defer l.mu.Unlock() + old = l.file + for i, w := range writers { + logging.file[i] = w + } + return +} + +// newBuffers sets the log writers to all new byte buffers and returns the old array. +func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { + return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) +} + +// contents returns the specified log value as a string. +func contents(s severity) string { + return logging.file[s].(*flushBuffer).String() +} + +// contains reports whether the string is contained in the log. +func contains(s severity, str string, t *testing.T) bool { + return strings.Contains(contents(s), str) +} + +// setFlags configures the logging flags how the test expects them. +func setFlags() { + logging.toStderr = false +} + +// Test that Info works as advertised. +func TestInfo(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + Info("test") + if !contains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", contents(infoLog)) + } + if !contains(infoLog, "test", t) { + t.Error("Info failed") + } +} + +func TestInfoDepth(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + + f := func() { InfoDepth(1, "depth-test1") } + + // The next three lines must stay together + _, _, wantLine, _ := runtime.Caller(0) + InfoDepth(0, "depth-test0") + f() + + msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") + if len(msgs) != 2 { + t.Fatalf("Got %d lines, expected 2", len(msgs)) + } + + for i, m := range msgs { + if !strings.HasPrefix(m, "I") { + t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) + } + w := fmt.Sprintf("depth-test%d", i) + if !strings.Contains(m, w) { + t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) + } + + // pull out the line number (between : and ]) + msg := m[strings.LastIndex(m, ":")+1:] + x := strings.Index(msg, "]") + if x < 0 { + t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) + continue + } + line, err := strconv.Atoi(msg[:x]) + if err != nil { + t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) + continue + } + wantLine++ + if wantLine != line { + t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) + } + } +} + +func init() { + CopyStandardLogTo("INFO") +} + +// Test that CopyStandardLogTo panics on bad input. +func TestCopyStandardLogToPanic(t *testing.T) { + defer func() { + if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { + t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) + } + }() + CopyStandardLogTo("LOG") +} + +// Test that using the standard log package logs to INFO. +func TestStandardLog(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + stdLog.Print("test") + if !contains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", contents(infoLog)) + } + if !contains(infoLog, "test", t) { + t.Error("Info failed") + } +} + +// Test that the header has the correct format. +func TestHeader(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + defer func(previous func() time.Time) { timeNow = previous }(timeNow) + timeNow = func() time.Time { + return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) + } + pid = 1234 + Info("test") + var line int + format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" + n, err := fmt.Sscanf(contents(infoLog), format, &line) + if n != 1 || err != nil { + t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) + } + // Scanf treats multiple spaces as equivalent to a single space, + // so check for correct space-padding also. + want := fmt.Sprintf(format, line) + if contents(infoLog) != want { + t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) + } +} + +// Test that an Error log goes to Warning and Info. +// Even in the Info log, the source character will be E, so the data should +// all be identical. +func TestError(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + Error("test") + if !contains(errorLog, "E", t) { + t.Errorf("Error has wrong character: %q", contents(errorLog)) + } + if !contains(errorLog, "test", t) { + t.Error("Error failed") + } + str := contents(errorLog) + if !contains(warningLog, str, t) { + t.Error("Warning failed") + } + if !contains(infoLog, str, t) { + t.Error("Info failed") + } +} + +// Test that a Warning log goes to Info. +// Even in the Info log, the source character will be W, so the data should +// all be identical. +func TestWarning(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + Warning("test") + if !contains(warningLog, "W", t) { + t.Errorf("Warning has wrong character: %q", contents(warningLog)) + } + if !contains(warningLog, "test", t) { + t.Error("Warning failed") + } + str := contents(warningLog) + if !contains(infoLog, str, t) { + t.Error("Info failed") + } +} + +// Test that a V log goes to Info. +func TestV(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + logging.verbosity.Set("2") + defer logging.verbosity.Set("0") + V(2).Info("test") + if !contains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", contents(infoLog)) + } + if !contains(infoLog, "test", t) { + t.Error("Info failed") + } +} + +// Test that a vmodule enables a log in this file. +func TestVmoduleOn(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + logging.vmodule.Set("glog_test.go=2") + defer logging.vmodule.Set("") + if !V(1) { + t.Error("V not enabled for 1") + } + if !V(2) { + t.Error("V not enabled for 2") + } + if V(3) { + t.Error("V enabled for 3") + } + V(2).Info("test") + if !contains(infoLog, "I", t) { + t.Errorf("Info has wrong character: %q", contents(infoLog)) + } + if !contains(infoLog, "test", t) { + t.Error("Info failed") + } +} + +// Test that a vmodule of another file does not enable a log in this file. +func TestVmoduleOff(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + logging.vmodule.Set("notthisfile=2") + defer logging.vmodule.Set("") + for i := 1; i <= 3; i++ { + if V(Level(i)) { + t.Errorf("V enabled for %d", i) + } + } + V(2).Info("test") + if contents(infoLog) != "" { + t.Error("V logged incorrectly") + } +} + +var patternTests = []struct{ input, want string }{ + {"foo/bar/x.go", ".*/foo/bar/x\\.go$"}, + {"foo/*/x.go", ".*/foo(/.*)?/x\\.go$"}, + {"foo/*", ".*/foo(/.*)?/[^/]+\\.go$"}, +} + +func TestCompileModulePattern(t *testing.T) { + for _, test := range patternTests { + re, err := compileModulePattern(test.input) + if err != nil { + t.Fatalf("%s: %v", test.input, err) + } + if re.String() != test.want { + t.Errorf("mismatch for %q: got %q, want %q", test.input, re.String(), test.want) + } + } +} + +// vGlobs are patterns that match/don't match this file at V=2. +var vGlobs = map[string]bool{ + // Easy to test the numeric match here. + "glog_test.go=1": false, // If -vmodule sets V to 1, V(2) will fail. + "glog_test.go=2": true, + "glog_test.go=3": true, // If -vmodule sets V to 1, V(3) will succeed. + + // Import path prefix matching + "logger/glog=1": false, + "logger/glog=2": true, + "logger/glog=3": true, + + // Import path glob matching + "logger/*=1": false, + "logger/*=2": true, + "logger/*=3": true, + + // These all use 2 and check the patterns. + "*=2": true, +} + +// Test that vmodule globbing works as advertised. +func testVmoduleGlob(pat string, match bool, t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + defer logging.vmodule.Set("") + logging.vmodule.Set(pat) + if V(2) != Verbose(match) { + t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) + } +} + +// Test that a vmodule globbing works as advertised. +func TestVmoduleGlob(t *testing.T) { + for glob, match := range vGlobs { + testVmoduleGlob(glob, match, t) + } +} + +func TestRollover(t *testing.T) { + setFlags() + var err error + defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) + logExitFunc = func(e error) { + err = e + } + defer func(previous uint64) { MaxSize = previous }(MaxSize) + MaxSize = 512 + + Info("x") // Be sure we have a file. + info, ok := logging.file[infoLog].(*syncBuffer) + if !ok { + t.Fatal("info wasn't created") + } + if err != nil { + t.Fatalf("info has initial error: %v", err) + } + fname0 := info.file.Name() + Info(strings.Repeat("x", int(MaxSize))) // force a rollover + if err != nil { + t.Fatalf("info has error after big write: %v", err) + } + + // Make sure the next log file gets a file name with a different + // time stamp. + // + // TODO: determine whether we need to support subsecond log + // rotation. C++ does not appear to handle this case (nor does it + // handle Daylight Savings Time properly). + time.Sleep(1 * time.Second) + + Info("x") // create a new file + if err != nil { + t.Fatalf("error after rotation: %v", err) + } + fname1 := info.file.Name() + if fname0 == fname1 { + t.Errorf("info.f.Name did not change: %v", fname0) + } + if info.nbytes >= MaxSize { + t.Errorf("file size was not reset: %d", info.nbytes) + } +} + +func TestLogBacktraceAt(t *testing.T) { + setFlags() + defer logging.swap(logging.newBuffers()) + // The peculiar style of this code simplifies line counting and maintenance of the + // tracing block below. + var infoLine string + setTraceLocation := func(file string, line int, ok bool, delta int) { + if !ok { + t.Fatal("could not get file:line") + } + _, file = filepath.Split(file) + infoLine = fmt.Sprintf("%s:%d", file, line+delta) + err := logging.traceLocation.Set(infoLine) + if err != nil { + t.Fatal("error setting log_backtrace_at: ", err) + } + } + { + // Start of tracing block. These lines know about each other's relative position. + _, file, line, ok := runtime.Caller(0) + setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. + Info("we want a stack trace here") + } + numAppearances := strings.Count(contents(infoLog), infoLine) + if numAppearances < 2 { + // Need 2 appearances, one in the log header and one in the trace: + // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here + // ... + // github.com/glog/glog_test.go:280 (0x41ba91) + // ... + // We could be more precise but that would require knowing the details + // of the traceback format, which may not be dependable. + t.Fatal("got no trace back; log is ", contents(infoLog)) + } +} + +func BenchmarkHeader(b *testing.B) { + for i := 0; i < b.N; i++ { + buf, _, _ := logging.header(infoLog, 0) + logging.putBuffer(buf) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/logger/verbosity.go b/vendor/github.com/ethereum/go-ethereum/logger/verbosity.go new file mode 100644 index 0000000..0771c50 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/logger/verbosity.go @@ -0,0 +1,27 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package logger + +const ( + Error = iota + 1 + Warn + Info + Debug + Detail + + Ridiculousness = 100 +) diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/disk.go b/vendor/github.com/ethereum/go-ethereum/metrics/disk.go new file mode 100644 index 0000000..25142d2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/metrics/disk.go @@ -0,0 +1,25 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package metrics + +// DiskStats is the per process disk io stats. +type DiskStats struct { + ReadCount int64 // Number of read operations executed + ReadBytes int64 // Total number of bytes read + WriteCount int64 // Number of write operations executed + WriteBytes int64 // Total number of byte written +} diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/disk_linux.go b/vendor/github.com/ethereum/go-ethereum/metrics/disk_linux.go new file mode 100644 index 0000000..8d610cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/metrics/disk_linux.go @@ -0,0 +1,72 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the Linux implementation of process disk IO counter retrieval. + +package metrics + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +// ReadDiskStats retrieves the disk IO stats belonging to the current process. +func ReadDiskStats(stats *DiskStats) error { + // Open the process disk IO counter file + inf, err := os.Open(fmt.Sprintf("/proc/%d/io", os.Getpid())) + if err != nil { + return err + } + defer inf.Close() + in := bufio.NewReader(inf) + + // Iterate over the IO counter, and extract what we need + for { + // Read the next line and split to key and value + line, err := in.ReadString('\n') + if err != nil { + if err == io.EOF { + return nil + } + return err + } + parts := strings.Split(line, ":") + if len(parts) != 2 { + continue + } + key := strings.TrimSpace(parts[0]) + value, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64) + if err != nil { + return err + } + + // Update the counter based on the key + switch key { + case "syscr": + stats.ReadCount = value + case "syscw": + stats.WriteCount = value + case "rchar": + stats.ReadBytes = value + case "wchar": + stats.WriteBytes = value + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/disk_nop.go b/vendor/github.com/ethereum/go-ethereum/metrics/disk_nop.go new file mode 100644 index 0000000..4319f8b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/metrics/disk_nop.go @@ -0,0 +1,26 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !linux + +package metrics + +import "errors" + +// ReadDiskStats retrieves the disk IO stats belonging to the current process. +func ReadDiskStats(stats *DiskStats) error { + return errors.New("Not implemented") +} diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go new file mode 100644 index 0000000..d756894 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/metrics/metrics.go @@ -0,0 +1,123 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package metrics provides general system and process level metrics collection. +package metrics + +import ( + "os" + "runtime" + "strings" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/rcrowley/go-metrics" + "github.com/rcrowley/go-metrics/exp" +) + +// MetricsEnabledFlag is the CLI flag name to use to enable metrics collections. +var MetricsEnabledFlag = "metrics" + +// Enabled is the flag specifying if metrics are enable or not. +var Enabled = false + +// Init enables or disables the metrics system. Since we need this to run before +// any other code gets to create meters and timers, we'll actually do an ugly hack +// and peek into the command line args for the metrics flag. +func init() { + for _, arg := range os.Args { + if strings.TrimLeft(arg, "-") == MetricsEnabledFlag { + glog.V(logger.Info).Infof("Enabling metrics collection") + Enabled = true + } + } + exp.Exp(metrics.DefaultRegistry) +} + +// NewCounter create a new metrics Counter, either a real one of a NOP stub depending +// on the metrics flag. +func NewCounter(name string) metrics.Counter { + if !Enabled { + return new(metrics.NilCounter) + } + return metrics.GetOrRegisterCounter(name, metrics.DefaultRegistry) +} + +// NewMeter create a new metrics Meter, either a real one of a NOP stub depending +// on the metrics flag. +func NewMeter(name string) metrics.Meter { + if !Enabled { + return new(metrics.NilMeter) + } + return metrics.GetOrRegisterMeter(name, metrics.DefaultRegistry) +} + +// NewTimer create a new metrics Timer, either a real one of a NOP stub depending +// on the metrics flag. +func NewTimer(name string) metrics.Timer { + if !Enabled { + return new(metrics.NilTimer) + } + return metrics.GetOrRegisterTimer(name, metrics.DefaultRegistry) +} + +// CollectProcessMetrics periodically collects various metrics about the running +// process. +func CollectProcessMetrics(refresh time.Duration) { + // Short circuit if the metrics system is disabled + if !Enabled { + return + } + // Create the various data collectors + memstats := make([]*runtime.MemStats, 2) + diskstats := make([]*DiskStats, 2) + for i := 0; i < len(memstats); i++ { + memstats[i] = new(runtime.MemStats) + diskstats[i] = new(DiskStats) + } + // Define the various metrics to collect + memAllocs := metrics.GetOrRegisterMeter("system/memory/allocs", metrics.DefaultRegistry) + memFrees := metrics.GetOrRegisterMeter("system/memory/frees", metrics.DefaultRegistry) + memInuse := metrics.GetOrRegisterMeter("system/memory/inuse", metrics.DefaultRegistry) + memPauses := metrics.GetOrRegisterMeter("system/memory/pauses", metrics.DefaultRegistry) + + var diskReads, diskReadBytes, diskWrites, diskWriteBytes metrics.Meter + if err := ReadDiskStats(diskstats[0]); err == nil { + diskReads = metrics.GetOrRegisterMeter("system/disk/readcount", metrics.DefaultRegistry) + diskReadBytes = metrics.GetOrRegisterMeter("system/disk/readdata", metrics.DefaultRegistry) + diskWrites = metrics.GetOrRegisterMeter("system/disk/writecount", metrics.DefaultRegistry) + diskWriteBytes = metrics.GetOrRegisterMeter("system/disk/writedata", metrics.DefaultRegistry) + } else { + glog.V(logger.Debug).Infof("failed to read disk metrics: %v", err) + } + // Iterate loading the different stats and updating the meters + for i := 1; ; i++ { + runtime.ReadMemStats(memstats[i%2]) + memAllocs.Mark(int64(memstats[i%2].Mallocs - memstats[(i-1)%2].Mallocs)) + memFrees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees)) + memInuse.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc)) + memPauses.Mark(int64(memstats[i%2].PauseTotalNs - memstats[(i-1)%2].PauseTotalNs)) + + if ReadDiskStats(diskstats[i%2]) == nil { + diskReads.Mark(int64(diskstats[i%2].ReadCount - diskstats[(i-1)%2].ReadCount)) + diskReadBytes.Mark(int64(diskstats[i%2].ReadBytes - diskstats[(i-1)%2].ReadBytes)) + diskWrites.Mark(int64(diskstats[i%2].WriteCount - diskstats[(i-1)%2].WriteCount)) + diskWriteBytes.Mark(int64(diskstats[i%2].WriteBytes - diskstats[(i-1)%2].WriteBytes)) + } + time.Sleep(refresh) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/agent.go b/vendor/github.com/ethereum/go-ethereum/miner/agent.go new file mode 100644 index 0000000..697e397 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/agent.go @@ -0,0 +1,125 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "sync" + + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/pow" +) + +type CpuAgent struct { + mu sync.Mutex + + workCh chan *Work + quit chan struct{} + quitCurrentOp chan struct{} + returnCh chan<- *Result + + index int + pow pow.PoW + + isMining int32 // isMining indicates whether the agent is currently mining +} + +func NewCpuAgent(index int, pow pow.PoW) *CpuAgent { + miner := &CpuAgent{ + pow: pow, + index: index, + quit: make(chan struct{}), + workCh: make(chan *Work, 1), + } + + return miner +} + +func (self *CpuAgent) Work() chan<- *Work { return self.workCh } +func (self *CpuAgent) Pow() pow.PoW { return self.pow } +func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } + +func (self *CpuAgent) Stop() { + close(self.quit) +} + +func (self *CpuAgent) Start() { + + if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) { + return // agent already started + } + + go self.update() +} + +func (self *CpuAgent) update() { +out: + for { + select { + case work := <-self.workCh: + self.mu.Lock() + if self.quitCurrentOp != nil { + close(self.quitCurrentOp) + } + self.quitCurrentOp = make(chan struct{}) + go self.mine(work, self.quitCurrentOp) + self.mu.Unlock() + case <-self.quit: + self.mu.Lock() + if self.quitCurrentOp != nil { + close(self.quitCurrentOp) + self.quitCurrentOp = nil + } + self.mu.Unlock() + break out + } + } + +done: + // Empty work channel + for { + select { + case <-self.workCh: + default: + close(self.workCh) + break done + } + } + + atomic.StoreInt32(&self.isMining, 0) +} + +func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) { + glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index) + + // Mine + nonce, mixDigest := self.pow.Search(work.Block, stop, self.index) + if nonce != 0 { + block := work.Block.WithMiningResult(types.EncodeNonce(nonce), common.BytesToHash(mixDigest)) + self.returnCh <- &Result{work, block} + } else { + self.returnCh <- nil + } +} + +func (self *CpuAgent) GetHashRate() int64 { + return self.pow.GetHashrate() +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/miner.go b/vendor/github.com/ethereum/go-ethereum/miner/miner.go new file mode 100644 index 0000000..83059f4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/miner.go @@ -0,0 +1,198 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package miner implements Ethereum block creation and mining. +package miner + +import ( + "fmt" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" +) + +// Backend wraps all methods required for mining. +type Backend interface { + AccountManager() *accounts.Manager + BlockChain() *core.BlockChain + TxPool() *core.TxPool + ChainDb() ethdb.Database +} + +// Miner creates blocks and searches for proof-of-work values. +type Miner struct { + mux *event.TypeMux + + worker *worker + + threads int + coinbase common.Address + mining int32 + eth Backend + pow pow.PoW + + canStart int32 // can start indicates whether we can start the mining operation + shouldStart int32 // should start indicates whether we should start after sync +} + +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { + miner := &Miner{ + eth: eth, + mux: mux, + pow: pow, + worker: newWorker(config, common.Address{}, eth, mux), + canStart: 1, + } + go miner.update() + + return miner +} + +// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. +// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and +// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks +// and halt your mining operation for as long as the DOS continues. +func (self *Miner) update() { + events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) +out: + for ev := range events.Chan() { + switch ev.Data.(type) { + case downloader.StartEvent: + atomic.StoreInt32(&self.canStart, 0) + if self.Mining() { + self.Stop() + atomic.StoreInt32(&self.shouldStart, 1) + glog.V(logger.Info).Infoln("Mining operation aborted due to sync operation") + } + case downloader.DoneEvent, downloader.FailedEvent: + shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 + + atomic.StoreInt32(&self.canStart, 1) + atomic.StoreInt32(&self.shouldStart, 0) + if shouldStart { + self.Start(self.coinbase, self.threads) + } + // unsubscribe. we're only interested in this event once + events.Unsubscribe() + // stop immediately and ignore all further pending events + break out + } + } +} + +func (m *Miner) GasPrice() *big.Int { + return new(big.Int).Set(m.worker.gasPrice) +} + +func (m *Miner) SetGasPrice(price *big.Int) { + // FIXME block tests set a nil gas price. Quick dirty fix + if price == nil { + return + } + m.worker.setGasPrice(price) +} + +func (self *Miner) Start(coinbase common.Address, threads int) { + atomic.StoreInt32(&self.shouldStart, 1) + self.worker.setEtherbase(coinbase) + self.coinbase = coinbase + self.threads = threads + + if atomic.LoadInt32(&self.canStart) == 0 { + glog.V(logger.Info).Infoln("Can not start mining operation due to network sync (starts when finished)") + return + } + atomic.StoreInt32(&self.mining, 1) + + for i := 0; i < threads; i++ { + self.worker.register(NewCpuAgent(i, self.pow)) + } + + glog.V(logger.Info).Infof("Starting mining operation (CPU=%d TOT=%d)\n", threads, len(self.worker.agents)) + self.worker.start() + self.worker.commitNewWork() +} + +func (self *Miner) Stop() { + self.worker.stop() + atomic.StoreInt32(&self.mining, 0) + atomic.StoreInt32(&self.shouldStart, 0) +} + +func (self *Miner) Register(agent Agent) { + if self.Mining() { + agent.Start() + } + self.worker.register(agent) +} + +func (self *Miner) Unregister(agent Agent) { + self.worker.unregister(agent) +} + +func (self *Miner) Mining() bool { + return atomic.LoadInt32(&self.mining) > 0 +} + +func (self *Miner) HashRate() (tot int64) { + tot += self.pow.GetHashrate() + // do we care this might race? is it worth we're rewriting some + // aspects of the worker/locking up agents so we can get an accurate + // hashrate? + for agent := range self.worker.agents { + tot += agent.GetHashRate() + } + return +} + +func (self *Miner) SetExtra(extra []byte) error { + if uint64(len(extra)) > params.MaximumExtraDataSize { + return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) + } + self.worker.setExtra(extra) + return nil +} + +// Pending returns the currently pending block and associated state. +func (self *Miner) Pending() (*types.Block, *state.StateDB) { + return self.worker.pending() +} + +// PendingBlock returns the currently pending block. +// +// Note, to access both the pending block and the pending state +// simultaneously, please use Pending(), as the pending state can +// change between multiple method calls +func (self *Miner) PendingBlock() *types.Block { + return self.worker.pendingBlock() +} + +func (self *Miner) SetEtherbase(addr common.Address) { + self.coinbase = addr + self.worker.setEtherbase(addr) +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/remote_agent.go b/vendor/github.com/ethereum/go-ethereum/miner/remote_agent.go new file mode 100644 index 0000000..23277ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/remote_agent.go @@ -0,0 +1,195 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "errors" + "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/pow" +) + +type hashrate struct { + ping time.Time + rate uint64 +} + +type RemoteAgent struct { + mu sync.Mutex + + quitCh chan struct{} + workCh chan *Work + returnCh chan<- *Result + + pow pow.PoW + currentWork *Work + work map[common.Hash]*Work + + hashrateMu sync.RWMutex + hashrate map[common.Hash]hashrate + + running int32 // running indicates whether the agent is active. Call atomically +} + +func NewRemoteAgent(pow pow.PoW) *RemoteAgent { + return &RemoteAgent{ + pow: pow, + work: make(map[common.Hash]*Work), + hashrate: make(map[common.Hash]hashrate), + } +} + +func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) { + a.hashrateMu.Lock() + defer a.hashrateMu.Unlock() + + a.hashrate[id] = hashrate{time.Now(), rate} +} + +func (a *RemoteAgent) Work() chan<- *Work { + return a.workCh +} + +func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) { + a.returnCh = returnCh +} + +func (a *RemoteAgent) Start() { + if !atomic.CompareAndSwapInt32(&a.running, 0, 1) { + return + } + a.quitCh = make(chan struct{}) + a.workCh = make(chan *Work, 1) + go a.loop(a.workCh, a.quitCh) +} + +func (a *RemoteAgent) Stop() { + if !atomic.CompareAndSwapInt32(&a.running, 1, 0) { + return + } + close(a.quitCh) + close(a.workCh) +} + +// GetHashRate returns the accumulated hashrate of all identifier combined +func (a *RemoteAgent) GetHashRate() (tot int64) { + a.hashrateMu.RLock() + defer a.hashrateMu.RUnlock() + + // this could overflow + for _, hashrate := range a.hashrate { + tot += int64(hashrate.rate) + } + return +} + +func (a *RemoteAgent) GetWork() ([3]string, error) { + a.mu.Lock() + defer a.mu.Unlock() + + var res [3]string + + if a.currentWork != nil { + block := a.currentWork.Block + + res[0] = block.HashNoNonce().Hex() + seedHash, _ := ethash.GetSeedHash(block.NumberU64()) + res[1] = common.BytesToHash(seedHash).Hex() + // Calculate the "target" to be returned to the external miner + n := big.NewInt(1) + n.Lsh(n, 255) + n.Div(n, block.Difficulty()) + n.Lsh(n, 1) + res[2] = common.BytesToHash(n.Bytes()).Hex() + + a.work[block.HashNoNonce()] = a.currentWork + return res, nil + } + return res, errors.New("No work available yet, don't panic.") +} + +// SubmitWork tries to inject a PoW solution tinto the remote agent, returning +// whether the solution was acceted or not (not can be both a bad PoW as well as +// any other error, like no work pending). +func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { + a.mu.Lock() + defer a.mu.Unlock() + + // Make sure the work submitted is present + work := a.work[hash] + if work == nil { + glog.V(logger.Info).Infof("Work was submitted for %x but no pending work found", hash) + return false + } + // Make sure the PoW solutions is indeed valid + block := work.Block.WithMiningResult(nonce, mixDigest) + if !a.pow.Verify(block) { + glog.V(logger.Warn).Infof("Invalid PoW submitted for %x", hash) + return false + } + // Solutions seems to be valid, return to the miner and notify acceptance + a.returnCh <- &Result{work, block} + delete(a.work, hash) + + return true +} + +// loop monitors mining events on the work and quit channels, updating the internal +// state of the rmeote miner until a termination is requested. +// +// Note, the reason the work and quit channels are passed as parameters is because +// RemoteAgent.Start() constantly recreates these channels, so the loop code cannot +// assume data stability in these member fields. +func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) { + ticker := time.Tick(5 * time.Second) + + for { + select { + case <-quitCh: + return + case work := <-workCh: + a.mu.Lock() + a.currentWork = work + a.mu.Unlock() + case <-ticker: + // cleanup + a.mu.Lock() + for hash, work := range a.work { + if time.Since(work.createdAt) > 7*(12*time.Second) { + delete(a.work, hash) + } + } + a.mu.Unlock() + + a.hashrateMu.Lock() + for id, hashrate := range a.hashrate { + if time.Since(hashrate.ping) > 10*time.Second { + delete(a.hashrate, id) + } + } + a.hashrateMu.Unlock() + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed.go b/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed.go new file mode 100644 index 0000000..86a30de --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed.go @@ -0,0 +1,118 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "container/ring" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// headerRetriever is used by the unconfirmed block set to verify whether a previously +// mined block is part of the canonical chain or not. +type headerRetriever interface { + // GetHeaderByNumber retrieves the canonical header associated with a block number. + GetHeaderByNumber(number uint64) *types.Header +} + +// unconfirmedBlock is a small collection of metadata about a locally mined block +// that is placed into a unconfirmed set for canonical chain inclusion tracking. +type unconfirmedBlock struct { + index uint64 + hash common.Hash +} + +// unconfirmedBlocks implements a data structure to maintain locally mined blocks +// have have not yet reached enough maturity to guarantee chain inclusion. It is +// used by the miner to provide logs to the user when a previously mined block +// has a high enough guarantee to not be reorged out of te canonical chain. +type unconfirmedBlocks struct { + chain headerRetriever // Blockchain to verify canonical status through + depth uint // Depth after which to discard previous blocks + blocks *ring.Ring // Block infos to allow canonical chain cross checks + lock sync.RWMutex // Protects the fields from concurrent access +} + +// newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. +func newUnconfirmedBlocks(chain headerRetriever, depth uint) *unconfirmedBlocks { + return &unconfirmedBlocks{ + chain: chain, + depth: depth, + } +} + +// Insert adds a new block to the set of unconfirmed ones. +func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { + // If a new block was mined locally, shift out any old enough blocks + set.Shift(index) + + // Create the new item as its own ring + item := ring.New(1) + item.Value = &unconfirmedBlock{ + index: index, + hash: hash, + } + // Set as the initial ring or append to the end + set.lock.Lock() + defer set.lock.Unlock() + + if set.blocks == nil { + set.blocks = item + } else { + set.blocks.Move(-1).Link(item) + } + // Display a log for the user to notify of a new mined block unconfirmed + glog.V(logger.Info).Infof("🔨 mined potential block #%d [%x…], waiting for %d blocks to confirm", index, hash.Bytes()[:4], set.depth) +} + +// Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth +// allowance, checking them against the canonical chain for inclusion or staleness +// report. +func (set *unconfirmedBlocks) Shift(height uint64) { + set.lock.Lock() + defer set.lock.Unlock() + + for set.blocks != nil { + // Retrieve the next unconfirmed block and abort if too fresh + next := set.blocks.Value.(*unconfirmedBlock) + if next.index+uint64(set.depth) > height { + break + } + // Block seems to exceed depth allowance, check for canonical status + header := set.chain.GetHeaderByNumber(next.index) + switch { + case header == nil: + glog.V(logger.Warn).Infof("failed to retrieve header of mined block #%d [%x…]", next.index, next.hash.Bytes()[:4]) + case header.Hash() == next.hash: + glog.V(logger.Info).Infof("🔗 mined block #%d [%x…] reached canonical chain", next.index, next.hash.Bytes()[:4]) + default: + glog.V(logger.Info).Infof("⑂ mined block #%d [%x…] became a side fork", next.index, next.hash.Bytes()[:4]) + } + // Drop the block out of the ring + if set.blocks.Value == set.blocks.Next().Value { + set.blocks = nil + } else { + set.blocks = set.blocks.Move(-1) + set.blocks.Unlink(1) + set.blocks = set.blocks.Move(1) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed_test.go b/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed_test.go new file mode 100644 index 0000000..456af17 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/unconfirmed_test.go @@ -0,0 +1,85 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// noopHeaderRetriever is an implementation of headerRetriever that always +// returns nil for any requested headers. +type noopHeaderRetriever struct{} + +func (r *noopHeaderRetriever) GetHeaderByNumber(number uint64) *types.Header { + return nil +} + +// Tests that inserting blocks into the unconfirmed set accumulates them until +// the desired depth is reached, after which they begin to be dropped. +func TestUnconfirmedInsertBounds(t *testing.T) { + limit := uint(10) + + pool := newUnconfirmedBlocks(new(noopHeaderRetriever), limit) + for depth := uint64(0); depth < 2*uint64(limit); depth++ { + // Insert multiple blocks for the same level just to stress it + for i := 0; i < int(depth); i++ { + pool.Insert(depth, common.Hash([32]byte{byte(depth), byte(i)})) + } + // Validate that no blocks below the depth allowance are left in + pool.blocks.Do(func(block interface{}) { + if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth { + t.Errorf("depth %d: block %x not dropped", depth, block.hash) + } + }) + } +} + +// Tests that shifting blocks out of the unconfirmed set works both for normal +// cases as well as for corner cases such as empty sets, empty shifts or full +// shifts. +func TestUnconfirmedShifts(t *testing.T) { + // Create a pool with a few blocks on various depths + limit, start := uint(10), uint64(25) + + pool := newUnconfirmedBlocks(new(noopHeaderRetriever), limit) + for depth := start; depth < start+uint64(limit); depth++ { + pool.Insert(depth, common.Hash([32]byte{byte(depth)})) + } + // Try to shift below the limit and ensure no blocks are dropped + pool.Shift(start + uint64(limit) - 1) + if n := pool.blocks.Len(); n != int(limit) { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit) + } + // Try to shift half the blocks out and verify remainder + pool.Shift(start + uint64(limit) - 1 + uint64(limit/2)) + if n := pool.blocks.Len(); n != int(limit)/2 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) + } + // Try to shift all the remaining blocks out and verify emptyness + pool.Shift(start + 2*uint64(limit)) + if n := pool.blocks.Len(); n != 0 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) + } + // Try to shift out from the empty set and make sure it doesn't break + pool.Shift(start + 3*uint64(limit)) + if n := pool.blocks.Len(); n != 0 { + t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go new file mode 100644 index 0000000..ef64c8f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -0,0 +1,651 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "bytes" + "fmt" + "math/big" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/pow" + "gopkg.in/fatih/set.v0" +) + +const ( + resultQueueSize = 10 + miningLogAtDepth = 5 +) + +// Agent can register themself with the worker +type Agent interface { + Work() chan<- *Work + SetReturnCh(chan<- *Result) + Stop() + Start() + GetHashRate() int64 +} + +// Work is the workers current environment and holds +// all of the current state information +type Work struct { + config *params.ChainConfig + signer types.Signer + + state *state.StateDB // apply state changes here + ancestors *set.Set // ancestor set (used for checking uncle parent validity) + family *set.Set // family set (used for checking uncle invalidity) + uncles *set.Set // uncle set + tcount int // tx count in cycle + ownedAccounts *set.Set + lowGasTxs types.Transactions + failedTxs types.Transactions + + Block *types.Block // the new block + + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt + + createdAt time.Time +} + +type Result struct { + Work *Work + Block *types.Block +} + +// worker is the main object which takes care of applying messages to the new state +type worker struct { + config *params.ChainConfig + + mu sync.Mutex + + // update loop + mux *event.TypeMux + events *event.TypeMuxSubscription + wg sync.WaitGroup + + agents map[Agent]struct{} + recv chan *Result + pow pow.PoW + + eth Backend + chain *core.BlockChain + proc core.Validator + chainDb ethdb.Database + + coinbase common.Address + gasPrice *big.Int + extra []byte + + currentMu sync.Mutex + current *Work + + uncleMu sync.Mutex + possibleUncles map[common.Hash]*types.Block + + txQueueMu sync.Mutex + txQueue map[common.Hash]*types.Transaction + + unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations + + // atomic status counters + mining int32 + atWork int32 + + fullValidation bool +} + +func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { + worker := &worker{ + config: config, + eth: eth, + mux: mux, + chainDb: eth.ChainDb(), + recv: make(chan *Result, resultQueueSize), + gasPrice: new(big.Int), + chain: eth.BlockChain(), + proc: eth.BlockChain().Validator(), + possibleUncles: make(map[common.Hash]*types.Block), + coinbase: coinbase, + txQueue: make(map[common.Hash]*types.Transaction), + agents: make(map[Agent]struct{}), + unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), 5), + fullValidation: false, + } + worker.events = worker.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{}) + go worker.update() + + go worker.wait() + worker.commitNewWork() + + return worker +} + +func (self *worker) setEtherbase(addr common.Address) { + self.mu.Lock() + defer self.mu.Unlock() + self.coinbase = addr +} + +func (self *worker) setExtra(extra []byte) { + self.mu.Lock() + defer self.mu.Unlock() + self.extra = extra +} + +func (self *worker) pending() (*types.Block, *state.StateDB) { + self.currentMu.Lock() + defer self.currentMu.Unlock() + + if atomic.LoadInt32(&self.mining) == 0 { + return types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ), self.current.state.Copy() + } + return self.current.Block, self.current.state.Copy() +} + +func (self *worker) pendingBlock() *types.Block { + self.currentMu.Lock() + defer self.currentMu.Unlock() + + if atomic.LoadInt32(&self.mining) == 0 { + return types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ) + } + return self.current.Block +} + +func (self *worker) start() { + self.mu.Lock() + defer self.mu.Unlock() + + atomic.StoreInt32(&self.mining, 1) + + // spin up agents + for agent := range self.agents { + agent.Start() + } +} + +func (self *worker) stop() { + self.wg.Wait() + + self.mu.Lock() + defer self.mu.Unlock() + if atomic.LoadInt32(&self.mining) == 1 { + // Stop all agents. + for agent := range self.agents { + agent.Stop() + // Remove CPU agents. + if _, ok := agent.(*CpuAgent); ok { + delete(self.agents, agent) + } + } + } + + atomic.StoreInt32(&self.mining, 0) + atomic.StoreInt32(&self.atWork, 0) +} + +func (self *worker) register(agent Agent) { + self.mu.Lock() + defer self.mu.Unlock() + self.agents[agent] = struct{}{} + agent.SetReturnCh(self.recv) +} + +func (self *worker) unregister(agent Agent) { + self.mu.Lock() + defer self.mu.Unlock() + delete(self.agents, agent) + agent.Stop() +} + +func (self *worker) update() { + for event := range self.events.Chan() { + // A real event arrived, process interesting content + switch ev := event.Data.(type) { + case core.ChainHeadEvent: + self.commitNewWork() + case core.ChainSideEvent: + self.uncleMu.Lock() + self.possibleUncles[ev.Block.Hash()] = ev.Block + self.uncleMu.Unlock() + case core.TxPreEvent: + // Apply transaction to the pending state if we're not mining + if atomic.LoadInt32(&self.mining) == 0 { + self.currentMu.Lock() + + acc, _ := types.Sender(self.current.signer, ev.Tx) + txs := map[common.Address]types.Transactions{acc: {ev.Tx}} + txset := types.NewTransactionsByPriceAndNonce(txs) + + self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain) + self.currentMu.Unlock() + } + } + } +} + +func (self *worker) wait() { + for { + mustCommitNewWork := true + for result := range self.recv { + atomic.AddInt32(&self.atWork, -1) + + if result == nil { + continue + } + block := result.Block + work := result.Work + + if self.fullValidation { + if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil { + glog.V(logger.Error).Infoln("mining err", err) + continue + } + go self.mux.Post(core.NewMinedBlockEvent{Block: block}) + } else { + work.state.Commit(self.config.IsEIP158(block.Number())) + parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + glog.V(logger.Error).Infoln("Invalid block found during mining") + continue + } + + auxValidator := self.eth.BlockChain().AuxValidator() + if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { + glog.V(logger.Error).Infoln("Invalid header on mined block:", err) + continue + } + + stat, err := self.chain.WriteBlock(block) + if err != nil { + glog.V(logger.Error).Infoln("error writing block to chain", err) + continue + } + + // update block hash since it is now available and not when the receipt/log of individual transactions were created + for _, r := range work.receipts { + for _, l := range r.Logs { + l.BlockHash = block.Hash() + } + } + for _, log := range work.state.Logs() { + log.BlockHash = block.Hash() + } + + // check if canon block and write transactions + if stat == core.CanonStatTy { + // This puts transactions in a extra db for rpc + core.WriteTransactions(self.chainDb, block) + // store the receipts + core.WriteReceipts(self.chainDb, work.receipts) + // Write map map bloom filters + core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts) + // implicit by posting ChainHeadEvent + mustCommitNewWork = false + } + + // broadcast before waiting for validation + go func(block *types.Block, logs []*types.Log, receipts []*types.Receipt) { + self.mux.Post(core.NewMinedBlockEvent{Block: block}) + self.mux.Post(core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) + + if stat == core.CanonStatTy { + self.mux.Post(core.ChainHeadEvent{Block: block}) + self.mux.Post(logs) + } + if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil { + glog.V(logger.Warn).Infoln("error writing block receipts:", err) + } + }(block, work.state.Logs(), work.receipts) + } + // Insert the block into the set of pending ones to wait for confirmations + self.unconfirmed.Insert(block.NumberU64(), block.Hash()) + + if mustCommitNewWork { + self.commitNewWork() + } + } + } +} + +// push sends a new work task to currently live miner agents. +func (self *worker) push(work *Work) { + if atomic.LoadInt32(&self.mining) != 1 { + return + } + for agent := range self.agents { + atomic.AddInt32(&self.atWork, 1) + if ch := agent.Work(); ch != nil { + ch <- work + } + } +} + +// makeCurrent creates a new environment for the current cycle. +func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { + state, err := self.chain.StateAt(parent.Root()) + if err != nil { + return err + } + work := &Work{ + config: self.config, + signer: types.NewEIP155Signer(self.config.ChainId), + state: state, + ancestors: set.New(), + family: set.New(), + uncles: set.New(), + header: header, + createdAt: time.Now(), + } + + // when 08 is processed ancestors contain 07 (quick block) + for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { + for _, uncle := range ancestor.Uncles() { + work.family.Add(uncle.Hash()) + } + work.family.Add(ancestor.Hash()) + work.ancestors.Add(ancestor.Hash()) + } + wallets := self.eth.AccountManager().Wallets() + accounts := make([]accounts.Account, 0, len(wallets)) + for _, wallet := range wallets { + accounts = append(accounts, wallet.Accounts()...) + } + // Keep track of transactions which return errors so they can be removed + work.tcount = 0 + work.ownedAccounts = accountAddressesSet(accounts) + self.current = work + return nil +} + +func (w *worker) setGasPrice(p *big.Int) { + w.mu.Lock() + defer w.mu.Unlock() + + // calculate the minimal gas price the miner accepts when sorting out transactions. + const pct = int64(90) + w.gasPrice = gasprice(p, pct) + + w.mux.Post(core.GasPriceChanged{Price: w.gasPrice}) +} + +func (self *worker) commitNewWork() { + self.mu.Lock() + defer self.mu.Unlock() + self.uncleMu.Lock() + defer self.uncleMu.Unlock() + self.currentMu.Lock() + defer self.currentMu.Unlock() + + tstart := time.Now() + parent := self.chain.CurrentBlock() + + tstamp := tstart.Unix() + if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { + tstamp = parent.Time().Int64() + 1 + } + // this will ensure we're not going off too far in the future + if now := time.Now().Unix(); tstamp > now+4 { + wait := time.Duration(tstamp-now) * time.Second + glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait) + time.Sleep(wait) + } + + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), + Number: num.Add(num, common.Big1), + Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), + GasLimit: core.CalcGasLimit(parent), + GasUsed: new(big.Int), + Coinbase: self.coinbase, + Extra: self.extra, + Time: big.NewInt(tstamp), + } + // If we are care about TheDAO hard-fork check whether to override the extra-data or not + if daoBlock := self.config.DAOForkBlock; daoBlock != nil { + // Check whether the block is among the fork extra-override range + limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) + if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { + // Depending whether we support or oppose the fork, override differently + if self.config.DAOForkSupport { + header.Extra = common.CopyBytes(params.DAOForkBlockExtra) + } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { + header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data + } + } + } + // Could potentially happen if starting to mine in an odd state. + err := self.makeCurrent(parent, header) + if err != nil { + glog.V(logger.Info).Infoln("Could not create new env for mining, retrying on next block.") + return + } + // Create the current work task and check any fork transitions needed + work := self.current + if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { + core.ApplyDAOHardFork(work.state) + } + + pending, err := self.eth.TxPool().Pending() + if err != nil { + glog.Errorf("Could not fetch pending transactions: %v", err) + return + } + + txs := types.NewTransactionsByPriceAndNonce(pending) + work.commitTransactions(self.mux, txs, self.gasPrice, self.chain) + + self.eth.TxPool().RemoveBatch(work.lowGasTxs) + self.eth.TxPool().RemoveBatch(work.failedTxs) + + // compute uncles for the new block. + var ( + uncles []*types.Header + badUncles []common.Hash + ) + for hash, uncle := range self.possibleUncles { + if len(uncles) == 2 { + break + } + if err := self.commitUncle(work, uncle.Header()); err != nil { + if glog.V(logger.Ridiculousness) { + glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) + glog.V(logger.Detail).Infoln(uncle) + } + badUncles = append(badUncles, hash) + } else { + glog.V(logger.Debug).Infof("committing %x as uncle\n", hash[:4]) + uncles = append(uncles, uncle.Header()) + } + } + for _, hash := range badUncles { + delete(self.possibleUncles, hash) + } + + if atomic.LoadInt32(&self.mining) == 1 { + // commit state root after all state transitions. + core.AccumulateRewards(work.state, header, uncles) + header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number)) + } + + // create the new block whose nonce will be mined. + work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) + + // We only care about logging if we're actually mining. + if atomic.LoadInt32(&self.mining) == 1 { + glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart)) + self.unconfirmed.Shift(work.Block.NumberU64() - 1) + } + self.push(work) +} + +func (self *worker) commitUncle(work *Work, uncle *types.Header) error { + hash := uncle.Hash() + if work.uncles.Has(hash) { + return core.UncleError("Uncle not unique") + } + if !work.ancestors.Has(uncle.ParentHash) { + return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) + } + if work.family.Has(hash) { + return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) + } + work.uncles.Add(uncle.Hash()) + return nil +} + +func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) { + gp := new(core.GasPool).AddGas(env.header.GasLimit) + + var coalescedLogs []*types.Log + + for { + // Retrieve the next transaction and abort if all done + tx := txs.Peek() + if tx == nil { + break + } + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + // + // We use the eip155 signer regardless of the current hf. + from, _ := types.Sender(env.signer, tx) + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !env.config.IsEIP155(env.header.Number) { + glog.V(logger.Detail).Infof("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash()) + + txs.Pop() + continue + } + + // Ignore any transactions (and accounts subsequently) with low gas limits + if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { + // Pop the current low-priced transaction without shifting in the next from the account + glog.V(logger.Info).Infof("Transaction (%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) + + env.lowGasTxs = append(env.lowGasTxs, tx) + txs.Pop() + + continue + } + // Start executing the transaction + env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) + + err, logs := env.commitTransaction(tx, bc, gp) + switch { + case core.IsGasLimitErr(err): + // Pop the current out-of-gas transaction without shifting in the next from the account + glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) + txs.Pop() + + case err != nil: + // Pop the current failed transaction without shifting in the next from the account + glog.V(logger.Detail).Infof("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) + env.failedTxs = append(env.failedTxs, tx) + txs.Pop() + + default: + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + txs.Shift() + } + } + + if len(coalescedLogs) > 0 || env.tcount > 0 { + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make([]*types.Log, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(types.Log) + *cpy[i] = *l + } + go func(logs []*types.Log, tcount int) { + if len(logs) > 0 { + mux.Post(core.PendingLogsEvent{Logs: logs}) + } + if tcount > 0 { + mux.Post(core.PendingStateEvent{}) + } + }(cpy, env.tcount) + } +} + +func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, []*types.Log) { + snap := env.state.Snapshot() + + receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) + if err != nil { + env.state.RevertToSnapshot(snap) + return err, nil + } + env.txs = append(env.txs, tx) + env.receipts = append(env.receipts, receipt) + + return nil, receipt.Logs +} + +// TODO: remove or use +func (self *worker) HashRate() int64 { + return 0 +} + +// gasprice calculates a reduced gas price based on the pct +// XXX Use big.Rat? +func gasprice(price *big.Int, pct int64) *big.Int { + p := new(big.Int).Set(price) + p.Div(p, big.NewInt(100)) + p.Mul(p, big.NewInt(pct)) + return p +} + +func accountAddressesSet(accounts []accounts.Account) *set.Set { + accountSet := set.New() + for _, account := range accounts { + accountSet.Add(account.Address) + } + return accountSet +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go new file mode 100644 index 0000000..fbaa3bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/accounts.go @@ -0,0 +1,200 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the accounts package to support client side key +// management on mobile platforms. + +package geth + +import ( + "errors" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" +) + +const ( + // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptN = int(keystore.StandardScryptN) + + // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB + // memory and taking approximately 1s CPU time on a modern processor. + StandardScryptP = int(keystore.StandardScryptP) + + // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptN = int(keystore.LightScryptN) + + // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB + // memory and taking approximately 100ms CPU time on a modern processor. + LightScryptP = int(keystore.LightScryptP) +) + +// Account represents a stored key. +type Account struct{ account accounts.Account } + +// Accounts represents a slice of accounts. +type Accounts struct{ accounts []accounts.Account } + +// Size returns the number of accounts in the slice. +func (a *Accounts) Size() int { + return len(a.accounts) +} + +// Get returns the account at the given index from the slice. +func (a *Accounts) Get(index int) (account *Account, _ error) { + if index < 0 || index >= len(a.accounts) { + return nil, errors.New("index out of bounds") + } + return &Account{a.accounts[index]}, nil +} + +// Set sets the account at the given index in the slice. +func (a *Accounts) Set(index int, account *Account) error { + if index < 0 || index >= len(a.accounts) { + return errors.New("index out of bounds") + } + a.accounts[index] = account.account + return nil +} + +// GetAddress retrieves the address associated with the account. +func (a *Account) GetAddress() *Address { + return &Address{a.account.Address} +} + +// GetURL retrieves the canonical URL of the account. +func (a *Account) GetURL() string { + return a.account.URL.String() +} + +// KeyStore manages a key storage directory on disk. +type KeyStore struct{ keystore *keystore.KeyStore } + +// NewKeyStore creates a keystore for the given directory. +func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { + return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)} +} + +// HasAddress reports whether a key with the given address is present. +func (ks *KeyStore) HasAddress(address *Address) bool { + return ks.keystore.HasAddress(address.address) +} + +// GetAccounts returns all key files present in the directory. +func (ks *KeyStore) GetAccounts() *Accounts { + return &Accounts{ks.keystore.Accounts()} +} + +// DeleteAccount deletes the key matched by account if the passphrase is correct. +// If a contains no filename, the address must match a unique key. +func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error { + return ks.keystore.Delete(account.account, passphrase) +} + +// SignHash calculates a ECDSA signature for the given hash. The produced signature +// is in the [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) { + return ks.keystore.SignHash(accounts.Account{Address: address.address}, hash) +} + +// SignTx signs the given transaction with the requested account. +func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) { + signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint) + if err != nil { + return nil, err + } + return &Transaction{signed}, nil +} + +// SignHashPassphrase signs hash if the private key matching the given address can +// be decrypted with the given passphrase. The produced signature is in the +// [R || S || V] format where V is 0 or 1. +func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) { + return ks.keystore.SignHashWithPassphrase(account.account, passphrase, hash) +} + +// SignTxPassphrase signs the transaction if the private key matching the +// given address can be decrypted with the given passphrase. +func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) { + signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint) + if err != nil { + return nil, err + } + return &Transaction{signed}, nil +} + +// Unlock unlocks the given account indefinitely. +func (ks *KeyStore) Unlock(account *Account, passphrase string) error { + return ks.keystore.TimedUnlock(account.account, passphrase, 0) +} + +// Lock removes the private key with the given address from memory. +func (ks *KeyStore) Lock(address *Address) error { + return ks.keystore.Lock(address.address) +} + +// TimedUnlock unlocks the given account with the passphrase. The account stays +// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the +// account until the program exits. The account must match a unique key file. +// +// If the account address is already unlocked for a duration, TimedUnlock extends or +// shortens the active unlock timeout. If the address was previously unlocked +// indefinitely the timeout is not altered. +func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error { + return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout)) +} + +// NewAccount generates a new key and stores it into the key directory, +// encrypting it with the passphrase. +func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) { + account, err := ks.keystore.NewAccount(passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} + +// ExportKey exports as a JSON key, encrypted with newPassphrase. +func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) { + return ks.keystore.Export(account.account, passphrase, newPassphrase) +} + +// ImportKey stores the given encrypted JSON key into the key directory. +func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) { + acc, err := ks.keystore.Import(keyJSON, passphrase, newPassphrase) + if err != nil { + return nil, err + } + return &Account{acc}, nil +} + +// UpdateAccount changes the passphrase of an existing account. +func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error { + return ks.keystore.Update(account.account, passphrase, newPassphrase) +} + +// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores +// a key file in the key directory. The key file is encrypted with the same passphrase. +func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) { + account, err := ks.keystore.ImportPreSaleKey(keyJSON, passphrase) + if err != nil { + return nil, err + } + return &Account{account}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/android_test.go b/vendor/github.com/ethereum/go-ethereum/mobile/android_test.go new file mode 100644 index 0000000..6f5076d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/android_test.go @@ -0,0 +1,232 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package geth + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" + "time" + + "github.com/ethereum/go-ethereum/internal/build" +) + +// androidTestClass is a Java class to do some lightweight tests against the Android +// bindings. The goal is not to test each individual functionality, rather just to +// catch breaking API and/or implementation changes. +const androidTestClass = ` +package go; + +import android.test.InstrumentationTestCase; +import android.test.MoreAsserts; + +import org.ethereum.geth.*; + +public class AndroidTest extends InstrumentationTestCase { + public AndroidTest() {} + + public void testAccountManagement() { + // Create an encrypted keystore with light crypto parameters. + KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP); + + try { + // Create a new account with the specified encryption passphrase. + Account newAcc = ks.newAccount("Creation password"); + + // Export the newly created account with a different passphrase. The returned + // data from this method invocation is a JSON encoded, encrypted key-file. + byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password"); + + // Update the passphrase on the account created above inside the local keystore. + ks.updateAccount(newAcc, "Creation password", "Update password"); + + // Delete the account updated above from the local keystore. + ks.deleteAccount(newAcc, "Update password"); + + // Import back the account we've exported (and then deleted) above with yet + // again a fresh passphrase. + Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password"); + + // Create a new account to sign transactions with + Account signer = ks.newAccount("Signer password"); + + Transaction tx = new Transaction( + 1, new Address("0x0000000000000000000000000000000000000000"), + new BigInt(0), new BigInt(0), new BigInt(1), null); // Random empty transaction + BigInt chain = new BigInt(1); // Chain identifier of the main net + + // Sign a transaction with a single authorization + Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); + + // Sign a transaction with multiple manually cancelled authorizations + ks.unlock(signer, "Signer password"); + signed = ks.signTx(signer, tx, chain); + ks.lock(signer.getAddress()); + + // Sign a transaction with multiple automatically cancelled authorizations + ks.timedUnlock(signer, "Signer password", 1000000000); + signed = ks.signTx(signer, tx, chain); + } catch (Exception e) { + fail(e.toString()); + } + } + + public void testInprocNode() { + Context ctx = new Context(); + + try { + // Start up a new inprocess node + Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig()); + node.start(); + + // Retrieve some data via function calls (we don't really care about the results) + NodeInfo info = node.getNodeInfo(); + info.getName(); + info.getListenerAddress(); + info.getProtocols(); + + // Retrieve some data via the APIs (we don't really care about the results) + EthereumClient ec = node.getEthereumClient(); + ec.getBlockByNumber(ctx, -1).getNumber(); + + NewHeadHandler handler = new NewHeadHandler() { + @Override public void onError(String error) {} + @Override public void onNewHead(final Header header) {} + }; + ec.subscribeNewHead(ctx, handler, 16); + } catch (Exception e) { + fail(e.toString()); + } + } +} +` + +// TestAndroid runs the Android java test class specified above. +// +// This requires the gradle command in PATH and the Android SDK whose path is available +// through ANDROID_HOME environment variable. To successfully run the tests, an Android +// device must also be available with debugging enabled. +// +// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest +func TestAndroid(t *testing.T) { + // Skip tests on Windows altogether + if runtime.GOOS == "windows" { + t.Skip("cannot test Android bindings on Windows, skipping") + } + // Make sure all the Android tools are installed + if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil { + t.Skip("command gradle not found, skipping") + } + if sdk := os.Getenv("ANDROID_HOME"); sdk == "" { + t.Skip("ANDROID_HOME environment var not set, skipping") + } + if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil { + t.Log("gomobile missing, installing it...") + if _, err := exec.Command("go", "install", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil { + t.Fatalf("install failed: %v", err) + } + t.Log("initializing gomobile...") + start := time.Now() + if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil { + t.Fatalf("initialization failed: %v", err) + } + t.Logf("initialization took %v", time.Since(start)) + } + // Create and switch to a temporary workspace + workspace, err := ioutil.TempDir("", "geth-android-") + if err != nil { + t.Fatalf("failed to create temporary workspace: %v", err) + } + defer os.RemoveAll(workspace) + + pwd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get current working directory: %v", err) + } + if err := os.Chdir(workspace); err != nil { + t.Fatalf("failed to switch to temporary workspace: %v", err) + } + defer os.Chdir(pwd) + + // Create the skeleton of the Android project + for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} { + err = os.MkdirAll(dir, os.ModePerm) + if err != nil { + t.Fatal(err) + } + } + // Generate the mobile bindings for Geth and add the tester class + gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/ethereum/go-ethereum/mobile") + if output, err := gobind.CombinedOutput(); err != nil { + t.Logf("%s", output) + t.Fatalf("failed to run gomobile bind: %v", err) + } + build.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar", os.ModePerm) + + if err = ioutil.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil { + t.Fatalf("failed to write Android test class: %v", err) + } + // Finish creating the project and run the tests via gradle + if err = ioutil.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil { + t.Fatalf("failed to write Android manifest: %v", err) + } + if err = ioutil.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil { + t.Fatalf("failed to write gradle build file: %v", err) + } + if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil { + t.Logf("%s", output) + t.Errorf("failed to run gradle test: %v", err) + } +} + +const androidManifest = ` + + + +` + +const gradleConfig = `buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.5.0' + } +} +allprojects { + repositories { jcenter() } +} +apply plugin: 'com.android.library' +android { + compileSdkVersion 'android-19' + buildToolsVersion '21.1.2' + defaultConfig { minSdkVersion 15 } +} +repositories { + flatDir { dirs 'libs' } +} +dependencies { + compile 'com.android.support:appcompat-v7:19.0.0' + compile(name: "geth", ext: "aar") +} +` diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big.go b/vendor/github.com/ethereum/go-ethereum/mobile/big.go new file mode 100644 index 0000000..9a55836 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big.go @@ -0,0 +1,95 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the math/big package. + +package geth + +import ( + "errors" + "math/big" +) + +// A BigInt represents a signed multi-precision integer. +type BigInt struct { + bigint *big.Int +} + +// NewBigInt allocates and returns a new BigInt set to x. +func NewBigInt(x int64) *BigInt { + return &BigInt{big.NewInt(x)} +} + +// GetBytes returns the absolute value of x as a big-endian byte slice. +func (bi *BigInt) GetBytes() []byte { + return bi.bigint.Bytes() +} + +// String returns the value of x as a formatted decimal string. +func (bi *BigInt) String() string { + return bi.bigint.String() +} + +// GetInt64 returns the int64 representation of x. If x cannot be represented in +// an int64, the result is undefined. +func (bi *BigInt) GetInt64() int64 { + return bi.bigint.Int64() +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets +// the big int to that value. +func (bi *BigInt) SetBytes(buf []byte) { + bi.bigint.SetBytes(buf) +} + +// SetInt64 sets the big int to x. +func (bi *BigInt) SetInt64(x int64) { + bi.bigint.SetInt64(x) +} + +// SetString sets the big int to x. +// +// The string prefix determines the actual conversion base. A prefix of "0x" or +// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix +// selects base 2. Otherwise the selected base is 10. +func (bi *BigInt) SetString(x string, base int) { + bi.bigint.SetString(x, base) +} + +// BigInts represents a slice of big ints. +type BigInts struct{ bigints []*big.Int } + +// Size returns the number of big ints in the slice. +func (bi *BigInts) Size() int { + return len(bi.bigints) +} + +// Get returns the bigint at the given index from the slice. +func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) { + if index < 0 || index >= len(bi.bigints) { + return nil, errors.New("index out of bounds") + } + return &BigInt{bi.bigints[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (bi *BigInts) Set(index int, bigint *BigInt) error { + if index < 0 || index >= len(bi.bigints) { + return errors.New("index out of bounds") + } + bi.bigints[index] = bigint.bigint + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go new file mode 100644 index 0000000..0447e1f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/big_go1.7.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the wrappers from the math/big package that require Go 1.7 and above. + +// +build go1.7 + +package geth + +// GetString returns the value of x as a formatted string in some number base. +func (bi *BigInt) GetString(base int) string { + return bi.bigint.Text(base) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/bind.go b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go new file mode 100644 index 0000000..bc4eb25 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/bind.go @@ -0,0 +1,183 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the bind package. + +package geth + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Signer is an interaface defining the callback when a contract requires a +// method to sign the transaction before submission. +type Signer interface { + Sign(*Address, *Transaction) (tx *Transaction, _ error) +} + +type signer struct { + sign bind.SignerFn +} + +func (s *signer) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) { + sig, err := s.sign(types.HomesteadSigner{}, addr.address, unsignedTx.tx) + if err != nil { + return nil, err + } + return &Transaction{sig}, nil +} + +// CallOpts is the collection of options to fine tune a contract call request. +type CallOpts struct { + opts bind.CallOpts +} + +// NewCallOpts creates a new option set for contract calls. +func NewCallOpts() *CallOpts { + return new(CallOpts) +} + +func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } +func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } +func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } +func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// TransactOpts is the collection of authorization data required to create a +// valid Ethereum transaction. +type TransactOpts struct { + opts bind.TransactOpts +} + +func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } +func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } +func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } +func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } +func (opts *TransactOpts) GetGasLimit() int64 { return opts.opts.GasLimit.Int64() } + +// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } + +// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) +// Even then it's awkward to unpack the subtleties of a Go context out to Java. +//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } + +func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } +func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } +func (opts *TransactOpts) SetSigner(s Signer) { + opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { + sig, err := s.Sign(&Address{addr}, &Transaction{tx}) + if err != nil { + return nil, err + } + return sig.tx, nil + } +} +func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } +func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } +func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = big.NewInt(limit) } +func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } + +// BoundContract is the base wrapper object that reflects a contract on the +// Ethereum network. It contains a collection of methods that are used by the +// higher level contract bindings to operate. +type BoundContract struct { + contract *bind.BoundContract + address common.Address + deployer *types.Transaction +} + +// DeployContract deploys a contract onto the Ethereum blockchain and binds the +// deployment address with a wrapper. +func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) { + // Deploy the contract to the network + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, bytecode, client.client, args.objects...) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bound, + address: addr, + deployer: tx, + }, nil +} + +// BindContract creates a low level contract interface through which calls and +// transactions may be made through. +func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) { + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + if err != nil { + return nil, err + } + return &BoundContract{ + contract: bind.NewBoundContract(address.address, parsed, client.client, client.client), + address: address.address, + }, nil +} + +func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } +func (c *BoundContract) GetDeployer() *Transaction { + if c.deployer == nil { + return nil + } + return &Transaction{c.deployer} +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. +func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { + results := make([]interface{}, len(out.objects)) + copy(results, out.objects) + if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { + return err + } + copy(out.objects, results) + return nil +} + +// Transact invokes the (paid) contract method with params as input values. +func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) { + rawTx, err := c.contract.Transact(&opts.opts, method, args.objects) + if err != nil { + return nil, err + } + return &Transaction{rawTx}, nil +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) { + rawTx, err := c.contract.Transfer(&opts.opts) + if err != nil { + return nil, err + } + return &Transaction{rawTx}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/common.go b/vendor/github.com/ethereum/go-ethereum/mobile/common.go new file mode 100644 index 0000000..779f22b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/common.go @@ -0,0 +1,187 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the common package. + +package geth + +import ( + "encoding/hex" + "errors" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// Hash represents the 32 byte Keccak256 hash of arbitrary data. +type Hash struct { + hash common.Hash +} + +// NewHashFromBytes converts a slice of bytes to a hash value. +func NewHashFromBytes(binary []byte) (hash *Hash, _ error) { + h := new(Hash) + if err := h.SetBytes(binary); err != nil { + return nil, err + } + return h, nil +} + +// NewHashFromHex converts a hex string to a hash value. +func NewHashFromHex(hex string) (hash *Hash, _ error) { + h := new(Hash) + if err := h.SetHex(hex); err != nil { + return nil, err + } + return h, nil +} + +// SetBytes sets the specified slice of bytes as the hash value. +func (h *Hash) SetBytes(hash []byte) error { + if length := len(hash); length != common.HashLength { + return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) + } + copy(h.hash[:], hash) + return nil +} + +// GetBytes retrieves the byte representation of the hash. +func (h *Hash) GetBytes() []byte { + return h.hash[:] +} + +// SetHex sets the specified hex string as the hash value. +func (h *Hash) SetHex(hash string) error { + hash = strings.ToLower(hash) + if len(hash) >= 2 && hash[:2] == "0x" { + hash = hash[2:] + } + if length := len(hash); length != 2*common.HashLength { + return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) + } + bin, err := hex.DecodeString(hash) + if err != nil { + return err + } + copy(h.hash[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the hash. +func (h *Hash) GetHex() string { + return h.hash.Hex() +} + +// Hashes represents a slice of hashes. +type Hashes struct{ hashes []common.Hash } + +// Size returns the number of hashes in the slice. +func (h *Hashes) Size() int { + return len(h.hashes) +} + +// Get returns the hash at the given index from the slice. +func (h *Hashes) Get(index int) (hash *Hash, _ error) { + if index < 0 || index >= len(h.hashes) { + return nil, errors.New("index out of bounds") + } + return &Hash{h.hashes[index]}, nil +} + +// Address represents the 20 byte address of an Ethereum account. +type Address struct { + address common.Address +} + +// NewAddressFromBytes converts a slice of bytes to a hash value. +func NewAddressFromBytes(binary []byte) (address *Address, _ error) { + a := new(Address) + if err := a.SetBytes(binary); err != nil { + return nil, err + } + return a, nil +} + +// NewAddressFromHex converts a hex string to a address value. +func NewAddressFromHex(hex string) (address *Address, _ error) { + a := new(Address) + if err := a.SetHex(hex); err != nil { + return nil, err + } + return a, nil +} + +// SetBytes sets the specified slice of bytes as the address value. +func (a *Address) SetBytes(address []byte) error { + if length := len(address); length != common.AddressLength { + return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) + } + copy(a.address[:], address) + return nil +} + +// GetBytes retrieves the byte representation of the address. +func (a *Address) GetBytes() []byte { + return a.address[:] +} + +// SetHex sets the specified hex string as the address value. +func (a *Address) SetHex(address string) error { + address = strings.ToLower(address) + if len(address) >= 2 && address[:2] == "0x" { + address = address[2:] + } + if length := len(address); length != 2*common.AddressLength { + return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) + } + bin, err := hex.DecodeString(address) + if err != nil { + return err + } + copy(a.address[:], bin) + return nil +} + +// GetHex retrieves the hex string representation of the address. +func (a *Address) GetHex() string { + return a.address.Hex() +} + +// Addresses represents a slice of addresses. +type Addresses struct{ addresses []common.Address } + +// Size returns the number of addresses in the slice. +func (a *Addresses) Size() int { + return len(a.addresses) +} + +// Get returns the address at the given index from the slice. +func (a *Addresses) Get(index int) (address *Address, _ error) { + if index < 0 || index >= len(a.addresses) { + return nil, errors.New("index out of bounds") + } + return &Address{a.addresses[index]}, nil +} + +// Set sets the address at the given index in the slice. +func (a *Addresses) Set(index int, address *Address) error { + if index < 0 || index >= len(a.addresses) { + return errors.New("index out of bounds") + } + a.addresses[index] = address.address + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/context.go b/vendor/github.com/ethereum/go-ethereum/mobile/context.go new file mode 100644 index 0000000..9df94b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/context.go @@ -0,0 +1,81 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the golang.org/x/net/context package to support +// client side context management on mobile platforms. + +package geth + +import ( + "time" + + "golang.org/x/net/context" +) + +// Context carries a deadline, a cancelation signal, and other values across API +// boundaries. +type Context struct { + context context.Context + cancel context.CancelFunc +} + +// NewContext returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming requests. +func NewContext() *Context { + return &Context{ + context: context.Background(), + } +} + +// WithCancel returns a copy of the original context with cancellation mechanism +// included. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithCancel() *Context { + child, cancel := context.WithCancel(c.context) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithDeadline returns a copy of the original context with the deadline adjusted +// to be no later than the specified time. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithDeadline(sec int64, nsec int64) *Context { + child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} + +// WithTimeout returns a copy of the original context with the deadline adjusted +// to be no later than now + the duration specified. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func (c *Context) WithTimeout(nsec int64) *Context { + child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) + return &Context{ + context: child, + cancel: cancel, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/discover.go b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go new file mode 100644 index 0000000..9b3c93c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/discover.go @@ -0,0 +1,104 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the accounts package to support client side enode +// management on mobile platforms. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p/discv5" +) + +// Enode represents a host on the network. +type Enode struct { + node *discv5.Node +} + +// NewEnode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func NewEnode(rawurl string) (enode *Enode, _ error) { + node, err := discv5.ParseNode(rawurl) + if err != nil { + return nil, err + } + return &Enode{node}, nil +} + +// Enodes represents a slice of accounts. +type Enodes struct{ nodes []*discv5.Node } + +// NewEnodes creates a slice of uninitialized enodes. +func NewEnodes(size int) *Enodes { + return &Enodes{ + nodes: make([]*discv5.Node, size), + } +} + +// NewEnodesEmpty creates an empty slice of Enode values. +func NewEnodesEmpty() *Enodes { + return NewEnodes(0) +} + +// Size returns the number of enodes in the slice. +func (e *Enodes) Size() int { + return len(e.nodes) +} + +// Get returns the enode at the given index from the slice. +func (e *Enodes) Get(index int) (enode *Enode, _ error) { + if index < 0 || index >= len(e.nodes) { + return nil, errors.New("index out of bounds") + } + return &Enode{e.nodes[index]}, nil +} + +// Set sets the enode at the given index in the slice. +func (e *Enodes) Set(index int, enode *Enode) error { + if index < 0 || index >= len(e.nodes) { + return errors.New("index out of bounds") + } + e.nodes[index] = enode.node + return nil +} + +// Append adds a new enode element to the end of the slice. +func (e *Enodes) Append(enode *Enode) { + e.nodes = append(e.nodes, enode.node) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/doc.go b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go new file mode 100644 index 0000000..89be470 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/doc.go @@ -0,0 +1,61 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package geth contains the simplified mobile APIs to go-ethereum. +// +// The scope of this package is *not* to allow writing a custom Ethereun client +// with pieces plucked from go-ethereum, rather to allow writing native dapps on +// mobile platforms. Keep this in mind when using or extending this package! +// +// API limitations +// +// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the +// exposed APIs need to be manually wrapped into simplified types, with custom +// constructors and getters/setters to ensure that they can be meaninfully used +// from Java/ObjC too. +// +// With this in mind, please try to limit the scope of this package and only add +// essentials without which mobile support cannot work, especially since manually +// syncing the code will be unwieldy otherwise. In the long term we might consider +// writing custom library generators, but those are out of scope now. +// +// Content wise each file in this package corresponds to an entire Go package +// from the go-ethereum repository. Please adhere to this scoping to prevent this +// package getting unmaintainable. +// +// Wrapping guidelines: +// +// Every type that is to be exposed should be wrapped into its own plain struct, +// which internally contains a single field: the original go-ethereum version. +// This is needed because gomobile cannot expose named types for now. +// +// Whenever a method argument or a return type is a custom struct, the pointer +// variant should always be used as value types crossing over between language +// boundaries might have strange behaviors. +// +// Slices of types should be converted into a single multiplicative type wrapping +// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations +// should not be provided to limit the remote code complexity. Arrays should be +// avoided as much as possible since they complicate bounds checking. +// +// If a method has multiple return values (e.g. some return + an error), those +// are generated as output arguments in ObjC. To avoid weird generated names like +// ret_0 for them, please always assign names to output variables if tuples. +// +// Note, a panic *cannot* cross over language boundaries, instead will result in +// an undebuggable SEGFAULT in the process. For error handling only ever use error +// returns, which may be the only or the second return. +package geth diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go new file mode 100644 index 0000000..4e83285 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethclient.go @@ -0,0 +1,305 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains a wrapper for the Ethereum client. + +package geth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" +) + +// EthereumClient provides access to the Ethereum APIs. +type EthereumClient struct { + client *ethclient.Client +} + +// NewEthereumClient connects a client to the given URL. +func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) { + rawClient, err := ethclient.Dial(rawurl) + return &EthereumClient{rawClient}, err +} + +// GetBlockByHash returns the given full block. +func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) { + rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash) + return &Block{rawBlock}, err +} + +// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the +// latest known block is returned. +func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) { + if number < 0 { + rawBlock, err := ec.client.BlockByNumber(ctx.context, nil) + return &Block{rawBlock}, err + } + rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) + return &Block{rawBlock}, err +} + +// GetHeaderByHash returns the block header with the given hash. +func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) { + rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash) + return &Header{rawHeader}, err +} + +// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, +// the latest known header is returned. +func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) { + if number < 0 { + rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil) + return &Header{rawHeader}, err + } + rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) + return &Header{rawHeader}, err +} + +// GetTransactionByHash returns the transaction with the given hash. +func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) { + // TODO(karalabe): handle isPending + rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) + return &Transaction{rawTx}, err +} + +// GetTransactionCount returns the total number of transactions in the given block. +func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) { + rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash) + return int(rawCount), err +} + +// GetTransactionInBlock returns a single transaction at index in the given block. +func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) { + rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) + return &Transaction{rawTx}, err + +} + +// GetTransactionReceipt returns the receipt of a transaction by transaction hash. +// Note that the receipt is not available for pending transactions. +func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) { + rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) + return &Receipt{rawReceipt}, err +} + +// SyncProgress retrieves the current progress of the sync algorithm. If there's +// no sync currently running, it returns nil. +func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) { + rawProgress, err := ec.client.SyncProgress(ctx.context) + if rawProgress == nil { + return nil, err + } + return &SyncProgress{*rawProgress}, err +} + +// NewHeadHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type NewHeadHandler interface { + OnNewHead(header *Header) + OnError(failure string) +} + +// SubscribeNewHead subscribes to notifications about the current blockchain head +// on the given channel. +func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) { + // Subscribe to the event internally + ch := make(chan *types.Header, buffer) + rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case header := <-ch: + handler.OnNewHead(&Header{header}) + + case err := <-rawSub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{rawSub}, nil +} + +// State Access + +// GetBalanceAt returns the wei balance of the given account. +// The block number can be <0, in which case the balance is taken from the latest known block. +func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) { + if number < 0 { + rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil) + return &BigInt{rawBalance}, err + } + rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) + return &BigInt{rawBalance}, err +} + +// GetStorageAt returns the value of key in the contract storage of the given account. +// The block number can be <0, in which case the value is taken from the latest known block. +func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) { + if number < 0 { + return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) + } + return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) +} + +// GetCodeAt returns the contract code of the given account. +// The block number can be <0, in which case the code is taken from the latest known block. +func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) { + if number < 0 { + return ec.client.CodeAt(ctx.context, account.address, nil) + } + return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) +} + +// GetNonceAt returns the account nonce of the given account. +// The block number can be <0, in which case the nonce is taken from the latest known block. +func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) { + if number < 0 { + rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil) + return int64(rawNonce), err + } + rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) + return int64(rawNonce), err +} + +// Filters + +// FilterLogs executes a filter query. +func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) { + rawLogs, err := ec.client.FilterLogs(ctx.context, query.query) + if err != nil { + return nil, err + } + // Temp hack due to vm.Logs being []*vm.Log + res := make([]*types.Log, len(rawLogs)) + for i, log := range rawLogs { + res[i] = &log + } + return &Logs{res}, nil +} + +// FilterLogsHandler is a client-side subscription callback to invoke on events and +// subscription failure. +type FilterLogsHandler interface { + OnFilterLogs(log *Log) + OnError(failure string) +} + +// SubscribeFilterLogs subscribes to the results of a streaming filter query. +func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) { + // Subscribe to the event internally + ch := make(chan types.Log, buffer) + rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) + if err != nil { + return nil, err + } + // Start up a dispatcher to feed into the callback + go func() { + for { + select { + case log := <-ch: + handler.OnFilterLogs(&Log{&log}) + + case err := <-rawSub.Err(): + handler.OnError(err.Error()) + return + } + } + }() + return &Subscription{rawSub}, nil +} + +// Pending State + +// GetPendingBalanceAt returns the wei balance of the given account in the pending state. +func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) { + rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address) + return &BigInt{rawBalance}, err +} + +// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. +func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) { + return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) +} + +// GetPendingCodeAt returns the contract code of the given account in the pending state. +func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) { + return ec.client.PendingCodeAt(ctx.context, account.address) +} + +// GetPendingNonceAt returns the account nonce of the given account in the pending state. +// This is the nonce that should be used for the next transaction. +func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) { + rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address) + return int64(rawNonce), err +} + +// GetPendingTransactionCount returns the total number of transactions in the pending state. +func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) { + rawCount, err := ec.client.PendingTransactionCount(ctx.context) + return int(rawCount), err +} + +// Contract Calling + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +// +// blockNumber selects the block height at which the call runs. It can be <0, in which +// case the code is taken from the latest known block. Note that state from very old +// blocks might not be available. +func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) { + if number < 0 { + return ec.client.CallContract(ctx.context, msg.msg, nil) + } + return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) +} + +// PendingCallContract executes a message call transaction using the EVM. +// The state seen by the contract call is the pending state. +func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) { + return ec.client.PendingCallContract(ctx.context, msg.msg) +} + +// SuggestGasPrice retrieves the currently suggested gas price to allow a timely +// execution of a transaction. +func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) { + rawPrice, err := ec.client.SuggestGasPrice(ctx.context) + return &BigInt{rawPrice}, err +} + +// EstimateGas tries to estimate the gas needed to execute a specific transaction based on +// the current pending state of the backend blockchain. There is no guarantee that this is +// the true gas limit requirement as other transactions may be added or removed by miners, +// but it should provide a basis for setting a reasonable default. +func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas *BigInt, _ error) { + rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg) + return &BigInt{rawGas}, err +} + +// SendTransaction injects a signed transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { + return ec.client.SendTransaction(ctx.context, tx.tx) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go new file mode 100644 index 0000000..94f707a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/ethereum.go @@ -0,0 +1,125 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the go-ethereum root package. + +package geth + +import ( + "errors" + "math/big" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" +) + +// Subscription represents an event subscription where events are +// delivered on a data channel. +type Subscription struct { + sub ethereum.Subscription +} + +// Unsubscribe cancels the sending of events to the data channel +// and closes the error channel. +func (s *Subscription) Unsubscribe() { + s.sub.Unsubscribe() +} + +// CallMsg contains parameters for contract calls. +type CallMsg struct { + msg ethereum.CallMsg +} + +// NewCallMsg creates an empty contract call parameter list. +func NewCallMsg() *CallMsg { + return new(CallMsg) +} + +func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } +func (msg *CallMsg) GetGas() int64 { return msg.msg.Gas.Int64() } +func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } +func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } +func (msg *CallMsg) GetData() []byte { return msg.msg.Data } +func (msg *CallMsg) GetTo() *Address { + if to := msg.msg.To; to != nil { + return &Address{*msg.msg.To} + } + return nil +} + +func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } +func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = big.NewInt(gas) } +func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } +func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } +func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = data } +func (msg *CallMsg) SetTo(address *Address) { + if address == nil { + msg.msg.To = nil + } + msg.msg.To = &address.address +} + +// SyncProgress gives progress indications when the node is synchronising with +// the Ethereum network. +type SyncProgress struct { + progress ethereum.SyncProgress +} + +func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } +func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } +func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } +func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } +func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } + +// Topics is a set of topic lists to filter events with. +type Topics struct{ topics [][]common.Hash } + +// Size returns the number of topic lists inside the set +func (t *Topics) Size() int { + return len(t.topics) +} + +// Get returns the topic list at the given index from the slice. +func (t *Topics) Get(index int) (hashes *Hashes, _ error) { + if index < 0 || index >= len(t.topics) { + return nil, errors.New("index out of bounds") + } + return &Hashes{t.topics[index]}, nil +} + +// Set sets the topic list at the given index in the slice. +func (t *Topics) Set(index int, topics *Hashes) error { + if index < 0 || index >= len(t.topics) { + return errors.New("index out of bounds") + } + t.topics[index] = topics.hashes + return nil +} + +// FilterQuery contains options for contact log filtering. +type FilterQuery struct { + query ethereum.FilterQuery +} + +// NewFilterQuery creates an empty filter query for contact log filtering. +func NewFilterQuery() *FilterQuery { + return new(FilterQuery) +} + +func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } +func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } +func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } +func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go new file mode 100644 index 0000000..52c6986 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth.go @@ -0,0 +1,210 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the node package to support client side node +// management on mobile platforms. + +package geth + +import ( + "fmt" + "math/big" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/params" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" +) + +// NodeConfig represents the collection of configuration values to fine tune the Geth +// node embedded into a mobile process. The available values are a subset of the +// entire API provided by go-ethereum to reduce the maintenance surface and dev +// complexity. +type NodeConfig struct { + // Bootstrap nodes used to establish connectivity with the rest of the network. + BootstrapNodes *Enodes + + // MaxPeers is the maximum number of peers that can be connected. If this is + // set to zero, then only the configured static and trusted peers can connect. + MaxPeers int + + // EthereumEnabled specifies whether the node should run the Ethereum protocol. + EthereumEnabled bool + + // EthereumNetworkID is the network identifier used by the Ethereum protocol to + // decide if remote peers should be accepted or not. + EthereumNetworkID int + + // EthereumChainConfig is the default parameters of the blockchain to use. If no + // configuration is specified, it defaults to the main network. + EthereumChainConfig *ChainConfig + + // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An + // empty genesis state is equivalent to using the mainnet's state. + EthereumGenesis string + + // EthereumDatabaseCache is the system memory in MB to allocate for database caching. + // A minimum of 16MB is always reserved. + EthereumDatabaseCache int + + // EthereumNetStats is a netstats connection string to use to report various + // chain, transaction and node stats to a monitoring server. + // + // It has the form "nodename:secret@host:port" + EthereumNetStats string + + // WhisperEnabled specifies whether the node should run the Whisper protocol. + WhisperEnabled bool +} + +// defaultNodeConfig contains the default node configuration values to use if all +// or some fields are missing from the user's specified list. +var defaultNodeConfig = &NodeConfig{ + BootstrapNodes: FoundationBootnodes(), + MaxPeers: 25, + EthereumEnabled: true, + EthereumNetworkID: 1, + EthereumChainConfig: MainnetChainConfig(), + EthereumDatabaseCache: 16, +} + +// NewNodeConfig creates a new node option set, initialized to the default values. +func NewNodeConfig() *NodeConfig { + config := *defaultNodeConfig + return &config +} + +// Node represents a Geth Ethereum node instance. +type Node struct { + node *node.Node +} + +// NewNode creates and configures a new Geth node. +func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { + // If no or partial configurations were specified, use defaults + if config == nil { + config = NewNodeConfig() + } + if config.MaxPeers == 0 { + config.MaxPeers = defaultNodeConfig.MaxPeers + } + if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { + config.BootstrapNodes = defaultNodeConfig.BootstrapNodes + } + // Create the empty networking stack + nodeConf := &node.Config{ + Name: clientIdentifier, + Version: params.Version, + DataDir: datadir, + KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! + NoDiscovery: true, + DiscoveryV5: true, + DiscoveryV5Addr: ":0", + BootstrapNodesV5: config.BootstrapNodes.nodes, + ListenAddr: ":0", + NAT: nat.Any(), + MaxPeers: config.MaxPeers, + } + rawStack, err := node.New(nodeConf) + if err != nil { + return nil, err + } + // Register the Ethereum protocol if requested + if config.EthereumEnabled { + ethConf := ð.Config{ + ChainConfig: ¶ms.ChainConfig{ + ChainId: big.NewInt(config.EthereumChainConfig.ChainID), + HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock), + DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock), + DAOForkSupport: config.EthereumChainConfig.DAOForkSupport, + EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block), + EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash, + EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block), + EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block), + }, + Genesis: config.EthereumGenesis, + LightMode: true, + DatabaseCache: config.EthereumDatabaseCache, + NetworkId: config.EthereumNetworkID, + GasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMinGasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), + GpoMaxGasPrice: new(big.Int).Mul(big.NewInt(500), common.Shannon), + GpoFullBlockRatio: 80, + GpobaseStepDown: 10, + GpobaseStepUp: 100, + GpobaseCorrectionFactor: 110, + } + if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, ethConf) + }); err != nil { + return nil, fmt.Errorf("ethereum init: %v", err) + } + // If netstats reporting is requested, do it + if config.EthereumNetStats != "" { + if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + var lesServ *les.LightEthereum + ctx.Service(&lesServ) + + return ethstats.New(config.EthereumNetStats, nil, lesServ) + }); err != nil { + return nil, fmt.Errorf("netstats init: %v", err) + } + } + } + // Register the Whisper protocol if requested + if config.WhisperEnabled { + if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { + return nil, fmt.Errorf("whisper init: %v", err) + } + } + return &Node{rawStack}, nil +} + +// Start creates a live P2P node and starts running it. +func (n *Node) Start() error { + return n.node.Start() +} + +// Stop terminates a running node along with all it's services. In the node was +// not started, an error is returned. +func (n *Node) Stop() error { + return n.node.Stop() +} + +// GetEthereumClient retrieves a client to access the Ethereum subsystem. +func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) { + rpc, err := n.node.Attach() + if err != nil { + return nil, err + } + return &EthereumClient{ethclient.NewClient(rpc)}, nil +} + +// GetNodeInfo gathers and returns a collection of metadata known about the host. +func (n *Node) GetNodeInfo() *NodeInfo { + return &NodeInfo{n.node.Server().NodeInfo()} +} + +// GetPeersInfo returns an array of metadata objects describing connected peers. +func (n *Node) GetPeersInfo() *PeerInfos { + return &PeerInfos{n.node.Server().PeersInfo()} +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go new file mode 100644 index 0000000..8e4ebe6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_android.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build android + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethDroid" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go new file mode 100644 index 0000000..307cd08 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_ios.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build ios + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "iGeth" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go new file mode 100644 index 0000000..6f0c5dd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/geth_other.go @@ -0,0 +1,22 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !android,!ios + +package geth + +// clientIdentifier is a hard coded identifier to report into the network. +var clientIdentifier = "GethMobile" diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/init.go b/vendor/github.com/ethereum/go-ethereum/mobile/init.go new file mode 100644 index 0000000..0fbc6bd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/init.go @@ -0,0 +1,35 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains initialization code for the mbile library. + +package geth + +import ( + "runtime" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +func init() { + // Initialize the logger + glog.SetV(logger.Info) + glog.SetToStderr(true) + + // Initialize the goroutine count + runtime.GOMAXPROCS(runtime.NumCPU()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/interface.go b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go new file mode 100644 index 0000000..10eac5f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/interface.go @@ -0,0 +1,148 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains perverted wrappers to allow crossing over empty interfaces. + +package geth + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Interface represents a wrapped version of Go's interface{}, with the capacity +// to store arbitrary data types. +// +// Since it's impossible to get the arbitrary-ness converted between Go and mobile +// platforms, we're using explicit getters and setters for the conversions. There +// is of course no point in enumerating everything, just enough to support the +// contract bindins requiring client side generated code. +type Interface struct { + object interface{} +} + +// NewInterface creates a new empty interface that can be used to pass around +// generic types. +func NewInterface() *Interface { + return new(Interface) +} + +func (i *Interface) SetBool(b bool) { i.object = &b } +func (i *Interface) SetBools(bs []bool) { i.object = &bs } +func (i *Interface) SetString(str string) { i.object = &str } +func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } +func (i *Interface) SetBinary(binary []byte) { i.object = &binary } +func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries } +func (i *Interface) SetAddress(address *Address) { i.object = &address.address } +func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } +func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } +func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } +func (i *Interface) SetInt8(n int8) { i.object = &n } +func (i *Interface) SetInt16(n int16) { i.object = &n } +func (i *Interface) SetInt32(n int32) { i.object = &n } +func (i *Interface) SetInt64(n int64) { i.object = &n } +func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetUint64(bigint *BigInt) { n := uint64(bigint.bigint.Uint64()); i.object = &n } +func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } +func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } + +func (i *Interface) SetDefaultBool() { i.object = new(bool) } +func (i *Interface) SetDefaultBools() { i.object = new([]bool) } +func (i *Interface) SetDefaultString() { i.object = new(string) } +func (i *Interface) SetDefaultStrings() { i.object = new([]string) } +func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } +func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } +func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } +func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } +func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } +func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } +func (i *Interface) SetDefaultInt8() { i.object = new(int8) } +func (i *Interface) SetDefaultInt16() { i.object = new(int16) } +func (i *Interface) SetDefaultInt32() { i.object = new(int32) } +func (i *Interface) SetDefaultInt64() { i.object = new(int64) } +func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } +func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } +func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } +func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } +func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } +func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } + +func (i *Interface) GetBool() bool { return *i.object.(*bool) } +func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) } +func (i *Interface) GetString() string { return *i.object.(*string) } +func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } +func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } +func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) } +func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } +func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } +func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } +func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } +func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } +func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } +func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } +func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } +func (i *Interface) GetUint8() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} +} +func (i *Interface) GetUint16() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} +} +func (i *Interface) GetUint32() *BigInt { + return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} +} +func (i *Interface) GetUint64() *BigInt { + return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} +} +func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } +func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } + +// Interfaces is a slices of wrapped generic objects. +type Interfaces struct { + objects []interface{} +} + +// NewInterfaces creates a slice of uninitialized interfaces. +func NewInterfaces(size int) *Interfaces { + return &Interfaces{ + objects: make([]interface{}, size), + } +} + +// Size returns the number of interfaces in the slice. +func (i *Interfaces) Size() int { + return len(i.objects) +} + +// Get returns the bigint at the given index from the slice. +func (i *Interfaces) Get(index int) (iface *Interface, _ error) { + if index < 0 || index >= len(i.objects) { + return nil, errors.New("index out of bounds") + } + return &Interface{i.objects[index]}, nil +} + +// Set sets the big int at the given index in the slice. +func (i *Interfaces) Set(index int, object *Interface) error { + if index < 0 || index >= len(i.objects) { + return errors.New("index out of bounds") + } + i.objects[index] = object.object + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/logger.go b/vendor/github.com/ethereum/go-ethereum/mobile/logger.go new file mode 100644 index 0000000..97f80f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/logger.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package geth + +import ( + "github.com/ethereum/go-ethereum/logger/glog" +) + +// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go). +func SetVerbosity(level int) { + glog.SetV(level) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go new file mode 100644 index 0000000..8d21639 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/p2p.go @@ -0,0 +1,74 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received pi copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains wrappers for the p2p package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/p2p" +) + +// NodeInfo represents pi short summary of the information known about the host. +type NodeInfo struct { + info *p2p.NodeInfo +} + +func (ni *NodeInfo) GetID() string { return ni.info.ID } +func (ni *NodeInfo) GetName() string { return ni.info.Name } +func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } +func (ni *NodeInfo) GetIP() string { return ni.info.IP } +func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } +func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } +func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } +func (ni *NodeInfo) GetProtocols() *Strings { + protos := []string{} + for proto := range ni.info.Protocols { + protos = append(protos, proto) + } + return &Strings{protos} +} + +// PeerInfo represents pi short summary of the information known about pi connected peer. +type PeerInfo struct { + info *p2p.PeerInfo +} + +func (pi *PeerInfo) GetID() string { return pi.info.ID } +func (pi *PeerInfo) GetName() string { return pi.info.Name } +func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } +func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } +func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } + +// PeerInfos represents a slice of infos about remote peers. +type PeerInfos struct { + infos []*p2p.PeerInfo +} + +// Size returns the number of peer info entries in the slice. +func (pi *PeerInfos) Size() int { + return len(pi.infos) +} + +// Get returns the peer info at the given index from the slice. +func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) { + if index < 0 || index >= len(pi.infos) { + return nil, errors.New("index out of bounds") + } + return &PeerInfo{pi.infos[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/params.go b/vendor/github.com/ethereum/go-ethereum/mobile/params.go new file mode 100644 index 0000000..87747c7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/params.go @@ -0,0 +1,92 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the params package. + +package geth + +import ( + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" +) + +// MainnetChainConfig returns the chain configurations for the main Ethereum network. +func MainnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.MainNetChainID.Int64(), + HomesteadBlock: params.MainNetHomesteadBlock.Int64(), + DAOForkBlock: params.MainNetDAOForkBlock.Int64(), + DAOForkSupport: true, + EIP150Block: params.MainNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash}, + EIP155Block: params.MainNetSpuriousDragon.Int64(), + EIP158Block: params.MainNetSpuriousDragon.Int64(), + } +} + +// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It +// is actually empty since that defaults to the hard coded binary genesis block. +func MainnetGenesis() string { + return "" +} + +// TestnetChainConfig returns the chain configurations for the Ethereum test network. +func TestnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: params.TestNetChainID.Int64(), + HomesteadBlock: params.TestNetHomesteadBlock.Int64(), + DAOForkBlock: 0, + DAOForkSupport: false, + EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(), + EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash}, + EIP155Block: params.TestNetSpuriousDragon.Int64(), + EIP158Block: params.TestNetSpuriousDragon.Int64(), + } +} + +// TestnetGenesis returns the JSON spec to use for the Ethereum test network. +func TestnetGenesis() string { + return core.DefaultTestnetGenesisBlock() +} + +// ChainConfig is the core config which determines the blockchain settings. +type ChainConfig struct { + ChainID int64 // Chain ID for replay protection + HomesteadBlock int64 // Homestead switch block + DAOForkBlock int64 // TheDAO hard-fork switch block + DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork + EIP150Block int64 // Homestead gas reprice switch block + EIP150Hash Hash // Homestead gas reprice switch block hash + EIP155Block int64 // Replay protection switch block + EIP158Block int64 // Empty account pruning switch block +} + +// NewChainConfig creates a new chain configuration that transitions immediately +// to homestead and has no notion of the DAO fork (ideal for a private network). +func NewChainConfig() *ChainConfig { + return new(ChainConfig) +} + +// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated +// by the foundation running the V5 discovery protocol. +func FoundationBootnodes() *Enodes { + nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))} + for i, url := range params.DiscoveryV5Bootnodes { + nodes.nodes[i] = discv5.MustParseNode(url) + } + return nodes +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go new file mode 100644 index 0000000..54b25df --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/primitives.go @@ -0,0 +1,54 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received s copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains various wrappers for primitive types. + +package geth + +import ( + "errors" + "fmt" +) + +// Strings represents s slice of strs. +type Strings struct{ strs []string } + +// Size returns the number of strs in the slice. +func (s *Strings) Size() int { + return len(s.strs) +} + +// Get returns the string at the given index from the slice. +func (s *Strings) Get(index int) (str string, _ error) { + if index < 0 || index >= len(s.strs) { + return "", errors.New("index out of bounds") + } + return s.strs[index], nil +} + +// Set sets the string at the given index in the slice. +func (s *Strings) Set(index int, str string) error { + if index < 0 || index >= len(s.strs) { + return errors.New("index out of bounds") + } + s.strs[index] = str + return nil +} + +// String implements the Stringer interface. +func (s *Strings) String() string { + return fmt.Sprintf("%v", s.strs) +} diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/types.go b/vendor/github.com/ethereum/go-ethereum/mobile/types.go new file mode 100644 index 0000000..a9c8cf6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/types.go @@ -0,0 +1,194 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core/types" +) + +// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that +// a sufficient amount of computation has been carried out on a block. +type Nonce struct { + nonce types.BlockNonce +} + +// GetBytes retrieves the byte representation of the block nonce. +func (n *Nonce) GetBytes() []byte { + return n.nonce[:] +} + +// GetHex retrieves the hex string representation of the block nonce. +func (n *Nonce) GetHex() string { + return fmt.Sprintf("0x%x", n.nonce[:]) +} + +// Bloom represents a 256 bit bloom filter. +type Bloom struct { + bloom types.Bloom +} + +// GetBytes retrieves the byte representation of the bloom filter. +func (b *Bloom) GetBytes() []byte { + return b.bloom[:] +} + +// GetHex retrieves the hex string representation of the bloom filter. +func (b *Bloom) GetHex() string { + return fmt.Sprintf("0x%x", b.bloom[:]) +} + +// Header represents a block header in the Ethereum blockchain. +type Header struct { + header *types.Header +} + +func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } +func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } +func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } +func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } +func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } +func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } +func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } +func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } +func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } +func (h *Header) GetGasLimit() int64 { return h.header.GasLimit.Int64() } +func (h *Header) GetGasUsed() int64 { return h.header.GasUsed.Int64() } +func (h *Header) GetTime() int64 { return h.header.Time.Int64() } +func (h *Header) GetExtra() []byte { return h.header.Extra } +func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } +func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } + +func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } +func (h *Header) GetHashNoNonce() *Hash { return &Hash{h.header.HashNoNonce()} } + +// Headers represents a slice of headers. +type Headers struct{ headers []*types.Header } + +// Size returns the number of headers in the slice. +func (h *Headers) Size() int { + return len(h.headers) +} + +// Get returns the header at the given index from the slice. +func (h *Headers) Get(index int) (header *Header, _ error) { + if index < 0 || index >= len(h.headers) { + return nil, errors.New("index out of bounds") + } + return &Header{h.headers[index]}, nil +} + +// Block represents an entire block in the Ethereum blockchain. +type Block struct { + block *types.Block +} + +func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } +func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } +func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } +func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } +func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } +func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } +func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } +func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } +func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } +func (b *Block) GetGasLimit() int64 { return b.block.GasLimit().Int64() } +func (b *Block) GetGasUsed() int64 { return b.block.GasUsed().Int64() } +func (b *Block) GetTime() int64 { return b.block.Time().Int64() } +func (b *Block) GetExtra() []byte { return b.block.Extra() } +func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } +func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } + +func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } +func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} } + +func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } +func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } +func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } +func (b *Block) GetTransaction(hash *Hash) *Transaction { + return &Transaction{b.block.Transaction(hash.hash)} +} + +// Transaction represents a single Ethereum transaction. +type Transaction struct { + tx *types.Transaction +} + +// NewTransaction creates a new transaction with the given properties. +func NewTransaction(nonce int64, to *Address, amount, gasLimit, gasPrice *BigInt, data []byte) *Transaction { + return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, gasLimit.bigint, gasPrice.bigint, data)} +} + +func (tx *Transaction) GetData() []byte { return tx.tx.Data() } +func (tx *Transaction) GetGas() int64 { return tx.tx.Gas().Int64() } +func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } +func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } +func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } + +func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } +func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.HomesteadSigner{})} } +func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } + +func (tx *Transaction) GetFrom() (address *Address, _ error) { + from, err := types.Sender(types.HomesteadSigner{}, tx.tx) + return &Address{from}, err +} + +func (tx *Transaction) GetTo() *Address { + if to := tx.tx.To(); to != nil { + return &Address{*to} + } + return nil +} + +func (tx *Transaction) WithSignature(sig []byte) (signedTx *Transaction, _ error) { + rawTx, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig) + return &Transaction{rawTx}, err +} + +// Transactions represents a slice of transactions. +type Transactions struct{ txs types.Transactions } + +// Size returns the number of transactions in the slice. +func (txs *Transactions) Size() int { + return len(txs.txs) +} + +// Get returns the transaction at the given index from the slice. +func (txs *Transactions) Get(index int) (tx *Transaction, _ error) { + if index < 0 || index >= len(txs.txs) { + return nil, errors.New("index out of bounds") + } + return &Transaction{txs.txs[index]}, nil +} + +// Receipt represents the results of a transaction. +type Receipt struct { + receipt *types.Receipt +} + +func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } +func (r *Receipt) GetCumulativeGasUsed() *BigInt { return &BigInt{r.receipt.CumulativeGasUsed} } +func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } +func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } +func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } +func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } +func (r *Receipt) GetGasUsed() *BigInt { return &BigInt{r.receipt.GasUsed} } diff --git a/vendor/github.com/ethereum/go-ethereum/mobile/vm.go b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go new file mode 100644 index 0000000..72093e3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/mobile/vm.go @@ -0,0 +1,56 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains all the wrappers from the core/types package. + +package geth + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core/types" +) + +// Log represents a contract log event. These events are generated by the LOG +// opcode and stored/indexed by the node. +type Log struct { + log *types.Log +} + +func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } +func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } +func (l *Log) GetData() []byte { return l.log.Data } +func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } +func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } +func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } +func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } +func (l *Log) GetIndex() int { return int(l.log.Index) } + +// Logs represents a slice of VM logs. +type Logs struct{ logs []*types.Log } + +// Size returns the number of logs in the slice. +func (l *Logs) Size() int { + return len(l.logs) +} + +// Get returns the log at the given index from the slice. +func (l *Logs) Get(index int) (log *Log, _ error) { + if index < 0 || index >= len(l.logs) { + return nil, errors.New("index out of bounds") + } + return &Log{l.logs[index]}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/api.go b/vendor/github.com/ethereum/go-ethereum/node/api.go new file mode 100644 index 0000000..3c451fc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/api.go @@ -0,0 +1,335 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "fmt" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/rcrowley/go-metrics" +) + +// PrivateAdminAPI is the collection of administrative API methods exposed only +// over a secure RPC channel. +type PrivateAdminAPI struct { + node *Node // Node interfaced by this API +} + +// NewPrivateAdminAPI creates a new API definition for the private admin methods +// of the node itself. +func NewPrivateAdminAPI(node *Node) *PrivateAdminAPI { + return &PrivateAdminAPI{node: node} +} + +// AddPeer requests connecting to a remote node, and also maintaining the new +// connection at all times, even reconnecting if it is lost. +func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) { + // Make sure the server is running, fail otherwise + server := api.node.Server() + if server == nil { + return false, ErrNodeStopped + } + // Try to add the url as a static peer and return + node, err := discover.ParseNode(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + server.AddPeer(node) + return true, nil +} + +// RemovePeer disconnects from a a remote node if the connection exists +func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) { + // Make sure the server is running, fail otherwise + server := api.node.Server() + if server == nil { + return false, ErrNodeStopped + } + // Try to remove the url as a static peer and return + node, err := discover.ParseNode(url) + if err != nil { + return false, fmt.Errorf("invalid enode: %v", err) + } + server.RemovePeer(node) + return true, nil +} + +// StartRPC starts the HTTP RPC API server. +func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string) (bool, error) { + api.node.lock.Lock() + defer api.node.lock.Unlock() + + if api.node.httpHandler != nil { + return false, fmt.Errorf("HTTP RPC already running on %s", api.node.httpEndpoint) + } + + if host == nil { + h := DefaultHTTPHost + if api.node.config.HTTPHost != "" { + h = api.node.config.HTTPHost + } + host = &h + } + if port == nil { + port = &api.node.config.HTTPPort + } + if cors == nil { + cors = &api.node.config.HTTPCors + } + + modules := api.node.httpWhitelist + if apis != nil { + modules = nil + for _, m := range strings.Split(*apis, ",") { + modules = append(modules, strings.TrimSpace(m)) + } + } + + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *cors); err != nil { + return false, err + } + return true, nil +} + +// StopRPC terminates an already running HTTP RPC API endpoint. +func (api *PrivateAdminAPI) StopRPC() (bool, error) { + api.node.lock.Lock() + defer api.node.lock.Unlock() + + if api.node.httpHandler == nil { + return false, fmt.Errorf("HTTP RPC not running") + } + api.node.stopHTTP() + return true, nil +} + +// StartWS starts the websocket RPC API server. +func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) { + api.node.lock.Lock() + defer api.node.lock.Unlock() + + if api.node.wsHandler != nil { + return false, fmt.Errorf("WebSocket RPC already running on %s", api.node.wsEndpoint) + } + + if host == nil { + h := DefaultWSHost + if api.node.config.WSHost != "" { + h = api.node.config.WSHost + } + host = &h + } + if port == nil { + port = &api.node.config.WSPort + } + if allowedOrigins == nil { + allowedOrigins = &api.node.config.WSOrigins + } + + modules := api.node.config.WSModules + if apis != nil { + modules = nil + for _, m := range strings.Split(*apis, ",") { + modules = append(modules, strings.TrimSpace(m)) + } + } + + if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *allowedOrigins); err != nil { + return false, err + } + return true, nil +} + +// StopRPC terminates an already running websocket RPC API endpoint. +func (api *PrivateAdminAPI) StopWS() (bool, error) { + api.node.lock.Lock() + defer api.node.lock.Unlock() + + if api.node.wsHandler == nil { + return false, fmt.Errorf("WebSocket RPC not running") + } + api.node.stopWS() + return true, nil +} + +// PublicAdminAPI is the collection of administrative API methods exposed over +// both secure and unsecure RPC channels. +type PublicAdminAPI struct { + node *Node // Node interfaced by this API +} + +// NewPublicAdminAPI creates a new API definition for the public admin methods +// of the node itself. +func NewPublicAdminAPI(node *Node) *PublicAdminAPI { + return &PublicAdminAPI{node: node} +} + +// Peers retrieves all the information we know about each individual peer at the +// protocol granularity. +func (api *PublicAdminAPI) Peers() ([]*p2p.PeerInfo, error) { + server := api.node.Server() + if server == nil { + return nil, ErrNodeStopped + } + return server.PeersInfo(), nil +} + +// NodeInfo retrieves all the information we know about the host node at the +// protocol granularity. +func (api *PublicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) { + server := api.node.Server() + if server == nil { + return nil, ErrNodeStopped + } + return server.NodeInfo(), nil +} + +// Datadir retrieves the current data directory the node is using. +func (api *PublicAdminAPI) Datadir() string { + return api.node.DataDir() +} + +// PublicDebugAPI is the collection of debugging related API methods exposed over +// both secure and unsecure RPC channels. +type PublicDebugAPI struct { + node *Node // Node interfaced by this API +} + +// NewPublicDebugAPI creates a new API definition for the public debug methods +// of the node itself. +func NewPublicDebugAPI(node *Node) *PublicDebugAPI { + return &PublicDebugAPI{node: node} +} + +// Metrics retrieves all the known system metric collected by the node. +func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) { + // Create a rate formatter + units := []string{"", "K", "M", "G", "T", "E", "P"} + round := func(value float64, prec int) string { + unit := 0 + for value >= 1000 { + unit, value, prec = unit+1, value/1000, 2 + } + return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value) + } + format := func(total float64, rate float64) string { + return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) + } + // Iterate over all the metrics, and just dump for now + counters := make(map[string]interface{}) + metrics.DefaultRegistry.Each(func(name string, metric interface{}) { + // Create or retrieve the counter hierarchy for this metric + root, parts := counters, strings.Split(name, "/") + for _, part := range parts[:len(parts)-1] { + if _, ok := root[part]; !ok { + root[part] = make(map[string]interface{}) + } + root = root[part].(map[string]interface{}) + } + name = parts[len(parts)-1] + + // Fill the counter with the metric details, formatting if requested + if raw { + switch metric := metric.(type) { + case metrics.Meter: + root[name] = map[string]interface{}{ + "AvgRate01Min": metric.Rate1(), + "AvgRate05Min": metric.Rate5(), + "AvgRate15Min": metric.Rate15(), + "MeanRate": metric.RateMean(), + "Overall": float64(metric.Count()), + } + + case metrics.Timer: + root[name] = map[string]interface{}{ + "AvgRate01Min": metric.Rate1(), + "AvgRate05Min": metric.Rate5(), + "AvgRate15Min": metric.Rate15(), + "MeanRate": metric.RateMean(), + "Overall": float64(metric.Count()), + "Percentiles": map[string]interface{}{ + "5": metric.Percentile(0.05), + "20": metric.Percentile(0.2), + "50": metric.Percentile(0.5), + "80": metric.Percentile(0.8), + "95": metric.Percentile(0.95), + }, + } + + default: + root[name] = "Unknown metric type" + } + } else { + switch metric := metric.(type) { + case metrics.Meter: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Overall": format(float64(metric.Count()), metric.RateMean()), + } + + case metrics.Timer: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Overall": format(float64(metric.Count()), metric.RateMean()), + "Maximum": time.Duration(metric.Max()).String(), + "Minimum": time.Duration(metric.Min()).String(), + "Percentiles": map[string]interface{}{ + "5": time.Duration(metric.Percentile(0.05)).String(), + "20": time.Duration(metric.Percentile(0.2)).String(), + "50": time.Duration(metric.Percentile(0.5)).String(), + "80": time.Duration(metric.Percentile(0.8)).String(), + "95": time.Duration(metric.Percentile(0.95)).String(), + }, + } + + default: + root[name] = "Unknown metric type" + } + } + }) + return counters, nil +} + +// PublicWeb3API offers helper utils +type PublicWeb3API struct { + stack *Node +} + +// NewPublicWeb3API creates a new Web3Service instance +func NewPublicWeb3API(stack *Node) *PublicWeb3API { + return &PublicWeb3API{stack} +} + +// ClientVersion returns the node name +func (s *PublicWeb3API) ClientVersion() string { + return s.stack.Server().Name +} + +// Sha3 applies the ethereum sha3 implementation on the input. +// It assumes the input is hex encoded. +func (s *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { + return crypto.Keccak256(input) +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/config.go b/vendor/github.com/ethereum/go-ethereum/node/config.go new file mode 100644 index 0000000..c09f517 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/config.go @@ -0,0 +1,450 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/accounts/usbwallet" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +var ( + datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key + datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore + datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list + datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list + datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos +) + +// Config represents a small collection of configuration values to fine tune the +// P2P network layer of a protocol stack. These values can be further extended by +// all registered services. +type Config struct { + // Name sets the instance name of the node. It must not contain the / character and is + // used in the devp2p node identifier. The instance name of geth is "geth". If no + // value is specified, the basename of the current executable is used. + Name string + + // UserIdent, if set, is used as an additional component in the devp2p node identifier. + UserIdent string + + // Version should be set to the version number of the program. It is used + // in the devp2p node identifier. + Version string + + // DataDir is the file system folder the node should use for any data storage + // requirements. The configured data directory will not be directly shared with + // registered services, instead those can use utility methods to create/access + // databases or flat files. This enables ephemeral nodes which can fully reside + // in memory. + DataDir string + + // KeyStoreDir is the file system folder that contains private keys. The directory can + // be specified as a relative path, in which case it is resolved relative to the + // current directory. + // + // If KeyStoreDir is empty, the default location is the "keystore" subdirectory of + // DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory + // is created by New and destroyed when the node is stopped. + KeyStoreDir string + + // UseLightweightKDF lowers the memory and CPU requirements of the key store + // scrypt KDF at the expense of security. + UseLightweightKDF bool + + // IPCPath is the requested location to place the IPC endpoint. If the path is + // a simple file name, it is placed inside the data directory (or on the root + // pipe path on Windows), whereas if it's a resolvable path name (absolute or + // relative), then that specific path is enforced. An empty path disables IPC. + IPCPath string + + // This field should be a valid secp256k1 private key that will be used for both + // remote peer identification as well as network traffic encryption. If no key + // is configured, the preset one is loaded from the data dir, generating it if + // needed. + PrivateKey *ecdsa.PrivateKey + + // NoDiscovery specifies whether the peer discovery mechanism should be started + // or not. Disabling is usually useful for protocol debugging (manual topology). + NoDiscovery bool + + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. + DiscoveryV5 bool + + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + + // Restrict communication to white listed IP networks. + // The whitelist only applies when non-nil. + NetRestrict *netutil.Netlist + + // BootstrapNodes used to establish connectivity with the rest of the network. + BootstrapNodes []*discover.Node + + // BootstrapNodesV5 used to establish connectivity with the rest of the network + // using the V5 discovery protocol. + BootstrapNodesV5 []*discv5.Node + + // Network interface address on which the node should listen for inbound peers. + ListenAddr string + + // If set to a non-nil value, the given NAT port mapper is used to make the + // listening port available to the Internet. + NAT nat.Interface + + // If Dialer is set to a non-nil value, the given Dialer is used to dial outbound + // peer connections. + Dialer *net.Dialer + + // If NoDial is true, the node will not dial any peers. + NoDial bool + + // MaxPeers is the maximum number of peers that can be connected. If this is + // set to zero, then only the configured static and trusted peers can connect. + MaxPeers int + + // MaxPendingPeers is the maximum number of peers that can be pending in the + // handshake phase, counted separately for inbound and outbound connections. + // Zero defaults to preset values. + MaxPendingPeers int + + // HTTPHost is the host interface on which to start the HTTP RPC server. If this + // field is empty, no HTTP API endpoint will be started. + HTTPHost string + + // HTTPPort is the TCP port number on which to start the HTTP RPC server. The + // default zero value is/ valid and will pick a port number randomly (useful + // for ephemeral nodes). + HTTPPort int + + // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting + // clients. Please be aware that CORS is a browser enforced security, it's fully + // useless for custom HTTP clients. + HTTPCors string + + // HTTPModules is a list of API modules to expose via the HTTP RPC interface. + // If the module list is empty, all RPC API endpoints designated public will be + // exposed. + HTTPModules []string + + // WSHost is the host interface on which to start the websocket RPC server. If + // this field is empty, no websocket API endpoint will be started. + WSHost string + + // WSPort is the TCP port number on which to start the websocket RPC server. The + // default zero value is/ valid and will pick a port number randomly (useful for + // ephemeral nodes). + WSPort int + + // WSOrigins is the list of domain to accept websocket requests from. Please be + // aware that the server can only act upon the HTTP request the client sends and + // cannot verify the validity of the request header. + WSOrigins string + + // WSModules is a list of API modules to expose via the websocket RPC interface. + // If the module list is empty, all RPC API endpoints designated public will be + // exposed. + WSModules []string +} + +// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into +// account the set data folders as well as the designated platform we're currently +// running on. +func (c *Config) IPCEndpoint() string { + // Short circuit if IPC has not been enabled + if c.IPCPath == "" { + return "" + } + // On windows we can only use plain top-level pipes + if runtime.GOOS == "windows" { + if strings.HasPrefix(c.IPCPath, `\\.\pipe\`) { + return c.IPCPath + } + return `\\.\pipe\` + c.IPCPath + } + // Resolve names into the data directory full paths otherwise + if filepath.Base(c.IPCPath) == c.IPCPath { + if c.DataDir == "" { + return filepath.Join(os.TempDir(), c.IPCPath) + } + return filepath.Join(c.DataDir, c.IPCPath) + } + return c.IPCPath +} + +// NodeDB returns the path to the discovery node database. +func (c *Config) NodeDB() string { + if c.DataDir == "" { + return "" // ephemeral + } + return c.resolvePath("nodes") +} + +// DefaultIPCEndpoint returns the IPC path used by default. +func DefaultIPCEndpoint(clientIdentifier string) string { + if clientIdentifier == "" { + clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") + if clientIdentifier == "" { + panic("empty executable name") + } + } + config := &Config{DataDir: DefaultDataDir(), IPCPath: clientIdentifier + ".ipc"} + return config.IPCEndpoint() +} + +// HTTPEndpoint resolves an HTTP endpoint based on the configured host interface +// and port parameters. +func (c *Config) HTTPEndpoint() string { + if c.HTTPHost == "" { + return "" + } + return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort) +} + +// DefaultHTTPEndpoint returns the HTTP endpoint used by default. +func DefaultHTTPEndpoint() string { + config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort} + return config.HTTPEndpoint() +} + +// WSEndpoint resolves an websocket endpoint based on the configured host interface +// and port parameters. +func (c *Config) WSEndpoint() string { + if c.WSHost == "" { + return "" + } + return fmt.Sprintf("%s:%d", c.WSHost, c.WSPort) +} + +// DefaultWSEndpoint returns the websocket endpoint used by default. +func DefaultWSEndpoint() string { + config := &Config{WSHost: DefaultWSHost, WSPort: DefaultWSPort} + return config.WSEndpoint() +} + +// NodeName returns the devp2p node identifier. +func (c *Config) NodeName() string { + name := c.name() + // Backwards compatibility: previous versions used title-cased "Geth", keep that. + if name == "geth" || name == "geth-testnet" { + name = "Geth" + } + if c.UserIdent != "" { + name += "/" + c.UserIdent + } + if c.Version != "" { + name += "/v" + c.Version + } + name += "/" + runtime.GOOS + name += "/" + runtime.Version() + return name +} + +func (c *Config) name() string { + if c.Name == "" { + progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") + if progname == "" { + panic("empty executable name, set Config.Name") + } + return progname + } + return c.Name +} + +// These resources are resolved differently for "geth" instances. +var isOldGethResource = map[string]bool{ + "chaindata": true, + "nodes": true, + "nodekey": true, + "static-nodes.json": true, + "trusted-nodes.json": true, +} + +// resolvePath resolves path in the instance directory. +func (c *Config) resolvePath(path string) string { + if filepath.IsAbs(path) { + return path + } + if c.DataDir == "" { + return "" + } + // Backwards-compatibility: ensure that data directory files created + // by geth 1.4 are used if they exist. + if c.name() == "geth" && isOldGethResource[path] { + oldpath := "" + if c.Name == "geth" { + oldpath = filepath.Join(c.DataDir, path) + } + if oldpath != "" && common.FileExist(oldpath) { + // TODO: print warning + return oldpath + } + } + return filepath.Join(c.instanceDir(), path) +} + +func (c *Config) instanceDir() string { + if c.DataDir == "" { + return "" + } + return filepath.Join(c.DataDir, c.name()) +} + +// NodeKey retrieves the currently configured private key of the node, checking +// first any manually set key, falling back to the one found in the configured +// data folder. If no key can be found, a new one is generated. +func (c *Config) NodeKey() *ecdsa.PrivateKey { + // Use any specifically configured key. + if c.PrivateKey != nil { + return c.PrivateKey + } + // Generate ephemeral key if no datadir is being used. + if c.DataDir == "" { + key, err := crypto.GenerateKey() + if err != nil { + glog.Fatalf("Failed to generate ephemeral node key: %v", err) + } + return key + } + + keyfile := c.resolvePath(datadirPrivateKey) + if key, err := crypto.LoadECDSA(keyfile); err == nil { + return key + } + // No persistent key found, generate and store a new one. + key, err := crypto.GenerateKey() + if err != nil { + glog.Fatalf("Failed to generate node key: %v", err) + } + instanceDir := filepath.Join(c.DataDir, c.name()) + if err := os.MkdirAll(instanceDir, 0700); err != nil { + glog.V(logger.Error).Infof("Failed to persist node key: %v", err) + return key + } + keyfile = filepath.Join(instanceDir, datadirPrivateKey) + if err := crypto.SaveECDSA(keyfile, key); err != nil { + glog.V(logger.Error).Infof("Failed to persist node key: %v", err) + } + return key +} + +// StaticNodes returns a list of node enode URLs configured as static nodes. +func (c *Config) StaticNodes() []*discover.Node { + return c.parsePersistentNodes(c.resolvePath(datadirStaticNodes)) +} + +// TrusterNodes returns a list of node enode URLs configured as trusted nodes. +func (c *Config) TrusterNodes() []*discover.Node { + return c.parsePersistentNodes(c.resolvePath(datadirTrustedNodes)) +} + +// parsePersistentNodes parses a list of discovery node URLs loaded from a .json +// file from within the data directory. +func (c *Config) parsePersistentNodes(path string) []*discover.Node { + // Short circuit if no node config is present + if c.DataDir == "" { + return nil + } + if _, err := os.Stat(path); err != nil { + return nil + } + // Load the nodes from the config file. + var nodelist []string + if err := common.LoadJSON(path, &nodelist); err != nil { + glog.V(logger.Error).Infof("Can't load node file %s: %v", path, err) + return nil + } + // Interpret the list as a discovery node array + var nodes []*discover.Node + for _, url := range nodelist { + if url == "" { + continue + } + node, err := discover.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err) + continue + } + nodes = append(nodes, node) + } + return nodes +} + +func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { + scryptN := keystore.StandardScryptN + scryptP := keystore.StandardScryptP + if conf.UseLightweightKDF { + scryptN = keystore.LightScryptN + scryptP = keystore.LightScryptP + } + + var ( + keydir string + ephemeral string + err error + ) + switch { + case filepath.IsAbs(conf.KeyStoreDir): + keydir = conf.KeyStoreDir + case conf.DataDir != "": + if conf.KeyStoreDir == "" { + keydir = filepath.Join(conf.DataDir, datadirDefaultKeyStore) + } else { + keydir, err = filepath.Abs(conf.KeyStoreDir) + } + case conf.KeyStoreDir != "": + keydir, err = filepath.Abs(conf.KeyStoreDir) + default: + // There is no datadir. + keydir, err = ioutil.TempDir("", "go-ethereum-keystore") + ephemeral = keydir + } + if err != nil { + return nil, "", err + } + if err := os.MkdirAll(keydir, 0700); err != nil { + return nil, "", err + } + // Assemble the account manager and supported backends + backends := []accounts.Backend{ + keystore.NewKeyStore(keydir, scryptN, scryptP), + } + if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { + glog.V(logger.Warn).Infof("Failed to start Ledger hub, disabling: %v", err) + } else { + backends = append(backends, ledgerhub) + } + return accounts.NewManager(backends...), ephemeral, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/config_test.go b/vendor/github.com/ethereum/go-ethereum/node/config_test.go new file mode 100644 index 0000000..c0eda72 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/config_test.go @@ -0,0 +1,149 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/ethereum/go-ethereum/crypto" +) + +// Tests that datadirs can be successfully created, be them manually configured +// ones or automatically generated temporary ones. +func TestDatadirCreation(t *testing.T) { + // Create a temporary data dir and check that it can be used by a node + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create manual data dir: %v", err) + } + defer os.RemoveAll(dir) + + if _, err := New(&Config{DataDir: dir}); err != nil { + t.Fatalf("failed to create stack with existing datadir: %v", err) + } + // Generate a long non-existing datadir path and check that it gets created by a node + dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f") + if _, err := New(&Config{DataDir: dir}); err != nil { + t.Fatalf("failed to create stack with creatable datadir: %v", err) + } + if _, err := os.Stat(dir); err != nil { + t.Fatalf("freshly created datadir not accessible: %v", err) + } + // Verify that an impossible datadir fails creation + file, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("failed to create temporary file: %v", err) + } + defer os.Remove(file.Name()) + + dir = filepath.Join(file.Name(), "invalid/path") + if _, err := New(&Config{DataDir: dir}); err == nil { + t.Fatalf("protocol stack created with an invalid datadir") + } +} + +// Tests that IPC paths are correctly resolved to valid endpoints of different +// platforms. +func TestIPCPathResolution(t *testing.T) { + var tests = []struct { + DataDir string + IPCPath string + Windows bool + Endpoint string + }{ + {"", "", false, ""}, + {"data", "", false, ""}, + {"", "geth.ipc", false, filepath.Join(os.TempDir(), "geth.ipc")}, + {"data", "geth.ipc", false, "data/geth.ipc"}, + {"data", "./geth.ipc", false, "./geth.ipc"}, + {"data", "/geth.ipc", false, "/geth.ipc"}, + {"", "", true, ``}, + {"data", "", true, ``}, + {"", "geth.ipc", true, `\\.\pipe\geth.ipc`}, + {"data", "geth.ipc", true, `\\.\pipe\geth.ipc`}, + {"data", `\\.\pipe\geth.ipc`, true, `\\.\pipe\geth.ipc`}, + } + for i, test := range tests { + // Only run when platform/test match + if (runtime.GOOS == "windows") == test.Windows { + if endpoint := (&Config{DataDir: test.DataDir, IPCPath: test.IPCPath}).IPCEndpoint(); endpoint != test.Endpoint { + t.Errorf("test %d: IPC endpoint mismatch: have %s, want %s", i, endpoint, test.Endpoint) + } + } + } +} + +// Tests that node keys can be correctly created, persisted, loaded and/or made +// ephemeral. +func TestNodeKeyPersistency(t *testing.T) { + // Create a temporary folder and make sure no key is present + dir, err := ioutil.TempDir("", "node-test") + if err != nil { + t.Fatalf("failed to create temporary data directory: %v", err) + } + defer os.RemoveAll(dir) + + keyfile := filepath.Join(dir, "unit-test", datadirPrivateKey) + + // Configure a node with a preset key and ensure it's not persisted + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("failed to generate one-shot node key: %v", err) + } + config := &Config{Name: "unit-test", DataDir: dir, PrivateKey: key} + config.NodeKey() + if _, err := os.Stat(filepath.Join(keyfile)); err == nil { + t.Fatalf("one-shot node key persisted to data directory") + } + + // Configure a node with no preset key and ensure it is persisted this time + config = &Config{Name: "unit-test", DataDir: dir} + config.NodeKey() + if _, err := os.Stat(keyfile); err != nil { + t.Fatalf("node key not persisted to data directory: %v", err) + } + if _, err = crypto.LoadECDSA(keyfile); err != nil { + t.Fatalf("failed to load freshly persisted node key: %v", err) + } + blob1, err := ioutil.ReadFile(keyfile) + if err != nil { + t.Fatalf("failed to read freshly persisted node key: %v", err) + } + + // Configure a new node and ensure the previously persisted key is loaded + config = &Config{Name: "unit-test", DataDir: dir} + config.NodeKey() + blob2, err := ioutil.ReadFile(filepath.Join(keyfile)) + if err != nil { + t.Fatalf("failed to read previously persisted node key: %v", err) + } + if !bytes.Equal(blob1, blob2) { + t.Fatalf("persisted node key mismatch: have %x, want %x", blob2, blob1) + } + + // Configure ephemeral node and ensure no key is dumped locally + config = &Config{Name: "unit-test", DataDir: ""} + config.NodeKey() + if _, err := os.Stat(filepath.Join(".", "unit-test", datadirPrivateKey)); err == nil { + t.Fatalf("ephemeral node key persisted to disk") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/defaults.go b/vendor/github.com/ethereum/go-ethereum/node/defaults.go new file mode 100644 index 0000000..bfe257c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/defaults.go @@ -0,0 +1,60 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "os" + "os/user" + "path/filepath" + "runtime" +) + +const ( + DefaultIPCSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket + DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server + DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server + DefaultWSHost = "localhost" // Default host interface for the websocket RPC server + DefaultWSPort = 8546 // Default TCP port for the websocket RPC server +) + +// DefaultDataDir is the default data directory to use for the databases and other +// persistence requirements. +func DefaultDataDir() string { + // Try to place the data folder in the user's home dir + home := homeDir() + if home != "" { + if runtime.GOOS == "darwin" { + return filepath.Join(home, "Library", "Ethereum") + } else if runtime.GOOS == "windows" { + return filepath.Join(home, "AppData", "Roaming", "Ethereum") + } else { + return filepath.Join(home, ".ethereum") + } + } + // As we cannot guess a stable location, return empty and handle later + return "" +} + +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/doc.go b/vendor/github.com/ethereum/go-ethereum/node/doc.go new file mode 100644 index 0000000..f009e6f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/doc.go @@ -0,0 +1,90 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package node sets up multi-protocol Ethereum nodes. + +In the model exposed by this package, a node is a collection of services which use shared +resources to provide RPC APIs. Services can also offer devp2p protocols, which are wired +up to the devp2p network when the node instance is started. + + +Resources Managed By Node + +All file-system resources used by a node instance are located in a directory called the +data directory. The location of each resource can be overridden through additional node +configuration. The data directory is optional. If it is not set and the location of a +resource is otherwise unspecified, package node will create the resource in memory. + +To access to the devp2p network, Node configures and starts p2p.Server. Each host on the +devp2p network has a unique identifier, the node key. The Node instance persists this key +across restarts. Node also loads static and trusted node lists and ensures that knowledge +about other hosts is persisted. + +JSON-RPC servers which run HTTP, WebSocket or IPC can be started on a Node. RPC modules +offered by registered services will be offered on those endpoints. Users can restrict any +endpoint to a subset of RPC modules. Node itself offers the "debug", "admin" and "web3" +modules. + +Service implementations can open LevelDB databases through the service context. Package +node chooses the file system location of each database. If the node is configured to run +without a data directory, databases are opened in memory instead. + +Node also creates the shared store of encrypted Ethereum account keys. Services can access +the account manager through the service context. + + +Sharing Data Directory Among Instances + +Multiple node instances can share a single data directory if they have distinct instance +names (set through the Name config option). Sharing behaviour depends on the type of +resource. + +devp2p-related resources (node key, static/trusted node lists, known hosts database) are +stored in a directory with the same name as the instance. Thus, multiple node instances +using the same data directory will store this information in different subdirectories of +the data directory. + +LevelDB databases are also stored within the instance subdirectory. If multiple node +instances use the same data directory, openening the databases with identical names will +create one database for each instance. + +The account key store is shared among all node instances using the same data directory +unless its location is changed through the KeyStoreDir configuration option. + + +Data Directory Sharing Example + +In this exanple, two node instances named A and B are started with the same data +directory. Mode instance A opens the database "db", node instance B opens the databases +"db" and "db-2". The following files will be created in the data directory: + + data-directory/ + A/ + nodekey -- devp2p node key of instance A + nodes/ -- devp2p discovery knowledge database of instance A + db/ -- LevelDB content for "db" + A.ipc -- JSON-RPC UNIX domain socket endpoint of instance A + B/ + nodekey -- devp2p node key of node B + nodes/ -- devp2p discovery knowledge database of instance B + static-nodes.json -- devp2p static node list of instance B + db/ -- LevelDB content for "db" + db-2/ -- LevelDB content for "db-2" + B.ipc -- JSON-RPC UNIX domain socket endpoint of instance A + keystore/ -- account key store, used by both instances +*/ +package node diff --git a/vendor/github.com/ethereum/go-ethereum/node/errors.go b/vendor/github.com/ethereum/go-ethereum/node/errors.go new file mode 100644 index 0000000..bd5ddeb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/errors.go @@ -0,0 +1,45 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "fmt" + "reflect" +) + +// DuplicateServiceError is returned during Node startup if a registered service +// constructor returns a service of the same type that was already started. +type DuplicateServiceError struct { + Kind reflect.Type +} + +// Error generates a textual representation of the duplicate service error. +func (e *DuplicateServiceError) Error() string { + return fmt.Sprintf("duplicate service: %v", e.Kind) +} + +// StopError is returned if a Node fails to stop either any of its registered +// services or itself. +type StopError struct { + Server error + Services map[reflect.Type]error +} + +// Error generates a textual representation of the stop error. +func (e *StopError) Error() string { + return fmt.Sprintf("server: %v, services: %v", e.Server, e.Services) +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/node.go b/vendor/github.com/ethereum/go-ethereum/node/node.go new file mode 100644 index 0000000..4b56fba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/node.go @@ -0,0 +1,685 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "errors" + "net" + "os" + "path/filepath" + "reflect" + "strings" + "sync" + "syscall" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +var ( + ErrDatadirUsed = errors.New("datadir already used") + ErrNodeStopped = errors.New("node not started") + ErrNodeRunning = errors.New("node already running") + ErrServiceUnknown = errors.New("unknown service") + + datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true} +) + +// Node is a container on which services can be registered. +type Node struct { + eventmux *event.TypeMux // Event multiplexer used between the services of a stack + config *Config + accman *accounts.Manager + + ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop + instanceDirLock storage.Storage // prevents concurrent use of instance directory + + serverConfig p2p.Config + server *p2p.Server // Currently running P2P networking layer + + serviceFuncs []ServiceConstructor // Service constructors (in dependency order) + services map[reflect.Type]Service // Currently running services + + rpcAPIs []rpc.API // List of APIs currently provided by the node + inprocHandler *rpc.Server // In-process RPC request handler to process the API requests + + ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled) + ipcListener net.Listener // IPC RPC listener socket to serve API requests + ipcHandler *rpc.Server // IPC RPC request handler to process the API requests + + httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) + httpWhitelist []string // HTTP RPC modules to allow through this endpoint + httpListener net.Listener // HTTP RPC listener socket to server API requests + httpHandler *rpc.Server // HTTP RPC request handler to process the API requests + + wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) + wsListener net.Listener // Websocket RPC listener socket to server API requests + wsHandler *rpc.Server // Websocket RPC request handler to process the API requests + + stop chan struct{} // Channel to wait for termination notifications + lock sync.RWMutex +} + +// New creates a new P2P node, ready for protocol registration. +func New(conf *Config) (*Node, error) { + // Copy config and resolve the datadir so future changes to the current + // working directory don't affect the node. + confCopy := *conf + conf = &confCopy + if conf.DataDir != "" { + absdatadir, err := filepath.Abs(conf.DataDir) + if err != nil { + return nil, err + } + conf.DataDir = absdatadir + } + // Ensure that the instance name doesn't cause weird conflicts with + // other files in the data directory. + if strings.ContainsAny(conf.Name, `/\`) { + return nil, errors.New(`Config.Name must not contain '/' or '\'`) + } + if conf.Name == datadirDefaultKeyStore { + return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) + } + if strings.HasSuffix(conf.Name, ".ipc") { + return nil, errors.New(`Config.Name cannot end in ".ipc"`) + } + // Ensure that the AccountManager method works before the node has started. + // We rely on this in cmd/geth. + am, ephemeralKeystore, err := makeAccountManager(conf) + if err != nil { + return nil, err + } + // Note: any interaction with Config that would create/touch files + // in the data directory or instance directory is delayed until Start. + return &Node{ + accman: am, + ephemeralKeystore: ephemeralKeystore, + config: conf, + serviceFuncs: []ServiceConstructor{}, + ipcEndpoint: conf.IPCEndpoint(), + httpEndpoint: conf.HTTPEndpoint(), + wsEndpoint: conf.WSEndpoint(), + eventmux: new(event.TypeMux), + }, nil +} + +// Register injects a new service into the node's stack. The service created by +// the passed constructor must be unique in its type with regard to sibling ones. +func (n *Node) Register(constructor ServiceConstructor) error { + n.lock.Lock() + defer n.lock.Unlock() + + if n.server != nil { + return ErrNodeRunning + } + n.serviceFuncs = append(n.serviceFuncs, constructor) + return nil +} + +// Start create a live P2P node and starts running it. +func (n *Node) Start() error { + n.lock.Lock() + defer n.lock.Unlock() + + // Short circuit if the node's already running + if n.server != nil { + return ErrNodeRunning + } + if err := n.openDataDir(); err != nil { + return err + } + + // Initialize the p2p server. This creates the node key and + // discovery databases. + n.serverConfig = p2p.Config{ + PrivateKey: n.config.NodeKey(), + Name: n.config.NodeName(), + Discovery: !n.config.NoDiscovery, + DiscoveryV5: n.config.DiscoveryV5, + DiscoveryV5Addr: n.config.DiscoveryV5Addr, + BootstrapNodes: n.config.BootstrapNodes, + BootstrapNodesV5: n.config.BootstrapNodesV5, + StaticNodes: n.config.StaticNodes(), + TrustedNodes: n.config.TrusterNodes(), + NodeDatabase: n.config.NodeDB(), + ListenAddr: n.config.ListenAddr, + NetRestrict: n.config.NetRestrict, + NAT: n.config.NAT, + Dialer: n.config.Dialer, + NoDial: n.config.NoDial, + MaxPeers: n.config.MaxPeers, + MaxPendingPeers: n.config.MaxPendingPeers, + } + running := &p2p.Server{Config: n.serverConfig} + glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name) + + // Otherwise copy and specialize the P2P configuration + services := make(map[reflect.Type]Service) + for _, constructor := range n.serviceFuncs { + // Create a new context for the particular service + ctx := &ServiceContext{ + config: n.config, + services: make(map[reflect.Type]Service), + EventMux: n.eventmux, + AccountManager: n.accman, + } + for kind, s := range services { // copy needed for threaded access + ctx.services[kind] = s + } + // Construct and save the service + service, err := constructor(ctx) + if err != nil { + return err + } + kind := reflect.TypeOf(service) + if _, exists := services[kind]; exists { + return &DuplicateServiceError{Kind: kind} + } + services[kind] = service + } + // Gather the protocols and start the freshly assembled P2P server + for _, service := range services { + running.Protocols = append(running.Protocols, service.Protocols()...) + } + if err := running.Start(); err != nil { + if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { + return ErrDatadirUsed + } + return err + } + // Start each of the services + started := []reflect.Type{} + for kind, service := range services { + // Start the next service, stopping all previous upon failure + if err := service.Start(running); err != nil { + for _, kind := range started { + services[kind].Stop() + } + running.Stop() + + return err + } + // Mark the service started for potential cleanup + started = append(started, kind) + } + // Lastly start the configured RPC interfaces + if err := n.startRPC(services); err != nil { + for _, service := range services { + service.Stop() + } + running.Stop() + return err + } + // Finish initializing the startup + n.services = services + n.server = running + n.stop = make(chan struct{}) + + return nil +} + +func (n *Node) openDataDir() error { + if n.config.DataDir == "" { + return nil // ephemeral + } + + instdir := filepath.Join(n.config.DataDir, n.config.name()) + if err := os.MkdirAll(instdir, 0700); err != nil { + return err + } + // Try to open the instance directory as LevelDB storage. This creates a lock file + // which prevents concurrent use by another instance as well as accidental use of the + // instance directory as a database. + storage, err := storage.OpenFile(instdir, true) + if err != nil { + return err + } + n.instanceDirLock = storage + return nil +} + +// startRPC is a helper method to start all the various RPC endpoint during node +// startup. It's not meant to be called at any time afterwards as it makes certain +// assumptions about the state of the node. +func (n *Node) startRPC(services map[reflect.Type]Service) error { + // Gather all the possible APIs to surface + apis := n.apis() + for _, service := range services { + apis = append(apis, service.APIs()...) + } + // Start the various API endpoints, terminating all in case of errors + if err := n.startInProc(apis); err != nil { + return err + } + if err := n.startIPC(apis); err != nil { + n.stopInProc() + return err + } + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors); err != nil { + n.stopIPC() + n.stopInProc() + return err + } + if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins); err != nil { + n.stopHTTP() + n.stopIPC() + n.stopInProc() + return err + } + // All API endpoints started successfully + n.rpcAPIs = apis + return nil +} + +// startInProc initializes an in-process RPC endpoint. +func (n *Node) startInProc(apis []rpc.API) error { + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + glog.V(logger.Debug).Infof("InProc registered %T under '%s'", api.Service, api.Namespace) + } + n.inprocHandler = handler + return nil +} + +// stopInProc terminates the in-process RPC endpoint. +func (n *Node) stopInProc() { + if n.inprocHandler != nil { + n.inprocHandler.Stop() + n.inprocHandler = nil + } +} + +// startIPC initializes and starts the IPC RPC endpoint. +func (n *Node) startIPC(apis []rpc.API) error { + // Short circuit if the IPC endpoint isn't being exposed + if n.ipcEndpoint == "" { + return nil + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + glog.V(logger.Debug).Infof("IPC registered %T under '%s'", api.Service, api.Namespace) + } + // All APIs registered, start the IPC listener + var ( + listener net.Listener + err error + ) + if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil { + return err + } + go func() { + glog.V(logger.Info).Infof("IPC endpoint opened: %s", n.ipcEndpoint) + + for { + conn, err := listener.Accept() + if err != nil { + // Terminate if the listener was closed + n.lock.RLock() + closed := n.ipcListener == nil + n.lock.RUnlock() + if closed { + return + } + // Not closed, just some error; report and continue + glog.V(logger.Error).Infof("IPC accept failed: %v", err) + continue + } + go handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation|rpc.OptionSubscriptions) + } + }() + // All listeners booted successfully + n.ipcListener = listener + n.ipcHandler = handler + + return nil +} + +// stopIPC terminates the IPC RPC endpoint. +func (n *Node) stopIPC() { + if n.ipcListener != nil { + n.ipcListener.Close() + n.ipcListener = nil + + glog.V(logger.Info).Infof("IPC endpoint closed: %s", n.ipcEndpoint) + } + if n.ipcHandler != nil { + n.ipcHandler.Stop() + n.ipcHandler = nil + } +} + +// startHTTP initializes and starts the HTTP RPC endpoint. +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors string) error { + // Short circuit if the HTTP endpoint isn't being exposed + if endpoint == "" { + return nil + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + glog.V(logger.Debug).Infof("HTTP registered %T under '%s'", api.Service, api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return err + } + go rpc.NewHTTPServer(cors, handler).Serve(listener) + glog.V(logger.Info).Infof("HTTP endpoint opened: http://%s", endpoint) + + // All listeners booted successfully + n.httpEndpoint = endpoint + n.httpListener = listener + n.httpHandler = handler + + return nil +} + +// stopHTTP terminates the HTTP RPC endpoint. +func (n *Node) stopHTTP() { + if n.httpListener != nil { + n.httpListener.Close() + n.httpListener = nil + + glog.V(logger.Info).Infof("HTTP endpoint closed: http://%s", n.httpEndpoint) + } + if n.httpHandler != nil { + n.httpHandler.Stop() + n.httpHandler = nil + } +} + +// startWS initializes and starts the websocket RPC endpoint. +func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins string) error { + // Short circuit if the WS endpoint isn't being exposed + if endpoint == "" { + return nil + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return err + } + glog.V(logger.Debug).Infof("WebSocket registered %T under '%s'", api.Service, api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return err + } + go rpc.NewWSServer(wsOrigins, handler).Serve(listener) + glog.V(logger.Info).Infof("WebSocket endpoint opened: ws://%s", endpoint) + + // All listeners booted successfully + n.wsEndpoint = endpoint + n.wsListener = listener + n.wsHandler = handler + + return nil +} + +// stopWS terminates the websocket RPC endpoint. +func (n *Node) stopWS() { + if n.wsListener != nil { + n.wsListener.Close() + n.wsListener = nil + + glog.V(logger.Info).Infof("WebSocket endpoint closed: ws://%s", n.wsEndpoint) + } + if n.wsHandler != nil { + n.wsHandler.Stop() + n.wsHandler = nil + } +} + +// Stop terminates a running node along with all it's services. In the node was +// not started, an error is returned. +func (n *Node) Stop() error { + n.lock.Lock() + defer n.lock.Unlock() + + // Short circuit if the node's not running + if n.server == nil { + return ErrNodeStopped + } + + // Terminate the API, services and the p2p server. + n.stopWS() + n.stopHTTP() + n.stopIPC() + n.rpcAPIs = nil + failure := &StopError{ + Services: make(map[reflect.Type]error), + } + for kind, service := range n.services { + if err := service.Stop(); err != nil { + failure.Services[kind] = err + } + } + n.server.Stop() + n.services = nil + n.server = nil + + // Release instance directory lock. + if n.instanceDirLock != nil { + n.instanceDirLock.Close() + n.instanceDirLock = nil + } + + // unblock n.Wait + close(n.stop) + + // Remove the keystore if it was created ephemerally. + var keystoreErr error + if n.ephemeralKeystore != "" { + keystoreErr = os.RemoveAll(n.ephemeralKeystore) + } + + if len(failure.Services) > 0 { + return failure + } + if keystoreErr != nil { + return keystoreErr + } + return nil +} + +// Wait blocks the thread until the node is stopped. If the node is not running +// at the time of invocation, the method immediately returns. +func (n *Node) Wait() { + n.lock.RLock() + if n.server == nil { + return + } + stop := n.stop + n.lock.RUnlock() + + <-stop +} + +// Restart terminates a running node and boots up a new one in its place. If the +// node isn't running, an error is returned. +func (n *Node) Restart() error { + if err := n.Stop(); err != nil { + return err + } + if err := n.Start(); err != nil { + return err + } + return nil +} + +// Attach creates an RPC client attached to an in-process API handler. +func (n *Node) Attach() (*rpc.Client, error) { + n.lock.RLock() + defer n.lock.RUnlock() + + if n.server == nil { + return nil, ErrNodeStopped + } + return rpc.DialInProc(n.inprocHandler), nil +} + +// Server retrieves the currently running P2P network layer. This method is meant +// only to inspect fields of the currently running server, life cycle management +// should be left to this Node entity. +func (n *Node) Server() *p2p.Server { + n.lock.RLock() + defer n.lock.RUnlock() + + return n.server +} + +// Service retrieves a currently running service registered of a specific type. +func (n *Node) Service(service interface{}) error { + n.lock.RLock() + defer n.lock.RUnlock() + + // Short circuit if the node's not running + if n.server == nil { + return ErrNodeStopped + } + // Otherwise try to find the service to return + element := reflect.ValueOf(service).Elem() + if running, ok := n.services[element.Type()]; ok { + element.Set(reflect.ValueOf(running)) + return nil + } + return ErrServiceUnknown +} + +// DataDir retrieves the current datadir used by the protocol stack. +// Deprecated: No files should be stored in this directory, use InstanceDir instead. +func (n *Node) DataDir() string { + return n.config.DataDir +} + +// InstanceDir retrieves the instance directory used by the protocol stack. +func (n *Node) InstanceDir() string { + return n.config.instanceDir() +} + +// AccountManager retrieves the account manager used by the protocol stack. +func (n *Node) AccountManager() *accounts.Manager { + return n.accman +} + +// IPCEndpoint retrieves the current IPC endpoint used by the protocol stack. +func (n *Node) IPCEndpoint() string { + return n.ipcEndpoint +} + +// HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack. +func (n *Node) HTTPEndpoint() string { + return n.httpEndpoint +} + +// WSEndpoint retrieves the current WS endpoint used by the protocol stack. +func (n *Node) WSEndpoint() string { + return n.wsEndpoint +} + +// EventMux retrieves the event multiplexer used by all the network services in +// the current protocol stack. +func (n *Node) EventMux() *event.TypeMux { + return n.eventmux +} + +// OpenDatabase opens an existing database with the given name (or creates one if no +// previous can be found) from within the node's instance directory. If the node is +// ephemeral, a memory database is returned. +func (n *Node) OpenDatabase(name string, cache, handles int) (ethdb.Database, error) { + if n.config.DataDir == "" { + return ethdb.NewMemDatabase() + } + return ethdb.NewLDBDatabase(n.config.resolvePath(name), cache, handles) +} + +// ResolvePath returns the absolute path of a resource in the instance directory. +func (n *Node) ResolvePath(x string) string { + return n.config.resolvePath(x) +} + +// apis returns the collection of RPC descriptors this node offers. +func (n *Node) apis() []rpc.API { + return []rpc.API{ + { + Namespace: "admin", + Version: "1.0", + Service: NewPrivateAdminAPI(n), + }, { + Namespace: "admin", + Version: "1.0", + Service: NewPublicAdminAPI(n), + Public: true, + }, { + Namespace: "debug", + Version: "1.0", + Service: debug.Handler, + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPublicDebugAPI(n), + Public: true, + }, { + Namespace: "web3", + Version: "1.0", + Service: NewPublicWeb3API(n), + Public: true, + }, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/node_example_test.go b/vendor/github.com/ethereum/go-ethereum/node/node_example_test.go new file mode 100644 index 0000000..d2872cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/node_example_test.go @@ -0,0 +1,90 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node_test + +import ( + "fmt" + "log" + + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rpc" +) + +// SampleService is a trivial network service that can be attached to a node for +// life cycle management. +// +// The following methods are needed to implement a node.Service: +// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on +// - APIs() []rpc.API - api methods the service wants to expose on rpc channels +// - Start() error - method invoked when the node is ready to start the service +// - Stop() error - method invoked when the node terminates the service +type SampleService struct{} + +func (s *SampleService) Protocols() []p2p.Protocol { return nil } +func (s *SampleService) APIs() []rpc.API { return nil } +func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil } +func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil } + +func ExampleService() { + // Create a network node to run protocols with the default values. The below list + // is only used to display each of the configuration options. All of these could + // have been omitted if the default behavior is desired. + nodeConfig := &node.Config{ + DataDir: "", // Empty uses ephemeral storage + PrivateKey: nil, // Nil generates a node key on the fly + Name: "", // Any textual node name is allowed + NoDiscovery: false, // Can disable discovering remote nodes + BootstrapNodes: []*discover.Node{}, // List of bootstrap nodes to use + ListenAddr: ":0", // Network interface to listen on + NAT: nil, // UPnP port mapper to use for crossing firewalls + Dialer: nil, // Custom dialer to use for establishing peer connections + NoDial: false, // Can prevent this node from dialing out + MaxPeers: 0, // Number of peers to allow + MaxPendingPeers: 0, // Number of peers allowed to handshake concurrently + } + stack, err := node.New(nodeConfig) + if err != nil { + log.Fatalf("Failed to create network node: %v", err) + } + // Create and register a simple network service. This is done through the definition + // of a node.ServiceConstructor that will instantiate a node.Service. The reason for + // the factory method approach is to support service restarts without relying on the + // individual implementations' support for such operations. + constructor := func(context *node.ServiceContext) (node.Service, error) { + return new(SampleService), nil + } + if err := stack.Register(constructor); err != nil { + log.Fatalf("Failed to register service: %v", err) + } + // Boot up the entire protocol stack, do a restart and terminate + if err := stack.Start(); err != nil { + log.Fatalf("Failed to start the protocol stack: %v", err) + } + if err := stack.Restart(); err != nil { + log.Fatalf("Failed to restart the protocol stack: %v", err) + } + if err := stack.Stop(); err != nil { + log.Fatalf("Failed to stop the protocol stack: %v", err) + } + // Output: + // Service starting... + // Service stopping... + // Service starting... + // Service stopping... +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/node_test.go b/vendor/github.com/ethereum/go-ethereum/node/node_test.go new file mode 100644 index 0000000..408d4cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/node_test.go @@ -0,0 +1,575 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "errors" + "io/ioutil" + "os" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +var ( + testNodeKey, _ = crypto.GenerateKey() +) + +func testNodeConfig() *Config { + return &Config{ + PrivateKey: testNodeKey, + Name: "test node", + } +} + +// Tests that an empty protocol stack can be started, restarted and stopped. +func TestNodeLifeCycle(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Ensure that a stopped node can be stopped again + for i := 0; i < 3; i++ { + if err := stack.Stop(); err != ErrNodeStopped { + t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped) + } + } + // Ensure that a node can be successfully started, but only once + if err := stack.Start(); err != nil { + t.Fatalf("failed to start node: %v", err) + } + if err := stack.Start(); err != ErrNodeRunning { + t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning) + } + // Ensure that a node can be restarted arbitrarily many times + for i := 0; i < 3; i++ { + if err := stack.Restart(); err != nil { + t.Fatalf("iter %d: failed to restart node: %v", i, err) + } + } + // Ensure that a node can be stopped, but only once + if err := stack.Stop(); err != nil { + t.Fatalf("failed to stop node: %v", err) + } + if err := stack.Stop(); err != ErrNodeStopped { + t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped) + } +} + +// Tests that if the data dir is already in use, an appropriate error is returned. +func TestNodeUsedDataDir(t *testing.T) { + // Create a temporary folder to use as the data directory + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create temporary data directory: %v", err) + } + defer os.RemoveAll(dir) + + // Create a new node based on the data directory + original, err := New(&Config{DataDir: dir}) + if err != nil { + t.Fatalf("failed to create original protocol stack: %v", err) + } + if err := original.Start(); err != nil { + t.Fatalf("failed to start original protocol stack: %v", err) + } + defer original.Stop() + + // Create a second node based on the same data directory and ensure failure + duplicate, err := New(&Config{DataDir: dir}) + if err != nil { + t.Fatalf("failed to create duplicate protocol stack: %v", err) + } + if err := duplicate.Start(); err != ErrDatadirUsed { + t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed) + } +} + +// Tests whether services can be registered and duplicates caught. +func TestServiceRegistry(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of unique services and ensure they start successfully + services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC} + for i, constructor := range services { + if err := stack.Register(constructor); err != nil { + t.Fatalf("service #%d: registration failed: %v", i, err) + } + } + if err := stack.Start(); err != nil { + t.Fatalf("failed to start original service stack: %v", err) + } + if err := stack.Stop(); err != nil { + t.Fatalf("failed to stop original service stack: %v", err) + } + // Duplicate one of the services and retry starting the node + if err := stack.Register(NewNoopServiceB); err != nil { + t.Fatalf("duplicate registration failed: %v", err) + } + if err := stack.Start(); err == nil { + t.Fatalf("duplicate service started") + } else { + if _, ok := err.(*DuplicateServiceError); !ok { + t.Fatalf("duplicate error mismatch: have %v, want %v", err, DuplicateServiceError{}) + } + } +} + +// Tests that registered services get started and stopped correctly. +func TestServiceLifeCycle(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of life-cycle instrumented services + services := map[string]InstrumentingWrapper{ + "A": InstrumentedServiceMakerA, + "B": InstrumentedServiceMakerB, + "C": InstrumentedServiceMakerC, + } + started := make(map[string]bool) + stopped := make(map[string]bool) + + for id, maker := range services { + id := id // Closure for the constructor + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + startHook: func(*p2p.Server) { started[id] = true }, + stopHook: func() { stopped[id] = true }, + }, nil + } + if err := stack.Register(maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Start the node and check that all services are running + if err := stack.Start(); err != nil { + t.Fatalf("failed to start protocol stack: %v", err) + } + for id := range services { + if !started[id] { + t.Fatalf("service %s: freshly started service not running", id) + } + if stopped[id] { + t.Fatalf("service %s: freshly started service already stopped", id) + } + } + // Stop the node and check that all services have been stopped + if err := stack.Stop(); err != nil { + t.Fatalf("failed to stop protocol stack: %v", err) + } + for id := range services { + if !stopped[id] { + t.Fatalf("service %s: freshly terminated service still running", id) + } + } +} + +// Tests that services are restarted cleanly as new instances. +func TestServiceRestarts(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Define a service that does not support restarts + var ( + running bool + started int + ) + constructor := func(*ServiceContext) (Service, error) { + running = false + + return &InstrumentedService{ + startHook: func(*p2p.Server) { + if running { + panic("already running") + } + running = true + started++ + }, + }, nil + } + // Register the service and start the protocol stack + if err := stack.Register(constructor); err != nil { + t.Fatalf("failed to register the service: %v", err) + } + if err := stack.Start(); err != nil { + t.Fatalf("failed to start protocol stack: %v", err) + } + defer stack.Stop() + + if !running || started != 1 { + t.Fatalf("running/started mismatch: have %v/%d, want true/1", running, started) + } + // Restart the stack a few times and check successful service restarts + for i := 0; i < 3; i++ { + if err := stack.Restart(); err != nil { + t.Fatalf("iter %d: failed to restart stack: %v", i, err) + } + } + if !running || started != 4 { + t.Fatalf("running/started mismatch: have %v/%d, want true/4", running, started) + } +} + +// Tests that if a service fails to initialize itself, none of the other services +// will be allowed to even start. +func TestServiceConstructionAbortion(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Define a batch of good services + services := map[string]InstrumentingWrapper{ + "A": InstrumentedServiceMakerA, + "B": InstrumentedServiceMakerB, + "C": InstrumentedServiceMakerC, + } + started := make(map[string]bool) + for id, maker := range services { + id := id // Closure for the constructor + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + startHook: func(*p2p.Server) { started[id] = true }, + }, nil + } + if err := stack.Register(maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Register a service that fails to construct itself + failure := errors.New("fail") + failer := func(*ServiceContext) (Service, error) { + return nil, failure + } + if err := stack.Register(failer); err != nil { + t.Fatalf("failer registration failed: %v", err) + } + // Start the protocol stack and ensure none of the services get started + for i := 0; i < 100; i++ { + if err := stack.Start(); err != failure { + t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) + } + for id := range services { + if started[id] { + t.Fatalf("service %s: started should not have", id) + } + delete(started, id) + } + } +} + +// Tests that if a service fails to start, all others started before it will be +// shut down. +func TestServiceStartupAbortion(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of good services + services := map[string]InstrumentingWrapper{ + "A": InstrumentedServiceMakerA, + "B": InstrumentedServiceMakerB, + "C": InstrumentedServiceMakerC, + } + started := make(map[string]bool) + stopped := make(map[string]bool) + + for id, maker := range services { + id := id // Closure for the constructor + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + startHook: func(*p2p.Server) { started[id] = true }, + stopHook: func() { stopped[id] = true }, + }, nil + } + if err := stack.Register(maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Register a service that fails to start + failure := errors.New("fail") + failer := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + start: failure, + }, nil + } + if err := stack.Register(failer); err != nil { + t.Fatalf("failer registration failed: %v", err) + } + // Start the protocol stack and ensure all started services stop + for i := 0; i < 100; i++ { + if err := stack.Start(); err != failure { + t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) + } + for id := range services { + if started[id] && !stopped[id] { + t.Fatalf("service %s: started but not stopped", id) + } + delete(started, id) + delete(stopped, id) + } + } +} + +// Tests that even if a registered service fails to shut down cleanly, it does +// not influece the rest of the shutdown invocations. +func TestServiceTerminationGuarantee(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of good services + services := map[string]InstrumentingWrapper{ + "A": InstrumentedServiceMakerA, + "B": InstrumentedServiceMakerB, + "C": InstrumentedServiceMakerC, + } + started := make(map[string]bool) + stopped := make(map[string]bool) + + for id, maker := range services { + id := id // Closure for the constructor + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + startHook: func(*p2p.Server) { started[id] = true }, + stopHook: func() { stopped[id] = true }, + }, nil + } + if err := stack.Register(maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Register a service that fails to shot down cleanly + failure := errors.New("fail") + failer := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + stop: failure, + }, nil + } + if err := stack.Register(failer); err != nil { + t.Fatalf("failer registration failed: %v", err) + } + // Start the protocol stack, and ensure that a failing shut down terminates all + for i := 0; i < 100; i++ { + // Start the stack and make sure all is online + if err := stack.Start(); err != nil { + t.Fatalf("iter %d: failed to start protocol stack: %v", i, err) + } + for id := range services { + if !started[id] { + t.Fatalf("iter %d, service %s: service not running", i, id) + } + if stopped[id] { + t.Fatalf("iter %d, service %s: service already stopped", i, id) + } + } + // Stop the stack, verify failure and check all terminations + err := stack.Stop() + if err, ok := err.(*StopError); !ok { + t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err) + } else { + failer := reflect.TypeOf(&InstrumentedService{}) + if err.Services[failer] != failure { + t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure) + } + if len(err.Services) != 1 { + t.Fatalf("iter %d: failure count mismatch: have %d, want %d", i, len(err.Services), 1) + } + } + for id := range services { + if !stopped[id] { + t.Fatalf("iter %d, service %s: service not terminated", i, id) + } + delete(started, id) + delete(stopped, id) + } + } +} + +// TestServiceRetrieval tests that individual services can be retrieved. +func TestServiceRetrieval(t *testing.T) { + // Create a simple stack and register two service types + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + if err := stack.Register(NewNoopService); err != nil { + t.Fatalf("noop service registration failed: %v", err) + } + if err := stack.Register(NewInstrumentedService); err != nil { + t.Fatalf("instrumented service registration failed: %v", err) + } + // Make sure none of the services can be retrieved until started + var noopServ *NoopService + if err := stack.Service(&noopServ); err != ErrNodeStopped { + t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) + } + var instServ *InstrumentedService + if err := stack.Service(&instServ); err != ErrNodeStopped { + t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) + } + // Start the stack and ensure everything is retrievable now + if err := stack.Start(); err != nil { + t.Fatalf("failed to start stack: %v", err) + } + defer stack.Stop() + + if err := stack.Service(&noopServ); err != nil { + t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil) + } + if err := stack.Service(&instServ); err != nil { + t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil) + } +} + +// Tests that all protocols defined by individual services get launched. +func TestProtocolGather(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of services with some configured number of protocols + services := map[string]struct { + Count int + Maker InstrumentingWrapper + }{ + "Zero Protocols": {0, InstrumentedServiceMakerA}, + "Single Protocol": {1, InstrumentedServiceMakerB}, + "Many Protocols": {25, InstrumentedServiceMakerC}, + } + for id, config := range services { + protocols := make([]p2p.Protocol, config.Count) + for i := 0; i < len(protocols); i++ { + protocols[i].Name = id + protocols[i].Version = uint(i) + } + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{ + protocols: protocols, + }, nil + } + if err := stack.Register(config.Maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Start the services and ensure all protocols start successfully + if err := stack.Start(); err != nil { + t.Fatalf("failed to start protocol stack: %v", err) + } + defer stack.Stop() + + protocols := stack.Server().Protocols + if len(protocols) != 26 { + t.Fatalf("mismatching number of protocols launched: have %d, want %d", len(protocols), 26) + } + for id, config := range services { + for ver := 0; ver < config.Count; ver++ { + launched := false + for i := 0; i < len(protocols); i++ { + if protocols[i].Name == id && protocols[i].Version == uint(ver) { + launched = true + break + } + } + if !launched { + t.Errorf("configured protocol not launched: %s v%d", id, ver) + } + } + } +} + +// Tests that all APIs defined by individual services get exposed. +func TestAPIGather(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Register a batch of services with some configured APIs + calls := make(chan string, 1) + makeAPI := func(result string) *OneMethodApi { + return &OneMethodApi{fun: func() { calls <- result }} + } + services := map[string]struct { + APIs []rpc.API + Maker InstrumentingWrapper + }{ + "Zero APIs": { + []rpc.API{}, InstrumentedServiceMakerA}, + "Single API": { + []rpc.API{ + {Namespace: "single", Version: "1", Service: makeAPI("single.v1"), Public: true}, + }, InstrumentedServiceMakerB}, + "Many APIs": { + []rpc.API{ + {Namespace: "multi", Version: "1", Service: makeAPI("multi.v1"), Public: true}, + {Namespace: "multi.v2", Version: "2", Service: makeAPI("multi.v2"), Public: true}, + {Namespace: "multi.v2.nested", Version: "2", Service: makeAPI("multi.v2.nested"), Public: true}, + }, InstrumentedServiceMakerC}, + } + + for id, config := range services { + config := config + constructor := func(*ServiceContext) (Service, error) { + return &InstrumentedService{apis: config.APIs}, nil + } + if err := stack.Register(config.Maker(constructor)); err != nil { + t.Fatalf("service %s: registration failed: %v", id, err) + } + } + // Start the services and ensure all API start successfully + if err := stack.Start(); err != nil { + t.Fatalf("failed to start protocol stack: %v", err) + } + defer stack.Stop() + + // Connect to the RPC server and verify the various registered endpoints + client, err := stack.Attach() + if err != nil { + t.Fatalf("failed to connect to the inproc API server: %v", err) + } + defer client.Close() + + tests := []struct { + Method string + Result string + }{ + {"single_theOneMethod", "single.v1"}, + {"multi_theOneMethod", "multi.v1"}, + {"multi.v2_theOneMethod", "multi.v2"}, + {"multi.v2.nested_theOneMethod", "multi.v2.nested"}, + } + for i, test := range tests { + if err := client.Call(nil, test.Method); err != nil { + t.Errorf("test %d: API request failed: %v", i, err) + } + select { + case result := <-calls: + if result != test.Result { + t.Errorf("test %d: result mismatch: have %s, want %s", i, result, test.Result) + } + case <-time.After(time.Second): + t.Fatalf("test %d: rpc execution timeout", i) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/service.go b/vendor/github.com/ethereum/go-ethereum/node/service.go new file mode 100644 index 0000000..1cd1fe8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/service.go @@ -0,0 +1,87 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +// ServiceContext is a collection of service independent options inherited from +// the protocol stack, that is passed to all constructors to be optionally used; +// as well as utility methods to operate on the service environment. +type ServiceContext struct { + config *Config + services map[reflect.Type]Service // Index of the already constructed services + EventMux *event.TypeMux // Event multiplexer used for decoupled notifications + AccountManager *accounts.Manager // Account manager created by the node. +} + +// OpenDatabase opens an existing database with the given name (or creates one +// if no previous can be found) from within the node's data directory. If the +// node is an ephemeral one, a memory database is returned. +func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int) (ethdb.Database, error) { + if ctx.config.DataDir == "" { + return ethdb.NewMemDatabase() + } + return ethdb.NewLDBDatabase(ctx.config.resolvePath(name), cache, handles) +} + +// Service retrieves a currently running service registered of a specific type. +func (ctx *ServiceContext) Service(service interface{}) error { + element := reflect.ValueOf(service).Elem() + if running, ok := ctx.services[element.Type()]; ok { + element.Set(reflect.ValueOf(running)) + return nil + } + return ErrServiceUnknown +} + +// ServiceConstructor is the function signature of the constructors needed to be +// registered for service instantiation. +type ServiceConstructor func(ctx *ServiceContext) (Service, error) + +// Service is an individual protocol that can be registered into a node. +// +// Notes: +// +// • Service life-cycle management is delegated to the node. The service is allowed to +// initialize itself upon creation, but no goroutines should be spun up outside of the +// Start method. +// +// • Restart logic is not required as the node will create a fresh instance +// every time a service is started. +type Service interface { + // Protocols retrieves the P2P protocols the service wishes to start. + Protocols() []p2p.Protocol + + // APIs retrieves the list of RPC descriptors the service provides + APIs() []rpc.API + + // Start is called after all services have been constructed and the networking + // layer was also initialized to spawn any goroutines required by the service. + Start(server *p2p.Server) error + + // Stop terminates all goroutines belonging to the service, blocking until they + // are all terminated. + Stop() error +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/service_test.go b/vendor/github.com/ethereum/go-ethereum/node/service_test.go new file mode 100644 index 0000000..a7ae439 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/service_test.go @@ -0,0 +1,97 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package node + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +// Tests that databases are correctly created persistent or ephemeral based on +// the configured service context. +func TestContextDatabases(t *testing.T) { + // Create a temporary folder and ensure no database is contained within + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create temporary data directory: %v", err) + } + defer os.RemoveAll(dir) + + if _, err := os.Stat(filepath.Join(dir, "database")); err == nil { + t.Fatalf("non-created database already exists") + } + // Request the opening/creation of a database and ensure it persists to disk + ctx := &ServiceContext{config: &Config{Name: "unit-test", DataDir: dir}} + db, err := ctx.OpenDatabase("persistent", 0, 0) + if err != nil { + t.Fatalf("failed to open persistent database: %v", err) + } + db.Close() + + if _, err := os.Stat(filepath.Join(dir, "unit-test", "persistent")); err != nil { + t.Fatalf("persistent database doesn't exists: %v", err) + } + // Request th opening/creation of an ephemeral database and ensure it's not persisted + ctx = &ServiceContext{config: &Config{DataDir: ""}} + db, err = ctx.OpenDatabase("ephemeral", 0, 0) + if err != nil { + t.Fatalf("failed to open ephemeral database: %v", err) + } + db.Close() + + if _, err := os.Stat(filepath.Join(dir, "ephemeral")); err == nil { + t.Fatalf("ephemeral database exists") + } +} + +// Tests that already constructed services can be retrieves by later ones. +func TestContextServices(t *testing.T) { + stack, err := New(testNodeConfig()) + if err != nil { + t.Fatalf("failed to create protocol stack: %v", err) + } + // Define a verifier that ensures a NoopA is before it and NoopB after + verifier := func(ctx *ServiceContext) (Service, error) { + var objA *NoopServiceA + if ctx.Service(&objA) != nil { + return nil, fmt.Errorf("former service not found") + } + var objB *NoopServiceB + if err := ctx.Service(&objB); err != ErrServiceUnknown { + return nil, fmt.Errorf("latters lookup error mismatch: have %v, want %v", err, ErrServiceUnknown) + } + return new(NoopService), nil + } + // Register the collection of services + if err := stack.Register(NewNoopServiceA); err != nil { + t.Fatalf("former failed to register service: %v", err) + } + if err := stack.Register(verifier); err != nil { + t.Fatalf("failed to register service verifier: %v", err) + } + if err := stack.Register(NewNoopServiceB); err != nil { + t.Fatalf("latter failed to register service: %v", err) + } + // Start the protocol stack and ensure services are constructed in order + if err := stack.Start(); err != nil { + t.Fatalf("failed to start stack: %v", err) + } + defer stack.Stop() +} diff --git a/vendor/github.com/ethereum/go-ethereum/node/utils_test.go b/vendor/github.com/ethereum/go-ethereum/node/utils_test.go new file mode 100644 index 0000000..7cdfc2b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/node/utils_test.go @@ -0,0 +1,135 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains a batch of utility type declarations used by the tests. As the node +// operates on unique types, a lot of them are needed to check various features. + +package node + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" +) + +// NoopService is a trivial implementation of the Service interface. +type NoopService struct{} + +func (s *NoopService) Protocols() []p2p.Protocol { return nil } +func (s *NoopService) APIs() []rpc.API { return nil } +func (s *NoopService) Start(*p2p.Server) error { return nil } +func (s *NoopService) Stop() error { return nil } + +func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil } + +// Set of services all wrapping the base NoopService resulting in the same method +// signatures but different outer types. +type NoopServiceA struct{ NoopService } +type NoopServiceB struct{ NoopService } +type NoopServiceC struct{ NoopService } +type NoopServiceD struct{ NoopService } + +func NewNoopServiceA(*ServiceContext) (Service, error) { return new(NoopServiceA), nil } +func NewNoopServiceB(*ServiceContext) (Service, error) { return new(NoopServiceB), nil } +func NewNoopServiceC(*ServiceContext) (Service, error) { return new(NoopServiceC), nil } +func NewNoopServiceD(*ServiceContext) (Service, error) { return new(NoopServiceD), nil } + +// InstrumentedService is an implementation of Service for which all interface +// methods can be instrumented both return value as well as event hook wise. +type InstrumentedService struct { + protocols []p2p.Protocol + apis []rpc.API + start error + stop error + + protocolsHook func() + startHook func(*p2p.Server) + stopHook func() +} + +func NewInstrumentedService(*ServiceContext) (Service, error) { return new(InstrumentedService), nil } + +func (s *InstrumentedService) Protocols() []p2p.Protocol { + if s.protocolsHook != nil { + s.protocolsHook() + } + return s.protocols +} + +func (s *InstrumentedService) APIs() []rpc.API { + return s.apis +} + +func (s *InstrumentedService) Start(server *p2p.Server) error { + if s.startHook != nil { + s.startHook(server) + } + return s.start +} + +func (s *InstrumentedService) Stop() error { + if s.stopHook != nil { + s.stopHook() + } + return s.stop +} + +// InstrumentingWrapper is a method to specialize a service constructor returning +// a generic InstrumentedService into one returning a wrapping specific one. +type InstrumentingWrapper func(base ServiceConstructor) ServiceConstructor + +func InstrumentingWrapperMaker(base ServiceConstructor, kind reflect.Type) ServiceConstructor { + return func(ctx *ServiceContext) (Service, error) { + obj, err := base(ctx) + if err != nil { + return nil, err + } + wrapper := reflect.New(kind) + wrapper.Elem().Field(0).Set(reflect.ValueOf(obj).Elem()) + + return wrapper.Interface().(Service), nil + } +} + +// Set of services all wrapping the base InstrumentedService resulting in the +// same method signatures but different outer types. +type InstrumentedServiceA struct{ InstrumentedService } +type InstrumentedServiceB struct{ InstrumentedService } +type InstrumentedServiceC struct{ InstrumentedService } + +func InstrumentedServiceMakerA(base ServiceConstructor) ServiceConstructor { + return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceA{})) +} + +func InstrumentedServiceMakerB(base ServiceConstructor) ServiceConstructor { + return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceB{})) +} + +func InstrumentedServiceMakerC(base ServiceConstructor) ServiceConstructor { + return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{})) +} + +// OneMethodApi is a single-method API handler to be returned by test services. +type OneMethodApi struct { + fun func() +} + +func (api *OneMethodApi) TheOneMethod() { + if api.fun != nil { + api.fun() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/dial.go b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go new file mode 100644 index 0000000..57fba13 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go @@ -0,0 +1,377 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "container/heap" + "crypto/rand" + "errors" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +const ( + // This is the amount of time spent waiting in between + // redialing a certain node. + dialHistoryExpiration = 30 * time.Second + + // Discovery lookups are throttled and can only run + // once every few seconds. + lookupInterval = 4 * time.Second + + // Endpoint resolution is throttled with bounded backoff. + initialResolveDelay = 60 * time.Second + maxResolveDelay = time.Hour +) + +// dialstate schedules dials and discovery lookups. +// it get's a chance to compute new tasks on every iteration +// of the main loop in Server.run. +type dialstate struct { + maxDynDials int + ntab discoverTable + netrestrict *netutil.Netlist + + lookupRunning bool + dialing map[discover.NodeID]connFlag + lookupBuf []*discover.Node // current discovery lookup results + randomNodes []*discover.Node // filled from Table + static map[discover.NodeID]*dialTask + hist *dialHistory +} + +type discoverTable interface { + Self() *discover.Node + Close() + Resolve(target discover.NodeID) *discover.Node + Lookup(target discover.NodeID) []*discover.Node + ReadRandomNodes([]*discover.Node) int +} + +// the dial history remembers recent dials. +type dialHistory []pastDial + +// pastDial is an entry in the dial history. +type pastDial struct { + id discover.NodeID + exp time.Time +} + +type task interface { + Do(*Server) +} + +// A dialTask is generated for each node that is dialed. Its +// fields cannot be accessed while the task is running. +type dialTask struct { + flags connFlag + dest *discover.Node + lastResolved time.Time + resolveDelay time.Duration +} + +// discoverTask runs discovery table operations. +// Only one discoverTask is active at any time. +// discoverTask.Do performs a random lookup. +type discoverTask struct { + results []*discover.Node +} + +// A waitExpireTask is generated if there are no other tasks +// to keep the loop in Server.run ticking. +type waitExpireTask struct { + time.Duration +} + +func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate { + s := &dialstate{ + maxDynDials: maxdyn, + ntab: ntab, + netrestrict: netrestrict, + static: make(map[discover.NodeID]*dialTask), + dialing: make(map[discover.NodeID]connFlag), + randomNodes: make([]*discover.Node, maxdyn/2), + hist: new(dialHistory), + } + for _, n := range static { + s.addStatic(n) + } + return s +} + +func (s *dialstate) addStatic(n *discover.Node) { + // This overwites the task instead of updating an existing + // entry, giving users the opportunity to force a resolve operation. + s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n} +} + +func (s *dialstate) removeStatic(n *discover.Node) { + // This removes a task so future attempts to connect will not be made. + delete(s.static, n.ID) +} + +func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { + var newtasks []task + addDial := func(flag connFlag, n *discover.Node) bool { + if err := s.checkDial(n, peers); err != nil { + glog.V(logger.Debug).Infof("skipping dial candidate %x@%v:%d: %v", n.ID[:8], n.IP, n.TCP, err) + return false + } + s.dialing[n.ID] = flag + newtasks = append(newtasks, &dialTask{flags: flag, dest: n}) + return true + } + + // Compute number of dynamic dials necessary at this point. + needDynDials := s.maxDynDials + for _, p := range peers { + if p.rw.is(dynDialedConn) { + needDynDials-- + } + } + for _, flag := range s.dialing { + if flag&dynDialedConn != 0 { + needDynDials-- + } + } + + // Expire the dial history on every invocation. + s.hist.expire(now) + + // Create dials for static nodes if they are not connected. + for id, t := range s.static { + err := s.checkDial(t.dest, peers) + switch err { + case errNotWhitelisted, errSelf: + glog.V(logger.Debug).Infof("removing static dial candidate %x@%v:%d: %v", t.dest.ID[:8], t.dest.IP, t.dest.TCP, err) + delete(s.static, t.dest.ID) + case nil: + s.dialing[id] = t.flags + newtasks = append(newtasks, t) + } + } + + // Use random nodes from the table for half of the necessary + // dynamic dials. + randomCandidates := needDynDials / 2 + if randomCandidates > 0 { + n := s.ntab.ReadRandomNodes(s.randomNodes) + for i := 0; i < randomCandidates && i < n; i++ { + if addDial(dynDialedConn, s.randomNodes[i]) { + needDynDials-- + } + } + } + // Create dynamic dials from random lookup results, removing tried + // items from the result buffer. + i := 0 + for ; i < len(s.lookupBuf) && needDynDials > 0; i++ { + if addDial(dynDialedConn, s.lookupBuf[i]) { + needDynDials-- + } + } + s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])] + // Launch a discovery lookup if more candidates are needed. + if len(s.lookupBuf) < needDynDials && !s.lookupRunning { + s.lookupRunning = true + newtasks = append(newtasks, &discoverTask{}) + } + + // Launch a timer to wait for the next node to expire if all + // candidates have been tried and no task is currently active. + // This should prevent cases where the dialer logic is not ticked + // because there are no pending events. + if nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 { + t := &waitExpireTask{s.hist.min().exp.Sub(now)} + newtasks = append(newtasks, t) + } + return newtasks +} + +var ( + errSelf = errors.New("is self") + errAlreadyDialing = errors.New("already dialing") + errAlreadyConnected = errors.New("already connected") + errRecentlyDialed = errors.New("recently dialed") + errNotWhitelisted = errors.New("not contained in netrestrict whitelist") +) + +func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error { + _, dialing := s.dialing[n.ID] + switch { + case dialing: + return errAlreadyDialing + case peers[n.ID] != nil: + return errAlreadyConnected + case s.ntab != nil && n.ID == s.ntab.Self().ID: + return errSelf + case s.netrestrict != nil && !s.netrestrict.Contains(n.IP): + return errNotWhitelisted + case s.hist.contains(n.ID): + return errRecentlyDialed + } + return nil +} + +func (s *dialstate) taskDone(t task, now time.Time) { + switch t := t.(type) { + case *dialTask: + s.hist.add(t.dest.ID, now.Add(dialHistoryExpiration)) + delete(s.dialing, t.dest.ID) + case *discoverTask: + s.lookupRunning = false + s.lookupBuf = append(s.lookupBuf, t.results...) + } +} + +func (t *dialTask) Do(srv *Server) { + if t.dest.Incomplete() { + if !t.resolve(srv) { + return + } + } + success := t.dial(srv, t.dest) + // Try resolving the ID of static nodes if dialing failed. + if !success && t.flags&staticDialedConn != 0 { + if t.resolve(srv) { + t.dial(srv, t.dest) + } + } +} + +// resolve attempts to find the current endpoint for the destination +// using discovery. +// +// Resolve operations are throttled with backoff to avoid flooding the +// discovery network with useless queries for nodes that don't exist. +// The backoff delay resets when the node is found. +func (t *dialTask) resolve(srv *Server) bool { + if srv.ntab == nil { + glog.V(logger.Debug).Infof("can't resolve node %x: discovery is disabled", t.dest.ID[:6]) + return false + } + if t.resolveDelay == 0 { + t.resolveDelay = initialResolveDelay + } + if time.Since(t.lastResolved) < t.resolveDelay { + return false + } + resolved := srv.ntab.Resolve(t.dest.ID) + t.lastResolved = time.Now() + if resolved == nil { + t.resolveDelay *= 2 + if t.resolveDelay > maxResolveDelay { + t.resolveDelay = maxResolveDelay + } + glog.V(logger.Debug).Infof("resolving node %x failed (new delay: %v)", t.dest.ID[:6], t.resolveDelay) + return false + } + // The node was found. + t.resolveDelay = initialResolveDelay + t.dest = resolved + glog.V(logger.Debug).Infof("resolved node %x: %v:%d", t.dest.ID[:6], t.dest.IP, t.dest.TCP) + return true +} + +// dial performs the actual connection attempt. +func (t *dialTask) dial(srv *Server, dest *discover.Node) bool { + addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} + glog.V(logger.Debug).Infof("dial tcp %v (%x)\n", addr, dest.ID[:6]) + fd, err := srv.Dialer.Dial("tcp", addr.String()) + if err != nil { + glog.V(logger.Detail).Infof("%v", err) + return false + } + mfd := newMeteredConn(fd, false) + srv.setupConn(mfd, t.flags, dest) + return true +} + +func (t *dialTask) String() string { + return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP) +} + +func (t *discoverTask) Do(srv *Server) { + // newTasks generates a lookup task whenever dynamic dials are + // necessary. Lookups need to take some time, otherwise the + // event loop spins too fast. + next := srv.lastLookup.Add(lookupInterval) + if now := time.Now(); now.Before(next) { + time.Sleep(next.Sub(now)) + } + srv.lastLookup = time.Now() + var target discover.NodeID + rand.Read(target[:]) + t.results = srv.ntab.Lookup(target) +} + +func (t *discoverTask) String() string { + s := "discovery lookup" + if len(t.results) > 0 { + s += fmt.Sprintf(" (%d results)", len(t.results)) + } + return s +} + +func (t waitExpireTask) Do(*Server) { + time.Sleep(t.Duration) +} +func (t waitExpireTask) String() string { + return fmt.Sprintf("wait for dial hist expire (%v)", t.Duration) +} + +// Use only these methods to access or modify dialHistory. +func (h dialHistory) min() pastDial { + return h[0] +} +func (h *dialHistory) add(id discover.NodeID, exp time.Time) { + heap.Push(h, pastDial{id, exp}) +} +func (h dialHistory) contains(id discover.NodeID) bool { + for _, v := range h { + if v.id == id { + return true + } + } + return false +} +func (h *dialHistory) expire(now time.Time) { + for h.Len() > 0 && h.min().exp.Before(now) { + heap.Pop(h) + } +} + +// heap.Interface boilerplate +func (h dialHistory) Len() int { return len(h) } +func (h dialHistory) Less(i, j int) bool { return h[i].exp.Before(h[j].exp) } +func (h dialHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *dialHistory) Push(x interface{}) { + *h = append(*h, x.(pastDial)) +} +func (h *dialHistory) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/dial_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/dial_test.go new file mode 100644 index 0000000..c850233 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/dial_test.go @@ -0,0 +1,564 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "encoding/binary" + "net" + "reflect" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +func init() { + spew.Config.Indent = "\t" +} + +type dialtest struct { + init *dialstate // state before and after the test. + rounds []round +} + +type round struct { + peers []*Peer // current peer set + done []task // tasks that got done this round + new []task // the result must match this one +} + +func runDialTest(t *testing.T, test dialtest) { + var ( + vtime time.Time + running int + ) + pm := func(ps []*Peer) map[discover.NodeID]*Peer { + m := make(map[discover.NodeID]*Peer) + for _, p := range ps { + m[p.rw.id] = p + } + return m + } + for i, round := range test.rounds { + for _, task := range round.done { + running-- + if running < 0 { + panic("running task counter underflow") + } + test.init.taskDone(task, vtime) + } + + new := test.init.newTasks(running, pm(round.peers), vtime) + if !sametasks(new, round.new) { + t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n", + i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running)) + } + + // Time advances by 16 seconds on every round. + vtime = vtime.Add(16 * time.Second) + running += len(new) + } +} + +type fakeTable []*discover.Node + +func (t fakeTable) Self() *discover.Node { return new(discover.Node) } +func (t fakeTable) Close() {} +func (t fakeTable) Lookup(discover.NodeID) []*discover.Node { return nil } +func (t fakeTable) Resolve(discover.NodeID) *discover.Node { return nil } +func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) } + +// This test checks that dynamic dials are launched from discovery results. +func TestDialStateDynDial(t *testing.T) { + runDialTest(t, dialtest{ + init: newDialState(nil, fakeTable{}, 5, nil), + rounds: []round{ + // A discovery query is launched. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + new: []task{&discoverTask{}}, + }, + // Dynamic dials are launched when it completes. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + done: []task{ + &discoverTask{results: []*discover.Node{ + {ID: uintID(2)}, // this one is already connected and not dialed. + {ID: uintID(3)}, + {ID: uintID(4)}, + {ID: uintID(5)}, + {ID: uintID(6)}, // these are not tried because max dyn dials is 5 + {ID: uintID(7)}, // ... + }}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // Some of the dials complete but no new ones are launched yet because + // the sum of active dial count and dynamic peer count is == maxDynDials. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + }, + }, + // No new dial tasks are launched in the this round because + // maxDynDials has been reached. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + new: []task{ + &waitExpireTask{Duration: 14 * time.Second}, + }, + }, + // In this round, the peer with id 2 drops off. The query + // results from last discovery lookup are reused. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}}, + }, + }, + // More peers (3,4) drop off and dial for ID 6 completes. + // The last query result from the discovery lookup is reused + // and a new one is spawned because more candidates are needed. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}}, + &discoverTask{}, + }, + }, + // Peer 7 is connected, but there still aren't enough dynamic peers + // (4 out of 5). However, a discovery is already running, so ensure + // no new is started. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(7)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}}, + }, + }, + // Finish the running node discovery with an empty set. A new lookup + // should be immediately requested. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(7)}}, + }, + done: []task{ + &discoverTask{}, + }, + new: []task{ + &discoverTask{}, + }, + }, + }, + }) +} + +func TestDialStateDynDialFromTable(t *testing.T) { + // This table always returns the same random nodes + // in the order given below. + table := fakeTable{ + {ID: uintID(1)}, + {ID: uintID(2)}, + {ID: uintID(3)}, + {ID: uintID(4)}, + {ID: uintID(5)}, + {ID: uintID(6)}, + {ID: uintID(7)}, + {ID: uintID(8)}, + } + + runDialTest(t, dialtest{ + init: newDialState(nil, table, 10, nil), + rounds: []round{ + // 5 out of 8 of the nodes returned by ReadRandomNodes are dialed. + { + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + &discoverTask{}, + }, + }, + // Dialing nodes 1,2 succeeds. Dials from the lookup are launched. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, + &discoverTask{results: []*discover.Node{ + {ID: uintID(10)}, + {ID: uintID(11)}, + {ID: uintID(12)}, + }}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, + &discoverTask{}, + }, + }, + // Dialing nodes 3,4,5 fails. The dials from the lookup succeed. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, + }, + }, + // Waiting for expiry. No waitExpireTask is launched because the + // discovery query is still running. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, + }, + }, + // Nodes 3,4 are not tried again because only the first two + // returned random nodes (nodes 1,2) are tried and they're + // already connected. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, + }, + }, + }, + }) +} + +// This test checks that candidates that do not match the netrestrict list are not dialed. +func TestDialStateNetRestrict(t *testing.T) { + // This table always returns the same random nodes + // in the order given below. + table := fakeTable{ + {ID: uintID(1), IP: net.ParseIP("127.0.0.1")}, + {ID: uintID(2), IP: net.ParseIP("127.0.0.2")}, + {ID: uintID(3), IP: net.ParseIP("127.0.0.3")}, + {ID: uintID(4), IP: net.ParseIP("127.0.0.4")}, + {ID: uintID(5), IP: net.ParseIP("127.0.2.5")}, + {ID: uintID(6), IP: net.ParseIP("127.0.2.6")}, + {ID: uintID(7), IP: net.ParseIP("127.0.2.7")}, + {ID: uintID(8), IP: net.ParseIP("127.0.2.8")}, + } + restrict := new(netutil.Netlist) + restrict.Add("127.0.2.0/24") + + runDialTest(t, dialtest{ + init: newDialState(nil, table, 10, restrict), + rounds: []round{ + { + new: []task{ + &dialTask{flags: dynDialedConn, dest: table[4]}, + &discoverTask{}, + }, + }, + }, + }) +} + +// This test checks that static dials are launched. +func TestDialStateStaticDial(t *testing.T) { + wantStatic := []*discover.Node{ + {ID: uintID(1)}, + {ID: uintID(2)}, + {ID: uintID(3)}, + {ID: uintID(4)}, + {ID: uintID(5)}, + } + + runDialTest(t, dialtest{ + init: newDialState(wantStatic, fakeTable{}, 0, nil), + rounds: []round{ + // Static dials are launched for the nodes that + // aren't yet connected. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + new: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // No new tasks are launched in this round because all static + // nodes are either connected or still being dialed. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, + }, + done: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + }, + // No new dial tasks are launched because all static + // nodes are now connected. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(4)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, + }, + done: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + new: []task{ + &waitExpireTask{Duration: 14 * time.Second}, + }, + }, + // Wait a round for dial history to expire, no new tasks should spawn. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(4)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, + }, + }, + // If a static node is dropped, it should be immediately redialed, + // irrespective whether it was originally static or dynamic. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, + }, + new: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, + }, + }, + }, + }) +} + +// This test checks that past dials are not retried for some time. +func TestDialStateCache(t *testing.T) { + wantStatic := []*discover.Node{ + {ID: uintID(1)}, + {ID: uintID(2)}, + {ID: uintID(3)}, + } + + runDialTest(t, dialtest{ + init: newDialState(wantStatic, fakeTable{}, 0, nil), + rounds: []round{ + // Static dials are launched for the nodes that + // aren't yet connected. + { + peers: nil, + new: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + }, + // No new tasks are launched in this round because all static + // nodes are either connected or still being dialed. + { + peers: []*Peer{ + {rw: &conn{flags: staticDialedConn, id: uintID(1)}}, + {rw: &conn{flags: staticDialedConn, id: uintID(2)}}, + }, + done: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, + }, + }, + // A salvage task is launched to wait for node 3's history + // entry to expire. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + done: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + new: []task{ + &waitExpireTask{Duration: 14 * time.Second}, + }, + }, + // Still waiting for node 3's entry to expire in the cache. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + }, + // The cache entry for node 3 has expired and is retried. + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, + {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, + }, + new: []task{ + &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + }, + }, + }) +} + +func TestDialResolve(t *testing.T) { + resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444) + table := &resolveMock{answer: resolved} + state := newDialState(nil, table, 0, nil) + + // Check that the task is generated with an incomplete ID. + dest := discover.NewNode(uintID(1), nil, 0, 0) + state.addStatic(dest) + tasks := state.newTasks(0, nil, time.Time{}) + if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) { + t.Fatalf("expected dial task, got %#v", tasks) + } + + // Now run the task, it should resolve the ID once. + config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}} + srv := &Server{ntab: table, Config: config} + tasks[0].Do(srv) + if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) { + t.Fatalf("wrong resolve calls, got %v", table.resolveCalls) + } + + // Report it as done to the dialer, which should update the static node record. + state.taskDone(tasks[0], time.Now()) + if state.static[uintID(1)].dest != resolved { + t.Fatalf("state.dest not updated") + } +} + +// compares task lists but doesn't care about the order. +func sametasks(a, b []task) bool { + if len(a) != len(b) { + return false + } +next: + for _, ta := range a { + for _, tb := range b { + if reflect.DeepEqual(ta, tb) { + continue next + } + } + return false + } + return true +} + +func uintID(i uint32) discover.NodeID { + var id discover.NodeID + binary.BigEndian.PutUint32(id[:], i) + return id +} + +// implements discoverTable for TestDialResolve +type resolveMock struct { + resolveCalls []discover.NodeID + answer *discover.Node +} + +func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node { + t.resolveCalls = append(t.resolveCalls, id) + return t.answer +} + +func (t *resolveMock) Self() *discover.Node { return new(discover.Node) } +func (t *resolveMock) Close() {} +func (t *resolveMock) Bootstrap([]*discover.Node) {} +func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil } +func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/database.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/database.go new file mode 100644 index 0000000..8d20d1e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/database.go @@ -0,0 +1,369 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the node database, storing previously seen nodes and any collected +// metadata about them for QoS purposes. + +package discover + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +var ( + nodeDBNilNodeID = NodeID{} // Special node ID to use as a nil element. + nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. + nodeDBCleanupCycle = time.Hour // Time period for running the expiration task. +) + +// nodeDB stores all nodes we know about. +type nodeDB struct { + lvl *leveldb.DB // Interface to the database itself + self NodeID // Own node id to prevent adding it into the database + runner sync.Once // Ensures we can start at most one expirer + quit chan struct{} // Channel to signal the expiring thread to stop +} + +// Schema layout for the node database +var ( + nodeDBVersionKey = []byte("version") // Version of the database to flush if changes + nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with + + nodeDBDiscoverRoot = ":discover" + nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" + nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" + nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" +) + +// newNodeDB creates a new node database for storing and retrieving infos about +// known peers in the network. If no path is given, an in-memory, temporary +// database is constructed. +func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + if path == "" { + return newMemoryNodeDB(self) + } + return newPersistentNodeDB(path, version, self) +} + +// newMemoryNodeDB creates a new in-memory node database without a persistent +// backend. +func newMemoryNodeDB(self NodeID) (*nodeDB, error) { + db, err := leveldb.Open(storage.NewMemStorage(), nil) + if err != nil { + return nil, err + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// newPersistentNodeDB creates/opens a leveldb backed persistent node database, +// also flushing its contents in case of a version mismatch. +func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + opts := &opt.Options{OpenFilesCacheCapacity: 5} + db, err := leveldb.OpenFile(path, opts) + if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { + db, err = leveldb.RecoverFile(path, nil) + } + if err != nil { + return nil, err + } + // The nodes contained in the cache correspond to a certain protocol version. + // Flush all nodes if the version doesn't match. + currentVer := make([]byte, binary.MaxVarintLen64) + currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] + + blob, err := db.Get(nodeDBVersionKey, nil) + switch err { + case leveldb.ErrNotFound: + // Version not found (i.e. empty cache), insert it + if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { + db.Close() + return nil, err + } + + case nil: + // Version present, flush if different + if !bytes.Equal(blob, currentVer) { + db.Close() + if err = os.RemoveAll(path); err != nil { + return nil, err + } + return newPersistentNodeDB(path, version, self) + } + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// makeKey generates the leveldb key-blob from a node id and its particular +// field of interest. +func makeKey(id NodeID, field string) []byte { + if bytes.Equal(id[:], nodeDBNilNodeID[:]) { + return []byte(field) + } + return append(nodeDBItemPrefix, append(id[:], field...)...) +} + +// splitKey tries to split a database key into a node id and a field part. +func splitKey(key []byte) (id NodeID, field string) { + // If the key is not of a node, return it plainly + if !bytes.HasPrefix(key, nodeDBItemPrefix) { + return NodeID{}, string(key) + } + // Otherwise split the id and field + item := key[len(nodeDBItemPrefix):] + copy(id[:], item[:len(id)]) + field = string(item[len(id):]) + + return id, field +} + +// fetchInt64 retrieves an integer instance associated with a particular +// database key. +func (db *nodeDB) fetchInt64(key []byte) int64 { + blob, err := db.lvl.Get(key, nil) + if err != nil { + return 0 + } + val, read := binary.Varint(blob) + if read <= 0 { + return 0 + } + return val +} + +// storeInt64 update a specific database entry to the current time instance as a +// unix timestamp. +func (db *nodeDB) storeInt64(key []byte, n int64) error { + blob := make([]byte, binary.MaxVarintLen64) + blob = blob[:binary.PutVarint(blob, n)] + + return db.lvl.Put(key, blob, nil) +} + +// node retrieves a node with a given id from the database. +func (db *nodeDB) node(id NodeID) *Node { + blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil) + if err != nil { + glog.V(logger.Detail).Infof("failed to retrieve node %v: %v", id, err) + return nil + } + node := new(Node) + if err := rlp.DecodeBytes(blob, node); err != nil { + glog.V(logger.Warn).Infof("failed to decode node RLP: %v", err) + return nil + } + node.sha = crypto.Keccak256Hash(node.ID[:]) + return node +} + +// updateNode inserts - potentially overwriting - a node into the peer database. +func (db *nodeDB) updateNode(node *Node) error { + blob, err := rlp.EncodeToBytes(node) + if err != nil { + return err + } + return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil) +} + +// deleteNode deletes all information/keys associated with a node. +func (db *nodeDB) deleteNode(id NodeID) error { + deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) + for deleter.Next() { + if err := db.lvl.Delete(deleter.Key(), nil); err != nil { + return err + } + } + return nil +} + +// ensureExpirer is a small helper method ensuring that the data expiration +// mechanism is running. If the expiration goroutine is already running, this +// method simply returns. +// +// The goal is to start the data evacuation only after the network successfully +// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since +// it would require significant overhead to exactly trace the first successful +// convergence, it's simpler to "ensure" the correct state when an appropriate +// condition occurs (i.e. a successful bonding), and discard further events. +func (db *nodeDB) ensureExpirer() { + db.runner.Do(func() { go db.expirer() }) +} + +// expirer should be started in a go routine, and is responsible for looping ad +// infinitum and dropping stale data from the database. +func (db *nodeDB) expirer() { + tick := time.Tick(nodeDBCleanupCycle) + for { + select { + case <-tick: + if err := db.expireNodes(); err != nil { + glog.V(logger.Error).Infof("Failed to expire nodedb items: %v", err) + } + + case <-db.quit: + return + } + } +} + +// expireNodes iterates over the database and deletes all nodes that have not +// been seen (i.e. received a pong from) for some allotted time. +func (db *nodeDB) expireNodes() error { + threshold := time.Now().Add(-nodeDBNodeExpiration) + + // Find discovered nodes that are older than the allowance + it := db.lvl.NewIterator(nil, nil) + defer it.Release() + + for it.Next() { + // Skip the item if not a discovery node + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + // Skip the node if not expired yet (and not self) + if !bytes.Equal(id[:], db.self[:]) { + if seen := db.lastPong(id); seen.After(threshold) { + continue + } + } + // Otherwise delete all associated information + db.deleteNode(id) + } + return nil +} + +// lastPing retrieves the time of the last ping packet send to a remote node, +// requesting binding. +func (db *nodeDB) lastPing(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) +} + +// updateLastPing updates the last time we tried contacting a remote node. +func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) +} + +// lastPong retrieves the time of the last successful contact from remote node. +func (db *nodeDB) lastPong(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) +} + +// updateLastPong updates the last time a remote node successfully contacted. +func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) +} + +// findFails retrieves the number of findnode failures since bonding. +func (db *nodeDB) findFails(id NodeID) int { + return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) +} + +// updateFindFails updates the number of findnode failures since bonding. +func (db *nodeDB) updateFindFails(id NodeID, fails int) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) +} + +// querySeeds retrieves random nodes to be used as potential seed nodes +// for bootstrapping. +func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { + var ( + now = time.Now() + nodes = make([]*Node, 0, n) + it = db.lvl.NewIterator(nil, nil) + id NodeID + ) + defer it.Release() + +seek: + for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { + // Seek to a random entry. The first byte is incremented by a + // random amount each time in order to increase the likelihood + // of hitting all existing nodes in very small databases. + ctr := id[0] + rand.Read(id[:]) + id[0] = ctr + id[0]%16 + it.Seek(makeKey(id, nodeDBDiscoverRoot)) + + n := nextNode(it) + if n == nil { + id[0] = 0 + continue seek // iterator exhausted + } + if n.ID == db.self { + continue seek + } + if now.Sub(db.lastPong(n.ID)) > maxAge { + continue seek + } + for i := range nodes { + if nodes[i].ID == n.ID { + continue seek // duplicate + } + } + nodes = append(nodes, n) + } + return nodes +} + +// reads the next node record from the iterator, skipping over other +// database entries. +func nextNode(it iterator.Iterator) *Node { + for end := false; !end; end = !it.Next() { + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + var n Node + if err := rlp.DecodeBytes(it.Value(), &n); err != nil { + if glog.V(logger.Warn) { + glog.Errorf("invalid node %x: %v", id, err) + } + continue + } + return &n + } + return nil +} + +// close flushes and closes the database files. +func (db *nodeDB) close() { + close(db.quit) + db.lvl.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/database_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/database_test.go new file mode 100644 index 0000000..be972fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/database_test.go @@ -0,0 +1,380 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "bytes" + "io/ioutil" + "net" + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +var nodeDBKeyTests = []struct { + id NodeID + field string + key []byte +}{ + { + id: NodeID{}, + field: "version", + key: []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field + }, + { + id: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + field: ":discover", + key: []byte{0x6e, 0x3a, // prefix + 0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id + 0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, // + 0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, // + 0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, // + 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // + 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, // + 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, // + 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, // + 0x3a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, // field + }, + }, +} + +func TestNodeDBKeys(t *testing.T) { + for i, tt := range nodeDBKeyTests { + if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { + t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) + } + id, field := splitKey(tt.key) + if !bytes.Equal(id[:], tt.id[:]) { + t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) + } + if field != tt.field { + t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) + } + } +} + +var nodeDBInt64Tests = []struct { + key []byte + value int64 +}{ + {key: []byte{0x01}, value: 1}, + {key: []byte{0x02}, value: 2}, + {key: []byte{0x03}, value: 3}, +} + +func TestNodeDBInt64(t *testing.T) { + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + tests := nodeDBInt64Tests + for i := 0; i < len(tests); i++ { + // Insert the next value + if err := db.storeInt64(tests[i].key, tests[i].value); err != nil { + t.Errorf("test %d: failed to store value: %v", i, err) + } + // Check all existing and non existing values + for j := 0; j < len(tests); j++ { + num := db.fetchInt64(tests[j].key) + switch { + case j <= i && num != tests[j].value: + t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value) + case j > i && num != 0: + t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0) + } + } + } +} + +func TestNodeDBFetchStore(t *testing.T) { + node := NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{192, 168, 0, 1}, + 30303, + 30303, + ) + inst := time.Now() + num := 314 + + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + // Check fetch/store operations on a node ping object + if stored := db.lastPing(node.ID); stored.Unix() != 0 { + t.Errorf("ping: non-existing object: %v", stored) + } + if err := db.updateLastPing(node.ID, inst); err != nil { + t.Errorf("ping: failed to update: %v", err) + } + if stored := db.lastPing(node.ID); stored.Unix() != inst.Unix() { + t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) + } + // Check fetch/store operations on a node pong object + if stored := db.lastPong(node.ID); stored.Unix() != 0 { + t.Errorf("pong: non-existing object: %v", stored) + } + if err := db.updateLastPong(node.ID, inst); err != nil { + t.Errorf("pong: failed to update: %v", err) + } + if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() { + t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) + } + // Check fetch/store operations on a node findnode-failure object + if stored := db.findFails(node.ID); stored != 0 { + t.Errorf("find-node fails: non-existing object: %v", stored) + } + if err := db.updateFindFails(node.ID, num); err != nil { + t.Errorf("find-node fails: failed to update: %v", err) + } + if stored := db.findFails(node.ID); stored != num { + t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) + } + // Check fetch/store operations on an actual node object + if stored := db.node(node.ID); stored != nil { + t.Errorf("node: non-existing object: %v", stored) + } + if err := db.updateNode(node); err != nil { + t.Errorf("node: failed to update: %v", err) + } + if stored := db.node(node.ID); stored == nil { + t.Errorf("node: not found") + } else if !reflect.DeepEqual(stored, node) { + t.Errorf("node: data mismatch: have %v, want %v", stored, node) + } +} + +var nodeDBSeedQueryNodes = []struct { + node *Node + pong time.Time +}{ + // This one should not be in the result set because its last + // pong time is too far in the past. + { + node: NewNode( + MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-3 * time.Hour), + }, + // This one shouldn't be in in the result set because its + // nodeID is the local node's ID. + { + node: NewNode( + MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-4 * time.Second), + }, + + // These should be in the result set. + { + node: NewNode( + MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 1}, + 30303, + 30303, + ), + pong: time.Now().Add(-2 * time.Second), + }, + { + node: NewNode( + MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 2}, + 30303, + 30303, + ), + pong: time.Now().Add(-3 * time.Second), + }, + { + node: NewNode( + MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-1 * time.Second), + }, +} + +func TestNodeDBSeedQuery(t *testing.T) { + db, _ := newNodeDB("", Version, nodeDBSeedQueryNodes[1].node.ID) + defer db.close() + + // Insert a batch of nodes for querying + for i, seed := range nodeDBSeedQueryNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to insert lastPong: %v", i, err) + } + } + + // Retrieve the entire batch and check for duplicates + seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour) + have := make(map[NodeID]struct{}) + for _, seed := range seeds { + have[seed.ID] = struct{}{} + } + want := make(map[NodeID]struct{}) + for _, seed := range nodeDBSeedQueryNodes[2:] { + want[seed.node.ID] = struct{}{} + } + if len(seeds) != len(want) { + t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want)) + } + for id := range have { + if _, ok := want[id]; !ok { + t.Errorf("extra seed: %v", id) + } + } + for id := range want { + if _, ok := have[id]; !ok { + t.Errorf("missing seed: %v", id) + } + } +} + +func TestNodeDBPersistency(t *testing.T) { + root, err := ioutil.TempDir("", "nodedb-") + if err != nil { + t.Fatalf("failed to create temporary data folder: %v", err) + } + defer os.RemoveAll(root) + + var ( + testKey = []byte("somekey") + testInt = int64(314) + ) + + // Create a persistent database and store some values + db, err := newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) + if err != nil { + t.Fatalf("failed to create persistent database: %v", err) + } + if err := db.storeInt64(testKey, testInt); err != nil { + t.Fatalf("failed to store value: %v.", err) + } + db.close() + + // Reopen the database and check the value + db, err = newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) + if err != nil { + t.Fatalf("failed to open persistent database: %v", err) + } + if val := db.fetchInt64(testKey); val != testInt { + t.Fatalf("value mismatch: have %v, want %v", val, testInt) + } + db.close() + + // Change the database version and check flush + db, err = newNodeDB(filepath.Join(root, "database"), Version+1, NodeID{}) + if err != nil { + t.Fatalf("failed to open persistent database: %v", err) + } + if val := db.fetchInt64(testKey); val != 0 { + t.Fatalf("value mismatch: have %v, want %v", val, 0) + } + db.close() +} + +var nodeDBExpirationNodes = []struct { + node *Node + pong time.Time + exp bool +}{ + { + node: NewNode( + MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 1}, + 30303, + 30303, + ), + pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute), + exp: false, + }, { + node: NewNode( + MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 2}, + 30303, + 30303, + ), + pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute), + exp: true, + }, +} + +func TestNodeDBExpiration(t *testing.T) { + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + // Add all the test nodes and set their last pong time + for i, seed := range nodeDBExpirationNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to update pong: %v", i, err) + } + } + // Expire some of them, and check the rest + if err := db.expireNodes(); err != nil { + t.Fatalf("failed to expire nodes: %v", err) + } + for i, seed := range nodeDBExpirationNodes { + node := db.node(seed.node.ID) + if (node == nil && !seed.exp) || (node != nil && seed.exp) { + t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp) + } + } +} + +func TestNodeDBSelfExpiration(t *testing.T) { + // Find a node in the tests that shouldn't expire, and assign it as self + var self NodeID + for _, node := range nodeDBExpirationNodes { + if !node.exp { + self = node.node.ID + break + } + } + db, _ := newNodeDB("", Version, self) + defer db.close() + + // Add all the test nodes and set their last pong time + for i, seed := range nodeDBExpirationNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to update pong: %v", i, err) + } + } + // Expire the nodes and make sure self has been evacuated too + if err := db.expireNodes(); err != nil { + t.Fatalf("failed to expire nodes: %v", err) + } + node := db.node(self) + if node != nil { + t.Errorf("self not evacuated") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go new file mode 100644 index 0000000..f026276 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node.go @@ -0,0 +1,373 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/hex" + "errors" + "fmt" + "math/big" + "math/rand" + "net" + "net/url" + "regexp" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +const NodeIDBits = 512 + +// Node represents a host on the network. +// The fields of Node may not be modified. +type Node struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP, TCP uint16 // port numbers + ID NodeID // the node's public key + + // This is a cached copy of sha3(ID) which is used for node + // distance calculations. This is part of Node in order to make it + // possible to write tests that need a node at a certain distance. + // In those tests, the content of sha will not actually correspond + // with ID. + sha common.Hash + + // whether this node is currently being pinged in order to replace + // it in a bucket + contested bool +} + +// NewNode creates a new node. It is mostly meant to be used for +// testing purposes. +func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + return &Node{ + IP: ip, + UDP: udpPort, + TCP: tcpPort, + ID: id, + sha: crypto.Keccak256Hash(id[:]), + } +} + +func (n *Node) addr() *net.UDPAddr { + return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} +} + +// Incomplete returns true for nodes with no IP address. +func (n *Node) Incomplete() bool { + return n.IP == nil +} + +// checks whether n is a valid complete node. +func (n *Node) validateComplete() error { + if n.Incomplete() { + return errors.New("incomplete node") + } + if n.UDP == 0 { + return errors.New("missing UDP port") + } + if n.TCP == 0 { + return errors.New("missing TCP port") + } + if n.IP.IsMulticast() || n.IP.IsUnspecified() { + return errors.New("invalid IP (multicast/unspecified)") + } + _, err := n.ID.Pubkey() // validate the key (on curve, etc.) + return err +} + +// The string representation of a Node is a URL. +// Please see ParseNode for a description of the format. +func (n *Node) String() string { + u := url.URL{Scheme: "enode"} + if n.Incomplete() { + u.Host = fmt.Sprintf("%x", n.ID[:]) + } else { + addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} + u.User = url.User(fmt.Sprintf("%x", n.ID[:])) + u.Host = addr.String() + if n.UDP != n.TCP { + u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) + } + } + return u.String() +} + +var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") + +// ParseNode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func ParseNode(rawurl string) (*Node, error) { + if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { + id, err := HexID(m[1]) + if err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + return NewNode(id, nil, 0, 0), nil + } + return parseComplete(rawurl) +} + +func parseComplete(rawurl string) (*Node, error) { + var ( + id NodeID + ip net.IP + tcpPort, udpPort uint64 + ) + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + if u.Scheme != "enode" { + return nil, errors.New("invalid URL scheme, want \"enode\"") + } + // Parse the Node ID from the user portion. + if u.User == nil { + return nil, errors.New("does not contain node ID") + } + if id, err = HexID(u.User.String()); err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + // Parse the IP address. + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + return nil, fmt.Errorf("invalid host: %v", err) + } + if ip = net.ParseIP(host); ip == nil { + return nil, errors.New("invalid IP address") + } + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + // Parse the port numbers. + if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { + return nil, errors.New("invalid port") + } + udpPort = tcpPort + qv := u.Query() + if qv.Get("discport") != "" { + udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) + if err != nil { + return nil, errors.New("invalid discport in query") + } + } + return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil +} + +// MustParseNode parses a node URL. It panics if the URL is not valid. +func MustParseNode(rawurl string) *Node { + n, err := ParseNode(rawurl) + if err != nil { + panic("invalid node URL: " + err.Error()) + } + return n +} + +// NodeID is a unique identifier for each node. +// The node identifier is a marshaled elliptic curve public key. +type NodeID [NodeIDBits / 8]byte + +// NodeID prints as a long hexadecimal number. +func (n NodeID) String() string { + return fmt.Sprintf("%x", n[:]) +} + +// The Go syntax representation of a NodeID is a call to HexID. +func (n NodeID) GoString() string { + return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) +} + +// HexID converts a hex string to a NodeID. +// The string may be prefixed with 0x. +func HexID(in string) (NodeID, error) { + var id NodeID + b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) + if err != nil { + return id, err + } else if len(b) != len(id) { + return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) + } + copy(id[:], b) + return id, nil +} + +// MustHexID converts a hex string to a NodeID. +// It panics if the string is not a valid NodeID. +func MustHexID(in string) NodeID { + id, err := HexID(in) + if err != nil { + panic(err) + } + return id +} + +// PubkeyID returns a marshaled representation of the given public key. +func PubkeyID(pub *ecdsa.PublicKey) NodeID { + var id NodeID + pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) + if len(pbytes)-1 != len(id) { + panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) + } + copy(id[:], pbytes[1:]) + return id +} + +// Pubkey returns the public key represented by the node ID. +// It returns an error if the ID is not a point on the curve. +func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { + p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} + half := len(id) / 2 + p.X.SetBytes(id[:half]) + p.Y.SetBytes(id[half:]) + if !p.Curve.IsOnCurve(p.X, p.Y) { + return nil, errors.New("id is invalid secp256k1 curve point") + } + return p, nil +} + +// recoverNodeID computes the public key used to sign the +// given hash from the signature. +func recoverNodeID(hash, sig []byte) (id NodeID, err error) { + pubkey, err := secp256k1.RecoverPubkey(hash, sig) + if err != nil { + return id, err + } + if len(pubkey)-1 != len(id) { + return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) + } + for i := range id { + id[i] = pubkey[i+1] + } + return id, nil +} + +// distcmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func distcmp(target, a, b common.Hash) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// table of leading zero counts for bytes [0..255] +var lzcount = [256]int{ + 8, 7, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} + +// logdist returns the logarithmic distance between a and b, log2(a ^ b). +func logdist(a, b common.Hash) int { + lz := 0 + for i := range a { + x := a[i] ^ b[i] + if x == 0 { + lz += 8 + } else { + lz += lzcount[x] + break + } + } + return len(a)*8 - lz +} + +// hashAtDistance returns a random hash such that logdist(a, b) == n +func hashAtDistance(a common.Hash, n int) (b common.Hash) { + if n == 0 { + return a + } + // flip bit at position n, fill the rest with random bits + b = a + pos := len(a) - n/8 - 1 + bit := byte(0x01) << (byte(n%8) - 1) + if bit == 0 { + pos++ + bit = 0x80 + } + b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits + for i := pos + 1; i < len(a); i++ { + b[i] = byte(rand.Intn(255)) + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/node_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node_test.go new file mode 100644 index 0000000..3d1662d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/node_test.go @@ -0,0 +1,305 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "fmt" + "math/big" + "math/rand" + "net" + "reflect" + "strings" + "testing" + "testing/quick" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func ExampleNewNode() { + id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") + + // Complete nodes contain UDP and TCP endpoints: + n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303) + fmt.Println("n1:", n1) + fmt.Println("n1.Incomplete() ->", n1.Incomplete()) + + // An incomplete node can be created by passing zero values + // for all parameters except id. + n2 := NewNode(id, nil, 0, 0) + fmt.Println("n2:", n2) + fmt.Println("n2.Incomplete() ->", n2.Incomplete()) + + // Output: + // n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150 + // n1.Incomplete() -> false + // n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439 + // n2.Incomplete() -> true +} + +var parseNodeTests = []struct { + rawurl string + wantError string + wantResult *Node +}{ + { + rawurl: "http://foobar", + wantError: `invalid URL scheme, want "enode"`, + }, + { + rawurl: "enode://01010101@123.124.125.126:3", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + // Complete nodes with IP address. + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", + wantError: `invalid IP address`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", + wantError: `invalid port`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", + wantError: `invalid discport in query`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x7f, 0x0, 0x0, 0x1}, + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("::"), + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x7f, 0x0, 0x0, 0x1}, + 22334, + 52150, + ), + }, + // Incomplete nodes with no address. + { + rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, 0, 0, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, 0, 0, + ), + }, + // Invalid URLs + { + rawurl: "01010101", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + { + rawurl: "enode://01010101", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + { + // This test checks that errors from url.Parse are handled. + rawurl: "://foo", + wantError: `parse ://foo: missing protocol scheme`, + }, +} + +func TestParseNode(t *testing.T) { + for _, test := range parseNodeTests { + n, err := ParseNode(test.rawurl) + if test.wantError != "" { + if err == nil { + t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError) + continue + } else if err.Error() != test.wantError { + t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError) + continue + } + } else { + if err != nil { + t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err) + continue + } + if !reflect.DeepEqual(n, test.wantResult) { + t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult) + } + } + } +} + +func TestNodeString(t *testing.T) { + for i, test := range parseNodeTests { + if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") { + str := test.wantResult.String() + if str != test.rawurl { + t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl) + } + } + } +} + +func TestHexID(t *testing.T) { + ref := NodeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188} + id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") + id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") + + if id1 != ref { + t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:]) + } + if id2 != ref { + t.Errorf("wrong id2\ngot %v\nwant %v", id2[:], ref[:]) + } +} + +func TestNodeID_recover(t *testing.T) { + prv := newkey() + hash := make([]byte, 32) + sig, err := crypto.Sign(hash, prv) + if err != nil { + t.Fatalf("signing error: %v", err) + } + + pub := PubkeyID(&prv.PublicKey) + recpub, err := recoverNodeID(hash, sig) + if err != nil { + t.Fatalf("recovery error: %v", err) + } + if pub != recpub { + t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub) + } + + ecdsa, err := pub.Pubkey() + if err != nil { + t.Errorf("Pubkey error: %v", err) + } + if !reflect.DeepEqual(ecdsa, &prv.PublicKey) { + t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey) + } +} + +func TestNodeID_pubkeyBad(t *testing.T) { + ecdsa, err := NodeID{}.Pubkey() + if err == nil { + t.Error("expected error for zero ID") + } + if ecdsa != nil { + t.Error("expected nil result") + } +} + +func TestNodeID_distcmp(t *testing.T) { + distcmpBig := func(target, a, b common.Hash) int { + tbig := new(big.Int).SetBytes(target[:]) + abig := new(big.Int).SetBytes(a[:]) + bbig := new(big.Int).SetBytes(b[:]) + return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig)) + } + if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil { + t.Error(err) + } +} + +// the random tests is likely to miss the case where they're equal. +func TestNodeID_distcmpEqual(t *testing.T) { + base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + if distcmp(base, x, x) != 0 { + t.Errorf("distcmp(base, x, x) != 0") + } +} + +func TestNodeID_logdist(t *testing.T) { + logdistBig := func(a, b common.Hash) int { + abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:]) + return new(big.Int).Xor(abig, bbig).BitLen() + } + if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil { + t.Error(err) + } +} + +// the random tests is likely to miss the case where they're equal. +func TestNodeID_logdistEqual(t *testing.T) { + x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + if logdist(x, x) != 0 { + t.Errorf("logdist(x, x) != 0") + } +} + +func TestNodeID_hashAtDistance(t *testing.T) { + // we don't use quick.Check here because its output isn't + // very helpful when the test fails. + cfg := quickcfg() + for i := 0; i < cfg.MaxCount; i++ { + a := gen(common.Hash{}, cfg.Rand).(common.Hash) + dist := cfg.Rand.Intn(len(common.Hash{}) * 8) + result := hashAtDistance(a, dist) + actualdist := logdist(result, a) + + if dist != actualdist { + t.Log("a: ", a) + t.Log("result:", result) + t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist) + } + } +} + +func quickcfg() *quick.Config { + return &quick.Config{ + MaxCount: 5000, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + } +} + +// TODO: The Generate method can be dropped when we require Go >= 1.5 +// because testing/quick learned to generate arrays in 1.5. + +func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value { + var id NodeID + m := rand.Intn(len(id)) + for i := len(id) - 1; i > m; i-- { + id[i] = byte(rand.Uint32()) + } + return reflect.ValueOf(id) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/ntp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/ntp.go new file mode 100644 index 0000000..c1a4b3a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/ntp.go @@ -0,0 +1,127 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the NTP time drift detection via the SNTP protocol: +// https://tools.ietf.org/html/rfc4330 + +package discover + +import ( + "fmt" + "net" + "sort" + "strings" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + ntpPool = "pool.ntp.org" // ntpPool is the NTP server to query for the current time + ntpChecks = 3 // Number of measurements to do against the NTP server +) + +// durationSlice attaches the methods of sort.Interface to []time.Duration, +// sorting in increasing order. +type durationSlice []time.Duration + +func (s durationSlice) Len() int { return len(s) } +func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// checkClockDrift queries an NTP server for clock drifts and warns the user if +// one large enough is detected. +func checkClockDrift() { + drift, err := sntpDrift(ntpChecks) + if err != nil { + return + } + if drift < -driftThreshold || drift > driftThreshold { + warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift) + howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") + separator := strings.Repeat("-", len(warning)) + + glog.V(logger.Warn).Info(separator) + glog.V(logger.Warn).Info(warning) + glog.V(logger.Warn).Info(howtofix) + glog.V(logger.Warn).Info(separator) + } else { + glog.V(logger.Debug).Infof("Sanity NTP check reported %v drift, all ok", drift) + } +} + +// sntpDrift does a naive time resolution against an NTP server and returns the +// measured drift. This method uses the simple version of NTP. It's not precise +// but should be fine for these purposes. +// +// Note, it executes two extra measurements compared to the number of requested +// ones to be able to discard the two extremes as outliers. +func sntpDrift(measurements int) (time.Duration, error) { + // Resolve the address of the NTP server + addr, err := net.ResolveUDPAddr("udp", ntpPool+":123") + if err != nil { + return 0, err + } + // Construct the time request (empty package with only 2 fields set): + // Bits 3-5: Protocol version, 3 + // Bits 6-8: Mode of operation, client, 3 + request := make([]byte, 48) + request[0] = 3<<3 | 3 + + // Execute each of the measurements + drifts := []time.Duration{} + for i := 0; i < measurements+2; i++ { + // Dial the NTP server and send the time retrieval request + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + return 0, err + } + defer conn.Close() + + sent := time.Now() + if _, err = conn.Write(request); err != nil { + return 0, err + } + // Retrieve the reply and calculate the elapsed time + conn.SetDeadline(time.Now().Add(5 * time.Second)) + + reply := make([]byte, 48) + if _, err = conn.Read(reply); err != nil { + return 0, err + } + elapsed := time.Since(sent) + + // Reconstruct the time from the reply data + sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24 + frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24 + + nanosec := sec*1e9 + (frac*1e9)>>32 + + t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local() + + // Calculate the drift based on an assumed answer time of RRT/2 + drifts = append(drifts, sent.Sub(t)+elapsed/2) + } + // Calculate average drif (drop two extremities to avoid outliers) + sort.Sort(durationSlice(drifts)) + + drift := time.Duration(0) + for i := 1; i < len(drifts)-1; i++ { + drift += drifts[i] + } + return drift / time.Duration(measurements), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/table.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/table.go new file mode 100644 index 0000000..839e3ec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/table.go @@ -0,0 +1,682 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package discover implements the Node Discovery Protocol. +// +// The Node Discovery protocol provides a way to find RLPx nodes that +// can be connected to. It uses a Kademlia-like protocol to maintain a +// distributed database of the IDs and endpoints of all listening +// nodes. +package discover + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "net" + "sort" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + alpha = 3 // Kademlia concurrency factor + bucketSize = 16 // Kademlia bucket size + hashBits = len(common.Hash{}) * 8 + nBuckets = hashBits + 1 // Number of buckets + + maxBondingPingPongs = 16 + maxFindnodeFailures = 5 + + autoRefreshInterval = 1 * time.Hour + seedCount = 30 + seedMaxAge = 5 * 24 * time.Hour +) + +type Table struct { + mutex sync.Mutex // protects buckets, their content, and nursery + buckets [nBuckets]*bucket // index of known nodes by distance + nursery []*Node // bootstrap nodes + db *nodeDB // database of known nodes + + refreshReq chan chan struct{} + closeReq chan struct{} + closed chan struct{} + + bondmu sync.Mutex + bonding map[NodeID]*bondproc + bondslots chan struct{} // limits total number of active bonding processes + + nodeAddedHook func(*Node) // for testing + + net transport + self *Node // metadata of the local node +} + +type bondproc struct { + err error + n *Node + done chan struct{} +} + +// transport is implemented by the UDP transport. +// it is an interface so we can test without opening lots of UDP +// sockets and without generating a private key. +type transport interface { + ping(NodeID, *net.UDPAddr) error + waitping(NodeID) error + findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error) + close() +} + +// bucket contains nodes, ordered by their last activity. the entry +// that was most recently active is the first element in entries. +type bucket struct{ entries []*Node } + +func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string) (*Table, error) { + // If no node database was given, use an in-memory one + db, err := newNodeDB(nodeDBPath, Version, ourID) + if err != nil { + return nil, err + } + tab := &Table{ + net: t, + db: db, + self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)), + bonding: make(map[NodeID]*bondproc), + bondslots: make(chan struct{}, maxBondingPingPongs), + refreshReq: make(chan chan struct{}), + closeReq: make(chan struct{}), + closed: make(chan struct{}), + } + for i := 0; i < cap(tab.bondslots); i++ { + tab.bondslots <- struct{}{} + } + for i := range tab.buckets { + tab.buckets[i] = new(bucket) + } + go tab.refreshLoop() + return tab, nil +} + +// Self returns the local node. +// The returned node should not be modified by the caller. +func (tab *Table) Self() *Node { + return tab.self +} + +// ReadRandomNodes fills the given slice with random nodes from the +// table. It will not write the same node more than once. The nodes in +// the slice are copies and can be modified by the caller. +func (tab *Table) ReadRandomNodes(buf []*Node) (n int) { + tab.mutex.Lock() + defer tab.mutex.Unlock() + // TODO: tree-based buckets would help here + // Find all non-empty buckets and get a fresh slice of their entries. + var buckets [][]*Node + for _, b := range tab.buckets { + if len(b.entries) > 0 { + buckets = append(buckets, b.entries[:]) + } + } + if len(buckets) == 0 { + return 0 + } + // Shuffle the buckets. + for i := uint32(len(buckets)) - 1; i > 0; i-- { + j := randUint(i) + buckets[i], buckets[j] = buckets[j], buckets[i] + } + // Move head of each bucket into buf, removing buckets that become empty. + var i, j int + for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { + b := buckets[j] + buf[i] = &(*b[0]) + buckets[j] = b[1:] + if len(b) == 1 { + buckets = append(buckets[:j], buckets[j+1:]...) + } + if len(buckets) == 0 { + break + } + } + return i + 1 +} + +func randUint(max uint32) uint32 { + if max == 0 { + return 0 + } + var b [4]byte + rand.Read(b[:]) + return binary.BigEndian.Uint32(b[:]) % max +} + +// Close terminates the network listener and flushes the node database. +func (tab *Table) Close() { + select { + case <-tab.closed: + // already closed. + case tab.closeReq <- struct{}{}: + <-tab.closed // wait for refreshLoop to end. + } +} + +// SetFallbackNodes sets the initial points of contact. These nodes +// are used to connect to the network if the table is empty and there +// are no known nodes in the database. +func (tab *Table) SetFallbackNodes(nodes []*Node) error { + for _, n := range nodes { + if err := n.validateComplete(); err != nil { + return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) + } + } + tab.mutex.Lock() + tab.nursery = make([]*Node, 0, len(nodes)) + for _, n := range nodes { + cpy := *n + // Recompute cpy.sha because the node might not have been + // created by NewNode or ParseNode. + cpy.sha = crypto.Keccak256Hash(n.ID[:]) + tab.nursery = append(tab.nursery, &cpy) + } + tab.mutex.Unlock() + tab.refresh() + return nil +} + +// Resolve searches for a specific node with the given ID. +// It returns nil if the node could not be found. +func (tab *Table) Resolve(targetID NodeID) *Node { + // If the node is present in the local table, no + // network interaction is required. + hash := crypto.Keccak256Hash(targetID[:]) + tab.mutex.Lock() + cl := tab.closest(hash, 1) + tab.mutex.Unlock() + if len(cl.entries) > 0 && cl.entries[0].ID == targetID { + return cl.entries[0] + } + // Otherwise, do a network lookup. + result := tab.Lookup(targetID) + for _, n := range result { + if n.ID == targetID { + return n + } + } + return nil +} + +// Lookup performs a network search for nodes close +// to the given target. It approaches the target by querying +// nodes that are closer to it on each iteration. +// The given target does not need to be an actual node +// identifier. +func (tab *Table) Lookup(targetID NodeID) []*Node { + return tab.lookup(targetID, true) +} + +func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node { + var ( + target = crypto.Keccak256Hash(targetID[:]) + asked = make(map[NodeID]bool) + seen = make(map[NodeID]bool) + reply = make(chan []*Node, alpha) + pendingQueries = 0 + result *nodesByDistance + ) + // don't query further if we hit ourself. + // unlikely to happen often in practice. + asked[tab.self.ID] = true + + for { + tab.mutex.Lock() + // generate initial result set + result = tab.closest(target, bucketSize) + tab.mutex.Unlock() + if len(result.entries) > 0 || !refreshIfEmpty { + break + } + // The result set is empty, all nodes were dropped, refresh. + // We actually wait for the refresh to complete here. The very + // first query will hit this case and run the bootstrapping + // logic. + <-tab.refresh() + refreshIfEmpty = false + } + + for { + // ask the alpha closest nodes that we haven't asked yet + for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { + n := result.entries[i] + if !asked[n.ID] { + asked[n.ID] = true + pendingQueries++ + go func() { + // Find potential neighbors to bond with + r, err := tab.net.findnode(n.ID, n.addr(), targetID) + if err != nil { + // Bump the failure counter to detect and evacuate non-bonded entries + fails := tab.db.findFails(n.ID) + 1 + tab.db.updateFindFails(n.ID, fails) + glog.V(logger.Detail).Infof("Bumping failures for %x: %d", n.ID[:8], fails) + + if fails >= maxFindnodeFailures { + glog.V(logger.Detail).Infof("Evacuating node %x: %d findnode failures", n.ID[:8], fails) + tab.delete(n) + } + } + reply <- tab.bondall(r) + }() + } + } + if pendingQueries == 0 { + // we have asked all closest nodes, stop the search + break + } + // wait for the next reply + for _, n := range <-reply { + if n != nil && !seen[n.ID] { + seen[n.ID] = true + result.push(n, bucketSize) + } + } + pendingQueries-- + } + return result.entries +} + +func (tab *Table) refresh() <-chan struct{} { + done := make(chan struct{}) + select { + case tab.refreshReq <- done: + case <-tab.closed: + close(done) + } + return done +} + +// refreshLoop schedules doRefresh runs and coordinates shutdown. +func (tab *Table) refreshLoop() { + var ( + timer = time.NewTicker(autoRefreshInterval) + waiting []chan struct{} // accumulates waiting callers while doRefresh runs + done chan struct{} // where doRefresh reports completion + ) +loop: + for { + select { + case <-timer.C: + if done == nil { + done = make(chan struct{}) + go tab.doRefresh(done) + } + case req := <-tab.refreshReq: + waiting = append(waiting, req) + if done == nil { + done = make(chan struct{}) + go tab.doRefresh(done) + } + case <-done: + for _, ch := range waiting { + close(ch) + } + waiting = nil + done = nil + case <-tab.closeReq: + break loop + } + } + + if tab.net != nil { + tab.net.close() + } + if done != nil { + <-done + } + for _, ch := range waiting { + close(ch) + } + tab.db.close() + close(tab.closed) +} + +// doRefresh performs a lookup for a random target to keep buckets +// full. seed nodes are inserted if the table is empty (initial +// bootstrap or discarded faulty peers). +func (tab *Table) doRefresh(done chan struct{}) { + defer close(done) + + // The Kademlia paper specifies that the bucket refresh should + // perform a lookup in the least recently used bucket. We cannot + // adhere to this because the findnode target is a 512bit value + // (not hash-sized) and it is not easily possible to generate a + // sha3 preimage that falls into a chosen bucket. + // We perform a lookup with a random target instead. + var target NodeID + rand.Read(target[:]) + result := tab.lookup(target, false) + if len(result) > 0 { + return + } + + // The table is empty. Load nodes from the database and insert + // them. This should yield a few previously seen nodes that are + // (hopefully) still alive. + seeds := tab.db.querySeeds(seedCount, seedMaxAge) + seeds = tab.bondall(append(seeds, tab.nursery...)) + if glog.V(logger.Debug) { + if len(seeds) == 0 { + glog.Infof("no seed nodes found") + } + for _, n := range seeds { + age := time.Since(tab.db.lastPong(n.ID)) + glog.Infof("seed node (age %v): %v", age, n) + } + } + tab.mutex.Lock() + tab.stuff(seeds) + tab.mutex.Unlock() + + // Finally, do a self lookup to fill up the buckets. + tab.lookup(tab.self.ID, false) +} + +// closest returns the n nodes in the table that are closest to the +// given id. The caller must hold tab.mutex. +func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { + // This is a very wasteful way to find the closest nodes but + // obviously correct. I believe that tree-based buckets would make + // this easier to implement efficiently. + close := &nodesByDistance{target: target} + for _, b := range tab.buckets { + for _, n := range b.entries { + close.push(n, nresults) + } + } + return close +} + +func (tab *Table) len() (n int) { + for _, b := range tab.buckets { + n += len(b.entries) + } + return n +} + +// bondall bonds with all given nodes concurrently and returns +// those nodes for which bonding has probably succeeded. +func (tab *Table) bondall(nodes []*Node) (result []*Node) { + rc := make(chan *Node, len(nodes)) + for i := range nodes { + go func(n *Node) { + nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCP)) + rc <- nn + }(nodes[i]) + } + for range nodes { + if n := <-rc; n != nil { + result = append(result, n) + } + } + return result +} + +// bond ensures the local node has a bond with the given remote node. +// It also attempts to insert the node into the table if bonding succeeds. +// The caller must not hold tab.mutex. +// +// A bond is must be established before sending findnode requests. +// Both sides must have completed a ping/pong exchange for a bond to +// exist. The total number of active bonding processes is limited in +// order to restrain network use. +// +// bond is meant to operate idempotently in that bonding with a remote +// node which still remembers a previously established bond will work. +// The remote node will simply not send a ping back, causing waitping +// to time out. +// +// If pinged is true, the remote node has just pinged us and one half +// of the process can be skipped. +func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) { + if id == tab.self.ID { + return nil, errors.New("is self") + } + // Retrieve a previously known node and any recent findnode failures + node, fails := tab.db.node(id), 0 + if node != nil { + fails = tab.db.findFails(id) + } + // If the node is unknown (non-bonded) or failed (remotely unknown), bond from scratch + var result error + age := time.Since(tab.db.lastPong(id)) + if node == nil || fails > 0 || age > nodeDBNodeExpiration { + glog.V(logger.Detail).Infof("Bonding %x: known=%t, fails=%d age=%v", id[:8], node != nil, fails, age) + + tab.bondmu.Lock() + w := tab.bonding[id] + if w != nil { + // Wait for an existing bonding process to complete. + tab.bondmu.Unlock() + <-w.done + } else { + // Register a new bonding process. + w = &bondproc{done: make(chan struct{})} + tab.bonding[id] = w + tab.bondmu.Unlock() + // Do the ping/pong. The result goes into w. + tab.pingpong(w, pinged, id, addr, tcpPort) + // Unregister the process after it's done. + tab.bondmu.Lock() + delete(tab.bonding, id) + tab.bondmu.Unlock() + } + // Retrieve the bonding results + result = w.err + if result == nil { + node = w.n + } + } + if node != nil { + // Add the node to the table even if the bonding ping/pong + // fails. It will be relaced quickly if it continues to be + // unresponsive. + tab.add(node) + tab.db.updateFindFails(id, 0) + } + return node, result +} + +func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) { + // Request a bonding slot to limit network usage + <-tab.bondslots + defer func() { tab.bondslots <- struct{}{} }() + + // Ping the remote side and wait for a pong. + if w.err = tab.ping(id, addr); w.err != nil { + close(w.done) + return + } + if !pinged { + // Give the remote node a chance to ping us before we start + // sending findnode requests. If they still remember us, + // waitping will simply time out. + tab.net.waitping(id) + } + // Bonding succeeded, update the node database. + w.n = NewNode(id, addr.IP, uint16(addr.Port), tcpPort) + tab.db.updateNode(w.n) + close(w.done) +} + +// ping a remote endpoint and wait for a reply, also updating the node +// database accordingly. +func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { + tab.db.updateLastPing(id, time.Now()) + if err := tab.net.ping(id, addr); err != nil { + return err + } + tab.db.updateLastPong(id, time.Now()) + + // Start the background expiration goroutine after the first + // successful communication. Subsequent calls have no effect if it + // is already running. We do this here instead of somewhere else + // so that the search for seed nodes also considers older nodes + // that would otherwise be removed by the expiration. + tab.db.ensureExpirer() + return nil +} + +// add attempts to add the given node its corresponding bucket. If the +// bucket has space available, adding the node succeeds immediately. +// Otherwise, the node is added if the least recently active node in +// the bucket does not respond to a ping packet. +// +// The caller must not hold tab.mutex. +func (tab *Table) add(new *Node) { + b := tab.buckets[logdist(tab.self.sha, new.sha)] + tab.mutex.Lock() + defer tab.mutex.Unlock() + if b.bump(new) { + return + } + var oldest *Node + if len(b.entries) == bucketSize { + oldest = b.entries[bucketSize-1] + if oldest.contested { + // The node is already being replaced, don't attempt + // to replace it. + return + } + oldest.contested = true + // Let go of the mutex so other goroutines can access + // the table while we ping the least recently active node. + tab.mutex.Unlock() + err := tab.ping(oldest.ID, oldest.addr()) + tab.mutex.Lock() + oldest.contested = false + if err == nil { + // The node responded, don't replace it. + return + } + } + added := b.replace(new, oldest) + if added && tab.nodeAddedHook != nil { + tab.nodeAddedHook(new) + } +} + +// stuff adds nodes the table to the end of their corresponding bucket +// if the bucket is not full. The caller must hold tab.mutex. +func (tab *Table) stuff(nodes []*Node) { +outer: + for _, n := range nodes { + if n.ID == tab.self.ID { + continue // don't add self + } + bucket := tab.buckets[logdist(tab.self.sha, n.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == n.ID { + continue outer // already in bucket + } + } + if len(bucket.entries) < bucketSize { + bucket.entries = append(bucket.entries, n) + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(n) + } + } + } +} + +// delete removes an entry from the node table (used to evacuate +// failed/non-bonded discovery peers). +func (tab *Table) delete(node *Node) { + tab.mutex.Lock() + defer tab.mutex.Unlock() + bucket := tab.buckets[logdist(tab.self.sha, node.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == node.ID { + bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...) + return + } + } +} + +func (b *bucket) replace(n *Node, last *Node) bool { + // Don't add if b already contains n. + for i := range b.entries { + if b.entries[i].ID == n.ID { + return false + } + } + // Replace last if it is still the last entry or just add n if b + // isn't full. If is no longer the last entry, it has either been + // replaced with someone else or became active. + if len(b.entries) == bucketSize && (last == nil || b.entries[bucketSize-1].ID != last.ID) { + return false + } + if len(b.entries) < bucketSize { + b.entries = append(b.entries, nil) + } + copy(b.entries[1:], b.entries) + b.entries[0] = n + return true +} + +func (b *bucket) bump(n *Node) bool { + for i := range b.entries { + if b.entries[i].ID == n.ID { + // move it to the front + copy(b.entries[1:], b.entries[:i]) + b.entries[0] = n + return true + } + } + return false +} + +// nodesByDistance is a list of nodes, ordered by +// distance to target. +type nodesByDistance struct { + entries []*Node + target common.Hash +} + +// push adds the given node to the list, keeping the total size below maxElems. +func (h *nodesByDistance) push(n *Node, maxElems int) { + ix := sort.Search(len(h.entries), func(i int) bool { + return distcmp(h.target, h.entries[i].sha, n.sha) > 0 + }) + if len(h.entries) < maxElems { + h.entries = append(h.entries, n) + } + if ix == len(h.entries) { + // farther away than all nodes we already have. + // if there was room for it, the node is now the last element. + } else { + // slide existing entries down to make room + // this will overwrite the entry we just appended. + copy(h.entries[ix+1:], h.entries[ix:]) + h.entries[ix] = n + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/table_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/table_test.go new file mode 100644 index 0000000..1037cc6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/table_test.go @@ -0,0 +1,615 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "crypto/ecdsa" + "fmt" + "math/rand" + + "net" + "reflect" + "testing" + "testing/quick" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestTable_pingReplace(t *testing.T) { + doit := func(newNodeIsResponding, lastInBucketIsResponding bool) { + transport := newPingRecorder() + tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "") + defer tab.Close() + pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99) + + // fill up the sender's bucket. + last := fillBucket(tab, 253) + + // this call to bond should replace the last node + // in its bucket if the node is not responding. + transport.responding[last.ID] = lastInBucketIsResponding + transport.responding[pingSender.ID] = newNodeIsResponding + tab.bond(true, pingSender.ID, &net.UDPAddr{}, 0) + + // first ping goes to sender (bonding pingback) + if !transport.pinged[pingSender.ID] { + t.Error("table did not ping back sender") + } + if newNodeIsResponding { + // second ping goes to oldest node in bucket + // to see whether it is still alive. + if !transport.pinged[last.ID] { + t.Error("table did not ping last node in bucket") + } + } + + tab.mutex.Lock() + defer tab.mutex.Unlock() + if l := len(tab.buckets[253].entries); l != bucketSize { + t.Errorf("wrong bucket size after bond: got %d, want %d", l, bucketSize) + } + + if lastInBucketIsResponding || !newNodeIsResponding { + if !contains(tab.buckets[253].entries, last.ID) { + t.Error("last entry was removed") + } + if contains(tab.buckets[253].entries, pingSender.ID) { + t.Error("new entry was added") + } + } else { + if contains(tab.buckets[253].entries, last.ID) { + t.Error("last entry was not removed") + } + if !contains(tab.buckets[253].entries, pingSender.ID) { + t.Error("new entry was not added") + } + } + } + + doit(true, true) + doit(false, true) + doit(true, false) + doit(false, false) +} + +func TestBucket_bumpNoDuplicates(t *testing.T) { + t.Parallel() + cfg := &quick.Config{ + MaxCount: 1000, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + Values: func(args []reflect.Value, rand *rand.Rand) { + // generate a random list of nodes. this will be the content of the bucket. + n := rand.Intn(bucketSize-1) + 1 + nodes := make([]*Node, n) + for i := range nodes { + nodes[i] = nodeAtDistance(common.Hash{}, 200) + } + args[0] = reflect.ValueOf(nodes) + // generate random bump positions. + bumps := make([]int, rand.Intn(100)) + for i := range bumps { + bumps[i] = rand.Intn(len(nodes)) + } + args[1] = reflect.ValueOf(bumps) + }, + } + + prop := func(nodes []*Node, bumps []int) (ok bool) { + b := &bucket{entries: make([]*Node, len(nodes))} + copy(b.entries, nodes) + for i, pos := range bumps { + b.bump(b.entries[pos]) + if hasDuplicates(b.entries) { + t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) + for _, n := range b.entries { + t.Logf(" %p", n) + } + return false + } + } + return true + } + if err := quick.Check(prop, cfg); err != nil { + t.Error(err) + } +} + +// fillBucket inserts nodes into the given bucket until +// it is full. The node's IDs dont correspond to their +// hashes. +func fillBucket(tab *Table, ld int) (last *Node) { + b := tab.buckets[ld] + for len(b.entries) < bucketSize { + b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld)) + } + return b.entries[bucketSize-1] +} + +// nodeAtDistance creates a node for which logdist(base, n.sha) == ld. +// The node's ID does not correspond to n.sha. +func nodeAtDistance(base common.Hash, ld int) (n *Node) { + n = new(Node) + n.sha = hashAtDistance(base, ld) + n.IP = net.IP{10, 0, 2, byte(ld)} + copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID + return n +} + +type pingRecorder struct{ responding, pinged map[NodeID]bool } + +func newPingRecorder() *pingRecorder { + return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)} +} + +func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { + panic("findnode called on pingRecorder") +} +func (t *pingRecorder) close() {} +func (t *pingRecorder) waitping(from NodeID) error { + return nil // remote always pings +} +func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error { + t.pinged[toid] = true + if t.responding[toid] { + return nil + } else { + return errTimeout + } +} + +func TestTable_closest(t *testing.T) { + t.Parallel() + + test := func(test *closeTest) bool { + // for any node table, Target and N + tab, _ := newTable(nil, test.Self, &net.UDPAddr{}, "") + defer tab.Close() + tab.stuff(test.All) + + // check that doClosest(Target, N) returns nodes + result := tab.closest(test.Target, test.N).entries + if hasDuplicates(result) { + t.Errorf("result contains duplicates") + return false + } + if !sortedByDistanceTo(test.Target, result) { + t.Errorf("result is not sorted by distance to target") + return false + } + + // check that the number of results is min(N, tablen) + wantN := test.N + if tlen := tab.len(); tlen < test.N { + wantN = tlen + } + if len(result) != wantN { + t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN) + return false + } else if len(result) == 0 { + return true // no need to check distance + } + + // check that the result nodes have minimum distance to target. + for _, b := range tab.buckets { + for _, n := range b.entries { + if contains(result, n.ID) { + continue // don't run the check below for nodes in result + } + farthestResult := result[len(result)-1].sha + if distcmp(test.Target, n.sha, farthestResult) < 0 { + t.Errorf("table contains node that is closer to target but it's not in result") + t.Logf(" Target: %v", test.Target) + t.Logf(" Farthest Result: %v", farthestResult) + t.Logf(" ID: %v", n.ID) + return false + } + } + } + return true + } + if err := quick.Check(test, quickcfg()); err != nil { + t.Error(err) + } +} + +func TestTable_ReadRandomNodesGetAll(t *testing.T) { + cfg := &quick.Config{ + MaxCount: 200, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + Values: func(args []reflect.Value, rand *rand.Rand) { + args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000))) + }, + } + test := func(buf []*Node) bool { + tab, _ := newTable(nil, NodeID{}, &net.UDPAddr{}, "") + defer tab.Close() + for i := 0; i < len(buf); i++ { + ld := cfg.Rand.Intn(len(tab.buckets)) + tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) + } + gotN := tab.ReadRandomNodes(buf) + if gotN != tab.len() { + t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.len()) + return false + } + if hasDuplicates(buf[:gotN]) { + t.Errorf("result contains duplicates") + return false + } + return true + } + if err := quick.Check(test, cfg); err != nil { + t.Error(err) + } +} + +type closeTest struct { + Self NodeID + Target common.Hash + All []*Node + N int +} + +func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { + t := &closeTest{ + Self: gen(NodeID{}, rand).(NodeID), + Target: gen(common.Hash{}, rand).(common.Hash), + N: rand.Intn(bucketSize), + } + for _, id := range gen([]NodeID{}, rand).([]NodeID) { + t.All = append(t.All, &Node{ID: id}) + } + return reflect.ValueOf(t) +} + +func TestTable_Lookup(t *testing.T) { + self := nodeAtDistance(common.Hash{}, 0) + tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "") + defer tab.Close() + + // lookup on empty table returns no nodes + if results := tab.Lookup(lookupTestnet.target); len(results) > 0 { + t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) + } + // seed table with initial node (otherwise lookup will terminate immediately) + seed := NewNode(lookupTestnet.dists[256][0], net.IP{}, 256, 0) + tab.stuff([]*Node{seed}) + + results := tab.Lookup(lookupTestnet.target) + t.Logf("results:") + for _, e := range results { + t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:]) + } + if len(results) != bucketSize { + t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) + } + if hasDuplicates(results) { + t.Errorf("result set contains duplicate entries") + } + if !sortedByDistanceTo(lookupTestnet.targetSha, results) { + t.Errorf("result set not sorted by distance to target") + } + // TODO: check result nodes are actually closest +} + +// This is the test network for the Lookup test. +// The nodes were obtained by running testnet.mine with a random NodeID as target. +var lookupTestnet = &preminedTestnet{ + target: MustHexID("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"), + targetSha: common.Hash{0x5c, 0x94, 0x4e, 0xe5, 0x1c, 0x5a, 0xe9, 0xf7, 0x2a, 0x95, 0xec, 0xcb, 0x8a, 0xed, 0x3, 0x74, 0xee, 0xcb, 0x51, 0x19, 0xd7, 0x20, 0xcb, 0xea, 0x68, 0x13, 0xe8, 0xe0, 0xd6, 0xad, 0x92, 0x61}, + dists: [257][]NodeID{ + 240: { + MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"), + MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"), + }, + 244: { + MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"), + }, + 246: { + MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"), + MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"), + MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"), + }, + 247: { + MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), + MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), + MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), + MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), + MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"), + MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"), + MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"), + MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"), + MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"), + MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"), + }, + 248: { + MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"), + MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"), + MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"), + MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"), + MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"), + MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"), + MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"), + MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"), + MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"), + MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"), + MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"), + MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"), + MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"), + MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"), + MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"), + MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"), + }, + 249: { + MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"), + MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"), + MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"), + MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"), + MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"), + MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"), + MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"), + MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"), + MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"), + MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"), + MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"), + MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"), + MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"), + MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"), + MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"), + MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"), + }, + 250: { + MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"), + MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"), + MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"), + MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"), + MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"), + MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"), + MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"), + MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"), + MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"), + MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"), + MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"), + MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"), + MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"), + MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"), + MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"), + MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"), + }, + 251: { + MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"), + MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"), + MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"), + MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"), + MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"), + MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"), + MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"), + MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"), + MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"), + MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"), + MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"), + MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"), + MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"), + MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"), + MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"), + MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"), + }, + 252: { + MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"), + MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"), + MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"), + MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"), + MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"), + MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"), + MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"), + MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"), + MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"), + MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"), + MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"), + MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"), + MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"), + MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"), + MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"), + MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"), + }, + 253: { + MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"), + MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"), + MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"), + MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"), + MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"), + MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"), + MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"), + MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"), + MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"), + MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"), + MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"), + MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"), + MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"), + MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"), + MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"), + MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"), + }, + 254: { + MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"), + MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"), + MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"), + MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"), + MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"), + MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"), + MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"), + MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"), + MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"), + MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"), + MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"), + MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"), + MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"), + MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"), + MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"), + MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"), + }, + 255: { + MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"), + MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"), + MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"), + MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"), + MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"), + MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"), + MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"), + MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"), + MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"), + MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"), + MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"), + MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"), + MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"), + MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"), + MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"), + MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"), + }, + 256: { + MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"), + MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"), + MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"), + MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"), + MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"), + MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"), + MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"), + MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"), + MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"), + MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"), + MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"), + MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"), + MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"), + MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"), + MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"), + MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"), + }, + }, +} + +type preminedTestnet struct { + target NodeID + targetSha common.Hash // sha3(target) + dists [hashBits + 1][]NodeID +} + +func (tn *preminedTestnet) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { + // current log distance is encoded in port number + // fmt.Println("findnode query at dist", toaddr.Port) + if toaddr.Port == 0 { + panic("query to node at distance 0") + } + next := uint16(toaddr.Port) - 1 + var result []*Node + for i, id := range tn.dists[toaddr.Port] { + result = append(result, NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i))) + } + return result, nil +} + +func (*preminedTestnet) close() {} +func (*preminedTestnet) waitping(from NodeID) error { return nil } +func (*preminedTestnet) ping(toid NodeID, toaddr *net.UDPAddr) error { return nil } + +// mine generates a testnet struct literal with nodes at +// various distances to the given target. +func (n *preminedTestnet) mine(target NodeID) { + n.target = target + n.targetSha = crypto.Keccak256Hash(n.target[:]) + found := 0 + for found < bucketSize*10 { + k := newkey() + id := PubkeyID(&k.PublicKey) + sha := crypto.Keccak256Hash(id[:]) + ld := logdist(n.targetSha, sha) + if len(n.dists[ld]) < bucketSize { + n.dists[ld] = append(n.dists[ld], id) + fmt.Println("found ID with ld", ld) + found++ + } + } + fmt.Println("&preminedTestnet{") + fmt.Printf(" target: %#v,\n", n.target) + fmt.Printf(" targetSha: %#v,\n", n.targetSha) + fmt.Printf(" dists: [%d][]NodeID{\n", len(n.dists)) + for ld, ns := range n.dists { + if len(ns) == 0 { + continue + } + fmt.Printf(" %d: []NodeID{\n", ld) + for _, n := range ns { + fmt.Printf(" MustHexID(\"%x\"),\n", n[:]) + } + fmt.Println(" },") + } + fmt.Println(" },") + fmt.Println("}") +} + +func hasDuplicates(slice []*Node) bool { + seen := make(map[NodeID]bool) + for i, e := range slice { + if e == nil { + panic(fmt.Sprintf("nil *Node at %d", i)) + } + if seen[e.ID] { + return true + } + seen[e.ID] = true + } + return false +} + +func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool { + var last common.Hash + for i, e := range slice { + if i > 0 && distcmp(distbase, e.sha, last) < 0 { + return false + } + last = e.sha + } + return true +} + +func contains(ns []*Node, id NodeID) bool { + for _, n := range ns { + if n.ID == id { + return true + } + } + return false +} + +// gen wraps quick.Value so it's easier to use. +// it generates a random value of the given value's type. +func gen(typ interface{}, rand *rand.Rand) interface{} { + v, ok := quick.Value(reflect.TypeOf(typ), rand) + if !ok { + panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) + } + return v.Interface() +} + +func newkey() *ecdsa.PrivateKey { + key, err := crypto.GenerateKey() + if err != nil { + panic("couldn't generate key: " + err.Error()) + } + return key +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go new file mode 100644 index 0000000..e09c63f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp.go @@ -0,0 +1,636 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "bytes" + "container/list" + "crypto/ecdsa" + "errors" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/rlp" +) + +const Version = 4 + +// Errors +var ( + errPacketTooSmall = errors.New("too small") + errBadHash = errors.New("bad hash") + errExpired = errors.New("expired") + errUnsolicitedReply = errors.New("unsolicited reply") + errUnknownNode = errors.New("unknown node") + errTimeout = errors.New("RPC timeout") + errClockWarp = errors.New("reply deadline too far in the future") + errClosed = errors.New("socket closed") +) + +// Timeouts +const ( + respTimeout = 500 * time.Millisecond + sendTimeout = 500 * time.Millisecond + expiration = 20 * time.Second + + ntpFailureThreshold = 32 // Continuous timeouts after which to check NTP + ntpWarningCooldown = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning + driftThreshold = 10 * time.Second // Allowed clock drift before warning user +) + +// RPC packet types +const ( + pingPacket = iota + 1 // zero is 'reserved' + pongPacket + findnodePacket + neighborsPacket +) + +// RPC request structures +type ( + ping struct { + Version uint + From, To rpcEndpoint + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // pong is the reply to ping. + pong struct { + // This field should mirror the UDP envelope address + // of the ping packet, which provides a way to discover the + // the external address (after NAT). + To rpcEndpoint + + ReplyTok []byte // This contains the hash of the ping packet. + Expiration uint64 // Absolute timestamp at which the packet becomes invalid. + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // findnode is a query for nodes close to the given target. + findnode struct { + Target NodeID // doesn't need to be an actual public key + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // reply to findnode + neighbors struct { + Nodes []rpcNode + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + rpcNode struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + ID NodeID + } + + rpcEndpoint struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + } +) + +func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { + ip := addr.IP.To4() + if ip == nil { + ip = addr.IP.To16() + } + return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} +} + +func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { + if rn.UDP <= 1024 { + return nil, errors.New("low port") + } + if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { + return nil, err + } + if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { + return nil, errors.New("not contained in netrestrict whitelist") + } + n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) + err := n.validateComplete() + return n, err +} + +func nodeToRPC(n *Node) rpcNode { + return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} +} + +type packet interface { + handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error +} + +type conn interface { + ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) + WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) + Close() error + LocalAddr() net.Addr +} + +// udp implements the RPC protocol. +type udp struct { + conn conn + netrestrict *netutil.Netlist + priv *ecdsa.PrivateKey + ourEndpoint rpcEndpoint + + addpending chan *pending + gotreply chan reply + + closing chan struct{} + nat nat.Interface + + *Table +} + +// pending represents a pending reply. +// +// some implementations of the protocol wish to send more than one +// reply packet to findnode. in general, any neighbors packet cannot +// be matched up with a specific findnode packet. +// +// our implementation handles this by storing a callback function for +// each pending reply. incoming packets from a node are dispatched +// to all the callback functions for that node. +type pending struct { + // these fields must match in the reply. + from NodeID + ptype byte + + // time when the request must complete + deadline time.Time + + // callback is called when a matching reply arrives. if it returns + // true, the callback is removed from the pending reply queue. + // if it returns false, the reply is considered incomplete and + // the callback will be invoked again for the next matching reply. + callback func(resp interface{}) (done bool) + + // errc receives nil when the callback indicates completion or an + // error if no further reply is received within the timeout. + errc chan<- error +} + +type reply struct { + from NodeID + ptype byte + data interface{} + // loop indicates whether there was + // a matching request by sending on this channel. + matched chan<- bool +} + +// ListenUDP returns a new table that listens for UDP packets on laddr. +func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) { + addr, err := net.ResolveUDPAddr("udp", laddr) + if err != nil { + return nil, err + } + conn, err := net.ListenUDP("udp", addr) + if err != nil { + return nil, err + } + tab, _, err := newUDP(priv, conn, natm, nodeDBPath, netrestrict) + if err != nil { + return nil, err + } + glog.V(logger.Info).Infoln("Listening,", tab.self) + return tab, nil +} + +func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) { + udp := &udp{ + conn: c, + priv: priv, + netrestrict: netrestrict, + closing: make(chan struct{}), + gotreply: make(chan reply), + addpending: make(chan *pending), + } + realaddr := c.LocalAddr().(*net.UDPAddr) + if natm != nil { + if !realaddr.IP.IsLoopback() { + go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery") + } + // TODO: react to external IP changes over time. + if ext, err := natm.ExternalIP(); err == nil { + realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port} + } + } + // TODO: separate TCP port + udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port)) + tab, err := newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath) + if err != nil { + return nil, nil, err + } + udp.Table = tab + + go udp.loop() + go udp.readLoop() + return udp.Table, udp, nil +} + +func (t *udp) close() { + close(t.closing) + t.conn.Close() + // TODO: wait for the loops to end. +} + +// ping sends a ping message to the given node and waits for a reply. +func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error { + // TODO: maybe check for ReplyTo field in callback to measure RTT + errc := t.pending(toid, pongPacket, func(interface{}) bool { return true }) + t.send(toaddr, pingPacket, ping{ + Version: Version, + From: t.ourEndpoint, + To: makeEndpoint(toaddr, 0), // TODO: maybe use known TCP port from DB + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) + return <-errc +} + +func (t *udp) waitping(from NodeID) error { + return <-t.pending(from, pingPacket, func(interface{}) bool { return true }) +} + +// findnode sends a findnode request to the given node and waits until +// the node has sent up to k neighbors. +func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { + nodes := make([]*Node, 0, bucketSize) + nreceived := 0 + errc := t.pending(toid, neighborsPacket, func(r interface{}) bool { + reply := r.(*neighbors) + for _, rn := range reply.Nodes { + nreceived++ + n, err := t.nodeFromRPC(toaddr, rn) + if err != nil { + glog.V(logger.Detail).Infof("invalid neighbor node (%v) from %v: %v", rn.IP, toaddr, err) + continue + } + nodes = append(nodes, n) + } + return nreceived >= bucketSize + }) + t.send(toaddr, findnodePacket, findnode{ + Target: target, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) + err := <-errc + return nodes, err +} + +// pending adds a reply callback to the pending reply queue. +// see the documentation of type pending for a detailed explanation. +func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-chan error { + ch := make(chan error, 1) + p := &pending{from: id, ptype: ptype, callback: callback, errc: ch} + select { + case t.addpending <- p: + // loop will handle it + case <-t.closing: + ch <- errClosed + } + return ch +} + +func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool { + matched := make(chan bool, 1) + select { + case t.gotreply <- reply{from, ptype, req, matched}: + // loop will handle it + return <-matched + case <-t.closing: + return false + } +} + +// loop runs in its own goroutine. it keeps track of +// the refresh timer and the pending reply queue. +func (t *udp) loop() { + var ( + plist = list.New() + timeout = time.NewTimer(0) + nextTimeout *pending // head of plist when timeout was last reset + contTimeouts = 0 // number of continuous timeouts to do NTP checks + ntpWarnTime = time.Unix(0, 0) + ) + <-timeout.C // ignore first timeout + defer timeout.Stop() + + resetTimeout := func() { + if plist.Front() == nil || nextTimeout == plist.Front().Value { + return + } + // Start the timer so it fires when the next pending reply has expired. + now := time.Now() + for el := plist.Front(); el != nil; el = el.Next() { + nextTimeout = el.Value.(*pending) + if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout { + timeout.Reset(dist) + return + } + // Remove pending replies whose deadline is too far in the + // future. These can occur if the system clock jumped + // backwards after the deadline was assigned. + nextTimeout.errc <- errClockWarp + plist.Remove(el) + } + nextTimeout = nil + timeout.Stop() + } + + for { + resetTimeout() + + select { + case <-t.closing: + for el := plist.Front(); el != nil; el = el.Next() { + el.Value.(*pending).errc <- errClosed + } + return + + case p := <-t.addpending: + p.deadline = time.Now().Add(respTimeout) + plist.PushBack(p) + + case r := <-t.gotreply: + var matched bool + for el := plist.Front(); el != nil; el = el.Next() { + p := el.Value.(*pending) + if p.from == r.from && p.ptype == r.ptype { + matched = true + // Remove the matcher if its callback indicates + // that all replies have been received. This is + // required for packet types that expect multiple + // reply packets. + if p.callback(r.data) { + p.errc <- nil + plist.Remove(el) + } + // Reset the continuous timeout counter (time drift detection) + contTimeouts = 0 + } + } + r.matched <- matched + + case now := <-timeout.C: + nextTimeout = nil + + // Notify and remove callbacks whose deadline is in the past. + for el := plist.Front(); el != nil; el = el.Next() { + p := el.Value.(*pending) + if now.After(p.deadline) || now.Equal(p.deadline) { + p.errc <- errTimeout + plist.Remove(el) + contTimeouts++ + } + } + // If we've accumulated too many timeouts, do an NTP time sync check + if contTimeouts > ntpFailureThreshold { + if time.Since(ntpWarnTime) >= ntpWarningCooldown { + ntpWarnTime = time.Now() + go checkClockDrift() + } + contTimeouts = 0 + } + } + } +} + +const ( + macSize = 256 / 8 + sigSize = 520 / 8 + headSize = macSize + sigSize // space of packet frame data +) + +var ( + headSpace = make([]byte, headSize) + + // Neighbors replies are sent across multiple packets to + // stay below the 1280 byte limit. We compute the maximum number + // of entries by stuffing a packet until it grows too large. + maxNeighbors int +) + +func init() { + p := neighbors{Expiration: ^uint64(0)} + maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} + for n := 0; ; n++ { + p.Nodes = append(p.Nodes, maxSizeNode) + size, _, err := rlp.EncodeToReader(p) + if err != nil { + // If this ever happens, it will be caught by the unit tests. + panic("cannot encode: " + err.Error()) + } + if headSize+size+1 >= 1280 { + maxNeighbors = n + break + } + } +} + +func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req interface{}) error { + packet, err := encodePacket(t.priv, ptype, req) + if err != nil { + return err + } + glog.V(logger.Detail).Infof(">>> %v %T\n", toaddr, req) + if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil { + glog.V(logger.Detail).Infoln("UDP send failed:", err) + } + return err +} + +func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte, error) { + b := new(bytes.Buffer) + b.Write(headSpace) + b.WriteByte(ptype) + if err := rlp.Encode(b, req); err != nil { + glog.V(logger.Error).Infoln("error encoding packet:", err) + return nil, err + } + packet := b.Bytes() + sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) + if err != nil { + glog.V(logger.Error).Infoln("could not sign packet:", err) + return nil, err + } + copy(packet[macSize:], sig) + // add the hash to the front. Note: this doesn't protect the + // packet in any way. Our public key will be part of this hash in + // The future. + copy(packet, crypto.Keccak256(packet[macSize:])) + return packet, nil +} + +// readLoop runs in its own goroutine. it handles incoming UDP packets. +func (t *udp) readLoop() { + defer t.conn.Close() + // Discovery packets are defined to be no larger than 1280 bytes. + // Packets larger than this size will be cut at the end and treated + // as invalid because their hash won't match. + buf := make([]byte, 1280) + for { + nbytes, from, err := t.conn.ReadFromUDP(buf) + if netutil.IsTemporaryError(err) { + // Ignore temporary read errors. + glog.V(logger.Debug).Infof("Temporary read error: %v", err) + continue + } else if err != nil { + // Shut down the loop for permament errors. + glog.V(logger.Debug).Infof("Read error: %v", err) + return + } + t.handlePacket(from, buf[:nbytes]) + } +} + +func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { + packet, fromID, hash, err := decodePacket(buf) + if err != nil { + glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) + return err + } + status := "ok" + if err = packet.handle(t, from, fromID, hash); err != nil { + status = err.Error() + } + glog.V(logger.Detail).Infof("<<< %v %T: %s\n", from, packet, status) + return err +} + +func decodePacket(buf []byte) (packet, NodeID, []byte, error) { + if len(buf) < headSize+1 { + return nil, NodeID{}, nil, errPacketTooSmall + } + hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] + shouldhash := crypto.Keccak256(buf[macSize:]) + if !bytes.Equal(hash, shouldhash) { + return nil, NodeID{}, nil, errBadHash + } + fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) + if err != nil { + return nil, NodeID{}, hash, err + } + var req packet + switch ptype := sigdata[0]; ptype { + case pingPacket: + req = new(ping) + case pongPacket: + req = new(pong) + case findnodePacket: + req = new(findnode) + case neighborsPacket: + req = new(neighbors) + default: + return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype) + } + s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) + err = s.Decode(req) + return req, fromID, hash, err +} + +func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { + if expired(req.Expiration) { + return errExpired + } + t.send(from, pongPacket, pong{ + To: makeEndpoint(from, req.From.TCP), + ReplyTok: mac, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) + if !t.handleReply(fromID, pingPacket, req) { + // Note: we're ignoring the provided IP address right now + go t.bond(true, fromID, from, req.From.TCP) + } + return nil +} + +func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { + if expired(req.Expiration) { + return errExpired + } + if !t.handleReply(fromID, pongPacket, req) { + return errUnsolicitedReply + } + return nil +} + +func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { + if expired(req.Expiration) { + return errExpired + } + if t.db.node(fromID) == nil { + // No bond exists, we don't process the packet. This prevents + // an attack vector where the discovery protocol could be used + // to amplify traffic in a DDOS attack. A malicious actor + // would send a findnode request with the IP address and UDP + // port of the target as the source address. The recipient of + // the findnode packet would then send a neighbors packet + // (which is a much bigger packet than findnode) to the victim. + return errUnknownNode + } + target := crypto.Keccak256Hash(req.Target[:]) + t.mutex.Lock() + closest := t.closest(target, bucketSize).entries + t.mutex.Unlock() + + p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} + // Send neighbors in chunks with at most maxNeighbors per packet + // to stay below the 1280 byte limit. + for i, n := range closest { + if netutil.CheckRelayIP(from.IP, n.IP) != nil { + continue + } + p.Nodes = append(p.Nodes, nodeToRPC(n)) + if len(p.Nodes) == maxNeighbors || i == len(closest)-1 { + t.send(from, neighborsPacket, p) + p.Nodes = p.Nodes[:0] + } + } + return nil +} + +func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { + if expired(req.Expiration) { + return errExpired + } + if !t.handleReply(fromID, neighborsPacket, req) { + return errUnsolicitedReply + } + return nil +} + +func expired(ts uint64) bool { + return time.Unix(int64(ts), 0).Before(time.Now()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_test.go new file mode 100644 index 0000000..21e8b56 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/udp_test.go @@ -0,0 +1,562 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "bytes" + "crypto/ecdsa" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + "math/rand" + "net" + "path/filepath" + "reflect" + "runtime" + "sync" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + spew.Config.DisableMethods = true +} + +// shared test variables +var ( + futureExp = uint64(time.Now().Add(10 * time.Hour).Unix()) + testTarget = NodeID{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} + testRemote = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2} + testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4} + testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6} +) + +type udpTest struct { + t *testing.T + pipe *dgramPipe + table *Table + udp *udp + sent [][]byte + localkey, remotekey *ecdsa.PrivateKey + remoteaddr *net.UDPAddr +} + +func newUDPTest(t *testing.T) *udpTest { + test := &udpTest{ + t: t, + pipe: newpipe(), + localkey: newkey(), + remotekey: newkey(), + remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, + } + test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "", nil) + return test +} + +// handles a packet as if it had been sent to the transport. +func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error { + enc, err := encodePacket(test.remotekey, ptype, data) + if err != nil { + return test.errorf("packet (%d) encode error: %v", ptype, err) + } + test.sent = append(test.sent, enc) + if err = test.udp.handlePacket(test.remoteaddr, enc); err != wantError { + return test.errorf("error mismatch: got %q, want %q", err, wantError) + } + return nil +} + +// waits for a packet to be sent by the transport. +// validate should have type func(*udpTest, X) error, where X is a packet type. +func (test *udpTest) waitPacketOut(validate interface{}) error { + dgram := test.pipe.waitPacketOut() + p, _, _, err := decodePacket(dgram) + if err != nil { + return test.errorf("sent packet decode error: %v", err) + } + fn := reflect.ValueOf(validate) + exptype := fn.Type().In(0) + if reflect.TypeOf(p) != exptype { + return test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) + } + fn.Call([]reflect.Value{reflect.ValueOf(p)}) + return nil +} + +func (test *udpTest) errorf(format string, args ...interface{}) error { + _, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut + if ok { + file = filepath.Base(file) + } else { + file = "???" + line = 1 + } + err := fmt.Errorf(format, args...) + fmt.Printf("\t%s:%d: %v\n", file, line, err) + test.t.Fail() + return err +} + +func TestUDP_packetErrors(t *testing.T) { + test := newUDPTest(t) + defer test.table.Close() + + test.packetIn(errExpired, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version}) + test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp}) + test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp}) + test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp}) +} + +func TestUDP_pingTimeout(t *testing.T) { + t.Parallel() + test := newUDPTest(t) + defer test.table.Close() + + toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} + toid := NodeID{1, 2, 3, 4} + if err := test.udp.ping(toid, toaddr); err != errTimeout { + t.Error("expected timeout error, got", err) + } +} + +func TestUDP_responseTimeouts(t *testing.T) { + t.Parallel() + test := newUDPTest(t) + defer test.table.Close() + + rand.Seed(time.Now().UnixNano()) + randomDuration := func(max time.Duration) time.Duration { + return time.Duration(rand.Int63n(int64(max))) + } + + var ( + nReqs = 200 + nTimeouts = 0 // number of requests with ptype > 128 + nilErr = make(chan error, nReqs) // for requests that get a reply + timeoutErr = make(chan error, nReqs) // for requests that time out + ) + for i := 0; i < nReqs; i++ { + // Create a matcher for a random request in udp.loop. Requests + // with ptype <= 128 will not get a reply and should time out. + // For all other requests, a reply is scheduled to arrive + // within the timeout window. + p := &pending{ + ptype: byte(rand.Intn(255)), + callback: func(interface{}) bool { return true }, + } + binary.BigEndian.PutUint64(p.from[:], uint64(i)) + if p.ptype <= 128 { + p.errc = timeoutErr + test.udp.addpending <- p + nTimeouts++ + } else { + p.errc = nilErr + test.udp.addpending <- p + time.AfterFunc(randomDuration(60*time.Millisecond), func() { + if !test.udp.handleReply(p.from, p.ptype, nil) { + t.Logf("not matched: %v", p) + } + }) + } + time.Sleep(randomDuration(30 * time.Millisecond)) + } + + // Check that all timeouts were delivered and that the rest got nil errors. + // The replies must be delivered. + var ( + recvDeadline = time.After(20 * time.Second) + nTimeoutsRecv, nNil = 0, 0 + ) + for i := 0; i < nReqs; i++ { + select { + case err := <-timeoutErr: + if err != errTimeout { + t.Fatalf("got non-timeout error on timeoutErr %d: %v", i, err) + } + nTimeoutsRecv++ + case err := <-nilErr: + if err != nil { + t.Fatalf("got non-nil error on nilErr %d: %v", i, err) + } + nNil++ + case <-recvDeadline: + t.Fatalf("exceeded recv deadline") + } + } + if nTimeoutsRecv != nTimeouts { + t.Errorf("wrong number of timeout errors received: got %d, want %d", nTimeoutsRecv, nTimeouts) + } + if nNil != nReqs-nTimeouts { + t.Errorf("wrong number of successful replies: got %d, want %d", nNil, nReqs-nTimeouts) + } +} + +func TestUDP_findnodeTimeout(t *testing.T) { + t.Parallel() + test := newUDPTest(t) + defer test.table.Close() + + toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} + toid := NodeID{1, 2, 3, 4} + target := NodeID{4, 5, 6, 7} + result, err := test.udp.findnode(toid, toaddr, target) + if err != errTimeout { + t.Error("expected timeout error, got", err) + } + if len(result) > 0 { + t.Error("expected empty result, got", result) + } +} + +func TestUDP_findnode(t *testing.T) { + test := newUDPTest(t) + defer test.table.Close() + + // put a few nodes into the table. their exact + // distribution shouldn't matter much, although we need to + // take care not to overflow any bucket. + targetHash := crypto.Keccak256Hash(testTarget[:]) + nodes := &nodesByDistance{target: targetHash} + for i := 0; i < bucketSize; i++ { + nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize) + } + test.table.stuff(nodes.entries) + + // ensure there's a bond with the test node, + // findnode won't be accepted otherwise. + test.table.db.updateNode(NewNode( + PubkeyID(&test.remotekey.PublicKey), + test.remoteaddr.IP, + uint16(test.remoteaddr.Port), + 99, + )) + // check that closest neighbors are returned. + test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp}) + expected := test.table.closest(targetHash, bucketSize) + + waitNeighbors := func(want []*Node) { + test.waitPacketOut(func(p *neighbors) { + if len(p.Nodes) != len(want) { + t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize) + } + for i := range p.Nodes { + if p.Nodes[i].ID != want[i].ID { + t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i]) + } + } + }) + } + waitNeighbors(expected.entries[:maxNeighbors]) + waitNeighbors(expected.entries[maxNeighbors:]) +} + +func TestUDP_findnodeMultiReply(t *testing.T) { + test := newUDPTest(t) + defer test.table.Close() + + // queue a pending findnode request + resultc, errc := make(chan []*Node), make(chan error) + go func() { + rid := PubkeyID(&test.remotekey.PublicKey) + ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget) + if err != nil && len(ns) == 0 { + errc <- err + } else { + resultc <- ns + } + }() + + // wait for the findnode to be sent. + // after it is sent, the transport is waiting for a reply + test.waitPacketOut(func(p *findnode) { + if p.Target != testTarget { + t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) + } + }) + + // send the reply as two packets. + list := []*Node{ + MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"), + MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"), + MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"), + MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"), + } + rpclist := make([]rpcNode, len(list)) + for i := range list { + rpclist[i] = nodeToRPC(list[i]) + } + test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]}) + test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]}) + + // check that the sent neighbors are all returned by findnode + select { + case result := <-resultc: + want := append(list[:2], list[3:]...) + if !reflect.DeepEqual(result, want) { + t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, want) + } + case err := <-errc: + t.Errorf("findnode error: %v", err) + case <-time.After(5 * time.Second): + t.Error("findnode did not return within 5 seconds") + } +} + +func TestUDP_successfulPing(t *testing.T) { + test := newUDPTest(t) + added := make(chan *Node, 1) + test.table.nodeAddedHook = func(n *Node) { added <- n } + defer test.table.Close() + + // The remote side sends a ping packet to initiate the exchange. + go test.packetIn(nil, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version, Expiration: futureExp}) + + // the ping is replied to. + test.waitPacketOut(func(p *pong) { + pinghash := test.sent[0][:macSize] + if !bytes.Equal(p.ReplyTok, pinghash) { + t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) + } + wantTo := rpcEndpoint{ + // The mirrored UDP address is the UDP packet sender + IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), + // The mirrored TCP port is the one from the ping packet + TCP: testRemote.TCP, + } + if !reflect.DeepEqual(p.To, wantTo) { + t.Errorf("got pong.To %v, want %v", p.To, wantTo) + } + }) + + // remote is unknown, the table pings back. + test.waitPacketOut(func(p *ping) error { + if !reflect.DeepEqual(p.From, test.udp.ourEndpoint) { + t.Errorf("got ping.From %v, want %v", p.From, test.udp.ourEndpoint) + } + wantTo := rpcEndpoint{ + // The mirrored UDP address is the UDP packet sender. + IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), + TCP: 0, + } + if !reflect.DeepEqual(p.To, wantTo) { + t.Errorf("got ping.To %v, want %v", p.To, wantTo) + } + return nil + }) + test.packetIn(nil, pongPacket, &pong{Expiration: futureExp}) + + // the node should be added to the table shortly after getting the + // pong packet. + select { + case n := <-added: + rid := PubkeyID(&test.remotekey.PublicKey) + if n.ID != rid { + t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid) + } + if !n.IP.Equal(test.remoteaddr.IP) { + t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP) + } + if int(n.UDP) != test.remoteaddr.Port { + t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port) + } + if n.TCP != testRemote.TCP { + t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP) + } + case <-time.After(2 * time.Second): + t.Errorf("node was not added within 2 seconds") + } +} + +var testPackets = []struct { + input string + wantPacket interface{} +}{ + { + input: "71dbda3a79554728d4f94411e42ee1f8b0d561c10e1e5f5893367948c6a7d70bb87b235fa28a77070271b6c164a2dce8c7e13a5739b53b5e96f2e5acb0e458a02902f5965d55ecbeb2ebb6cabb8b2b232896a36b737666c55265ad0a68412f250001ea04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a355", + wantPacket: &ping{ + Version: 4, + From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333}, + Expiration: 1136239445, + Rest: []rlp.RawValue{}, + }, + }, + { + input: "e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102", + wantPacket: &ping{ + Version: 4, + From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333}, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x01}, {0x02}}, + }, + }, + { + input: "577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba76023fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee1917084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c76d922dc3", + wantPacket: &ping{ + Version: 555, + From: rpcEndpoint{net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338}, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0xC5, 0x01, 0x02, 0x03, 0x04, 0x05}}, + }, + }, + { + input: "09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b2069869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f055542124e", + wantPacket: &pong{ + To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338}, + ReplyTok: common.Hex2Bytes("fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c954"), + Expiration: 1136239445, + Rest: []rlp.RawValue{{0xC6, 0x01, 0x02, 0x03, 0xC2, 0x04, 0x05}, {0x06}}, + }, + }, + { + input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396", + wantPacket: &findnode{ + Target: MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"), + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}}, + }, + }, + { + input: "c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0", + wantPacket: &neighbors{ + Nodes: []rpcNode{ + { + ID: MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), + IP: net.ParseIP("99.33.22.55").To4(), + UDP: 4444, + TCP: 4445, + }, + { + ID: MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), + IP: net.ParseIP("1.2.3.4").To4(), + UDP: 1, + TCP: 1, + }, + { + ID: MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), + IP: net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), + UDP: 3333, + TCP: 3333, + }, + { + ID: MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), + IP: net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), + UDP: 999, + TCP: 1000, + }, + }, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x01}, {0x02}, {0x03}}, + }, + }, +} + +func TestForwardCompatibility(t *testing.T) { + testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + wantNodeID := PubkeyID(&testkey.PublicKey) + + for _, test := range testPackets { + input, err := hex.DecodeString(test.input) + if err != nil { + t.Fatalf("invalid hex: %s", test.input) + } + packet, nodeid, _, err := decodePacket(input) + if err != nil { + t.Errorf("did not accept packet %s\n%v", test.input, err) + continue + } + if !reflect.DeepEqual(packet, test.wantPacket) { + t.Errorf("got %s\nwant %s", spew.Sdump(packet), spew.Sdump(test.wantPacket)) + } + if nodeid != wantNodeID { + t.Errorf("got id %v\nwant id %v", nodeid, wantNodeID) + } + } +} + +// dgramPipe is a fake UDP socket. It queues all sent datagrams. +type dgramPipe struct { + mu *sync.Mutex + cond *sync.Cond + closing chan struct{} + closed bool + queue [][]byte +} + +func newpipe() *dgramPipe { + mu := new(sync.Mutex) + return &dgramPipe{ + closing: make(chan struct{}), + cond: &sync.Cond{L: mu}, + mu: mu, + } +} + +// WriteToUDP queues a datagram. +func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { + msg := make([]byte, len(b)) + copy(msg, b) + c.mu.Lock() + defer c.mu.Unlock() + if c.closed { + return 0, errors.New("closed") + } + c.queue = append(c.queue, msg) + c.cond.Signal() + return len(b), nil +} + +// ReadFromUDP just hangs until the pipe is closed. +func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { + <-c.closing + return 0, nil, io.EOF +} + +func (c *dgramPipe) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + if !c.closed { + close(c.closing) + c.closed = true + } + return nil +} + +func (c *dgramPipe) LocalAddr() net.Addr { + return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)} +} + +func (c *dgramPipe) waitPacketOut() []byte { + c.mu.Lock() + defer c.mu.Unlock() + for len(c.queue) == 0 { + c.cond.Wait() + } + p := c.queue[0] + copy(c.queue, c.queue[1:]) + c.queue = c.queue[:len(c.queue)-1] + return p +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go new file mode 100644 index 0000000..44be8a7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database.go @@ -0,0 +1,413 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the node database, storing previously seen nodes and any collected +// metadata about them for QoS purposes. + +package discv5 + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +var ( + nodeDBNilNodeID = NodeID{} // Special node ID to use as a nil element. + nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. + nodeDBCleanupCycle = time.Hour // Time period for running the expiration task. +) + +// nodeDB stores all nodes we know about. +type nodeDB struct { + lvl *leveldb.DB // Interface to the database itself + self NodeID // Own node id to prevent adding it into the database + runner sync.Once // Ensures we can start at most one expirer + quit chan struct{} // Channel to signal the expiring thread to stop +} + +// Schema layout for the node database +var ( + nodeDBVersionKey = []byte("version") // Version of the database to flush if changes + nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with + + nodeDBDiscoverRoot = ":discover" + nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" + nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" + nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" + nodeDBDiscoverLocalEndpoint = nodeDBDiscoverRoot + ":localendpoint" + nodeDBTopicRegTickets = ":tickets" +) + +// newNodeDB creates a new node database for storing and retrieving infos about +// known peers in the network. If no path is given, an in-memory, temporary +// database is constructed. +func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + if path == "" { + return newMemoryNodeDB(self) + } + return newPersistentNodeDB(path, version, self) +} + +// newMemoryNodeDB creates a new in-memory node database without a persistent +// backend. +func newMemoryNodeDB(self NodeID) (*nodeDB, error) { + db, err := leveldb.Open(storage.NewMemStorage(), nil) + if err != nil { + return nil, err + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// newPersistentNodeDB creates/opens a leveldb backed persistent node database, +// also flushing its contents in case of a version mismatch. +func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { + opts := &opt.Options{OpenFilesCacheCapacity: 5} + db, err := leveldb.OpenFile(path, opts) + if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { + db, err = leveldb.RecoverFile(path, nil) + } + if err != nil { + return nil, err + } + // The nodes contained in the cache correspond to a certain protocol version. + // Flush all nodes if the version doesn't match. + currentVer := make([]byte, binary.MaxVarintLen64) + currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] + + blob, err := db.Get(nodeDBVersionKey, nil) + switch err { + case leveldb.ErrNotFound: + // Version not found (i.e. empty cache), insert it + if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { + db.Close() + return nil, err + } + + case nil: + // Version present, flush if different + if !bytes.Equal(blob, currentVer) { + db.Close() + if err = os.RemoveAll(path); err != nil { + return nil, err + } + return newPersistentNodeDB(path, version, self) + } + } + return &nodeDB{ + lvl: db, + self: self, + quit: make(chan struct{}), + }, nil +} + +// makeKey generates the leveldb key-blob from a node id and its particular +// field of interest. +func makeKey(id NodeID, field string) []byte { + if bytes.Equal(id[:], nodeDBNilNodeID[:]) { + return []byte(field) + } + return append(nodeDBItemPrefix, append(id[:], field...)...) +} + +// splitKey tries to split a database key into a node id and a field part. +func splitKey(key []byte) (id NodeID, field string) { + // If the key is not of a node, return it plainly + if !bytes.HasPrefix(key, nodeDBItemPrefix) { + return NodeID{}, string(key) + } + // Otherwise split the id and field + item := key[len(nodeDBItemPrefix):] + copy(id[:], item[:len(id)]) + field = string(item[len(id):]) + + return id, field +} + +// fetchInt64 retrieves an integer instance associated with a particular +// database key. +func (db *nodeDB) fetchInt64(key []byte) int64 { + blob, err := db.lvl.Get(key, nil) + if err != nil { + return 0 + } + val, read := binary.Varint(blob) + if read <= 0 { + return 0 + } + return val +} + +// storeInt64 update a specific database entry to the current time instance as a +// unix timestamp. +func (db *nodeDB) storeInt64(key []byte, n int64) error { + blob := make([]byte, binary.MaxVarintLen64) + blob = blob[:binary.PutVarint(blob, n)] + return db.lvl.Put(key, blob, nil) +} + +func (db *nodeDB) storeRLP(key []byte, val interface{}) error { + blob, err := rlp.EncodeToBytes(val) + if err != nil { + return err + } + return db.lvl.Put(key, blob, nil) +} + +func (db *nodeDB) fetchRLP(key []byte, val interface{}) error { + blob, err := db.lvl.Get(key, nil) + if err != nil { + return err + } + err = rlp.DecodeBytes(blob, val) + if err != nil { + glog.V(logger.Warn).Infof("key %x (%T) %v", key, val, err) + } + return err +} + +// node retrieves a node with a given id from the database. +func (db *nodeDB) node(id NodeID) *Node { + var node Node + if err := db.fetchRLP(makeKey(id, nodeDBDiscoverRoot), &node); err != nil { + return nil + } + node.sha = crypto.Keccak256Hash(node.ID[:]) + return &node +} + +// updateNode inserts - potentially overwriting - a node into the peer database. +func (db *nodeDB) updateNode(node *Node) error { + return db.storeRLP(makeKey(node.ID, nodeDBDiscoverRoot), node) +} + +// deleteNode deletes all information/keys associated with a node. +func (db *nodeDB) deleteNode(id NodeID) error { + deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) + for deleter.Next() { + if err := db.lvl.Delete(deleter.Key(), nil); err != nil { + return err + } + } + return nil +} + +// ensureExpirer is a small helper method ensuring that the data expiration +// mechanism is running. If the expiration goroutine is already running, this +// method simply returns. +// +// The goal is to start the data evacuation only after the network successfully +// bootstrapped itself (to prevent dumping potentially useful seed nodes). Since +// it would require significant overhead to exactly trace the first successful +// convergence, it's simpler to "ensure" the correct state when an appropriate +// condition occurs (i.e. a successful bonding), and discard further events. +func (db *nodeDB) ensureExpirer() { + db.runner.Do(func() { go db.expirer() }) +} + +// expirer should be started in a go routine, and is responsible for looping ad +// infinitum and dropping stale data from the database. +func (db *nodeDB) expirer() { + tick := time.Tick(nodeDBCleanupCycle) + for { + select { + case <-tick: + if err := db.expireNodes(); err != nil { + glog.V(logger.Error).Infof("Failed to expire nodedb items: %v", err) + } + + case <-db.quit: + return + } + } +} + +// expireNodes iterates over the database and deletes all nodes that have not +// been seen (i.e. received a pong from) for some allotted time. +func (db *nodeDB) expireNodes() error { + threshold := time.Now().Add(-nodeDBNodeExpiration) + + // Find discovered nodes that are older than the allowance + it := db.lvl.NewIterator(nil, nil) + defer it.Release() + + for it.Next() { + // Skip the item if not a discovery node + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + // Skip the node if not expired yet (and not self) + if !bytes.Equal(id[:], db.self[:]) { + if seen := db.lastPong(id); seen.After(threshold) { + continue + } + } + // Otherwise delete all associated information + db.deleteNode(id) + } + return nil +} + +// lastPing retrieves the time of the last ping packet send to a remote node, +// requesting binding. +func (db *nodeDB) lastPing(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) +} + +// updateLastPing updates the last time we tried contacting a remote node. +func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) +} + +// lastPong retrieves the time of the last successful contact from remote node. +func (db *nodeDB) lastPong(id NodeID) time.Time { + return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) +} + +// updateLastPong updates the last time a remote node successfully contacted. +func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) +} + +// findFails retrieves the number of findnode failures since bonding. +func (db *nodeDB) findFails(id NodeID) int { + return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) +} + +// updateFindFails updates the number of findnode failures since bonding. +func (db *nodeDB) updateFindFails(id NodeID, fails int) error { + return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) +} + +// localEndpoint returns the last local endpoint communicated to the +// given remote node. +func (db *nodeDB) localEndpoint(id NodeID) *rpcEndpoint { + var ep rpcEndpoint + if err := db.fetchRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep); err != nil { + return nil + } + return &ep +} + +func (db *nodeDB) updateLocalEndpoint(id NodeID, ep rpcEndpoint) error { + return db.storeRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep) +} + +// querySeeds retrieves random nodes to be used as potential seed nodes +// for bootstrapping. +func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { + var ( + now = time.Now() + nodes = make([]*Node, 0, n) + it = db.lvl.NewIterator(nil, nil) + id NodeID + ) + defer it.Release() + +seek: + for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { + // Seek to a random entry. The first byte is incremented by a + // random amount each time in order to increase the likelihood + // of hitting all existing nodes in very small databases. + ctr := id[0] + rand.Read(id[:]) + id[0] = ctr + id[0]%16 + it.Seek(makeKey(id, nodeDBDiscoverRoot)) + + n := nextNode(it) + if n == nil { + id[0] = 0 + continue seek // iterator exhausted + } + if n.ID == db.self { + continue seek + } + if now.Sub(db.lastPong(n.ID)) > maxAge { + continue seek + } + for i := range nodes { + if nodes[i].ID == n.ID { + continue seek // duplicate + } + } + nodes = append(nodes, n) + } + return nodes +} + +func (db *nodeDB) fetchTopicRegTickets(id NodeID) (issued, used uint32) { + key := makeKey(id, nodeDBTopicRegTickets) + blob, _ := db.lvl.Get(key, nil) + if len(blob) != 8 { + return 0, 0 + } + issued = binary.BigEndian.Uint32(blob[0:4]) + used = binary.BigEndian.Uint32(blob[4:8]) + return +} + +func (db *nodeDB) updateTopicRegTickets(id NodeID, issued, used uint32) error { + key := makeKey(id, nodeDBTopicRegTickets) + blob := make([]byte, 8) + binary.BigEndian.PutUint32(blob[0:4], issued) + binary.BigEndian.PutUint32(blob[4:8], used) + return db.lvl.Put(key, blob, nil) +} + +// reads the next node record from the iterator, skipping over other +// database entries. +func nextNode(it iterator.Iterator) *Node { + for end := false; !end; end = !it.Next() { + id, field := splitKey(it.Key()) + if field != nodeDBDiscoverRoot { + continue + } + var n Node + if err := rlp.DecodeBytes(it.Value(), &n); err != nil { + if glog.V(logger.Warn) { + glog.Errorf("invalid node %x: %v", id, err) + } + continue + } + return &n + } + return nil +} + +// close flushes and closes the database files. +func (db *nodeDB) close() { + close(db.quit) + db.lvl.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database_test.go new file mode 100644 index 0000000..a2ccb64 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/database_test.go @@ -0,0 +1,380 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "io/ioutil" + "net" + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +var nodeDBKeyTests = []struct { + id NodeID + field string + key []byte +}{ + { + id: NodeID{}, + field: "version", + key: []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field + }, + { + id: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + field: ":discover", + key: []byte{0x6e, 0x3a, // prefix + 0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id + 0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, // + 0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, // + 0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, // + 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // + 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, // + 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, // + 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, // + 0x3a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, // field + }, + }, +} + +func TestNodeDBKeys(t *testing.T) { + for i, tt := range nodeDBKeyTests { + if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) { + t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key) + } + id, field := splitKey(tt.key) + if !bytes.Equal(id[:], tt.id[:]) { + t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id) + } + if field != tt.field { + t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field) + } + } +} + +var nodeDBInt64Tests = []struct { + key []byte + value int64 +}{ + {key: []byte{0x01}, value: 1}, + {key: []byte{0x02}, value: 2}, + {key: []byte{0x03}, value: 3}, +} + +func TestNodeDBInt64(t *testing.T) { + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + tests := nodeDBInt64Tests + for i := 0; i < len(tests); i++ { + // Insert the next value + if err := db.storeInt64(tests[i].key, tests[i].value); err != nil { + t.Errorf("test %d: failed to store value: %v", i, err) + } + // Check all existing and non existing values + for j := 0; j < len(tests); j++ { + num := db.fetchInt64(tests[j].key) + switch { + case j <= i && num != tests[j].value: + t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value) + case j > i && num != 0: + t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0) + } + } + } +} + +func TestNodeDBFetchStore(t *testing.T) { + node := NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{192, 168, 0, 1}, + 30303, + 30303, + ) + inst := time.Now() + num := 314 + + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + // Check fetch/store operations on a node ping object + if stored := db.lastPing(node.ID); stored.Unix() != 0 { + t.Errorf("ping: non-existing object: %v", stored) + } + if err := db.updateLastPing(node.ID, inst); err != nil { + t.Errorf("ping: failed to update: %v", err) + } + if stored := db.lastPing(node.ID); stored.Unix() != inst.Unix() { + t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) + } + // Check fetch/store operations on a node pong object + if stored := db.lastPong(node.ID); stored.Unix() != 0 { + t.Errorf("pong: non-existing object: %v", stored) + } + if err := db.updateLastPong(node.ID, inst); err != nil { + t.Errorf("pong: failed to update: %v", err) + } + if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() { + t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) + } + // Check fetch/store operations on a node findnode-failure object + if stored := db.findFails(node.ID); stored != 0 { + t.Errorf("find-node fails: non-existing object: %v", stored) + } + if err := db.updateFindFails(node.ID, num); err != nil { + t.Errorf("find-node fails: failed to update: %v", err) + } + if stored := db.findFails(node.ID); stored != num { + t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) + } + // Check fetch/store operations on an actual node object + if stored := db.node(node.ID); stored != nil { + t.Errorf("node: non-existing object: %v", stored) + } + if err := db.updateNode(node); err != nil { + t.Errorf("node: failed to update: %v", err) + } + if stored := db.node(node.ID); stored == nil { + t.Errorf("node: not found") + } else if !reflect.DeepEqual(stored, node) { + t.Errorf("node: data mismatch: have %v, want %v", stored, node) + } +} + +var nodeDBSeedQueryNodes = []struct { + node *Node + pong time.Time +}{ + // This one should not be in the result set because its last + // pong time is too far in the past. + { + node: NewNode( + MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-3 * time.Hour), + }, + // This one shouldn't be in in the result set because its + // nodeID is the local node's ID. + { + node: NewNode( + MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-4 * time.Second), + }, + + // These should be in the result set. + { + node: NewNode( + MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 1}, + 30303, + 30303, + ), + pong: time.Now().Add(-2 * time.Second), + }, + { + node: NewNode( + MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 2}, + 30303, + 30303, + ), + pong: time.Now().Add(-3 * time.Second), + }, + { + node: NewNode( + MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 3}, + 30303, + 30303, + ), + pong: time.Now().Add(-1 * time.Second), + }, +} + +func TestNodeDBSeedQuery(t *testing.T) { + db, _ := newNodeDB("", Version, nodeDBSeedQueryNodes[1].node.ID) + defer db.close() + + // Insert a batch of nodes for querying + for i, seed := range nodeDBSeedQueryNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to insert lastPong: %v", i, err) + } + } + + // Retrieve the entire batch and check for duplicates + seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour) + have := make(map[NodeID]struct{}) + for _, seed := range seeds { + have[seed.ID] = struct{}{} + } + want := make(map[NodeID]struct{}) + for _, seed := range nodeDBSeedQueryNodes[2:] { + want[seed.node.ID] = struct{}{} + } + if len(seeds) != len(want) { + t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want)) + } + for id := range have { + if _, ok := want[id]; !ok { + t.Errorf("extra seed: %v", id) + } + } + for id := range want { + if _, ok := have[id]; !ok { + t.Errorf("missing seed: %v", id) + } + } +} + +func TestNodeDBPersistency(t *testing.T) { + root, err := ioutil.TempDir("", "nodedb-") + if err != nil { + t.Fatalf("failed to create temporary data folder: %v", err) + } + defer os.RemoveAll(root) + + var ( + testKey = []byte("somekey") + testInt = int64(314) + ) + + // Create a persistent database and store some values + db, err := newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) + if err != nil { + t.Fatalf("failed to create persistent database: %v", err) + } + if err := db.storeInt64(testKey, testInt); err != nil { + t.Fatalf("failed to store value: %v.", err) + } + db.close() + + // Reopen the database and check the value + db, err = newNodeDB(filepath.Join(root, "database"), Version, NodeID{}) + if err != nil { + t.Fatalf("failed to open persistent database: %v", err) + } + if val := db.fetchInt64(testKey); val != testInt { + t.Fatalf("value mismatch: have %v, want %v", val, testInt) + } + db.close() + + // Change the database version and check flush + db, err = newNodeDB(filepath.Join(root, "database"), Version+1, NodeID{}) + if err != nil { + t.Fatalf("failed to open persistent database: %v", err) + } + if val := db.fetchInt64(testKey); val != 0 { + t.Fatalf("value mismatch: have %v, want %v", val, 0) + } + db.close() +} + +var nodeDBExpirationNodes = []struct { + node *Node + pong time.Time + exp bool +}{ + { + node: NewNode( + MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 1}, + 30303, + 30303, + ), + pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute), + exp: false, + }, { + node: NewNode( + MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{127, 0, 0, 2}, + 30303, + 30303, + ), + pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute), + exp: true, + }, +} + +func TestNodeDBExpiration(t *testing.T) { + db, _ := newNodeDB("", Version, NodeID{}) + defer db.close() + + // Add all the test nodes and set their last pong time + for i, seed := range nodeDBExpirationNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to update pong: %v", i, err) + } + } + // Expire some of them, and check the rest + if err := db.expireNodes(); err != nil { + t.Fatalf("failed to expire nodes: %v", err) + } + for i, seed := range nodeDBExpirationNodes { + node := db.node(seed.node.ID) + if (node == nil && !seed.exp) || (node != nil && seed.exp) { + t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp) + } + } +} + +func TestNodeDBSelfExpiration(t *testing.T) { + // Find a node in the tests that shouldn't expire, and assign it as self + var self NodeID + for _, node := range nodeDBExpirationNodes { + if !node.exp { + self = node.node.ID + break + } + } + db, _ := newNodeDB("", Version, self) + defer db.close() + + // Add all the test nodes and set their last pong time + for i, seed := range nodeDBExpirationNodes { + if err := db.updateNode(seed.node); err != nil { + t.Fatalf("node %d: failed to insert: %v", i, err) + } + if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil { + t.Fatalf("node %d: failed to update pong: %v", i, err) + } + } + // Expire the nodes and make sure self has been evacuated too + if err := db.expireNodes(); err != nil { + t.Fatalf("failed to expire nodes: %v", err) + } + node := db.node(self) + if node != nil { + t.Errorf("self not evacuated") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go new file mode 100644 index 0000000..74d4858 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net.go @@ -0,0 +1,1274 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + errInvalidEvent = errors.New("invalid in current state") + errNoQuery = errors.New("no pending query") + errWrongAddress = errors.New("unknown sender address") +) + +const ( + autoRefreshInterval = 1 * time.Hour + bucketRefreshInterval = 1 * time.Minute + seedCount = 30 + seedMaxAge = 5 * 24 * time.Hour + lowPort = 1024 +) + +const testTopic = "foo" + +const ( + printDebugLogs = false + printTestImgLogs = false +) + +func debugLog(s string) { + if printDebugLogs { + fmt.Println(s) + } +} + +// Network manages the table and all protocol interaction. +type Network struct { + db *nodeDB // database of known nodes + conn transport + netrestrict *netutil.Netlist + + closed chan struct{} // closed when loop is done + closeReq chan struct{} // 'request to close' + refreshReq chan []*Node // lookups ask for refresh on this channel + refreshResp chan (<-chan struct{}) // ...and get the channel to block on from this one + read chan ingressPacket // ingress packets arrive here + timeout chan timeoutEvent + queryReq chan *findnodeQuery // lookups submit findnode queries on this channel + tableOpReq chan func() + tableOpResp chan struct{} + topicRegisterReq chan topicRegisterReq + topicSearchReq chan topicSearchReq + + // State of the main loop. + tab *Table + topictab *topicTable + ticketStore *ticketStore + nursery []*Node + nodes map[NodeID]*Node // tracks active nodes with state != known + timeoutTimers map[timeoutEvent]*time.Timer + + // Revalidation queues. + // Nodes put on these queues will be pinged eventually. + slowRevalidateQueue []*Node + fastRevalidateQueue []*Node + + // Buffers for state transition. + sendBuf []*ingressPacket +} + +// transport is implemented by the UDP transport. +// it is an interface so we can test without opening lots of UDP +// sockets and without generating a private key. +type transport interface { + sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) (hash []byte) + sendNeighbours(remote *Node, nodes []*Node) + sendFindnodeHash(remote *Node, target common.Hash) + sendTopicRegister(remote *Node, topics []Topic, topicIdx int, pong []byte) + sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) + + send(remote *Node, ptype nodeEvent, p interface{}) (hash []byte) + + localAddr() *net.UDPAddr + Close() +} + +type findnodeQuery struct { + remote *Node + target common.Hash + reply chan<- []*Node + nresults int // counter for received nodes +} + +type topicRegisterReq struct { + add bool + topic Topic +} + +type topicSearchReq struct { + topic Topic + found chan<- *Node + lookup chan<- bool + delay time.Duration +} + +type topicSearchResult struct { + target lookupInfo + nodes []*Node +} + +type timeoutEvent struct { + ev nodeEvent + node *Node +} + +func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string, netrestrict *netutil.Netlist) (*Network, error) { + ourID := PubkeyID(&ourPubkey) + + var db *nodeDB + if dbPath != "" { + var err error + if db, err = newNodeDB(dbPath, Version, ourID); err != nil { + return nil, err + } + } + + tab := newTable(ourID, conn.localAddr()) + net := &Network{ + db: db, + conn: conn, + netrestrict: netrestrict, + tab: tab, + topictab: newTopicTable(db, tab.self), + ticketStore: newTicketStore(), + refreshReq: make(chan []*Node), + refreshResp: make(chan (<-chan struct{})), + closed: make(chan struct{}), + closeReq: make(chan struct{}), + read: make(chan ingressPacket, 100), + timeout: make(chan timeoutEvent), + timeoutTimers: make(map[timeoutEvent]*time.Timer), + tableOpReq: make(chan func()), + tableOpResp: make(chan struct{}), + queryReq: make(chan *findnodeQuery), + topicRegisterReq: make(chan topicRegisterReq), + topicSearchReq: make(chan topicSearchReq), + nodes: make(map[NodeID]*Node), + } + go net.loop() + return net, nil +} + +// Close terminates the network listener and flushes the node database. +func (net *Network) Close() { + net.conn.Close() + select { + case <-net.closed: + case net.closeReq <- struct{}{}: + <-net.closed + } +} + +// Self returns the local node. +// The returned node should not be modified by the caller. +func (net *Network) Self() *Node { + return net.tab.self +} + +// ReadRandomNodes fills the given slice with random nodes from the +// table. It will not write the same node more than once. The nodes in +// the slice are copies and can be modified by the caller. +func (net *Network) ReadRandomNodes(buf []*Node) (n int) { + net.reqTableOp(func() { n = net.tab.readRandomNodes(buf) }) + return n +} + +// SetFallbackNodes sets the initial points of contact. These nodes +// are used to connect to the network if the table is empty and there +// are no known nodes in the database. +func (net *Network) SetFallbackNodes(nodes []*Node) error { + nursery := make([]*Node, 0, len(nodes)) + for _, n := range nodes { + if err := n.validateComplete(); err != nil { + return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) + } + // Recompute cpy.sha because the node might not have been + // created by NewNode or ParseNode. + cpy := *n + cpy.sha = crypto.Keccak256Hash(n.ID[:]) + nursery = append(nursery, &cpy) + } + net.reqRefresh(nursery) + return nil +} + +// Resolve searches for a specific node with the given ID. +// It returns nil if the node could not be found. +func (net *Network) Resolve(targetID NodeID) *Node { + result := net.lookup(crypto.Keccak256Hash(targetID[:]), true) + for _, n := range result { + if n.ID == targetID { + return n + } + } + return nil +} + +// Lookup performs a network search for nodes close +// to the given target. It approaches the target by querying +// nodes that are closer to it on each iteration. +// The given target does not need to be an actual node +// identifier. +// +// The local node may be included in the result. +func (net *Network) Lookup(targetID NodeID) []*Node { + return net.lookup(crypto.Keccak256Hash(targetID[:]), false) +} + +func (net *Network) lookup(target common.Hash, stopOnMatch bool) []*Node { + var ( + asked = make(map[NodeID]bool) + seen = make(map[NodeID]bool) + reply = make(chan []*Node, alpha) + result = nodesByDistance{target: target} + pendingQueries = 0 + ) + // Get initial answers from the local node. + result.push(net.tab.self, bucketSize) + for { + // Ask the α closest nodes that we haven't asked yet. + for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { + n := result.entries[i] + if !asked[n.ID] { + asked[n.ID] = true + pendingQueries++ + net.reqQueryFindnode(n, target, reply) + } + } + if pendingQueries == 0 { + // We have asked all closest nodes, stop the search. + break + } + // Wait for the next reply. + select { + case nodes := <-reply: + for _, n := range nodes { + if n != nil && !seen[n.ID] { + seen[n.ID] = true + result.push(n, bucketSize) + if stopOnMatch && n.sha == target { + return result.entries + } + } + } + pendingQueries-- + case <-time.After(respTimeout): + // forget all pending requests, start new ones + pendingQueries = 0 + reply = make(chan []*Node, alpha) + } + } + return result.entries +} + +func (net *Network) RegisterTopic(topic Topic, stop <-chan struct{}) { + select { + case net.topicRegisterReq <- topicRegisterReq{true, topic}: + case <-net.closed: + return + } + select { + case <-net.closed: + case <-stop: + select { + case net.topicRegisterReq <- topicRegisterReq{false, topic}: + case <-net.closed: + } + } +} + +func (net *Network) SearchTopic(topic Topic, setPeriod <-chan time.Duration, found chan<- *Node, lookup chan<- bool) { + for { + select { + case <-net.closed: + return + case delay, ok := <-setPeriod: + select { + case net.topicSearchReq <- topicSearchReq{topic: topic, found: found, lookup: lookup, delay: delay}: + case <-net.closed: + return + } + if !ok { + return + } + } + } +} + +func (net *Network) reqRefresh(nursery []*Node) <-chan struct{} { + select { + case net.refreshReq <- nursery: + return <-net.refreshResp + case <-net.closed: + return net.closed + } +} + +func (net *Network) reqQueryFindnode(n *Node, target common.Hash, reply chan []*Node) bool { + q := &findnodeQuery{remote: n, target: target, reply: reply} + select { + case net.queryReq <- q: + return true + case <-net.closed: + return false + } +} + +func (net *Network) reqReadPacket(pkt ingressPacket) { + select { + case net.read <- pkt: + case <-net.closed: + } +} + +func (net *Network) reqTableOp(f func()) (called bool) { + select { + case net.tableOpReq <- f: + <-net.tableOpResp + return true + case <-net.closed: + return false + } +} + +// TODO: external address handling. + +type topicSearchInfo struct { + lookupChn chan<- bool + period time.Duration +} + +const maxSearchCount = 5 + +func (net *Network) loop() { + var ( + refreshTimer = time.NewTicker(autoRefreshInterval) + bucketRefreshTimer = time.NewTimer(bucketRefreshInterval) + refreshDone chan struct{} // closed when the 'refresh' lookup has ended + ) + + // Tracking the next ticket to register. + var ( + nextTicket *ticketRef + nextRegisterTimer *time.Timer + nextRegisterTime <-chan time.Time + ) + defer func() { + if nextRegisterTimer != nil { + nextRegisterTimer.Stop() + } + }() + resetNextTicket := func() { + t, timeout := net.ticketStore.nextFilteredTicket() + if t != nextTicket { + nextTicket = t + if nextRegisterTimer != nil { + nextRegisterTimer.Stop() + nextRegisterTime = nil + } + if t != nil { + nextRegisterTimer = time.NewTimer(timeout) + nextRegisterTime = nextRegisterTimer.C + } + } + } + + // Tracking registration and search lookups. + var ( + topicRegisterLookupTarget lookupInfo + topicRegisterLookupDone chan []*Node + topicRegisterLookupTick = time.NewTimer(0) + searchReqWhenRefreshDone []topicSearchReq + searchInfo = make(map[Topic]topicSearchInfo) + activeSearchCount int + ) + topicSearchLookupDone := make(chan topicSearchResult, 100) + topicSearch := make(chan Topic, 100) + <-topicRegisterLookupTick.C + + statsDump := time.NewTicker(10 * time.Second) + +loop: + for { + resetNextTicket() + + select { + case <-net.closeReq: + debugLog("<-net.closeReq") + break loop + + // Ingress packet handling. + case pkt := <-net.read: + //fmt.Println("read", pkt.ev) + debugLog("<-net.read") + n := net.internNode(&pkt) + prestate := n.state + status := "ok" + if err := net.handle(n, pkt.ev, &pkt); err != nil { + status = err.Error() + } + if glog.V(logger.Detail) { + glog.Infof("<<< (%d) %v from %x@%v: %v -> %v (%v)", + net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status) + } + // TODO: persist state if n.state goes >= known, delete if it goes <= known + + // State transition timeouts. + case timeout := <-net.timeout: + debugLog("<-net.timeout") + if net.timeoutTimers[timeout] == nil { + // Stale timer (was aborted). + continue + } + delete(net.timeoutTimers, timeout) + prestate := timeout.node.state + status := "ok" + if err := net.handle(timeout.node, timeout.ev, nil); err != nil { + status = err.Error() + } + if glog.V(logger.Detail) { + glog.Infof("--- (%d) %v for %x@%v: %v -> %v (%v)", + net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status) + } + + // Querying. + case q := <-net.queryReq: + debugLog("<-net.queryReq") + if !q.start(net) { + q.remote.deferQuery(q) + } + + // Interacting with the table. + case f := <-net.tableOpReq: + debugLog("<-net.tableOpReq") + f() + net.tableOpResp <- struct{}{} + + // Topic registration stuff. + case req := <-net.topicRegisterReq: + debugLog("<-net.topicRegisterReq") + if !req.add { + net.ticketStore.removeRegisterTopic(req.topic) + continue + } + net.ticketStore.addTopic(req.topic, true) + // If we're currently waiting idle (nothing to look up), give the ticket store a + // chance to start it sooner. This should speed up convergence of the radius + // determination for new topics. + // if topicRegisterLookupDone == nil { + if topicRegisterLookupTarget.target == (common.Hash{}) { + debugLog("topicRegisterLookupTarget == null") + if topicRegisterLookupTick.Stop() { + <-topicRegisterLookupTick.C + } + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + } + + case nodes := <-topicRegisterLookupDone: + debugLog("<-topicRegisterLookupDone") + net.ticketStore.registerLookupDone(topicRegisterLookupTarget, nodes, func(n *Node) []byte { + net.ping(n, n.addr()) + return n.pingEcho + }) + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + topicRegisterLookupDone = nil + + case <-topicRegisterLookupTick.C: + debugLog("<-topicRegisterLookupTick") + if (topicRegisterLookupTarget.target == common.Hash{}) { + target, delay := net.ticketStore.nextRegisterLookup() + topicRegisterLookupTarget = target + topicRegisterLookupTick.Reset(delay) + topicRegisterLookupDone = nil + } else { + topicRegisterLookupDone = make(chan []*Node) + target := topicRegisterLookupTarget.target + go func() { topicRegisterLookupDone <- net.lookup(target, false) }() + } + + case <-nextRegisterTime: + debugLog("<-nextRegisterTime") + net.ticketStore.ticketRegistered(*nextTicket) + //fmt.Println("sendTopicRegister", nextTicket.t.node.addr().String(), nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) + net.conn.sendTopicRegister(nextTicket.t.node, nextTicket.t.topics, nextTicket.idx, nextTicket.t.pong) + + case req := <-net.topicSearchReq: + if refreshDone == nil { + debugLog("<-net.topicSearchReq") + info, ok := searchInfo[req.topic] + if ok { + if req.delay == time.Duration(0) { + delete(searchInfo, req.topic) + net.ticketStore.removeSearchTopic(req.topic) + } else { + info.period = req.delay + searchInfo[req.topic] = info + } + continue + } + if req.delay != time.Duration(0) { + var info topicSearchInfo + info.period = req.delay + info.lookupChn = req.lookup + searchInfo[req.topic] = info + net.ticketStore.addSearchTopic(req.topic, req.found) + topicSearch <- req.topic + } + } else { + searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req) + } + + case topic := <-topicSearch: + if activeSearchCount < maxSearchCount { + activeSearchCount++ + target := net.ticketStore.nextSearchLookup(topic) + go func() { + nodes := net.lookup(target.target, false) + topicSearchLookupDone <- topicSearchResult{target: target, nodes: nodes} + }() + } + period := searchInfo[topic].period + if period != time.Duration(0) { + go func() { + time.Sleep(period) + topicSearch <- topic + }() + } + + case res := <-topicSearchLookupDone: + activeSearchCount-- + if lookupChn := searchInfo[res.target.topic].lookupChn; lookupChn != nil { + lookupChn <- net.ticketStore.radius[res.target.topic].converged + } + net.ticketStore.searchLookupDone(res.target, res.nodes, func(n *Node) []byte { + net.ping(n, n.addr()) + return n.pingEcho + }, func(n *Node, topic Topic) []byte { + if n.state == known { + return net.conn.send(n, topicQueryPacket, topicQuery{Topic: topic}) // TODO: set expiration + } else { + if n.state == unknown { + net.ping(n, n.addr()) + } + return nil + } + }) + + case <-statsDump.C: + debugLog("<-statsDump.C") + /*r, ok := net.ticketStore.radius[testTopic] + if !ok { + fmt.Printf("(%x) no radius @ %v\n", net.tab.self.ID[:8], time.Now()) + } else { + topics := len(net.ticketStore.tickets) + tickets := len(net.ticketStore.nodes) + rad := r.radius / (maxRadius/10000+1) + fmt.Printf("(%x) topics:%d radius:%d tickets:%d @ %v\n", net.tab.self.ID[:8], topics, rad, tickets, time.Now()) + }*/ + + tm := mclock.Now() + for topic, r := range net.ticketStore.radius { + if printTestImgLogs { + rad := r.radius / (maxRadius/1000000 + 1) + minrad := r.minRadius / (maxRadius/1000000 + 1) + fmt.Printf("*R %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], rad) + fmt.Printf("*MR %d %v %016x %v\n", tm/1000000, topic, net.tab.self.sha[:8], minrad) + } + } + for topic, t := range net.topictab.topics { + wp := t.wcl.nextWaitPeriod(tm) + if printTestImgLogs { + fmt.Printf("*W %d %v %016x %d\n", tm/1000000, topic, net.tab.self.sha[:8], wp/1000000) + } + } + + // Periodic / lookup-initiated bucket refresh. + case <-refreshTimer.C: + debugLog("<-refreshTimer.C") + // TODO: ideally we would start the refresh timer after + // fallback nodes have been set for the first time. + if refreshDone == nil { + refreshDone = make(chan struct{}) + net.refresh(refreshDone) + } + case <-bucketRefreshTimer.C: + target := net.tab.chooseBucketRefreshTarget() + go func() { + net.lookup(target, false) + bucketRefreshTimer.Reset(bucketRefreshInterval) + }() + case newNursery := <-net.refreshReq: + debugLog("<-net.refreshReq") + if newNursery != nil { + net.nursery = newNursery + } + if refreshDone == nil { + refreshDone = make(chan struct{}) + net.refresh(refreshDone) + } + net.refreshResp <- refreshDone + case <-refreshDone: + debugLog("<-net.refreshDone") + refreshDone = nil + list := searchReqWhenRefreshDone + searchReqWhenRefreshDone = nil + go func() { + for _, req := range list { + net.topicSearchReq <- req + } + }() + } + } + debugLog("loop stopped") + + glog.V(logger.Debug).Infof("shutting down") + if net.conn != nil { + net.conn.Close() + } + if refreshDone != nil { + // TODO: wait for pending refresh. + //<-refreshResults + } + // Cancel all pending timeouts. + for _, timer := range net.timeoutTimers { + timer.Stop() + } + if net.db != nil { + net.db.close() + } + close(net.closed) +} + +// Everything below runs on the Network.loop goroutine +// and can modify Node, Table and Network at any time without locking. + +func (net *Network) refresh(done chan<- struct{}) { + var seeds []*Node + if net.db != nil { + seeds = net.db.querySeeds(seedCount, seedMaxAge) + } + if len(seeds) == 0 { + seeds = net.nursery + } + if len(seeds) == 0 { + glog.V(logger.Detail).Info("no seed nodes found") + close(done) + return + } + for _, n := range seeds { + if glog.V(logger.Debug) { + var age string + if net.db != nil { + age = time.Since(net.db.lastPong(n.ID)).String() + } else { + age = "unknown" + } + glog.Infof("seed node (age %s): %v", age, n) + } + n = net.internNodeFromDB(n) + if n.state == unknown { + net.transition(n, verifyinit) + } + // Force-add the seed node so Lookup does something. + // It will be deleted again if verification fails. + net.tab.add(n) + } + // Start self lookup to fill up the buckets. + go func() { + net.Lookup(net.tab.self.ID) + close(done) + }() +} + +// Node Interning. + +func (net *Network) internNode(pkt *ingressPacket) *Node { + if n := net.nodes[pkt.remoteID]; n != nil { + n.IP = pkt.remoteAddr.IP + n.UDP = uint16(pkt.remoteAddr.Port) + n.TCP = uint16(pkt.remoteAddr.Port) + return n + } + n := NewNode(pkt.remoteID, pkt.remoteAddr.IP, uint16(pkt.remoteAddr.Port), uint16(pkt.remoteAddr.Port)) + n.state = unknown + net.nodes[pkt.remoteID] = n + return n +} + +func (net *Network) internNodeFromDB(dbn *Node) *Node { + if n := net.nodes[dbn.ID]; n != nil { + return n + } + n := NewNode(dbn.ID, dbn.IP, dbn.UDP, dbn.TCP) + n.state = unknown + net.nodes[n.ID] = n + return n +} + +func (net *Network) internNodeFromNeighbours(sender *net.UDPAddr, rn rpcNode) (n *Node, err error) { + if rn.ID == net.tab.self.ID { + return nil, errors.New("is self") + } + if rn.UDP <= lowPort { + return nil, errors.New("low port") + } + n = net.nodes[rn.ID] + if n == nil { + // We haven't seen this node before. + n, err = nodeFromRPC(sender, rn) + if net.netrestrict != nil && !net.netrestrict.Contains(n.IP) { + return n, errors.New("not contained in netrestrict whitelist") + } + if err == nil { + n.state = unknown + net.nodes[n.ID] = n + } + return n, err + } + if !n.IP.Equal(rn.IP) || n.UDP != rn.UDP || n.TCP != rn.TCP { + err = fmt.Errorf("metadata mismatch: got %v, want %v", rn, n) + } + return n, err +} + +// nodeNetGuts is embedded in Node and contains fields. +type nodeNetGuts struct { + // This is a cached copy of sha3(ID) which is used for node + // distance calculations. This is part of Node in order to make it + // possible to write tests that need a node at a certain distance. + // In those tests, the content of sha will not actually correspond + // with ID. + sha common.Hash + + // State machine fields. Access to these fields + // is restricted to the Network.loop goroutine. + state *nodeState + pingEcho []byte // hash of last ping sent by us + pingTopics []Topic // topic set sent by us in last ping + deferredQueries []*findnodeQuery // queries that can't be sent yet + pendingNeighbours *findnodeQuery // current query, waiting for reply + queryTimeouts int +} + +func (n *nodeNetGuts) deferQuery(q *findnodeQuery) { + n.deferredQueries = append(n.deferredQueries, q) +} + +func (n *nodeNetGuts) startNextQuery(net *Network) { + if len(n.deferredQueries) == 0 { + return + } + nextq := n.deferredQueries[0] + if nextq.start(net) { + n.deferredQueries = append(n.deferredQueries[:0], n.deferredQueries[1:]...) + } +} + +func (q *findnodeQuery) start(net *Network) bool { + // Satisfy queries against the local node directly. + if q.remote == net.tab.self { + closest := net.tab.closest(crypto.Keccak256Hash(q.target[:]), bucketSize) + q.reply <- closest.entries + return true + } + if q.remote.state.canQuery && q.remote.pendingNeighbours == nil { + net.conn.sendFindnodeHash(q.remote, q.target) + net.timedEvent(respTimeout, q.remote, neighboursTimeout) + q.remote.pendingNeighbours = q + return true + } + // If the node is not known yet, it won't accept queries. + // Initiate the transition to known. + // The request will be sent later when the node reaches known state. + if q.remote.state == unknown { + net.transition(q.remote, verifyinit) + } + return false +} + +// Node Events (the input to the state machine). + +type nodeEvent uint + +//go:generate stringer -type=nodeEvent + +const ( + invalidEvent nodeEvent = iota // zero is reserved + + // Packet type events. + // These correspond to packet types in the UDP protocol. + pingPacket + pongPacket + findnodePacket + neighborsPacket + findnodeHashPacket + topicRegisterPacket + topicQueryPacket + topicNodesPacket + + // Non-packet events. + // Event values in this category are allocated outside + // the packet type range (packet types are encoded as a single byte). + pongTimeout nodeEvent = iota + 256 + pingTimeout + neighboursTimeout +) + +// Node State Machine. + +type nodeState struct { + name string + handle func(*Network, *Node, nodeEvent, *ingressPacket) (next *nodeState, err error) + enter func(*Network, *Node) + canQuery bool +} + +func (s *nodeState) String() string { + return s.name +} + +var ( + unknown *nodeState + verifyinit *nodeState + verifywait *nodeState + remoteverifywait *nodeState + known *nodeState + contested *nodeState + unresponsive *nodeState +) + +func init() { + unknown = &nodeState{ + name: "unknown", + enter: func(net *Network, n *Node) { + net.tab.delete(n) + n.pingEcho = nil + // Abort active queries. + for _, q := range n.deferredQueries { + q.reply <- nil + } + n.deferredQueries = nil + if n.pendingNeighbours != nil { + n.pendingNeighbours.reply <- nil + n.pendingNeighbours = nil + } + n.queryTimeouts = 0 + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + net.ping(n, pkt.remoteAddr) + return verifywait, nil + default: + return unknown, errInvalidEvent + } + }, + } + + verifyinit = &nodeState{ + name: "verifyinit", + enter: func(net *Network, n *Node) { + net.ping(n, n.addr()) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return verifywait, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return remoteverifywait, err + case pongTimeout: + return unknown, nil + default: + return verifyinit, errInvalidEvent + } + }, + } + + verifywait = &nodeState{ + name: "verifywait", + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return verifywait, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + case pongTimeout: + return unknown, nil + default: + return verifywait, errInvalidEvent + } + }, + } + + remoteverifywait = &nodeState{ + name: "remoteverifywait", + enter: func(net *Network, n *Node) { + net.timedEvent(respTimeout, n, pingTimeout) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return remoteverifywait, nil + case pingTimeout: + return known, nil + default: + return remoteverifywait, errInvalidEvent + } + }, + } + + known = &nodeState{ + name: "known", + canQuery: true, + enter: func(net *Network, n *Node) { + n.queryTimeouts = 0 + n.startNextQuery(net) + // Insert into the table and start revalidation of the last node + // in the bucket if it is full. + last := net.tab.add(n) + if last != nil && last.state == known { + // TODO: do this asynchronously + net.transition(last, contested) + } + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return known, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } + + contested = &nodeState{ + name: "contested", + canQuery: true, + enter: func(net *Network, n *Node) { + net.ping(n, n.addr()) + }, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pongPacket: + // Node is still alive. + err := net.handleKnownPong(n, pkt) + return known, err + case pongTimeout: + net.tab.deleteReplace(n) + return unresponsive, nil + case pingPacket: + net.handlePing(n, pkt) + return contested, nil + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } + + unresponsive = &nodeState{ + name: "unresponsive", + canQuery: true, + handle: func(net *Network, n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case pingPacket: + net.handlePing(n, pkt) + return known, nil + case pongPacket: + err := net.handleKnownPong(n, pkt) + return known, err + default: + return net.handleQueryEvent(n, ev, pkt) + } + }, + } +} + +// handle processes packets sent by n and events related to n. +func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error { + //fmt.Println("handle", n.addr().String(), n.state, ev) + if pkt != nil { + if err := net.checkPacket(n, ev, pkt); err != nil { + //fmt.Println("check err:", err) + return err + } + // Start the background expiration goroutine after the first + // successful communication. Subsequent calls have no effect if it + // is already running. We do this here instead of somewhere else + // so that the search for seed nodes also considers older nodes + // that would otherwise be removed by the expirer. + if net.db != nil { + net.db.ensureExpirer() + } + } + if n.state == nil { + n.state = unknown //??? + } + next, err := n.state.handle(net, n, ev, pkt) + net.transition(n, next) + //fmt.Println("new state:", n.state) + return err +} + +func (net *Network) checkPacket(n *Node, ev nodeEvent, pkt *ingressPacket) error { + // Replay prevention checks. + switch ev { + case pingPacket, findnodeHashPacket, neighborsPacket: + // TODO: check date is > last date seen + // TODO: check ping version + case pongPacket: + if !bytes.Equal(pkt.data.(*pong).ReplyTok, n.pingEcho) { + // fmt.Println("pong reply token mismatch") + return fmt.Errorf("pong reply token mismatch") + } + n.pingEcho = nil + } + // Address validation. + // TODO: Ideally we would do the following: + // - reject all packets with wrong address except ping. + // - for ping with new address, transition to verifywait but keep the + // previous node (with old address) around. if the new one reaches known, + // swap it out. + return nil +} + +func (net *Network) transition(n *Node, next *nodeState) { + if n.state != next { + n.state = next + if next.enter != nil { + next.enter(net, n) + } + } + + // TODO: persist/unpersist node +} + +func (net *Network) timedEvent(d time.Duration, n *Node, ev nodeEvent) { + timeout := timeoutEvent{ev, n} + net.timeoutTimers[timeout] = time.AfterFunc(d, func() { + select { + case net.timeout <- timeout: + case <-net.closed: + } + }) +} + +func (net *Network) abortTimedEvent(n *Node, ev nodeEvent) { + timer := net.timeoutTimers[timeoutEvent{ev, n}] + if timer != nil { + timer.Stop() + delete(net.timeoutTimers, timeoutEvent{ev, n}) + } +} + +func (net *Network) ping(n *Node, addr *net.UDPAddr) { + //fmt.Println("ping", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.pingEcho != nil || n.ID == net.tab.self.ID { + //fmt.Println(" not sent") + return + } + debugLog(fmt.Sprintf("ping(node = %x)", n.ID[:8])) + n.pingTopics = net.ticketStore.regTopicSet() + n.pingEcho = net.conn.sendPing(n, addr, n.pingTopics) + net.timedEvent(respTimeout, n, pongTimeout) +} + +func (net *Network) handlePing(n *Node, pkt *ingressPacket) { + debugLog(fmt.Sprintf("handlePing(node = %x)", n.ID[:8])) + ping := pkt.data.(*ping) + n.TCP = ping.From.TCP + t := net.topictab.getTicket(n, ping.Topics) + + pong := &pong{ + To: makeEndpoint(n.addr(), n.TCP), // TODO: maybe use known TCP port from DB + ReplyTok: pkt.hash, + Expiration: uint64(time.Now().Add(expiration).Unix()), + } + ticketToPong(t, pong) + net.conn.send(n, pongPacket, pong) +} + +func (net *Network) handleKnownPong(n *Node, pkt *ingressPacket) error { + debugLog(fmt.Sprintf("handleKnownPong(node = %x)", n.ID[:8])) + net.abortTimedEvent(n, pongTimeout) + now := mclock.Now() + ticket, err := pongToTicket(now, n.pingTopics, n, pkt) + if err == nil { + // fmt.Printf("(%x) ticket: %+v\n", net.tab.self.ID[:8], pkt.data) + net.ticketStore.addTicket(now, pkt.data.(*pong).ReplyTok, ticket) + } else { + debugLog(fmt.Sprintf(" error: %v", err)) + } + + n.pingEcho = nil + n.pingTopics = nil + return err +} + +func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket) (*nodeState, error) { + switch ev { + case findnodePacket: + target := crypto.Keccak256Hash(pkt.data.(*findnode).Target[:]) + results := net.tab.closest(target, bucketSize).entries + net.conn.sendNeighbours(n, results) + return n.state, nil + case neighborsPacket: + err := net.handleNeighboursPacket(n, pkt) + return n.state, err + case neighboursTimeout: + if n.pendingNeighbours != nil { + n.pendingNeighbours.reply <- nil + n.pendingNeighbours = nil + } + n.queryTimeouts++ + if n.queryTimeouts > maxFindnodeFailures && n.state == known { + return contested, errors.New("too many timeouts") + } + return n.state, nil + + // v5 + + case findnodeHashPacket: + results := net.tab.closest(pkt.data.(*findnodeHash).Target, bucketSize).entries + net.conn.sendNeighbours(n, results) + return n.state, nil + case topicRegisterPacket: + //fmt.Println("got topicRegisterPacket") + regdata := pkt.data.(*topicRegister) + pong, err := net.checkTopicRegister(regdata) + if err != nil { + //fmt.Println(err) + return n.state, fmt.Errorf("bad waiting ticket: %v", err) + } + net.topictab.useTicket(n, pong.TicketSerial, regdata.Topics, int(regdata.Idx), pong.Expiration, pong.WaitPeriods) + return n.state, nil + case topicQueryPacket: + // TODO: handle expiration + topic := pkt.data.(*topicQuery).Topic + results := net.topictab.getEntries(topic) + if _, ok := net.ticketStore.tickets[topic]; ok { + results = append(results, net.tab.self) // we're not registering in our own table but if we're advertising, return ourselves too + } + if len(results) > 10 { + results = results[:10] + } + var hash common.Hash + copy(hash[:], pkt.hash) + net.conn.sendTopicNodes(n, hash, results) + return n.state, nil + case topicNodesPacket: + p := pkt.data.(*topicNodes) + if net.ticketStore.gotTopicNodes(n, p.Echo, p.Nodes) { + n.queryTimeouts++ + if n.queryTimeouts > maxFindnodeFailures && n.state == known { + return contested, errors.New("too many timeouts") + } + } + return n.state, nil + + default: + return n.state, errInvalidEvent + } +} + +func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) { + var pongpkt ingressPacket + if err := decodePacket(data.Pong, &pongpkt); err != nil { + return nil, err + } + if pongpkt.ev != pongPacket { + return nil, errors.New("is not pong packet") + } + if pongpkt.remoteID != net.tab.self.ID { + return nil, errors.New("not signed by us") + } + // check that we previously authorised all topics + // that the other side is trying to register. + if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash { + return nil, errors.New("topic hash mismatch") + } + if data.Idx < 0 || int(data.Idx) >= len(data.Topics) { + return nil, errors.New("topic index out of range") + } + return pongpkt.data.(*pong), nil +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} + +func (net *Network) handleNeighboursPacket(n *Node, pkt *ingressPacket) error { + if n.pendingNeighbours == nil { + return errNoQuery + } + net.abortTimedEvent(n, neighboursTimeout) + + req := pkt.data.(*neighbors) + nodes := make([]*Node, len(req.Nodes)) + for i, rn := range req.Nodes { + nn, err := net.internNodeFromNeighbours(pkt.remoteAddr, rn) + if err != nil { + glog.V(logger.Debug).Infof("invalid neighbour (%v) from %x@%v: %v", rn.IP, n.ID[:8], pkt.remoteAddr, err) + continue + } + nodes[i] = nn + // Start validation of query results immediately. + // This fills the table quickly. + // TODO: generates way too many packets, maybe do it via queue. + if nn.state == unknown { + net.transition(nn, verifyinit) + } + } + // TODO: don't ignore second packet + n.pendingNeighbours.reply <- nodes + n.pendingNeighbours = nil + // Now that this query is done, start the next one. + n.startNextQuery(net) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net_test.go new file mode 100644 index 0000000..bd234f5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/net_test.go @@ -0,0 +1,374 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "fmt" + "net" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func TestNetwork_Lookup(t *testing.T) { + key, _ := crypto.GenerateKey() + network, err := newNetwork(lookupTestnet, key.PublicKey, nil, "", nil) + if err != nil { + t.Fatal(err) + } + lookupTestnet.net = network + defer network.Close() + + // lookup on empty table returns no nodes + // if results := network.Lookup(lookupTestnet.target, false); len(results) > 0 { + // t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) + // } + // seed table with initial node (otherwise lookup will terminate immediately) + seeds := []*Node{NewNode(lookupTestnet.dists[256][0], net.IP{10, 0, 2, 99}, lowPort+256, 999)} + if err := network.SetFallbackNodes(seeds); err != nil { + t.Fatal(err) + } + time.Sleep(3 * time.Second) + + results := network.Lookup(lookupTestnet.target) + t.Logf("results:") + for _, e := range results { + t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:]) + } + if len(results) != bucketSize { + t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) + } + if hasDuplicates(results) { + t.Errorf("result set contains duplicate entries") + } + if !sortedByDistanceTo(lookupTestnet.targetSha, results) { + t.Errorf("result set not sorted by distance to target") + } + // TODO: check result nodes are actually closest +} + +// This is the test network for the Lookup test. +// The nodes were obtained by running testnet.mine with a random NodeID as target. +var lookupTestnet = &preminedTestnet{ + target: MustHexID("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"), + targetSha: common.Hash{0x5c, 0x94, 0x4e, 0xe5, 0x1c, 0x5a, 0xe9, 0xf7, 0x2a, 0x95, 0xec, 0xcb, 0x8a, 0xed, 0x3, 0x74, 0xee, 0xcb, 0x51, 0x19, 0xd7, 0x20, 0xcb, 0xea, 0x68, 0x13, 0xe8, 0xe0, 0xd6, 0xad, 0x92, 0x61}, + dists: [257][]NodeID{ + 240: { + MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"), + MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"), + }, + 244: { + MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"), + }, + 246: { + MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"), + MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"), + MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"), + }, + 247: { + MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), + MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), + MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), + MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), + MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"), + MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"), + MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"), + MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"), + MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"), + MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"), + }, + 248: { + MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"), + MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"), + MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"), + MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"), + MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"), + MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"), + MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"), + MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"), + MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"), + MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"), + MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"), + MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"), + MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"), + MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"), + MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"), + MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"), + }, + 249: { + MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"), + MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"), + MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"), + MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"), + MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"), + MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"), + MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"), + MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"), + MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"), + MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"), + MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"), + MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"), + MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"), + MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"), + MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"), + MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"), + }, + 250: { + MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"), + MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"), + MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"), + MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"), + MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"), + MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"), + MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"), + MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"), + MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"), + MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"), + MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"), + MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"), + MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"), + MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"), + MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"), + MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"), + }, + 251: { + MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"), + MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"), + MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"), + MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"), + MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"), + MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"), + MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"), + MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"), + MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"), + MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"), + MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"), + MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"), + MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"), + MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"), + MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"), + MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"), + }, + 252: { + MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"), + MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"), + MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"), + MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"), + MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"), + MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"), + MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"), + MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"), + MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"), + MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"), + MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"), + MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"), + MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"), + MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"), + MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"), + MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"), + }, + 253: { + MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"), + MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"), + MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"), + MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"), + MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"), + MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"), + MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"), + MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"), + MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"), + MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"), + MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"), + MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"), + MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"), + MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"), + MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"), + MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"), + }, + 254: { + MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"), + MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"), + MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"), + MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"), + MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"), + MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"), + MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"), + MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"), + MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"), + MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"), + MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"), + MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"), + MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"), + MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"), + MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"), + MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"), + }, + 255: { + MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"), + MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"), + MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"), + MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"), + MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"), + MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"), + MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"), + MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"), + MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"), + MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"), + MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"), + MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"), + MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"), + MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"), + MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"), + MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"), + }, + 256: { + MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"), + MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"), + MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"), + MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"), + MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"), + MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"), + MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"), + MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"), + MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"), + MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"), + MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"), + MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"), + MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"), + MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"), + MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"), + MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"), + }, + }, +} + +type preminedTestnet struct { + target NodeID + targetSha common.Hash // sha3(target) + dists [hashBits + 1][]NodeID + net *Network +} + +func (tn *preminedTestnet) sendFindnode(to *Node, target NodeID) { + panic("sendFindnode called") +} + +func (tn *preminedTestnet) sendFindnodeHash(to *Node, target common.Hash) { + // current log distance is encoded in port number + // fmt.Println("findnode query at dist", toaddr.Port) + if to.UDP <= lowPort { + panic("query to node at or below distance 0") + } + next := to.UDP - 1 + var result []rpcNode + for i, id := range tn.dists[to.UDP-lowPort] { + result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort))) + } + injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result}) +} + +func (tn *preminedTestnet) sendPing(to *Node, addr *net.UDPAddr, topics []Topic) []byte { + injectResponse(tn.net, to, pongPacket, &pong{ReplyTok: []byte{1}}) + return []byte{1} +} + +func (tn *preminedTestnet) send(to *Node, ptype nodeEvent, data interface{}) (hash []byte) { + switch ptype { + case pingPacket: + injectResponse(tn.net, to, pongPacket, &pong{ReplyTok: []byte{1}}) + case pongPacket: + // ignored + case findnodeHashPacket: + // current log distance is encoded in port number + // fmt.Println("findnode query at dist", toaddr.Port-lowPort) + if to.UDP <= lowPort { + panic("query to node at or below distance 0") + } + next := to.UDP - 1 + var result []rpcNode + for i, id := range tn.dists[to.UDP-lowPort] { + result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort))) + } + injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result}) + default: + panic("send(" + ptype.String() + ")") + } + return []byte{2} +} + +func (tn *preminedTestnet) sendNeighbours(to *Node, nodes []*Node) { + panic("sendNeighbours called") +} + +func (tn *preminedTestnet) sendTopicQuery(to *Node, topic Topic) { + panic("sendTopicQuery called") +} + +func (tn *preminedTestnet) sendTopicNodes(to *Node, queryHash common.Hash, nodes []*Node) { + panic("sendTopicNodes called") +} + +func (tn *preminedTestnet) sendTopicRegister(to *Node, topics []Topic, idx int, pong []byte) { + panic("sendTopicRegister called") +} + +func (*preminedTestnet) Close() {} + +func (*preminedTestnet) localAddr() *net.UDPAddr { + return &net.UDPAddr{IP: net.ParseIP("10.0.1.1"), Port: 40000} +} + +// mine generates a testnet struct literal with nodes at +// various distances to the given target. +func (n *preminedTestnet) mine(target NodeID) { + n.target = target + n.targetSha = crypto.Keccak256Hash(n.target[:]) + found := 0 + for found < bucketSize*10 { + k := newkey() + id := PubkeyID(&k.PublicKey) + sha := crypto.Keccak256Hash(id[:]) + ld := logdist(n.targetSha, sha) + if len(n.dists[ld]) < bucketSize { + n.dists[ld] = append(n.dists[ld], id) + fmt.Println("found ID with ld", ld) + found++ + } + } + fmt.Println("&preminedTestnet{") + fmt.Printf(" target: %#v,\n", n.target) + fmt.Printf(" targetSha: %#v,\n", n.targetSha) + fmt.Printf(" dists: [%d][]NodeID{\n", len(n.dists)) + for ld, ns := range n.dists { + if len(ns) == 0 { + continue + } + fmt.Printf(" %d: []NodeID{\n", ld) + for _, n := range ns { + fmt.Printf(" MustHexID(\"%x\"),\n", n[:]) + } + fmt.Println(" },") + } + fmt.Println(" },") + fmt.Println("}") +} + +func injectResponse(net *Network, from *Node, ev nodeEvent, packet interface{}) { + go net.reqReadPacket(ingressPacket{remoteID: from.ID, remoteAddr: from.addr(), ev: ev, data: packet}) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go new file mode 100644 index 0000000..c99b4da --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node.go @@ -0,0 +1,419 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/hex" + "errors" + "fmt" + "math/big" + "math/rand" + "net" + "net/url" + "regexp" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// Node represents a host on the network. +// The public fields of Node may not be modified. +type Node struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP, TCP uint16 // port numbers + ID NodeID // the node's public key + + // Network-related fields are contained in nodeNetGuts. + // These fields are not supposed to be used off the + // Network.loop goroutine. + nodeNetGuts +} + +// NewNode creates a new node. It is mostly meant to be used for +// testing purposes. +func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + return &Node{ + IP: ip, + UDP: udpPort, + TCP: tcpPort, + ID: id, + nodeNetGuts: nodeNetGuts{sha: crypto.Keccak256Hash(id[:])}, + } +} + +func (n *Node) addr() *net.UDPAddr { + return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)} +} + +func (n *Node) setAddr(a *net.UDPAddr) { + n.IP = a.IP + if ipv4 := a.IP.To4(); ipv4 != nil { + n.IP = ipv4 + } + n.UDP = uint16(a.Port) +} + +// compares the given address against the stored values. +func (n *Node) addrEqual(a *net.UDPAddr) bool { + ip := a.IP + if ipv4 := a.IP.To4(); ipv4 != nil { + ip = ipv4 + } + return n.UDP == uint16(a.Port) && n.IP.Equal(ip) +} + +// Incomplete returns true for nodes with no IP address. +func (n *Node) Incomplete() bool { + return n.IP == nil +} + +// checks whether n is a valid complete node. +func (n *Node) validateComplete() error { + if n.Incomplete() { + return errors.New("incomplete node") + } + if n.UDP == 0 { + return errors.New("missing UDP port") + } + if n.TCP == 0 { + return errors.New("missing TCP port") + } + if n.IP.IsMulticast() || n.IP.IsUnspecified() { + return errors.New("invalid IP (multicast/unspecified)") + } + _, err := n.ID.Pubkey() // validate the key (on curve, etc.) + return err +} + +// The string representation of a Node is a URL. +// Please see ParseNode for a description of the format. +func (n *Node) String() string { + u := url.URL{Scheme: "enode"} + if n.Incomplete() { + u.Host = fmt.Sprintf("%x", n.ID[:]) + } else { + addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)} + u.User = url.User(fmt.Sprintf("%x", n.ID[:])) + u.Host = addr.String() + if n.UDP != n.TCP { + u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP)) + } + } + return u.String() +} + +var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") + +// ParseNode parses a node designator. +// +// There are two basic forms of node designators +// - incomplete nodes, which only have the public key (node ID) +// - complete nodes, which contain the public key and IP/Port information +// +// For incomplete nodes, the designator must look like one of these +// +// enode:// +// +// +// For complete nodes, the node ID is encoded in the username portion +// of the URL, separated from the host by an @ sign. The hostname can +// only be given as an IP address, DNS domain names are not allowed. +// The port in the host name section is the TCP listening port. If the +// TCP and UDP (discovery) ports differ, the UDP port is specified as +// query parameter "discport". +// +// In the following example, the node URL describes +// a node with IP address 10.3.58.6, TCP listening port 30303 +// and UDP discovery port 30301. +// +// enode://@10.3.58.6:30303?discport=30301 +func ParseNode(rawurl string) (*Node, error) { + if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil { + id, err := HexID(m[1]) + if err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + return NewNode(id, nil, 0, 0), nil + } + return parseComplete(rawurl) +} + +func parseComplete(rawurl string) (*Node, error) { + var ( + id NodeID + ip net.IP + tcpPort, udpPort uint64 + ) + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + if u.Scheme != "enode" { + return nil, errors.New("invalid URL scheme, want \"enode\"") + } + // Parse the Node ID from the user portion. + if u.User == nil { + return nil, errors.New("does not contain node ID") + } + if id, err = HexID(u.User.String()); err != nil { + return nil, fmt.Errorf("invalid node ID (%v)", err) + } + // Parse the IP address. + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + return nil, fmt.Errorf("invalid host: %v", err) + } + if ip = net.ParseIP(host); ip == nil { + return nil, errors.New("invalid IP address") + } + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + // Parse the port numbers. + if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { + return nil, errors.New("invalid port") + } + udpPort = tcpPort + qv := u.Query() + if qv.Get("discport") != "" { + udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16) + if err != nil { + return nil, errors.New("invalid discport in query") + } + } + return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil +} + +// MustParseNode parses a node URL. It panics if the URL is not valid. +func MustParseNode(rawurl string) *Node { + n, err := ParseNode(rawurl) + if err != nil { + panic("invalid node URL: " + err.Error()) + } + return n +} + +// type nodeQueue []*Node +// +// // pushNew adds n to the end if it is not present. +// func (nl *nodeList) appendNew(n *Node) { +// for _, entry := range n { +// if entry == n { +// return +// } +// } +// *nq = append(*nq, n) +// } +// +// // popRandom removes a random node. Nodes closer to +// // to the head of the beginning of the have a slightly higher probability. +// func (nl *nodeList) popRandom() *Node { +// ix := rand.Intn(len(*nq)) +// //TODO: probability as mentioned above. +// nl.removeIndex(ix) +// } +// +// func (nl *nodeList) removeIndex(i int) *Node { +// slice = *nl +// if len(*slice) <= i { +// return nil +// } +// *nl = append(slice[:i], slice[i+1:]...) +// } + +const nodeIDBits = 512 + +// NodeID is a unique identifier for each node. +// The node identifier is a marshaled elliptic curve public key. +type NodeID [nodeIDBits / 8]byte + +// NodeID prints as a long hexadecimal number. +func (n NodeID) String() string { + return fmt.Sprintf("%x", n[:]) +} + +// The Go syntax representation of a NodeID is a call to HexID. +func (n NodeID) GoString() string { + return fmt.Sprintf("discover.HexID(\"%x\")", n[:]) +} + +// HexID converts a hex string to a NodeID. +// The string may be prefixed with 0x. +func HexID(in string) (NodeID, error) { + var id NodeID + b, err := hex.DecodeString(strings.TrimPrefix(in, "0x")) + if err != nil { + return id, err + } else if len(b) != len(id) { + return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2) + } + copy(id[:], b) + return id, nil +} + +// MustHexID converts a hex string to a NodeID. +// It panics if the string is not a valid NodeID. +func MustHexID(in string) NodeID { + id, err := HexID(in) + if err != nil { + panic(err) + } + return id +} + +// PubkeyID returns a marshaled representation of the given public key. +func PubkeyID(pub *ecdsa.PublicKey) NodeID { + var id NodeID + pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y) + if len(pbytes)-1 != len(id) { + panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes))) + } + copy(id[:], pbytes[1:]) + return id +} + +// Pubkey returns the public key represented by the node ID. +// It returns an error if the ID is not a point on the curve. +func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { + p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} + half := len(id) / 2 + p.X.SetBytes(id[:half]) + p.Y.SetBytes(id[half:]) + if !p.Curve.IsOnCurve(p.X, p.Y) { + return nil, errors.New("id is invalid secp256k1 curve point") + } + return p, nil +} + +func (id NodeID) mustPubkey() ecdsa.PublicKey { + pk, err := id.Pubkey() + if err != nil { + panic(err) + } + return *pk +} + +// recoverNodeID computes the public key used to sign the +// given hash from the signature. +func recoverNodeID(hash, sig []byte) (id NodeID, err error) { + pubkey, err := crypto.Ecrecover(hash, sig) + if err != nil { + return id, err + } + if len(pubkey)-1 != len(id) { + return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8) + } + for i := range id { + id[i] = pubkey[i+1] + } + return id, nil +} + +// distcmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func distcmp(target, a, b common.Hash) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// table of leading zero counts for bytes [0..255] +var lzcount = [256]int{ + 8, 7, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} + +// logdist returns the logarithmic distance between a and b, log2(a ^ b). +func logdist(a, b common.Hash) int { + lz := 0 + for i := range a { + x := a[i] ^ b[i] + if x == 0 { + lz += 8 + } else { + lz += lzcount[x] + break + } + } + return len(a)*8 - lz +} + +// hashAtDistance returns a random hash such that logdist(a, b) == n +func hashAtDistance(a common.Hash, n int) (b common.Hash) { + if n == 0 { + return a + } + // flip bit at position n, fill the rest with random bits + b = a + pos := len(a) - n/8 - 1 + bit := byte(0x01) << (byte(n%8) - 1) + if bit == 0 { + pos++ + bit = 0x80 + } + b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits + for i := pos + 1; i < len(a); i++ { + b[i] = byte(rand.Intn(255)) + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node_test.go new file mode 100644 index 0000000..ce4ad9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/node_test.go @@ -0,0 +1,305 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "fmt" + "math/big" + "math/rand" + "net" + "reflect" + "strings" + "testing" + "testing/quick" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func ExampleNewNode() { + id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") + + // Complete nodes contain UDP and TCP endpoints: + n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303) + fmt.Println("n1:", n1) + fmt.Println("n1.Incomplete() ->", n1.Incomplete()) + + // An incomplete node can be created by passing zero values + // for all parameters except id. + n2 := NewNode(id, nil, 0, 0) + fmt.Println("n2:", n2) + fmt.Println("n2.Incomplete() ->", n2.Incomplete()) + + // Output: + // n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150 + // n1.Incomplete() -> false + // n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439 + // n2.Incomplete() -> true +} + +var parseNodeTests = []struct { + rawurl string + wantError string + wantResult *Node +}{ + { + rawurl: "http://foobar", + wantError: `invalid URL scheme, want "enode"`, + }, + { + rawurl: "enode://01010101@123.124.125.126:3", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + // Complete nodes with IP address. + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", + wantError: `invalid IP address`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", + wantError: `invalid port`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", + wantError: `invalid discport in query`, + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x7f, 0x0, 0x0, 0x1}, + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("::"), + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), + 52150, + 52150, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + net.IP{0x7f, 0x0, 0x0, 0x1}, + 22334, + 52150, + ), + }, + // Incomplete nodes with no address. + { + rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, 0, 0, + ), + }, + { + rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", + wantResult: NewNode( + MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, 0, 0, + ), + }, + // Invalid URLs + { + rawurl: "01010101", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + { + rawurl: "enode://01010101", + wantError: `invalid node ID (wrong length, want 128 hex chars)`, + }, + { + // This test checks that errors from url.Parse are handled. + rawurl: "://foo", + wantError: `parse ://foo: missing protocol scheme`, + }, +} + +func TestParseNode(t *testing.T) { + for _, test := range parseNodeTests { + n, err := ParseNode(test.rawurl) + if test.wantError != "" { + if err == nil { + t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError) + continue + } else if err.Error() != test.wantError { + t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError) + continue + } + } else { + if err != nil { + t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err) + continue + } + if !reflect.DeepEqual(n, test.wantResult) { + t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult) + } + } + } +} + +func TestNodeString(t *testing.T) { + for i, test := range parseNodeTests { + if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") { + str := test.wantResult.String() + if str != test.rawurl { + t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl) + } + } + } +} + +func TestHexID(t *testing.T) { + ref := NodeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188} + id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") + id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") + + if id1 != ref { + t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:]) + } + if id2 != ref { + t.Errorf("wrong id2\ngot %v\nwant %v", id2[:], ref[:]) + } +} + +func TestNodeID_recover(t *testing.T) { + prv := newkey() + hash := make([]byte, 32) + sig, err := crypto.Sign(hash, prv) + if err != nil { + t.Fatalf("signing error: %v", err) + } + + pub := PubkeyID(&prv.PublicKey) + recpub, err := recoverNodeID(hash, sig) + if err != nil { + t.Fatalf("recovery error: %v", err) + } + if pub != recpub { + t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub) + } + + ecdsa, err := pub.Pubkey() + if err != nil { + t.Errorf("Pubkey error: %v", err) + } + if !reflect.DeepEqual(ecdsa, &prv.PublicKey) { + t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey) + } +} + +func TestNodeID_pubkeyBad(t *testing.T) { + ecdsa, err := NodeID{}.Pubkey() + if err == nil { + t.Error("expected error for zero ID") + } + if ecdsa != nil { + t.Error("expected nil result") + } +} + +func TestNodeID_distcmp(t *testing.T) { + distcmpBig := func(target, a, b common.Hash) int { + tbig := new(big.Int).SetBytes(target[:]) + abig := new(big.Int).SetBytes(a[:]) + bbig := new(big.Int).SetBytes(b[:]) + return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig)) + } + if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil { + t.Error(err) + } +} + +// the random tests is likely to miss the case where they're equal. +func TestNodeID_distcmpEqual(t *testing.T) { + base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} + if distcmp(base, x, x) != 0 { + t.Errorf("distcmp(base, x, x) != 0") + } +} + +func TestNodeID_logdist(t *testing.T) { + logdistBig := func(a, b common.Hash) int { + abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:]) + return new(big.Int).Xor(abig, bbig).BitLen() + } + if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil { + t.Error(err) + } +} + +// the random tests is likely to miss the case where they're equal. +func TestNodeID_logdistEqual(t *testing.T) { + x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + if logdist(x, x) != 0 { + t.Errorf("logdist(x, x) != 0") + } +} + +func TestNodeID_hashAtDistance(t *testing.T) { + // we don't use quick.Check here because its output isn't + // very helpful when the test fails. + cfg := quickcfg() + for i := 0; i < cfg.MaxCount; i++ { + a := gen(common.Hash{}, cfg.Rand).(common.Hash) + dist := cfg.Rand.Intn(len(common.Hash{}) * 8) + result := hashAtDistance(a, dist) + actualdist := logdist(result, a) + + if dist != actualdist { + t.Log("a: ", a) + t.Log("result:", result) + t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist) + } + } +} + +func quickcfg() *quick.Config { + return &quick.Config{ + MaxCount: 5000, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + } +} + +// TODO: The Generate method can be dropped when we require Go >= 1.5 +// because testing/quick learned to generate arrays in 1.5. + +func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value { + var id NodeID + m := rand.Intn(len(id)) + for i := len(id) - 1; i > m; i-- { + id[i] = byte(rand.Uint32()) + } + return reflect.ValueOf(id) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go new file mode 100644 index 0000000..fde9045 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/nodeevent_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type nodeEvent"; DO NOT EDIT + +package discv5 + +import "fmt" + +const ( + _nodeEvent_name_0 = "invalidEventpingPacketpongPacketfindnodePacketneighborsPacketfindnodeHashPackettopicRegisterPackettopicQueryPackettopicNodesPacket" + _nodeEvent_name_1 = "pongTimeoutpingTimeoutneighboursTimeout" +) + +var ( + _nodeEvent_index_0 = [...]uint8{0, 12, 22, 32, 46, 61, 79, 98, 114, 130} + _nodeEvent_index_1 = [...]uint8{0, 11, 22, 39} +) + +func (i nodeEvent) String() string { + switch { + case 0 <= i && i <= 8: + return _nodeEvent_name_0[_nodeEvent_index_0[i]:_nodeEvent_index_0[i+1]] + case 265 <= i && i <= 267: + i -= 265 + return _nodeEvent_name_1[_nodeEvent_index_1[i]:_nodeEvent_index_1[i+1]] + default: + return fmt.Sprintf("nodeEvent(%d)", i) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go new file mode 100644 index 0000000..81c0e63 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ntp.go @@ -0,0 +1,127 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the NTP time drift detection via the SNTP protocol: +// https://tools.ietf.org/html/rfc4330 + +package discv5 + +import ( + "fmt" + "net" + "sort" + "strings" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + ntpPool = "pool.ntp.org" // ntpPool is the NTP server to query for the current time + ntpChecks = 3 // Number of measurements to do against the NTP server +) + +// durationSlice attaches the methods of sort.Interface to []time.Duration, +// sorting in increasing order. +type durationSlice []time.Duration + +func (s durationSlice) Len() int { return len(s) } +func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] } +func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// checkClockDrift queries an NTP server for clock drifts and warns the user if +// one large enough is detected. +func checkClockDrift() { + drift, err := sntpDrift(ntpChecks) + if err != nil { + return + } + if drift < -driftThreshold || drift > driftThreshold { + warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift) + howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") + separator := strings.Repeat("-", len(warning)) + + glog.V(logger.Warn).Info(separator) + glog.V(logger.Warn).Info(warning) + glog.V(logger.Warn).Info(howtofix) + glog.V(logger.Warn).Info(separator) + } else { + glog.V(logger.Debug).Infof("Sanity NTP check reported %v drift, all ok", drift) + } +} + +// sntpDrift does a naive time resolution against an NTP server and returns the +// measured drift. This method uses the simple version of NTP. It's not precise +// but should be fine for these purposes. +// +// Note, it executes two extra measurements compared to the number of requested +// ones to be able to discard the two extremes as outliers. +func sntpDrift(measurements int) (time.Duration, error) { + // Resolve the address of the NTP server + addr, err := net.ResolveUDPAddr("udp", ntpPool+":123") + if err != nil { + return 0, err + } + // Construct the time request (empty package with only 2 fields set): + // Bits 3-5: Protocol version, 3 + // Bits 6-8: Mode of operation, client, 3 + request := make([]byte, 48) + request[0] = 3<<3 | 3 + + // Execute each of the measurements + drifts := []time.Duration{} + for i := 0; i < measurements+2; i++ { + // Dial the NTP server and send the time retrieval request + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + return 0, err + } + defer conn.Close() + + sent := time.Now() + if _, err = conn.Write(request); err != nil { + return 0, err + } + // Retrieve the reply and calculate the elapsed time + conn.SetDeadline(time.Now().Add(5 * time.Second)) + + reply := make([]byte, 48) + if _, err = conn.Read(reply); err != nil { + return 0, err + } + elapsed := time.Since(sent) + + // Reconstruct the time from the reply data + sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24 + frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24 + + nanosec := sec*1e9 + (frac*1e9)>>32 + + t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local() + + // Calculate the drift based on an assumed answer time of RRT/2 + drifts = append(drifts, sent.Sub(t)+elapsed/2) + } + // Calculate average drif (drop two extremities to avoid outliers) + sort.Sort(durationSlice(drifts)) + + drift := time.Duration(0) + for i := 1; i < len(drifts)-1; i++ { + drift += drifts[i] + } + return drift / time.Duration(measurements), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_run_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_run_test.go new file mode 100644 index 0000000..bded0cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_run_test.go @@ -0,0 +1,126 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "os/exec" + "runtime" + "strings" + "testing" +) + +func getnacl() (string, error) { + switch runtime.GOARCH { + case "amd64": + _, err := exec.LookPath("sel_ldr_x86_64") + return "amd64p32", err + case "i386": + _, err := exec.LookPath("sel_ldr_i386") + return "i386", err + default: + return "", errors.New("nacl is not supported on " + runtime.GOARCH) + } +} + +// runWithPlaygroundTime executes the caller +// in the NaCl sandbox with faketime enabled. +// +// This function must be called from a Test* function +// and the caller must skip the actual test when isHost is true. +func runWithPlaygroundTime(t *testing.T) (isHost bool) { + if runtime.GOOS == "nacl" { + return false + } + + // Get the caller. + callerPC, _, _, ok := runtime.Caller(1) + if !ok { + panic("can't get caller") + } + callerFunc := runtime.FuncForPC(callerPC) + if callerFunc == nil { + panic("can't get caller") + } + callerName := callerFunc.Name()[strings.LastIndexByte(callerFunc.Name(), '.')+1:] + if !strings.HasPrefix(callerName, "Test") { + panic("must be called from witin a Test* function") + } + testPattern := "^" + callerName + "$" + + // Unfortunately runtime.faketime (playground time mode) only works on NaCl. The NaCl + // SDK must be installed and linked into PATH for this to work. + arch, err := getnacl() + if err != nil { + t.Skip(err) + } + + // Compile and run the calling test using NaCl. + // The extra tag ensures that the TestMain function in sim_main_test.go is used. + cmd := exec.Command("go", "test", "-v", "-tags", "faketime_simulation", "-timeout", "100h", "-run", testPattern, ".") + cmd.Env = append([]string{"GOOS=nacl", "GOARCH=" + arch}, os.Environ()...) + stdout, _ := cmd.StdoutPipe() + stderr, _ := cmd.StderrPipe() + go skipPlaygroundOutputHeaders(os.Stdout, stdout) + go skipPlaygroundOutputHeaders(os.Stderr, stderr) + if err := cmd.Run(); err != nil { + t.Error(err) + } + + // Ensure that the test function doesn't run in the (non-NaCl) host process. + return true +} + +func skipPlaygroundOutputHeaders(out io.Writer, in io.Reader) { + // Additional output can be printed without the headers + // before the NaCl binary starts running (e.g. compiler error messages). + bufin := bufio.NewReader(in) + output, err := bufin.ReadBytes(0) + output = bytes.TrimSuffix(output, []byte{0}) + if len(output) > 0 { + out.Write(output) + } + if err != nil { + return + } + bufin.UnreadByte() + + // Playback header: 0 0 P B <8-byte time> <4-byte data length> + head := make([]byte, 4+8+4) + for { + if _, err := io.ReadFull(bufin, head); err != nil { + if err != io.EOF { + fmt.Fprintln(out, "read error:", err) + } + return + } + if !bytes.HasPrefix(head, []byte{0x00, 0x00, 'P', 'B'}) { + fmt.Fprintf(out, "expected playback header, got %q\n", head) + io.Copy(out, bufin) + return + } + // Copy data until next header. + size := binary.BigEndian.Uint32(head[12:]) + io.CopyN(out, bufin, int64(size)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_test.go new file mode 100644 index 0000000..3f7fe74 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_test.go @@ -0,0 +1,464 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "crypto/ecdsa" + "encoding/binary" + "fmt" + "math/rand" + "net" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" +) + +// In this test, nodes try to randomly resolve each other. +func TestSimRandomResolve(t *testing.T) { + t.Skip("boring") + if runWithPlaygroundTime(t) { + return + } + + sim := newSimulation() + bootnode := sim.launchNode(false) + + // A new node joins every 10s. + launcher := time.NewTicker(10 * time.Second) + go func() { + for range launcher.C { + net := sim.launchNode(false) + go randomResolves(t, sim, net) + if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { + panic(err) + } + fmt.Printf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16]) + } + }() + + time.Sleep(3 * time.Hour) + launcher.Stop() + sim.shutdown() + sim.printStats() +} + +func TestSimTopics(t *testing.T) { + t.Skip("NaCl test") + if runWithPlaygroundTime(t) { + return + } + + // glog.SetV(6) + // glog.SetToStderr(true) + + sim := newSimulation() + bootnode := sim.launchNode(false) + + go func() { + nets := make([]*Network, 1024) + for i := range nets { + net := sim.launchNode(false) + nets[i] = net + if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { + panic(err) + } + time.Sleep(time.Second * 5) + } + + for i, net := range nets { + if i < 256 { + stop := make(chan struct{}) + go net.RegisterTopic(testTopic, stop) + go func() { + //time.Sleep(time.Second * 36000) + time.Sleep(time.Second * 40000) + close(stop) + }() + time.Sleep(time.Millisecond * 100) + } + // time.Sleep(time.Second * 10) + //time.Sleep(time.Second) + /*if i%500 == 499 { + time.Sleep(time.Second * 9501) + } else { + time.Sleep(time.Second) + }*/ + } + }() + + // A new node joins every 10s. + /* launcher := time.NewTicker(5 * time.Second) + cnt := 0 + var printNet *Network + go func() { + for range launcher.C { + cnt++ + if cnt <= 1000 { + log := false //(cnt == 500) + net := sim.launchNode(log) + if log { + printNet = net + } + if cnt > 500 { + go net.RegisterTopic(testTopic, nil) + } + if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { + panic(err) + } + } + //fmt.Printf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16]) + } + }() + */ + time.Sleep(55000 * time.Second) + //launcher.Stop() + sim.shutdown() + //sim.printStats() + //printNet.log.printLogs() +} + +/*func testHierarchicalTopics(i int) []Topic { + digits := strconv.FormatInt(int64(256+i/4), 4) + res := make([]Topic, 5) + for i, _ := range res { + res[i] = Topic("foo" + digits[1:i+1]) + } + return res +}*/ + +func testHierarchicalTopics(i int) []Topic { + digits := strconv.FormatInt(int64(128+i/8), 2) + res := make([]Topic, 8) + for i := range res { + res[i] = Topic("foo" + digits[1:i+1]) + } + return res +} + +func TestSimTopicHierarchy(t *testing.T) { + t.Skip("NaCl test") + if runWithPlaygroundTime(t) { + return + } + + // glog.SetV(6) + // glog.SetToStderr(true) + + sim := newSimulation() + bootnode := sim.launchNode(false) + + go func() { + nets := make([]*Network, 1024) + for i := range nets { + net := sim.launchNode(false) + nets[i] = net + if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil { + panic(err) + } + time.Sleep(time.Second * 5) + } + + stop := make(chan struct{}) + for i, net := range nets { + //if i < 256 { + for _, topic := range testHierarchicalTopics(i)[:5] { + //fmt.Println("reg", topic) + go net.RegisterTopic(topic, stop) + } + time.Sleep(time.Millisecond * 100) + //} + } + time.Sleep(time.Second * 90000) + close(stop) + }() + + time.Sleep(100000 * time.Second) + sim.shutdown() +} + +func randomResolves(t *testing.T, s *simulation, net *Network) { + randtime := func() time.Duration { + return time.Duration(rand.Intn(50)+20) * time.Second + } + lookup := func(target NodeID) bool { + result := net.Resolve(target) + return result != nil && result.ID == target + } + + timer := time.NewTimer(randtime()) + for { + select { + case <-timer.C: + target := s.randomNode().Self().ID + if !lookup(target) { + t.Errorf("node %x: target %x not found", net.Self().ID[:8], target[:8]) + } + timer.Reset(randtime()) + case <-net.closed: + return + } + } +} + +type simulation struct { + mu sync.RWMutex + nodes map[NodeID]*Network + nodectr uint32 +} + +func newSimulation() *simulation { + return &simulation{nodes: make(map[NodeID]*Network)} +} + +func (s *simulation) shutdown() { + s.mu.RLock() + alive := make([]*Network, 0, len(s.nodes)) + for _, n := range s.nodes { + alive = append(alive, n) + } + defer s.mu.RUnlock() + + for _, n := range alive { + n.Close() + } +} + +func (s *simulation) printStats() { + s.mu.Lock() + defer s.mu.Unlock() + fmt.Println("node counter:", s.nodectr) + fmt.Println("alive nodes:", len(s.nodes)) + + // for _, n := range s.nodes { + // fmt.Printf("%x\n", n.tab.self.ID[:8]) + // transport := n.conn.(*simTransport) + // fmt.Println(" joined:", transport.joinTime) + // fmt.Println(" sends:", transport.hashctr) + // fmt.Println(" table size:", n.tab.count) + // } + + /*for _, n := range s.nodes { + fmt.Println() + fmt.Printf("*** Node %x\n", n.tab.self.ID[:8]) + n.log.printLogs() + }*/ + +} + +func (s *simulation) randomNode() *Network { + s.mu.Lock() + defer s.mu.Unlock() + + n := rand.Intn(len(s.nodes)) + for _, net := range s.nodes { + if n == 0 { + return net + } + n-- + } + return nil +} + +func (s *simulation) launchNode(log bool) *Network { + var ( + num = s.nodectr + key = newkey() + id = PubkeyID(&key.PublicKey) + ip = make(net.IP, 4) + ) + s.nodectr++ + binary.BigEndian.PutUint32(ip, num) + ip[0] = 10 + addr := &net.UDPAddr{IP: ip, Port: 30303} + + transport := &simTransport{joinTime: time.Now(), sender: id, senderAddr: addr, sim: s, priv: key} + net, err := newNetwork(transport, key.PublicKey, nil, "", nil) + if err != nil { + panic("cannot launch new node: " + err.Error()) + } + + s.mu.Lock() + s.nodes[id] = net + s.mu.Unlock() + + return net +} + +func (s *simulation) dropNode(id NodeID) { + s.mu.Lock() + n := s.nodes[id] + delete(s.nodes, id) + s.mu.Unlock() + + n.Close() +} + +type simTransport struct { + joinTime time.Time + sender NodeID + senderAddr *net.UDPAddr + sim *simulation + hashctr uint64 + priv *ecdsa.PrivateKey +} + +func (st *simTransport) localAddr() *net.UDPAddr { + return st.senderAddr +} + +func (st *simTransport) Close() {} + +func (st *simTransport) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { + hash = st.nextHash() + var raw []byte + if ptype == pongPacket { + var err error + raw, _, err = encodePacket(st.priv, byte(ptype), data) + if err != nil { + panic(err) + } + } + + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: hash, + ev: ptype, + data: data, + rawData: raw, + }) + return hash +} + +func (st *simTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) []byte { + hash := st.nextHash() + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: hash, + ev: pingPacket, + data: &ping{ + Version: 4, + From: rpcEndpoint{IP: st.senderAddr.IP, UDP: uint16(st.senderAddr.Port), TCP: 30303}, + To: rpcEndpoint{IP: remoteAddr.IP, UDP: uint16(remoteAddr.Port), TCP: 30303}, + Expiration: uint64(time.Now().Unix() + int64(expiration)), + Topics: topics, + }, + }) + return hash +} + +func (st *simTransport) sendPong(remote *Node, pingHash []byte) { + raddr := remote.addr() + + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: st.nextHash(), + ev: pongPacket, + data: &pong{ + To: rpcEndpoint{IP: raddr.IP, UDP: uint16(raddr.Port), TCP: 30303}, + ReplyTok: pingHash, + Expiration: uint64(time.Now().Unix() + int64(expiration)), + }, + }) +} + +func (st *simTransport) sendFindnodeHash(remote *Node, target common.Hash) { + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: st.nextHash(), + ev: findnodeHashPacket, + data: &findnodeHash{ + Target: target, + Expiration: uint64(time.Now().Unix() + int64(expiration)), + }, + }) +} + +func (st *simTransport) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { + //fmt.Println("send", topics, pong) + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: st.nextHash(), + ev: topicRegisterPacket, + data: &topicRegister{ + Topics: topics, + Idx: uint(idx), + Pong: pong, + }, + }) +} + +func (st *simTransport) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { + rnodes := make([]rpcNode, len(nodes)) + for i := range nodes { + rnodes[i] = nodeToRPC(nodes[i]) + } + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: st.nextHash(), + ev: topicNodesPacket, + data: &topicNodes{Echo: queryHash, Nodes: rnodes}, + }) +} + +func (st *simTransport) sendNeighbours(remote *Node, nodes []*Node) { + // TODO: send multiple packets + rnodes := make([]rpcNode, len(nodes)) + for i := range nodes { + rnodes[i] = nodeToRPC(nodes[i]) + } + st.sendPacket(remote.ID, ingressPacket{ + remoteID: st.sender, + remoteAddr: st.senderAddr, + hash: st.nextHash(), + ev: neighborsPacket, + data: &neighbors{ + Nodes: rnodes, + Expiration: uint64(time.Now().Unix() + int64(expiration)), + }, + }) +} + +func (st *simTransport) nextHash() []byte { + v := atomic.AddUint64(&st.hashctr, 1) + var hash common.Hash + binary.BigEndian.PutUint64(hash[:], v) + return hash[:] +} + +const packetLoss = 0 // 1/1000 + +func (st *simTransport) sendPacket(remote NodeID, p ingressPacket) { + if rand.Int31n(1000) >= packetLoss { + st.sim.mu.RLock() + recipient := st.sim.nodes[remote] + st.sim.mu.RUnlock() + + time.AfterFunc(200*time.Millisecond, func() { + recipient.reqReadPacket(p) + }) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_testmain_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_testmain_test.go new file mode 100644 index 0000000..77e751c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/sim_testmain_test.go @@ -0,0 +1,43 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build go1.4,nacl,faketime_simulation + +package discv5 + +import ( + "os" + "runtime" + "testing" + "unsafe" +) + +// Enable fake time mode in the runtime, like on the go playground. +// There is a slight chance that this won't work because some go code +// might have executed before the variable is set. + +//go:linkname faketime runtime.faketime +var faketime = 1 + +func TestMain(m *testing.M) { + // We need to use unsafe somehow in order to get access to go:linkname. + _ = unsafe.Sizeof(0) + + // Run the actual test. runWithPlaygroundTime ensures that the only test + // that runs is the one calling it. + runtime.GOMAXPROCS(8) + os.Exit(m.Run()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go new file mode 100644 index 0000000..2cf0500 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table.go @@ -0,0 +1,323 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package discv5 implements the RLPx v5 Topic Discovery Protocol. +// +// The Topic Discovery protocol provides a way to find RLPx nodes that +// can be connected to. It uses a Kademlia-like protocol to maintain a +// distributed database of the IDs and endpoints of all listening +// nodes. +package discv5 + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "net" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + alpha = 3 // Kademlia concurrency factor + bucketSize = 16 // Kademlia bucket size + hashBits = len(common.Hash{}) * 8 + nBuckets = hashBits + 1 // Number of buckets + + maxBondingPingPongs = 16 + maxFindnodeFailures = 5 +) + +type Table struct { + count int // number of nodes + buckets [nBuckets]*bucket // index of known nodes by distance + nodeAddedHook func(*Node) // for testing + self *Node // metadata of the local node +} + +// bucket contains nodes, ordered by their last activity. the entry +// that was most recently active is the first element in entries. +type bucket struct { + entries []*Node + replacements []*Node +} + +func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table { + self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)) + tab := &Table{self: self} + for i := range tab.buckets { + tab.buckets[i] = new(bucket) + } + return tab +} + +const printTable = false + +// chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia +// buckets filled with live connections and keep the network topology healthy. +// This requires selecting addresses closer to our own with a higher probability +// in order to refresh closer buckets too. +// +// This algorithm approximates the distance distribution of existing nodes in the +// table by selecting a random node from the table and selecting a target address +// with a distance less than twice of that of the selected node. +// This algorithm will be improved later to specifically target the least recently +// used buckets. +func (tab *Table) chooseBucketRefreshTarget() common.Hash { + entries := 0 + if printTable { + fmt.Println() + } + for i, b := range tab.buckets { + entries += len(b.entries) + if printTable { + for _, e := range b.entries { + fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex()) + } + } + } + + prefix := binary.BigEndian.Uint64(tab.self.sha[0:8]) + dist := ^uint64(0) + entry := int(randUint(uint32(entries + 1))) + for _, b := range tab.buckets { + if entry < len(b.entries) { + n := b.entries[entry] + dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix + break + } + entry -= len(b.entries) + } + + ddist := ^uint64(0) + if dist+dist > dist { + ddist = dist + } + targetPrefix := prefix ^ randUint64n(ddist) + + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], targetPrefix) + rand.Read(target[8:]) + return target +} + +// readRandomNodes fills the given slice with random nodes from the +// table. It will not write the same node more than once. The nodes in +// the slice are copies and can be modified by the caller. +func (tab *Table) readRandomNodes(buf []*Node) (n int) { + // TODO: tree-based buckets would help here + // Find all non-empty buckets and get a fresh slice of their entries. + var buckets [][]*Node + for _, b := range tab.buckets { + if len(b.entries) > 0 { + buckets = append(buckets, b.entries[:]) + } + } + if len(buckets) == 0 { + return 0 + } + // Shuffle the buckets. + for i := uint32(len(buckets)) - 1; i > 0; i-- { + j := randUint(i) + buckets[i], buckets[j] = buckets[j], buckets[i] + } + // Move head of each bucket into buf, removing buckets that become empty. + var i, j int + for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { + b := buckets[j] + buf[i] = &(*b[0]) + buckets[j] = b[1:] + if len(b) == 1 { + buckets = append(buckets[:j], buckets[j+1:]...) + } + if len(buckets) == 0 { + break + } + } + return i + 1 +} + +func randUint(max uint32) uint32 { + if max < 2 { + return 0 + } + var b [4]byte + rand.Read(b[:]) + return binary.BigEndian.Uint32(b[:]) % max +} + +func randUint64n(max uint64) uint64 { + if max < 2 { + return 0 + } + var b [8]byte + rand.Read(b[:]) + return binary.BigEndian.Uint64(b[:]) % max +} + +// closest returns the n nodes in the table that are closest to the +// given id. The caller must hold tab.mutex. +func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance { + // This is a very wasteful way to find the closest nodes but + // obviously correct. I believe that tree-based buckets would make + // this easier to implement efficiently. + close := &nodesByDistance{target: target} + for _, b := range tab.buckets { + for _, n := range b.entries { + close.push(n, nresults) + } + } + return close +} + +// add attempts to add the given node its corresponding bucket. If the +// bucket has space available, adding the node succeeds immediately. +// Otherwise, the node is added to the replacement cache for the bucket. +func (tab *Table) add(n *Node) (contested *Node) { + //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex()) + if n.ID == tab.self.ID { + return + } + b := tab.buckets[logdist(tab.self.sha, n.sha)] + switch { + case b.bump(n): + // n exists in b. + return nil + case len(b.entries) < bucketSize: + // b has space available. + b.addFront(n) + tab.count++ + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(n) + } + return nil + default: + // b has no space left, add to replacement cache + // and revalidate the last entry. + // TODO: drop previous node + b.replacements = append(b.replacements, n) + if len(b.replacements) > bucketSize { + copy(b.replacements, b.replacements[1:]) + b.replacements = b.replacements[:len(b.replacements)-1] + } + return b.entries[len(b.entries)-1] + } +} + +// stuff adds nodes the table to the end of their corresponding bucket +// if the bucket is not full. +func (tab *Table) stuff(nodes []*Node) { +outer: + for _, n := range nodes { + if n.ID == tab.self.ID { + continue // don't add self + } + bucket := tab.buckets[logdist(tab.self.sha, n.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == n.ID { + continue outer // already in bucket + } + } + if len(bucket.entries) < bucketSize { + bucket.entries = append(bucket.entries, n) + tab.count++ + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(n) + } + } + } +} + +// delete removes an entry from the node table (used to evacuate +// failed/non-bonded discovery peers). +func (tab *Table) delete(node *Node) { + //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex()) + bucket := tab.buckets[logdist(tab.self.sha, node.sha)] + for i := range bucket.entries { + if bucket.entries[i].ID == node.ID { + bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...) + tab.count-- + return + } + } +} + +func (tab *Table) deleteReplace(node *Node) { + b := tab.buckets[logdist(tab.self.sha, node.sha)] + i := 0 + for i < len(b.entries) { + if b.entries[i].ID == node.ID { + b.entries = append(b.entries[:i], b.entries[i+1:]...) + tab.count-- + } else { + i++ + } + } + // refill from replacement cache + // TODO: maybe use random index + if len(b.entries) < bucketSize && len(b.replacements) > 0 { + ri := len(b.replacements) - 1 + b.addFront(b.replacements[ri]) + tab.count++ + b.replacements[ri] = nil + b.replacements = b.replacements[:ri] + } +} + +func (b *bucket) addFront(n *Node) { + b.entries = append(b.entries, nil) + copy(b.entries[1:], b.entries) + b.entries[0] = n +} + +func (b *bucket) bump(n *Node) bool { + for i := range b.entries { + if b.entries[i].ID == n.ID { + // move it to the front + copy(b.entries[1:], b.entries[:i]) + b.entries[0] = n + return true + } + } + return false +} + +// nodesByDistance is a list of nodes, ordered by +// distance to target. +type nodesByDistance struct { + entries []*Node + target common.Hash +} + +// push adds the given node to the list, keeping the total size below maxElems. +func (h *nodesByDistance) push(n *Node, maxElems int) { + ix := sort.Search(len(h.entries), func(i int) bool { + return distcmp(h.target, h.entries[i].sha, n.sha) > 0 + }) + if len(h.entries) < maxElems { + h.entries = append(h.entries, n) + } + if ix == len(h.entries) { + // farther away than all nodes we already have. + // if there was room for it, the node is now the last element. + } else { + // slide existing entries down to make room + // this will overwrite the entry we just appended. + copy(h.entries[ix+1:], h.entries[ix:]) + h.entries[ix] = n + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table_test.go new file mode 100644 index 0000000..a29943d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/table_test.go @@ -0,0 +1,337 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "crypto/ecdsa" + "fmt" + "math/rand" + + "net" + "reflect" + "testing" + "testing/quick" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +type nullTransport struct{} + +func (nullTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr) []byte { return []byte{1} } +func (nullTransport) sendPong(remote *Node, pingHash []byte) {} +func (nullTransport) sendFindnode(remote *Node, target NodeID) {} +func (nullTransport) sendNeighbours(remote *Node, nodes []*Node) {} +func (nullTransport) localAddr() *net.UDPAddr { return new(net.UDPAddr) } +func (nullTransport) Close() {} + +// func TestTable_pingReplace(t *testing.T) { +// doit := func(newNodeIsResponding, lastInBucketIsResponding bool) { +// transport := newPingRecorder() +// tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}) +// defer tab.Close() +// pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99) +// +// // fill up the sender's bucket. +// last := fillBucket(tab, 253) +// +// // this call to bond should replace the last node +// // in its bucket if the node is not responding. +// transport.responding[last.ID] = lastInBucketIsResponding +// transport.responding[pingSender.ID] = newNodeIsResponding +// tab.bond(true, pingSender.ID, &net.UDPAddr{}, 0) +// +// // first ping goes to sender (bonding pingback) +// if !transport.pinged[pingSender.ID] { +// t.Error("table did not ping back sender") +// } +// if newNodeIsResponding { +// // second ping goes to oldest node in bucket +// // to see whether it is still alive. +// if !transport.pinged[last.ID] { +// t.Error("table did not ping last node in bucket") +// } +// } +// +// tab.mutex.Lock() +// defer tab.mutex.Unlock() +// if l := len(tab.buckets[253].entries); l != bucketSize { +// t.Errorf("wrong bucket size after bond: got %d, want %d", l, bucketSize) +// } +// +// if lastInBucketIsResponding || !newNodeIsResponding { +// if !contains(tab.buckets[253].entries, last.ID) { +// t.Error("last entry was removed") +// } +// if contains(tab.buckets[253].entries, pingSender.ID) { +// t.Error("new entry was added") +// } +// } else { +// if contains(tab.buckets[253].entries, last.ID) { +// t.Error("last entry was not removed") +// } +// if !contains(tab.buckets[253].entries, pingSender.ID) { +// t.Error("new entry was not added") +// } +// } +// } +// +// doit(true, true) +// doit(false, true) +// doit(true, false) +// doit(false, false) +// } + +func TestBucket_bumpNoDuplicates(t *testing.T) { + t.Parallel() + cfg := &quick.Config{ + MaxCount: 1000, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + Values: func(args []reflect.Value, rand *rand.Rand) { + // generate a random list of nodes. this will be the content of the bucket. + n := rand.Intn(bucketSize-1) + 1 + nodes := make([]*Node, n) + for i := range nodes { + nodes[i] = nodeAtDistance(common.Hash{}, 200) + } + args[0] = reflect.ValueOf(nodes) + // generate random bump positions. + bumps := make([]int, rand.Intn(100)) + for i := range bumps { + bumps[i] = rand.Intn(len(nodes)) + } + args[1] = reflect.ValueOf(bumps) + }, + } + + prop := func(nodes []*Node, bumps []int) (ok bool) { + b := &bucket{entries: make([]*Node, len(nodes))} + copy(b.entries, nodes) + for i, pos := range bumps { + b.bump(b.entries[pos]) + if hasDuplicates(b.entries) { + t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) + for _, n := range b.entries { + t.Logf(" %p", n) + } + return false + } + } + return true + } + if err := quick.Check(prop, cfg); err != nil { + t.Error(err) + } +} + +// fillBucket inserts nodes into the given bucket until +// it is full. The node's IDs dont correspond to their +// hashes. +func fillBucket(tab *Table, ld int) (last *Node) { + b := tab.buckets[ld] + for len(b.entries) < bucketSize { + b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld)) + } + return b.entries[bucketSize-1] +} + +// nodeAtDistance creates a node for which logdist(base, n.sha) == ld. +// The node's ID does not correspond to n.sha. +func nodeAtDistance(base common.Hash, ld int) (n *Node) { + n = new(Node) + n.sha = hashAtDistance(base, ld) + copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID + return n +} + +type pingRecorder struct{ responding, pinged map[NodeID]bool } + +func newPingRecorder() *pingRecorder { + return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)} +} + +func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { + panic("findnode called on pingRecorder") +} +func (t *pingRecorder) close() {} +func (t *pingRecorder) waitping(from NodeID) error { + return nil // remote always pings +} +func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error { + t.pinged[toid] = true + if t.responding[toid] { + return nil + } else { + return errTimeout + } +} + +func TestTable_closest(t *testing.T) { + t.Parallel() + + test := func(test *closeTest) bool { + // for any node table, Target and N + tab := newTable(test.Self, &net.UDPAddr{}) + tab.stuff(test.All) + + // check that doClosest(Target, N) returns nodes + result := tab.closest(test.Target, test.N).entries + if hasDuplicates(result) { + t.Errorf("result contains duplicates") + return false + } + if !sortedByDistanceTo(test.Target, result) { + t.Errorf("result is not sorted by distance to target") + return false + } + + // check that the number of results is min(N, tablen) + wantN := test.N + if tab.count < test.N { + wantN = tab.count + } + if len(result) != wantN { + t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN) + return false + } else if len(result) == 0 { + return true // no need to check distance + } + + // check that the result nodes have minimum distance to target. + for _, b := range tab.buckets { + for _, n := range b.entries { + if contains(result, n.ID) { + continue // don't run the check below for nodes in result + } + farthestResult := result[len(result)-1].sha + if distcmp(test.Target, n.sha, farthestResult) < 0 { + t.Errorf("table contains node that is closer to target but it's not in result") + t.Logf(" Target: %v", test.Target) + t.Logf(" Farthest Result: %v", farthestResult) + t.Logf(" ID: %v", n.ID) + return false + } + } + } + return true + } + if err := quick.Check(test, quickcfg()); err != nil { + t.Error(err) + } +} + +func TestTable_ReadRandomNodesGetAll(t *testing.T) { + cfg := &quick.Config{ + MaxCount: 200, + Rand: rand.New(rand.NewSource(time.Now().Unix())), + Values: func(args []reflect.Value, rand *rand.Rand) { + args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000))) + }, + } + test := func(buf []*Node) bool { + tab := newTable(NodeID{}, &net.UDPAddr{}) + for i := 0; i < len(buf); i++ { + ld := cfg.Rand.Intn(len(tab.buckets)) + tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) + } + gotN := tab.readRandomNodes(buf) + if gotN != tab.count { + t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.count) + return false + } + if hasDuplicates(buf[:gotN]) { + t.Errorf("result contains duplicates") + return false + } + return true + } + if err := quick.Check(test, cfg); err != nil { + t.Error(err) + } +} + +type closeTest struct { + Self NodeID + Target common.Hash + All []*Node + N int +} + +func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { + t := &closeTest{ + Self: gen(NodeID{}, rand).(NodeID), + Target: gen(common.Hash{}, rand).(common.Hash), + N: rand.Intn(bucketSize), + } + for _, id := range gen([]NodeID{}, rand).([]NodeID) { + t.All = append(t.All, &Node{ID: id}) + } + return reflect.ValueOf(t) +} + +func hasDuplicates(slice []*Node) bool { + seen := make(map[NodeID]bool) + for i, e := range slice { + if e == nil { + panic(fmt.Sprintf("nil *Node at %d", i)) + } + if seen[e.ID] { + return true + } + seen[e.ID] = true + } + return false +} + +func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool { + var last common.Hash + for i, e := range slice { + if i > 0 && distcmp(distbase, e.sha, last) < 0 { + return false + } + last = e.sha + } + return true +} + +func contains(ns []*Node, id NodeID) bool { + for _, n := range ns { + if n.ID == id { + return true + } + } + return false +} + +// gen wraps quick.Value so it's easier to use. +// it generates a random value of the given value's type. +func gen(typ interface{}, rand *rand.Rand) interface{} { + v, ok := quick.Value(reflect.TypeOf(typ), rand) + if !ok { + panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) + } + return v.Interface() +} + +func newkey() *ecdsa.PrivateKey { + key, err := crypto.GenerateKey() + if err != nil { + panic("couldn't generate key: " + err.Error()) + } + return key +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go new file mode 100644 index 0000000..48dd114 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/ticket.go @@ -0,0 +1,955 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "math/rand" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/crypto" +) + +const ( + ticketTimeBucketLen = time.Minute + timeWindow = 10 // * ticketTimeBucketLen + wantTicketsInWindow = 10 + collectFrequency = time.Second * 30 + registerFrequency = time.Second * 60 + maxCollectDebt = 10 + maxRegisterDebt = 5 + keepTicketConst = time.Minute * 10 + keepTicketExp = time.Minute * 5 + targetWaitTime = time.Minute * 10 + topicQueryTimeout = time.Second * 5 + topicQueryResend = time.Minute + // topic radius detection + maxRadius = 0xffffffffffffffff + radiusTC = time.Minute * 20 + radiusBucketsPerBit = 8 + minSlope = 1 + minPeakSize = 40 + maxNoAdjust = 20 + lookupWidth = 8 + minRightSum = 20 + searchForceQuery = 4 +) + +// timeBucket represents absolute monotonic time in minutes. +// It is used as the index into the per-topic ticket buckets. +type timeBucket int + +type ticket struct { + topics []Topic + regTime []mclock.AbsTime // Per-topic local absolute time when the ticket can be used. + + // The serial number that was issued by the server. + serial uint32 + // Used by registrar, tracks absolute time when the ticket was created. + issueTime mclock.AbsTime + + // Fields used only by registrants + node *Node // the registrar node that signed this ticket + refCnt int // tracks number of topics that will be registered using this ticket + pong []byte // encoded pong packet signed by the registrar +} + +// ticketRef refers to a single topic in a ticket. +type ticketRef struct { + t *ticket + idx int // index of the topic in t.topics and t.regTime +} + +func (ref ticketRef) topic() Topic { + return ref.t.topics[ref.idx] +} + +func (ref ticketRef) topicRegTime() mclock.AbsTime { + return ref.t.regTime[ref.idx] +} + +func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) { + wps := p.data.(*pong).WaitPeriods + if len(topics) != len(wps) { + return nil, fmt.Errorf("bad wait period list: got %d values, want %d", len(topics), len(wps)) + } + if rlpHash(topics) != p.data.(*pong).TopicHash { + return nil, fmt.Errorf("bad topic hash") + } + t := &ticket{ + issueTime: localTime, + node: node, + topics: topics, + pong: p.rawData, + regTime: make([]mclock.AbsTime, len(wps)), + } + // Convert wait periods to local absolute time. + for i, wp := range wps { + t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp)) + } + return t, nil +} + +func ticketToPong(t *ticket, pong *pong) { + pong.Expiration = uint64(t.issueTime / mclock.AbsTime(time.Second)) + pong.TopicHash = rlpHash(t.topics) + pong.TicketSerial = t.serial + pong.WaitPeriods = make([]uint32, len(t.regTime)) + for i, regTime := range t.regTime { + pong.WaitPeriods[i] = uint32(time.Duration(regTime-t.issueTime) / time.Second) + } +} + +type ticketStore struct { + // radius detector and target address generator + // exists for both searched and registered topics + radius map[Topic]*topicRadius + + // Contains buckets (for each absolute minute) of tickets + // that can be used in that minute. + // This is only set if the topic is being registered. + tickets map[Topic]topicTickets + regtopics []Topic + nodes map[*Node]*ticket + nodeLastReq map[*Node]reqInfo + + lastBucketFetched timeBucket + nextTicketCached *ticketRef + nextTicketReg mclock.AbsTime + + searchTopicMap map[Topic]searchTopic + nextTopicQueryCleanup mclock.AbsTime + queriesSent map[*Node]map[common.Hash]sentQuery +} + +type searchTopic struct { + foundChn chan<- *Node +} + +type sentQuery struct { + sent mclock.AbsTime + lookup lookupInfo +} + +type topicTickets struct { + buckets map[timeBucket][]ticketRef + nextLookup, nextReg mclock.AbsTime +} + +func newTicketStore() *ticketStore { + return &ticketStore{ + radius: make(map[Topic]*topicRadius), + tickets: make(map[Topic]topicTickets), + nodes: make(map[*Node]*ticket), + nodeLastReq: make(map[*Node]reqInfo), + searchTopicMap: make(map[Topic]searchTopic), + queriesSent: make(map[*Node]map[common.Hash]sentQuery), + } +} + +// addTopic starts tracking a topic. If register is true, +// the local node will register the topic and tickets will be collected. +func (s *ticketStore) addTopic(t Topic, register bool) { + debugLog(fmt.Sprintf(" addTopic(%v, %v)", t, register)) + if s.radius[t] == nil { + s.radius[t] = newTopicRadius(t) + } + if register && s.tickets[t].buckets == nil { + s.tickets[t] = topicTickets{buckets: make(map[timeBucket][]ticketRef)} + } +} + +func (s *ticketStore) addSearchTopic(t Topic, foundChn chan<- *Node) { + s.addTopic(t, false) + if s.searchTopicMap[t].foundChn == nil { + s.searchTopicMap[t] = searchTopic{foundChn: foundChn} + } +} + +func (s *ticketStore) removeSearchTopic(t Topic) { + if st := s.searchTopicMap[t]; st.foundChn != nil { + delete(s.searchTopicMap, t) + } +} + +// removeRegisterTopic deletes all tickets for the given topic. +func (s *ticketStore) removeRegisterTopic(topic Topic) { + debugLog(fmt.Sprintf(" removeRegisterTopic(%v)", topic)) + for _, list := range s.tickets[topic].buckets { + for _, ref := range list { + ref.t.refCnt-- + if ref.t.refCnt == 0 { + delete(s.nodes, ref.t.node) + delete(s.nodeLastReq, ref.t.node) + } + } + } + delete(s.tickets, topic) +} + +func (s *ticketStore) regTopicSet() []Topic { + topics := make([]Topic, 0, len(s.tickets)) + for topic := range s.tickets { + topics = append(topics, topic) + } + return topics +} + +// nextRegisterLookup returns the target of the next lookup for ticket collection. +func (s *ticketStore) nextRegisterLookup() (lookup lookupInfo, delay time.Duration) { + debugLog("nextRegisterLookup()") + firstTopic, ok := s.iterRegTopics() + for topic := firstTopic; ok; { + debugLog(fmt.Sprintf(" checking topic %v, len(s.tickets[topic]) = %d", topic, len(s.tickets[topic].buckets))) + if s.tickets[topic].buckets != nil && s.needMoreTickets(topic) { + next := s.radius[topic].nextTarget(false) + debugLog(fmt.Sprintf(" %x 1s", next.target[:8])) + return next, 100 * time.Millisecond + } + topic, ok = s.iterRegTopics() + if topic == firstTopic { + break // We have checked all topics. + } + } + debugLog(" null, 40s") + return lookupInfo{}, 40 * time.Second +} + +func (s *ticketStore) nextSearchLookup(topic Topic) lookupInfo { + tr := s.radius[topic] + target := tr.nextTarget(tr.radiusLookupCnt >= searchForceQuery) + if target.radiusLookup { + tr.radiusLookupCnt++ + } else { + tr.radiusLookupCnt = 0 + } + return target +} + +// iterRegTopics returns topics to register in arbitrary order. +// The second return value is false if there are no topics. +func (s *ticketStore) iterRegTopics() (Topic, bool) { + debugLog("iterRegTopics()") + if len(s.regtopics) == 0 { + if len(s.tickets) == 0 { + debugLog(" false") + return "", false + } + // Refill register list. + for t := range s.tickets { + s.regtopics = append(s.regtopics, t) + } + } + topic := s.regtopics[len(s.regtopics)-1] + s.regtopics = s.regtopics[:len(s.regtopics)-1] + debugLog(" " + string(topic) + " true") + return topic, true +} + +func (s *ticketStore) needMoreTickets(t Topic) bool { + return s.tickets[t].nextLookup < mclock.Now() +} + +// ticketsInWindow returns the tickets of a given topic in the registration window. +func (s *ticketStore) ticketsInWindow(t Topic) []ticketRef { + ltBucket := s.lastBucketFetched + var res []ticketRef + tickets := s.tickets[t].buckets + for g := ltBucket; g < ltBucket+timeWindow; g++ { + res = append(res, tickets[g]...) + } + debugLog(fmt.Sprintf("ticketsInWindow(%v) = %v", t, len(res))) + return res +} + +func (s *ticketStore) removeExcessTickets(t Topic) { + tickets := s.ticketsInWindow(t) + if len(tickets) <= wantTicketsInWindow { + return + } + sort.Sort(ticketRefByWaitTime(tickets)) + for _, r := range tickets[wantTicketsInWindow:] { + s.removeTicketRef(r) + } +} + +type ticketRefByWaitTime []ticketRef + +// Len is the number of elements in the collection. +func (s ticketRefByWaitTime) Len() int { + return len(s) +} + +func (r ticketRef) waitTime() mclock.AbsTime { + return r.t.regTime[r.idx] - r.t.issueTime +} + +// Less reports whether the element with +// index i should sort before the element with index j. +func (s ticketRefByWaitTime) Less(i, j int) bool { + return s[i].waitTime() < s[j].waitTime() +} + +// Swap swaps the elements with indexes i and j. +func (s ticketRefByWaitTime) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s *ticketStore) addTicketRef(r ticketRef) { + topic := r.t.topics[r.idx] + t := s.tickets[topic] + if t.buckets == nil { + return + } + bucket := timeBucket(r.t.regTime[r.idx] / mclock.AbsTime(ticketTimeBucketLen)) + t.buckets[bucket] = append(t.buckets[bucket], r) + r.t.refCnt++ + + min := mclock.Now() - mclock.AbsTime(collectFrequency)*maxCollectDebt + if t.nextLookup < min { + t.nextLookup = min + } + t.nextLookup += mclock.AbsTime(collectFrequency) + s.tickets[topic] = t + + //s.removeExcessTickets(topic) +} + +func (s *ticketStore) nextFilteredTicket() (t *ticketRef, wait time.Duration) { + now := mclock.Now() + for { + t, wait = s.nextRegisterableTicket() + if t == nil { + return + } + regTime := now + mclock.AbsTime(wait) + topic := t.t.topics[t.idx] + if regTime >= s.tickets[topic].nextReg { + return + } + s.removeTicketRef(*t) + } +} + +func (s *ticketStore) ticketRegistered(t ticketRef) { + now := mclock.Now() + + topic := t.t.topics[t.idx] + tt := s.tickets[topic] + min := now - mclock.AbsTime(registerFrequency)*maxRegisterDebt + if min > tt.nextReg { + tt.nextReg = min + } + tt.nextReg += mclock.AbsTime(registerFrequency) + s.tickets[topic] = tt + + s.removeTicketRef(t) +} + +// nextRegisterableTicket returns the next ticket that can be used +// to register. +// +// If the returned wait time <= zero the ticket can be used. For a positive +// wait time, the caller should requery the next ticket later. +// +// A ticket can be returned more than once with <= zero wait time in case +// the ticket contains multiple topics. +func (s *ticketStore) nextRegisterableTicket() (t *ticketRef, wait time.Duration) { + defer func() { + if t == nil { + debugLog(" nil") + } else { + debugLog(fmt.Sprintf(" node = %x sn = %v wait = %v", t.t.node.ID[:8], t.t.serial, wait)) + } + }() + + debugLog("nextRegisterableTicket()") + now := mclock.Now() + if s.nextTicketCached != nil { + return s.nextTicketCached, time.Duration(s.nextTicketCached.topicRegTime() - now) + } + + for bucket := s.lastBucketFetched; ; bucket++ { + var ( + empty = true // true if there are no tickets + nextTicket ticketRef // uninitialized if this bucket is empty + ) + for _, tickets := range s.tickets { + //s.removeExcessTickets(topic) + if len(tickets.buckets) != 0 { + empty = false + if list := tickets.buckets[bucket]; list != nil { + for _, ref := range list { + //debugLog(fmt.Sprintf(" nrt bucket = %d node = %x sn = %v wait = %v", bucket, ref.t.node.ID[:8], ref.t.serial, time.Duration(ref.topicRegTime()-now))) + if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() { + nextTicket = ref + } + } + } + } + } + if empty { + return nil, 0 + } + if nextTicket.t != nil { + wait = time.Duration(nextTicket.topicRegTime() - now) + s.nextTicketCached = &nextTicket + return &nextTicket, wait + } + s.lastBucketFetched = bucket + } +} + +// removeTicket removes a ticket from the ticket store +func (s *ticketStore) removeTicketRef(ref ticketRef) { + debugLog(fmt.Sprintf("removeTicketRef(node = %x sn = %v)", ref.t.node.ID[:8], ref.t.serial)) + topic := ref.topic() + tickets := s.tickets[topic].buckets + if tickets == nil { + return + } + bucket := timeBucket(ref.t.regTime[ref.idx] / mclock.AbsTime(ticketTimeBucketLen)) + list := tickets[bucket] + idx := -1 + for i, bt := range list { + if bt.t == ref.t { + idx = i + break + } + } + if idx == -1 { + panic(nil) + } + list = append(list[:idx], list[idx+1:]...) + if len(list) != 0 { + tickets[bucket] = list + } else { + delete(tickets, bucket) + } + ref.t.refCnt-- + if ref.t.refCnt == 0 { + delete(s.nodes, ref.t.node) + delete(s.nodeLastReq, ref.t.node) + } + + // Make nextRegisterableTicket return the next available ticket. + s.nextTicketCached = nil +} + +type lookupInfo struct { + target common.Hash + topic Topic + radiusLookup bool +} + +type reqInfo struct { + pingHash []byte + lookup lookupInfo + time mclock.AbsTime +} + +// returns -1 if not found +func (t *ticket) findIdx(topic Topic) int { + for i, tt := range t.topics { + if tt == topic { + return i + } + } + return -1 +} + +func (s *ticketStore) registerLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte) { + now := mclock.Now() + for i, n := range nodes { + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } else { + if s.nodes[n] == nil { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } + } + } +} + +func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte, query func(n *Node, topic Topic) []byte) { + now := mclock.Now() + for i, n := range nodes { + if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius { + if lookup.radiusLookup { + if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC { + s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now} + } + } // else { + if s.canQueryTopic(n, lookup.topic) { + hash := query(n, lookup.topic) + if hash != nil { + s.addTopicQuery(common.BytesToHash(hash), n, lookup) + } + } + //} + } + } +} + +func (s *ticketStore) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t *ticket) { + for i, topic := range t.topics { + if tt, ok := s.radius[topic]; ok { + tt.adjustWithTicket(now, targetHash, ticketRef{t, i}) + } + } +} + +func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, t *ticket) { + debugLog(fmt.Sprintf("add(node = %x sn = %v)", t.node.ID[:8], t.serial)) + + lastReq, ok := s.nodeLastReq[t.node] + if !(ok && bytes.Equal(pingHash, lastReq.pingHash)) { + return + } + s.adjustWithTicket(localTime, lastReq.lookup.target, t) + + if lastReq.lookup.radiusLookup || s.nodes[t.node] != nil { + return + } + + topic := lastReq.lookup.topic + topicIdx := t.findIdx(topic) + if topicIdx == -1 { + return + } + + bucket := timeBucket(localTime / mclock.AbsTime(ticketTimeBucketLen)) + if s.lastBucketFetched == 0 || bucket < s.lastBucketFetched { + s.lastBucketFetched = bucket + } + + if _, ok := s.tickets[topic]; ok { + wait := t.regTime[topicIdx] - localTime + rnd := rand.ExpFloat64() + if rnd > 10 { + rnd = 10 + } + if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd { + // use the ticket to register this topic + //fmt.Println("addTicket", t.node.ID[:8], t.node.addr().String(), t.serial, t.pong) + s.addTicketRef(ticketRef{t, topicIdx}) + } + } + + if t.refCnt > 0 { + s.nextTicketCached = nil + s.nodes[t.node] = t + } +} + +func (s *ticketStore) getNodeTicket(node *Node) *ticket { + if s.nodes[node] == nil { + debugLog(fmt.Sprintf("getNodeTicket(%x) sn = nil", node.ID[:8])) + } else { + debugLog(fmt.Sprintf("getNodeTicket(%x) sn = %v", node.ID[:8], s.nodes[node].serial)) + } + return s.nodes[node] +} + +func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool { + qq := s.queriesSent[node] + if qq != nil { + now := mclock.Now() + for _, sq := range qq { + if sq.lookup.topic == topic && sq.sent > now-mclock.AbsTime(topicQueryResend) { + return false + } + } + } + return true +} + +func (s *ticketStore) addTopicQuery(hash common.Hash, node *Node, lookup lookupInfo) { + now := mclock.Now() + qq := s.queriesSent[node] + if qq == nil { + qq = make(map[common.Hash]sentQuery) + s.queriesSent[node] = qq + } + qq[hash] = sentQuery{sent: now, lookup: lookup} + s.cleanupTopicQueries(now) +} + +func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) { + if s.nextTopicQueryCleanup > now { + return + } + exp := now - mclock.AbsTime(topicQueryResend) + for n, qq := range s.queriesSent { + for h, q := range qq { + if q.sent < exp { + delete(qq, h) + } + } + if len(qq) == 0 { + delete(s.queriesSent, n) + } + } + s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout) +} + +func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) { + now := mclock.Now() + //fmt.Println("got", from.addr().String(), hash, len(nodes)) + qq := s.queriesSent[from] + if qq == nil { + return true + } + q, ok := qq[hash] + if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) { + return true + } + inside := float64(0) + if len(nodes) > 0 { + inside = 1 + } + s.radius[q.lookup.topic].adjust(now, q.lookup.target, from.sha, inside) + chn := s.searchTopicMap[q.lookup.topic].foundChn + if chn == nil { + //fmt.Println("no channel") + return false + } + for _, node := range nodes { + ip := node.IP + if ip.IsUnspecified() || ip.IsLoopback() { + ip = from.IP + } + n := NewNode(node.ID, ip, node.UDP-1, node.TCP-1) // subtract one from port while discv5 is running in test mode on UDPport+1 + select { + case chn <- n: + default: + return false + } + } + return false +} + +type topicRadius struct { + topic Topic + topicHashPrefix uint64 + radius, minRadius uint64 + buckets []topicRadiusBucket + converged bool + radiusLookupCnt int +} + +type topicRadiusEvent int + +const ( + trOutside topicRadiusEvent = iota + trInside + trNoAdjust + trCount +) + +type topicRadiusBucket struct { + weights [trCount]float64 + lastTime mclock.AbsTime + value float64 + lookupSent map[common.Hash]mclock.AbsTime +} + +func (b *topicRadiusBucket) update(now mclock.AbsTime) { + if now == b.lastTime { + return + } + exp := math.Exp(-float64(now-b.lastTime) / float64(radiusTC)) + for i, w := range b.weights { + b.weights[i] = w * exp + } + b.lastTime = now + + for target, tm := range b.lookupSent { + if now-tm > mclock.AbsTime(respTimeout) { + b.weights[trNoAdjust] += 1 + delete(b.lookupSent, target) + } + } +} + +func (b *topicRadiusBucket) adjust(now mclock.AbsTime, inside float64) { + b.update(now) + if inside <= 0 { + b.weights[trOutside] += 1 + } else { + if inside >= 1 { + b.weights[trInside] += 1 + } else { + b.weights[trInside] += inside + b.weights[trOutside] += 1 - inside + } + } +} + +func newTopicRadius(t Topic) *topicRadius { + topicHash := crypto.Keccak256Hash([]byte(t)) + topicHashPrefix := binary.BigEndian.Uint64(topicHash[0:8]) + + return &topicRadius{ + topic: t, + topicHashPrefix: topicHashPrefix, + radius: maxRadius, + minRadius: maxRadius, + } +} + +func (r *topicRadius) getBucketIdx(addrHash common.Hash) int { + prefix := binary.BigEndian.Uint64(addrHash[0:8]) + var log2 float64 + if prefix != r.topicHashPrefix { + log2 = math.Log2(float64(prefix ^ r.topicHashPrefix)) + } + bucket := int((64 - log2) * radiusBucketsPerBit) + max := 64*radiusBucketsPerBit - 1 + if bucket > max { + return max + } + if bucket < 0 { + return 0 + } + return bucket +} + +func (r *topicRadius) targetForBucket(bucket int) common.Hash { + min := math.Pow(2, 64-float64(bucket+1)/radiusBucketsPerBit) + max := math.Pow(2, 64-float64(bucket)/radiusBucketsPerBit) + a := uint64(min) + b := randUint64n(uint64(max - min)) + xor := a + b + if xor < a { + xor = ^uint64(0) + } + prefix := r.topicHashPrefix ^ xor + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) + return target +} + +// package rand provides a Read function in Go 1.6 and later, but +// we can't use it yet because we still support Go 1.5. +func globalRandRead(b []byte) { + pos := 0 + val := 0 + for n := 0; n < len(b); n++ { + if pos == 0 { + val = rand.Int() + pos = 7 + } + b[n] = byte(val) + val >>= 8 + pos-- + } +} + +func (r *topicRadius) isInRadius(addrHash common.Hash) bool { + nodePrefix := binary.BigEndian.Uint64(addrHash[0:8]) + dist := nodePrefix ^ r.topicHashPrefix + return dist < r.radius +} + +func (r *topicRadius) chooseLookupBucket(a, b int) int { + if a < 0 { + a = 0 + } + if a > b { + return -1 + } + c := 0 + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + c++ + } + } + if c == 0 { + return -1 + } + rnd := randUint(uint32(c)) + for i := a; i <= b; i++ { + if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust { + if rnd == 0 { + return i + } + rnd-- + } + } + panic(nil) // should never happen +} + +func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool { + var max float64 + if a < 0 { + a = 0 + } + if b >= len(r.buckets) { + b = len(r.buckets) - 1 + if r.buckets[b].value > max { + max = r.buckets[b].value + } + } + if b >= a { + for i := a; i <= b; i++ { + if r.buckets[i].value > max { + max = r.buckets[i].value + } + } + } + return maxValue-max < minPeakSize +} + +func (r *topicRadius) recalcRadius() (radius uint64, radiusLookup int) { + maxBucket := 0 + maxValue := float64(0) + now := mclock.Now() + v := float64(0) + for i := range r.buckets { + r.buckets[i].update(now) + v += r.buckets[i].weights[trOutside] - r.buckets[i].weights[trInside] + r.buckets[i].value = v + //fmt.Printf("%v %v | ", v, r.buckets[i].weights[trNoAdjust]) + } + //fmt.Println() + slopeCross := -1 + for i, b := range r.buckets { + v := b.value + if v < float64(i)*minSlope { + slopeCross = i + break + } + if v > maxValue { + maxValue = v + maxBucket = i + 1 + } + } + + minRadBucket := len(r.buckets) + sum := float64(0) + for minRadBucket > 0 && sum < minRightSum { + minRadBucket-- + b := r.buckets[minRadBucket] + sum += b.weights[trInside] + b.weights[trOutside] + } + r.minRadius = uint64(math.Pow(2, 64-float64(minRadBucket)/radiusBucketsPerBit)) + + lookupLeft := -1 + if r.needMoreLookups(0, maxBucket-lookupWidth-1, maxValue) { + lookupLeft = r.chooseLookupBucket(maxBucket-lookupWidth, maxBucket-1) + } + lookupRight := -1 + if slopeCross != maxBucket && (minRadBucket <= maxBucket || r.needMoreLookups(maxBucket+lookupWidth, len(r.buckets)-1, maxValue)) { + for len(r.buckets) <= maxBucket+lookupWidth { + r.buckets = append(r.buckets, topicRadiusBucket{lookupSent: make(map[common.Hash]mclock.AbsTime)}) + } + lookupRight = r.chooseLookupBucket(maxBucket, maxBucket+lookupWidth-1) + } + if lookupLeft == -1 { + radiusLookup = lookupRight + } else { + if lookupRight == -1 { + radiusLookup = lookupLeft + } else { + if randUint(2) == 0 { + radiusLookup = lookupLeft + } else { + radiusLookup = lookupRight + } + } + } + + //fmt.Println("mb", maxBucket, "sc", slopeCross, "mrb", minRadBucket, "ll", lookupLeft, "lr", lookupRight, "mv", maxValue) + + if radiusLookup == -1 { + // no more radius lookups needed at the moment, return a radius + r.converged = true + rad := maxBucket + if minRadBucket < rad { + rad = minRadBucket + } + radius = ^uint64(0) + if rad > 0 { + radius = uint64(math.Pow(2, 64-float64(rad)/radiusBucketsPerBit)) + } + r.radius = radius + } + + return +} + +func (r *topicRadius) nextTarget(forceRegular bool) lookupInfo { + if !forceRegular { + _, radiusLookup := r.recalcRadius() + if radiusLookup != -1 { + target := r.targetForBucket(radiusLookup) + r.buckets[radiusLookup].lookupSent[target] = mclock.Now() + return lookupInfo{target: target, topic: r.topic, radiusLookup: true} + } + } + + radExt := r.radius / 2 + if radExt > maxRadius-r.radius { + radExt = maxRadius - r.radius + } + rnd := randUint64n(r.radius) + randUint64n(2*radExt) + if rnd > radExt { + rnd -= radExt + } else { + rnd = radExt - rnd + } + + prefix := r.topicHashPrefix ^ rnd + var target common.Hash + binary.BigEndian.PutUint64(target[0:8], prefix) + globalRandRead(target[8:]) + return lookupInfo{target: target, topic: r.topic, radiusLookup: false} +} + +func (r *topicRadius) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t ticketRef) { + wait := t.t.regTime[t.idx] - t.t.issueTime + inside := float64(wait)/float64(targetWaitTime) - 0.5 + if inside > 1 { + inside = 1 + } + if inside < 0 { + inside = 0 + } + r.adjust(now, targetHash, t.t.node.sha, inside) +} + +func (r *topicRadius) adjust(now mclock.AbsTime, targetHash, addrHash common.Hash, inside float64) { + bucket := r.getBucketIdx(addrHash) + //fmt.Println("adjust", bucket, len(r.buckets), inside) + if bucket >= len(r.buckets) { + return + } + r.buckets[bucket].adjust(now, inside) + delete(r.buckets[bucket].lookupSent, targetHash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go new file mode 100644 index 0000000..b6bea01 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic.go @@ -0,0 +1,406 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "container/heap" + "fmt" + "math" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" +) + +const ( + maxEntries = 10000 + maxEntriesPerTopic = 50 + + fallbackRegistrationExpiry = 1 * time.Hour +) + +type Topic string + +type topicEntry struct { + topic Topic + fifoIdx uint64 + node *Node + expire mclock.AbsTime +} + +type topicInfo struct { + entries map[uint64]*topicEntry + fifoHead, fifoTail uint64 + rqItem *topicRequestQueueItem + wcl waitControlLoop +} + +// removes tail element from the fifo +func (t *topicInfo) getFifoTail() *topicEntry { + for t.entries[t.fifoTail] == nil { + t.fifoTail++ + } + tail := t.entries[t.fifoTail] + t.fifoTail++ + return tail +} + +type nodeInfo struct { + entries map[Topic]*topicEntry + lastIssuedTicket, lastUsedTicket uint32 + // you can't register a ticket newer than lastUsedTicket before noRegUntil (absolute time) + noRegUntil mclock.AbsTime +} + +type topicTable struct { + db *nodeDB + self *Node + nodes map[*Node]*nodeInfo + topics map[Topic]*topicInfo + globalEntries uint64 + requested topicRequestQueue + requestCnt uint64 + lastGarbageCollection mclock.AbsTime +} + +func newTopicTable(db *nodeDB, self *Node) *topicTable { + if printTestImgLogs { + fmt.Printf("*N %016x\n", self.sha[:8]) + } + return &topicTable{ + db: db, + nodes: make(map[*Node]*nodeInfo), + topics: make(map[Topic]*topicInfo), + self: self, + } +} + +func (t *topicTable) getOrNewTopic(topic Topic) *topicInfo { + ti := t.topics[topic] + if ti == nil { + rqItem := &topicRequestQueueItem{ + topic: topic, + priority: t.requestCnt, + } + ti = &topicInfo{ + entries: make(map[uint64]*topicEntry), + rqItem: rqItem, + } + t.topics[topic] = ti + heap.Push(&t.requested, rqItem) + } + return ti +} + +func (t *topicTable) checkDeleteTopic(topic Topic) { + ti := t.topics[topic] + if ti == nil { + return + } + if len(ti.entries) == 0 && ti.wcl.hasMinimumWaitPeriod() { + delete(t.topics, topic) + heap.Remove(&t.requested, ti.rqItem.index) + } +} + +func (t *topicTable) getOrNewNode(node *Node) *nodeInfo { + n := t.nodes[node] + if n == nil { + //fmt.Printf("newNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) + var issued, used uint32 + if t.db != nil { + issued, used = t.db.fetchTopicRegTickets(node.ID) + } + n = &nodeInfo{ + entries: make(map[Topic]*topicEntry), + lastIssuedTicket: issued, + lastUsedTicket: used, + } + t.nodes[node] = n + } + return n +} + +func (t *topicTable) checkDeleteNode(node *Node) { + if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() { + //fmt.Printf("deleteNode %016x %016x\n", t.self.sha[:8], node.sha[:8]) + delete(t.nodes, node) + } +} + +func (t *topicTable) storeTicketCounters(node *Node) { + n := t.getOrNewNode(node) + if t.db != nil { + t.db.updateTopicRegTickets(node.ID, n.lastIssuedTicket, n.lastUsedTicket) + } +} + +func (t *topicTable) getEntries(topic Topic) []*Node { + t.collectGarbage() + + te := t.topics[topic] + if te == nil { + return nil + } + nodes := make([]*Node, len(te.entries)) + i := 0 + for _, e := range te.entries { + nodes[i] = e.node + i++ + } + t.requestCnt++ + t.requested.update(te.rqItem, t.requestCnt) + return nodes +} + +func (t *topicTable) addEntry(node *Node, topic Topic) { + n := t.getOrNewNode(node) + // clear previous entries by the same node + for _, e := range n.entries { + t.deleteEntry(e) + } + // *** + n = t.getOrNewNode(node) + + tm := mclock.Now() + te := t.getOrNewTopic(topic) + + if len(te.entries) == maxEntriesPerTopic { + t.deleteEntry(te.getFifoTail()) + } + + if t.globalEntries == maxEntries { + t.deleteEntry(t.leastRequested()) // not empty, no need to check for nil + } + + fifoIdx := te.fifoHead + te.fifoHead++ + entry := &topicEntry{ + topic: topic, + fifoIdx: fifoIdx, + node: node, + expire: tm + mclock.AbsTime(fallbackRegistrationExpiry), + } + if printTestImgLogs { + fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8]) + } + te.entries[fifoIdx] = entry + n.entries[topic] = entry + t.globalEntries++ + te.wcl.registered(tm) +} + +// removes least requested element from the fifo +func (t *topicTable) leastRequested() *topicEntry { + for t.requested.Len() > 0 && t.topics[t.requested[0].topic] == nil { + heap.Pop(&t.requested) + } + if t.requested.Len() == 0 { + return nil + } + return t.topics[t.requested[0].topic].getFifoTail() +} + +// entry should exist +func (t *topicTable) deleteEntry(e *topicEntry) { + if printTestImgLogs { + fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8]) + } + ne := t.nodes[e.node].entries + delete(ne, e.topic) + if len(ne) == 0 { + t.checkDeleteNode(e.node) + } + te := t.topics[e.topic] + delete(te.entries, e.fifoIdx) + if len(te.entries) == 0 { + t.checkDeleteTopic(e.topic) + } + t.globalEntries-- +} + +// It is assumed that topics and waitPeriods have the same length. +func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) { + debugLog(fmt.Sprintf("useTicket %v %v %v", serialNo, topics, waitPeriods)) + //fmt.Println("useTicket", serialNo, topics, waitPeriods) + t.collectGarbage() + + n := t.getOrNewNode(node) + if serialNo < n.lastUsedTicket { + return false + } + + tm := mclock.Now() + if serialNo > n.lastUsedTicket && tm < n.noRegUntil { + return false + } + if serialNo != n.lastUsedTicket { + n.lastUsedTicket = serialNo + n.noRegUntil = tm + mclock.AbsTime(noRegTimeout()) + t.storeTicketCounters(node) + } + + currTime := uint64(tm / mclock.AbsTime(time.Second)) + regTime := issueTime + uint64(waitPeriods[idx]) + relTime := int64(currTime - regTime) + if relTime >= -1 && relTime <= regTimeWindow+1 { // give clients a little security margin on both ends + if e := n.entries[topics[idx]]; e == nil { + t.addEntry(node, topics[idx]) + } else { + // if there is an active entry, don't move to the front of the FIFO but prolong expire time + e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry) + } + return true + } + + return false +} + +func (topictab *topicTable) getTicket(node *Node, topics []Topic) *ticket { + topictab.collectGarbage() + + now := mclock.Now() + n := topictab.getOrNewNode(node) + n.lastIssuedTicket++ + topictab.storeTicketCounters(node) + + t := &ticket{ + issueTime: now, + topics: topics, + serial: n.lastIssuedTicket, + regTime: make([]mclock.AbsTime, len(topics)), + } + for i, topic := range topics { + var waitPeriod time.Duration + if topic := topictab.topics[topic]; topic != nil { + waitPeriod = topic.wcl.waitPeriod + } else { + waitPeriod = minWaitPeriod + } + + t.regTime[i] = now + mclock.AbsTime(waitPeriod) + } + return t +} + +const gcInterval = time.Minute + +func (t *topicTable) collectGarbage() { + tm := mclock.Now() + if time.Duration(tm-t.lastGarbageCollection) < gcInterval { + return + } + t.lastGarbageCollection = tm + + for node, n := range t.nodes { + for _, e := range n.entries { + if e.expire <= tm { + t.deleteEntry(e) + } + } + + t.checkDeleteNode(node) + } + + for topic := range t.topics { + t.checkDeleteTopic(topic) + } +} + +const ( + minWaitPeriod = time.Minute + regTimeWindow = 10 // seconds + avgnoRegTimeout = time.Minute * 10 + // target average interval between two incoming ad requests + wcTargetRegInterval = time.Minute * 10 / maxEntriesPerTopic + // + wcTimeConst = time.Minute * 10 +) + +// initialization is not required, will set to minWaitPeriod at first registration +type waitControlLoop struct { + lastIncoming mclock.AbsTime + waitPeriod time.Duration +} + +func (w *waitControlLoop) registered(tm mclock.AbsTime) { + w.waitPeriod = w.nextWaitPeriod(tm) + w.lastIncoming = tm +} + +func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration { + period := tm - w.lastIncoming + wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst))) + if wp < minWaitPeriod { + wp = minWaitPeriod + } + return wp +} + +func (w *waitControlLoop) hasMinimumWaitPeriod() bool { + return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod +} + +func noRegTimeout() time.Duration { + e := rand.ExpFloat64() + if e > 100 { + e = 100 + } + return time.Duration(float64(avgnoRegTimeout) * e) +} + +type topicRequestQueueItem struct { + topic Topic + priority uint64 + index int +} + +// A topicRequestQueue implements heap.Interface and holds topicRequestQueueItems. +type topicRequestQueue []*topicRequestQueueItem + +func (tq topicRequestQueue) Len() int { return len(tq) } + +func (tq topicRequestQueue) Less(i, j int) bool { + return tq[i].priority < tq[j].priority +} + +func (tq topicRequestQueue) Swap(i, j int) { + tq[i], tq[j] = tq[j], tq[i] + tq[i].index = i + tq[j].index = j +} + +func (tq *topicRequestQueue) Push(x interface{}) { + n := len(*tq) + item := x.(*topicRequestQueueItem) + item.index = n + *tq = append(*tq, item) +} + +func (tq *topicRequestQueue) Pop() interface{} { + old := *tq + n := len(old) + item := old[n-1] + item.index = -1 + *tq = old[0 : n-1] + return item +} + +func (tq *topicRequestQueue) update(item *topicRequestQueueItem, priority uint64) { + item.priority = priority + heap.Fix(tq, item.index) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic_test.go new file mode 100644 index 0000000..ba79993 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/topic_test.go @@ -0,0 +1,71 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "encoding/binary" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" +) + +func TestTopicRadius(t *testing.T) { + now := mclock.Now() + topic := Topic("qwerty") + rad := newTopicRadius(topic) + targetRad := (^uint64(0)) / 100 + + waitFn := func(addr common.Hash) time.Duration { + prefix := binary.BigEndian.Uint64(addr[0:8]) + dist := prefix ^ rad.topicHashPrefix + relDist := float64(dist) / float64(targetRad) + relTime := (1 - relDist/2) * 2 + if relTime < 0 { + relTime = 0 + } + return time.Duration(float64(targetWaitTime) * relTime) + } + + bcnt := 0 + cnt := 0 + var sum float64 + for cnt < 100 { + addr := rad.nextTarget(false).target + wait := waitFn(addr) + ticket := &ticket{ + topics: []Topic{topic}, + regTime: []mclock.AbsTime{mclock.AbsTime(wait)}, + node: &Node{nodeNetGuts: nodeNetGuts{sha: addr}}, + } + rad.adjustWithTicket(now, addr, ticketRef{ticket, 0}) + if rad.radius != maxRadius { + cnt++ + sum += float64(rad.radius) + } else { + bcnt++ + if bcnt > 500 { + t.Errorf("Radius did not converge in 500 iterations") + } + } + } + avgRel := sum / float64(cnt) / float64(targetRad) + if avgRel > 1.05 || avgRel < 0.95 { + t.Errorf("Average/target ratio is too far from 1 (%v)", avgRel) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go new file mode 100644 index 0000000..b43f6d1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp.go @@ -0,0 +1,459 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/rlp" +) + +const Version = 4 + +// Errors +var ( + errPacketTooSmall = errors.New("too small") + errBadHash = errors.New("bad hash") + errExpired = errors.New("expired") + errUnsolicitedReply = errors.New("unsolicited reply") + errUnknownNode = errors.New("unknown node") + errTimeout = errors.New("RPC timeout") + errClockWarp = errors.New("reply deadline too far in the future") + errClosed = errors.New("socket closed") +) + +// Timeouts +const ( + respTimeout = 500 * time.Millisecond + sendTimeout = 500 * time.Millisecond + expiration = 20 * time.Second + + ntpFailureThreshold = 32 // Continuous timeouts after which to check NTP + ntpWarningCooldown = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning + driftThreshold = 10 * time.Second // Allowed clock drift before warning user +) + +// RPC request structures +type ( + ping struct { + Version uint + From, To rpcEndpoint + Expiration uint64 + + // v5 + Topics []Topic + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // pong is the reply to ping. + pong struct { + // This field should mirror the UDP envelope address + // of the ping packet, which provides a way to discover the + // the external address (after NAT). + To rpcEndpoint + + ReplyTok []byte // This contains the hash of the ping packet. + Expiration uint64 // Absolute timestamp at which the packet becomes invalid. + + // v5 + TopicHash common.Hash + TicketSerial uint32 + WaitPeriods []uint32 + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // findnode is a query for nodes close to the given target. + findnode struct { + Target NodeID // doesn't need to be an actual public key + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // findnode is a query for nodes close to the given target. + findnodeHash struct { + Target common.Hash + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + // reply to findnode + neighbors struct { + Nodes []rpcNode + Expiration uint64 + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` + } + + topicRegister struct { + Topics []Topic + Idx uint + Pong []byte + } + + topicQuery struct { + Topic Topic + Expiration uint64 + } + + // reply to topicQuery + topicNodes struct { + Echo common.Hash + Nodes []rpcNode + } + + rpcNode struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + ID NodeID + } + + rpcEndpoint struct { + IP net.IP // len 4 for IPv4 or 16 for IPv6 + UDP uint16 // for discovery protocol + TCP uint16 // for RLPx protocol + } +) + +const ( + macSize = 256 / 8 + sigSize = 520 / 8 + headSize = macSize + sigSize // space of packet frame data +) + +// Neighbors replies are sent across multiple packets to +// stay below the 1280 byte limit. We compute the maximum number +// of entries by stuffing a packet until it grows too large. +var maxNeighbors = func() int { + p := neighbors{Expiration: ^uint64(0)} + maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} + for n := 0; ; n++ { + p.Nodes = append(p.Nodes, maxSizeNode) + size, _, err := rlp.EncodeToReader(p) + if err != nil { + // If this ever happens, it will be caught by the unit tests. + panic("cannot encode: " + err.Error()) + } + if headSize+size+1 >= 1280 { + return n + } + } +}() + +var maxTopicNodes = func() int { + p := topicNodes{} + maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)} + for n := 0; ; n++ { + p.Nodes = append(p.Nodes, maxSizeNode) + size, _, err := rlp.EncodeToReader(p) + if err != nil { + // If this ever happens, it will be caught by the unit tests. + panic("cannot encode: " + err.Error()) + } + if headSize+size+1 >= 1280 { + return n + } + } +}() + +func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint { + ip := addr.IP.To4() + if ip == nil { + ip = addr.IP.To16() + } + return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} +} + +func (e1 rpcEndpoint) equal(e2 rpcEndpoint) bool { + return e1.UDP == e2.UDP && e1.TCP == e2.TCP && e1.IP.Equal(e2.IP) +} + +func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) { + if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { + return nil, err + } + n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP) + err := n.validateComplete() + return n, err +} + +func nodeToRPC(n *Node) rpcNode { + return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} +} + +type ingressPacket struct { + remoteID NodeID + remoteAddr *net.UDPAddr + ev nodeEvent + hash []byte + data interface{} // one of the RPC structs + rawData []byte +} + +type conn interface { + ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) + WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) + Close() error + LocalAddr() net.Addr +} + +// udp implements the RPC protocol. +type udp struct { + conn conn + priv *ecdsa.PrivateKey + ourEndpoint rpcEndpoint + nat nat.Interface + net *Network +} + +// ListenUDP returns a new table that listens for UDP packets on laddr. +func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) { + transport, err := listenUDP(priv, laddr) + if err != nil { + return nil, err + } + net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath, netrestrict) + if err != nil { + return nil, err + } + transport.net = net + go transport.readLoop() + return net, nil +} + +func listenUDP(priv *ecdsa.PrivateKey, laddr string) (*udp, error) { + addr, err := net.ResolveUDPAddr("udp", laddr) + if err != nil { + return nil, err + } + conn, err := net.ListenUDP("udp", addr) + if err != nil { + return nil, err + } + return &udp{conn: conn, priv: priv, ourEndpoint: makeEndpoint(addr, uint16(addr.Port))}, nil +} + +func (t *udp) localAddr() *net.UDPAddr { + return t.conn.LocalAddr().(*net.UDPAddr) +} + +func (t *udp) Close() { + t.conn.Close() +} + +func (t *udp) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) { + hash, _ = t.sendPacket(remote.ID, remote.addr(), byte(ptype), data) + return hash +} + +func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash []byte) { + hash, _ = t.sendPacket(remote.ID, toaddr, byte(pingPacket), ping{ + Version: Version, + From: t.ourEndpoint, + To: makeEndpoint(toaddr, uint16(toaddr.Port)), // TODO: maybe use known TCP port from DB + Expiration: uint64(time.Now().Add(expiration).Unix()), + Topics: topics, + }) + return hash +} + +func (t *udp) sendFindnode(remote *Node, target NodeID) { + t.sendPacket(remote.ID, remote.addr(), byte(findnodePacket), findnode{ + Target: target, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) +} + +func (t *udp) sendNeighbours(remote *Node, results []*Node) { + // Send neighbors in chunks with at most maxNeighbors per packet + // to stay below the 1280 byte limit. + p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} + for i, result := range results { + p.Nodes = append(p.Nodes, nodeToRPC(result)) + if len(p.Nodes) == maxNeighbors || i == len(results)-1 { + t.sendPacket(remote.ID, remote.addr(), byte(neighborsPacket), p) + p.Nodes = p.Nodes[:0] + } + } +} + +func (t *udp) sendFindnodeHash(remote *Node, target common.Hash) { + t.sendPacket(remote.ID, remote.addr(), byte(findnodeHashPacket), findnodeHash{ + Target: target, + Expiration: uint64(time.Now().Add(expiration).Unix()), + }) +} + +func (t *udp) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) { + t.sendPacket(remote.ID, remote.addr(), byte(topicRegisterPacket), topicRegister{ + Topics: topics, + Idx: uint(idx), + Pong: pong, + }) +} + +func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) { + p := topicNodes{Echo: queryHash} + if len(nodes) == 0 { + t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) + return + } + for i, result := range nodes { + if netutil.CheckRelayIP(remote.IP, result.IP) != nil { + continue + } + p.Nodes = append(p.Nodes, nodeToRPC(result)) + if len(p.Nodes) == maxTopicNodes || i == len(nodes)-1 { + t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p) + p.Nodes = p.Nodes[:0] + } + } +} + +func (t *udp) sendPacket(toid NodeID, toaddr *net.UDPAddr, ptype byte, req interface{}) (hash []byte, err error) { + //fmt.Println("sendPacket", nodeEvent(ptype), toaddr.String(), toid.String()) + packet, hash, err := encodePacket(t.priv, ptype, req) + if err != nil { + //fmt.Println(err) + return hash, err + } + glog.V(logger.Detail).Infof(">>> %v to %x@%v\n", nodeEvent(ptype), toid[:8], toaddr) + if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil { + glog.V(logger.Detail).Infoln("UDP send failed:", err) + } + //fmt.Println(err) + return hash, err +} + +// zeroed padding space for encodePacket. +var headSpace = make([]byte, headSize) + +func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (p, hash []byte, err error) { + b := new(bytes.Buffer) + b.Write(headSpace) + b.WriteByte(ptype) + if err := rlp.Encode(b, req); err != nil { + glog.V(logger.Error).Infoln("error encoding packet:", err) + return nil, nil, err + } + packet := b.Bytes() + sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv) + if err != nil { + glog.V(logger.Error).Infoln("could not sign packet:", err) + return nil, nil, err + } + copy(packet[macSize:], sig) + // add the hash to the front. Note: this doesn't protect the + // packet in any way. + hash = crypto.Keccak256(packet[macSize:]) + copy(packet, hash) + return packet, hash, nil +} + +// readLoop runs in its own goroutine. it injects ingress UDP packets +// into the network loop. +func (t *udp) readLoop() { + defer t.conn.Close() + // Discovery packets are defined to be no larger than 1280 bytes. + // Packets larger than this size will be cut at the end and treated + // as invalid because their hash won't match. + buf := make([]byte, 1280) + for { + nbytes, from, err := t.conn.ReadFromUDP(buf) + if netutil.IsTemporaryError(err) { + // Ignore temporary read errors. + glog.V(logger.Debug).Infof("Temporary read error: %v", err) + continue + } else if err != nil { + // Shut down the loop for permament errors. + glog.V(logger.Debug).Infof("Read error: %v", err) + return + } + t.handlePacket(from, buf[:nbytes]) + } +} + +func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error { + pkt := ingressPacket{remoteAddr: from} + if err := decodePacket(buf, &pkt); err != nil { + glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) + //fmt.Println("bad packet", err) + return err + } + t.net.reqReadPacket(pkt) + return nil +} + +func decodePacket(buffer []byte, pkt *ingressPacket) error { + if len(buffer) < headSize+1 { + return errPacketTooSmall + } + buf := make([]byte, len(buffer)) + copy(buf, buffer) + hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] + shouldhash := crypto.Keccak256(buf[macSize:]) + if !bytes.Equal(hash, shouldhash) { + return errBadHash + } + fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig) + if err != nil { + return err + } + pkt.rawData = buf + pkt.hash = hash + pkt.remoteID = fromID + switch pkt.ev = nodeEvent(sigdata[0]); pkt.ev { + case pingPacket: + pkt.data = new(ping) + case pongPacket: + pkt.data = new(pong) + case findnodePacket: + pkt.data = new(findnode) + case neighborsPacket: + pkt.data = new(neighbors) + case findnodeHashPacket: + pkt.data = new(findnodeHash) + case topicRegisterPacket: + pkt.data = new(topicRegister) + case topicQueryPacket: + pkt.data = new(topicQuery) + case topicNodesPacket: + pkt.data = new(topicNodes) + default: + return fmt.Errorf("unknown packet type: %d", sigdata[0]) + } + s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0) + err = s.Decode(pkt.data) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_test.go new file mode 100644 index 0000000..7d31815 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discv5/udp_test.go @@ -0,0 +1,455 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discv5 + +import ( + "encoding/hex" + "errors" + "io" + "net" + "reflect" + "sync" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + spew.Config.DisableMethods = true +} + +// shared test variables +var ( + futureExp = uint64(time.Now().Add(10 * time.Hour).Unix()) + testTarget = NodeID{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} + testRemote = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2} + testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4} + testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6} +) + +// type udpTest struct { +// t *testing.T +// pipe *dgramPipe +// table *Table +// udp *udp +// sent [][]byte +// localkey, remotekey *ecdsa.PrivateKey +// remoteaddr *net.UDPAddr +// } +// +// func newUDPTest(t *testing.T) *udpTest { +// test := &udpTest{ +// t: t, +// pipe: newpipe(), +// localkey: newkey(), +// remotekey: newkey(), +// remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303}, +// } +// test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "") +// return test +// } +// +// // handles a packet as if it had been sent to the transport. +// func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error { +// enc, err := encodePacket(test.remotekey, ptype, data) +// if err != nil { +// return test.errorf("packet (%d) encode error: %v", ptype, err) +// } +// test.sent = append(test.sent, enc) +// if err = test.udp.handlePacket(test.remoteaddr, enc); err != wantError { +// return test.errorf("error mismatch: got %q, want %q", err, wantError) +// } +// return nil +// } +// +// // waits for a packet to be sent by the transport. +// // validate should have type func(*udpTest, X) error, where X is a packet type. +// func (test *udpTest) waitPacketOut(validate interface{}) error { +// dgram := test.pipe.waitPacketOut() +// p, _, _, err := decodePacket(dgram) +// if err != nil { +// return test.errorf("sent packet decode error: %v", err) +// } +// fn := reflect.ValueOf(validate) +// exptype := fn.Type().In(0) +// if reflect.TypeOf(p) != exptype { +// return test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) +// } +// fn.Call([]reflect.Value{reflect.ValueOf(p)}) +// return nil +// } +// +// func (test *udpTest) errorf(format string, args ...interface{}) error { +// _, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut +// if ok { +// file = filepath.Base(file) +// } else { +// file = "???" +// line = 1 +// } +// err := fmt.Errorf(format, args...) +// fmt.Printf("\t%s:%d: %v\n", file, line, err) +// test.t.Fail() +// return err +// } +// +// func TestUDP_packetErrors(t *testing.T) { +// test := newUDPTest(t) +// defer test.table.Close() +// +// test.packetIn(errExpired, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version}) +// test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp}) +// test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp}) +// test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp}) +// } +// +// func TestUDP_findnode(t *testing.T) { +// test := newUDPTest(t) +// defer test.table.Close() +// +// // put a few nodes into the table. their exact +// // distribution shouldn't matter much, although we need to +// // take care not to overflow any bucket. +// targetHash := crypto.Keccak256Hash(testTarget[:]) +// nodes := &nodesByDistance{target: targetHash} +// for i := 0; i < bucketSize; i++ { +// nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize) +// } +// test.table.stuff(nodes.entries) +// +// // ensure there's a bond with the test node, +// // findnode won't be accepted otherwise. +// test.table.db.updateNode(NewNode( +// PubkeyID(&test.remotekey.PublicKey), +// test.remoteaddr.IP, +// uint16(test.remoteaddr.Port), +// 99, +// )) +// // check that closest neighbors are returned. +// test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp}) +// expected := test.table.closest(targetHash, bucketSize) +// +// waitNeighbors := func(want []*Node) { +// test.waitPacketOut(func(p *neighbors) { +// if len(p.Nodes) != len(want) { +// t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize) +// } +// for i := range p.Nodes { +// if p.Nodes[i].ID != want[i].ID { +// t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i]) +// } +// } +// }) +// } +// waitNeighbors(expected.entries[:maxNeighbors]) +// waitNeighbors(expected.entries[maxNeighbors:]) +// } +// +// func TestUDP_findnodeMultiReply(t *testing.T) { +// test := newUDPTest(t) +// defer test.table.Close() +// +// // queue a pending findnode request +// resultc, errc := make(chan []*Node), make(chan error) +// go func() { +// rid := PubkeyID(&test.remotekey.PublicKey) +// ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget) +// if err != nil && len(ns) == 0 { +// errc <- err +// } else { +// resultc <- ns +// } +// }() +// +// // wait for the findnode to be sent. +// // after it is sent, the transport is waiting for a reply +// test.waitPacketOut(func(p *findnode) { +// if p.Target != testTarget { +// t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) +// } +// }) +// +// // send the reply as two packets. +// list := []*Node{ +// MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"), +// MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"), +// MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"), +// MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"), +// } +// rpclist := make([]rpcNode, len(list)) +// for i := range list { +// rpclist[i] = nodeToRPC(list[i]) +// } +// test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]}) +// test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]}) +// +// // check that the sent neighbors are all returned by findnode +// select { +// case result := <-resultc: +// if !reflect.DeepEqual(result, list) { +// t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, list) +// } +// case err := <-errc: +// t.Errorf("findnode error: %v", err) +// case <-time.After(5 * time.Second): +// t.Error("findnode did not return within 5 seconds") +// } +// } +// +// func TestUDP_successfulPing(t *testing.T) { +// test := newUDPTest(t) +// added := make(chan *Node, 1) +// test.table.nodeAddedHook = func(n *Node) { added <- n } +// defer test.table.Close() +// +// // The remote side sends a ping packet to initiate the exchange. +// go test.packetIn(nil, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version, Expiration: futureExp}) +// +// // the ping is replied to. +// test.waitPacketOut(func(p *pong) { +// pinghash := test.sent[0][:macSize] +// if !bytes.Equal(p.ReplyTok, pinghash) { +// t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) +// } +// wantTo := rpcEndpoint{ +// // The mirrored UDP address is the UDP packet sender +// IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), +// // The mirrored TCP port is the one from the ping packet +// TCP: testRemote.TCP, +// } +// if !reflect.DeepEqual(p.To, wantTo) { +// t.Errorf("got pong.To %v, want %v", p.To, wantTo) +// } +// }) +// +// // remote is unknown, the table pings back. +// test.waitPacketOut(func(p *ping) error { +// if !reflect.DeepEqual(p.From, test.udp.ourEndpoint) { +// t.Errorf("got ping.From %v, want %v", p.From, test.udp.ourEndpoint) +// } +// wantTo := rpcEndpoint{ +// // The mirrored UDP address is the UDP packet sender. +// IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), +// TCP: 0, +// } +// if !reflect.DeepEqual(p.To, wantTo) { +// t.Errorf("got ping.To %v, want %v", p.To, wantTo) +// } +// return nil +// }) +// test.packetIn(nil, pongPacket, &pong{Expiration: futureExp}) +// +// // the node should be added to the table shortly after getting the +// // pong packet. +// select { +// case n := <-added: +// rid := PubkeyID(&test.remotekey.PublicKey) +// if n.ID != rid { +// t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid) +// } +// if !bytes.Equal(n.IP, test.remoteaddr.IP) { +// t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP) +// } +// if int(n.UDP) != test.remoteaddr.Port { +// t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port) +// } +// if n.TCP != testRemote.TCP { +// t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP) +// } +// case <-time.After(2 * time.Second): +// t.Errorf("node was not added within 2 seconds") +// } +// } + +var testPackets = []struct { + input string + wantPacket interface{} +}{ + { + input: "71dbda3a79554728d4f94411e42ee1f8b0d561c10e1e5f5893367948c6a7d70bb87b235fa28a77070271b6c164a2dce8c7e13a5739b53b5e96f2e5acb0e458a02902f5965d55ecbeb2ebb6cabb8b2b232896a36b737666c55265ad0a68412f250001ea04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a355", + wantPacket: &ping{ + Version: 4, + From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333}, + Expiration: 1136239445, + Rest: []rlp.RawValue{}, + }, + }, + { + input: "e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102", + wantPacket: &ping{ + Version: 4, + From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333}, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x01}, {0x02}}, + }, + }, + { + input: "577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba76023fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee1917084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c76d922dc3", + wantPacket: &ping{ + Version: 555, + From: rpcEndpoint{net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 3322, 5544}, + To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338}, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0xC5, 0x01, 0x02, 0x03, 0x04, 0x05}}, + }, + }, + { + input: "09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b2069869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f055542124e", + wantPacket: &pong{ + To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338}, + ReplyTok: common.Hex2Bytes("fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c954"), + Expiration: 1136239445, + Rest: []rlp.RawValue{{0xC6, 0x01, 0x02, 0x03, 0xC2, 0x04, 0x05}, {0x06}}, + }, + }, + { + input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396", + wantPacket: &findnode{ + Target: MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"), + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}}, + }, + }, + { + input: "c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0", + wantPacket: &neighbors{ + Nodes: []rpcNode{ + { + ID: MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), + IP: net.ParseIP("99.33.22.55").To4(), + UDP: 4444, + TCP: 4445, + }, + { + ID: MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), + IP: net.ParseIP("1.2.3.4").To4(), + UDP: 1, + TCP: 1, + }, + { + ID: MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), + IP: net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), + UDP: 3333, + TCP: 3333, + }, + { + ID: MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), + IP: net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), + UDP: 999, + TCP: 1000, + }, + }, + Expiration: 1136239445, + Rest: []rlp.RawValue{{0x01}, {0x02}, {0x03}}, + }, + }, +} + +func TestForwardCompatibility(t *testing.T) { + t.Skip("skipped while working on discovery v5") + + testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + wantNodeID := PubkeyID(&testkey.PublicKey) + + for _, test := range testPackets { + input, err := hex.DecodeString(test.input) + if err != nil { + t.Fatalf("invalid hex: %s", test.input) + } + var pkt ingressPacket + if err := decodePacket(input, &pkt); err != nil { + t.Errorf("did not accept packet %s\n%v", test.input, err) + continue + } + if !reflect.DeepEqual(pkt.data, test.wantPacket) { + t.Errorf("got %s\nwant %s", spew.Sdump(pkt.data), spew.Sdump(test.wantPacket)) + } + if pkt.remoteID != wantNodeID { + t.Errorf("got id %v\nwant id %v", pkt.remoteID, wantNodeID) + } + } +} + +// dgramPipe is a fake UDP socket. It queues all sent datagrams. +type dgramPipe struct { + mu *sync.Mutex + cond *sync.Cond + closing chan struct{} + closed bool + queue [][]byte +} + +func newpipe() *dgramPipe { + mu := new(sync.Mutex) + return &dgramPipe{ + closing: make(chan struct{}), + cond: &sync.Cond{L: mu}, + mu: mu, + } +} + +// WriteToUDP queues a datagram. +func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { + msg := make([]byte, len(b)) + copy(msg, b) + c.mu.Lock() + defer c.mu.Unlock() + if c.closed { + return 0, errors.New("closed") + } + c.queue = append(c.queue, msg) + c.cond.Signal() + return len(b), nil +} + +// ReadFromUDP just hangs until the pipe is closed. +func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { + <-c.closing + return 0, nil, io.EOF +} + +func (c *dgramPipe) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + if !c.closed { + close(c.closing) + c.closed = true + } + return nil +} + +func (c *dgramPipe) LocalAddr() net.Addr { + return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)} +} + +func (c *dgramPipe) waitPacketOut() []byte { + c.mu.Lock() + defer c.mu.Unlock() + for len(c.queue) == 0 { + c.cond.Wait() + } + p := c.queue[0] + copy(c.queue, c.queue[1:]) + c.queue = c.queue[:len(c.queue)-1] + return p +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/message.go b/vendor/github.com/ethereum/go-ethereum/p2p/message.go new file mode 100644 index 0000000..1292d21 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/message.go @@ -0,0 +1,273 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/rlp" +) + +// Msg defines the structure of a p2p message. +// +// Note that a Msg can only be sent once since the Payload reader is +// consumed during sending. It is not possible to create a Msg and +// send it any number of times. If you want to reuse an encoded +// structure, encode the payload into a byte array and create a +// separate Msg with a bytes.Reader as Payload for each send. +type Msg struct { + Code uint64 + Size uint32 // size of the paylod + Payload io.Reader + ReceivedAt time.Time +} + +// Decode parses the RLP content of a message into +// the given value, which must be a pointer. +// +// For the decoding rules, please see package rlp. +func (msg Msg) Decode(val interface{}) error { + s := rlp.NewStream(msg.Payload, uint64(msg.Size)) + if err := s.Decode(val); err != nil { + return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", msg.Code, msg.Size, err) + } + return nil +} + +func (msg Msg) String() string { + return fmt.Sprintf("msg #%v (%v bytes)", msg.Code, msg.Size) +} + +// Discard reads any remaining payload data into a black hole. +func (msg Msg) Discard() error { + _, err := io.Copy(ioutil.Discard, msg.Payload) + return err +} + +type MsgReader interface { + ReadMsg() (Msg, error) +} + +type MsgWriter interface { + // WriteMsg sends a message. It will block until the message's + // Payload has been consumed by the other end. + // + // Note that messages can be sent only once because their + // payload reader is drained. + WriteMsg(Msg) error +} + +// MsgReadWriter provides reading and writing of encoded messages. +// Implementations should ensure that ReadMsg and WriteMsg can be +// called simultaneously from multiple goroutines. +type MsgReadWriter interface { + MsgReader + MsgWriter +} + +// Send writes an RLP-encoded message with the given code. +// data should encode as an RLP list. +func Send(w MsgWriter, msgcode uint64, data interface{}) error { + size, r, err := rlp.EncodeToReader(data) + if err != nil { + return err + } + return w.WriteMsg(Msg{Code: msgcode, Size: uint32(size), Payload: r}) +} + +// SendItems writes an RLP with the given code and data elements. +// For a call such as: +// +// SendItems(w, code, e1, e2, e3) +// +// the message payload will be an RLP list containing the items: +// +// [e1, e2, e3] +// +func SendItems(w MsgWriter, msgcode uint64, elems ...interface{}) error { + return Send(w, msgcode, elems) +} + +// netWrapper wraps a MsgReadWriter with locks around +// ReadMsg/WriteMsg and applies read/write deadlines. +type netWrapper struct { + rmu, wmu sync.Mutex + + rtimeout, wtimeout time.Duration + conn net.Conn + wrapped MsgReadWriter +} + +func (rw *netWrapper) ReadMsg() (Msg, error) { + rw.rmu.Lock() + defer rw.rmu.Unlock() + rw.conn.SetReadDeadline(time.Now().Add(rw.rtimeout)) + return rw.wrapped.ReadMsg() +} + +func (rw *netWrapper) WriteMsg(msg Msg) error { + rw.wmu.Lock() + defer rw.wmu.Unlock() + rw.conn.SetWriteDeadline(time.Now().Add(rw.wtimeout)) + return rw.wrapped.WriteMsg(msg) +} + +// eofSignal wraps a reader with eof signaling. the eof channel is +// closed when the wrapped reader returns an error or when count bytes +// have been read. +type eofSignal struct { + wrapped io.Reader + count uint32 // number of bytes left + eof chan<- struct{} +} + +// note: when using eofSignal to detect whether a message payload +// has been read, Read might not be called for zero sized messages. +func (r *eofSignal) Read(buf []byte) (int, error) { + if r.count == 0 { + if r.eof != nil { + r.eof <- struct{}{} + r.eof = nil + } + return 0, io.EOF + } + + max := len(buf) + if int(r.count) < len(buf) { + max = int(r.count) + } + n, err := r.wrapped.Read(buf[:max]) + r.count -= uint32(n) + if (err != nil || r.count == 0) && r.eof != nil { + r.eof <- struct{}{} // tell Peer that msg has been consumed + r.eof = nil + } + return n, err +} + +// MsgPipe creates a message pipe. Reads on one end are matched +// with writes on the other. The pipe is full-duplex, both ends +// implement MsgReadWriter. +func MsgPipe() (*MsgPipeRW, *MsgPipeRW) { + var ( + c1, c2 = make(chan Msg), make(chan Msg) + closing = make(chan struct{}) + closed = new(int32) + rw1 = &MsgPipeRW{c1, c2, closing, closed} + rw2 = &MsgPipeRW{c2, c1, closing, closed} + ) + return rw1, rw2 +} + +// ErrPipeClosed is returned from pipe operations after the +// pipe has been closed. +var ErrPipeClosed = errors.New("p2p: read or write on closed message pipe") + +// MsgPipeRW is an endpoint of a MsgReadWriter pipe. +type MsgPipeRW struct { + w chan<- Msg + r <-chan Msg + closing chan struct{} + closed *int32 +} + +// WriteMsg sends a messsage on the pipe. +// It blocks until the receiver has consumed the message payload. +func (p *MsgPipeRW) WriteMsg(msg Msg) error { + if atomic.LoadInt32(p.closed) == 0 { + consumed := make(chan struct{}, 1) + msg.Payload = &eofSignal{msg.Payload, msg.Size, consumed} + select { + case p.w <- msg: + if msg.Size > 0 { + // wait for payload read or discard + select { + case <-consumed: + case <-p.closing: + } + } + return nil + case <-p.closing: + } + } + return ErrPipeClosed +} + +// ReadMsg returns a message sent on the other end of the pipe. +func (p *MsgPipeRW) ReadMsg() (Msg, error) { + if atomic.LoadInt32(p.closed) == 0 { + select { + case msg := <-p.r: + return msg, nil + case <-p.closing: + } + } + return Msg{}, ErrPipeClosed +} + +// Close unblocks any pending ReadMsg and WriteMsg calls on both ends +// of the pipe. They will return ErrPipeClosed. Close also +// interrupts any reads from a message payload. +func (p *MsgPipeRW) Close() error { + if atomic.AddInt32(p.closed, 1) != 1 { + // someone else is already closing + atomic.StoreInt32(p.closed, 1) // avoid overflow + return nil + } + close(p.closing) + return nil +} + +// ExpectMsg reads a message from r and verifies that its +// code and encoded RLP content match the provided values. +// If content is nil, the payload is discarded and not verified. +func ExpectMsg(r MsgReader, code uint64, content interface{}) error { + msg, err := r.ReadMsg() + if err != nil { + return err + } + if msg.Code != code { + return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, code) + } + if content == nil { + return msg.Discard() + } else { + contentEnc, err := rlp.EncodeToBytes(content) + if err != nil { + panic("content encode error: " + err.Error()) + } + if int(msg.Size) != len(contentEnc) { + return fmt.Errorf("message size mismatch: got %d, want %d", msg.Size, len(contentEnc)) + } + actualContent, err := ioutil.ReadAll(msg.Payload) + if err != nil { + return err + } + if !bytes.Equal(actualContent, contentEnc) { + return fmt.Errorf("message payload mismatch:\ngot: %x\nwant: %x", actualContent, contentEnc) + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/message_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/message_test.go new file mode 100644 index 0000000..a01f755 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/message_test.go @@ -0,0 +1,152 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "runtime" + "strings" + "testing" + "time" +) + +func ExampleMsgPipe() { + rw1, rw2 := MsgPipe() + go func() { + Send(rw1, 8, [][]byte{{0, 0}}) + Send(rw1, 5, [][]byte{{1, 1}}) + rw1.Close() + }() + + for { + msg, err := rw2.ReadMsg() + if err != nil { + break + } + var data [][]byte + msg.Decode(&data) + fmt.Printf("msg: %d, %x\n", msg.Code, data[0]) + } + // Output: + // msg: 8, 0000 + // msg: 5, 0101 +} + +func TestMsgPipeUnblockWrite(t *testing.T) { +loop: + for i := 0; i < 100; i++ { + rw1, rw2 := MsgPipe() + done := make(chan struct{}) + go func() { + if err := SendItems(rw1, 1); err == nil { + t.Error("EncodeMsg returned nil error") + } else if err != ErrPipeClosed { + t.Errorf("EncodeMsg returned wrong error: got %v, want %v", err, ErrPipeClosed) + } + close(done) + }() + + // this call should ensure that EncodeMsg is waiting to + // deliver sometimes. if this isn't done, Close is likely to + // be executed before EncodeMsg starts and then we won't test + // all the cases. + runtime.Gosched() + + rw2.Close() + select { + case <-done: + case <-time.After(200 * time.Millisecond): + t.Errorf("write didn't unblock") + break loop + } + } +} + +// This test should panic if concurrent close isn't implemented correctly. +func TestMsgPipeConcurrentClose(t *testing.T) { + rw1, _ := MsgPipe() + for i := 0; i < 10; i++ { + go rw1.Close() + } +} + +func TestEOFSignal(t *testing.T) { + rb := make([]byte, 10) + + // empty reader + eof := make(chan struct{}, 1) + sig := &eofSignal{new(bytes.Buffer), 0, eof} + if n, err := sig.Read(rb); n != 0 || err != io.EOF { + t.Errorf("Read returned unexpected values: (%v, %v)", n, err) + } + select { + case <-eof: + default: + t.Error("EOF chan not signaled") + } + + // count before error + eof = make(chan struct{}, 1) + sig = &eofSignal{bytes.NewBufferString("aaaaaaaa"), 4, eof} + if n, err := sig.Read(rb); n != 4 || err != nil { + t.Errorf("Read returned unexpected values: (%v, %v)", n, err) + } + select { + case <-eof: + default: + t.Error("EOF chan not signaled") + } + + // error before count + eof = make(chan struct{}, 1) + sig = &eofSignal{bytes.NewBufferString("aaaa"), 999, eof} + if n, err := sig.Read(rb); n != 4 || err != nil { + t.Errorf("Read returned unexpected values: (%v, %v)", n, err) + } + if n, err := sig.Read(rb); n != 0 || err != io.EOF { + t.Errorf("Read returned unexpected values: (%v, %v)", n, err) + } + select { + case <-eof: + default: + t.Error("EOF chan not signaled") + } + + // no signal if neither occurs + eof = make(chan struct{}, 1) + sig = &eofSignal{bytes.NewBufferString("aaaaaaaaaaaaaaaaaaaaa"), 999, eof} + if n, err := sig.Read(rb); n != 10 || err != nil { + t.Errorf("Read returned unexpected values: (%v, %v)", n, err) + } + select { + case <-eof: + t.Error("unexpected EOF signal") + default: + } +} + +func unhex(str string) []byte { + r := strings.NewReplacer("\t", "", " ", "", "\n", "") + b, err := hex.DecodeString(r.Replace(str)) + if err != nil { + panic(fmt.Sprintf("invalid hex string: %q", str)) + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go b/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go new file mode 100644 index 0000000..98b6190 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go @@ -0,0 +1,71 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Contains the meters and timers used by the networking layer. + +package p2p + +import ( + "net" + + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + ingressConnectMeter = metrics.NewMeter("p2p/InboundConnects") + ingressTrafficMeter = metrics.NewMeter("p2p/InboundTraffic") + egressConnectMeter = metrics.NewMeter("p2p/OutboundConnects") + egressTrafficMeter = metrics.NewMeter("p2p/OutboundTraffic") +) + +// meteredConn is a wrapper around a network TCP connection that meters both the +// inbound and outbound network traffic. +type meteredConn struct { + *net.TCPConn // Network connection to wrap with metering +} + +// newMeteredConn creates a new metered connection, also bumping the ingress or +// egress connection meter. If the metrics system is disabled, this function +// returns the original object. +func newMeteredConn(conn net.Conn, ingress bool) net.Conn { + // Short circuit if metrics are disabled + if !metrics.Enabled { + return conn + } + // Otherwise bump the connection counters and wrap the connection + if ingress { + ingressConnectMeter.Mark(1) + } else { + egressConnectMeter.Mark(1) + } + return &meteredConn{conn.(*net.TCPConn)} +} + +// Read delegates a network read to the underlying connection, bumping the ingress +// traffic meter along the way. +func (c *meteredConn) Read(b []byte) (n int, err error) { + n, err = c.TCPConn.Read(b) + ingressTrafficMeter.Mark(int64(n)) + return +} + +// Write delegates a network write to the underlying connection, bumping the +// egress traffic meter along the way. +func (c *meteredConn) Write(b []byte) (n int, err error) { + n, err = c.TCPConn.Write(b) + egressTrafficMeter.Mark(int64(n)) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go new file mode 100644 index 0000000..f9ba936 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat.go @@ -0,0 +1,245 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package nat provides access to common network port mapping protocols. +package nat + +import ( + "errors" + "fmt" + "net" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/jackpal/go-nat-pmp" +) + +// An implementation of nat.Interface can map local ports to ports +// accessible from the Internet. +type Interface interface { + // These methods manage a mapping between a port on the local + // machine to a port that can be connected to from the internet. + // + // protocol is "UDP" or "TCP". Some implementations allow setting + // a display name for the mapping. The mapping may be removed by + // the gateway when its lifetime ends. + AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error + DeleteMapping(protocol string, extport, intport int) error + + // This method should return the external (Internet-facing) + // address of the gateway device. + ExternalIP() (net.IP, error) + + // Should return name of the method. This is used for logging. + String() string +} + +// Parse parses a NAT interface description. +// The following formats are currently accepted. +// Note that mechanism names are not case-sensitive. +// +// "" or "none" return nil +// "extip:77.12.33.4" will assume the local machine is reachable on the given IP +// "any" uses the first auto-detected mechanism +// "upnp" uses the Universal Plug and Play protocol +// "pmp" uses NAT-PMP with an auto-detected gateway address +// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address +func Parse(spec string) (Interface, error) { + var ( + parts = strings.SplitN(spec, ":", 2) + mech = strings.ToLower(parts[0]) + ip net.IP + ) + if len(parts) > 1 { + ip = net.ParseIP(parts[1]) + if ip == nil { + return nil, errors.New("invalid IP address") + } + } + switch mech { + case "", "none", "off": + return nil, nil + case "any", "auto", "on": + return Any(), nil + case "extip", "ip": + if ip == nil { + return nil, errors.New("missing IP address") + } + return ExtIP(ip), nil + case "upnp": + return UPnP(), nil + case "pmp", "natpmp", "nat-pmp": + return PMP(ip), nil + default: + return nil, fmt.Errorf("unknown mechanism %q", parts[0]) + } +} + +const ( + mapTimeout = 20 * time.Minute + mapUpdateInterval = 15 * time.Minute +) + +// Map adds a port mapping on m and keeps it alive until c is closed. +// This function is typically invoked in its own goroutine. +func Map(m Interface, c chan struct{}, protocol string, extport, intport int, name string) { + refresh := time.NewTimer(mapUpdateInterval) + defer func() { + refresh.Stop() + glog.V(logger.Debug).Infof("deleting port mapping: %s %d -> %d (%s) using %s\n", protocol, extport, intport, name, m) + m.DeleteMapping(protocol, extport, intport) + }() + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { + glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) + } else { + glog.V(logger.Info).Infof("mapped network port %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) + } + for { + select { + case _, ok := <-c: + if !ok { + return + } + case <-refresh.C: + glog.V(logger.Detail).Infof("refresh port mapping %s:%d -> %d (%s) using %s\n", protocol, extport, intport, name, m) + if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil { + glog.V(logger.Debug).Infof("network port %s:%d could not be mapped: %v\n", protocol, intport, err) + } + refresh.Reset(mapUpdateInterval) + } + } +} + +// ExtIP assumes that the local machine is reachable on the given +// external IP address, and that any required ports were mapped manually. +// Mapping operations will not return an error but won't actually do anything. +func ExtIP(ip net.IP) Interface { + if ip == nil { + panic("IP must not be nil") + } + return extIP(ip) +} + +type extIP net.IP + +func (n extIP) ExternalIP() (net.IP, error) { return net.IP(n), nil } +func (n extIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) } + +// These do nothing. +func (extIP) AddMapping(string, int, int, string, time.Duration) error { return nil } +func (extIP) DeleteMapping(string, int, int) error { return nil } + +// Any returns a port mapper that tries to discover any supported +// mechanism on the local network. +func Any() Interface { + // TODO: attempt to discover whether the local machine has an + // Internet-class address. Return ExtIP in this case. + return startautodisc("UPnP or NAT-PMP", func() Interface { + found := make(chan Interface, 2) + go func() { found <- discoverUPnP() }() + go func() { found <- discoverPMP() }() + for i := 0; i < cap(found); i++ { + if c := <-found; c != nil { + return c + } + } + return nil + }) +} + +// UPnP returns a port mapper that uses UPnP. It will attempt to +// discover the address of your router using UDP broadcasts. +func UPnP() Interface { + return startautodisc("UPnP", discoverUPnP) +} + +// PMP returns a port mapper that uses NAT-PMP. The provided gateway +// address should be the IP of your router. If the given gateway +// address is nil, PMP will attempt to auto-discover the router. +func PMP(gateway net.IP) Interface { + if gateway != nil { + return &pmp{gw: gateway, c: natpmp.NewClient(gateway)} + } + return startautodisc("NAT-PMP", discoverPMP) +} + +// autodisc represents a port mapping mechanism that is still being +// auto-discovered. Calls to the Interface methods on this type will +// wait until the discovery is done and then call the method on the +// discovered mechanism. +// +// This type is useful because discovery can take a while but we +// want return an Interface value from UPnP, PMP and Auto immediately. +type autodisc struct { + what string // type of interface being autodiscovered + once sync.Once + doit func() Interface + + mu sync.Mutex + found Interface +} + +func startautodisc(what string, doit func() Interface) Interface { + // TODO: monitor network configuration and rerun doit when it changes. + return &autodisc{what: what, doit: doit} +} + +func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error { + if err := n.wait(); err != nil { + return err + } + return n.found.AddMapping(protocol, extport, intport, name, lifetime) +} + +func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error { + if err := n.wait(); err != nil { + return err + } + return n.found.DeleteMapping(protocol, extport, intport) +} + +func (n *autodisc) ExternalIP() (net.IP, error) { + if err := n.wait(); err != nil { + return nil, err + } + return n.found.ExternalIP() +} + +func (n *autodisc) String() string { + n.mu.Lock() + defer n.mu.Unlock() + if n.found == nil { + return n.what + } else { + return n.found.String() + } +} + +// wait blocks until auto-discovery has been performed. +func (n *autodisc) wait() error { + n.once.Do(func() { + n.mu.Lock() + n.found = n.doit() + n.mu.Unlock() + }) + if n.found == nil { + return fmt.Errorf("no %s router discovered", n.what) + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat_test.go new file mode 100644 index 0000000..469101e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/nat_test.go @@ -0,0 +1,63 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package nat + +import ( + "net" + "testing" + "time" +) + +// This test checks that autodisc doesn't hang and returns +// consistent results when multiple goroutines call its methods +// concurrently. +func TestAutoDiscRace(t *testing.T) { + ad := startautodisc("thing", func() Interface { + time.Sleep(500 * time.Millisecond) + return extIP{33, 44, 55, 66} + }) + + // Spawn a few concurrent calls to ad.ExternalIP. + type rval struct { + ip net.IP + err error + } + results := make(chan rval, 50) + for i := 0; i < cap(results); i++ { + go func() { + ip, err := ad.ExternalIP() + results <- rval{ip, err} + }() + } + + // Check that they all return the correct result within the deadline. + deadline := time.After(2 * time.Second) + for i := 0; i < cap(results); i++ { + select { + case <-deadline: + t.Fatal("deadline exceeded") + case rval := <-results: + if rval.err != nil { + t.Errorf("result %d: unexpected error: %v", i, rval.err) + } + wantIP := net.IP{33, 44, 55, 66} + if !rval.ip.Equal(wantIP) { + t.Errorf("result %d: got IP %v, want %v", i, rval.ip, wantIP) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/natpmp.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natpmp.go new file mode 100644 index 0000000..577a424 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natpmp.go @@ -0,0 +1,131 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package nat + +import ( + "fmt" + "net" + "strings" + "time" + + "github.com/jackpal/go-nat-pmp" +) + +// natPMPClient adapts the NAT-PMP protocol implementation so it conforms to +// the common interface. +type pmp struct { + gw net.IP + c *natpmp.Client +} + +func (n *pmp) String() string { + return fmt.Sprintf("NAT-PMP(%v)", n.gw) +} + +func (n *pmp) ExternalIP() (net.IP, error) { + response, err := n.c.GetExternalAddress() + if err != nil { + return nil, err + } + return response.ExternalIPAddress[:], nil +} + +func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error { + if lifetime <= 0 { + return fmt.Errorf("lifetime must not be <= 0") + } + // Note order of port arguments is switched between our + // AddMapping and the client's AddPortMapping. + _, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second)) + return err +} + +func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) { + // To destroy a mapping, send an add-port with an internalPort of + // the internal port to destroy, an external port of zero and a + // time of zero. + _, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0) + return err +} + +func discoverPMP() Interface { + // run external address lookups on all potential gateways + gws := potentialGateways() + found := make(chan *pmp, len(gws)) + for i := range gws { + gw := gws[i] + go func() { + c := natpmp.NewClient(gw) + if _, err := c.GetExternalAddress(); err != nil { + found <- nil + } else { + found <- &pmp{gw, c} + } + }() + } + // return the one that responds first. + // discovery needs to be quick, so we stop caring about + // any responses after a very short timeout. + timeout := time.NewTimer(1 * time.Second) + defer timeout.Stop() + for range gws { + select { + case c := <-found: + if c != nil { + return c + } + case <-timeout.C: + return nil + } + } + return nil +} + +var ( + // LAN IP ranges + _, lan10, _ = net.ParseCIDR("10.0.0.0/8") + _, lan176, _ = net.ParseCIDR("172.16.0.0/12") + _, lan192, _ = net.ParseCIDR("192.168.0.0/16") +) + +// TODO: improve this. We currently assume that (on most networks) +// the router is X.X.X.1 in a local LAN range. +func potentialGateways() (gws []net.IP) { + ifaces, err := net.Interfaces() + if err != nil { + return nil + } + for _, iface := range ifaces { + ifaddrs, err := iface.Addrs() + if err != nil { + return gws + } + for _, addr := range ifaddrs { + switch x := addr.(type) { + case *net.IPNet: + if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) { + ip := x.IP.Mask(x.Mask).To4() + if ip != nil { + ip[3] = ip[3] | 0x01 + gws = append(gws, ip) + } + } + } + } + } + return gws +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp.go new file mode 100644 index 0000000..6b3fa3f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp.go @@ -0,0 +1,177 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package nat + +import ( + "errors" + "fmt" + "net" + "strings" + "time" + + "github.com/huin/goupnp" + "github.com/huin/goupnp/dcps/internetgateway1" + "github.com/huin/goupnp/dcps/internetgateway2" +) + +const soapRequestTimeout = 3 * time.Second + +type upnp struct { + dev *goupnp.RootDevice + service string + client upnpClient +} + +type upnpClient interface { + GetExternalIPAddress() (string, error) + AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error + DeletePortMapping(string, uint16, string) error + GetNATRSIPStatus() (sip bool, nat bool, err error) +} + +func (n *upnp) ExternalIP() (addr net.IP, err error) { + ipString, err := n.client.GetExternalIPAddress() + if err != nil { + return nil, err + } + ip := net.ParseIP(ipString) + if ip == nil { + return nil, errors.New("bad IP in response") + } + return ip, nil +} + +func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error { + ip, err := n.internalAddress() + if err != nil { + return nil + } + protocol = strings.ToUpper(protocol) + lifetimeS := uint32(lifetime / time.Second) + return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) +} + +func (n *upnp) internalAddress() (net.IP, error) { + devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host) + if err != nil { + return nil, err + } + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + for _, iface := range ifaces { + addrs, err := iface.Addrs() + if err != nil { + return nil, err + } + for _, addr := range addrs { + switch x := addr.(type) { + case *net.IPNet: + if x.Contains(devaddr.IP) { + return x.IP, nil + } + } + } + } + return nil, fmt.Errorf("could not find local address in same net as %v", devaddr) +} + +func (n *upnp) DeleteMapping(protocol string, extport, intport int) error { + return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol)) +} + +func (n *upnp) String() string { + return "UPNP " + n.service +} + +// discoverUPnP searches for Internet Gateway Devices +// and returns the first one it can find on the local network. +func discoverUPnP() Interface { + found := make(chan *upnp, 2) + // IGDv1 + go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { + switch sc.Service.ServiceType { + case internetgateway1.URN_WANIPConnection_1: + return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}} + case internetgateway1.URN_WANPPPConnection_1: + return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}} + } + return nil + }) + // IGDv2 + go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp { + switch sc.Service.ServiceType { + case internetgateway2.URN_WANIPConnection_1: + return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}} + case internetgateway2.URN_WANIPConnection_2: + return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}} + case internetgateway2.URN_WANPPPConnection_1: + return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}} + } + return nil + }) + for i := 0; i < cap(found); i++ { + if c := <-found; c != nil { + return c + } + } + return nil +} + +// finds devices matching the given target and calls matcher for all +// advertised services of each device. The first non-nil service found +// is sent into out. If no service matched, nil is sent. +func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) { + devs, err := goupnp.DiscoverDevices(target) + if err != nil { + out <- nil + return + } + found := false + for i := 0; i < len(devs) && !found; i++ { + if devs[i].Root == nil { + continue + } + devs[i].Root.Device.VisitServices(func(service *goupnp.Service) { + if found { + return + } + // check for a matching IGD service + sc := goupnp.ServiceClient{ + SOAPClient: service.NewSOAPClient(), + RootDevice: devs[i].Root, + Location: devs[i].Location, + Service: service, + } + sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout + upnp := matcher(devs[i].Root, sc) + if upnp == nil { + return + } + // check whether port mapping is enabled + if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat { + return + } + out <- upnp + found = true + }) + } + if !found { + out <- nil + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp_test.go new file mode 100644 index 0000000..79f6d25 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/nat/natupnp_test.go @@ -0,0 +1,244 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package nat + +import ( + "fmt" + "io" + "net" + "net/http" + "runtime" + "strings" + "testing" + + "github.com/huin/goupnp/httpu" +) + +func TestUPNP_DDWRT(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skipf("disabled to avoid firewall prompt") + } + + dev := &fakeIGD{ + t: t, + ssdpResp: "HTTP/1.1 200 OK\r\n" + + "Cache-Control: max-age=300\r\n" + + "Date: Sun, 10 May 2015 10:05:33 GMT\r\n" + + "Ext: \r\n" + + "Location: http://{{listenAddr}}/InternetGatewayDevice.xml\r\n" + + "Server: POSIX UPnP/1.0 DD-WRT Linux/V24\r\n" + + "ST: urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" + + "USN: uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800::urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" + + "\r\n", + httpResps: map[string]string{ + "GET /InternetGatewayDevice.xml": ` + + + + 1 + 0 + + + urn:schemas-upnp-org:device:InternetGatewayDevice:1 + DD-WRT + http://www.dd-wrt.com + Gateway + Asus RT-N16:DD-WRT + Asus RT-N16 + V24 + 0000001 + http://www.dd-wrt.com + uuid:A13AB4C3-3A14-E386-DE6A-EFEA923A06FE + + + urn:schemas-upnp-org:service:Layer3Forwarding:1 + urn:upnp-org:serviceId:L3Forwarding1 + /x_layer3forwarding.xml + /control?Layer3Forwarding + /event?Layer3Forwarding + + + + + urn:schemas-upnp-org:device:WANDevice:1 + WANDevice + DD-WRT + http://www.dd-wrt.com + Gateway + router + http://www.dd-wrt.com + uuid:48FD569B-F9A9-96AE-4EE6-EB403D3DB91A + + + urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + urn:upnp-org:serviceId:WANCommonIFC1 + /x_wancommoninterfaceconfig.xml + /control?WANCommonInterfaceConfig + /event?WANCommonInterfaceConfig + + + + + urn:schemas-upnp-org:device:WANConnectionDevice:1 + WAN Connection Device + DD-WRT + http://www.dd-wrt.com + Gateway + router + http://www.dd-wrt.com + uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800 + + + urn:schemas-upnp-org:service:WANIPConnection:1 + urn:upnp-org:serviceId:WANIPConn1 + /x_wanipconnection.xml + /control?WANIPConnection + /event?WANIPConnection + + + + + + + urn:schemas-upnp-org:device:LANDevice:1 + LANDevice + DD-WRT + http://www.dd-wrt.com + Gateway + router + http://www.dd-wrt.com + uuid:04021998-3B35-2BDB-7B3C-99DA4435DA09 + + + urn:schemas-upnp-org:service:LANHostConfigManagement:1 + urn:upnp-org:serviceId:LANHostCfg1 + /x_lanhostconfigmanagement.xml + /control?LANHostConfigManagement + /event?LANHostConfigManagement + + + + + http://{{listenAddr}} + + + `, + // The response to our GetNATRSIPStatus call. This + // particular implementation has a bug where the elements + // inside u:GetNATRSIPStatusResponse are not properly + // namespaced. + "POST /control?WANIPConnection": ` + + + + 0 + 1 + + + + `, + }, + } + if err := dev.listen(); err != nil { + t.Skipf("cannot listen: %v", err) + } + dev.serve() + defer dev.close() + + // Attempt to discover the fake device. + discovered := discoverUPnP() + if discovered == nil { + t.Fatalf("not discovered") + } + upnp, _ := discovered.(*upnp) + if upnp.service != "IGDv1-IP1" { + t.Errorf("upnp.service mismatch: got %q, want %q", upnp.service, "IGDv1-IP1") + } + wantURL := "http://" + dev.listener.Addr().String() + "/InternetGatewayDevice.xml" + if upnp.dev.URLBaseStr != wantURL { + t.Errorf("upnp.dev.URLBaseStr mismatch: got %q, want %q", upnp.dev.URLBaseStr, wantURL) + } +} + +// fakeIGD presents itself as a discoverable UPnP device which sends +// canned responses to HTTPU and HTTP requests. +type fakeIGD struct { + t *testing.T // for logging + + listener net.Listener + mcastListener *net.UDPConn + + // This should be a complete HTTP response (including headers). + // It is sent as the response to any sspd packet. Any occurrence + // of "{{listenAddr}}" is replaced with the actual TCP listen + // address of the HTTP server. + ssdpResp string + // This one should contain XML payloads for all requests + // performed. The keys contain method and path, e.g. "GET /foo/bar". + // As with ssdpResp, "{{listenAddr}}" is replaced with the TCP + // listen address. + httpResps map[string]string +} + +// httpu.Handler +func (dev *fakeIGD) ServeMessage(r *http.Request) { + dev.t.Logf(`HTTPU request %s %s`, r.Method, r.RequestURI) + conn, err := net.Dial("udp4", r.RemoteAddr) + if err != nil { + fmt.Printf("reply Dial error: %v", err) + return + } + defer conn.Close() + io.WriteString(conn, dev.replaceListenAddr(dev.ssdpResp)) +} + +// http.Handler +func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if resp, ok := dev.httpResps[r.Method+" "+r.RequestURI]; ok { + dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 200) + io.WriteString(w, dev.replaceListenAddr(resp)) + } else { + dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 404) + w.WriteHeader(http.StatusNotFound) + } +} + +func (dev *fakeIGD) replaceListenAddr(resp string) string { + return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1) +} + +func (dev *fakeIGD) listen() (err error) { + if dev.listener, err = net.Listen("tcp", "127.0.0.1:0"); err != nil { + return err + } + laddr := &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 1900} + if dev.mcastListener, err = net.ListenMulticastUDP("udp", nil, laddr); err != nil { + dev.listener.Close() + return err + } + return nil +} + +func (dev *fakeIGD) serve() { + go httpu.Serve(dev.mcastListener, dev) + go http.Serve(dev.listener, dev) +} + +func (dev *fakeIGD) close() { + dev.mcastListener.Close() + dev.listener.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go new file mode 100644 index 0000000..cb21b9c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error.go @@ -0,0 +1,25 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package netutil + +// IsTemporaryError checks whether the given error should be considered temporary. +func IsTemporaryError(err error) bool { + tempErr, ok := err.(interface { + Temporary() bool + }) + return ok && tempErr.Temporary() || isPacketTooBig(err) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error_test.go new file mode 100644 index 0000000..645e48f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/error_test.go @@ -0,0 +1,73 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package netutil + +import ( + "net" + "testing" + "time" +) + +// This test checks that isPacketTooBig correctly identifies +// errors that result from receiving a UDP packet larger +// than the supplied receive buffer. +func TestIsPacketTooBig(t *testing.T) { + listener, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer listener.Close() + sender, err := net.Dial("udp", listener.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + defer sender.Close() + + sendN := 1800 + recvN := 300 + for i := 0; i < 20; i++ { + go func() { + buf := make([]byte, sendN) + for i := range buf { + buf[i] = byte(i) + } + sender.Write(buf) + }() + + buf := make([]byte, recvN) + listener.SetDeadline(time.Now().Add(1 * time.Second)) + n, _, err := listener.ReadFrom(buf) + if err != nil { + if nerr, ok := err.(net.Error); ok && nerr.Timeout() { + continue + } + if !isPacketTooBig(err) { + t.Fatalf("unexpected read error: %v", err) + } + continue + } + if n != recvN { + t.Fatalf("short read: %d, want %d", n, recvN) + } + for i := range buf { + if buf[i] != byte(i) { + t.Fatalf("error in pattern") + break + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go new file mode 100644 index 0000000..3c37157 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net.go @@ -0,0 +1,166 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package netutil contains extensions to the net package. +package netutil + +import ( + "errors" + "net" + "strings" +) + +var lan4, lan6, special4, special6 Netlist + +func init() { + // Lists from RFC 5735, RFC 5156, + // https://www.iana.org/assignments/iana-ipv4-special-registry/ + lan4.Add("0.0.0.0/8") // "This" network + lan4.Add("10.0.0.0/8") // Private Use + lan4.Add("172.16.0.0/12") // Private Use + lan4.Add("192.168.0.0/16") // Private Use + lan6.Add("fe80::/10") // Link-Local + lan6.Add("fc00::/7") // Unique-Local + special4.Add("192.0.0.0/29") // IPv4 Service Continuity + special4.Add("192.0.0.9/32") // PCP Anycast + special4.Add("192.0.0.170/32") // NAT64/DNS64 Discovery + special4.Add("192.0.0.171/32") // NAT64/DNS64 Discovery + special4.Add("192.0.2.0/24") // TEST-NET-1 + special4.Add("192.31.196.0/24") // AS112 + special4.Add("192.52.193.0/24") // AMT + special4.Add("192.88.99.0/24") // 6to4 Relay Anycast + special4.Add("192.175.48.0/24") // AS112 + special4.Add("198.18.0.0/15") // Device Benchmark Testing + special4.Add("198.51.100.0/24") // TEST-NET-2 + special4.Add("203.0.113.0/24") // TEST-NET-3 + special4.Add("255.255.255.255/32") // Limited Broadcast + + // http://www.iana.org/assignments/iana-ipv6-special-registry/ + special6.Add("100::/64") + special6.Add("2001::/32") + special6.Add("2001:1::1/128") + special6.Add("2001:2::/48") + special6.Add("2001:3::/32") + special6.Add("2001:4:112::/48") + special6.Add("2001:5::/32") + special6.Add("2001:10::/28") + special6.Add("2001:20::/28") + special6.Add("2001:db8::/32") + special6.Add("2002::/16") +} + +// Netlist is a list of IP networks. +type Netlist []net.IPNet + +// ParseNetlist parses a comma-separated list of CIDR masks. +// Whitespace and extra commas are ignored. +func ParseNetlist(s string) (*Netlist, error) { + ws := strings.NewReplacer(" ", "", "\n", "", "\t", "") + masks := strings.Split(ws.Replace(s), ",") + l := make(Netlist, 0) + for _, mask := range masks { + if mask == "" { + continue + } + _, n, err := net.ParseCIDR(mask) + if err != nil { + return nil, err + } + l = append(l, *n) + } + return &l, nil +} + +// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is +// intended to be used for setting up static lists. +func (l *Netlist) Add(cidr string) { + _, n, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + *l = append(*l, *n) +} + +// Contains reports whether the given IP is contained in the list. +func (l *Netlist) Contains(ip net.IP) bool { + if l == nil { + return false + } + for _, net := range *l { + if net.Contains(ip) { + return true + } + } + return false +} + +// IsLAN reports whether an IP is a local network address. +func IsLAN(ip net.IP) bool { + if ip.IsLoopback() { + return true + } + if v4 := ip.To4(); v4 != nil { + return lan4.Contains(v4) + } + return lan6.Contains(ip) +} + +// IsSpecialNetwork reports whether an IP is located in a special-use network range +// This includes broadcast, multicast and documentation addresses. +func IsSpecialNetwork(ip net.IP) bool { + if ip.IsMulticast() { + return true + } + if v4 := ip.To4(); v4 != nil { + return special4.Contains(v4) + } + return special6.Contains(ip) +} + +var ( + errInvalid = errors.New("invalid IP") + errUnspecified = errors.New("zero address") + errSpecial = errors.New("special network") + errLoopback = errors.New("loopback address from non-loopback host") + errLAN = errors.New("LAN address from WAN host") +) + +// CheckRelayIP reports whether an IP relayed from the given sender IP +// is a valid connection target. +// +// There are four rules: +// - Special network addresses are never valid. +// - Loopback addresses are OK if relayed by a loopback host. +// - LAN addresses are OK if relayed by a LAN host. +// - All other addresses are always acceptable. +func CheckRelayIP(sender, addr net.IP) error { + if len(addr) != net.IPv4len && len(addr) != net.IPv6len { + return errInvalid + } + if addr.IsUnspecified() { + return errUnspecified + } + if IsSpecialNetwork(addr) { + return errSpecial + } + if addr.IsLoopback() && !sender.IsLoopback() { + return errLoopback + } + if IsLAN(addr) && !IsLAN(sender) { + return errLAN + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net_test.go new file mode 100644 index 0000000..1ee1fcb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/net_test.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package netutil + +import ( + "net" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestParseNetlist(t *testing.T) { + var tests = []struct { + input string + wantErr error + wantList *Netlist + }{ + { + input: "", + wantList: &Netlist{}, + }, + { + input: "127.0.0.0/8", + wantErr: nil, + wantList: &Netlist{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(8, 32)}}, + }, + { + input: "127.0.0.0/44", + wantErr: &net.ParseError{Type: "CIDR address", Text: "127.0.0.0/44"}, + }, + { + input: "127.0.0.0/16, 23.23.23.23/24,", + wantList: &Netlist{ + {IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(16, 32)}, + {IP: net.IP{23, 23, 23, 0}, Mask: net.CIDRMask(24, 32)}, + }, + }, + } + + for _, test := range tests { + l, err := ParseNetlist(test.input) + if !reflect.DeepEqual(err, test.wantErr) { + t.Errorf("%q: got error %q, want %q", test.input, err, test.wantErr) + continue + } + if !reflect.DeepEqual(l, test.wantList) { + spew.Dump(l) + spew.Dump(test.wantList) + t.Errorf("%q: got %v, want %v", test.input, l, test.wantList) + } + } +} + +func TestNilNetListContains(t *testing.T) { + var list *Netlist + checkContains(t, list.Contains, nil, []string{"1.2.3.4"}) +} + +func TestIsLAN(t *testing.T) { + checkContains(t, IsLAN, + []string{ // included + "0.0.0.0", + "0.2.0.8", + "127.0.0.1", + "10.0.1.1", + "10.22.0.3", + "172.31.252.251", + "192.168.1.4", + "fe80::f4a1:8eff:fec5:9d9d", + "febf::ab32:2233", + "fc00::4", + }, + []string{ // excluded + "192.0.2.1", + "1.0.0.0", + "172.32.0.1", + "fec0::2233", + }, + ) +} + +func TestIsSpecialNetwork(t *testing.T) { + checkContains(t, IsSpecialNetwork, + []string{ // included + "192.0.2.1", + "192.0.2.44", + "2001:db8:85a3:8d3:1319:8a2e:370:7348", + "255.255.255.255", + "224.0.0.22", // IPv4 multicast + "ff05::1:3", // IPv6 multicast + }, + []string{ // excluded + "192.0.3.1", + "1.0.0.0", + "172.32.0.1", + "fec0::2233", + }, + ) +} + +func checkContains(t *testing.T, fn func(net.IP) bool, inc, exc []string) { + for _, s := range inc { + if !fn(parseIP(s)) { + t.Error("returned false for included address", s) + } + } + for _, s := range exc { + if fn(parseIP(s)) { + t.Error("returned true for excluded address", s) + } + } +} + +func parseIP(s string) net.IP { + ip := net.ParseIP(s) + if ip == nil { + panic("invalid " + s) + } + return ip +} + +func TestCheckRelayIP(t *testing.T) { + tests := []struct { + sender, addr string + want error + }{ + {"127.0.0.1", "0.0.0.0", errUnspecified}, + {"192.168.0.1", "0.0.0.0", errUnspecified}, + {"23.55.1.242", "0.0.0.0", errUnspecified}, + {"127.0.0.1", "255.255.255.255", errSpecial}, + {"192.168.0.1", "255.255.255.255", errSpecial}, + {"23.55.1.242", "255.255.255.255", errSpecial}, + {"192.168.0.1", "127.0.2.19", errLoopback}, + {"23.55.1.242", "192.168.0.1", errLAN}, + + {"127.0.0.1", "127.0.2.19", nil}, + {"127.0.0.1", "192.168.0.1", nil}, + {"127.0.0.1", "23.55.1.242", nil}, + {"192.168.0.1", "192.168.0.1", nil}, + {"192.168.0.1", "23.55.1.242", nil}, + {"23.55.1.242", "23.55.1.242", nil}, + } + + for _, test := range tests { + err := CheckRelayIP(parseIP(test.sender), parseIP(test.addr)) + if err != test.want { + t.Errorf("%s from %s: got %q, want %q", test.addr, test.sender, err, test.want) + } + } +} + +func BenchmarkCheckRelayIP(b *testing.B) { + sender := parseIP("23.55.1.242") + addr := parseIP("23.55.1.2") + for i := 0; i < b.N; i++ { + CheckRelayIP(sender, addr) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go new file mode 100644 index 0000000..47b6438 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_notwindows.go @@ -0,0 +1,26 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build !windows + +package netutil + +// isPacketTooBig reports whether err indicates that a UDP packet didn't +// fit the receive buffer. There is no such error on +// non-Windows platforms. +func isPacketTooBig(err error) bool { + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go new file mode 100644 index 0000000..dfbb6d4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/netutil/toobig_windows.go @@ -0,0 +1,40 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//+build windows + +package netutil + +import ( + "net" + "os" + "syscall" +) + +const _WSAEMSGSIZE = syscall.Errno(10040) + +// isPacketTooBig reports whether err indicates that a UDP packet didn't +// fit the receive buffer. On Windows, WSARecvFrom returns +// code WSAEMSGSIZE and no data if this happens. +func isPacketTooBig(err error) bool { + if opErr, ok := err.(*net.OpError); ok { + if scErr, ok := opErr.Err.(*os.SyscallError); ok { + return scErr.Err == _WSAEMSGSIZE + } + return opErr.Err == _WSAEMSGSIZE + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/peer.go b/vendor/github.com/ethereum/go-ethereum/p2p/peer.go new file mode 100644 index 0000000..b9d6c09 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/peer.go @@ -0,0 +1,410 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "errors" + "fmt" + "io" + "net" + "sort" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + baseProtocolVersion = 4 + baseProtocolLength = uint64(16) + baseProtocolMaxMsgSize = 2 * 1024 + + pingInterval = 15 * time.Second +) + +const ( + // devp2p message codes + handshakeMsg = 0x00 + discMsg = 0x01 + pingMsg = 0x02 + pongMsg = 0x03 + getPeersMsg = 0x04 + peersMsg = 0x05 +) + +// protoHandshake is the RLP structure of the protocol handshake. +type protoHandshake struct { + Version uint64 + Name string + Caps []Cap + ListenPort uint64 + ID discover.NodeID + + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` +} + +// Peer represents a connected remote node. +type Peer struct { + rw *conn + running map[string]*protoRW + + wg sync.WaitGroup + protoErr chan error + closed chan struct{} + disc chan DiscReason +} + +// NewPeer returns a peer for testing purposes. +func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { + pipe, _ := net.Pipe() + conn := &conn{fd: pipe, transport: nil, id: id, caps: caps, name: name} + peer := newPeer(conn, nil) + close(peer.closed) // ensures Disconnect doesn't block + return peer +} + +// ID returns the node's public key. +func (p *Peer) ID() discover.NodeID { + return p.rw.id +} + +// Name returns the node name that the remote node advertised. +func (p *Peer) Name() string { + return p.rw.name +} + +// Caps returns the capabilities (supported subprotocols) of the remote peer. +func (p *Peer) Caps() []Cap { + // TODO: maybe return copy + return p.rw.caps +} + +// RemoteAddr returns the remote address of the network connection. +func (p *Peer) RemoteAddr() net.Addr { + return p.rw.fd.RemoteAddr() +} + +// LocalAddr returns the local address of the network connection. +func (p *Peer) LocalAddr() net.Addr { + return p.rw.fd.LocalAddr() +} + +// Disconnect terminates the peer connection with the given reason. +// It returns immediately and does not wait until the connection is closed. +func (p *Peer) Disconnect(reason DiscReason) { + select { + case p.disc <- reason: + case <-p.closed: + } +} + +// String implements fmt.Stringer. +func (p *Peer) String() string { + return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr()) +} + +func newPeer(conn *conn, protocols []Protocol) *Peer { + protomap := matchProtocols(protocols, conn.caps, conn) + p := &Peer{ + rw: conn, + running: protomap, + disc: make(chan DiscReason), + protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop + closed: make(chan struct{}), + } + return p +} + +func (p *Peer) run() DiscReason { + var ( + writeStart = make(chan struct{}, 1) + writeErr = make(chan error, 1) + readErr = make(chan error, 1) + reason DiscReason + requested bool + ) + p.wg.Add(2) + go p.readLoop(readErr) + go p.pingLoop() + + // Start all protocol handlers. + writeStart <- struct{}{} + p.startProtocols(writeStart, writeErr) + + // Wait for an error or disconnect. +loop: + for { + select { + case err := <-writeErr: + // A write finished. Allow the next write to start if + // there was no error. + if err != nil { + glog.V(logger.Detail).Infof("%v: write error: %v\n", p, err) + reason = DiscNetworkError + break loop + } + writeStart <- struct{}{} + case err := <-readErr: + if r, ok := err.(DiscReason); ok { + glog.V(logger.Debug).Infof("%v: remote requested disconnect: %v\n", p, r) + requested = true + reason = r + } else { + glog.V(logger.Detail).Infof("%v: read error: %v\n", p, err) + reason = DiscNetworkError + } + break loop + case err := <-p.protoErr: + reason = discReasonForError(err) + glog.V(logger.Debug).Infof("%v: protocol error: %v (%v)\n", p, err, reason) + break loop + case reason = <-p.disc: + glog.V(logger.Debug).Infof("%v: locally requested disconnect: %v\n", p, reason) + break loop + } + } + + close(p.closed) + p.rw.close(reason) + p.wg.Wait() + if requested { + reason = DiscRequested + } + return reason +} + +func (p *Peer) pingLoop() { + ping := time.NewTicker(pingInterval) + defer p.wg.Done() + defer ping.Stop() + for { + select { + case <-ping.C: + if err := SendItems(p.rw, pingMsg); err != nil { + p.protoErr <- err + return + } + case <-p.closed: + return + } + } +} + +func (p *Peer) readLoop(errc chan<- error) { + defer p.wg.Done() + for { + msg, err := p.rw.ReadMsg() + if err != nil { + errc <- err + return + } + msg.ReceivedAt = time.Now() + if err = p.handle(msg); err != nil { + errc <- err + return + } + } +} + +func (p *Peer) handle(msg Msg) error { + switch { + case msg.Code == pingMsg: + msg.Discard() + go SendItems(p.rw, pongMsg) + case msg.Code == discMsg: + var reason [1]DiscReason + // This is the last message. We don't need to discard or + // check errors because, the connection will be closed after it. + rlp.Decode(msg.Payload, &reason) + return reason[0] + case msg.Code < baseProtocolLength: + // ignore other base protocol messages + return msg.Discard() + default: + // it's a subprotocol message + proto, err := p.getProto(msg.Code) + if err != nil { + return fmt.Errorf("msg code out of range: %v", msg.Code) + } + select { + case proto.in <- msg: + return nil + case <-p.closed: + return io.EOF + } + } + return nil +} + +func countMatchingProtocols(protocols []Protocol, caps []Cap) int { + n := 0 + for _, cap := range caps { + for _, proto := range protocols { + if proto.Name == cap.Name && proto.Version == cap.Version { + n++ + } + } + } + return n +} + +// matchProtocols creates structures for matching named subprotocols. +func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { + sort.Sort(capsByNameAndVersion(caps)) + offset := baseProtocolLength + result := make(map[string]*protoRW) + +outer: + for _, cap := range caps { + for _, proto := range protocols { + if proto.Name == cap.Name && proto.Version == cap.Version { + // If an old protocol version matched, revert it + if old := result[cap.Name]; old != nil { + offset -= old.Length + } + // Assign the new match + result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} + offset += proto.Length + + continue outer + } + } + } + return result +} + +func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { + p.wg.Add(len(p.running)) + for _, proto := range p.running { + proto := proto + proto.closed = p.closed + proto.wstart = writeStart + proto.werr = writeErr + glog.V(logger.Detail).Infof("%v: Starting protocol %s/%d\n", p, proto.Name, proto.Version) + go func() { + err := proto.Run(p, proto) + if err == nil { + glog.V(logger.Detail).Infof("%v: Protocol %s/%d returned\n", p, proto.Name, proto.Version) + err = errors.New("protocol returned") + } else if err != io.EOF { + glog.V(logger.Detail).Infof("%v: Protocol %s/%d error: %v\n", p, proto.Name, proto.Version, err) + } + p.protoErr <- err + p.wg.Done() + }() + } +} + +// getProto finds the protocol responsible for handling +// the given message code. +func (p *Peer) getProto(code uint64) (*protoRW, error) { + for _, proto := range p.running { + if code >= proto.offset && code < proto.offset+proto.Length { + return proto, nil + } + } + return nil, newPeerError(errInvalidMsgCode, "%d", code) +} + +type protoRW struct { + Protocol + in chan Msg // receices read messages + closed <-chan struct{} // receives when peer is shutting down + wstart <-chan struct{} // receives when write may start + werr chan<- error // for write results + offset uint64 + w MsgWriter +} + +func (rw *protoRW) WriteMsg(msg Msg) (err error) { + if msg.Code >= rw.Length { + return newPeerError(errInvalidMsgCode, "not handled") + } + msg.Code += rw.offset + select { + case <-rw.wstart: + err = rw.w.WriteMsg(msg) + // Report write status back to Peer.run. It will initiate + // shutdown if the error is non-nil and unblock the next write + // otherwise. The calling protocol code should exit for errors + // as well but we don't want to rely on that. + rw.werr <- err + case <-rw.closed: + err = fmt.Errorf("shutting down") + } + return err +} + +func (rw *protoRW) ReadMsg() (Msg, error) { + select { + case msg := <-rw.in: + msg.Code -= rw.offset + return msg, nil + case <-rw.closed: + return Msg{}, io.EOF + } +} + +// PeerInfo represents a short summary of the information known about a connected +// peer. Sub-protocol independent fields are contained and initialized here, with +// protocol specifics delegated to all connected sub-protocols. +type PeerInfo struct { + ID string `json:"id"` // Unique node identifier (also the encryption key) + Name string `json:"name"` // Name of the node, including client type, version, OS, custom data + Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer + Network struct { + LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection + RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection + } `json:"network"` + Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields +} + +// Info gathers and returns a collection of metadata known about a peer. +func (p *Peer) Info() *PeerInfo { + // Gather the protocol capabilities + var caps []string + for _, cap := range p.Caps() { + caps = append(caps, cap.String()) + } + // Assemble the generic peer metadata + info := &PeerInfo{ + ID: p.ID().String(), + Name: p.Name(), + Caps: caps, + Protocols: make(map[string]interface{}), + } + info.Network.LocalAddress = p.LocalAddr().String() + info.Network.RemoteAddress = p.RemoteAddr().String() + + // Gather all the running protocol infos + for _, proto := range p.running { + protoInfo := interface{}("unknown") + if query := proto.Protocol.PeerInfo; query != nil { + if metadata := query(p.ID()); metadata != nil { + protoInfo = metadata + } else { + protoInfo = "handshake" + } + } + info.Protocols[proto.Name] = protoInfo + } + return info +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/peer_error.go b/vendor/github.com/ethereum/go-ethereum/p2p/peer_error.go new file mode 100644 index 0000000..62c7b66 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/peer_error.go @@ -0,0 +1,113 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "fmt" +) + +const ( + errInvalidMsgCode = iota + errInvalidMsg +) + +var errorToString = map[int]string{ + errInvalidMsgCode: "invalid message code", + errInvalidMsg: "invalid message", +} + +type peerError struct { + code int + message string +} + +func newPeerError(code int, format string, v ...interface{}) *peerError { + desc, ok := errorToString[code] + if !ok { + panic("invalid error code") + } + err := &peerError{code, desc} + if format != "" { + err.message += ": " + fmt.Sprintf(format, v...) + } + return err +} + +func (self *peerError) Error() string { + return self.message +} + +type DiscReason uint + +const ( + DiscRequested DiscReason = iota + DiscNetworkError + DiscProtocolError + DiscUselessPeer + DiscTooManyPeers + DiscAlreadyConnected + DiscIncompatibleVersion + DiscInvalidIdentity + DiscQuitting + DiscUnexpectedIdentity + DiscSelf + DiscReadTimeout + DiscSubprotocolError = 0x10 +) + +var discReasonToString = [...]string{ + DiscRequested: "Disconnect requested", + DiscNetworkError: "Network error", + DiscProtocolError: "Breach of protocol", + DiscUselessPeer: "Useless peer", + DiscTooManyPeers: "Too many peers", + DiscAlreadyConnected: "Already connected", + DiscIncompatibleVersion: "Incompatible P2P protocol version", + DiscInvalidIdentity: "Invalid node identity", + DiscQuitting: "Client quitting", + DiscUnexpectedIdentity: "Unexpected identity", + DiscSelf: "Connected to self", + DiscReadTimeout: "Read timeout", + DiscSubprotocolError: "Subprotocol error", +} + +func (d DiscReason) String() string { + if len(discReasonToString) < int(d) { + return fmt.Sprintf("Unknown Reason(%d)", d) + } + return discReasonToString[d] +} + +func (d DiscReason) Error() string { + return d.String() +} + +func discReasonForError(err error) DiscReason { + if reason, ok := err.(DiscReason); ok { + return reason + } + peerError, ok := err.(*peerError) + if ok { + switch peerError.code { + case errInvalidMsgCode, errInvalidMsg: + return DiscProtocolError + default: + return DiscSubprotocolError + } + } + return DiscSubprotocolError +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/peer_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/peer_test.go new file mode 100644 index 0000000..f44300b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/peer_test.go @@ -0,0 +1,309 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "errors" + "fmt" + "math/rand" + "net" + "reflect" + "testing" + "time" +) + +var discard = Protocol{ + Name: "discard", + Length: 1, + Run: func(p *Peer, rw MsgReadWriter) error { + for { + msg, err := rw.ReadMsg() + if err != nil { + return err + } + fmt.Printf("discarding %d\n", msg.Code) + if err = msg.Discard(); err != nil { + return err + } + } + }, +} + +func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan DiscReason) { + fd1, fd2 := net.Pipe() + c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)} + c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)} + for _, p := range protos { + c1.caps = append(c1.caps, p.cap()) + c2.caps = append(c2.caps, p.cap()) + } + + peer := newPeer(c1, protos) + errc := make(chan DiscReason, 1) + go func() { errc <- peer.run() }() + + closer := func() { c2.close(errors.New("close func called")) } + return closer, c2, peer, errc +} + +func TestPeerProtoReadMsg(t *testing.T) { + done := make(chan struct{}) + proto := Protocol{ + Name: "a", + Length: 5, + Run: func(peer *Peer, rw MsgReadWriter) error { + if err := ExpectMsg(rw, 2, []uint{1}); err != nil { + t.Error(err) + } + if err := ExpectMsg(rw, 3, []uint{2}); err != nil { + t.Error(err) + } + if err := ExpectMsg(rw, 4, []uint{3}); err != nil { + t.Error(err) + } + close(done) + return nil + }, + } + + closer, rw, _, errc := testPeer([]Protocol{proto}) + defer closer() + + Send(rw, baseProtocolLength+2, []uint{1}) + Send(rw, baseProtocolLength+3, []uint{2}) + Send(rw, baseProtocolLength+4, []uint{3}) + + select { + case <-done: + case err := <-errc: + t.Errorf("peer returned: %v", err) + case <-time.After(2 * time.Second): + t.Errorf("receive timeout") + } +} + +func TestPeerProtoEncodeMsg(t *testing.T) { + proto := Protocol{ + Name: "a", + Length: 2, + Run: func(peer *Peer, rw MsgReadWriter) error { + if err := SendItems(rw, 2); err == nil { + t.Error("expected error for out-of-range msg code, got nil") + } + if err := SendItems(rw, 1, "foo", "bar"); err != nil { + t.Errorf("write error: %v", err) + } + return nil + }, + } + closer, rw, _, _ := testPeer([]Protocol{proto}) + defer closer() + + if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { + t.Error(err) + } +} + +func TestPeerPing(t *testing.T) { + closer, rw, _, _ := testPeer(nil) + defer closer() + if err := SendItems(rw, pingMsg); err != nil { + t.Fatal(err) + } + if err := ExpectMsg(rw, pongMsg, nil); err != nil { + t.Error(err) + } +} + +func TestPeerDisconnect(t *testing.T) { + closer, rw, _, disc := testPeer(nil) + defer closer() + if err := SendItems(rw, discMsg, DiscQuitting); err != nil { + t.Fatal(err) + } + select { + case reason := <-disc: + if reason != DiscRequested { + t.Errorf("run returned wrong reason: got %v, want %v", reason, DiscRequested) + } + case <-time.After(500 * time.Millisecond): + t.Error("peer did not return") + } +} + +// This test is supposed to verify that Peer can reliably handle +// multiple causes of disconnection occurring at the same time. +func TestPeerDisconnectRace(t *testing.T) { + maybe := func() bool { return rand.Intn(1) == 1 } + + for i := 0; i < 1000; i++ { + protoclose := make(chan error) + protodisc := make(chan DiscReason) + closer, rw, p, disc := testPeer([]Protocol{ + { + Name: "closereq", + Run: func(p *Peer, rw MsgReadWriter) error { return <-protoclose }, + Length: 1, + }, + { + Name: "disconnect", + Run: func(p *Peer, rw MsgReadWriter) error { p.Disconnect(<-protodisc); return nil }, + Length: 1, + }, + }) + + // Simulate incoming messages. + go SendItems(rw, baseProtocolLength+1) + go SendItems(rw, baseProtocolLength+2) + // Close the network connection. + go closer() + // Make protocol "closereq" return. + protoclose <- errors.New("protocol closed") + // Make protocol "disconnect" call peer.Disconnect + protodisc <- DiscAlreadyConnected + // In some cases, simulate something else calling peer.Disconnect. + if maybe() { + go p.Disconnect(DiscInvalidIdentity) + } + // In some cases, simulate remote requesting a disconnect. + if maybe() { + go SendItems(rw, discMsg, DiscQuitting) + } + + select { + case <-disc: + case <-time.After(2 * time.Second): + // Peer.run should return quickly. If it doesn't the Peer + // goroutines are probably deadlocked. Call panic in order to + // show the stacks. + panic("Peer.run took to long to return.") + } + } +} + +func TestNewPeer(t *testing.T) { + name := "nodename" + caps := []Cap{{"foo", 2}, {"bar", 3}} + id := randomID() + p := NewPeer(id, name, caps) + if p.ID() != id { + t.Errorf("ID mismatch: got %v, expected %v", p.ID(), id) + } + if p.Name() != name { + t.Errorf("Name mismatch: got %v, expected %v", p.Name(), name) + } + if !reflect.DeepEqual(p.Caps(), caps) { + t.Errorf("Caps mismatch: got %v, expected %v", p.Caps(), caps) + } + + p.Disconnect(DiscAlreadyConnected) // Should not hang +} + +func TestMatchProtocols(t *testing.T) { + tests := []struct { + Remote []Cap + Local []Protocol + Match map[string]protoRW + }{ + { + // No remote capabilities + Local: []Protocol{{Name: "a"}}, + }, + { + // No local protocols + Remote: []Cap{{Name: "a"}}, + }, + { + // No mutual protocols + Remote: []Cap{{Name: "a"}}, + Local: []Protocol{{Name: "b"}}, + }, + { + // Some matches, some differences + Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, + Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, + Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, + }, + { + // Various alphabetical ordering + Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, + Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, + Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, + }, + { + // No mutual versions + Remote: []Cap{{Version: 1}}, + Local: []Protocol{{Version: 2}}, + }, + { + // Multiple versions, single common + Remote: []Cap{{Version: 1}, {Version: 2}}, + Local: []Protocol{{Version: 2}, {Version: 3}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, + }, + { + // Multiple versions, multiple common + Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, + Local: []Protocol{{Version: 2}, {Version: 3}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, + }, + { + // Various version orderings + Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, + Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, + }, + { + // Versions overriding sub-protocol lengths + Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, + Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, + }, + } + + for i, tt := range tests { + result := matchProtocols(tt.Local, tt.Remote, nil) + if len(result) != len(tt.Match) { + t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) + continue + } + // Make sure all negotiated protocols are needed and correct + for name, proto := range result { + match, ok := tt.Match[name] + if !ok { + t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) + continue + } + if proto.Name != match.Name { + t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) + } + if proto.Version != match.Version { + t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) + } + if proto.offset-baseProtocolLength != match.offset { + t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) + } + } + // Make sure no protocols missed negotiation + for name := range tt.Match { + if _, ok := result[name]; !ok { + t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) + continue + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go b/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go new file mode 100644 index 0000000..ee747ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go @@ -0,0 +1,81 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/p2p/discover" +) + +// Protocol represents a P2P subprotocol implementation. +type Protocol struct { + // Name should contain the official protocol name, + // often a three-letter word. + Name string + + // Version should contain the version number of the protocol. + Version uint + + // Length should contain the number of message codes used + // by the protocol. + Length uint64 + + // Run is called in a new groutine when the protocol has been + // negotiated with a peer. It should read and write messages from + // rw. The Payload for each message must be fully consumed. + // + // The peer connection is closed when Start returns. It should return + // any protocol-level error (such as an I/O error) that is + // encountered. + Run func(peer *Peer, rw MsgReadWriter) error + + // NodeInfo is an optional helper method to retrieve protocol specific metadata + // about the host node. + NodeInfo func() interface{} + + // PeerInfo is an optional helper method to retrieve protocol specific metadata + // about a certain peer in the network. If an info retrieval function is set, + // but returns nil, it is assumed that the protocol handshake is still running. + PeerInfo func(id discover.NodeID) interface{} +} + +func (p Protocol) cap() Cap { + return Cap{p.Name, p.Version} +} + +// Cap is the structure of a peer capability. +type Cap struct { + Name string + Version uint +} + +func (cap Cap) RlpData() interface{} { + return []interface{}{cap.Name, cap.Version} +} + +func (cap Cap) String() string { + return fmt.Sprintf("%s/%d", cap.Name, cap.Version) +} + +type capsByNameAndVersion []Cap + +func (cs capsByNameAndVersion) Len() int { return len(cs) } +func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } +func (cs capsByNameAndVersion) Less(i, j int) bool { + return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go new file mode 100644 index 0000000..b2775ca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go @@ -0,0 +1,694 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + mrand "math/rand" + "net" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + maxUint24 = ^uint32(0) >> 8 + + sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 + sigLen = 65 // elliptic S256 + pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte + shaLen = 32 // hash length (for nonce etc) + + authMsgLen = sigLen + shaLen + pubLen + shaLen + 1 + authRespLen = pubLen + shaLen + 1 + + eciesOverhead = 65 /* pubkey */ + 16 /* IV */ + 32 /* MAC */ + + encAuthMsgLen = authMsgLen + eciesOverhead // size of encrypted pre-EIP-8 initiator handshake + encAuthRespLen = authRespLen + eciesOverhead // size of encrypted pre-EIP-8 handshake reply + + // total timeout for encryption handshake and protocol + // handshake in both directions. + handshakeTimeout = 5 * time.Second + + // This is the timeout for sending the disconnect reason. + // This is shorter than the usual timeout because we don't want + // to wait if the connection is known to be bad anyway. + discWriteTimeout = 1 * time.Second +) + +// rlpx is the transport protocol used by actual (non-test) connections. +// It wraps the frame encoder with locks and read/write deadlines. +type rlpx struct { + fd net.Conn + + rmu, wmu sync.Mutex + rw *rlpxFrameRW +} + +func newRLPX(fd net.Conn) transport { + fd.SetDeadline(time.Now().Add(handshakeTimeout)) + return &rlpx{fd: fd} +} + +func (t *rlpx) ReadMsg() (Msg, error) { + t.rmu.Lock() + defer t.rmu.Unlock() + t.fd.SetReadDeadline(time.Now().Add(frameReadTimeout)) + return t.rw.ReadMsg() +} + +func (t *rlpx) WriteMsg(msg Msg) error { + t.wmu.Lock() + defer t.wmu.Unlock() + t.fd.SetWriteDeadline(time.Now().Add(frameWriteTimeout)) + return t.rw.WriteMsg(msg) +} + +func (t *rlpx) close(err error) { + t.wmu.Lock() + defer t.wmu.Unlock() + // Tell the remote end why we're disconnecting if possible. + if t.rw != nil { + if r, ok := err.(DiscReason); ok && r != DiscNetworkError { + t.fd.SetWriteDeadline(time.Now().Add(discWriteTimeout)) + SendItems(t.rw, discMsg, r) + } + } + t.fd.Close() +} + +// doEncHandshake runs the protocol handshake using authenticated +// messages. the protocol handshake is the first authenticated message +// and also verifies whether the encryption handshake 'worked' and the +// remote side actually provided the right public key. +func (t *rlpx) doProtoHandshake(our *protoHandshake) (their *protoHandshake, err error) { + // Writing our handshake happens concurrently, we prefer + // returning the handshake read error. If the remote side + // disconnects us early with a valid reason, we should return it + // as the error so it can be tracked elsewhere. + werr := make(chan error, 1) + go func() { werr <- Send(t.rw, handshakeMsg, our) }() + if their, err = readProtocolHandshake(t.rw, our); err != nil { + <-werr // make sure the write terminates too + return nil, err + } + if err := <-werr; err != nil { + return nil, fmt.Errorf("write error: %v", err) + } + return their, nil +} + +func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake, error) { + msg, err := rw.ReadMsg() + if err != nil { + return nil, err + } + if msg.Size > baseProtocolMaxMsgSize { + return nil, fmt.Errorf("message too big") + } + if msg.Code == discMsg { + // Disconnect before protocol handshake is valid according to the + // spec and we send it ourself if the posthanshake checks fail. + // We can't return the reason directly, though, because it is echoed + // back otherwise. Wrap it in a string instead. + var reason [1]DiscReason + rlp.Decode(msg.Payload, &reason) + return nil, reason[0] + } + if msg.Code != handshakeMsg { + return nil, fmt.Errorf("expected handshake, got %x", msg.Code) + } + var hs protoHandshake + if err := msg.Decode(&hs); err != nil { + return nil, err + } + if (hs.ID == discover.NodeID{}) { + return nil, DiscInvalidIdentity + } + return &hs, nil +} + +func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (discover.NodeID, error) { + var ( + sec secrets + err error + ) + if dial == nil { + sec, err = receiverEncHandshake(t.fd, prv, nil) + } else { + sec, err = initiatorEncHandshake(t.fd, prv, dial.ID, nil) + } + if err != nil { + return discover.NodeID{}, err + } + t.wmu.Lock() + t.rw = newRLPXFrameRW(t.fd, sec) + t.wmu.Unlock() + return sec.RemoteID, nil +} + +// encHandshake contains the state of the encryption handshake. +type encHandshake struct { + initiator bool + remoteID discover.NodeID + + remotePub *ecies.PublicKey // remote-pubk + initNonce, respNonce []byte // nonce + randomPrivKey *ecies.PrivateKey // ecdhe-random + remoteRandomPub *ecies.PublicKey // ecdhe-random-pubk +} + +// secrets represents the connection secrets +// which are negotiated during the encryption handshake. +type secrets struct { + RemoteID discover.NodeID + AES, MAC []byte + EgressMAC, IngressMAC hash.Hash + Token []byte +} + +// RLPx v4 handshake auth (defined in EIP-8). +type authMsgV4 struct { + gotPlain bool // whether read packet had plain format. + + Signature [sigLen]byte + InitiatorPubkey [pubLen]byte + Nonce [shaLen]byte + Version uint + + // Ignore additional fields (forward-compatibility) + Rest []rlp.RawValue `rlp:"tail"` +} + +// RLPx v4 handshake response (defined in EIP-8). +type authRespV4 struct { + RandomPubkey [pubLen]byte + Nonce [shaLen]byte + Version uint + + // Ignore additional fields (forward-compatibility) + Rest []rlp.RawValue `rlp:"tail"` +} + +// secrets is called after the handshake is completed. +// It extracts the connection secrets from the handshake values. +func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) { + ecdheSecret, err := h.randomPrivKey.GenerateShared(h.remoteRandomPub, sskLen, sskLen) + if err != nil { + return secrets{}, err + } + + // derive base secrets from ephemeral key agreement + sharedSecret := crypto.Keccak256(ecdheSecret, crypto.Keccak256(h.respNonce, h.initNonce)) + aesSecret := crypto.Keccak256(ecdheSecret, sharedSecret) + s := secrets{ + RemoteID: h.remoteID, + AES: aesSecret, + MAC: crypto.Keccak256(ecdheSecret, aesSecret), + } + + // setup sha3 instances for the MACs + mac1 := sha3.NewKeccak256() + mac1.Write(xor(s.MAC, h.respNonce)) + mac1.Write(auth) + mac2 := sha3.NewKeccak256() + mac2.Write(xor(s.MAC, h.initNonce)) + mac2.Write(authResp) + if h.initiator { + s.EgressMAC, s.IngressMAC = mac1, mac2 + } else { + s.EgressMAC, s.IngressMAC = mac2, mac1 + } + + return s, nil +} + +// staticSharedSecret returns the static shared secret, the result +// of key agreement between the local and remote static node key. +func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) { + return ecies.ImportECDSA(prv).GenerateShared(h.remotePub, sskLen, sskLen) +} + +// initiatorEncHandshake negotiates a session token on conn. +// it should be called on the dialing side of the connection. +// +// prv is the local client's private key. +func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID, token []byte) (s secrets, err error) { + h := &encHandshake{initiator: true, remoteID: remoteID} + authMsg, err := h.makeAuthMsg(prv, token) + if err != nil { + return s, err + } + authPacket, err := sealEIP8(authMsg, h) + if err != nil { + return s, err + } + if _, err = conn.Write(authPacket); err != nil { + return s, err + } + + authRespMsg := new(authRespV4) + authRespPacket, err := readHandshakeMsg(authRespMsg, encAuthRespLen, prv, conn) + if err != nil { + return s, err + } + if err := h.handleAuthResp(authRespMsg); err != nil { + return s, err + } + return h.secrets(authPacket, authRespPacket) +} + +// makeAuthMsg creates the initiator handshake message. +func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMsgV4, error) { + rpub, err := h.remoteID.Pubkey() + if err != nil { + return nil, fmt.Errorf("bad remoteID: %v", err) + } + h.remotePub = ecies.ImportECDSAPublic(rpub) + // Generate random initiator nonce. + h.initNonce = make([]byte, shaLen) + if _, err := rand.Read(h.initNonce); err != nil { + return nil, err + } + // Generate random keypair to for ECDH. + h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) + if err != nil { + return nil, err + } + + // Sign known message: static-shared-secret ^ nonce + token, err = h.staticSharedSecret(prv) + if err != nil { + return nil, err + } + signed := xor(token, h.initNonce) + signature, err := crypto.Sign(signed, h.randomPrivKey.ExportECDSA()) + if err != nil { + return nil, err + } + + msg := new(authMsgV4) + copy(msg.Signature[:], signature) + copy(msg.InitiatorPubkey[:], crypto.FromECDSAPub(&prv.PublicKey)[1:]) + copy(msg.Nonce[:], h.initNonce) + msg.Version = 4 + return msg, nil +} + +func (h *encHandshake) handleAuthResp(msg *authRespV4) (err error) { + h.respNonce = msg.Nonce[:] + h.remoteRandomPub, err = importPublicKey(msg.RandomPubkey[:]) + return err +} + +// receiverEncHandshake negotiates a session token on conn. +// it should be called on the listening side of the connection. +// +// prv is the local client's private key. +// token is the token from a previous session with this node. +func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, token []byte) (s secrets, err error) { + authMsg := new(authMsgV4) + authPacket, err := readHandshakeMsg(authMsg, encAuthMsgLen, prv, conn) + if err != nil { + return s, err + } + h := new(encHandshake) + if err := h.handleAuthMsg(authMsg, prv); err != nil { + return s, err + } + + authRespMsg, err := h.makeAuthResp() + if err != nil { + return s, err + } + var authRespPacket []byte + if authMsg.gotPlain { + authRespPacket, err = authRespMsg.sealPlain(h) + } else { + authRespPacket, err = sealEIP8(authRespMsg, h) + } + if err != nil { + return s, err + } + if _, err = conn.Write(authRespPacket); err != nil { + return s, err + } + return h.secrets(authPacket, authRespPacket) +} + +func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error { + // Import the remote identity. + h.initNonce = msg.Nonce[:] + h.remoteID = msg.InitiatorPubkey + rpub, err := h.remoteID.Pubkey() + if err != nil { + return fmt.Errorf("bad remoteID: %#v", err) + } + h.remotePub = ecies.ImportECDSAPublic(rpub) + + // Generate random keypair for ECDH. + // If a private key is already set, use it instead of generating one (for testing). + if h.randomPrivKey == nil { + h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) + if err != nil { + return err + } + } + + // Check the signature. + token, err := h.staticSharedSecret(prv) + if err != nil { + return err + } + signedMsg := xor(token, h.initNonce) + remoteRandomPub, err := secp256k1.RecoverPubkey(signedMsg, msg.Signature[:]) + if err != nil { + return err + } + h.remoteRandomPub, _ = importPublicKey(remoteRandomPub) + return nil +} + +func (h *encHandshake) makeAuthResp() (msg *authRespV4, err error) { + // Generate random nonce. + h.respNonce = make([]byte, shaLen) + if _, err = rand.Read(h.respNonce); err != nil { + return nil, err + } + + msg = new(authRespV4) + copy(msg.Nonce[:], h.respNonce) + copy(msg.RandomPubkey[:], exportPubkey(&h.randomPrivKey.PublicKey)) + msg.Version = 4 + return msg, nil +} + +func (msg *authMsgV4) sealPlain(h *encHandshake) ([]byte, error) { + buf := make([]byte, authMsgLen) + n := copy(buf, msg.Signature[:]) + n += copy(buf[n:], crypto.Keccak256(exportPubkey(&h.randomPrivKey.PublicKey))) + n += copy(buf[n:], msg.InitiatorPubkey[:]) + n += copy(buf[n:], msg.Nonce[:]) + buf[n] = 0 // token-flag + return ecies.Encrypt(rand.Reader, h.remotePub, buf, nil, nil) +} + +func (msg *authMsgV4) decodePlain(input []byte) { + n := copy(msg.Signature[:], input) + n += shaLen // skip sha3(initiator-ephemeral-pubk) + n += copy(msg.InitiatorPubkey[:], input[n:]) + copy(msg.Nonce[:], input[n:]) + msg.Version = 4 + msg.gotPlain = true +} + +func (msg *authRespV4) sealPlain(hs *encHandshake) ([]byte, error) { + buf := make([]byte, authRespLen) + n := copy(buf, msg.RandomPubkey[:]) + copy(buf[n:], msg.Nonce[:]) + return ecies.Encrypt(rand.Reader, hs.remotePub, buf, nil, nil) +} + +func (msg *authRespV4) decodePlain(input []byte) { + n := copy(msg.RandomPubkey[:], input) + copy(msg.Nonce[:], input[n:]) + msg.Version = 4 +} + +var padSpace = make([]byte, 300) + +func sealEIP8(msg interface{}, h *encHandshake) ([]byte, error) { + buf := new(bytes.Buffer) + if err := rlp.Encode(buf, msg); err != nil { + return nil, err + } + // pad with random amount of data. the amount needs to be at least 100 bytes to make + // the message distinguishable from pre-EIP-8 handshakes. + pad := padSpace[:mrand.Intn(len(padSpace)-100)+100] + buf.Write(pad) + prefix := make([]byte, 2) + binary.BigEndian.PutUint16(prefix, uint16(buf.Len()+eciesOverhead)) + + enc, err := ecies.Encrypt(rand.Reader, h.remotePub, buf.Bytes(), nil, prefix) + return append(prefix, enc...), err +} + +type plainDecoder interface { + decodePlain([]byte) +} + +func readHandshakeMsg(msg plainDecoder, plainSize int, prv *ecdsa.PrivateKey, r io.Reader) ([]byte, error) { + buf := make([]byte, plainSize) + if _, err := io.ReadFull(r, buf); err != nil { + return buf, err + } + // Attempt decoding pre-EIP-8 "plain" format. + key := ecies.ImportECDSA(prv) + if dec, err := key.Decrypt(rand.Reader, buf, nil, nil); err == nil { + msg.decodePlain(dec) + return buf, nil + } + // Could be EIP-8 format, try that. + prefix := buf[:2] + size := binary.BigEndian.Uint16(prefix) + if size < uint16(plainSize) { + return buf, fmt.Errorf("size underflow, need at least %d bytes", plainSize) + } + buf = append(buf, make([]byte, size-uint16(plainSize)+2)...) + if _, err := io.ReadFull(r, buf[plainSize:]); err != nil { + return buf, err + } + dec, err := key.Decrypt(rand.Reader, buf[2:], nil, prefix) + if err != nil { + return buf, err + } + // Can't use rlp.DecodeBytes here because it rejects + // trailing data (forward-compatibility). + s := rlp.NewStream(bytes.NewReader(dec), 0) + return buf, s.Decode(msg) +} + +// importPublicKey unmarshals 512 bit public keys. +func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) { + var pubKey65 []byte + switch len(pubKey) { + case 64: + // add 'uncompressed key' flag + pubKey65 = append([]byte{0x04}, pubKey...) + case 65: + pubKey65 = pubKey + default: + return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey)) + } + // TODO: fewer pointless conversions + pub := crypto.ToECDSAPub(pubKey65) + if pub.X == nil { + return nil, fmt.Errorf("invalid public key") + } + return ecies.ImportECDSAPublic(pub), nil +} + +func exportPubkey(pub *ecies.PublicKey) []byte { + if pub == nil { + panic("nil pubkey") + } + return elliptic.Marshal(pub.Curve, pub.X, pub.Y)[1:] +} + +func xor(one, other []byte) (xor []byte) { + xor = make([]byte, len(one)) + for i := 0; i < len(one); i++ { + xor[i] = one[i] ^ other[i] + } + return xor +} + +var ( + // this is used in place of actual frame header data. + // TODO: replace this when Msg contains the protocol type code. + zeroHeader = []byte{0xC2, 0x80, 0x80} + // sixteen zero bytes + zero16 = make([]byte, 16) +) + +// rlpxFrameRW implements a simplified version of RLPx framing. +// chunked messages are not supported and all headers are equal to +// zeroHeader. +// +// rlpxFrameRW is not safe for concurrent use from multiple goroutines. +type rlpxFrameRW struct { + conn io.ReadWriter + enc cipher.Stream + dec cipher.Stream + + macCipher cipher.Block + egressMAC hash.Hash + ingressMAC hash.Hash +} + +func newRLPXFrameRW(conn io.ReadWriter, s secrets) *rlpxFrameRW { + macc, err := aes.NewCipher(s.MAC) + if err != nil { + panic("invalid MAC secret: " + err.Error()) + } + encc, err := aes.NewCipher(s.AES) + if err != nil { + panic("invalid AES secret: " + err.Error()) + } + // we use an all-zeroes IV for AES because the key used + // for encryption is ephemeral. + iv := make([]byte, encc.BlockSize()) + return &rlpxFrameRW{ + conn: conn, + enc: cipher.NewCTR(encc, iv), + dec: cipher.NewCTR(encc, iv), + macCipher: macc, + egressMAC: s.EgressMAC, + ingressMAC: s.IngressMAC, + } +} + +func (rw *rlpxFrameRW) WriteMsg(msg Msg) error { + ptype, _ := rlp.EncodeToBytes(msg.Code) + + // write header + headbuf := make([]byte, 32) + fsize := uint32(len(ptype)) + msg.Size + if fsize > maxUint24 { + return errors.New("message size overflows uint24") + } + putInt24(fsize, headbuf) // TODO: check overflow + copy(headbuf[3:], zeroHeader) + rw.enc.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now encrypted + + // write header MAC + copy(headbuf[16:], updateMAC(rw.egressMAC, rw.macCipher, headbuf[:16])) + if _, err := rw.conn.Write(headbuf); err != nil { + return err + } + + // write encrypted frame, updating the egress MAC hash with + // the data written to conn. + tee := cipher.StreamWriter{S: rw.enc, W: io.MultiWriter(rw.conn, rw.egressMAC)} + if _, err := tee.Write(ptype); err != nil { + return err + } + if _, err := io.Copy(tee, msg.Payload); err != nil { + return err + } + if padding := fsize % 16; padding > 0 { + if _, err := tee.Write(zero16[:16-padding]); err != nil { + return err + } + } + + // write frame MAC. egress MAC hash is up to date because + // frame content was written to it as well. + fmacseed := rw.egressMAC.Sum(nil) + mac := updateMAC(rw.egressMAC, rw.macCipher, fmacseed) + _, err := rw.conn.Write(mac) + return err +} + +func (rw *rlpxFrameRW) ReadMsg() (msg Msg, err error) { + // read the header + headbuf := make([]byte, 32) + if _, err := io.ReadFull(rw.conn, headbuf); err != nil { + return msg, err + } + // verify header mac + shouldMAC := updateMAC(rw.ingressMAC, rw.macCipher, headbuf[:16]) + if !hmac.Equal(shouldMAC, headbuf[16:]) { + return msg, errors.New("bad header MAC") + } + rw.dec.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now decrypted + fsize := readInt24(headbuf) + // ignore protocol type for now + + // read the frame content + var rsize = fsize // frame size rounded up to 16 byte boundary + if padding := fsize % 16; padding > 0 { + rsize += 16 - padding + } + framebuf := make([]byte, rsize) + if _, err := io.ReadFull(rw.conn, framebuf); err != nil { + return msg, err + } + + // read and validate frame MAC. we can re-use headbuf for that. + rw.ingressMAC.Write(framebuf) + fmacseed := rw.ingressMAC.Sum(nil) + if _, err := io.ReadFull(rw.conn, headbuf[:16]); err != nil { + return msg, err + } + shouldMAC = updateMAC(rw.ingressMAC, rw.macCipher, fmacseed) + if !hmac.Equal(shouldMAC, headbuf[:16]) { + return msg, errors.New("bad frame MAC") + } + + // decrypt frame content + rw.dec.XORKeyStream(framebuf, framebuf) + + // decode message code + content := bytes.NewReader(framebuf[:fsize]) + if err := rlp.Decode(content, &msg.Code); err != nil { + return msg, err + } + msg.Size = uint32(content.Len()) + msg.Payload = content + return msg, nil +} + +// updateMAC reseeds the given hash with encrypted seed. +// it returns the first 16 bytes of the hash sum after seeding. +func updateMAC(mac hash.Hash, block cipher.Block, seed []byte) []byte { + aesbuf := make([]byte, aes.BlockSize) + block.Encrypt(aesbuf, mac.Sum(nil)) + for i := range aesbuf { + aesbuf[i] ^= seed[i] + } + mac.Write(aesbuf) + return mac.Sum(nil)[:16] +} + +func readInt24(b []byte) uint32 { + return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16 +} + +func putInt24(v uint32, b []byte) { + b[0] = byte(v >> 16) + b[1] = byte(v >> 8) + b[2] = byte(v) +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/rlpx_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx_test.go new file mode 100644 index 0000000..f4cefa6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx_test.go @@ -0,0 +1,599 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "reflect" + "strings" + "sync" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rlp" +) + +func TestSharedSecret(t *testing.T) { + prv0, _ := crypto.GenerateKey() // = ecdsa.GenerateKey(crypto.S256(), rand.Reader) + pub0 := &prv0.PublicKey + prv1, _ := crypto.GenerateKey() + pub1 := &prv1.PublicKey + + ss0, err := ecies.ImportECDSA(prv0).GenerateShared(ecies.ImportECDSAPublic(pub1), sskLen, sskLen) + if err != nil { + return + } + ss1, err := ecies.ImportECDSA(prv1).GenerateShared(ecies.ImportECDSAPublic(pub0), sskLen, sskLen) + if err != nil { + return + } + t.Logf("Secret:\n%v %x\n%v %x", len(ss0), ss0, len(ss0), ss1) + if !bytes.Equal(ss0, ss1) { + t.Errorf("dont match :(") + } +} + +func TestEncHandshake(t *testing.T) { + for i := 0; i < 10; i++ { + start := time.Now() + if err := testEncHandshake(nil); err != nil { + t.Fatalf("i=%d %v", i, err) + } + t.Logf("(without token) %d %v\n", i+1, time.Since(start)) + } + for i := 0; i < 10; i++ { + tok := make([]byte, shaLen) + rand.Reader.Read(tok) + start := time.Now() + if err := testEncHandshake(tok); err != nil { + t.Fatalf("i=%d %v", i, err) + } + t.Logf("(with token) %d %v\n", i+1, time.Since(start)) + } +} + +func testEncHandshake(token []byte) error { + type result struct { + side string + id discover.NodeID + err error + } + var ( + prv0, _ = crypto.GenerateKey() + prv1, _ = crypto.GenerateKey() + fd0, fd1 = net.Pipe() + c0, c1 = newRLPX(fd0).(*rlpx), newRLPX(fd1).(*rlpx) + output = make(chan result) + ) + + go func() { + r := result{side: "initiator"} + defer func() { output <- r }() + defer fd0.Close() + + dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)} + r.id, r.err = c0.doEncHandshake(prv0, dest) + if r.err != nil { + return + } + id1 := discover.PubkeyID(&prv1.PublicKey) + if r.id != id1 { + r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id1) + } + }() + go func() { + r := result{side: "receiver"} + defer func() { output <- r }() + defer fd1.Close() + + r.id, r.err = c1.doEncHandshake(prv1, nil) + if r.err != nil { + return + } + id0 := discover.PubkeyID(&prv0.PublicKey) + if r.id != id0 { + r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id0) + } + }() + + // wait for results from both sides + r1, r2 := <-output, <-output + if r1.err != nil { + return fmt.Errorf("%s side error: %v", r1.side, r1.err) + } + if r2.err != nil { + return fmt.Errorf("%s side error: %v", r2.side, r2.err) + } + + // compare derived secrets + if !reflect.DeepEqual(c0.rw.egressMAC, c1.rw.ingressMAC) { + return fmt.Errorf("egress mac mismatch:\n c0.rw: %#v\n c1.rw: %#v", c0.rw.egressMAC, c1.rw.ingressMAC) + } + if !reflect.DeepEqual(c0.rw.ingressMAC, c1.rw.egressMAC) { + return fmt.Errorf("ingress mac mismatch:\n c0.rw: %#v\n c1.rw: %#v", c0.rw.ingressMAC, c1.rw.egressMAC) + } + if !reflect.DeepEqual(c0.rw.enc, c1.rw.enc) { + return fmt.Errorf("enc cipher mismatch:\n c0.rw: %#v\n c1.rw: %#v", c0.rw.enc, c1.rw.enc) + } + if !reflect.DeepEqual(c0.rw.dec, c1.rw.dec) { + return fmt.Errorf("dec cipher mismatch:\n c0.rw: %#v\n c1.rw: %#v", c0.rw.dec, c1.rw.dec) + } + return nil +} + +func TestProtocolHandshake(t *testing.T) { + var ( + prv0, _ = crypto.GenerateKey() + node0 = &discover.Node{ID: discover.PubkeyID(&prv0.PublicKey), IP: net.IP{1, 2, 3, 4}, TCP: 33} + hs0 = &protoHandshake{Version: 3, ID: node0.ID, Caps: []Cap{{"a", 0}, {"b", 2}}} + + prv1, _ = crypto.GenerateKey() + node1 = &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey), IP: net.IP{5, 6, 7, 8}, TCP: 44} + hs1 = &protoHandshake{Version: 3, ID: node1.ID, Caps: []Cap{{"c", 1}, {"d", 3}}} + + fd0, fd1 = net.Pipe() + wg sync.WaitGroup + ) + + wg.Add(2) + go func() { + defer wg.Done() + defer fd1.Close() + rlpx := newRLPX(fd0) + remid, err := rlpx.doEncHandshake(prv0, node1) + if err != nil { + t.Errorf("dial side enc handshake failed: %v", err) + return + } + if remid != node1.ID { + t.Errorf("dial side remote id mismatch: got %v, want %v", remid, node1.ID) + return + } + + phs, err := rlpx.doProtoHandshake(hs0) + if err != nil { + t.Errorf("dial side proto handshake error: %v", err) + return + } + phs.Rest = nil + if !reflect.DeepEqual(phs, hs1) { + t.Errorf("dial side proto handshake mismatch:\ngot: %s\nwant: %s\n", spew.Sdump(phs), spew.Sdump(hs1)) + return + } + rlpx.close(DiscQuitting) + }() + go func() { + defer wg.Done() + defer fd1.Close() + rlpx := newRLPX(fd1) + remid, err := rlpx.doEncHandshake(prv1, nil) + if err != nil { + t.Errorf("listen side enc handshake failed: %v", err) + return + } + if remid != node0.ID { + t.Errorf("listen side remote id mismatch: got %v, want %v", remid, node0.ID) + return + } + + phs, err := rlpx.doProtoHandshake(hs1) + if err != nil { + t.Errorf("listen side proto handshake error: %v", err) + return + } + phs.Rest = nil + if !reflect.DeepEqual(phs, hs0) { + t.Errorf("listen side proto handshake mismatch:\ngot: %s\nwant: %s\n", spew.Sdump(phs), spew.Sdump(hs0)) + return + } + + if err := ExpectMsg(rlpx, discMsg, []DiscReason{DiscQuitting}); err != nil { + t.Errorf("error receiving disconnect: %v", err) + } + }() + wg.Wait() +} + +func TestProtocolHandshakeErrors(t *testing.T) { + our := &protoHandshake{Version: 3, Caps: []Cap{{"foo", 2}, {"bar", 3}}, Name: "quux"} + tests := []struct { + code uint64 + msg interface{} + err error + }{ + { + code: discMsg, + msg: []DiscReason{DiscQuitting}, + err: DiscQuitting, + }, + { + code: 0x989898, + msg: []byte{1}, + err: errors.New("expected handshake, got 989898"), + }, + { + code: handshakeMsg, + msg: make([]byte, baseProtocolMaxMsgSize+2), + err: errors.New("message too big"), + }, + { + code: handshakeMsg, + msg: []byte{1, 2, 3}, + err: newPeerError(errInvalidMsg, "(code 0) (size 4) rlp: expected input list for p2p.protoHandshake"), + }, + { + code: handshakeMsg, + msg: &protoHandshake{Version: 3}, + err: DiscInvalidIdentity, + }, + } + + for i, test := range tests { + p1, p2 := MsgPipe() + go Send(p1, test.code, test.msg) + _, err := readProtocolHandshake(p2, our) + if !reflect.DeepEqual(err, test.err) { + t.Errorf("test %d: error mismatch: got %q, want %q", i, err, test.err) + } + } +} + +func TestRLPXFrameFake(t *testing.T) { + buf := new(bytes.Buffer) + hash := fakeHash([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) + rw := newRLPXFrameRW(buf, secrets{ + AES: crypto.Keccak256(), + MAC: crypto.Keccak256(), + IngressMAC: hash, + EgressMAC: hash, + }) + + golden := unhex(` +00828ddae471818bb0bfa6b551d1cb42 +01010101010101010101010101010101 +ba628a4ba590cb43f7848f41c4382885 +01010101010101010101010101010101 +`) + + // Check WriteMsg. This puts a message into the buffer. + if err := Send(rw, 8, []uint{1, 2, 3, 4}); err != nil { + t.Fatalf("WriteMsg error: %v", err) + } + written := buf.Bytes() + if !bytes.Equal(written, golden) { + t.Fatalf("output mismatch:\n got: %x\n want: %x", written, golden) + } + + // Check ReadMsg. It reads the message encoded by WriteMsg, which + // is equivalent to the golden message above. + msg, err := rw.ReadMsg() + if err != nil { + t.Fatalf("ReadMsg error: %v", err) + } + if msg.Size != 5 { + t.Errorf("msg size mismatch: got %d, want %d", msg.Size, 5) + } + if msg.Code != 8 { + t.Errorf("msg code mismatch: got %d, want %d", msg.Code, 8) + } + payload, _ := ioutil.ReadAll(msg.Payload) + wantPayload := unhex("C401020304") + if !bytes.Equal(payload, wantPayload) { + t.Errorf("msg payload mismatch:\ngot %x\nwant %x", payload, wantPayload) + } +} + +type fakeHash []byte + +func (fakeHash) Write(p []byte) (int, error) { return len(p), nil } +func (fakeHash) Reset() {} +func (fakeHash) BlockSize() int { return 0 } + +func (h fakeHash) Size() int { return len(h) } +func (h fakeHash) Sum(b []byte) []byte { return append(b, h...) } + +func TestRLPXFrameRW(t *testing.T) { + var ( + aesSecret = make([]byte, 16) + macSecret = make([]byte, 16) + egressMACinit = make([]byte, 32) + ingressMACinit = make([]byte, 32) + ) + for _, s := range [][]byte{aesSecret, macSecret, egressMACinit, ingressMACinit} { + rand.Read(s) + } + conn := new(bytes.Buffer) + + s1 := secrets{ + AES: aesSecret, + MAC: macSecret, + EgressMAC: sha3.NewKeccak256(), + IngressMAC: sha3.NewKeccak256(), + } + s1.EgressMAC.Write(egressMACinit) + s1.IngressMAC.Write(ingressMACinit) + rw1 := newRLPXFrameRW(conn, s1) + + s2 := secrets{ + AES: aesSecret, + MAC: macSecret, + EgressMAC: sha3.NewKeccak256(), + IngressMAC: sha3.NewKeccak256(), + } + s2.EgressMAC.Write(ingressMACinit) + s2.IngressMAC.Write(egressMACinit) + rw2 := newRLPXFrameRW(conn, s2) + + // send some messages + for i := 0; i < 10; i++ { + // write message into conn buffer + wmsg := []interface{}{"foo", "bar", strings.Repeat("test", i)} + err := Send(rw1, uint64(i), wmsg) + if err != nil { + t.Fatalf("WriteMsg error (i=%d): %v", i, err) + } + + // read message that rw1 just wrote + msg, err := rw2.ReadMsg() + if err != nil { + t.Fatalf("ReadMsg error (i=%d): %v", i, err) + } + if msg.Code != uint64(i) { + t.Fatalf("msg code mismatch: got %d, want %d", msg.Code, i) + } + payload, _ := ioutil.ReadAll(msg.Payload) + wantPayload, _ := rlp.EncodeToBytes(wmsg) + if !bytes.Equal(payload, wantPayload) { + t.Fatalf("msg payload mismatch:\ngot %x\nwant %x", payload, wantPayload) + } + } +} + +type handshakeAuthTest struct { + input string + isPlain bool + wantVersion uint + wantRest []rlp.RawValue +} + +var eip8HandshakeAuthTests = []handshakeAuthTest{ + // (Auth₁) RLPx v4 plain encoding + { + input: ` + 048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf + 913150cfc777ce0ce4af2758bf4810235f6e6ceccfee1acc6b22c005e9e3a49d6448610a58e98744 + ba3ac0399e82692d67c1f58849050b3024e21a52c9d3b01d871ff5f210817912773e610443a9ef14 + 2e91cdba0bd77b5fdf0769b05671fc35f83d83e4d3b0b000c6b2a1b1bba89e0fc51bf4e460df3105 + c444f14be226458940d6061c296350937ffd5e3acaceeaaefd3c6f74be8e23e0f45163cc7ebd7622 + 0f0128410fd05250273156d548a414444ae2f7dea4dfca2d43c057adb701a715bf59f6fb66b2d1d2 + 0f2c703f851cbf5ac47396d9ca65b6260bd141ac4d53e2de585a73d1750780db4c9ee4cd4d225173 + a4592ee77e2bd94d0be3691f3b406f9bba9b591fc63facc016bfa8 + `, + isPlain: true, + wantVersion: 4, + }, + // (Auth₂) EIP-8 encoding + { + input: ` + 01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b + 0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84 + 9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c + da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc + 147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6 + d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee + 70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09 + c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3 + 6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e + 2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c + 3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c + `, + wantVersion: 4, + wantRest: []rlp.RawValue{}, + }, + // (Auth₃) RLPx v4 EIP-8 encoding with version 56, additional list elements + { + input: ` + 01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7 + 2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf + 280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb + f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b + cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352 + bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19 + 6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757 + 1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15 + 116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740 + 7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2 + f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6 + d490 + `, + wantVersion: 56, + wantRest: []rlp.RawValue{{0x01}, {0x02}, {0xC2, 0x04, 0x05}}, + }, +} + +type handshakeAckTest struct { + input string + wantVersion uint + wantRest []rlp.RawValue +} + +var eip8HandshakeRespTests = []handshakeAckTest{ + // (Ack₁) RLPx v4 plain encoding + { + input: ` + 049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662 + b0ff2c08e9006d5a11a278b1b3331e5aaabf0a32f01281b6f4ede0e09a2d5f585b26513cb794d963 + 5a57563921c04a9090b4f14ee42be1a5461049af4ea7a7f49bf4c97a352d39c8d02ee4acc416388c + 1c66cec761d2bc1c72da6ba143477f049c9d2dde846c252c111b904f630ac98e51609b3b1f58168d + dca6505b7196532e5f85b259a20c45e1979491683fee108e9660edbf38f3add489ae73e3dda2c71b + d1497113d5c755e942d1 + `, + wantVersion: 4, + }, + // (Ack₂) EIP-8 encoding + { + input: ` + 01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470 + b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de + 05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814 + c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171 + ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f + 6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb + e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d + 3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b + 201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8 + 797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac + 8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7 + 1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7 + 5833c2464c805246155289f4 + `, + wantVersion: 4, + wantRest: []rlp.RawValue{}, + }, + // (Ack₃) EIP-8 encoding with version 57, additional list elements + { + input: ` + 01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7 + ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0 + 3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d + dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20 + 2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3 + d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8 + 590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1 + c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115 + 8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c + 436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59 + 3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f + 39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0 + 35b9593b48b9d3ca4c13d245d5f04169b0b1 + `, + wantVersion: 57, + wantRest: []rlp.RawValue{{0x06}, {0xC2, 0x07, 0x08}, {0x81, 0xFA}}, + }, +} + +func TestHandshakeForwardCompatibility(t *testing.T) { + var ( + keyA, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + keyB, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + pubA = crypto.FromECDSAPub(&keyA.PublicKey)[1:] + pubB = crypto.FromECDSAPub(&keyB.PublicKey)[1:] + ephA, _ = crypto.HexToECDSA("869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d") + ephB, _ = crypto.HexToECDSA("e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4") + ephPubA = crypto.FromECDSAPub(&ephA.PublicKey)[1:] + ephPubB = crypto.FromECDSAPub(&ephB.PublicKey)[1:] + nonceA = unhex("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6") + nonceB = unhex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd") + _, _, _, _ = pubA, pubB, ephPubA, ephPubB + authSignature = unhex("299ca6acfd35e3d72d8ba3d1e2b60b5561d5af5218eb5bc182045769eb4226910a301acae3b369fffc4a4899d6b02531e89fd4fe36a2cf0d93607ba470b50f7800") + _ = authSignature + ) + makeAuth := func(test handshakeAuthTest) *authMsgV4 { + msg := &authMsgV4{Version: test.wantVersion, Rest: test.wantRest, gotPlain: test.isPlain} + copy(msg.Signature[:], authSignature) + copy(msg.InitiatorPubkey[:], pubA) + copy(msg.Nonce[:], nonceA) + return msg + } + makeAck := func(test handshakeAckTest) *authRespV4 { + msg := &authRespV4{Version: test.wantVersion, Rest: test.wantRest} + copy(msg.RandomPubkey[:], ephPubB) + copy(msg.Nonce[:], nonceB) + return msg + } + + // check auth msg parsing + for _, test := range eip8HandshakeAuthTests { + r := bytes.NewReader(unhex(test.input)) + msg := new(authMsgV4) + ciphertext, err := readHandshakeMsg(msg, encAuthMsgLen, keyB, r) + if err != nil { + t.Errorf("error for input %x:\n %v", unhex(test.input), err) + continue + } + if !bytes.Equal(ciphertext, unhex(test.input)) { + t.Errorf("wrong ciphertext for input %x:\n %x", unhex(test.input), ciphertext) + } + want := makeAuth(test) + if !reflect.DeepEqual(msg, want) { + t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", unhex(test.input), spew.Sdump(msg), spew.Sdump(want)) + } + } + + // check auth resp parsing + for _, test := range eip8HandshakeRespTests { + input := unhex(test.input) + r := bytes.NewReader(input) + msg := new(authRespV4) + ciphertext, err := readHandshakeMsg(msg, encAuthRespLen, keyA, r) + if err != nil { + t.Errorf("error for input %x:\n %v", input, err) + continue + } + if !bytes.Equal(ciphertext, input) { + t.Errorf("wrong ciphertext for input %x:\n %x", input, err) + } + want := makeAck(test) + if !reflect.DeepEqual(msg, want) { + t.Errorf("wrong msg for input %x:\ngot %s\nwant %s", input, spew.Sdump(msg), spew.Sdump(want)) + } + } + + // check derivation for (Auth₂, Ack₂) on recipient side + var ( + hs = &encHandshake{ + initiator: false, + respNonce: nonceB, + randomPrivKey: ecies.ImportECDSA(ephB), + } + authCiphertext = unhex(eip8HandshakeAuthTests[1].input) + authRespCiphertext = unhex(eip8HandshakeRespTests[1].input) + authMsg = makeAuth(eip8HandshakeAuthTests[1]) + wantAES = unhex("80e8632c05fed6fc2a13b0f8d31a3cf645366239170ea067065aba8e28bac487") + wantMAC = unhex("2ea74ec5dae199227dff1af715362700e989d889d7a493cb0639691efb8e5f98") + wantFooIngressHash = unhex("0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5") + ) + if err := hs.handleAuthMsg(authMsg, keyB); err != nil { + t.Fatalf("handleAuthMsg: %v", err) + } + derived, err := hs.secrets(authCiphertext, authRespCiphertext) + if err != nil { + t.Fatalf("secrets: %v", err) + } + if !bytes.Equal(derived.AES, wantAES) { + t.Errorf("aes-secret mismatch:\ngot %x\nwant %x", derived.AES, wantAES) + } + if !bytes.Equal(derived.MAC, wantMAC) { + t.Errorf("mac-secret mismatch:\ngot %x\nwant %x", derived.MAC, wantMAC) + } + io.WriteString(derived.IngressMAC, "foo") + fooIngressHash := derived.IngressMAC.Sum(nil) + if !bytes.Equal(fooIngressHash, wantFooIngressHash) { + t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server.go b/vendor/github.com/ethereum/go-ethereum/p2p/server.go new file mode 100644 index 0000000..298148d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server.go @@ -0,0 +1,811 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package p2p implements the Ethereum p2p network protocols. +package p2p + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +const ( + defaultDialTimeout = 15 * time.Second + refreshPeersInterval = 30 * time.Second + staticPeerCheckInterval = 15 * time.Second + + // Maximum number of concurrently handshaking inbound connections. + maxAcceptConns = 50 + + // Maximum number of concurrently dialing outbound connections. + maxActiveDialTasks = 16 + + // Maximum time allowed for reading a complete message. + // This is effectively the amount of time a connection can be idle. + frameReadTimeout = 30 * time.Second + + // Maximum amount of time allowed for writing a complete message. + frameWriteTimeout = 20 * time.Second +) + +var errServerStopped = errors.New("server stopped") + +// Config holds Server options. +type Config struct { + // This field must be set to a valid secp256k1 private key. + PrivateKey *ecdsa.PrivateKey + + // MaxPeers is the maximum number of peers that can be + // connected. It must be greater than zero. + MaxPeers int + + // MaxPendingPeers is the maximum number of peers that can be pending in the + // handshake phase, counted separately for inbound and outbound connections. + // Zero defaults to preset values. + MaxPendingPeers int + + // Discovery specifies whether the peer discovery mechanism should be started + // or not. Disabling is usually useful for protocol debugging (manual topology). + Discovery bool + + // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery + // protocol should be started or not. + DiscoveryV5 bool + + // Listener address for the V5 discovery protocol UDP traffic. + DiscoveryV5Addr string + + // Name sets the node name of this server. + // Use common.MakeName to create a name that follows existing conventions. + Name string + + // BootstrapNodes are used to establish connectivity + // with the rest of the network. + BootstrapNodes []*discover.Node + + // BootstrapNodesV5 are used to establish connectivity + // with the rest of the network using the V5 discovery + // protocol. + BootstrapNodesV5 []*discv5.Node + + // Static nodes are used as pre-configured connections which are always + // maintained and re-connected on disconnects. + StaticNodes []*discover.Node + + // Trusted nodes are used as pre-configured connections which are always + // allowed to connect, even above the peer limit. + TrustedNodes []*discover.Node + + // Connectivity can be restricted to certain IP networks. + // If this option is set to a non-nil value, only hosts which match one of the + // IP networks contained in the list are considered. + NetRestrict *netutil.Netlist + + // NodeDatabase is the path to the database containing the previously seen + // live nodes in the network. + NodeDatabase string + + // Protocols should contain the protocols supported + // by the server. Matching protocols are launched for + // each peer. + Protocols []Protocol + + // If ListenAddr is set to a non-nil address, the server + // will listen for incoming connections. + // + // If the port is zero, the operating system will pick a port. The + // ListenAddr field will be updated with the actual address when + // the server is started. + ListenAddr string + + // If set to a non-nil value, the given NAT port mapper + // is used to make the listening port available to the + // Internet. + NAT nat.Interface + + // If Dialer is set to a non-nil value, the given Dialer + // is used to dial outbound peer connections. + Dialer *net.Dialer + + // If NoDial is true, the server will not dial any peers. + NoDial bool +} + +// Server manages all peer connections. +type Server struct { + // Config fields may not be modified while the server is running. + Config + + // Hooks for testing. These are useful because we can inhibit + // the whole protocol stack. + newTransport func(net.Conn) transport + newPeerHook func(*Peer) + + lock sync.Mutex // protects running + running bool + + ntab discoverTable + listener net.Listener + ourHandshake *protoHandshake + lastLookup time.Time + DiscV5 *discv5.Network + + // These are for Peers, PeerCount (and nothing else). + peerOp chan peerOpFunc + peerOpDone chan struct{} + + quit chan struct{} + addstatic chan *discover.Node + removestatic chan *discover.Node + posthandshake chan *conn + addpeer chan *conn + delpeer chan *Peer + loopWG sync.WaitGroup // loop, listenLoop +} + +type peerOpFunc func(map[discover.NodeID]*Peer) + +type connFlag int + +const ( + dynDialedConn connFlag = 1 << iota + staticDialedConn + inboundConn + trustedConn +) + +// conn wraps a network connection with information gathered +// during the two handshakes. +type conn struct { + fd net.Conn + transport + flags connFlag + cont chan error // The run loop uses cont to signal errors to setupConn. + id discover.NodeID // valid after the encryption handshake + caps []Cap // valid after the protocol handshake + name string // valid after the protocol handshake +} + +type transport interface { + // The two handshakes. + doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) + doProtoHandshake(our *protoHandshake) (*protoHandshake, error) + // The MsgReadWriter can only be used after the encryption + // handshake has completed. The code uses conn.id to track this + // by setting it to a non-nil value after the encryption handshake. + MsgReadWriter + // transports must provide Close because we use MsgPipe in some of + // the tests. Closing the actual network connection doesn't do + // anything in those tests because NsgPipe doesn't use it. + close(err error) +} + +func (c *conn) String() string { + s := c.flags.String() + " conn" + if (c.id != discover.NodeID{}) { + s += fmt.Sprintf(" %x", c.id[:8]) + } + s += " " + c.fd.RemoteAddr().String() + return s +} + +func (f connFlag) String() string { + s := "" + if f&trustedConn != 0 { + s += " trusted" + } + if f&dynDialedConn != 0 { + s += " dyn dial" + } + if f&staticDialedConn != 0 { + s += " static dial" + } + if f&inboundConn != 0 { + s += " inbound" + } + if s != "" { + s = s[1:] + } + return s +} + +func (c *conn) is(f connFlag) bool { + return c.flags&f != 0 +} + +// Peers returns all connected peers. +func (srv *Server) Peers() []*Peer { + var ps []*Peer + select { + // Note: We'd love to put this function into a variable but + // that seems to cause a weird compiler error in some + // environments. + case srv.peerOp <- func(peers map[discover.NodeID]*Peer) { + for _, p := range peers { + ps = append(ps, p) + } + }: + <-srv.peerOpDone + case <-srv.quit: + } + return ps +} + +// PeerCount returns the number of connected peers. +func (srv *Server) PeerCount() int { + var count int + select { + case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }: + <-srv.peerOpDone + case <-srv.quit: + } + return count +} + +// AddPeer connects to the given node and maintains the connection until the +// server is shut down. If the connection fails for any reason, the server will +// attempt to reconnect the peer. +func (srv *Server) AddPeer(node *discover.Node) { + select { + case srv.addstatic <- node: + case <-srv.quit: + } +} + +// RemovePeer disconnects from the given node +func (srv *Server) RemovePeer(node *discover.Node) { + select { + case srv.removestatic <- node: + case <-srv.quit: + } +} + +// Self returns the local node's endpoint information. +func (srv *Server) Self() *discover.Node { + srv.lock.Lock() + defer srv.lock.Unlock() + + // If the server's not running, return an empty node + if !srv.running { + return &discover.Node{IP: net.ParseIP("0.0.0.0")} + } + // If the node is running but discovery is off, manually assemble the node infos + if srv.ntab == nil { + // Inbound connections disabled, use zero address + if srv.listener == nil { + return &discover.Node{IP: net.ParseIP("0.0.0.0"), ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} + } + // Otherwise inject the listener address too + addr := srv.listener.Addr().(*net.TCPAddr) + return &discover.Node{ + ID: discover.PubkeyID(&srv.PrivateKey.PublicKey), + IP: addr.IP, + TCP: uint16(addr.Port), + } + } + // Otherwise return the live node infos + return srv.ntab.Self() +} + +// Stop terminates the server and all active peer connections. +// It blocks until all active connections have been closed. +func (srv *Server) Stop() { + srv.lock.Lock() + defer srv.lock.Unlock() + if !srv.running { + return + } + srv.running = false + if srv.listener != nil { + // this unblocks listener Accept + srv.listener.Close() + } + close(srv.quit) + srv.loopWG.Wait() +} + +// Start starts running the server. +// Servers can not be re-used after stopping. +func (srv *Server) Start() (err error) { + srv.lock.Lock() + defer srv.lock.Unlock() + if srv.running { + return errors.New("server already running") + } + srv.running = true + glog.V(logger.Info).Infoln("Starting Server") + + // static fields + if srv.PrivateKey == nil { + return fmt.Errorf("Server.PrivateKey must be set to a non-nil key") + } + if srv.newTransport == nil { + srv.newTransport = newRLPX + } + if srv.Dialer == nil { + srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout} + } + srv.quit = make(chan struct{}) + srv.addpeer = make(chan *conn) + srv.delpeer = make(chan *Peer) + srv.posthandshake = make(chan *conn) + srv.addstatic = make(chan *discover.Node) + srv.removestatic = make(chan *discover.Node) + srv.peerOp = make(chan peerOpFunc) + srv.peerOpDone = make(chan struct{}) + + // node table + if srv.Discovery { + ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict) + if err != nil { + return err + } + if err := ntab.SetFallbackNodes(srv.BootstrapNodes); err != nil { + return err + } + srv.ntab = ntab + } + + if srv.DiscoveryV5 { + ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "", srv.NetRestrict) //srv.NodeDatabase) + if err != nil { + return err + } + if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil { + return err + } + srv.DiscV5 = ntab + } + + dynPeers := (srv.MaxPeers + 1) / 2 + if !srv.Discovery { + dynPeers = 0 + } + dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers, srv.NetRestrict) + + // handshake + srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} + for _, p := range srv.Protocols { + srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap()) + } + // listen/dial + if srv.ListenAddr != "" { + if err := srv.startListening(); err != nil { + return err + } + } + if srv.NoDial && srv.ListenAddr == "" { + glog.V(logger.Warn).Infoln("I will be kind-of useless, neither dialing nor listening.") + } + + srv.loopWG.Add(1) + go srv.run(dialer) + srv.running = true + return nil +} + +func (srv *Server) startListening() error { + // Launch the TCP listener. + listener, err := net.Listen("tcp", srv.ListenAddr) + if err != nil { + return err + } + laddr := listener.Addr().(*net.TCPAddr) + srv.ListenAddr = laddr.String() + srv.listener = listener + srv.loopWG.Add(1) + go srv.listenLoop() + // Map the TCP listening port if NAT is configured. + if !laddr.IP.IsLoopback() && srv.NAT != nil { + srv.loopWG.Add(1) + go func() { + nat.Map(srv.NAT, srv.quit, "tcp", laddr.Port, laddr.Port, "ethereum p2p") + srv.loopWG.Done() + }() + } + return nil +} + +type dialer interface { + newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task + taskDone(task, time.Time) + addStatic(*discover.Node) + removeStatic(*discover.Node) +} + +func (srv *Server) run(dialstate dialer) { + defer srv.loopWG.Done() + var ( + peers = make(map[discover.NodeID]*Peer) + trusted = make(map[discover.NodeID]bool, len(srv.TrustedNodes)) + taskdone = make(chan task, maxActiveDialTasks) + runningTasks []task + queuedTasks []task // tasks that can't run yet + ) + // Put trusted nodes into a map to speed up checks. + // Trusted peers are loaded on startup and cannot be + // modified while the server is running. + for _, n := range srv.TrustedNodes { + trusted[n.ID] = true + } + + // removes t from runningTasks + delTask := func(t task) { + for i := range runningTasks { + if runningTasks[i] == t { + runningTasks = append(runningTasks[:i], runningTasks[i+1:]...) + break + } + } + } + // starts until max number of active tasks is satisfied + startTasks := func(ts []task) (rest []task) { + i := 0 + for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ { + t := ts[i] + glog.V(logger.Detail).Infoln("new task:", t) + go func() { t.Do(srv); taskdone <- t }() + runningTasks = append(runningTasks, t) + } + return ts[i:] + } + scheduleTasks := func() { + // Start from queue first. + queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...) + // Query dialer for new tasks and start as many as possible now. + if len(runningTasks) < maxActiveDialTasks { + nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now()) + queuedTasks = append(queuedTasks, startTasks(nt)...) + } + } + +running: + for { + scheduleTasks() + + select { + case <-srv.quit: + // The server was stopped. Run the cleanup logic. + glog.V(logger.Detail).Infoln("<-quit: spinning down") + break running + case n := <-srv.addstatic: + // This channel is used by AddPeer to add to the + // ephemeral static peer list. Add it to the dialer, + // it will keep the node connected. + glog.V(logger.Detail).Infoln("<-addstatic:", n) + dialstate.addStatic(n) + case n := <-srv.removestatic: + // This channel is used by RemovePeer to send a + // disconnect request to a peer and begin the + // stop keeping the node connected + glog.V(logger.Detail).Infoln("<-removestatic:", n) + dialstate.removeStatic(n) + if p, ok := peers[n.ID]; ok { + p.Disconnect(DiscRequested) + } + case op := <-srv.peerOp: + // This channel is used by Peers and PeerCount. + op(peers) + srv.peerOpDone <- struct{}{} + case t := <-taskdone: + // A task got done. Tell dialstate about it so it + // can update its state and remove it from the active + // tasks list. + glog.V(logger.Detail).Infoln("<-taskdone:", t) + dialstate.taskDone(t, time.Now()) + delTask(t) + case c := <-srv.posthandshake: + // A connection has passed the encryption handshake so + // the remote identity is known (but hasn't been verified yet). + if trusted[c.id] { + // Ensure that the trusted flag is set before checking against MaxPeers. + c.flags |= trustedConn + } + glog.V(logger.Detail).Infoln("<-posthandshake:", c) + // TODO: track in-progress inbound node IDs (pre-Peer) to avoid dialing them. + c.cont <- srv.encHandshakeChecks(peers, c) + case c := <-srv.addpeer: + // At this point the connection is past the protocol handshake. + // Its capabilities are known and the remote identity is verified. + glog.V(logger.Detail).Infoln("<-addpeer:", c) + err := srv.protoHandshakeChecks(peers, c) + if err != nil { + glog.V(logger.Detail).Infof("Not adding %v as peer: %v", c, err) + } else { + // The handshakes are done and it passed all checks. + p := newPeer(c, srv.Protocols) + peers[c.id] = p + go srv.runPeer(p) + } + // The dialer logic relies on the assumption that + // dial tasks complete after the peer has been added or + // discarded. Unblock the task last. + c.cont <- err + case p := <-srv.delpeer: + // A peer disconnected. + glog.V(logger.Detail).Infoln("<-delpeer:", p) + delete(peers, p.ID()) + } + } + + // Terminate discovery. If there is a running lookup it will terminate soon. + if srv.ntab != nil { + srv.ntab.Close() + } + if srv.DiscV5 != nil { + srv.DiscV5.Close() + } + // Disconnect all peers. + for _, p := range peers { + p.Disconnect(DiscQuitting) + } + // Wait for peers to shut down. Pending connections and tasks are + // not handled here and will terminate soon-ish because srv.quit + // is closed. + glog.V(logger.Detail).Infof("ignoring %d pending tasks at spindown", len(runningTasks)) + for len(peers) > 0 { + p := <-srv.delpeer + glog.V(logger.Detail).Infoln("<-delpeer (spindown):", p) + delete(peers, p.ID()) + } +} + +func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { + // Drop connections with no matching protocols. + if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 { + return DiscUselessPeer + } + // Repeat the encryption handshake checks because the + // peer set might have changed between the handshakes. + return srv.encHandshakeChecks(peers, c) +} + +func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { + switch { + case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers: + return DiscTooManyPeers + case peers[c.id] != nil: + return DiscAlreadyConnected + case c.id == srv.Self().ID: + return DiscSelf + default: + return nil + } +} + +type tempError interface { + Temporary() bool +} + +// listenLoop runs in its own goroutine and accepts +// inbound connections. +func (srv *Server) listenLoop() { + defer srv.loopWG.Done() + glog.V(logger.Info).Infoln("Listening on", srv.listener.Addr()) + + // This channel acts as a semaphore limiting + // active inbound connections that are lingering pre-handshake. + // If all slots are taken, no further connections are accepted. + tokens := maxAcceptConns + if srv.MaxPendingPeers > 0 { + tokens = srv.MaxPendingPeers + } + slots := make(chan struct{}, tokens) + for i := 0; i < tokens; i++ { + slots <- struct{}{} + } + + for { + // Wait for a handshake slot before accepting. + <-slots + + var ( + fd net.Conn + err error + ) + for { + fd, err = srv.listener.Accept() + if tempErr, ok := err.(tempError); ok && tempErr.Temporary() { + glog.V(logger.Debug).Infof("Temporary read error: %v", err) + continue + } else if err != nil { + glog.V(logger.Debug).Infof("Read error: %v", err) + return + } + break + } + + // Reject connections that do not match NetRestrict. + if srv.NetRestrict != nil { + if tcp, ok := fd.RemoteAddr().(*net.TCPAddr); ok && !srv.NetRestrict.Contains(tcp.IP) { + glog.V(logger.Debug).Infof("Rejected conn %v because it is not whitelisted in NetRestrict", fd.RemoteAddr()) + fd.Close() + slots <- struct{}{} + continue + } + } + + fd = newMeteredConn(fd, true) + glog.V(logger.Debug).Infof("Accepted conn %v", fd.RemoteAddr()) + + // Spawn the handler. It will give the slot back when the connection + // has been established. + go func() { + srv.setupConn(fd, inboundConn, nil) + slots <- struct{}{} + }() + } +} + +// setupConn runs the handshakes and attempts to add the connection +// as a peer. It returns when the connection has been added as a peer +// or the handshakes have failed. +func (srv *Server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { + // Prevent leftover pending conns from entering the handshake. + srv.lock.Lock() + running := srv.running + srv.lock.Unlock() + c := &conn{fd: fd, transport: srv.newTransport(fd), flags: flags, cont: make(chan error)} + if !running { + c.close(errServerStopped) + return + } + // Run the encryption handshake. + var err error + if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != nil { + glog.V(logger.Debug).Infof("%v faild enc handshake: %v", c, err) + c.close(err) + return + } + // For dialed connections, check that the remote public key matches. + if dialDest != nil && c.id != dialDest.ID { + c.close(DiscUnexpectedIdentity) + glog.V(logger.Debug).Infof("%v dialed identity mismatch, want %x", c, dialDest.ID[:8]) + return + } + if err := srv.checkpoint(c, srv.posthandshake); err != nil { + glog.V(logger.Debug).Infof("%v failed checkpoint posthandshake: %v", c, err) + c.close(err) + return + } + // Run the protocol handshake + phs, err := c.doProtoHandshake(srv.ourHandshake) + if err != nil { + glog.V(logger.Debug).Infof("%v failed proto handshake: %v", c, err) + c.close(err) + return + } + if phs.ID != c.id { + glog.V(logger.Debug).Infof("%v wrong proto handshake identity: %x", c, phs.ID[:8]) + c.close(DiscUnexpectedIdentity) + return + } + c.caps, c.name = phs.Caps, phs.Name + if err := srv.checkpoint(c, srv.addpeer); err != nil { + glog.V(logger.Debug).Infof("%v failed checkpoint addpeer: %v", c, err) + c.close(err) + return + } + // If the checks completed successfully, runPeer has now been + // launched by run. +} + +// checkpoint sends the conn to run, which performs the +// post-handshake checks for the stage (posthandshake, addpeer). +func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error { + select { + case stage <- c: + case <-srv.quit: + return errServerStopped + } + select { + case err := <-c.cont: + return err + case <-srv.quit: + return errServerStopped + } +} + +// runPeer runs in its own goroutine for each peer. +// it waits until the Peer logic returns and removes +// the peer. +func (srv *Server) runPeer(p *Peer) { + glog.V(logger.Debug).Infof("Added %v\n", p) + + if srv.newPeerHook != nil { + srv.newPeerHook(p) + } + discreason := p.run() + // Note: run waits for existing peers to be sent on srv.delpeer + // before returning, so this send should not select on srv.quit. + srv.delpeer <- p + + glog.V(logger.Debug).Infof("Removed %v (%v)\n", p, discreason) +} + +// NodeInfo represents a short summary of the information known about the host. +type NodeInfo struct { + ID string `json:"id"` // Unique node identifier (also the encryption key) + Name string `json:"name"` // Name of the node, including client type, version, OS, custom data + Enode string `json:"enode"` // Enode URL for adding this peer from remote peers + IP string `json:"ip"` // IP address of the node + Ports struct { + Discovery int `json:"discovery"` // UDP listening port for discovery protocol + Listener int `json:"listener"` // TCP listening port for RLPx + } `json:"ports"` + ListenAddr string `json:"listenAddr"` + Protocols map[string]interface{} `json:"protocols"` +} + +// NodeInfo gathers and returns a collection of metadata known about the host. +func (srv *Server) NodeInfo() *NodeInfo { + node := srv.Self() + + // Gather and assemble the generic node infos + info := &NodeInfo{ + Name: srv.Name, + Enode: node.String(), + ID: node.ID.String(), + IP: node.IP.String(), + ListenAddr: srv.ListenAddr, + Protocols: make(map[string]interface{}), + } + info.Ports.Discovery = int(node.UDP) + info.Ports.Listener = int(node.TCP) + + // Gather all the running protocol infos (only once per protocol type) + for _, proto := range srv.Protocols { + if _, ok := info.Protocols[proto.Name]; !ok { + nodeInfo := interface{}("unknown") + if query := proto.NodeInfo; query != nil { + nodeInfo = proto.NodeInfo() + } + info.Protocols[proto.Name] = nodeInfo + } + } + return info +} + +// PeersInfo returns an array of metadata objects describing connected peers. +func (srv *Server) PeersInfo() []*PeerInfo { + // Gather all the generic and sub-protocol specific infos + infos := make([]*PeerInfo, 0, srv.PeerCount()) + for _, peer := range srv.Peers() { + if peer != nil { + infos = append(infos, peer.Info()) + } + } + // Sort the result array alphabetically by node identifier + for i := 0; i < len(infos); i++ { + for j := i + 1; j < len(infos); j++ { + if infos[i].ID > infos[j].ID { + infos[i], infos[j] = infos[j], infos[i] + } + } + } + return infos +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server_test.go b/vendor/github.com/ethereum/go-ethereum/p2p/server_test.go new file mode 100644 index 0000000..313d086 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server_test.go @@ -0,0 +1,497 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package p2p + +import ( + "crypto/ecdsa" + "errors" + "math/rand" + "net" + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/p2p/discover" +) + +func init() { + // glog.SetV(6) + // glog.SetToStderr(true) +} + +type testTransport struct { + id discover.NodeID + *rlpx + + closeErr error +} + +func newTestTransport(id discover.NodeID, fd net.Conn) transport { + wrapped := newRLPX(fd).(*rlpx) + wrapped.rw = newRLPXFrameRW(fd, secrets{ + MAC: zero16, + AES: zero16, + IngressMAC: sha3.NewKeccak256(), + EgressMAC: sha3.NewKeccak256(), + }) + return &testTransport{id: id, rlpx: wrapped} +} + +func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) { + return c.id, nil +} + +func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) { + return &protoHandshake{ID: c.id, Name: "test"}, nil +} + +func (c *testTransport) close(err error) { + c.rlpx.fd.Close() + c.closeErr = err +} + +func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server { + config := Config{ + Name: "test", + MaxPeers: 10, + ListenAddr: "127.0.0.1:0", + PrivateKey: newkey(), + } + server := &Server{ + Config: config, + newPeerHook: pf, + newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) }, + } + if err := server.Start(); err != nil { + t.Fatalf("Could not start server: %v", err) + } + return server +} + +func TestServerListen(t *testing.T) { + // start the test server + connected := make(chan *Peer) + remid := randomID() + srv := startTestServer(t, remid, func(p *Peer) { + if p.ID() != remid { + t.Error("peer func called with wrong node id") + } + if p == nil { + t.Error("peer func called with nil conn") + } + connected <- p + }) + defer close(connected) + defer srv.Stop() + + // dial the test server + conn, err := net.DialTimeout("tcp", srv.ListenAddr, 5*time.Second) + if err != nil { + t.Fatalf("could not dial: %v", err) + } + defer conn.Close() + + select { + case peer := <-connected: + if peer.LocalAddr().String() != conn.RemoteAddr().String() { + t.Errorf("peer started with wrong conn: got %v, want %v", + peer.LocalAddr(), conn.RemoteAddr()) + } + peers := srv.Peers() + if !reflect.DeepEqual(peers, []*Peer{peer}) { + t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer}) + } + case <-time.After(1 * time.Second): + t.Error("server did not accept within one second") + } +} + +func TestServerDial(t *testing.T) { + // run a one-shot TCP server to handle the connection. + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("could not setup listener: %v", err) + } + defer listener.Close() + accepted := make(chan net.Conn) + go func() { + conn, err := listener.Accept() + if err != nil { + t.Error("accept error:", err) + return + } + accepted <- conn + }() + + // start the server + connected := make(chan *Peer) + remid := randomID() + srv := startTestServer(t, remid, func(p *Peer) { connected <- p }) + defer close(connected) + defer srv.Stop() + + // tell the server to connect + tcpAddr := listener.Addr().(*net.TCPAddr) + srv.AddPeer(&discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)}) + + select { + case conn := <-accepted: + defer conn.Close() + + select { + case peer := <-connected: + if peer.ID() != remid { + t.Errorf("peer has wrong id") + } + if peer.Name() != "test" { + t.Errorf("peer has wrong name") + } + if peer.RemoteAddr().String() != conn.LocalAddr().String() { + t.Errorf("peer started with wrong conn: got %v, want %v", + peer.RemoteAddr(), conn.LocalAddr()) + } + peers := srv.Peers() + if !reflect.DeepEqual(peers, []*Peer{peer}) { + t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer}) + } + case <-time.After(1 * time.Second): + t.Error("server did not launch peer within one second") + } + + case <-time.After(1 * time.Second): + t.Error("server did not connect within one second") + } +} + +// This test checks that tasks generated by dialstate are +// actually executed and taskdone is called for them. +func TestServerTaskScheduling(t *testing.T) { + var ( + done = make(chan *testTask) + quit, returned = make(chan struct{}), make(chan struct{}) + tc = 0 + tg = taskgen{ + newFunc: func(running int, peers map[discover.NodeID]*Peer) []task { + tc++ + return []task{&testTask{index: tc - 1}} + }, + doneFunc: func(t task) { + select { + case done <- t.(*testTask): + case <-quit: + } + }, + } + ) + + // The Server in this test isn't actually running + // because we're only interested in what run does. + srv := &Server{ + Config: Config{MaxPeers: 10}, + quit: make(chan struct{}), + ntab: fakeTable{}, + running: true, + } + srv.loopWG.Add(1) + go func() { + srv.run(tg) + close(returned) + }() + + var gotdone []*testTask + for i := 0; i < 100; i++ { + gotdone = append(gotdone, <-done) + } + for i, task := range gotdone { + if task.index != i { + t.Errorf("task %d has wrong index, got %d", i, task.index) + break + } + if !task.called { + t.Errorf("task %d was not called", i) + break + } + } + + close(quit) + srv.Stop() + select { + case <-returned: + case <-time.After(500 * time.Millisecond): + t.Error("Server.run did not return within 500ms") + } +} + +// This test checks that Server doesn't drop tasks, +// even if newTasks returns more than the maximum number of tasks. +func TestServerManyTasks(t *testing.T) { + alltasks := make([]task, 300) + for i := range alltasks { + alltasks[i] = &testTask{index: i} + } + + var ( + srv = &Server{quit: make(chan struct{}), ntab: fakeTable{}, running: true} + done = make(chan *testTask) + start, end = 0, 0 + ) + defer srv.Stop() + srv.loopWG.Add(1) + go srv.run(taskgen{ + newFunc: func(running int, peers map[discover.NodeID]*Peer) []task { + start, end = end, end+maxActiveDialTasks+10 + if end > len(alltasks) { + end = len(alltasks) + } + return alltasks[start:end] + }, + doneFunc: func(tt task) { + done <- tt.(*testTask) + }, + }) + + doneset := make(map[int]bool) + timeout := time.After(2 * time.Second) + for len(doneset) < len(alltasks) { + select { + case tt := <-done: + if doneset[tt.index] { + t.Errorf("task %d got done more than once", tt.index) + } else { + doneset[tt.index] = true + } + case <-timeout: + t.Errorf("%d of %d tasks got done within 2s", len(doneset), len(alltasks)) + for i := 0; i < len(alltasks); i++ { + if !doneset[i] { + t.Logf("task %d not done", i) + } + } + return + } + } +} + +type taskgen struct { + newFunc func(running int, peers map[discover.NodeID]*Peer) []task + doneFunc func(task) +} + +func (tg taskgen) newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task { + return tg.newFunc(running, peers) +} +func (tg taskgen) taskDone(t task, now time.Time) { + tg.doneFunc(t) +} +func (tg taskgen) addStatic(*discover.Node) { +} +func (tg taskgen) removeStatic(*discover.Node) { +} + +type testTask struct { + index int + called bool +} + +func (t *testTask) Do(srv *Server) { + t.called = true +} + +// This test checks that connections are disconnected +// just after the encryption handshake when the server is +// at capacity. Trusted connections should still be accepted. +func TestServerAtCap(t *testing.T) { + trustedID := randomID() + srv := &Server{ + Config: Config{ + PrivateKey: newkey(), + MaxPeers: 10, + NoDial: true, + TrustedNodes: []*discover.Node{{ID: trustedID}}, + }, + } + if err := srv.Start(); err != nil { + t.Fatalf("could not start: %v", err) + } + defer srv.Stop() + + newconn := func(id discover.NodeID) *conn { + fd, _ := net.Pipe() + tx := newTestTransport(id, fd) + return &conn{fd: fd, transport: tx, flags: inboundConn, id: id, cont: make(chan error)} + } + + // Inject a few connections to fill up the peer set. + for i := 0; i < 10; i++ { + c := newconn(randomID()) + if err := srv.checkpoint(c, srv.addpeer); err != nil { + t.Fatalf("could not add conn %d: %v", i, err) + } + } + // Try inserting a non-trusted connection. + c := newconn(randomID()) + if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers { + t.Error("wrong error for insert:", err) + } + // Try inserting a trusted connection. + c = newconn(trustedID) + if err := srv.checkpoint(c, srv.posthandshake); err != nil { + t.Error("unexpected error for trusted conn @posthandshake:", err) + } + if !c.is(trustedConn) { + t.Error("Server did not set trusted flag") + } + +} + +func TestServerSetupConn(t *testing.T) { + id := randomID() + srvkey := newkey() + srvid := discover.PubkeyID(&srvkey.PublicKey) + tests := []struct { + dontstart bool + tt *setupTransport + flags connFlag + dialDest *discover.Node + + wantCloseErr error + wantCalls string + }{ + { + dontstart: true, + tt: &setupTransport{id: id}, + wantCalls: "close,", + wantCloseErr: errServerStopped, + }, + { + tt: &setupTransport{id: id, encHandshakeErr: errors.New("read error")}, + flags: inboundConn, + wantCalls: "doEncHandshake,close,", + wantCloseErr: errors.New("read error"), + }, + { + tt: &setupTransport{id: id}, + dialDest: &discover.Node{ID: randomID()}, + flags: dynDialedConn, + wantCalls: "doEncHandshake,close,", + wantCloseErr: DiscUnexpectedIdentity, + }, + { + tt: &setupTransport{id: id, phs: &protoHandshake{ID: randomID()}}, + dialDest: &discover.Node{ID: id}, + flags: dynDialedConn, + wantCalls: "doEncHandshake,doProtoHandshake,close,", + wantCloseErr: DiscUnexpectedIdentity, + }, + { + tt: &setupTransport{id: id, protoHandshakeErr: errors.New("foo")}, + dialDest: &discover.Node{ID: id}, + flags: dynDialedConn, + wantCalls: "doEncHandshake,doProtoHandshake,close,", + wantCloseErr: errors.New("foo"), + }, + { + tt: &setupTransport{id: srvid, phs: &protoHandshake{ID: srvid}}, + flags: inboundConn, + wantCalls: "doEncHandshake,close,", + wantCloseErr: DiscSelf, + }, + { + tt: &setupTransport{id: id, phs: &protoHandshake{ID: id}}, + flags: inboundConn, + wantCalls: "doEncHandshake,doProtoHandshake,close,", + wantCloseErr: DiscUselessPeer, + }, + } + + for i, test := range tests { + srv := &Server{ + Config: Config{ + PrivateKey: srvkey, + MaxPeers: 10, + NoDial: true, + Protocols: []Protocol{discard}, + }, + newTransport: func(fd net.Conn) transport { return test.tt }, + } + if !test.dontstart { + if err := srv.Start(); err != nil { + t.Fatalf("couldn't start server: %v", err) + } + } + p1, _ := net.Pipe() + srv.setupConn(p1, test.flags, test.dialDest) + if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) { + t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr) + } + if test.tt.calls != test.wantCalls { + t.Errorf("test %d: calls mismatch: got %q, want %q", i, test.tt.calls, test.wantCalls) + } + } +} + +type setupTransport struct { + id discover.NodeID + encHandshakeErr error + + phs *protoHandshake + protoHandshakeErr error + + calls string + closeErr error +} + +func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) { + c.calls += "doEncHandshake," + return c.id, c.encHandshakeErr +} +func (c *setupTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) { + c.calls += "doProtoHandshake," + if c.protoHandshakeErr != nil { + return nil, c.protoHandshakeErr + } + return c.phs, nil +} +func (c *setupTransport) close(err error) { + c.calls += "close," + c.closeErr = err +} + +// setupConn shouldn't write to/read from the connection. +func (c *setupTransport) WriteMsg(Msg) error { + panic("WriteMsg called on setupTransport") +} +func (c *setupTransport) ReadMsg() (Msg, error) { + panic("ReadMsg called on setupTransport") +} + +func newkey() *ecdsa.PrivateKey { + key, err := crypto.GenerateKey() + if err != nil { + panic("couldn't generate key: " + err.Error()) + } + return key +} + +func randomID() (id discover.NodeID) { + for i := range id { + id[i] = byte(rand.Intn(255)) + } + return id +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go new file mode 100644 index 0000000..13414cb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go @@ -0,0 +1,45 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +// MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on +// the main Ethereum network. +var MainnetBootnodes = []string{ + // ETH/DEV Go Bootnodes + "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", // IE + "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", // BR + "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", // SG + + // ETH/DEV Cpp Bootnodes + "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", +} + +// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Morden test network. +var TestnetBootnodes = []string{ + // ETH/DEV Go Bootnodes + "enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404", + "enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303", +} + +// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the +// experimental RLPx v5 topic-discovery network. +var DiscoveryV5Bootnodes = []string{ + "enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305", + "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308", + "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309", +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/config.go b/vendor/github.com/ethereum/go-ethereum/params/config.go new file mode 100644 index 0000000..8c28578 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/config.go @@ -0,0 +1,150 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// MainnetChainConfig is the chain parameters to run a node on the main network. +var MainnetChainConfig = &ChainConfig{ + ChainId: MainNetChainID, + HomesteadBlock: MainNetHomesteadBlock, + DAOForkBlock: MainNetDAOForkBlock, + DAOForkSupport: true, + EIP150Block: MainNetHomesteadGasRepriceBlock, + EIP150Hash: MainNetHomesteadGasRepriceHash, + EIP155Block: MainNetSpuriousDragon, + EIP158Block: MainNetSpuriousDragon, +} + +// TestnetChainConfig is the chain parameters to run a node on the test network. +var TestnetChainConfig = &ChainConfig{ + ChainId: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), +} + +// ChainConfig is the core config which determines the blockchain settings. +// +// ChainConfig is stored in the database on a per block basis. This means +// that any network, identified by its genesis block, can have its own +// set of configuration options. +type ChainConfig struct { + ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection + + HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) + DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) + DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork + + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + EIP150Block *big.Int `json:"eip150Block"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"eip150Hash"` // EIP150 HF hash (fast sync aid) + + EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block + EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block +} + +// String implements the Stringer interface. +func (c *ChainConfig) String() string { + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}", + c.ChainId, + c.HomesteadBlock, + c.DAOForkBlock, + c.DAOForkSupport, + c.EIP150Block, + c.EIP155Block, + c.EIP158Block, + ) +} + +var ( + TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)} + TestRules = TestChainConfig.Rules(new(big.Int)) +) + +// IsHomestead returns whether num is either equal to the homestead block or greater. +func (c *ChainConfig) IsHomestead(num *big.Int) bool { + if c.HomesteadBlock == nil || num == nil { + return false + } + return num.Cmp(c.HomesteadBlock) >= 0 +} + +// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). +// +// The returned GasTable's fields shouldn't, under any circumstances, be changed. +func (c *ChainConfig) GasTable(num *big.Int) GasTable { + if num == nil { + return GasTableHomestead + } + + switch { + case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0: + return GasTableEIP158 + case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0: + return GasTableHomesteadGasRepriceFork + default: + return GasTableHomestead + } +} + +func (c *ChainConfig) IsEIP150(num *big.Int) bool { + if c.EIP150Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP150Block) >= 0 + +} + +func (c *ChainConfig) IsEIP155(num *big.Int) bool { + if c.EIP155Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP155Block) >= 0 + +} + +func (c *ChainConfig) IsEIP158(num *big.Int) bool { + if c.EIP158Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP158Block) >= 0 + +} + +// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions +// that do not have or require information about the block. +// +// Rules is a one time interface meaning that it shouldn't be used in between transition +// phases. +type Rules struct { + ChainId *big.Int + IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool +} + +func (c *ChainConfig) Rules(num *big.Int) Rules { + return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)} +} diff --git a/vendor/github.com/ethereum/go-ethereum/params/dao.go b/vendor/github.com/ethereum/go-ethereum/params/dao.go new file mode 100644 index 0000000..3e2a68c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/dao.go @@ -0,0 +1,418 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// TestNetDAOForkBlock is the block number where the DAO hard-fork commences on +// the Ethereum test network. It's enforced nil since it was decided not to do a +// testnet transition. +var TestNetDAOForkBlock *big.Int + +// MainNetDAOForkBlock is the block number where the DAO hard-fork commences on +// the Ethereum main network. +var MainNetDAOForkBlock = big.NewInt(1920000) + +// DAOForkBlockExtra is the block header extra-data field to set for the DAO fork +// point and a number of consecutive blocks to allow fast/light syncers to correctly +// pick the side they want ("dao-hard-fork"). +var DAOForkBlockExtra = common.FromHex("0x64616f2d686172642d666f726b") + +// DAOForkExtraRange is the number of consecutive blocks from the DAO fork point +// to override the extra-data in to prevent no-fork attacks. +var DAOForkExtraRange = big.NewInt(10) + +// DAORefundContract is the address of the refund contract to send DAO balances to. +var DAORefundContract = common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754") + +// DAODrainList is the list of accounts whose full balances will be moved into a +// refund contract at the beginning of the dao-fork block. +var DAODrainList []common.Address + +func init() { + // Parse the list of DAO accounts to drain + var list []map[string]string + if err := json.Unmarshal([]byte(daoDrainListJSON), &list); err != nil { + panic(fmt.Errorf("Failed to parse DAO drain list: %v", err)) + } + // Collect all the accounts that need draining + for _, dao := range list { + DAODrainList = append(DAODrainList, common.HexToAddress(dao["address"])) + DAODrainList = append(DAODrainList, common.HexToAddress(dao["extraBalanceAccount"])) + } +} + +// daoDrainListJSON is the JSON encoded list of accounts whose full balances will +// be moved into a refund contract at the beginning of the dao-fork block. +const daoDrainListJSON = ` +[ + { + "address":"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", + "balance":"186cc8bfaefb7be", + "extraBalance":"0", + "extraBalanceAccount":"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425" + }, + { + "address":"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "balance":"b14e8feab1ff435", + "extraBalance":"0", + "extraBalanceAccount":"0xecd135fa4f61a655311e86238c92adcd779555d2" + }, + { + "address":"0x1975bd06d486162d5dc297798dfc41edd5d160a7", + "balance":"359d26614cb5070c77", + "extraBalance":"0", + "extraBalanceAccount":"0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6" + }, + { + "address":"0x319f70bab6845585f412ec7724b744fec6095c85", + "balance":"6e075cd846d2cb1d42", + "extraBalance":"13d34fd41b545b81", + "extraBalanceAccount":"0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936" + }, + { + "address":"0x5c8536898fbb74fc7445814902fd08422eac56d0", + "balance":"b1e5593558008fd78", + "extraBalance":"0", + "extraBalanceAccount":"0x6966ab0d485353095148a2155858910e0965b6f9" + }, + { + "address":"0x779543a0491a837ca36ce8c635d6154e3c4911a6", + "balance":"392eaa20d1aad59a4c", + "extraBalance":"426938826a96c9", + "extraBalanceAccount":"0x2a5ed960395e2a49b1c758cef4aa15213cfd874c" + }, + { + "address":"0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "balance":"2875d22b29793d4ba7", + "extraBalance":"0", + "extraBalanceAccount":"0x9c50426be05db97f5d64fc54bf89eff947f0a321" + }, + { + "address":"0x200450f06520bdd6c527622a273333384d870efb", + "balance":"43c341d9f96954c049", + "extraBalance":"0", + "extraBalanceAccount":"0xbe8539bfe837b67d1282b2b1d61c3f723966f049" + }, + { + "address":"0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "balance":"75251057154d70fa816", + "extraBalance":"0", + "extraBalanceAccount":"0xf1385fb24aad0cd7432824085e42aff90886fef5" + }, + { + "address":"0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "balance":"392409769296cf67f36", + "extraBalance":"0", + "extraBalanceAccount":"0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd" + }, + { + "address":"0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "balance":"8ac72eccbf4e8083", + "extraBalance":"0", + "extraBalanceAccount":"0x627a0a960c079c21c34f7612d5d230e01b4ad4c7" + }, + { + "address":"0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", + "balance":"82289c3bb3e8c98799", + "extraBalance":"0", + "extraBalanceAccount":"0x24c4d950dfd4dd1902bbed3508144a54542bba94" + }, + { + "address":"0x9f27daea7aca0aa0446220b98d028715e3bc803d", + "balance":"56bc29049ebed40fd", + "extraBalance":"0", + "extraBalanceAccount":"0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90" + }, + { + "address":"0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "balance":"56bc7d3ff79110524", + "extraBalance":"0", + "extraBalanceAccount":"0x63ed5a272de2f6d968408b4acb9024f4cc208ebf" + }, + { + "address":"0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "balance":"23b651bd48cbc70cc", + "extraBalance":"0", + "extraBalanceAccount":"0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6" + }, + { + "address":"0x492ea3bb0f3315521c31f273e565b868fc090f17", + "balance":"13ea6d4fee651dd7c9", + "extraBalance":"0", + "extraBalanceAccount":"0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00" + }, + { + "address":"0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "balance":"35ac471a3836ae7de5a", + "extraBalance":"0", + "extraBalanceAccount":"0xceaeb481747ca6c540a000c1f3641f8cef161fa7" + }, + { + "address":"0xcc34673c6c40e791051898567a1222daf90be287", + "balance":"d529c0b76b7aa0", + "extraBalance":"0", + "extraBalanceAccount":"0x579a80d909f346fbfb1189493f521d7f48d52238" + }, + { + "address":"0xe308bd1ac5fda103967359b2712dd89deffb7973", + "balance":"5cd9e7df3a8e5cdd3", + "extraBalance":"0", + "extraBalanceAccount":"0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c" + }, + { + "address":"0xac1ecab32727358dba8962a0f3b261731aad9723", + "balance":"2c8442fe35363313b93", + "extraBalance":"0", + "extraBalanceAccount":"0x4fd6ace747f06ece9c49699c7cabc62d02211f75" + }, + { + "address":"0x440c59b325d2997a134c2c7c60a8c61611212bad", + "balance":"e77583a3958130e53", + "extraBalance":"0", + "extraBalanceAccount":"0x4486a3d68fac6967006d7a517b889fd3f98c102b" + }, + { + "address":"0x9c15b54878ba618f494b38f0ae7443db6af648ba", + "balance":"1f0b6ade348ca998", + "extraBalance":"0", + "extraBalanceAccount":"0x27b137a85656544b1ccb5a0f2e561a5703c6a68f" + }, + { + "address":"0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "balance":"61725880736659", + "extraBalance":"0", + "extraBalanceAccount":"0x23b75c2f6791eef49c69684db4c6c1f93bf49a50" + }, + { + "address":"0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "balance":"42948d8dc7ddbc22d", + "extraBalance":"0", + "extraBalanceAccount":"0xb9637156d330c0d605a791f1c31ba5890582fe1c" + }, + { + "address":"0x6131c42fa982e56929107413a9d526fd99405560", + "balance":"7306683851d1eafbfa", + "extraBalance":"0", + "extraBalanceAccount":"0x1591fc0f688c81fbeb17f5426a162a7024d430c2" + }, + { + "address":"0x542a9515200d14b68e934e9830d91645a980dd7a", + "balance":"2a8457d0d8432e21d0c", + "extraBalance":"0", + "extraBalanceAccount":"0xc4bbd073882dd2add2424cf47d35213405b01324" + }, + { + "address":"0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "balance":"d8d7391feaeaa8cdb", + "extraBalance":"0", + "extraBalanceAccount":"0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb" + }, + { + "address":"0x3ba4d81db016dc2890c81f3acec2454bff5aada5", + "balance":"1", + "extraBalance":"0", + "extraBalanceAccount":"0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab" + }, + { + "address":"0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", + "balance":"456397665fa74041", + "extraBalance":"0", + "extraBalanceAccount":"0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5" + }, + { + "address":"0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "balance":"6324dcb7126ecbef", + "extraBalance":"0", + "extraBalanceAccount":"0x97f43a37f595ab5dd318fb46e7a155eae057317a" + }, + { + "address":"0x52c5317c848ba20c7504cb2c8052abd1fde29d03", + "balance":"6c3419b0705c01cd0d", + "extraBalance":"0", + "extraBalanceAccount":"0x4863226780fe7c0356454236d3b1c8792785748d" + }, + { + "address":"0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "balance":"456397665fa74041", + "extraBalance":"0", + "extraBalanceAccount":"0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c" + }, + { + "address":"0x057b56736d32b86616a10f619859c6cd6f59092a", + "balance":"232c025bb44b46", + "extraBalance":"0", + "extraBalanceAccount":"0x9aa008f65de0b923a2a4f02012ad034a5e2e2192" + }, + { + "address":"0x304a554a310c7e546dfe434669c62820b7d83490", + "balance":"3034f5ca7d45e17df199b", + "extraBalance":"f7d15162c44e97b6e", + "extraBalanceAccount":"0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79" + }, + { + "address":"0x4deb0033bb26bc534b197e61d19e0733e5679784", + "balance":"4417e96ed796591e09", + "extraBalance":"0", + "extraBalanceAccount":"0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a" + }, + { + "address":"0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "balance":"d3ff7771412bbcc9", + "extraBalance":"0", + "extraBalanceAccount":"0x4fa802324e929786dbda3b8820dc7834e9134a2a" + }, + { + "address":"0x9da397b9e80755301a3b32173283a91c0ef6c87e", + "balance":"32ae324c233816b4c2", + "extraBalance":"0", + "extraBalanceAccount":"0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6" + }, + { + "address":"0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "balance":"1e530695b705f037c6", + "extraBalance":"0", + "extraBalanceAccount":"0x5dc28b15dffed94048d73806ce4b7a4612a1d48f" + }, + { + "address":"0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "balance":"68013bad5b4b1133fc5", + "extraBalance":"0", + "extraBalanceAccount":"0x12e626b0eebfe86a56d633b9864e389b45dcb260" + }, + { + "address":"0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "balance":"456397665fa74041", + "extraBalance":"0", + "extraBalanceAccount":"0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5" + }, + { + "address":"0xd164b088bd9108b60d0ca3751da4bceb207b0782", + "balance":"3635ce47fabaaa336e", + "extraBalance":"0", + "extraBalanceAccount":"0x6231b6d0d5e77fe001c2a460bd9584fee60d409b" + }, + { + "address":"0x1cba23d343a983e9b5cfd19496b9a9701ada385f", + "balance":"f3abd9906c170a", + "extraBalance":"0", + "extraBalanceAccount":"0xa82f360a8d3455c5c41366975bde739c37bfeb8a" + }, + { + "address":"0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "balance":"4c6679d9d9b95a4e08", + "extraBalance":"0", + "extraBalanceAccount":"0x005f5cee7a43331d5a3d3eec71305925a62f34b6" + }, + { + "address":"0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "balance":"40f622936475de31849", + "extraBalance":"671e1bbabded39754", + "extraBalanceAccount":"0xd131637d5275fd1a68a3200f4ad25c71a2a9522e" + }, + { + "address":"0xbc07118b9ac290e4622f5e77a0853539789effbe", + "balance":"1316ccfa4a35db5e58f", + "extraBalance":"0", + "extraBalanceAccount":"0x47e7aa56d6bdf3f36be34619660de61275420af8" + }, + { + "address":"0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", + "balance":"b3ad6bb72000bab9f", + "extraBalance":"0", + "extraBalanceAccount":"0xadf80daec7ba8dcf15392f1ac611fff65d94f880" + }, + { + "address":"0x5524c55fb03cf21f549444ccbecb664d0acad706", + "balance":"16f2da372a5c8a70967", + "extraBalance":"0", + "extraBalanceAccount":"0x40b803a9abce16f50f36a77ba41180eb90023925" + }, + { + "address":"0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", + "balance":"ea0b1bdc78f500a43", + "extraBalance":"0", + "extraBalanceAccount":"0x17802f43a0137c506ba92291391a8a8f207f487d" + }, + { + "address":"0x253488078a4edf4d6f42f113d1e62836a942cf1a", + "balance":"3060e3aed135cc80", + "extraBalance":"0", + "extraBalanceAccount":"0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915" + }, + { + "address":"0xb136707642a4ea12fb4bae820f03d2562ebff487", + "balance":"6050bdeb3354b5c98adc3", + "extraBalance":"0", + "extraBalanceAccount":"0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940" + }, + { + "address":"0xf14c14075d6c4ed84b86798af0956deef67365b5", + "balance":"1d77844e94c25ba2", + "extraBalance":"0", + "extraBalanceAccount":"0xca544e5c4687d109611d0f8f928b53a25af72448" + }, + { + "address":"0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "balance":"2e93a72de4fc5ec0ed", + "extraBalance":"0", + "extraBalanceAccount":"0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7" + }, + { + "address":"0x6d87578288b6cb5549d5076a207456a1f6a63dc0", + "balance":"1afd340799e48c18", + "extraBalance":"0", + "extraBalanceAccount":"0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e" + }, + { + "address":"0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "balance":"14d0944eb3be947a8", + "extraBalance":"0", + "extraBalanceAccount":"0x2b3455ec7fedf16e646268bf88846bd7a2319bb2" + }, + { + "address":"0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "balance":"6202b236a200e365eba", + "extraBalance":"11979be9020f03ec4ec", + "extraBalanceAccount":"0xd343b217de44030afaa275f54d31a9317c7f441e" + }, + { + "address":"0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "balance":"7ed634ebbba531901e07", + "extraBalance":"f9c5eff28cb08720c85", + "extraBalanceAccount":"0xda2fef9e4a3230988ff17df2165440f37e8b1708" + }, + { + "address":"0xf4c64518ea10f995918a454158c6b61407ea345c", + "balance":"39152e15508a96ff894a", + "extraBalance":"14041ca908bcc185c8", + "extraBalanceAccount":"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97" + }, + { + "address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "balance":"1", + "extraBalance":"5553ebc", + "extraBalanceAccount":"0x807640a13483f8ac783c557fcdf27be11ea4ac7a" + } +] +` diff --git a/vendor/github.com/ethereum/go-ethereum/params/gas_table.go b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go new file mode 100644 index 0000000..a060539 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/gas_table.go @@ -0,0 +1,77 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +type GasTable struct { + ExtcodeSize uint64 + ExtcodeCopy uint64 + Balance uint64 + SLoad uint64 + Calls uint64 + Suicide uint64 + + ExpByte uint64 + + // CreateBySuicide occurs when the + // refunded account is one that does + // not exist. This logic is similar + // to call. May be left nil. Nil means + // not charged. + CreateBySuicide uint64 +} + +var ( + // GasTableHomestead contain the gas prices for + // the homestead phase. + GasTableHomestead = GasTable{ + ExtcodeSize: 20, + ExtcodeCopy: 20, + Balance: 20, + SLoad: 50, + Calls: 40, + Suicide: 0, + ExpByte: 10, + } + + // GasTableHomestead contain the gas re-prices for + // the homestead phase. + // + // TODO rename to GasTableEIP150 + GasTableHomesteadGasRepriceFork = GasTable{ + ExtcodeSize: 700, + ExtcodeCopy: 700, + Balance: 400, + SLoad: 200, + Calls: 700, + Suicide: 5000, + ExpByte: 10, + + CreateBySuicide: 25000, + } + + GasTableEIP158 = GasTable{ + ExtcodeSize: 700, + ExtcodeCopy: 700, + Balance: 400, + SLoad: 200, + Calls: 700, + Suicide: 5000, + ExpByte: 50, + + CreateBySuicide: 25000, + } +) diff --git a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go new file mode 100644 index 0000000..f48bf49 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go @@ -0,0 +1,75 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import "math/big" + +const ( + MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. + ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. + SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. + CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. + CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. + TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. + TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. + TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. + QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. + SstoreSetGas uint64 = 20000 // Once per SLOAD operation. + LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. + CallStipend uint64 = 2300 // Free gas given at beginning of call. + EcrecoverGas uint64 = 3000 // + Sha256WordGas uint64 = 12 // + + Sha3Gas uint64 = 30 // Once per SHA3 operation. + Sha256Gas uint64 = 60 // + IdentityWordGas uint64 = 3 // + Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. + SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. + SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. + JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. + IdentityGas uint64 = 15 // + EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. + CallGas uint64 = 40 // Once per CALL operation & message call transaction. + CreateDataGas uint64 = 200 // + Ripemd160Gas uint64 = 600 // + Ripemd160WordGas uint64 = 120 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + ExpGas uint64 = 10 // Once per EXP instruction + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + TierStepGas uint64 = 0 // Once per operation, for a selection of them. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + + MaxCodeSize = 24576 +) + +var ( + GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations. + MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. + GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. + TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target + DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. + GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. + MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. + DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. +) diff --git a/vendor/github.com/ethereum/go-ethereum/params/util.go b/vendor/github.com/ethereum/go-ethereum/params/util.go new file mode 100644 index 0000000..546ebb3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/util.go @@ -0,0 +1,43 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package params + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + TestNetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on + MainNetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on + + TestNetHomesteadBlock = big.NewInt(0) // Testnet homestead block + MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block + + TestNetHomesteadGasRepriceBlock = big.NewInt(0) // Testnet gas reprice block + MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Mainnet gas reprice block + + TestNetHomesteadGasRepriceHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet gas reprice block hash (used by fast sync) + MainNetHomesteadGasRepriceHash = common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0") // Mainnet gas reprice block hash (used by fast sync) + + TestNetSpuriousDragon = big.NewInt(10) + MainNetSpuriousDragon = big.NewInt(2675000) + + TestNetChainID = big.NewInt(3) // Test net default chain ID + MainNetChainID = big.NewInt(1) // main net default chain ID +) diff --git a/vendor/github.com/ethereum/go-ethereum/params/version.go b/vendor/github.com/ethereum/go-ethereum/params/version.go new file mode 100644 index 0000000..fef3604 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/params/version.go @@ -0,0 +1,35 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package params + +import "fmt" + +const ( + VersionMajor = 1 // Major version component of the current release + VersionMinor = 6 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release + VersionMeta = "unstable" // Version metadata to append to the version string +) + +// Version holds the textual version string. +var Version = func() string { + v := fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) + if VersionMeta != "" { + v += "-" + VersionMeta + } + return v +}() diff --git a/vendor/github.com/ethereum/go-ethereum/pow/block.go b/vendor/github.com/ethereum/go-ethereum/pow/block.go new file mode 100644 index 0000000..1180711 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/pow/block.go @@ -0,0 +1,37 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package pow + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type Block interface { + Difficulty() *big.Int + HashNoNonce() common.Hash + Nonce() uint64 + MixDigest() common.Hash + NumberU64() uint64 +} + +type ChainManager interface { + GetBlockByNumber(uint64) *types.Block + CurrentBlock() *types.Block +} diff --git a/vendor/github.com/ethereum/go-ethereum/pow/pow.go b/vendor/github.com/ethereum/go-ethereum/pow/pow.go new file mode 100644 index 0000000..592d964 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/pow/pow.go @@ -0,0 +1,24 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package pow + +type PoW interface { + Search(block Block, stop <-chan struct{}, index int) (uint64, []byte) + Verify(block Block) bool + GetHashrate() int64 + Turbo(bool) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/decode.go b/vendor/github.com/ethereum/go-ethereum/rlp/decode.go new file mode 100644 index 0000000..c4e5869 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/decode.go @@ -0,0 +1,1037 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strings" +) + +var ( + errNoPointer = errors.New("rlp: interface given to Decode must be a pointer") + errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil") +) + +// Decoder is implemented by types that require custom RLP +// decoding rules or need to decode into private fields. +// +// The DecodeRLP method should read one value from the given +// Stream. It is not forbidden to read less or more, but it might +// be confusing. +type Decoder interface { + DecodeRLP(*Stream) error +} + +// Decode parses RLP-encoded data from r and stores the result in the +// value pointed to by val. Val must be a non-nil pointer. If r does +// not implement ByteReader, Decode will do its own buffering. +// +// Decode uses the following type-dependent decoding rules: +// +// If the type implements the Decoder interface, decode calls +// DecodeRLP. +// +// To decode into a pointer, Decode will decode into the value pointed +// to. If the pointer is nil, a new value of the pointer's element +// type is allocated. If the pointer is non-nil, the existing value +// will reused. +// +// To decode into a struct, Decode expects the input to be an RLP +// list. The decoded elements of the list are assigned to each public +// field in the order given by the struct's definition. The input list +// must contain an element for each decoded field. Decode returns an +// error if there are too few or too many elements. +// +// The decoding of struct fields honours two struct tags, "tail" and +// "nil". For an explanation of "tail", see the example. +// The "nil" tag applies to pointer-typed fields and changes the +// decoding rules for the field such that input values of size zero +// decode as a nil pointer. This tag can be useful when decoding +// recursive types. +// +// type StructWithEmptyOK struct { +// Foo *[20]byte `rlp:"nil"` +// } +// +// To decode into a slice, the input must be a list and the resulting +// slice will contain the input elements in order. For byte slices, +// the input must be an RLP string. Array types decode similarly, with +// the additional restriction that the number of input elements (or +// bytes) must match the array's length. +// +// To decode into a Go string, the input must be an RLP string. The +// input bytes are taken as-is and will not necessarily be valid UTF-8. +// +// To decode into an unsigned integer type, the input must also be an RLP +// string. The bytes are interpreted as a big endian representation of +// the integer. If the RLP string is larger than the bit size of the +// type, Decode will return an error. Decode also supports *big.Int. +// There is no size limit for big integers. +// +// To decode into an interface value, Decode stores one of these +// in the value: +// +// []interface{}, for RLP lists +// []byte, for RLP strings +// +// Non-empty interface types are not supported, nor are booleans, +// signed integers, floating point numbers, maps, channels and +// functions. +// +// Note that Decode does not set an input limit for all readers +// and may be vulnerable to panics cause by huge value sizes. If +// you need an input limit, use +// +// NewStream(r, limit).Decode(val) +func Decode(r io.Reader, val interface{}) error { + // TODO: this could use a Stream from a pool. + return NewStream(r, 0).Decode(val) +} + +// DecodeBytes parses RLP data from b into val. +// Please see the documentation of Decode for the decoding rules. +// The input must contain exactly one value and no trailing data. +func DecodeBytes(b []byte, val interface{}) error { + // TODO: this could use a Stream from a pool. + r := bytes.NewReader(b) + if err := NewStream(r, uint64(len(b))).Decode(val); err != nil { + return err + } + if r.Len() > 0 { + return ErrMoreThanOneValue + } + return nil +} + +type decodeError struct { + msg string + typ reflect.Type + ctx []string +} + +func (err *decodeError) Error() string { + ctx := "" + if len(err.ctx) > 0 { + ctx = ", decoding into " + for i := len(err.ctx) - 1; i >= 0; i-- { + ctx += err.ctx[i] + } + } + return fmt.Sprintf("rlp: %s for %v%s", err.msg, err.typ, ctx) +} + +func wrapStreamError(err error, typ reflect.Type) error { + switch err { + case ErrCanonInt: + return &decodeError{msg: "non-canonical integer (leading zero bytes)", typ: typ} + case ErrCanonSize: + return &decodeError{msg: "non-canonical size information", typ: typ} + case ErrExpectedList: + return &decodeError{msg: "expected input list", typ: typ} + case ErrExpectedString: + return &decodeError{msg: "expected input string or byte", typ: typ} + case errUintOverflow: + return &decodeError{msg: "input string too long", typ: typ} + case errNotAtEOL: + return &decodeError{msg: "input list has too many elements", typ: typ} + } + return err +} + +func addErrorContext(err error, ctx string) error { + if decErr, ok := err.(*decodeError); ok { + decErr.ctx = append(decErr.ctx, ctx) + } + return err +} + +var ( + decoderInterface = reflect.TypeOf(new(Decoder)).Elem() + bigInt = reflect.TypeOf(big.Int{}) +) + +func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { + kind := typ.Kind() + switch { + case typ == rawValueType: + return decodeRawValue, nil + case typ.Implements(decoderInterface): + return decodeDecoder, nil + case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface): + return decodeDecoderNoPtr, nil + case typ.AssignableTo(reflect.PtrTo(bigInt)): + return decodeBigInt, nil + case typ.AssignableTo(bigInt): + return decodeBigIntNoPtr, nil + case isUint(kind): + return decodeUint, nil + case kind == reflect.Bool: + return decodeBool, nil + case kind == reflect.String: + return decodeString, nil + case kind == reflect.Slice || kind == reflect.Array: + return makeListDecoder(typ, tags) + case kind == reflect.Struct: + return makeStructDecoder(typ) + case kind == reflect.Ptr: + if tags.nilOK { + return makeOptionalPtrDecoder(typ) + } + return makePtrDecoder(typ) + case kind == reflect.Interface: + return decodeInterface, nil + default: + return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ) + } +} + +func decodeRawValue(s *Stream, val reflect.Value) error { + r, err := s.Raw() + if err != nil { + return err + } + val.SetBytes(r) + return nil +} + +func decodeUint(s *Stream, val reflect.Value) error { + typ := val.Type() + num, err := s.uint(typ.Bits()) + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetUint(num) + return nil +} + +func decodeBool(s *Stream, val reflect.Value) error { + b, err := s.Bool() + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetBool(b) + return nil +} + +func decodeString(s *Stream, val reflect.Value) error { + b, err := s.Bytes() + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetString(string(b)) + return nil +} + +func decodeBigIntNoPtr(s *Stream, val reflect.Value) error { + return decodeBigInt(s, val.Addr()) +} + +func decodeBigInt(s *Stream, val reflect.Value) error { + b, err := s.Bytes() + if err != nil { + return wrapStreamError(err, val.Type()) + } + i := val.Interface().(*big.Int) + if i == nil { + i = new(big.Int) + val.Set(reflect.ValueOf(i)) + } + // Reject leading zero bytes + if len(b) > 0 && b[0] == 0 { + return wrapStreamError(ErrCanonInt, val.Type()) + } + i.SetBytes(b) + return nil +} + +func makeListDecoder(typ reflect.Type, tag tags) (decoder, error) { + etype := typ.Elem() + if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) { + if typ.Kind() == reflect.Array { + return decodeByteArray, nil + } else { + return decodeByteSlice, nil + } + } + etypeinfo, err := cachedTypeInfo1(etype, tags{}) + if err != nil { + return nil, err + } + var dec decoder + switch { + case typ.Kind() == reflect.Array: + dec = func(s *Stream, val reflect.Value) error { + return decodeListArray(s, val, etypeinfo.decoder) + } + case tag.tail: + // A slice with "tail" tag can occur as the last field + // of a struct and is upposed to swallow all remaining + // list elements. The struct decoder already called s.List, + // proceed directly to decoding the elements. + dec = func(s *Stream, val reflect.Value) error { + return decodeSliceElems(s, val, etypeinfo.decoder) + } + default: + dec = func(s *Stream, val reflect.Value) error { + return decodeListSlice(s, val, etypeinfo.decoder) + } + } + return dec, nil +} + +func decodeListSlice(s *Stream, val reflect.Value, elemdec decoder) error { + size, err := s.List() + if err != nil { + return wrapStreamError(err, val.Type()) + } + if size == 0 { + val.Set(reflect.MakeSlice(val.Type(), 0, 0)) + return s.ListEnd() + } + if err := decodeSliceElems(s, val, elemdec); err != nil { + return err + } + return s.ListEnd() +} + +func decodeSliceElems(s *Stream, val reflect.Value, elemdec decoder) error { + i := 0 + for ; ; i++ { + // grow slice if necessary + if i >= val.Cap() { + newcap := val.Cap() + val.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(val.Type(), val.Len(), newcap) + reflect.Copy(newv, val) + val.Set(newv) + } + if i >= val.Len() { + val.SetLen(i + 1) + } + // decode into element + if err := elemdec(s, val.Index(i)); err == EOL { + break + } else if err != nil { + return addErrorContext(err, fmt.Sprint("[", i, "]")) + } + } + if i < val.Len() { + val.SetLen(i) + } + return nil +} + +func decodeListArray(s *Stream, val reflect.Value, elemdec decoder) error { + if _, err := s.List(); err != nil { + return wrapStreamError(err, val.Type()) + } + vlen := val.Len() + i := 0 + for ; i < vlen; i++ { + if err := elemdec(s, val.Index(i)); err == EOL { + break + } else if err != nil { + return addErrorContext(err, fmt.Sprint("[", i, "]")) + } + } + if i < vlen { + return &decodeError{msg: "input list has too few elements", typ: val.Type()} + } + return wrapStreamError(s.ListEnd(), val.Type()) +} + +func decodeByteSlice(s *Stream, val reflect.Value) error { + b, err := s.Bytes() + if err != nil { + return wrapStreamError(err, val.Type()) + } + val.SetBytes(b) + return nil +} + +func decodeByteArray(s *Stream, val reflect.Value) error { + kind, size, err := s.Kind() + if err != nil { + return err + } + vlen := val.Len() + switch kind { + case Byte: + if vlen == 0 { + return &decodeError{msg: "input string too long", typ: val.Type()} + } + if vlen > 1 { + return &decodeError{msg: "input string too short", typ: val.Type()} + } + bv, _ := s.Uint() + val.Index(0).SetUint(bv) + case String: + if uint64(vlen) < size { + return &decodeError{msg: "input string too long", typ: val.Type()} + } + if uint64(vlen) > size { + return &decodeError{msg: "input string too short", typ: val.Type()} + } + slice := val.Slice(0, vlen).Interface().([]byte) + if err := s.readFull(slice); err != nil { + return err + } + // Reject cases where single byte encoding should have been used. + if size == 1 && slice[0] < 128 { + return wrapStreamError(ErrCanonSize, val.Type()) + } + case List: + return wrapStreamError(ErrExpectedString, val.Type()) + } + return nil +} + +func makeStructDecoder(typ reflect.Type) (decoder, error) { + fields, err := structFields(typ) + if err != nil { + return nil, err + } + dec := func(s *Stream, val reflect.Value) (err error) { + if _, err := s.List(); err != nil { + return wrapStreamError(err, typ) + } + for _, f := range fields { + err := f.info.decoder(s, val.Field(f.index)) + if err == EOL { + return &decodeError{msg: "too few elements", typ: typ} + } else if err != nil { + return addErrorContext(err, "."+typ.Field(f.index).Name) + } + } + return wrapStreamError(s.ListEnd(), typ) + } + return dec, nil +} + +// makePtrDecoder creates a decoder that decodes into +// the pointer's element type. +func makePtrDecoder(typ reflect.Type) (decoder, error) { + etype := typ.Elem() + etypeinfo, err := cachedTypeInfo1(etype, tags{}) + if err != nil { + return nil, err + } + dec := func(s *Stream, val reflect.Value) (err error) { + newval := val + if val.IsNil() { + newval = reflect.New(etype) + } + if err = etypeinfo.decoder(s, newval.Elem()); err == nil { + val.Set(newval) + } + return err + } + return dec, nil +} + +// makeOptionalPtrDecoder creates a decoder that decodes empty values +// as nil. Non-empty values are decoded into a value of the element type, +// just like makePtrDecoder does. +// +// This decoder is used for pointer-typed struct fields with struct tag "nil". +func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) { + etype := typ.Elem() + etypeinfo, err := cachedTypeInfo1(etype, tags{}) + if err != nil { + return nil, err + } + dec := func(s *Stream, val reflect.Value) (err error) { + kind, size, err := s.Kind() + if err != nil || size == 0 && kind != Byte { + // rearm s.Kind. This is important because the input + // position must advance to the next value even though + // we don't read anything. + s.kind = -1 + // set the pointer to nil. + val.Set(reflect.Zero(typ)) + return err + } + newval := val + if val.IsNil() { + newval = reflect.New(etype) + } + if err = etypeinfo.decoder(s, newval.Elem()); err == nil { + val.Set(newval) + } + return err + } + return dec, nil +} + +var ifsliceType = reflect.TypeOf([]interface{}{}) + +func decodeInterface(s *Stream, val reflect.Value) error { + if val.Type().NumMethod() != 0 { + return fmt.Errorf("rlp: type %v is not RLP-serializable", val.Type()) + } + kind, _, err := s.Kind() + if err != nil { + return err + } + if kind == List { + slice := reflect.New(ifsliceType).Elem() + if err := decodeListSlice(s, slice, decodeInterface); err != nil { + return err + } + val.Set(slice) + } else { + b, err := s.Bytes() + if err != nil { + return err + } + val.Set(reflect.ValueOf(b)) + } + return nil +} + +// This decoder is used for non-pointer values of types +// that implement the Decoder interface using a pointer receiver. +func decodeDecoderNoPtr(s *Stream, val reflect.Value) error { + return val.Addr().Interface().(Decoder).DecodeRLP(s) +} + +func decodeDecoder(s *Stream, val reflect.Value) error { + // Decoder instances are not handled using the pointer rule if the type + // implements Decoder with pointer receiver (i.e. always) + // because it might handle empty values specially. + // We need to allocate one here in this case, like makePtrDecoder does. + if val.Kind() == reflect.Ptr && val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + return val.Interface().(Decoder).DecodeRLP(s) +} + +// Kind represents the kind of value contained in an RLP stream. +type Kind int + +const ( + Byte Kind = iota + String + List +) + +func (k Kind) String() string { + switch k { + case Byte: + return "Byte" + case String: + return "String" + case List: + return "List" + default: + return fmt.Sprintf("Unknown(%d)", k) + } +} + +var ( + // EOL is returned when the end of the current list + // has been reached during streaming. + EOL = errors.New("rlp: end of list") + + // Actual Errors + ErrExpectedString = errors.New("rlp: expected String or Byte") + ErrExpectedList = errors.New("rlp: expected List") + ErrCanonInt = errors.New("rlp: non-canonical integer format") + ErrCanonSize = errors.New("rlp: non-canonical size information") + ErrElemTooLarge = errors.New("rlp: element is larger than containing list") + ErrValueTooLarge = errors.New("rlp: value size exceeds available input length") + + // This error is reported by DecodeBytes if the slice contains + // additional data after the first RLP value. + ErrMoreThanOneValue = errors.New("rlp: input contains more than one value") + + // internal errors + errNotInList = errors.New("rlp: call of ListEnd outside of any list") + errNotAtEOL = errors.New("rlp: call of ListEnd not positioned at EOL") + errUintOverflow = errors.New("rlp: uint overflow") +) + +// ByteReader must be implemented by any input reader for a Stream. It +// is implemented by e.g. bufio.Reader and bytes.Reader. +type ByteReader interface { + io.Reader + io.ByteReader +} + +// Stream can be used for piecemeal decoding of an input stream. This +// is useful if the input is very large or if the decoding rules for a +// type depend on the input structure. Stream does not keep an +// internal buffer. After decoding a value, the input reader will be +// positioned just before the type information for the next value. +// +// When decoding a list and the input position reaches the declared +// length of the list, all operations will return error EOL. +// The end of the list must be acknowledged using ListEnd to continue +// reading the enclosing list. +// +// Stream is not safe for concurrent use. +type Stream struct { + r ByteReader + + // number of bytes remaining to be read from r. + remaining uint64 + limited bool + + // auxiliary buffer for integer decoding + uintbuf []byte + + kind Kind // kind of value ahead + size uint64 // size of value ahead + byteval byte // value of single byte in type tag + kinderr error // error from last readKind + stack []listpos +} + +type listpos struct{ pos, size uint64 } + +// NewStream creates a new decoding stream reading from r. +// +// If r implements the ByteReader interface, Stream will +// not introduce any buffering. +// +// For non-toplevel values, Stream returns ErrElemTooLarge +// for values that do not fit into the enclosing list. +// +// Stream supports an optional input limit. If a limit is set, the +// size of any toplevel value will be checked against the remaining +// input length. Stream operations that encounter a value exceeding +// the remaining input length will return ErrValueTooLarge. The limit +// can be set by passing a non-zero value for inputLimit. +// +// If r is a bytes.Reader or strings.Reader, the input limit is set to +// the length of r's underlying data unless an explicit limit is +// provided. +func NewStream(r io.Reader, inputLimit uint64) *Stream { + s := new(Stream) + s.Reset(r, inputLimit) + return s +} + +// NewListStream creates a new stream that pretends to be positioned +// at an encoded list of the given length. +func NewListStream(r io.Reader, len uint64) *Stream { + s := new(Stream) + s.Reset(r, len) + s.kind = List + s.size = len + return s +} + +// Bytes reads an RLP string and returns its contents as a byte slice. +// If the input does not contain an RLP string, the returned +// error will be ErrExpectedString. +func (s *Stream) Bytes() ([]byte, error) { + kind, size, err := s.Kind() + if err != nil { + return nil, err + } + switch kind { + case Byte: + s.kind = -1 // rearm Kind + return []byte{s.byteval}, nil + case String: + b := make([]byte, size) + if err = s.readFull(b); err != nil { + return nil, err + } + if size == 1 && b[0] < 128 { + return nil, ErrCanonSize + } + return b, nil + default: + return nil, ErrExpectedString + } +} + +// Raw reads a raw encoded value including RLP type information. +func (s *Stream) Raw() ([]byte, error) { + kind, size, err := s.Kind() + if err != nil { + return nil, err + } + if kind == Byte { + s.kind = -1 // rearm Kind + return []byte{s.byteval}, nil + } + // the original header has already been read and is no longer + // available. read content and put a new header in front of it. + start := headsize(size) + buf := make([]byte, uint64(start)+size) + if err := s.readFull(buf[start:]); err != nil { + return nil, err + } + if kind == String { + puthead(buf, 0x80, 0xB8, size) + } else { + puthead(buf, 0xC0, 0xF7, size) + } + return buf, nil +} + +// Uint reads an RLP string of up to 8 bytes and returns its contents +// as an unsigned integer. If the input does not contain an RLP string, the +// returned error will be ErrExpectedString. +func (s *Stream) Uint() (uint64, error) { + return s.uint(64) +} + +func (s *Stream) uint(maxbits int) (uint64, error) { + kind, size, err := s.Kind() + if err != nil { + return 0, err + } + switch kind { + case Byte: + if s.byteval == 0 { + return 0, ErrCanonInt + } + s.kind = -1 // rearm Kind + return uint64(s.byteval), nil + case String: + if size > uint64(maxbits/8) { + return 0, errUintOverflow + } + v, err := s.readUint(byte(size)) + switch { + case err == ErrCanonSize: + // Adjust error because we're not reading a size right now. + return 0, ErrCanonInt + case err != nil: + return 0, err + case size > 0 && v < 128: + return 0, ErrCanonSize + default: + return v, nil + } + default: + return 0, ErrExpectedString + } +} + +// Bool reads an RLP string of up to 1 byte and returns its contents +// as an boolean. If the input does not contain an RLP string, the +// returned error will be ErrExpectedString. +func (s *Stream) Bool() (bool, error) { + num, err := s.uint(8) + if err != nil { + return false, err + } + switch num { + case 0: + return false, nil + case 1: + return true, nil + default: + return false, fmt.Errorf("rlp: invalid boolean value: %d", num) + } +} + +// List starts decoding an RLP list. If the input does not contain a +// list, the returned error will be ErrExpectedList. When the list's +// end has been reached, any Stream operation will return EOL. +func (s *Stream) List() (size uint64, err error) { + kind, size, err := s.Kind() + if err != nil { + return 0, err + } + if kind != List { + return 0, ErrExpectedList + } + s.stack = append(s.stack, listpos{0, size}) + s.kind = -1 + s.size = 0 + return size, nil +} + +// ListEnd returns to the enclosing list. +// The input reader must be positioned at the end of a list. +func (s *Stream) ListEnd() error { + if len(s.stack) == 0 { + return errNotInList + } + tos := s.stack[len(s.stack)-1] + if tos.pos != tos.size { + return errNotAtEOL + } + s.stack = s.stack[:len(s.stack)-1] // pop + if len(s.stack) > 0 { + s.stack[len(s.stack)-1].pos += tos.size + } + s.kind = -1 + s.size = 0 + return nil +} + +// Decode decodes a value and stores the result in the value pointed +// to by val. Please see the documentation for the Decode function +// to learn about the decoding rules. +func (s *Stream) Decode(val interface{}) error { + if val == nil { + return errDecodeIntoNil + } + rval := reflect.ValueOf(val) + rtyp := rval.Type() + if rtyp.Kind() != reflect.Ptr { + return errNoPointer + } + if rval.IsNil() { + return errDecodeIntoNil + } + info, err := cachedTypeInfo(rtyp.Elem(), tags{}) + if err != nil { + return err + } + + err = info.decoder(s, rval.Elem()) + if decErr, ok := err.(*decodeError); ok && len(decErr.ctx) > 0 { + // add decode target type to error so context has more meaning + decErr.ctx = append(decErr.ctx, fmt.Sprint("(", rtyp.Elem(), ")")) + } + return err +} + +// Reset discards any information about the current decoding context +// and starts reading from r. This method is meant to facilitate reuse +// of a preallocated Stream across many decoding operations. +// +// If r does not also implement ByteReader, Stream will do its own +// buffering. +func (s *Stream) Reset(r io.Reader, inputLimit uint64) { + if inputLimit > 0 { + s.remaining = inputLimit + s.limited = true + } else { + // Attempt to automatically discover + // the limit when reading from a byte slice. + switch br := r.(type) { + case *bytes.Reader: + s.remaining = uint64(br.Len()) + s.limited = true + case *strings.Reader: + s.remaining = uint64(br.Len()) + s.limited = true + default: + s.limited = false + } + } + // Wrap r with a buffer if it doesn't have one. + bufr, ok := r.(ByteReader) + if !ok { + bufr = bufio.NewReader(r) + } + s.r = bufr + // Reset the decoding context. + s.stack = s.stack[:0] + s.size = 0 + s.kind = -1 + s.kinderr = nil + if s.uintbuf == nil { + s.uintbuf = make([]byte, 8) + } +} + +// Kind returns the kind and size of the next value in the +// input stream. +// +// The returned size is the number of bytes that make up the value. +// For kind == Byte, the size is zero because the value is +// contained in the type tag. +// +// The first call to Kind will read size information from the input +// reader and leave it positioned at the start of the actual bytes of +// the value. Subsequent calls to Kind (until the value is decoded) +// will not advance the input reader and return cached information. +func (s *Stream) Kind() (kind Kind, size uint64, err error) { + var tos *listpos + if len(s.stack) > 0 { + tos = &s.stack[len(s.stack)-1] + } + if s.kind < 0 { + s.kinderr = nil + // Don't read further if we're at the end of the + // innermost list. + if tos != nil && tos.pos == tos.size { + return 0, 0, EOL + } + s.kind, s.size, s.kinderr = s.readKind() + if s.kinderr == nil { + if tos == nil { + // At toplevel, check that the value is smaller + // than the remaining input length. + if s.limited && s.size > s.remaining { + s.kinderr = ErrValueTooLarge + } + } else { + // Inside a list, check that the value doesn't overflow the list. + if s.size > tos.size-tos.pos { + s.kinderr = ErrElemTooLarge + } + } + } + } + // Note: this might return a sticky error generated + // by an earlier call to readKind. + return s.kind, s.size, s.kinderr +} + +func (s *Stream) readKind() (kind Kind, size uint64, err error) { + b, err := s.readByte() + if err != nil { + if len(s.stack) == 0 { + // At toplevel, Adjust the error to actual EOF. io.EOF is + // used by callers to determine when to stop decoding. + switch err { + case io.ErrUnexpectedEOF: + err = io.EOF + case ErrValueTooLarge: + err = io.EOF + } + } + return 0, 0, err + } + s.byteval = 0 + switch { + case b < 0x80: + // For a single byte whose value is in the [0x00, 0x7F] range, that byte + // is its own RLP encoding. + s.byteval = b + return Byte, 0, nil + case b < 0xB8: + // Otherwise, if a string is 0-55 bytes long, + // the RLP encoding consists of a single byte with value 0x80 plus the + // length of the string followed by the string. The range of the first + // byte is thus [0x80, 0xB7]. + return String, uint64(b - 0x80), nil + case b < 0xC0: + // If a string is more than 55 bytes long, the + // RLP encoding consists of a single byte with value 0xB7 plus the length + // of the length of the string in binary form, followed by the length of + // the string, followed by the string. For example, a length-1024 string + // would be encoded as 0xB90400 followed by the string. The range of + // the first byte is thus [0xB8, 0xBF]. + size, err = s.readUint(b - 0xB7) + if err == nil && size < 56 { + err = ErrCanonSize + } + return String, size, err + case b < 0xF8: + // If the total payload of a list + // (i.e. the combined length of all its items) is 0-55 bytes long, the + // RLP encoding consists of a single byte with value 0xC0 plus the length + // of the list followed by the concatenation of the RLP encodings of the + // items. The range of the first byte is thus [0xC0, 0xF7]. + return List, uint64(b - 0xC0), nil + default: + // If the total payload of a list is more than 55 bytes long, + // the RLP encoding consists of a single byte with value 0xF7 + // plus the length of the length of the payload in binary + // form, followed by the length of the payload, followed by + // the concatenation of the RLP encodings of the items. The + // range of the first byte is thus [0xF8, 0xFF]. + size, err = s.readUint(b - 0xF7) + if err == nil && size < 56 { + err = ErrCanonSize + } + return List, size, err + } +} + +func (s *Stream) readUint(size byte) (uint64, error) { + switch size { + case 0: + s.kind = -1 // rearm Kind + return 0, nil + case 1: + b, err := s.readByte() + return uint64(b), err + default: + start := int(8 - size) + for i := 0; i < start; i++ { + s.uintbuf[i] = 0 + } + if err := s.readFull(s.uintbuf[start:]); err != nil { + return 0, err + } + if s.uintbuf[start] == 0 { + // Note: readUint is also used to decode integer + // values. The error needs to be adjusted to become + // ErrCanonInt in this case. + return 0, ErrCanonSize + } + return binary.BigEndian.Uint64(s.uintbuf), nil + } +} + +func (s *Stream) readFull(buf []byte) (err error) { + if err := s.willRead(uint64(len(buf))); err != nil { + return err + } + var nn, n int + for n < len(buf) && err == nil { + nn, err = s.r.Read(buf[n:]) + n += nn + } + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return err +} + +func (s *Stream) readByte() (byte, error) { + if err := s.willRead(1); err != nil { + return 0, err + } + b, err := s.r.ReadByte() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return b, err +} + +func (s *Stream) willRead(n uint64) error { + s.kind = -1 // rearm Kind + + if len(s.stack) > 0 { + // check list overflow + tos := s.stack[len(s.stack)-1] + if n > tos.size-tos.pos { + return ErrElemTooLarge + } + s.stack[len(s.stack)-1].pos += n + } + if s.limited { + if n > s.remaining { + return ErrValueTooLarge + } + s.remaining -= n + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/decode_tail_test.go b/vendor/github.com/ethereum/go-ethereum/rlp/decode_tail_test.go new file mode 100644 index 0000000..884c114 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/decode_tail_test.go @@ -0,0 +1,49 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bytes" + "fmt" +) + +type structWithTail struct { + A, B uint + C []uint `rlp:"tail"` +} + +func ExampleDecode_structTagTail() { + // In this example, the "tail" struct tag is used to decode lists of + // differing length into a struct. + var val structWithTail + + err := Decode(bytes.NewReader([]byte{0xC4, 0x01, 0x02, 0x03, 0x04}), &val) + fmt.Printf("with 4 elements: err=%v val=%v\n", err, val) + + err = Decode(bytes.NewReader([]byte{0xC6, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}), &val) + fmt.Printf("with 6 elements: err=%v val=%v\n", err, val) + + // Note that at least two list elements must be present to + // fill fields A and B: + err = Decode(bytes.NewReader([]byte{0xC1, 0x01}), &val) + fmt.Printf("with 1 element: err=%q\n", err) + + // Output: + // with 4 elements: err= val={1 2 [3 4]} + // with 6 elements: err= val={1 2 [3 4 5 6]} + // with 1 element: err="rlp: too few elements for rlp.structWithTail" +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/decode_test.go b/vendor/github.com/ethereum/go-ethereum/rlp/decode_test.go new file mode 100644 index 0000000..2d465b7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/decode_test.go @@ -0,0 +1,791 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strings" + "testing" +) + +func TestStreamKind(t *testing.T) { + tests := []struct { + input string + wantKind Kind + wantLen uint64 + }{ + {"00", Byte, 0}, + {"01", Byte, 0}, + {"7F", Byte, 0}, + {"80", String, 0}, + {"B7", String, 55}, + {"B90400", String, 1024}, + {"BFFFFFFFFFFFFFFFFF", String, ^uint64(0)}, + {"C0", List, 0}, + {"C8", List, 8}, + {"F7", List, 55}, + {"F90400", List, 1024}, + {"FFFFFFFFFFFFFFFFFF", List, ^uint64(0)}, + } + + for i, test := range tests { + // using plainReader to inhibit input limit errors. + s := NewStream(newPlainReader(unhex(test.input)), 0) + kind, len, err := s.Kind() + if err != nil { + t.Errorf("test %d: Kind returned error: %v", i, err) + continue + } + if kind != test.wantKind { + t.Errorf("test %d: kind mismatch: got %d, want %d", i, kind, test.wantKind) + } + if len != test.wantLen { + t.Errorf("test %d: len mismatch: got %d, want %d", i, len, test.wantLen) + } + } +} + +func TestNewListStream(t *testing.T) { + ls := NewListStream(bytes.NewReader(unhex("0101010101")), 3) + if k, size, err := ls.Kind(); k != List || size != 3 || err != nil { + t.Errorf("Kind() returned (%v, %d, %v), expected (List, 3, nil)", k, size, err) + } + if size, err := ls.List(); size != 3 || err != nil { + t.Errorf("List() returned (%d, %v), expected (3, nil)", size, err) + } + for i := 0; i < 3; i++ { + if val, err := ls.Uint(); val != 1 || err != nil { + t.Errorf("Uint() returned (%d, %v), expected (1, nil)", val, err) + } + } + if err := ls.ListEnd(); err != nil { + t.Errorf("ListEnd() returned %v, expected (3, nil)", err) + } +} + +func TestStreamErrors(t *testing.T) { + withoutInputLimit := func(b []byte) *Stream { + return NewStream(newPlainReader(b), 0) + } + withCustomInputLimit := func(limit uint64) func([]byte) *Stream { + return func(b []byte) *Stream { + return NewStream(bytes.NewReader(b), limit) + } + } + + type calls []string + tests := []struct { + string + calls + newStream func([]byte) *Stream // uses bytes.Reader if nil + error error + }{ + {"C0", calls{"Bytes"}, nil, ErrExpectedString}, + {"C0", calls{"Uint"}, nil, ErrExpectedString}, + {"89000000000000000001", calls{"Uint"}, nil, errUintOverflow}, + {"00", calls{"List"}, nil, ErrExpectedList}, + {"80", calls{"List"}, nil, ErrExpectedList}, + {"C0", calls{"List", "Uint"}, nil, EOL}, + {"C8C9010101010101010101", calls{"List", "Kind"}, nil, ErrElemTooLarge}, + {"C3C2010201", calls{"List", "List", "Uint", "Uint", "ListEnd", "Uint"}, nil, EOL}, + {"00", calls{"ListEnd"}, nil, errNotInList}, + {"C401020304", calls{"List", "Uint", "ListEnd"}, nil, errNotAtEOL}, + + // Non-canonical integers (e.g. leading zero bytes). + {"00", calls{"Uint"}, nil, ErrCanonInt}, + {"820002", calls{"Uint"}, nil, ErrCanonInt}, + {"8133", calls{"Uint"}, nil, ErrCanonSize}, + {"817F", calls{"Uint"}, nil, ErrCanonSize}, + {"8180", calls{"Uint"}, nil, nil}, + + // Non-valid boolean + {"02", calls{"Bool"}, nil, errors.New("rlp: invalid boolean value: 2")}, + + // Size tags must use the smallest possible encoding. + // Leading zero bytes in the size tag are also rejected. + {"8100", calls{"Uint"}, nil, ErrCanonSize}, + {"8100", calls{"Bytes"}, nil, ErrCanonSize}, + {"8101", calls{"Bytes"}, nil, ErrCanonSize}, + {"817F", calls{"Bytes"}, nil, ErrCanonSize}, + {"8180", calls{"Bytes"}, nil, nil}, + {"B800", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"B90000", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"B90055", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"BA0002FFFF", calls{"Bytes"}, withoutInputLimit, ErrCanonSize}, + {"F800", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"F90000", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"F90055", calls{"Kind"}, withoutInputLimit, ErrCanonSize}, + {"FA0002FFFF", calls{"List"}, withoutInputLimit, ErrCanonSize}, + + // Expected EOF + {"", calls{"Kind"}, nil, io.EOF}, + {"", calls{"Uint"}, nil, io.EOF}, + {"", calls{"List"}, nil, io.EOF}, + {"8180", calls{"Uint", "Uint"}, nil, io.EOF}, + {"C0", calls{"List", "ListEnd", "List"}, nil, io.EOF}, + + {"", calls{"List"}, withoutInputLimit, io.EOF}, + {"8180", calls{"Uint", "Uint"}, withoutInputLimit, io.EOF}, + {"C0", calls{"List", "ListEnd", "List"}, withoutInputLimit, io.EOF}, + + // Input limit errors. + {"81", calls{"Bytes"}, nil, ErrValueTooLarge}, + {"81", calls{"Uint"}, nil, ErrValueTooLarge}, + {"81", calls{"Raw"}, nil, ErrValueTooLarge}, + {"BFFFFFFFFFFFFFFFFFFF", calls{"Bytes"}, nil, ErrValueTooLarge}, + {"C801", calls{"List"}, nil, ErrValueTooLarge}, + + // Test for list element size check overflow. + {"CD04040404FFFFFFFFFFFFFFFFFF0303", calls{"List", "Uint", "Uint", "Uint", "Uint", "List"}, nil, ErrElemTooLarge}, + + // Test for input limit overflow. Since we are counting the limit + // down toward zero in Stream.remaining, reading too far can overflow + // remaining to a large value, effectively disabling the limit. + {"C40102030401", calls{"Raw", "Uint"}, withCustomInputLimit(5), io.EOF}, + {"C4010203048180", calls{"Raw", "Uint"}, withCustomInputLimit(6), ErrValueTooLarge}, + + // Check that the same calls are fine without a limit. + {"C40102030401", calls{"Raw", "Uint"}, withoutInputLimit, nil}, + {"C4010203048180", calls{"Raw", "Uint"}, withoutInputLimit, nil}, + + // Unexpected EOF. This only happens when there is + // no input limit, so the reader needs to be 'dumbed down'. + {"81", calls{"Bytes"}, withoutInputLimit, io.ErrUnexpectedEOF}, + {"81", calls{"Uint"}, withoutInputLimit, io.ErrUnexpectedEOF}, + {"BFFFFFFFFFFFFFFF", calls{"Bytes"}, withoutInputLimit, io.ErrUnexpectedEOF}, + {"C801", calls{"List", "Uint", "Uint"}, withoutInputLimit, io.ErrUnexpectedEOF}, + + // This test verifies that the input position is advanced + // correctly when calling Bytes for empty strings. Kind can be called + // any number of times in between and doesn't advance. + {"C3808080", calls{ + "List", // enter the list + "Bytes", // past first element + + "Kind", "Kind", "Kind", // this shouldn't advance + + "Bytes", // past second element + + "Kind", "Kind", // can't hurt to try + + "Bytes", // past final element + "Bytes", // this one should fail + }, nil, EOL}, + } + +testfor: + for i, test := range tests { + if test.newStream == nil { + test.newStream = func(b []byte) *Stream { return NewStream(bytes.NewReader(b), 0) } + } + s := test.newStream(unhex(test.string)) + rs := reflect.ValueOf(s) + for j, call := range test.calls { + fval := rs.MethodByName(call) + ret := fval.Call(nil) + err := "" + if lastret := ret[len(ret)-1].Interface(); lastret != nil { + err = lastret.(error).Error() + } + if j == len(test.calls)-1 { + want := "" + if test.error != nil { + want = test.error.Error() + } + if err != want { + t.Log(test) + t.Errorf("test %d: last call (%s) error mismatch\ngot: %s\nwant: %s", + i, call, err, test.error) + } + } else if err != "" { + t.Log(test) + t.Errorf("test %d: call %d (%s) unexpected error: %q", i, j, call, err) + continue testfor + } + } + } +} + +func TestStreamList(t *testing.T) { + s := NewStream(bytes.NewReader(unhex("C80102030405060708")), 0) + + len, err := s.List() + if err != nil { + t.Fatalf("List error: %v", err) + } + if len != 8 { + t.Fatalf("List returned invalid length, got %d, want 8", len) + } + + for i := uint64(1); i <= 8; i++ { + v, err := s.Uint() + if err != nil { + t.Fatalf("Uint error: %v", err) + } + if i != v { + t.Errorf("Uint returned wrong value, got %d, want %d", v, i) + } + } + + if _, err := s.Uint(); err != EOL { + t.Errorf("Uint error mismatch, got %v, want %v", err, EOL) + } + if err = s.ListEnd(); err != nil { + t.Fatalf("ListEnd error: %v", err) + } +} + +func TestStreamRaw(t *testing.T) { + s := NewStream(bytes.NewReader(unhex("C58401010101")), 0) + s.List() + + want := unhex("8401010101") + raw, err := s.Raw() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, raw) { + t.Errorf("raw mismatch: got %x, want %x", raw, want) + } +} + +func TestDecodeErrors(t *testing.T) { + r := bytes.NewReader(nil) + + if err := Decode(r, nil); err != errDecodeIntoNil { + t.Errorf("Decode(r, nil) error mismatch, got %q, want %q", err, errDecodeIntoNil) + } + + var nilptr *struct{} + if err := Decode(r, nilptr); err != errDecodeIntoNil { + t.Errorf("Decode(r, nilptr) error mismatch, got %q, want %q", err, errDecodeIntoNil) + } + + if err := Decode(r, struct{}{}); err != errNoPointer { + t.Errorf("Decode(r, struct{}{}) error mismatch, got %q, want %q", err, errNoPointer) + } + + expectErr := "rlp: type chan bool is not RLP-serializable" + if err := Decode(r, new(chan bool)); err == nil || err.Error() != expectErr { + t.Errorf("Decode(r, new(chan bool)) error mismatch, got %q, want %q", err, expectErr) + } + + if err := Decode(r, new(uint)); err != io.EOF { + t.Errorf("Decode(r, new(int)) error mismatch, got %q, want %q", err, io.EOF) + } +} + +type decodeTest struct { + input string + ptr interface{} + value interface{} + error string +} + +type simplestruct struct { + A uint + B string +} + +type recstruct struct { + I uint + Child *recstruct `rlp:"nil"` +} + +type invalidTail1 struct { + A uint `rlp:"tail"` + B string +} + +type invalidTail2 struct { + A uint + B string `rlp:"tail"` +} + +type tailRaw struct { + A uint + Tail []RawValue `rlp:"tail"` +} + +type tailUint struct { + A uint + Tail []uint `rlp:"tail"` +} + +var ( + veryBigInt = big.NewInt(0).Add( + big.NewInt(0).Lsh(big.NewInt(0xFFFFFFFFFFFFFF), 16), + big.NewInt(0xFFFF), + ) +) + +var decodeTests = []decodeTest{ + // booleans + {input: "01", ptr: new(bool), value: true}, + {input: "80", ptr: new(bool), value: false}, + {input: "02", ptr: new(bool), error: "rlp: invalid boolean value: 2"}, + + // integers + {input: "05", ptr: new(uint32), value: uint32(5)}, + {input: "80", ptr: new(uint32), value: uint32(0)}, + {input: "820505", ptr: new(uint32), value: uint32(0x0505)}, + {input: "83050505", ptr: new(uint32), value: uint32(0x050505)}, + {input: "8405050505", ptr: new(uint32), value: uint32(0x05050505)}, + {input: "850505050505", ptr: new(uint32), error: "rlp: input string too long for uint32"}, + {input: "C0", ptr: new(uint32), error: "rlp: expected input string or byte for uint32"}, + {input: "00", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"}, + {input: "8105", ptr: new(uint32), error: "rlp: non-canonical size information for uint32"}, + {input: "820004", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"}, + {input: "B8020004", ptr: new(uint32), error: "rlp: non-canonical size information for uint32"}, + + // slices + {input: "C0", ptr: new([]uint), value: []uint{}}, + {input: "C80102030405060708", ptr: new([]uint), value: []uint{1, 2, 3, 4, 5, 6, 7, 8}}, + {input: "F8020004", ptr: new([]uint), error: "rlp: non-canonical size information for []uint"}, + + // arrays + {input: "C50102030405", ptr: new([5]uint), value: [5]uint{1, 2, 3, 4, 5}}, + {input: "C0", ptr: new([5]uint), error: "rlp: input list has too few elements for [5]uint"}, + {input: "C102", ptr: new([5]uint), error: "rlp: input list has too few elements for [5]uint"}, + {input: "C6010203040506", ptr: new([5]uint), error: "rlp: input list has too many elements for [5]uint"}, + {input: "F8020004", ptr: new([5]uint), error: "rlp: non-canonical size information for [5]uint"}, + + // zero sized arrays + {input: "C0", ptr: new([0]uint), value: [0]uint{}}, + {input: "C101", ptr: new([0]uint), error: "rlp: input list has too many elements for [0]uint"}, + + // byte slices + {input: "01", ptr: new([]byte), value: []byte{1}}, + {input: "80", ptr: new([]byte), value: []byte{}}, + {input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")}, + {input: "C0", ptr: new([]byte), error: "rlp: expected input string or byte for []uint8"}, + {input: "8105", ptr: new([]byte), error: "rlp: non-canonical size information for []uint8"}, + + // byte arrays + {input: "02", ptr: new([1]byte), value: [1]byte{2}}, + {input: "8180", ptr: new([1]byte), value: [1]byte{128}}, + {input: "850102030405", ptr: new([5]byte), value: [5]byte{1, 2, 3, 4, 5}}, + + // byte array errors + {input: "02", ptr: new([5]byte), error: "rlp: input string too short for [5]uint8"}, + {input: "80", ptr: new([5]byte), error: "rlp: input string too short for [5]uint8"}, + {input: "820000", ptr: new([5]byte), error: "rlp: input string too short for [5]uint8"}, + {input: "C0", ptr: new([5]byte), error: "rlp: expected input string or byte for [5]uint8"}, + {input: "C3010203", ptr: new([5]byte), error: "rlp: expected input string or byte for [5]uint8"}, + {input: "86010203040506", ptr: new([5]byte), error: "rlp: input string too long for [5]uint8"}, + {input: "8105", ptr: new([1]byte), error: "rlp: non-canonical size information for [1]uint8"}, + {input: "817F", ptr: new([1]byte), error: "rlp: non-canonical size information for [1]uint8"}, + + // zero sized byte arrays + {input: "80", ptr: new([0]byte), value: [0]byte{}}, + {input: "01", ptr: new([0]byte), error: "rlp: input string too long for [0]uint8"}, + {input: "8101", ptr: new([0]byte), error: "rlp: input string too long for [0]uint8"}, + + // strings + {input: "00", ptr: new(string), value: "\000"}, + {input: "8D6162636465666768696A6B6C6D", ptr: new(string), value: "abcdefghijklm"}, + {input: "C0", ptr: new(string), error: "rlp: expected input string or byte for string"}, + + // big ints + {input: "01", ptr: new(*big.Int), value: big.NewInt(1)}, + {input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt}, + {input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works + {input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"}, + {input: "820001", ptr: new(big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"}, + {input: "8105", ptr: new(big.Int), error: "rlp: non-canonical size information for *big.Int"}, + + // structs + { + input: "C50583343434", + ptr: new(simplestruct), + value: simplestruct{5, "444"}, + }, + { + input: "C601C402C203C0", + ptr: new(recstruct), + value: recstruct{1, &recstruct{2, &recstruct{3, nil}}}, + }, + + // struct errors + { + input: "C0", + ptr: new(simplestruct), + error: "rlp: too few elements for rlp.simplestruct", + }, + { + input: "C105", + ptr: new(simplestruct), + error: "rlp: too few elements for rlp.simplestruct", + }, + { + input: "C7C50583343434C0", + ptr: new([]*simplestruct), + error: "rlp: too few elements for rlp.simplestruct, decoding into ([]*rlp.simplestruct)[1]", + }, + { + input: "83222222", + ptr: new(simplestruct), + error: "rlp: expected input list for rlp.simplestruct", + }, + { + input: "C3010101", + ptr: new(simplestruct), + error: "rlp: input list has too many elements for rlp.simplestruct", + }, + { + input: "C501C3C00000", + ptr: new(recstruct), + error: "rlp: expected input string or byte for uint, decoding into (rlp.recstruct).Child.I", + }, + { + input: "C0", + ptr: new(invalidTail1), + error: "rlp: invalid struct tag \"tail\" for rlp.invalidTail1.A (must be on last field)", + }, + { + input: "C0", + ptr: new(invalidTail2), + error: "rlp: invalid struct tag \"tail\" for rlp.invalidTail2.B (field type is not slice)", + }, + { + input: "C50102C20102", + ptr: new(tailUint), + error: "rlp: expected input string or byte for uint, decoding into (rlp.tailUint).Tail[1]", + }, + + // struct tag "tail" + { + input: "C3010203", + ptr: new(tailRaw), + value: tailRaw{A: 1, Tail: []RawValue{unhex("02"), unhex("03")}}, + }, + { + input: "C20102", + ptr: new(tailRaw), + value: tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, + }, + { + input: "C101", + ptr: new(tailRaw), + value: tailRaw{A: 1, Tail: []RawValue{}}, + }, + + // RawValue + {input: "01", ptr: new(RawValue), value: RawValue(unhex("01"))}, + {input: "82FFFF", ptr: new(RawValue), value: RawValue(unhex("82FFFF"))}, + {input: "C20102", ptr: new([]RawValue), value: []RawValue{unhex("01"), unhex("02")}}, + + // pointers + {input: "00", ptr: new(*[]byte), value: &[]byte{0}}, + {input: "80", ptr: new(*uint), value: uintp(0)}, + {input: "C0", ptr: new(*uint), error: "rlp: expected input string or byte for uint"}, + {input: "07", ptr: new(*uint), value: uintp(7)}, + {input: "817F", ptr: new(*uint), error: "rlp: non-canonical size information for uint"}, + {input: "8180", ptr: new(*uint), value: uintp(0x80)}, + {input: "C109", ptr: new(*[]uint), value: &[]uint{9}}, + {input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}}, + + // check that input position is advanced also for empty values. + {input: "C3808005", ptr: new([]*uint), value: []*uint{uintp(0), uintp(0), uintp(5)}}, + + // interface{} + {input: "00", ptr: new(interface{}), value: []byte{0}}, + {input: "01", ptr: new(interface{}), value: []byte{1}}, + {input: "80", ptr: new(interface{}), value: []byte{}}, + {input: "850505050505", ptr: new(interface{}), value: []byte{5, 5, 5, 5, 5}}, + {input: "C0", ptr: new(interface{}), value: []interface{}{}}, + {input: "C50183040404", ptr: new(interface{}), value: []interface{}{[]byte{1}, []byte{4, 4, 4}}}, + { + input: "C3010203", + ptr: new([]io.Reader), + error: "rlp: type io.Reader is not RLP-serializable", + }, + + // fuzzer crashes + { + input: "c330f9c030f93030ce3030303030303030bd303030303030", + ptr: new(interface{}), + error: "rlp: element is larger than containing list", + }, +} + +func uintp(i uint) *uint { return &i } + +func runTests(t *testing.T, decode func([]byte, interface{}) error) { + for i, test := range decodeTests { + input, err := hex.DecodeString(test.input) + if err != nil { + t.Errorf("test %d: invalid hex input %q", i, test.input) + continue + } + err = decode(input, test.ptr) + if err != nil && test.error == "" { + t.Errorf("test %d: unexpected Decode error: %v\ndecoding into %T\ninput %q", + i, err, test.ptr, test.input) + continue + } + if test.error != "" && fmt.Sprint(err) != test.error { + t.Errorf("test %d: Decode error mismatch\ngot %v\nwant %v\ndecoding into %T\ninput %q", + i, err, test.error, test.ptr, test.input) + continue + } + deref := reflect.ValueOf(test.ptr).Elem().Interface() + if err == nil && !reflect.DeepEqual(deref, test.value) { + t.Errorf("test %d: value mismatch\ngot %#v\nwant %#v\ndecoding into %T\ninput %q", + i, deref, test.value, test.ptr, test.input) + } + } +} + +func TestDecodeWithByteReader(t *testing.T) { + runTests(t, func(input []byte, into interface{}) error { + return Decode(bytes.NewReader(input), into) + }) +} + +// plainReader reads from a byte slice but does not +// implement ReadByte. It is also not recognized by the +// size validation. This is useful to test how the decoder +// behaves on a non-buffered input stream. +type plainReader []byte + +func newPlainReader(b []byte) io.Reader { + return (*plainReader)(&b) +} + +func (r *plainReader) Read(buf []byte) (n int, err error) { + if len(*r) == 0 { + return 0, io.EOF + } + n = copy(buf, *r) + *r = (*r)[n:] + return n, nil +} + +func TestDecodeWithNonByteReader(t *testing.T) { + runTests(t, func(input []byte, into interface{}) error { + return Decode(newPlainReader(input), into) + }) +} + +func TestDecodeStreamReset(t *testing.T) { + s := NewStream(nil, 0) + runTests(t, func(input []byte, into interface{}) error { + s.Reset(bytes.NewReader(input), 0) + return s.Decode(into) + }) +} + +type testDecoder struct{ called bool } + +func (t *testDecoder) DecodeRLP(s *Stream) error { + if _, err := s.Uint(); err != nil { + return err + } + t.called = true + return nil +} + +func TestDecodeDecoder(t *testing.T) { + var s struct { + T1 testDecoder + T2 *testDecoder + T3 **testDecoder + } + if err := Decode(bytes.NewReader(unhex("C3010203")), &s); err != nil { + t.Fatalf("Decode error: %v", err) + } + + if !s.T1.called { + t.Errorf("DecodeRLP was not called for (non-pointer) testDecoder") + } + + if s.T2 == nil { + t.Errorf("*testDecoder has not been allocated") + } else if !s.T2.called { + t.Errorf("DecodeRLP was not called for *testDecoder") + } + + if s.T3 == nil || *s.T3 == nil { + t.Errorf("**testDecoder has not been allocated") + } else if !(*s.T3).called { + t.Errorf("DecodeRLP was not called for **testDecoder") + } +} + +type byteDecoder byte + +func (bd *byteDecoder) DecodeRLP(s *Stream) error { + _, err := s.Uint() + *bd = 255 + return err +} + +func (bd byteDecoder) called() bool { + return bd == 255 +} + +// This test verifies that the byte slice/byte array logic +// does not kick in for element types implementing Decoder. +func TestDecoderInByteSlice(t *testing.T) { + var slice []byteDecoder + if err := Decode(bytes.NewReader(unhex("C101")), &slice); err != nil { + t.Errorf("unexpected Decode error %v", err) + } else if !slice[0].called() { + t.Errorf("DecodeRLP not called for slice element") + } + + var array [1]byteDecoder + if err := Decode(bytes.NewReader(unhex("C101")), &array); err != nil { + t.Errorf("unexpected Decode error %v", err) + } else if !array[0].called() { + t.Errorf("DecodeRLP not called for array element") + } +} + +func ExampleDecode() { + input, _ := hex.DecodeString("C90A1486666F6F626172") + + type example struct { + A, B uint + private uint // private fields are ignored + String string + } + + var s example + err := Decode(bytes.NewReader(input), &s) + if err != nil { + fmt.Printf("Error: %v\n", err) + } else { + fmt.Printf("Decoded value: %#v\n", s) + } + // Output: + // Decoded value: rlp.example{A:0xa, B:0x14, private:0x0, String:"foobar"} +} + +func ExampleDecode_structTagNil() { + // In this example, we'll use the "nil" struct tag to change + // how a pointer-typed field is decoded. The input contains an RLP + // list of one element, an empty string. + input := []byte{0xC1, 0x80} + + // This type uses the normal rules. + // The empty input string is decoded as a pointer to an empty Go string. + var normalRules struct { + String *string + } + Decode(bytes.NewReader(input), &normalRules) + fmt.Printf("normal: String = %q\n", *normalRules.String) + + // This type uses the struct tag. + // The empty input string is decoded as a nil pointer. + var withEmptyOK struct { + String *string `rlp:"nil"` + } + Decode(bytes.NewReader(input), &withEmptyOK) + fmt.Printf("with nil tag: String = %v\n", withEmptyOK.String) + + // Output: + // normal: String = "" + // with nil tag: String = +} + +func ExampleStream() { + input, _ := hex.DecodeString("C90A1486666F6F626172") + s := NewStream(bytes.NewReader(input), 0) + + // Check what kind of value lies ahead + kind, size, _ := s.Kind() + fmt.Printf("Kind: %v size:%d\n", kind, size) + + // Enter the list + if _, err := s.List(); err != nil { + fmt.Printf("List error: %v\n", err) + return + } + + // Decode elements + fmt.Println(s.Uint()) + fmt.Println(s.Uint()) + fmt.Println(s.Bytes()) + + // Acknowledge end of list + if err := s.ListEnd(); err != nil { + fmt.Printf("ListEnd error: %v\n", err) + } + // Output: + // Kind: List size:9 + // 10 + // 20 + // [102 111 111 98 97 114] +} + +func BenchmarkDecode(b *testing.B) { + enc := encodeTestSlice(90000) + b.SetBytes(int64(len(enc))) + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var s []uint + r := bytes.NewReader(enc) + if err := Decode(r, &s); err != nil { + b.Fatalf("Decode error: %v", err) + } + } +} + +func BenchmarkDecodeIntSliceReuse(b *testing.B) { + enc := encodeTestSlice(100000) + b.SetBytes(int64(len(enc))) + b.ReportAllocs() + b.ResetTimer() + + var s []uint + for i := 0; i < b.N; i++ { + r := bytes.NewReader(enc) + if err := Decode(r, &s); err != nil { + b.Fatalf("Decode error: %v", err) + } + } +} + +func encodeTestSlice(n uint) []byte { + s := make([]uint, n) + for i := uint(0); i < n; i++ { + s[i] = i + } + b, err := EncodeToBytes(s) + if err != nil { + panic(fmt.Sprintf("encode error: %v", err)) + } + return b +} + +func unhex(str string) []byte { + b, err := hex.DecodeString(strings.Replace(str, " ", "", -1)) + if err != nil { + panic(fmt.Sprintf("invalid hex string: %q", str)) + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/doc.go b/vendor/github.com/ethereum/go-ethereum/rlp/doc.go new file mode 100644 index 0000000..7266741 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/doc.go @@ -0,0 +1,33 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package rlp implements the RLP serialization format. + +The purpose of RLP (Recursive Linear Prefix) qis to encode arbitrarily +nested arrays of binary data, and RLP is the main encoding method used +to serialize objects in Ethereum. The only purpose of RLP is to encode +structure; encoding specific atomic data types (eg. strings, ints, +floats) is left up to higher-order protocols; in Ethereum integers +must be represented in big endian binary form with no leading zeroes +(thus making the integer value zero be equivalent to the empty byte +array). + +RLP values are distinguished by a type tag. The type tag precedes the +value in the input stream and defines the size and kind of the bytes +that follow. +*/ +package rlp diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/encode.go b/vendor/github.com/ethereum/go-ethereum/rlp/encode.go new file mode 100644 index 0000000..c20897e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/encode.go @@ -0,0 +1,652 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "fmt" + "io" + "math/big" + "reflect" + "sync" +) + +var ( + // Common encoded values. + // These are useful when implementing EncodeRLP. + EmptyString = []byte{0x80} + EmptyList = []byte{0xC0} +) + +// Encoder is implemented by types that require custom +// encoding rules or want to encode private fields. +type Encoder interface { + // EncodeRLP should write the RLP encoding of its receiver to w. + // If the implementation is a pointer method, it may also be + // called for nil pointers. + // + // Implementations should generate valid RLP. The data written is + // not verified at the moment, but a future version might. It is + // recommended to write only a single value but writing multiple + // values or no value at all is also permitted. + EncodeRLP(io.Writer) error +} + +// Encode writes the RLP encoding of val to w. Note that Encode may +// perform many small writes in some cases. Consider making w +// buffered. +// +// Encode uses the following type-dependent encoding rules: +// +// If the type implements the Encoder interface, Encode calls +// EncodeRLP. This is true even for nil pointers, please see the +// documentation for Encoder. +// +// To encode a pointer, the value being pointed to is encoded. For nil +// pointers, Encode will encode the zero value of the type. A nil +// pointer to a struct type always encodes as an empty RLP list. +// A nil pointer to an array encodes as an empty list (or empty string +// if the array has element type byte). +// +// Struct values are encoded as an RLP list of all their encoded +// public fields. Recursive struct types are supported. +// +// To encode slices and arrays, the elements are encoded as an RLP +// list of the value's elements. Note that arrays and slices with +// element type uint8 or byte are always encoded as an RLP string. +// +// A Go string is encoded as an RLP string. +// +// An unsigned integer value is encoded as an RLP string. Zero always +// encodes as an empty RLP string. Encode also supports *big.Int. +// +// An interface value encodes as the value contained in the interface. +// +// Boolean values are not supported, nor are signed integers, floating +// point numbers, maps, channels and functions. +func Encode(w io.Writer, val interface{}) error { + if outer, ok := w.(*encbuf); ok { + // Encode was called by some type's EncodeRLP. + // Avoid copying by writing to the outer encbuf directly. + return outer.encode(val) + } + eb := encbufPool.Get().(*encbuf) + defer encbufPool.Put(eb) + eb.reset() + if err := eb.encode(val); err != nil { + return err + } + return eb.toWriter(w) +} + +// EncodeBytes returns the RLP encoding of val. +// Please see the documentation of Encode for the encoding rules. +func EncodeToBytes(val interface{}) ([]byte, error) { + eb := encbufPool.Get().(*encbuf) + defer encbufPool.Put(eb) + eb.reset() + if err := eb.encode(val); err != nil { + return nil, err + } + return eb.toBytes(), nil +} + +// EncodeReader returns a reader from which the RLP encoding of val +// can be read. The returned size is the total size of the encoded +// data. +// +// Please see the documentation of Encode for the encoding rules. +func EncodeToReader(val interface{}) (size int, r io.Reader, err error) { + eb := encbufPool.Get().(*encbuf) + eb.reset() + if err := eb.encode(val); err != nil { + return 0, nil, err + } + return eb.size(), &encReader{buf: eb}, nil +} + +type encbuf struct { + str []byte // string data, contains everything except list headers + lheads []*listhead // all list headers + lhsize int // sum of sizes of all encoded list headers + sizebuf []byte // 9-byte auxiliary buffer for uint encoding +} + +type listhead struct { + offset int // index of this header in string data + size int // total size of encoded data (including list headers) +} + +// encode writes head to the given buffer, which must be at least +// 9 bytes long. It returns the encoded bytes. +func (head *listhead) encode(buf []byte) []byte { + return buf[:puthead(buf, 0xC0, 0xF7, uint64(head.size))] +} + +// headsize returns the size of a list or string header +// for a value of the given size. +func headsize(size uint64) int { + if size < 56 { + return 1 + } + return 1 + intsize(size) +} + +// puthead writes a list or string header to buf. +// buf must be at least 9 bytes long. +func puthead(buf []byte, smalltag, largetag byte, size uint64) int { + if size < 56 { + buf[0] = smalltag + byte(size) + return 1 + } else { + sizesize := putint(buf[1:], size) + buf[0] = largetag + byte(sizesize) + return sizesize + 1 + } +} + +// encbufs are pooled. +var encbufPool = sync.Pool{ + New: func() interface{} { return &encbuf{sizebuf: make([]byte, 9)} }, +} + +func (w *encbuf) reset() { + w.lhsize = 0 + if w.str != nil { + w.str = w.str[:0] + } + if w.lheads != nil { + w.lheads = w.lheads[:0] + } +} + +// encbuf implements io.Writer so it can be passed it into EncodeRLP. +func (w *encbuf) Write(b []byte) (int, error) { + w.str = append(w.str, b...) + return len(b), nil +} + +func (w *encbuf) encode(val interface{}) error { + rval := reflect.ValueOf(val) + ti, err := cachedTypeInfo(rval.Type(), tags{}) + if err != nil { + return err + } + return ti.writer(rval, w) +} + +func (w *encbuf) encodeStringHeader(size int) { + if size < 56 { + w.str = append(w.str, 0x80+byte(size)) + } else { + // TODO: encode to w.str directly + sizesize := putint(w.sizebuf[1:], uint64(size)) + w.sizebuf[0] = 0xB7 + byte(sizesize) + w.str = append(w.str, w.sizebuf[:sizesize+1]...) + } +} + +func (w *encbuf) encodeString(b []byte) { + if len(b) == 1 && b[0] <= 0x7F { + // fits single byte, no string header + w.str = append(w.str, b[0]) + } else { + w.encodeStringHeader(len(b)) + w.str = append(w.str, b...) + } +} + +func (w *encbuf) list() *listhead { + lh := &listhead{offset: len(w.str), size: w.lhsize} + w.lheads = append(w.lheads, lh) + return lh +} + +func (w *encbuf) listEnd(lh *listhead) { + lh.size = w.size() - lh.offset - lh.size + if lh.size < 56 { + w.lhsize += 1 // length encoded into kind tag + } else { + w.lhsize += 1 + intsize(uint64(lh.size)) + } +} + +func (w *encbuf) size() int { + return len(w.str) + w.lhsize +} + +func (w *encbuf) toBytes() []byte { + out := make([]byte, w.size()) + strpos := 0 + pos := 0 + for _, head := range w.lheads { + // write string data before header + n := copy(out[pos:], w.str[strpos:head.offset]) + pos += n + strpos += n + // write the header + enc := head.encode(out[pos:]) + pos += len(enc) + } + // copy string data after the last list header + copy(out[pos:], w.str[strpos:]) + return out +} + +func (w *encbuf) toWriter(out io.Writer) (err error) { + strpos := 0 + for _, head := range w.lheads { + // write string data before header + if head.offset-strpos > 0 { + n, err := out.Write(w.str[strpos:head.offset]) + strpos += n + if err != nil { + return err + } + } + // write the header + enc := head.encode(w.sizebuf) + if _, err = out.Write(enc); err != nil { + return err + } + } + if strpos < len(w.str) { + // write string data after the last list header + _, err = out.Write(w.str[strpos:]) + } + return err +} + +// encReader is the io.Reader returned by EncodeToReader. +// It releases its encbuf at EOF. +type encReader struct { + buf *encbuf // the buffer we're reading from. this is nil when we're at EOF. + lhpos int // index of list header that we're reading + strpos int // current position in string buffer + piece []byte // next piece to be read +} + +func (r *encReader) Read(b []byte) (n int, err error) { + for { + if r.piece = r.next(); r.piece == nil { + // Put the encode buffer back into the pool at EOF when it + // is first encountered. Subsequent calls still return EOF + // as the error but the buffer is no longer valid. + if r.buf != nil { + encbufPool.Put(r.buf) + r.buf = nil + } + return n, io.EOF + } + nn := copy(b[n:], r.piece) + n += nn + if nn < len(r.piece) { + // piece didn't fit, see you next time. + r.piece = r.piece[nn:] + return n, nil + } + r.piece = nil + } +} + +// next returns the next piece of data to be read. +// it returns nil at EOF. +func (r *encReader) next() []byte { + switch { + case r.buf == nil: + return nil + + case r.piece != nil: + // There is still data available for reading. + return r.piece + + case r.lhpos < len(r.buf.lheads): + // We're before the last list header. + head := r.buf.lheads[r.lhpos] + sizebefore := head.offset - r.strpos + if sizebefore > 0 { + // String data before header. + p := r.buf.str[r.strpos:head.offset] + r.strpos += sizebefore + return p + } else { + r.lhpos++ + return head.encode(r.buf.sizebuf) + } + + case r.strpos < len(r.buf.str): + // String data at the end, after all list headers. + p := r.buf.str[r.strpos:] + r.strpos = len(r.buf.str) + return p + + default: + return nil + } +} + +var ( + encoderInterface = reflect.TypeOf(new(Encoder)).Elem() + big0 = big.NewInt(0) +) + +// makeWriter creates a writer function for the given type. +func makeWriter(typ reflect.Type, ts tags) (writer, error) { + kind := typ.Kind() + switch { + case typ == rawValueType: + return writeRawValue, nil + case typ.Implements(encoderInterface): + return writeEncoder, nil + case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(encoderInterface): + return writeEncoderNoPtr, nil + case kind == reflect.Interface: + return writeInterface, nil + case typ.AssignableTo(reflect.PtrTo(bigInt)): + return writeBigIntPtr, nil + case typ.AssignableTo(bigInt): + return writeBigIntNoPtr, nil + case isUint(kind): + return writeUint, nil + case kind == reflect.Bool: + return writeBool, nil + case kind == reflect.String: + return writeString, nil + case kind == reflect.Slice && isByte(typ.Elem()): + return writeBytes, nil + case kind == reflect.Array && isByte(typ.Elem()): + return writeByteArray, nil + case kind == reflect.Slice || kind == reflect.Array: + return makeSliceWriter(typ, ts) + case kind == reflect.Struct: + return makeStructWriter(typ) + case kind == reflect.Ptr: + return makePtrWriter(typ) + default: + return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ) + } +} + +func isByte(typ reflect.Type) bool { + return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface) +} + +func writeRawValue(val reflect.Value, w *encbuf) error { + w.str = append(w.str, val.Bytes()...) + return nil +} + +func writeUint(val reflect.Value, w *encbuf) error { + i := val.Uint() + if i == 0 { + w.str = append(w.str, 0x80) + } else if i < 128 { + // fits single byte + w.str = append(w.str, byte(i)) + } else { + // TODO: encode int to w.str directly + s := putint(w.sizebuf[1:], i) + w.sizebuf[0] = 0x80 + byte(s) + w.str = append(w.str, w.sizebuf[:s+1]...) + } + return nil +} + +func writeBool(val reflect.Value, w *encbuf) error { + if val.Bool() { + w.str = append(w.str, 0x01) + } else { + w.str = append(w.str, 0x80) + } + return nil +} + +func writeBigIntPtr(val reflect.Value, w *encbuf) error { + ptr := val.Interface().(*big.Int) + if ptr == nil { + w.str = append(w.str, 0x80) + return nil + } + return writeBigInt(ptr, w) +} + +func writeBigIntNoPtr(val reflect.Value, w *encbuf) error { + i := val.Interface().(big.Int) + return writeBigInt(&i, w) +} + +func writeBigInt(i *big.Int, w *encbuf) error { + if cmp := i.Cmp(big0); cmp == -1 { + return fmt.Errorf("rlp: cannot encode negative *big.Int") + } else if cmp == 0 { + w.str = append(w.str, 0x80) + } else { + w.encodeString(i.Bytes()) + } + return nil +} + +func writeBytes(val reflect.Value, w *encbuf) error { + w.encodeString(val.Bytes()) + return nil +} + +func writeByteArray(val reflect.Value, w *encbuf) error { + if !val.CanAddr() { + // Slice requires the value to be addressable. + // Make it addressable by copying. + copy := reflect.New(val.Type()).Elem() + copy.Set(val) + val = copy + } + size := val.Len() + slice := val.Slice(0, size).Bytes() + w.encodeString(slice) + return nil +} + +func writeString(val reflect.Value, w *encbuf) error { + s := val.String() + if len(s) == 1 && s[0] <= 0x7f { + // fits single byte, no string header + w.str = append(w.str, s[0]) + } else { + w.encodeStringHeader(len(s)) + w.str = append(w.str, s...) + } + return nil +} + +func writeEncoder(val reflect.Value, w *encbuf) error { + return val.Interface().(Encoder).EncodeRLP(w) +} + +// writeEncoderNoPtr handles non-pointer values that implement Encoder +// with a pointer receiver. +func writeEncoderNoPtr(val reflect.Value, w *encbuf) error { + if !val.CanAddr() { + // We can't get the address. It would be possible make the + // value addressable by creating a shallow copy, but this + // creates other problems so we're not doing it (yet). + // + // package json simply doesn't call MarshalJSON for cases like + // this, but encodes the value as if it didn't implement the + // interface. We don't want to handle it that way. + return fmt.Errorf("rlp: game over: unadressable value of type %v, EncodeRLP is pointer method", val.Type()) + } + return val.Addr().Interface().(Encoder).EncodeRLP(w) +} + +func writeInterface(val reflect.Value, w *encbuf) error { + if val.IsNil() { + // Write empty list. This is consistent with the previous RLP + // encoder that we had and should therefore avoid any + // problems. + w.str = append(w.str, 0xC0) + return nil + } + eval := val.Elem() + ti, err := cachedTypeInfo(eval.Type(), tags{}) + if err != nil { + return err + } + return ti.writer(eval, w) +} + +func makeSliceWriter(typ reflect.Type, ts tags) (writer, error) { + etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{}) + if err != nil { + return nil, err + } + writer := func(val reflect.Value, w *encbuf) error { + if !ts.tail { + defer w.listEnd(w.list()) + } + vlen := val.Len() + for i := 0; i < vlen; i++ { + if err := etypeinfo.writer(val.Index(i), w); err != nil { + return err + } + } + return nil + } + return writer, nil +} + +func makeStructWriter(typ reflect.Type) (writer, error) { + fields, err := structFields(typ) + if err != nil { + return nil, err + } + writer := func(val reflect.Value, w *encbuf) error { + lh := w.list() + for _, f := range fields { + if err := f.info.writer(val.Field(f.index), w); err != nil { + return err + } + } + w.listEnd(lh) + return nil + } + return writer, nil +} + +func makePtrWriter(typ reflect.Type) (writer, error) { + etypeinfo, err := cachedTypeInfo1(typ.Elem(), tags{}) + if err != nil { + return nil, err + } + + // determine nil pointer handler + var nilfunc func(*encbuf) error + kind := typ.Elem().Kind() + switch { + case kind == reflect.Array && isByte(typ.Elem().Elem()): + nilfunc = func(w *encbuf) error { + w.str = append(w.str, 0x80) + return nil + } + case kind == reflect.Struct || kind == reflect.Array: + nilfunc = func(w *encbuf) error { + // encoding the zero value of a struct/array could trigger + // infinite recursion, avoid that. + w.listEnd(w.list()) + return nil + } + default: + zero := reflect.Zero(typ.Elem()) + nilfunc = func(w *encbuf) error { + return etypeinfo.writer(zero, w) + } + } + + writer := func(val reflect.Value, w *encbuf) error { + if val.IsNil() { + return nilfunc(w) + } else { + return etypeinfo.writer(val.Elem(), w) + } + } + return writer, err +} + +// putint writes i to the beginning of b in with big endian byte +// order, using the least number of bytes needed to represent i. +func putint(b []byte, i uint64) (size int) { + switch { + case i < (1 << 8): + b[0] = byte(i) + return 1 + case i < (1 << 16): + b[0] = byte(i >> 8) + b[1] = byte(i) + return 2 + case i < (1 << 24): + b[0] = byte(i >> 16) + b[1] = byte(i >> 8) + b[2] = byte(i) + return 3 + case i < (1 << 32): + b[0] = byte(i >> 24) + b[1] = byte(i >> 16) + b[2] = byte(i >> 8) + b[3] = byte(i) + return 4 + case i < (1 << 40): + b[0] = byte(i >> 32) + b[1] = byte(i >> 24) + b[2] = byte(i >> 16) + b[3] = byte(i >> 8) + b[4] = byte(i) + return 5 + case i < (1 << 48): + b[0] = byte(i >> 40) + b[1] = byte(i >> 32) + b[2] = byte(i >> 24) + b[3] = byte(i >> 16) + b[4] = byte(i >> 8) + b[5] = byte(i) + return 6 + case i < (1 << 56): + b[0] = byte(i >> 48) + b[1] = byte(i >> 40) + b[2] = byte(i >> 32) + b[3] = byte(i >> 24) + b[4] = byte(i >> 16) + b[5] = byte(i >> 8) + b[6] = byte(i) + return 7 + default: + b[0] = byte(i >> 56) + b[1] = byte(i >> 48) + b[2] = byte(i >> 40) + b[3] = byte(i >> 32) + b[4] = byte(i >> 24) + b[5] = byte(i >> 16) + b[6] = byte(i >> 8) + b[7] = byte(i) + return 8 + } +} + +// intsize computes the minimum number of bytes required to store i. +func intsize(i uint64) (size int) { + for size = 1; ; size++ { + if i >>= 8; i == 0 { + return size + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/encode_test.go b/vendor/github.com/ethereum/go-ethereum/rlp/encode_test.go new file mode 100644 index 0000000..6f38294 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/encode_test.go @@ -0,0 +1,340 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "math/big" + "sync" + "testing" +) + +type testEncoder struct { + err error +} + +func (e *testEncoder) EncodeRLP(w io.Writer) error { + if e == nil { + w.Write([]byte{0, 0, 0, 0}) + } else if e.err != nil { + return e.err + } else { + w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1}) + } + return nil +} + +type byteEncoder byte + +func (e byteEncoder) EncodeRLP(w io.Writer) error { + w.Write(EmptyList) + return nil +} + +type encodableReader struct { + A, B uint +} + +func (e *encodableReader) Read(b []byte) (int, error) { + panic("called") +} + +type namedByteType byte + +var ( + _ = Encoder(&testEncoder{}) + _ = Encoder(byteEncoder(0)) + + reader io.Reader = &encodableReader{1, 2} +) + +type encTest struct { + val interface{} + output, error string +} + +var encTests = []encTest{ + // booleans + {val: true, output: "01"}, + {val: false, output: "80"}, + + // integers + {val: uint32(0), output: "80"}, + {val: uint32(127), output: "7F"}, + {val: uint32(128), output: "8180"}, + {val: uint32(256), output: "820100"}, + {val: uint32(1024), output: "820400"}, + {val: uint32(0xFFFFFF), output: "83FFFFFF"}, + {val: uint32(0xFFFFFFFF), output: "84FFFFFFFF"}, + {val: uint64(0xFFFFFFFF), output: "84FFFFFFFF"}, + {val: uint64(0xFFFFFFFFFF), output: "85FFFFFFFFFF"}, + {val: uint64(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"}, + {val: uint64(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"}, + {val: uint64(0xFFFFFFFFFFFFFFFF), output: "88FFFFFFFFFFFFFFFF"}, + + // big integers (should match uint for small values) + {val: big.NewInt(0), output: "80"}, + {val: big.NewInt(1), output: "01"}, + {val: big.NewInt(127), output: "7F"}, + {val: big.NewInt(128), output: "8180"}, + {val: big.NewInt(256), output: "820100"}, + {val: big.NewInt(1024), output: "820400"}, + {val: big.NewInt(0xFFFFFF), output: "83FFFFFF"}, + {val: big.NewInt(0xFFFFFFFF), output: "84FFFFFFFF"}, + {val: big.NewInt(0xFFFFFFFFFF), output: "85FFFFFFFFFF"}, + {val: big.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"}, + {val: big.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"}, + { + val: big.NewInt(0).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")), + output: "8F102030405060708090A0B0C0D0E0F2", + }, + { + val: big.NewInt(0).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")), + output: "9C0100020003000400050006000700080009000A000B000C000D000E01", + }, + { + val: big.NewInt(0).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")), + output: "A1010000000000000000000000000000000000000000000000000000000000000000", + }, + + // non-pointer big.Int + {val: *big.NewInt(0), output: "80"}, + {val: *big.NewInt(0xFFFFFF), output: "83FFFFFF"}, + + // negative ints are not supported + {val: big.NewInt(-1), error: "rlp: cannot encode negative *big.Int"}, + + // byte slices, strings + {val: []byte{}, output: "80"}, + {val: []byte{0x7E}, output: "7E"}, + {val: []byte{0x7F}, output: "7F"}, + {val: []byte{0x80}, output: "8180"}, + {val: []byte{1, 2, 3}, output: "83010203"}, + + {val: []namedByteType{1, 2, 3}, output: "83010203"}, + {val: [...]namedByteType{1, 2, 3}, output: "83010203"}, + + {val: "", output: "80"}, + {val: "\x7E", output: "7E"}, + {val: "\x7F", output: "7F"}, + {val: "\x80", output: "8180"}, + {val: "dog", output: "83646F67"}, + { + val: "Lorem ipsum dolor sit amet, consectetur adipisicing eli", + output: "B74C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C69", + }, + { + val: "Lorem ipsum dolor sit amet, consectetur adipisicing elit", + output: "B8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974", + }, + { + val: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat", + output: "B904004C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E20437572616269747572206D6175726973206D61676E612C20737573636970697420736564207665686963756C61206E6F6E2C20696163756C697320666175636962757320746F72746F722E2050726F696E20737573636970697420756C74726963696573206D616C6573756164612E204475697320746F72746F7220656C69742C2064696374756D2071756973207472697374697175652065752C20756C7472696365732061742072697375732E204D6F72626920612065737420696D70657264696574206D6920756C6C616D636F7270657220616C6971756574207375736369706974206E6563206C6F72656D2E2041656E65616E2071756973206C656F206D6F6C6C69732C2076756C70757461746520656C6974207661726975732C20636F6E73657175617420656E696D2E204E756C6C6120756C74726963657320747572706973206A7573746F2C20657420706F73756572652075726E6120636F6E7365637465747572206E65632E2050726F696E206E6F6E20636F6E76616C6C6973206D657475732E20446F6E65632074656D706F7220697073756D20696E206D617572697320636F6E67756520736F6C6C696369747564696E2E20566573746962756C756D20616E746520697073756D207072696D697320696E206661756369627573206F726369206C756374757320657420756C74726963657320706F737565726520637562696C69612043757261653B2053757370656E646973736520636F6E76616C6C69732073656D2076656C206D617373612066617563696275732C2065676574206C6163696E6961206C616375732074656D706F722E204E756C6C61207175697320756C747269636965732070757275732E2050726F696E20617563746F722072686F6E637573206E69626820636F6E64696D656E74756D206D6F6C6C69732E20416C697175616D20636F6E73657175617420656E696D206174206D65747573206C75637475732C206120656C656966656E6420707572757320656765737461732E20437572616269747572206174206E696268206D657475732E204E616D20626962656E64756D2C206E6571756520617420617563746F72207472697374697175652C206C6F72656D206C696265726F20616C697175657420617263752C206E6F6E20696E74657264756D2074656C6C7573206C65637475732073697420616D65742065726F732E20437261732072686F6E6375732C206D65747573206163206F726E617265206375727375732C20646F6C6F72206A7573746F20756C747269636573206D657475732C20617420756C6C616D636F7270657220766F6C7574706174", + }, + + // slices + {val: []uint{}, output: "C0"}, + {val: []uint{1, 2, 3}, output: "C3010203"}, + { + // [ [], [[]], [ [], [[]] ] ] + val: []interface{}{[]interface{}{}, [][]interface{}{{}}, []interface{}{[]interface{}{}, [][]interface{}{{}}}}, + output: "C7C0C1C0C3C0C1C0", + }, + { + val: []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk", "lll", "mmm", "nnn", "ooo"}, + output: "F83C836161618362626283636363836464648365656583666666836767678368686883696969836A6A6A836B6B6B836C6C6C836D6D6D836E6E6E836F6F6F", + }, + { + val: []interface{}{uint(1), uint(0xFFFFFF), []interface{}{[]uint{4, 5, 5}}, "abc"}, + output: "CE0183FFFFFFC4C304050583616263", + }, + { + val: [][]string{ + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + {"asdf", "qwer", "zxcv"}, + }, + output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376", + }, + + // RawValue + {val: RawValue(unhex("01")), output: "01"}, + {val: RawValue(unhex("82FFFF")), output: "82FFFF"}, + {val: []RawValue{unhex("01"), unhex("02")}, output: "C20102"}, + + // structs + {val: simplestruct{}, output: "C28080"}, + {val: simplestruct{A: 3, B: "foo"}, output: "C50383666F6F"}, + {val: &recstruct{5, nil}, output: "C205C0"}, + {val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"}, + {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02"), unhex("03")}}, output: "C3010203"}, + {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"}, + {val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"}, + {val: &tailRaw{A: 1, Tail: nil}, output: "C101"}, + + // nil + {val: (*uint)(nil), output: "80"}, + {val: (*string)(nil), output: "80"}, + {val: (*[]byte)(nil), output: "80"}, + {val: (*[10]byte)(nil), output: "80"}, + {val: (*big.Int)(nil), output: "80"}, + {val: (*[]string)(nil), output: "C0"}, + {val: (*[10]string)(nil), output: "C0"}, + {val: (*[]interface{})(nil), output: "C0"}, + {val: (*[]struct{ uint })(nil), output: "C0"}, + {val: (*interface{})(nil), output: "C0"}, + + // interfaces + {val: []io.Reader{reader}, output: "C3C20102"}, // the contained value is a struct + + // Encoder + {val: (*testEncoder)(nil), output: "00000000"}, + {val: &testEncoder{}, output: "00010001000100010001"}, + {val: &testEncoder{errors.New("test error")}, error: "test error"}, + // verify that pointer method testEncoder.EncodeRLP is called for + // addressable non-pointer values. + {val: &struct{ TE testEncoder }{testEncoder{}}, output: "CA00010001000100010001"}, + {val: &struct{ TE testEncoder }{testEncoder{errors.New("test error")}}, error: "test error"}, + // verify the error for non-addressable non-pointer Encoder + {val: testEncoder{}, error: "rlp: game over: unadressable value of type rlp.testEncoder, EncodeRLP is pointer method"}, + // verify the special case for []byte + {val: []byteEncoder{0, 1, 2, 3, 4}, output: "C5C0C0C0C0C0"}, +} + +func runEncTests(t *testing.T, f func(val interface{}) ([]byte, error)) { + for i, test := range encTests { + output, err := f(test.val) + if err != nil && test.error == "" { + t.Errorf("test %d: unexpected error: %v\nvalue %#v\ntype %T", + i, err, test.val, test.val) + continue + } + if test.error != "" && fmt.Sprint(err) != test.error { + t.Errorf("test %d: error mismatch\ngot %v\nwant %v\nvalue %#v\ntype %T", + i, err, test.error, test.val, test.val) + continue + } + if err == nil && !bytes.Equal(output, unhex(test.output)) { + t.Errorf("test %d: output mismatch:\ngot %X\nwant %s\nvalue %#v\ntype %T", + i, output, test.output, test.val, test.val) + } + } +} + +func TestEncode(t *testing.T) { + runEncTests(t, func(val interface{}) ([]byte, error) { + b := new(bytes.Buffer) + err := Encode(b, val) + return b.Bytes(), err + }) +} + +func TestEncodeToBytes(t *testing.T) { + runEncTests(t, EncodeToBytes) +} + +func TestEncodeToReader(t *testing.T) { + runEncTests(t, func(val interface{}) ([]byte, error) { + _, r, err := EncodeToReader(val) + if err != nil { + return nil, err + } + return ioutil.ReadAll(r) + }) +} + +func TestEncodeToReaderPiecewise(t *testing.T) { + runEncTests(t, func(val interface{}) ([]byte, error) { + size, r, err := EncodeToReader(val) + if err != nil { + return nil, err + } + + // read output piecewise + output := make([]byte, size) + for start, end := 0, 0; start < size; start = end { + if remaining := size - start; remaining < 3 { + end += remaining + } else { + end = start + 3 + } + n, err := r.Read(output[start:end]) + end = start + n + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + } + return output, nil + }) +} + +// This is a regression test verifying that encReader +// returns its encbuf to the pool only once. +func TestEncodeToReaderReturnToPool(t *testing.T) { + buf := make([]byte, 50) + wg := new(sync.WaitGroup) + for i := 0; i < 5; i++ { + wg.Add(1) + go func() { + for i := 0; i < 1000; i++ { + _, r, _ := EncodeToReader("foo") + ioutil.ReadAll(r) + r.Read(buf) + r.Read(buf) + r.Read(buf) + r.Read(buf) + } + wg.Done() + }() + } + wg.Wait() +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/encoder_example_test.go b/vendor/github.com/ethereum/go-ethereum/rlp/encoder_example_test.go new file mode 100644 index 0000000..1cffa24 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/encoder_example_test.go @@ -0,0 +1,54 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "fmt" + "io" +) + +type MyCoolType struct { + Name string + a, b uint +} + +// EncodeRLP writes x as RLP list [a, b] that omits the Name field. +func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) { + // Note: the receiver can be a nil pointer. This allows you to + // control the encoding of nil, but it also means that you have to + // check for a nil receiver. + if x == nil { + err = Encode(w, []uint{0, 0}) + } else { + err = Encode(w, []uint{x.a, x.b}) + } + return err +} + +func ExampleEncoder() { + var t *MyCoolType // t is nil pointer to MyCoolType + bytes, _ := EncodeToBytes(t) + fmt.Printf("%v → %X\n", t, bytes) + + t = &MyCoolType{Name: "foobar", a: 5, b: 6} + bytes, _ = EncodeToBytes(t) + fmt.Printf("%v → %X\n", t, bytes) + + // Output: + // → C28080 + // &{foobar 5 6} → C20506 +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/raw.go b/vendor/github.com/ethereum/go-ethereum/rlp/raw.go new file mode 100644 index 0000000..33aae6e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/raw.go @@ -0,0 +1,156 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "io" + "reflect" +) + +// RawValue represents an encoded RLP value and can be used to delay +// RLP decoding or precompute an encoding. Note that the decoder does +// not verify whether the content of RawValues is valid RLP. +type RawValue []byte + +var rawValueType = reflect.TypeOf(RawValue{}) + +// ListSize returns the encoded size of an RLP list with the given +// content size. +func ListSize(contentSize uint64) uint64 { + return uint64(headsize(contentSize)) + contentSize +} + +// Split returns the content of first RLP value and any +// bytes after the value as subslices of b. +func Split(b []byte) (k Kind, content, rest []byte, err error) { + k, ts, cs, err := readKind(b) + if err != nil { + return 0, nil, b, err + } + return k, b[ts : ts+cs], b[ts+cs:], nil +} + +// SplitString splits b into the content of an RLP string +// and any remaining bytes after the string. +func SplitString(b []byte) (content, rest []byte, err error) { + k, content, rest, err := Split(b) + if err != nil { + return nil, b, err + } + if k == List { + return nil, b, ErrExpectedString + } + return content, rest, nil +} + +// SplitList splits b into the content of a list and any remaining +// bytes after the list. +func SplitList(b []byte) (content, rest []byte, err error) { + k, content, rest, err := Split(b) + if err != nil { + return nil, b, err + } + if k != List { + return nil, b, ErrExpectedList + } + return content, rest, nil +} + +// CountValues counts the number of encoded values in b. +func CountValues(b []byte) (int, error) { + i := 0 + for ; len(b) > 0; i++ { + _, tagsize, size, err := readKind(b) + if err != nil { + return 0, err + } + b = b[tagsize+size:] + } + return i, nil +} + +func readKind(buf []byte) (k Kind, tagsize, contentsize uint64, err error) { + if len(buf) == 0 { + return 0, 0, 0, io.ErrUnexpectedEOF + } + b := buf[0] + switch { + case b < 0x80: + k = Byte + tagsize = 0 + contentsize = 1 + case b < 0xB8: + k = String + tagsize = 1 + contentsize = uint64(b - 0x80) + // Reject strings that should've been single bytes. + if contentsize == 1 && buf[1] < 128 { + return 0, 0, 0, ErrCanonSize + } + case b < 0xC0: + k = String + tagsize = uint64(b-0xB7) + 1 + contentsize, err = readSize(buf[1:], b-0xB7) + case b < 0xF8: + k = List + tagsize = 1 + contentsize = uint64(b - 0xC0) + default: + k = List + tagsize = uint64(b-0xF7) + 1 + contentsize, err = readSize(buf[1:], b-0xF7) + } + if err != nil { + return 0, 0, 0, err + } + // Reject values larger than the input slice. + if contentsize > uint64(len(buf))-tagsize { + return 0, 0, 0, ErrValueTooLarge + } + return k, tagsize, contentsize, err +} + +func readSize(b []byte, slen byte) (uint64, error) { + if int(slen) > len(b) { + return 0, io.ErrUnexpectedEOF + } + var s uint64 + switch slen { + case 1: + s = uint64(b[0]) + case 2: + s = uint64(b[0])<<8 | uint64(b[1]) + case 3: + s = uint64(b[0])<<16 | uint64(b[1])<<8 | uint64(b[2]) + case 4: + s = uint64(b[0])<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]) + case 5: + s = uint64(b[0])<<32 | uint64(b[1])<<24 | uint64(b[2])<<16 | uint64(b[3])<<8 | uint64(b[4]) + case 6: + s = uint64(b[0])<<40 | uint64(b[1])<<32 | uint64(b[2])<<24 | uint64(b[3])<<16 | uint64(b[4])<<8 | uint64(b[5]) + case 7: + s = uint64(b[0])<<48 | uint64(b[1])<<40 | uint64(b[2])<<32 | uint64(b[3])<<24 | uint64(b[4])<<16 | uint64(b[5])<<8 | uint64(b[6]) + case 8: + s = uint64(b[0])<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]) + } + // Reject sizes < 56 (shouldn't have separate size) and sizes with + // leading zero bytes. + if s < 56 || b[0] == 0 { + return 0, ErrCanonSize + } + return s, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/raw_test.go b/vendor/github.com/ethereum/go-ethereum/rlp/raw_test.go new file mode 100644 index 0000000..bac09d8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/raw_test.go @@ -0,0 +1,195 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "bytes" + "io" + "reflect" + "testing" +) + +func TestCountValues(t *testing.T) { + tests := []struct { + input string // note: spaces in input are stripped by unhex + count int + err error + }{ + // simple cases + {"", 0, nil}, + {"00", 1, nil}, + {"80", 1, nil}, + {"C0", 1, nil}, + {"01 02 03", 3, nil}, + {"01 C406070809 02", 3, nil}, + {"820101 820202 8403030303 04", 4, nil}, + + // size errors + {"8142", 0, ErrCanonSize}, + {"01 01 8142", 0, ErrCanonSize}, + {"02 84020202", 0, ErrValueTooLarge}, + + { + input: "A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", + count: 2, + }, + } + for i, test := range tests { + count, err := CountValues(unhex(test.input)) + if count != test.count { + t.Errorf("test %d: count mismatch, got %d want %d\ninput: %s", i, count, test.count, test.input) + } + if !reflect.DeepEqual(err, test.err) { + t.Errorf("test %d: err mismatch, got %q want %q\ninput: %s", i, err, test.err, test.input) + } + } +} + +func TestSplitTypes(t *testing.T) { + if _, _, err := SplitString(unhex("C100")); err != ErrExpectedString { + t.Errorf("SplitString returned %q, want %q", err, ErrExpectedString) + } + if _, _, err := SplitList(unhex("01")); err != ErrExpectedList { + t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList) + } + if _, _, err := SplitList(unhex("81FF")); err != ErrExpectedList { + t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList) + } +} + +func TestSplit(t *testing.T) { + tests := []struct { + input string + kind Kind + val, rest string + err error + }{ + {input: "01FFFF", kind: Byte, val: "01", rest: "FFFF"}, + {input: "80FFFF", kind: String, val: "", rest: "FFFF"}, + {input: "C3010203", kind: List, val: "010203"}, + + // errors + {input: "", err: io.ErrUnexpectedEOF}, + + {input: "8141", err: ErrCanonSize, rest: "8141"}, + {input: "B800", err: ErrCanonSize, rest: "B800"}, + {input: "B802FFFF", err: ErrCanonSize, rest: "B802FFFF"}, + {input: "B90000", err: ErrCanonSize, rest: "B90000"}, + {input: "B90055", err: ErrCanonSize, rest: "B90055"}, + {input: "BA0002FFFF", err: ErrCanonSize, rest: "BA0002FFFF"}, + {input: "F800", err: ErrCanonSize, rest: "F800"}, + {input: "F90000", err: ErrCanonSize, rest: "F90000"}, + {input: "F90055", err: ErrCanonSize, rest: "F90055"}, + {input: "FA0002FFFF", err: ErrCanonSize, rest: "FA0002FFFF"}, + + {input: "8501010101", err: ErrValueTooLarge, rest: "8501010101"}, + {input: "C60607080902", err: ErrValueTooLarge, rest: "C60607080902"}, + + // size check overflow + {input: "BFFFFFFFFFFFFFFFFF", err: ErrValueTooLarge, rest: "BFFFFFFFFFFFFFFFFF"}, + {input: "FFFFFFFFFFFFFFFFFF", err: ErrValueTooLarge, rest: "FFFFFFFFFFFFFFFFFF"}, + + { + input: "B838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + err: ErrValueTooLarge, + rest: "B838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + }, + { + input: "F838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + err: ErrValueTooLarge, + rest: "F838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + }, + + // a few bigger values, just for kicks + { + input: "F839FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + kind: List, + val: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + rest: "", + }, + { + input: "F90211A060EF29F20CC1007AE6E9530AEE16F4B31F8F1769A2D1264EC995C6D1241868D6A07C62AB8AC9838F5F5877B20BB37B387BC2106E97A3D52172CBEDB5EE17C36008A00EAB6B7324AADC0F6047C6AFC8229F09F7CF451B51D67C8DFB08D49BA8C3C626A04453343B2F3A6E42FCF87948F88AF7C8FC16D0C2735CBA7F026836239AB2C15FA024635C7291C882CE4C0763760C1A362DFC3FFCD802A55722236DE058D74202ACA0A220C808DE10F55E40AB25255201CFF009EA181D3906638E944EE2BF34049984A08D325AB26796F1CCB470F69C0F842501DC35D368A0C2575B2D243CFD1E8AB0FDA0B5298FF60DA5069463D610513C9F04F24051348391A143AFFAB7197DFACDEA72A02D2A7058A4463F8FB69378369E11EF33AE3252E2DB86CB545B36D3C26DDECE5AA0888F97BCA8E0BD83DC5B3B91CFF5FAF2F66F9501010682D67EF4A3B4E66115FBA0E8175A60C93BE9ED02921958F0EA55DA0FB5E4802AF5846147BAD92BC2D8AF26A08B3376FF433F3A4250FA64B7F804004CAC5807877D91C4427BD1CD05CF912ED8A09B32EF0F03BD13C37FF950C0CCCEFCCDD6669F2E7F2AA5CB859928E84E29763EA09BBA5E46610C8C8B1F8E921E5691BF8C7E40D75825D5EA3217AA9C3A8A355F39A0EEB95BC78251CCCEC54A97F19755C4A59A293544EEE6119AFA50531211E53C4FA00B6E86FE150BF4A9E0FEEE9C90F5465E617A861BB5E357F942881EE762212E2580", + kind: List, + val: "A060EF29F20CC1007AE6E9530AEE16F4B31F8F1769A2D1264EC995C6D1241868D6A07C62AB8AC9838F5F5877B20BB37B387BC2106E97A3D52172CBEDB5EE17C36008A00EAB6B7324AADC0F6047C6AFC8229F09F7CF451B51D67C8DFB08D49BA8C3C626A04453343B2F3A6E42FCF87948F88AF7C8FC16D0C2735CBA7F026836239AB2C15FA024635C7291C882CE4C0763760C1A362DFC3FFCD802A55722236DE058D74202ACA0A220C808DE10F55E40AB25255201CFF009EA181D3906638E944EE2BF34049984A08D325AB26796F1CCB470F69C0F842501DC35D368A0C2575B2D243CFD1E8AB0FDA0B5298FF60DA5069463D610513C9F04F24051348391A143AFFAB7197DFACDEA72A02D2A7058A4463F8FB69378369E11EF33AE3252E2DB86CB545B36D3C26DDECE5AA0888F97BCA8E0BD83DC5B3B91CFF5FAF2F66F9501010682D67EF4A3B4E66115FBA0E8175A60C93BE9ED02921958F0EA55DA0FB5E4802AF5846147BAD92BC2D8AF26A08B3376FF433F3A4250FA64B7F804004CAC5807877D91C4427BD1CD05CF912ED8A09B32EF0F03BD13C37FF950C0CCCEFCCDD6669F2E7F2AA5CB859928E84E29763EA09BBA5E46610C8C8B1F8E921E5691BF8C7E40D75825D5EA3217AA9C3A8A355F39A0EEB95BC78251CCCEC54A97F19755C4A59A293544EEE6119AFA50531211E53C4FA00B6E86FE150BF4A9E0FEEE9C90F5465E617A861BB5E357F942881EE762212E2580", + rest: "", + }, + { + input: "F877A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", + kind: List, + val: "A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", + rest: "", + }, + } + + for i, test := range tests { + kind, val, rest, err := Split(unhex(test.input)) + if kind != test.kind { + t.Errorf("test %d: kind mismatch: got %v, want %v", i, kind, test.kind) + } + if !bytes.Equal(val, unhex(test.val)) { + t.Errorf("test %d: val mismatch: got %x, want %s", i, val, test.val) + } + if !bytes.Equal(rest, unhex(test.rest)) { + t.Errorf("test %d: rest mismatch: got %x, want %s", i, rest, test.rest) + } + if err != test.err { + t.Errorf("test %d: error mismatch: got %q, want %q", i, err, test.err) + } + } +} + +func TestReadSize(t *testing.T) { + tests := []struct { + input string + slen byte + size uint64 + err error + }{ + {input: "", slen: 1, err: io.ErrUnexpectedEOF}, + {input: "FF", slen: 2, err: io.ErrUnexpectedEOF}, + {input: "00", slen: 1, err: ErrCanonSize}, + {input: "36", slen: 1, err: ErrCanonSize}, + {input: "37", slen: 1, err: ErrCanonSize}, + {input: "38", slen: 1, size: 0x38}, + {input: "FF", slen: 1, size: 0xFF}, + {input: "FFFF", slen: 2, size: 0xFFFF}, + {input: "FFFFFF", slen: 3, size: 0xFFFFFF}, + {input: "FFFFFFFF", slen: 4, size: 0xFFFFFFFF}, + {input: "FFFFFFFFFF", slen: 5, size: 0xFFFFFFFFFF}, + {input: "FFFFFFFFFFFF", slen: 6, size: 0xFFFFFFFFFFFF}, + {input: "FFFFFFFFFFFFFF", slen: 7, size: 0xFFFFFFFFFFFFFF}, + {input: "FFFFFFFFFFFFFFFF", slen: 8, size: 0xFFFFFFFFFFFFFFFF}, + {input: "0102", slen: 2, size: 0x0102}, + {input: "010203", slen: 3, size: 0x010203}, + {input: "01020304", slen: 4, size: 0x01020304}, + {input: "0102030405", slen: 5, size: 0x0102030405}, + {input: "010203040506", slen: 6, size: 0x010203040506}, + {input: "01020304050607", slen: 7, size: 0x01020304050607}, + {input: "0102030405060708", slen: 8, size: 0x0102030405060708}, + } + + for _, test := range tests { + size, err := readSize(unhex(test.input), test.slen) + if err != test.err { + t.Errorf("readSize(%s, %d): error mismatch: got %q, want %q", test.input, test.slen, err, test.err) + continue + } + if size != test.size { + t.Errorf("readSize(%s, %d): size mismatch: got %#x, want %#x", test.input, test.slen, size, test.size) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go b/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go new file mode 100644 index 0000000..a2f217c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go @@ -0,0 +1,150 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rlp + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +var ( + typeCacheMutex sync.RWMutex + typeCache = make(map[typekey]*typeinfo) +) + +type typeinfo struct { + decoder + writer +} + +// represents struct tags +type tags struct { + // rlp:"nil" controls whether empty input results in a nil pointer. + nilOK bool + + // rlp:"tail" controls whether this field swallows additional list + // elements. It can only be set for the last field, which must be + // of slice type. + tail bool +} + +type typekey struct { + reflect.Type + // the key must include the struct tags because they + // might generate a different decoder. + tags +} + +type decoder func(*Stream, reflect.Value) error + +type writer func(reflect.Value, *encbuf) error + +func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) { + typeCacheMutex.RLock() + info := typeCache[typekey{typ, tags}] + typeCacheMutex.RUnlock() + if info != nil { + return info, nil + } + // not in the cache, need to generate info for this type. + typeCacheMutex.Lock() + defer typeCacheMutex.Unlock() + return cachedTypeInfo1(typ, tags) +} + +func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) { + key := typekey{typ, tags} + info := typeCache[key] + if info != nil { + // another goroutine got the write lock first + return info, nil + } + // put a dummmy value into the cache before generating. + // if the generator tries to lookup itself, it will get + // the dummy value and won't call itself recursively. + typeCache[key] = new(typeinfo) + info, err := genTypeInfo(typ, tags) + if err != nil { + // remove the dummy value if the generator fails + delete(typeCache, key) + return nil, err + } + *typeCache[key] = *info + return typeCache[key], err +} + +type field struct { + index int + info *typeinfo +} + +func structFields(typ reflect.Type) (fields []field, err error) { + for i := 0; i < typ.NumField(); i++ { + if f := typ.Field(i); f.PkgPath == "" { // exported + tags, err := parseStructTag(typ, i) + if err != nil { + return nil, err + } + info, err := cachedTypeInfo1(f.Type, tags) + if err != nil { + return nil, err + } + fields = append(fields, field{i, info}) + } + } + return fields, nil +} + +func parseStructTag(typ reflect.Type, fi int) (tags, error) { + f := typ.Field(fi) + var ts tags + for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { + switch t = strings.TrimSpace(t); t { + case "": + case "nil": + ts.nilOK = true + case "tail": + ts.tail = true + if fi != typ.NumField()-1 { + return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name) + } + if f.Type.Kind() != reflect.Slice { + return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name) + } + default: + return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) + } + } + return ts, nil +} + +func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) { + info = new(typeinfo) + if info.decoder, err = makeDecoder(typ, tags); err != nil { + return nil, err + } + if info.writer, err = makeWriter(typ, tags); err != nil { + return nil, err + } + return info, nil +} + +func isUint(k reflect.Kind) bool { + return k >= reflect.Uint && k <= reflect.Uintptr +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client.go b/vendor/github.com/ethereum/go-ethereum/rpc/client.go new file mode 100644 index 0000000..269eb78 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client.go @@ -0,0 +1,779 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "container/list" + "encoding/json" + "errors" + "fmt" + "net" + "net/url" + "reflect" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +var ( + ErrClientQuit = errors.New("client is closed") + ErrNoResult = errors.New("no result in JSON-RPC response") + ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow") +) + +const ( + // Timeouts + tcpKeepAliveInterval = 30 * time.Second + defaultDialTimeout = 10 * time.Second // used when dialing if the context has no deadline + defaultWriteTimeout = 10 * time.Second // used for calls if the context has no deadline + subscribeTimeout = 5 * time.Second // overall timeout eth_subscribe, rpc_modules calls +) + +const ( + // Subscriptions are removed when the subscriber cannot keep up. + // + // This can be worked around by supplying a channel with sufficiently sized buffer, + // but this can be inconvenient and hard to explain in the docs. Another issue with + // buffered channels is that the buffer is static even though it might not be needed + // most of the time. + // + // The approach taken here is to maintain a per-subscription linked list buffer + // shrinks on demand. If the buffer reaches the size below, the subscription is + // dropped. + maxClientSubscriptionBuffer = 8000 +) + +// BatchElem is an element in a batch request. +type BatchElem struct { + Method string + Args []interface{} + // The result is unmarshaled into this field. Result must be set to a + // non-nil pointer value of the desired type, otherwise the response will be + // discarded. + Result interface{} + // Error is set if the server returns an error for this request, or if + // unmarshaling into Result fails. It is not set for I/O errors. + Error error +} + +// A value of this type can a JSON-RPC request, notification, successful response or +// error response. Which one it is depends on the fields. +type jsonrpcMessage struct { + Version string `json:"jsonrpc"` + ID json.RawMessage `json:"id,omitempty"` + Method string `json:"method,omitempty"` + Params json.RawMessage `json:"params,omitempty"` + Error *jsonError `json:"error,omitempty"` + Result json.RawMessage `json:"result,omitempty"` +} + +func (msg *jsonrpcMessage) isNotification() bool { + return msg.ID == nil && msg.Method != "" +} + +func (msg *jsonrpcMessage) isResponse() bool { + return msg.hasValidID() && msg.Method == "" && len(msg.Params) == 0 +} + +func (msg *jsonrpcMessage) hasValidID() bool { + return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' +} + +func (msg *jsonrpcMessage) String() string { + b, _ := json.Marshal(msg) + return string(b) +} + +// Client represents a connection to an RPC server. +type Client struct { + idCounter uint32 + connectFunc func(ctx context.Context) (net.Conn, error) + isHTTP bool + + // writeConn is only safe to access outside dispatch, with the + // write lock held. The write lock is taken by sending on + // requestOp and released by sending on sendDone. + writeConn net.Conn + + // for dispatch + close chan struct{} + didQuit chan struct{} // closed when client quits + reconnected chan net.Conn // where write/reconnect sends the new connection + readErr chan error // errors from read + readResp chan []*jsonrpcMessage // valid messages from read + requestOp chan *requestOp // for registering response IDs + sendDone chan error // signals write completion, releases write lock + respWait map[string]*requestOp // active requests + subs map[string]*ClientSubscription // active subscriptions +} + +type requestOp struct { + ids []json.RawMessage + err error + resp chan *jsonrpcMessage // receives up to len(ids) responses + sub *ClientSubscription // only set for EthSubscribe requests +} + +func (op *requestOp) wait(ctx context.Context) (*jsonrpcMessage, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case resp := <-op.resp: + return resp, op.err + } +} + +// Dial creates a new client for the given URL. +// +// The currently supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a +// file name with no URL scheme, a local socket connection is established using UNIX +// domain sockets on supported platforms and named pipes on Windows. If you want to +// configure transport options, use DialHTTP, DialWebsocket or DialIPC instead. +// +// For websocket connections, the origin is set to the local host name. +// +// The client reconnects automatically if the connection is lost. +func Dial(rawurl string) (*Client, error) { + return DialContext(context.Background(), rawurl) +} + +// DialContext creates a new RPC client, just like Dial. +// +// The context is used to cancel or time out the initial connection establishment. It does +// not affect subsequent interactions with the client. +func DialContext(ctx context.Context, rawurl string) (*Client, error) { + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + switch u.Scheme { + case "http", "https": + return DialHTTP(rawurl) + case "ws", "wss": + return DialWebsocket(ctx, rawurl, "") + case "": + return DialIPC(ctx, rawurl) + default: + return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme) + } +} + +func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) { + conn, err := connectFunc(initctx) + if err != nil { + return nil, err + } + _, isHTTP := conn.(*httpConn) + + c := &Client{ + writeConn: conn, + isHTTP: isHTTP, + connectFunc: connectFunc, + close: make(chan struct{}), + didQuit: make(chan struct{}), + reconnected: make(chan net.Conn), + readErr: make(chan error), + readResp: make(chan []*jsonrpcMessage), + requestOp: make(chan *requestOp), + sendDone: make(chan error, 1), + respWait: make(map[string]*requestOp), + subs: make(map[string]*ClientSubscription), + } + if !isHTTP { + go c.dispatch(conn) + } + return c, nil +} + +func (c *Client) nextID() json.RawMessage { + id := atomic.AddUint32(&c.idCounter, 1) + return []byte(strconv.FormatUint(uint64(id), 10)) +} + +// SupportedModules calls the rpc_modules method, retrieving the list of +// APIs that are available on the server. +func (c *Client) SupportedModules() (map[string]string, error) { + var result map[string]string + ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + defer cancel() + err := c.CallContext(ctx, &result, "rpc_modules") + return result, err +} + +// Close closes the client, aborting any in-flight requests. +func (c *Client) Close() { + if c.isHTTP { + return + } + select { + case c.close <- struct{}{}: + <-c.didQuit + case <-c.didQuit: + } +} + +// Call performs a JSON-RPC call with the given arguments and unmarshals into +// result if no error occurred. +// +// The result must be a pointer so that package json can unmarshal into it. You +// can also pass nil, in which case the result is ignored. +func (c *Client) Call(result interface{}, method string, args ...interface{}) error { + ctx := context.Background() + return c.CallContext(ctx, result, method, args...) +} + +// CallContext performs a JSON-RPC call with the given arguments. If the context is +// canceled before the call has successfully returned, CallContext returns immediately. +// +// The result must be a pointer so that package json can unmarshal into it. You +// can also pass nil, in which case the result is ignored. +func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + msg, err := c.newMessage(method, args...) + if err != nil { + return err + } + op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)} + + if c.isHTTP { + err = c.sendHTTP(ctx, op, msg) + } else { + err = c.send(ctx, op, msg) + } + if err != nil { + return err + } + + // dispatch has accepted the request and will close the channel it when it quits. + switch resp, err := op.wait(ctx); { + case err != nil: + return err + case resp.Error != nil: + return resp.Error + case len(resp.Result) == 0: + return ErrNoResult + default: + return json.Unmarshal(resp.Result, &result) + } +} + +// BatchCall sends all given requests as a single batch and waits for the server +// to return a response for all of them. +// +// In contrast to Call, BatchCall only returns I/O errors. Any error specific to +// a request is reported through the Error field of the corresponding BatchElem. +// +// Note that batch calls may not be executed atomically on the server side. +func (c *Client) BatchCall(b []BatchElem) error { + ctx := context.Background() + return c.BatchCallContext(ctx, b) +} + +// BatchCall sends all given requests as a single batch and waits for the server +// to return a response for all of them. The wait duration is bounded by the +// context's deadline. +// +// In contrast to CallContext, BatchCallContext only returns errors that have occurred +// while sending the request. Any error specific to a request is reported through the +// Error field of the corresponding BatchElem. +// +// Note that batch calls may not be executed atomically on the server side. +func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { + msgs := make([]*jsonrpcMessage, len(b)) + op := &requestOp{ + ids: make([]json.RawMessage, len(b)), + resp: make(chan *jsonrpcMessage, len(b)), + } + for i, elem := range b { + msg, err := c.newMessage(elem.Method, elem.Args...) + if err != nil { + return err + } + msgs[i] = msg + op.ids[i] = msg.ID + } + + var err error + if c.isHTTP { + err = c.sendBatchHTTP(ctx, op, msgs) + } else { + err = c.send(ctx, op, msgs) + } + + // Wait for all responses to come back. + for n := 0; n < len(b) && err == nil; n++ { + var resp *jsonrpcMessage + resp, err = op.wait(ctx) + if err != nil { + break + } + // Find the element corresponding to this response. + // The element is guaranteed to be present because dispatch + // only sends valid IDs to our channel. + var elem *BatchElem + for i := range msgs { + if bytes.Equal(msgs[i].ID, resp.ID) { + elem = &b[i] + break + } + } + if resp.Error != nil { + elem.Error = resp.Error + continue + } + if len(resp.Result) == 0 { + elem.Error = ErrNoResult + continue + } + elem.Error = json.Unmarshal(resp.Result, elem.Result) + } + return err +} + +// EthSubscribe calls the "eth_subscribe" method with the given arguments, +// registering a subscription. Server notifications for the subscription are +// sent to the given channel. The element type of the channel must match the +// expected type of content returned by the subscription. +// +// The context argument cancels the RPC request that sets up the subscription but has no +// effect on the subscription after EthSubscribe has returned. +// +// Slow subscribers will be dropped eventually. Client buffers up to 8000 notifications +// before considering the subscriber dead. The subscription Err channel will receive +// ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure +// that the channel usually has at least one reader to prevent this issue. +func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { + // Check type of channel first. + chanVal := reflect.ValueOf(channel) + if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { + panic("first argument to EthSubscribe must be a writable channel") + } + if chanVal.IsNil() { + panic("channel given to EthSubscribe must not be nil") + } + if c.isHTTP { + return nil, ErrNotificationsUnsupported + } + + msg, err := c.newMessage(subscribeMethod, args...) + if err != nil { + return nil, err + } + op := &requestOp{ + ids: []json.RawMessage{msg.ID}, + resp: make(chan *jsonrpcMessage), + sub: newClientSubscription(c, chanVal), + } + + // Send the subscription request. + // The arrival and validity of the response is signaled on sub.quit. + if err := c.send(ctx, op, msg); err != nil { + return nil, err + } + if _, err := op.wait(ctx); err != nil { + return nil, err + } + return op.sub, nil +} + +func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { + params, err := json.Marshal(paramsIn) + if err != nil { + return nil, err + } + return &jsonrpcMessage{Version: "2.0", ID: c.nextID(), Method: method, Params: params}, nil +} + +// send registers op with the dispatch loop, then sends msg on the connection. +// if sending fails, op is deregistered. +func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { + select { + case c.requestOp <- op: + if glog.V(logger.Detail) { + glog.Info("sending ", msg) + } + err := c.write(ctx, msg) + c.sendDone <- err + return err + case <-ctx.Done(): + // This can happen if the client is overloaded or unable to keep up with + // subscription notifications. + return ctx.Err() + case <-c.didQuit: + return ErrClientQuit + } +} + +func (c *Client) write(ctx context.Context, msg interface{}) error { + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(defaultWriteTimeout) + } + // The previous write failed. Try to establish a new connection. + if c.writeConn == nil { + if err := c.reconnect(ctx); err != nil { + return err + } + } + c.writeConn.SetWriteDeadline(deadline) + err := json.NewEncoder(c.writeConn).Encode(msg) + if err != nil { + c.writeConn = nil + } + return err +} + +func (c *Client) reconnect(ctx context.Context) error { + newconn, err := c.connectFunc(ctx) + if err != nil { + glog.V(logger.Detail).Infof("reconnect failed: %v", err) + return err + } + select { + case c.reconnected <- newconn: + c.writeConn = newconn + return nil + case <-c.didQuit: + newconn.Close() + return ErrClientQuit + } +} + +// dispatch is the main loop of the client. +// It sends read messages to waiting calls to Call and BatchCall +// and subscription notifications to registered subscriptions. +func (c *Client) dispatch(conn net.Conn) { + // Spawn the initial read loop. + go c.read(conn) + + var ( + lastOp *requestOp // tracks last send operation + requestOpLock = c.requestOp // nil while the send lock is held + reading = true // if true, a read loop is running + ) + defer close(c.didQuit) + defer func() { + c.closeRequestOps(ErrClientQuit) + conn.Close() + if reading { + // Empty read channels until read is dead. + for { + select { + case <-c.readResp: + case <-c.readErr: + return + } + } + } + }() + + for { + select { + case <-c.close: + return + + // Read path. + case batch := <-c.readResp: + for _, msg := range batch { + switch { + case msg.isNotification(): + if glog.V(logger.Detail) { + glog.Info("<-readResp: notification ", msg) + } + c.handleNotification(msg) + case msg.isResponse(): + if glog.V(logger.Detail) { + glog.Info("<-readResp: response ", msg) + } + c.handleResponse(msg) + default: + if glog.V(logger.Debug) { + glog.Error("<-readResp: dropping weird message", msg) + } + // TODO: maybe close + } + } + + case err := <-c.readErr: + glog.V(logger.Debug).Infof("<-readErr: %v", err) + c.closeRequestOps(err) + conn.Close() + reading = false + + case newconn := <-c.reconnected: + glog.V(logger.Debug).Infof("<-reconnected: (reading=%t) %v", reading, conn.RemoteAddr()) + if reading { + // Wait for the previous read loop to exit. This is a rare case. + conn.Close() + <-c.readErr + } + go c.read(newconn) + reading = true + conn = newconn + + // Send path. + case op := <-requestOpLock: + // Stop listening for further send ops until the current one is done. + requestOpLock = nil + lastOp = op + for _, id := range op.ids { + c.respWait[string(id)] = op + } + + case err := <-c.sendDone: + if err != nil { + // Remove response handlers for the last send. We remove those here + // because the error is already handled in Call or BatchCall. When the + // read loop goes down, it will signal all other current operations. + for _, id := range lastOp.ids { + delete(c.respWait, string(id)) + } + } + // Listen for send ops again. + requestOpLock = c.requestOp + lastOp = nil + } + } +} + +// closeRequestOps unblocks pending send ops and active subscriptions. +func (c *Client) closeRequestOps(err error) { + didClose := make(map[*requestOp]bool) + + for id, op := range c.respWait { + // Remove the op so that later calls will not close op.resp again. + delete(c.respWait, id) + + if !didClose[op] { + op.err = err + close(op.resp) + didClose[op] = true + } + } + for id, sub := range c.subs { + delete(c.subs, id) + sub.quitWithError(err, false) + } +} + +func (c *Client) handleNotification(msg *jsonrpcMessage) { + if msg.Method != notificationMethod { + glog.V(logger.Debug).Info("dropping non-subscription message: ", msg) + return + } + var subResult struct { + ID string `json:"subscription"` + Result json.RawMessage `json:"result"` + } + if err := json.Unmarshal(msg.Params, &subResult); err != nil { + glog.V(logger.Debug).Info("dropping invalid subscription message: ", msg) + return + } + if c.subs[subResult.ID] != nil { + c.subs[subResult.ID].deliver(subResult.Result) + } +} + +func (c *Client) handleResponse(msg *jsonrpcMessage) { + op := c.respWait[string(msg.ID)] + if op == nil { + glog.V(logger.Debug).Infof("unsolicited response %v", msg) + return + } + delete(c.respWait, string(msg.ID)) + // For normal responses, just forward the reply to Call/BatchCall. + if op.sub == nil { + op.resp <- msg + return + } + // For subscription responses, start the subscription if the server + // indicates success. EthSubscribe gets unblocked in either case through + // the op.resp channel. + defer close(op.resp) + if msg.Error != nil { + op.err = msg.Error + return + } + if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { + go op.sub.start() + c.subs[op.sub.subid] = op.sub + } +} + +// Reading happens on a dedicated goroutine. + +func (c *Client) read(conn net.Conn) error { + var ( + buf json.RawMessage + dec = json.NewDecoder(conn) + ) + readMessage := func() (rs []*jsonrpcMessage, err error) { + buf = buf[:0] + if err = dec.Decode(&buf); err != nil { + return nil, err + } + if isBatch(buf) { + err = json.Unmarshal(buf, &rs) + } else { + rs = make([]*jsonrpcMessage, 1) + err = json.Unmarshal(buf, &rs[0]) + } + return rs, err + } + + for { + resp, err := readMessage() + if err != nil { + c.readErr <- err + return err + } + c.readResp <- resp + } +} + +// Subscriptions. + +// A ClientSubscription represents a subscription established through EthSubscribe. +type ClientSubscription struct { + client *Client + etype reflect.Type + channel reflect.Value + subid string + in chan json.RawMessage + + quitOnce sync.Once // ensures quit is closed once + quit chan struct{} // quit is closed when the subscription exits + errOnce sync.Once // ensures err is closed once + err chan error +} + +func newClientSubscription(c *Client, channel reflect.Value) *ClientSubscription { + sub := &ClientSubscription{ + client: c, + etype: channel.Type().Elem(), + channel: channel, + quit: make(chan struct{}), + err: make(chan error, 1), + in: make(chan json.RawMessage), + } + return sub +} + +// Err returns the subscription error channel. The intended use of Err is to schedule +// resubscription when the client connection is closed unexpectedly. +// +// The error channel receives a value when the subscription has ended due +// to an error. The received error is nil if Close has been called +// on the underlying client and no other error has occurred. +// +// The error channel is closed when Unsubscribe is called on the subscription. +func (sub *ClientSubscription) Err() <-chan error { + return sub.err +} + +// Unsubscribe unsubscribes the notification and closes the error channel. +// It can safely be called more than once. +func (sub *ClientSubscription) Unsubscribe() { + sub.quitWithError(nil, true) + sub.errOnce.Do(func() { close(sub.err) }) +} + +func (sub *ClientSubscription) quitWithError(err error, unsubscribeServer bool) { + sub.quitOnce.Do(func() { + // The dispatch loop won't be able to execute the unsubscribe call + // if it is blocked on deliver. Close sub.quit first because it + // unblocks deliver. + close(sub.quit) + if unsubscribeServer { + sub.requestUnsubscribe() + } + if err != nil { + if err == ErrClientQuit { + err = nil // Adhere to subscription semantics. + } + sub.err <- err + } + }) +} + +func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { + select { + case sub.in <- result: + return true + case <-sub.quit: + return false + } +} + +func (sub *ClientSubscription) start() { + sub.quitWithError(sub.forward()) +} + +func (sub *ClientSubscription) forward() (err error, unsubscribeServer bool) { + cases := []reflect.SelectCase{ + {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, + {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, + {Dir: reflect.SelectSend, Chan: sub.channel}, + } + buffer := list.New() + defer buffer.Init() + for { + var chosen int + var recv reflect.Value + if buffer.Len() == 0 { + // Idle, omit send case. + chosen, recv, _ = reflect.Select(cases[:2]) + } else { + // Non-empty buffer, send the first queued item. + cases[2].Send = reflect.ValueOf(buffer.Front().Value) + chosen, recv, _ = reflect.Select(cases) + } + + switch chosen { + case 0: // <-sub.quit + return nil, false + case 1: // <-sub.in + val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) + if err != nil { + return err, true + } + if buffer.Len() == maxClientSubscriptionBuffer { + return ErrSubscriptionQueueOverflow, true + } + buffer.PushBack(val) + case 2: // sub.channel<- + cases[2].Send = reflect.Value{} // Don't hold onto the value. + buffer.Remove(buffer.Front()) + } + } +} + +func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { + val := reflect.New(sub.etype) + err := json.Unmarshal(result, val.Interface()) + return val.Elem().Interface(), err +} + +func (sub *ClientSubscription) requestUnsubscribe() error { + var result interface{} + return sub.client.Call(&result, unsubscribeMethod, sub.subid) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.4.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.4.go new file mode 100644 index 0000000..ac956a1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.4.go @@ -0,0 +1,60 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !go1.5 + +package rpc + +import ( + "net" + "net/http" + "time" + + "golang.org/x/net/context" +) + +// In older versions of Go (below 1.5), dials cannot be canceled +// via a channel or context. The context deadline can still applied. + +// contextDialer returns a dialer that applies the deadline value from the given context. +func contextDialer(ctx context.Context) *net.Dialer { + dialer := &net.Dialer{KeepAlive: tcpKeepAliveInterval} + if deadline, ok := ctx.Deadline(); ok { + dialer.Deadline = deadline + } else { + dialer.Deadline = time.Now().Add(defaultDialTimeout) + } + return dialer +} + +// dialContext connects to the given address, aborting the dial if ctx is canceled. +func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + return contextDialer(ctx).Dial(network, addr) +} + +// requestWithContext copies req, adding the cancelation channel and deadline from ctx. +func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { + // Set Timeout on the client if the context has a deadline. + // Note that there is no default timeout (unlike in contextDialer) because + // the timeout applies to the entire request, including reads from body. + if deadline, ok := ctx.Deadline(); ok { + c2 := *c + c2.Timeout = deadline.Sub(time.Now()) + c = &c2 + } + req2 := *req + return c, &req2 +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.5.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.5.go new file mode 100644 index 0000000..4a007d9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.5.go @@ -0,0 +1,61 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build go1.5,!go1.6 + +package rpc + +import ( + "net" + "net/http" + "time" + + "golang.org/x/net/context" +) + +// In Go 1.5, dials cannot be canceled via a channel or context. The context deadline can +// still be applied. Go 1.5 adds the ability to cancel HTTP requests via a channel. + +// contextDialer returns a dialer that applies the deadline value from the given context. +func contextDialer(ctx context.Context) *net.Dialer { + dialer := &net.Dialer{KeepAlive: tcpKeepAliveInterval} + if deadline, ok := ctx.Deadline(); ok { + dialer.Deadline = deadline + } else { + dialer.Deadline = time.Now().Add(defaultDialTimeout) + } + return dialer +} + +// dialContext connects to the given address, aborting the dial if ctx is canceled. +func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + return contextDialer(ctx).Dial(network, addr) +} + +// requestWithContext copies req, adding the cancelation channel and deadline from ctx. +func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { + // Set Timeout on the client if the context has a deadline. + // Note that there is no default timeout (unlike in contextDialer) because + // the timeout applies to the entire request, including reads from body. + if deadline, ok := ctx.Deadline(); ok { + c2 := *c + c2.Timeout = deadline.Sub(time.Now()) + c = &c2 + } + req2 := *req + req2.Cancel = ctx.Done() + return c, &req2 +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.6.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.6.go new file mode 100644 index 0000000..67777dd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.6.go @@ -0,0 +1,55 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build go1.6,!go1.7 + +package rpc + +import ( + "net" + "net/http" + "time" + + "golang.org/x/net/context" +) + +// In Go 1.6, net.Dialer gained the ability to cancel via a channel. + +// contextDialer returns a dialer that applies the deadline value from the given context. +func contextDialer(ctx context.Context) *net.Dialer { + dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} + if deadline, ok := ctx.Deadline(); ok { + dialer.Deadline = deadline + } else { + dialer.Deadline = time.Now().Add(defaultDialTimeout) + } + return dialer +} + +// dialContext connects to the given address, aborting the dial if ctx is canceled. +func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + return contextDialer(ctx).Dial(network, addr) +} + +// requestWithContext copies req, adding the cancelation channel and deadline from ctx. +func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { + // We set Timeout on the client for Go <= 1.5. There + // is no need to do that here because the dial will be canceled + // by package http. + req2 := *req + req2.Cancel = ctx.Done() + return c, &req2 +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.7.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.7.go new file mode 100644 index 0000000..56ce12a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_context_go1.7.go @@ -0,0 +1,51 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build go1.7 + +package rpc + +import ( + "context" + "net" + "net/http" + "time" +) + +// In Go 1.7, context moved into the standard library and support +// for cancelation via context was added to net.Dialer and http.Request. + +// contextDialer returns a dialer that applies the deadline value from the given context. +func contextDialer(ctx context.Context) *net.Dialer { + dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} + if deadline, ok := ctx.Deadline(); ok { + dialer.Deadline = deadline + } else { + dialer.Deadline = time.Now().Add(defaultDialTimeout) + } + return dialer +} + +// dialContext connects to the given address, aborting the dial if ctx is canceled. +func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + d := &net.Dialer{KeepAlive: tcpKeepAliveInterval} + return d.DialContext(ctx, network, addr) +} + +// requestWithContext copies req, adding the cancelation channel and deadline from ctx. +func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { + return c, req.WithContext(ctx) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_example_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_example_test.go new file mode 100644 index 0000000..3462b36 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_example_test.go @@ -0,0 +1,88 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc_test + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" +) + +// In this example, our client whishes to track the latest 'block number' +// known to the server. The server supports two methods: +// +// eth_getBlockByNumber("latest", {}) +// returns the latest block object. +// +// eth_subscribe("newBlocks") +// creates a subscription which fires block objects when new blocks arrive. + +type Block struct { + Number *big.Int +} + +func ExampleClientSubscription() { + // Connect the client. + client, _ := rpc.Dial("ws://127.0.0.1:8485") + subch := make(chan Block) + + // Ensure that subch receives the latest block. + go func() { + for i := 0; ; i++ { + if i > 0 { + time.Sleep(2 * time.Second) + } + subscribeBlocks(client, subch) + } + }() + + // Print events from the subscription as they arrive. + for block := range subch { + fmt.Println("latest block:", block.Number) + } +} + +// subscribeBlocks runs in its own goroutine and maintains +// a subscription for new blocks. +func subscribeBlocks(client *rpc.Client, subch chan Block) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Subscribe to new blocks. + sub, err := client.EthSubscribe(ctx, subch, "newBlocks") + if err != nil { + fmt.Println("subscribe error:", err) + return + } + + // The connection is established now. + // Update the channel with the current block. + var lastBlock Block + if err := client.CallContext(ctx, &lastBlock, "eth_getBlockByNumber", "latest"); err != nil { + fmt.Println("can't get latest block:", err) + return + } + subch <- lastBlock + + // The subscription will deliver events to the channel. Wait for the + // subscription to end for any reason, then loop around to re-establish + // the connection. + fmt.Println("connection lost: ", <-sub.Err()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/client_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/client_test.go new file mode 100644 index 0000000..476c8c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/client_test.go @@ -0,0 +1,540 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "fmt" + "math/rand" + "net" + "net/http" + "net/http/httptest" + "os" + "reflect" + "runtime" + "sync" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +func TestClientRequest(t *testing.T) { + server := newTestServer("service", new(Service)) + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + var resp Result + if err := client.Call(&resp, "service_echo", "hello", 10, &Args{"world"}); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) { + t.Errorf("incorrect result %#v", resp) + } +} + +func TestClientBatchRequest(t *testing.T) { + server := newTestServer("service", new(Service)) + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + batch := []BatchElem{ + { + Method: "service_echo", + Args: []interface{}{"hello", 10, &Args{"world"}}, + Result: new(Result), + }, + { + Method: "service_echo", + Args: []interface{}{"hello2", 11, &Args{"world"}}, + Result: new(Result), + }, + { + Method: "no_such_method", + Args: []interface{}{1, 2, 3}, + Result: new(int), + }, + } + if err := client.BatchCall(batch); err != nil { + t.Fatal(err) + } + wantResult := []BatchElem{ + { + Method: "service_echo", + Args: []interface{}{"hello", 10, &Args{"world"}}, + Result: &Result{"hello", 10, &Args{"world"}}, + }, + { + Method: "service_echo", + Args: []interface{}{"hello2", 11, &Args{"world"}}, + Result: &Result{"hello2", 11, &Args{"world"}}, + }, + { + Method: "no_such_method", + Args: []interface{}{1, 2, 3}, + Result: new(int), + Error: &jsonError{Code: -32601, Message: "The method no_such_method_ does not exist/is not available"}, + }, + } + if !reflect.DeepEqual(batch, wantResult) { + t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) + } +} + +// func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) } +func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } +func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } +func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } + +// This test checks that requests made through CallContext can be canceled by canceling +// the context. +func testClientCancel(transport string, t *testing.T) { + server := newTestServer("service", new(Service)) + defer server.Stop() + + // What we want to achieve is that the context gets canceled + // at various stages of request processing. The interesting cases + // are: + // - cancel during dial + // - cancel while performing a HTTP request + // - cancel while waiting for a response + // + // To trigger those, the times are chosen such that connections + // are killed within the deadline for every other call (maxKillTimeout + // is 2x maxCancelTimeout). + // + // Once a connection is dead, there is a fair chance it won't connect + // successfully because the accept is delayed by 1s. + maxContextCancelTimeout := 300 * time.Millisecond + fl := &flakeyListener{ + maxAcceptDelay: 1 * time.Second, + maxKillTimeout: 600 * time.Millisecond, + } + + var client *Client + switch transport { + case "ws", "http": + c, hs := httpTestClient(server, transport, fl) + defer hs.Close() + client = c + case "ipc": + c, l := ipcTestClient(server, fl) + defer l.Close() + client = c + default: + panic("unknown transport: " + transport) + } + + // These tests take a lot of time, run them all at once. + // You probably want to run with -parallel 1 or comment out + // the call to t.Parallel if you enable the logging. + t.Parallel() + // glog.SetV(6) + // glog.SetToStderr(true) + // defer glog.SetToStderr(false) + // glog.Infoln("testing ", transport) + + // The actual test starts here. + var ( + wg sync.WaitGroup + nreqs = 10 + ncallers = 6 + ) + caller := func(index int) { + defer wg.Done() + for i := 0; i < nreqs; i++ { + var ( + ctx context.Context + cancel func() + timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) + ) + if index < ncallers/2 { + // For half of the callers, create a context without deadline + // and cancel it later. + ctx, cancel = context.WithCancel(context.Background()) + time.AfterFunc(timeout, cancel) + } else { + // For the other half, create a context with a deadline instead. This is + // different because the context deadline is used to set the socket write + // deadline. + ctx, cancel = context.WithTimeout(context.Background(), timeout) + } + // Now perform a call with the context. + // The key thing here is that no call will ever complete successfully. + err := client.CallContext(ctx, nil, "service_sleep", 2*maxContextCancelTimeout) + if err != nil { + glog.V(logger.Debug).Infoln("got expected error:", err) + } else { + t.Errorf("no error for call with %v wait time", timeout) + } + cancel() + } + } + wg.Add(ncallers) + for i := 0; i < ncallers; i++ { + go caller(i) + } + wg.Wait() +} + +func TestClientSubscribeInvalidArg(t *testing.T) { + server := newTestServer("service", new(Service)) + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + check := func(shouldPanic bool, arg interface{}) { + defer func() { + err := recover() + if shouldPanic && err == nil { + t.Errorf("EthSubscribe should've panicked for %#v", arg) + } + if !shouldPanic && err != nil { + t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) + buf := make([]byte, 1024*1024) + buf = buf[:runtime.Stack(buf, false)] + t.Error(err) + t.Error(string(buf)) + } + }() + client.EthSubscribe(context.Background(), arg, "foo_bar") + } + check(true, nil) + check(true, 1) + check(true, (chan int)(nil)) + check(true, make(<-chan int)) + check(false, make(chan int)) + check(false, make(chan<- int)) +} + +func TestClientSubscribe(t *testing.T) { + server := newTestServer("eth", new(NotificationTestService)) + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + nc := make(chan int) + count := 10 + sub, err := client.EthSubscribe(context.Background(), nc, "someSubscription", count, 0) + if err != nil { + t.Fatal("can't subscribe:", err) + } + for i := 0; i < count; i++ { + if val := <-nc; val != i { + t.Fatalf("value mismatch: got %d, want %d", val, i) + } + } + + sub.Unsubscribe() + select { + case v := <-nc: + t.Fatal("received value after unsubscribe:", v) + case err := <-sub.Err(): + if err != nil { + t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) + } + case <-time.After(1 * time.Second): + t.Fatalf("subscription not closed within 1s after unsubscribe") + } +} + +// In this test, the connection drops while EthSubscribe is +// waiting for a response. +func TestClientSubscribeClose(t *testing.T) { + service := &NotificationTestService{ + gotHangSubscriptionReq: make(chan struct{}), + unblockHangSubscription: make(chan struct{}), + } + server := newTestServer("eth", service) + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + var ( + nc = make(chan int) + errc = make(chan error) + sub *ClientSubscription + err error + ) + go func() { + sub, err = client.EthSubscribe(context.Background(), nc, "hangSubscription", 999) + errc <- err + }() + + <-service.gotHangSubscriptionReq + client.Close() + service.unblockHangSubscription <- struct{}{} + + select { + case err := <-errc: + if err == nil { + t.Errorf("EthSubscribe returned nil error after Close") + } + if sub != nil { + t.Error("EthSubscribe returned non-nil subscription after Close") + } + case <-time.After(1 * time.Second): + t.Fatalf("EthSubscribe did not return within 1s after Close") + } +} + +// This test checks that Client doesn't lock up when a single subscriber +// doesn't read subscription events. +func TestClientNotificationStorm(t *testing.T) { + server := newTestServer("eth", new(NotificationTestService)) + defer server.Stop() + + doTest := func(count int, wantError bool) { + client := DialInProc(server) + defer client.Close() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Subscribe on the server. It will start sending many notifications + // very quickly. + nc := make(chan int) + sub, err := client.EthSubscribe(ctx, nc, "someSubscription", count, 0) + if err != nil { + t.Fatal("can't subscribe:", err) + } + defer sub.Unsubscribe() + + // Process each notification, try to run a call in between each of them. + for i := 0; i < count; i++ { + select { + case val := <-nc: + if val != i { + t.Fatalf("(%d/%d) unexpected value %d", i, count, val) + } + case err := <-sub.Err(): + if wantError && err != ErrSubscriptionQueueOverflow { + t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) + } else if !wantError { + t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) + } + return + } + var r int + err := client.CallContext(ctx, &r, "eth_echo", i) + if err != nil { + if !wantError { + t.Fatalf("(%d/%d) call error: %v", i, count, err) + } + return + } + } + } + + doTest(8000, false) + doTest(10000, true) +} + +func TestClientHTTP(t *testing.T) { + server := newTestServer("service", new(Service)) + defer server.Stop() + + client, hs := httpTestClient(server, "http", nil) + defer hs.Close() + defer client.Close() + + // Launch concurrent requests. + var ( + results = make([]Result, 100) + errc = make(chan error) + wantResult = Result{"a", 1, new(Args)} + ) + defer client.Close() + for i := range results { + i := i + go func() { + errc <- client.Call(&results[i], "service_echo", + wantResult.String, wantResult.Int, wantResult.Args) + }() + } + + // Wait for all of them to complete. + timeout := time.NewTimer(5 * time.Second) + defer timeout.Stop() + for i := range results { + select { + case err := <-errc: + if err != nil { + t.Fatal(err) + } + case <-timeout.C: + t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) + } + } + + // Check results. + for i := range results { + if !reflect.DeepEqual(results[i], wantResult) { + t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) + } + } +} + +func TestClientReconnect(t *testing.T) { + startServer := func(addr string) (*Server, net.Listener) { + srv := newTestServer("service", new(Service)) + l, err := net.Listen("tcp", addr) + if err != nil { + t.Fatal(err) + } + go http.Serve(l, srv.WebsocketHandler("*")) + return srv, l + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Start a server and corresponding client. + s1, l1 := startServer("127.0.0.1:0") + client, err := DialContext(ctx, "ws://"+l1.Addr().String()) + if err != nil { + t.Fatal("can't dial", err) + } + + // Perform a call. This should work because the server is up. + var resp Result + if err := client.CallContext(ctx, &resp, "service_echo", "", 1, nil); err != nil { + t.Fatal(err) + } + + // Shut down the server and try calling again. It shouldn't work. + l1.Close() + s1.Stop() + if err := client.CallContext(ctx, &resp, "service_echo", "", 2, nil); err == nil { + t.Error("successful call while the server is down") + t.Logf("resp: %#v", resp) + } + + // Allow for some cool down time so we can listen on the same address again. + time.Sleep(2 * time.Second) + + // Start it up again and call again. The connection should be reestablished. + // We spawn multiple calls here to check whether this hangs somehow. + s2, l2 := startServer(l1.Addr().String()) + defer l2.Close() + defer s2.Stop() + + start := make(chan struct{}) + errors := make(chan error, 20) + for i := 0; i < cap(errors); i++ { + go func() { + <-start + var resp Result + errors <- client.CallContext(ctx, &resp, "service_echo", "", 3, nil) + }() + } + close(start) + errcount := 0 + for i := 0; i < cap(errors); i++ { + if err = <-errors; err != nil { + errcount++ + } + } + t.Log("err:", err) + if errcount > 1 { + t.Errorf("expected one error after disconnect, got %d", errcount) + } +} + +func newTestServer(serviceName string, service interface{}) *Server { + server := NewServer() + if err := server.RegisterName(serviceName, service); err != nil { + panic(err) + } + return server +} + +func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { + // Create the HTTP server. + var hs *httptest.Server + switch transport { + case "ws": + hs = httptest.NewUnstartedServer(srv.WebsocketHandler("*")) + case "http": + hs = httptest.NewUnstartedServer(srv) + default: + panic("unknown HTTP transport: " + transport) + } + // Wrap the listener if required. + if fl != nil { + fl.Listener = hs.Listener + hs.Listener = fl + } + // Connect the client. + hs.Start() + client, err := Dial(transport + "://" + hs.Listener.Addr().String()) + if err != nil { + panic(err) + } + return client, hs +} + +func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { + // Listen on a random endpoint. + endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) + if runtime.GOOS == "windows" { + endpoint = `\\.\pipe\` + endpoint + } else { + endpoint = os.TempDir() + "/" + endpoint + } + l, err := ipcListen(endpoint) + if err != nil { + panic(err) + } + // Connect the listener to the server. + if fl != nil { + fl.Listener = l + l = fl + } + go srv.ServeListener(l) + // Connect the client. + client, err := Dial(endpoint) + if err != nil { + panic(err) + } + return client, l +} + +// flakeyListener kills accepted connections after a random timeout. +type flakeyListener struct { + net.Listener + maxKillTimeout time.Duration + maxAcceptDelay time.Duration +} + +func (l *flakeyListener) Accept() (net.Conn, error) { + delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) + time.Sleep(delay) + + c, err := l.Listener.Accept() + if err == nil { + timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) + time.AfterFunc(timeout, func() { + glog.V(logger.Debug).Infof("killing conn %v after %v", c.LocalAddr(), timeout) + c.Close() + }) + } + return c, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/doc.go b/vendor/github.com/ethereum/go-ethereum/rpc/doc.go new file mode 100644 index 0000000..14b3780 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/doc.go @@ -0,0 +1,98 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +Package rpc provides access to the exported methods of an object across a network +or other I/O connection. After creating a server instance objects can be registered, +making it visible from the outside. Exported methods that follow specific +conventions can be called remotely. It also has support for the publish/subscribe +pattern. + +Methods that satisfy the following criteria are made available for remote access: + - object must be exported + - method must be exported + - method returns 0, 1 (response or error) or 2 (response and error) values + - method argument(s) must be exported or builtin types + - method returned value(s) must be exported or builtin types + +An example method: + func (s *CalcService) Add(a, b int) (int, error) + +When the returned error isn't nil the returned integer is ignored and the error is +send back to the client. Otherwise the returned integer is send back to the client. + +Optional arguments are supported by accepting pointer values as arguments. E.g. +if we want to do the addition in an optional finite field we can accept a mod +argument as pointer value. + + func (s *CalService) Add(a, b int, mod *int) (int, error) + +This RPC method can be called with 2 integers and a null value as third argument. +In that case the mod argument will be nil. Or it can be called with 3 integers, +in that case mod will be pointing to the given third argument. Since the optional +argument is the last argument the RPC package will also accept 2 integers as +arguments. It will pass the mod argument as nil to the RPC method. + +The server offers the ServeCodec method which accepts a ServerCodec instance. It will +read requests from the codec, process the request and sends the response back to the +client using the codec. The server can execute requests concurrently. Responses +can be sent back to the client out of order. + +An example server which uses the JSON codec: + type CalculatorService struct {} + + func (s *CalculatorService) Add(a, b int) int { + return a + b + } + + func (s *CalculatorService Div(a, b int) (int, error) { + if b == 0 { + return 0, errors.New("divide by zero") + } + return a/b, nil + } + + calculator := new(CalculatorService) + server := NewServer() + server.RegisterName("calculator", calculator") + + l, _ := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: "/tmp/calculator.sock"}) + for { + c, _ := l.AcceptUnix() + codec := v2.NewJSONCodec(c) + go server.ServeCodec(codec) + } + +The package also supports the publish subscribe pattern through the use of subscriptions. +A method that is considered eligible for notifications must satisfy the following criteria: + - object must be exported + - method must be exported + - first method argument type must be context.Context + - method argument(s) must be exported or builtin types + - method must return the tuple Subscription, error + +An example method: + func (s *BlockChainService) NewBlocks(ctx context.Context) (Subscription, error) { + ... + } + +Subscriptions are deleted when: + - the user sends an unsubscribe request + - the connection which was used to create the subscription is closed. This can be initiated + by the client and server. The server will close the connection on an write error or when + the queue of buffered notifications gets too big. +*/ +package rpc diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/errors.go b/vendor/github.com/ethereum/go-ethereum/rpc/errors.go new file mode 100644 index 0000000..9cf9dc6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/errors.go @@ -0,0 +1,66 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import "fmt" + +// request is for an unknown service +type methodNotFoundError struct { + service string + method string +} + +func (e *methodNotFoundError) ErrorCode() int { return -32601 } + +func (e *methodNotFoundError) Error() string { + return fmt.Sprintf("The method %s%s%s does not exist/is not available", e.service, serviceMethodSeparator, e.method) +} + +// received message isn't a valid request +type invalidRequestError struct{ message string } + +func (e *invalidRequestError) ErrorCode() int { return -32600 } + +func (e *invalidRequestError) Error() string { return e.message } + +// received message is invalid +type invalidMessageError struct{ message string } + +func (e *invalidMessageError) ErrorCode() int { return -32700 } + +func (e *invalidMessageError) Error() string { return e.message } + +// unable to decode supplied params, or an invalid number of parameters +type invalidParamsError struct{ message string } + +func (e *invalidParamsError) ErrorCode() int { return -32602 } + +func (e *invalidParamsError) Error() string { return e.message } + +// logic error, callback returned an error +type callbackError struct{ message string } + +func (e *callbackError) ErrorCode() int { return -32000 } + +func (e *callbackError) Error() string { return e.message } + +// issued when a request is received after the server is issued to stop. +type shutdownError struct{} + +func (e *shutdownError) ErrorCode() int { return -32000 } + +func (e *shutdownError) Error() string { return "server is shutting down" } diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/http.go b/vendor/github.com/ethereum/go-ethereum/rpc/http.go new file mode 100644 index 0000000..7d4fe5d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/http.go @@ -0,0 +1,176 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "strings" + "sync" + "time" + + "github.com/rs/cors" + "golang.org/x/net/context" +) + +const ( + maxHTTPRequestContentLength = 1024 * 128 +) + +var nullAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:0") + +type httpConn struct { + client *http.Client + req *http.Request + closeOnce sync.Once + closed chan struct{} +} + +// httpConn is treated specially by Client. +func (hc *httpConn) LocalAddr() net.Addr { return nullAddr } +func (hc *httpConn) RemoteAddr() net.Addr { return nullAddr } +func (hc *httpConn) SetReadDeadline(time.Time) error { return nil } +func (hc *httpConn) SetWriteDeadline(time.Time) error { return nil } +func (hc *httpConn) SetDeadline(time.Time) error { return nil } +func (hc *httpConn) Write([]byte) (int, error) { panic("Write called") } + +func (hc *httpConn) Read(b []byte) (int, error) { + <-hc.closed + return 0, io.EOF +} + +func (hc *httpConn) Close() error { + hc.closeOnce.Do(func() { close(hc.closed) }) + return nil +} + +// DialHTTP creates a new RPC clients that connection to an RPC server over HTTP. +func DialHTTP(endpoint string) (*Client, error) { + req, err := http.NewRequest("POST", endpoint, nil) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + + initctx := context.Background() + return newClient(initctx, func(context.Context) (net.Conn, error) { + return &httpConn{client: new(http.Client), req: req, closed: make(chan struct{})}, nil + }) +} + +func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { + hc := c.writeConn.(*httpConn) + respBody, err := hc.doRequest(ctx, msg) + if err != nil { + return err + } + defer respBody.Close() + var respmsg jsonrpcMessage + if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { + return err + } + op.resp <- &respmsg + return nil +} + +func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { + hc := c.writeConn.(*httpConn) + respBody, err := hc.doRequest(ctx, msgs) + if err != nil { + return err + } + defer respBody.Close() + var respmsgs []jsonrpcMessage + if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { + return err + } + for _, respmsg := range respmsgs { + op.resp <- &respmsg + } + return nil +} + +func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { + body, err := json.Marshal(msg) + if err != nil { + return nil, err + } + client, req := requestWithContext(hc.client, hc.req, ctx) + req.Body = ioutil.NopCloser(bytes.NewReader(body)) + req.ContentLength = int64(len(body)) + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + return resp.Body, nil +} + +// httpReadWriteNopCloser wraps a io.Reader and io.Writer with a NOP Close method. +type httpReadWriteNopCloser struct { + io.Reader + io.Writer +} + +// Close does nothing and returns always nil +func (t *httpReadWriteNopCloser) Close() error { + return nil +} + +// NewHTTPServer creates a new HTTP RPC server around an API provider. +// +// Deprecated: Server implements http.Handler +func NewHTTPServer(corsString string, srv *Server) *http.Server { + return &http.Server{Handler: newCorsHandler(srv, corsString)} +} + +// ServeHTTP serves JSON-RPC requests over HTTP. +func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.ContentLength > maxHTTPRequestContentLength { + http.Error(w, + fmt.Sprintf("content length too large (%d>%d)", r.ContentLength, maxHTTPRequestContentLength), + http.StatusRequestEntityTooLarge) + return + } + w.Header().Set("content-type", "application/json") + + // create a codec that reads direct from the request body until + // EOF and writes the response to w and order the server to process + // a single request. + codec := NewJSONCodec(&httpReadWriteNopCloser{r.Body, w}) + defer codec.Close() + srv.ServeSingleRequest(codec, OptionMethodInvocation) +} + +func newCorsHandler(srv *Server, corsString string) http.Handler { + var allowedOrigins []string + for _, domain := range strings.Split(corsString, ",") { + allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) + } + c := cors.New(cors.Options{ + AllowedOrigins: allowedOrigins, + AllowedMethods: []string{"POST", "GET"}, + MaxAge: 600, + }) + return c.Handler(srv) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/inproc.go b/vendor/github.com/ethereum/go-ethereum/rpc/inproc.go new file mode 100644 index 0000000..f72b974 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/inproc.go @@ -0,0 +1,34 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "net" + + "golang.org/x/net/context" +) + +// NewInProcClient attaches an in-process connection to the given RPC server. +func DialInProc(handler *Server) *Client { + initctx := context.Background() + c, _ := newClient(initctx, func(context.Context) (net.Conn, error) { + p1, p2 := net.Pipe() + go handler.ServeCodec(NewJSONCodec(p1), OptionMethodInvocation|OptionSubscriptions) + return p2, nil + }) + return c +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/ipc.go b/vendor/github.com/ethereum/go-ethereum/rpc/ipc.go new file mode 100644 index 0000000..c2b9e38 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/ipc.go @@ -0,0 +1,55 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "net" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on +// Windows this is a named pipe +func CreateIPCListener(endpoint string) (net.Listener, error) { + return ipcListen(endpoint) +} + +// ServeListener accepts connections on l, serving JSON-RPC on them. +func (srv *Server) ServeListener(l net.Listener) error { + for { + conn, err := l.Accept() + if err != nil { + return err + } + glog.V(logger.Detail).Infoln("accepted conn", conn.RemoteAddr()) + go srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions) + } +} + +// DialIPC create a new IPC client that connects to the given endpoint. On Unix it assumes +// the endpoint is the full path to a unix socket, and Windows the endpoint is an +// identifier for a named pipe. +// +// The context is used for the initial connection establishment. It does not +// affect subsequent interactions with the client. +func DialIPC(ctx context.Context, endpoint string) (*Client, error) { + return newClient(ctx, func(ctx context.Context) (net.Conn, error) { + return newIPCConnection(ctx, endpoint) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/ipc_unix.go b/vendor/github.com/ethereum/go-ethereum/rpc/ipc_unix.go new file mode 100644 index 0000000..a25b216 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/ipc_unix.go @@ -0,0 +1,47 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package rpc + +import ( + "net" + "os" + "path/filepath" + + "golang.org/x/net/context" +) + +// ipcListen will create a Unix socket on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + // Ensure the IPC path exists and remove any previous leftover + if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil { + return nil, err + } + os.Remove(endpoint) + l, err := net.Listen("unix", endpoint) + if err != nil { + return nil, err + } + os.Chmod(endpoint, 0600) + return l, nil +} + +// newIPCConnection will connect to a Unix socket on the given endpoint. +func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { + return dialContext(ctx, "unix", endpoint) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/ipc_windows.go b/vendor/github.com/ethereum/go-ethereum/rpc/ipc_windows.go new file mode 100644 index 0000000..68234d2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/ipc_windows.go @@ -0,0 +1,48 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build windows + +package rpc + +import ( + "net" + "time" + + "golang.org/x/net/context" + "gopkg.in/natefinch/npipe.v2" +) + +// This is used if the dialing context has no deadline. It is much smaller than the +// defaultDialTimeout because named pipes are local and there is no need to wait so long. +const defaultPipeDialTimeout = 2 * time.Second + +// ipcListen will create a named pipe on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + return npipe.Listen(endpoint) +} + +// newIPCConnection will connect to a named pipe with the given endpoint as name. +func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { + timeout := defaultPipeDialTimeout + if deadline, ok := ctx.Deadline(); ok { + timeout = deadline.Sub(time.Now()) + if timeout < 0 { + timeout = 0 + } + } + return npipe.DialTimeout(endpoint, timeout) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/json.go b/vendor/github.com/ethereum/go-ethereum/rpc/json.go new file mode 100644 index 0000000..61a4ddf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/json.go @@ -0,0 +1,358 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "reflect" + "strconv" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + jsonrpcVersion = "2.0" + serviceMethodSeparator = "_" + subscribeMethod = "eth_subscribe" + unsubscribeMethod = "eth_unsubscribe" + notificationMethod = "eth_subscription" +) + +type jsonRequest struct { + Method string `json:"method"` + Version string `json:"jsonrpc"` + Id json.RawMessage `json:"id,omitempty"` + Payload json.RawMessage `json:"params,omitempty"` +} + +type jsonSuccessResponse struct { + Version string `json:"jsonrpc"` + Id interface{} `json:"id,omitempty"` + Result interface{} `json:"result"` +} + +type jsonError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +type jsonErrResponse struct { + Version string `json:"jsonrpc"` + Id interface{} `json:"id,omitempty"` + Error jsonError `json:"error"` +} + +type jsonSubscription struct { + Subscription string `json:"subscription"` + Result interface{} `json:"result,omitempty"` +} + +type jsonNotification struct { + Version string `json:"jsonrpc"` + Method string `json:"method"` + Params jsonSubscription `json:"params"` +} + +// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It +// also has support for parsing arguments and serializing (result) objects. +type jsonCodec struct { + closer sync.Once // close closed channel once + closed chan interface{} // closed on Close + decMu sync.Mutex // guards d + d *json.Decoder // decodes incoming requests + encMu sync.Mutex // guards e + e *json.Encoder // encodes responses + rw io.ReadWriteCloser // connection +} + +func (err *jsonError) Error() string { + if err.Message == "" { + return fmt.Sprintf("json-rpc error %d", err.Code) + } + return err.Message +} + +func (err *jsonError) ErrorCode() int { + return err.Code +} + +// NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0 +func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec { + d := json.NewDecoder(rwc) + d.UseNumber() + return &jsonCodec{closed: make(chan interface{}), d: d, e: json.NewEncoder(rwc), rw: rwc} +} + +// isBatch returns true when the first non-whitespace characters is '[' +func isBatch(msg json.RawMessage) bool { + for _, c := range msg { + // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) + if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { + continue + } + return c == '[' + } + return false +} + +// ReadRequestHeaders will read new requests without parsing the arguments. It will +// return a collection of requests, an indication if these requests are in batch +// form or an error when the incoming message could not be read/parsed. +func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, Error) { + c.decMu.Lock() + defer c.decMu.Unlock() + + var incomingMsg json.RawMessage + if err := c.d.Decode(&incomingMsg); err != nil { + return nil, false, &invalidRequestError{err.Error()} + } + + if isBatch(incomingMsg) { + return parseBatchRequest(incomingMsg) + } + + return parseRequest(incomingMsg) +} + +// checkReqId returns an error when the given reqId isn't valid for RPC method calls. +// valid id's are strings, numbers or null +func checkReqId(reqId json.RawMessage) error { + if len(reqId) == 0 { + return fmt.Errorf("missing request id") + } + if _, err := strconv.ParseFloat(string(reqId), 64); err == nil { + return nil + } + var str string + if err := json.Unmarshal(reqId, &str); err == nil { + return nil + } + return fmt.Errorf("invalid request id") +} + +// parseRequest will parse a single request from the given RawMessage. It will return +// the parsed request, an indication if the request was a batch or an error when +// the request could not be parsed. +func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { + var in jsonRequest + if err := json.Unmarshal(incomingMsg, &in); err != nil { + return nil, false, &invalidMessageError{err.Error()} + } + + if err := checkReqId(in.Id); err != nil { + return nil, false, &invalidMessageError{err.Error()} + } + + // subscribe are special, they will always use `subscribeMethod` as first param in the payload + if in.Method == subscribeMethod { + reqs := []rpcRequest{{id: &in.Id, isPubSub: true}} + if len(in.Payload) > 0 { + // first param must be subscription name + var subscribeMethod [1]string + if err := json.Unmarshal(in.Payload, &subscribeMethod); err != nil { + glog.V(logger.Debug).Infof("Unable to parse subscription method: %v\n", err) + return nil, false, &invalidRequestError{"Unable to parse subscription request"} + } + + // all subscriptions are made on the eth service + reqs[0].service, reqs[0].method = "eth", subscribeMethod[0] + reqs[0].params = in.Payload + return reqs, false, nil + } + return nil, false, &invalidRequestError{"Unable to parse subscription request"} + } + + if in.Method == unsubscribeMethod { + return []rpcRequest{{id: &in.Id, isPubSub: true, + method: unsubscribeMethod, params: in.Payload}}, false, nil + } + + elems := strings.Split(in.Method, serviceMethodSeparator) + if len(elems) != 2 { + return nil, false, &methodNotFoundError{in.Method, ""} + } + + // regular RPC call + if len(in.Payload) == 0 { + return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id}}, false, nil + } + + return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id, params: in.Payload}}, false, nil +} + +// parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication +// if the request was a batch or an error when the request could not be read. +func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { + var in []jsonRequest + if err := json.Unmarshal(incomingMsg, &in); err != nil { + return nil, false, &invalidMessageError{err.Error()} + } + + requests := make([]rpcRequest, len(in)) + for i, r := range in { + if err := checkReqId(r.Id); err != nil { + return nil, false, &invalidMessageError{err.Error()} + } + + id := &in[i].Id + + // subscribe are special, they will always use `subscribeMethod` as first param in the payload + if r.Method == subscribeMethod { + requests[i] = rpcRequest{id: id, isPubSub: true} + if len(r.Payload) > 0 { + // first param must be subscription name + var subscribeMethod [1]string + if err := json.Unmarshal(r.Payload, &subscribeMethod); err != nil { + glog.V(logger.Debug).Infof("Unable to parse subscription method: %v\n", err) + return nil, false, &invalidRequestError{"Unable to parse subscription request"} + } + + // all subscriptions are made on the eth service + requests[i].service, requests[i].method = "eth", subscribeMethod[0] + requests[i].params = r.Payload + continue + } + + return nil, true, &invalidRequestError{"Unable to parse (un)subscribe request arguments"} + } + + if r.Method == unsubscribeMethod { + requests[i] = rpcRequest{id: id, isPubSub: true, method: unsubscribeMethod, params: r.Payload} + continue + } + + if len(r.Payload) == 0 { + requests[i] = rpcRequest{id: id, params: nil} + } else { + requests[i] = rpcRequest{id: id, params: r.Payload} + } + if elem := strings.Split(r.Method, serviceMethodSeparator); len(elem) == 2 { + requests[i].service, requests[i].method = elem[0], elem[1] + } else { + requests[i].err = &methodNotFoundError{r.Method, ""} + } + } + + return requests, true, nil +} + +// ParseRequestArguments tries to parse the given params (json.RawMessage) with the given +// types. It returns the parsed values or an error when the parsing failed. +func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error) { + if args, ok := params.(json.RawMessage); !ok { + return nil, &invalidParamsError{"Invalid params supplied"} + } else { + return parsePositionalArguments(args, argTypes) + } +} + +// parsePositionalArguments tries to parse the given args to an array of values with the +// given types. It returns the parsed values or an error when the args could not be +// parsed. Missing optional arguments are returned as reflect.Zero values. +func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, Error) { + // Read beginning of the args array. + dec := json.NewDecoder(bytes.NewReader(rawArgs)) + if tok, _ := dec.Token(); tok != json.Delim('[') { + return nil, &invalidParamsError{"non-array args"} + } + // Read args. + args := make([]reflect.Value, 0, len(types)) + for i := 0; dec.More(); i++ { + if i >= len(types) { + return nil, &invalidParamsError{fmt.Sprintf("too many arguments, want at most %d", len(types))} + } + argval := reflect.New(types[i]) + if err := dec.Decode(argval.Interface()); err != nil { + return nil, &invalidParamsError{fmt.Sprintf("invalid argument %d: %v", i, err)} + } + if argval.IsNil() && types[i].Kind() != reflect.Ptr { + return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} + } + args = append(args, argval.Elem()) + } + // Read end of args array. + if _, err := dec.Token(); err != nil { + return nil, &invalidParamsError{err.Error()} + } + // Set any missing args to nil. + for i := len(args); i < len(types); i++ { + if types[i].Kind() != reflect.Ptr { + return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} + } + args = append(args, reflect.Zero(types[i])) + } + return args, nil +} + +// CreateResponse will create a JSON-RPC success response with the given id and reply as result. +func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} { + if isHexNum(reflect.TypeOf(reply)) { + return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} + } + return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: reply} +} + +// CreateErrorResponse will create a JSON-RPC error response with the given id and error. +func (c *jsonCodec) CreateErrorResponse(id interface{}, err Error) interface{} { + return &jsonErrResponse{Version: jsonrpcVersion, Id: id, Error: jsonError{Code: err.ErrorCode(), Message: err.Error()}} +} + +// CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error. +// info is optional and contains additional information about the error. When an empty string is passed it is ignored. +func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{} { + return &jsonErrResponse{Version: jsonrpcVersion, Id: id, + Error: jsonError{Code: err.ErrorCode(), Message: err.Error(), Data: info}} +} + +// CreateNotification will create a JSON-RPC notification with the given subscription id and event as params. +func (c *jsonCodec) CreateNotification(subid string, event interface{}) interface{} { + if isHexNum(reflect.TypeOf(event)) { + return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod, + Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}} + } + + return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod, + Params: jsonSubscription{Subscription: subid, Result: event}} +} + +// Write message to client +func (c *jsonCodec) Write(res interface{}) error { + c.encMu.Lock() + defer c.encMu.Unlock() + + return c.e.Encode(res) +} + +// Close the underlying connection +func (c *jsonCodec) Close() { + c.closer.Do(func() { + close(c.closed) + c.rw.Close() + }) +} + +// Closed returns a channel which will be closed when Close is called +func (c *jsonCodec) Closed() <-chan interface{} { + return c.closed +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/json_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/json_test.go new file mode 100644 index 0000000..5048d2f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/json_test.go @@ -0,0 +1,178 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bufio" + "bytes" + "encoding/json" + "reflect" + "strconv" + "testing" +) + +type RWC struct { + *bufio.ReadWriter +} + +func (rwc *RWC) Close() error { + return nil +} + +func TestJSONRequestParsing(t *testing.T) { + server := NewServer() + service := new(Service) + + if err := server.RegisterName("calc", service); err != nil { + t.Fatalf("%v", err) + } + + req := bytes.NewBufferString(`{"id": 1234, "jsonrpc": "2.0", "method": "calc_add", "params": [11, 22]}`) + var str string + reply := bytes.NewBufferString(str) + rw := &RWC{bufio.NewReadWriter(bufio.NewReader(req), bufio.NewWriter(reply))} + + codec := NewJSONCodec(rw) + + requests, batch, err := codec.ReadRequestHeaders() + if err != nil { + t.Fatalf("%v", err) + } + + if batch { + t.Fatalf("Request isn't a batch") + } + + if len(requests) != 1 { + t.Fatalf("Expected 1 request but got %d requests - %v", len(requests), requests) + } + + if requests[0].service != "calc" { + t.Fatalf("Expected service 'calc' but got '%s'", requests[0].service) + } + + if requests[0].method != "add" { + t.Fatalf("Expected method 'Add' but got '%s'", requests[0].method) + } + + if rawId, ok := requests[0].id.(*json.RawMessage); ok { + id, e := strconv.ParseInt(string(*rawId), 0, 64) + if e != nil { + t.Fatalf("%v", e) + } + if id != 1234 { + t.Fatalf("Expected id 1234 but got %d", id) + } + } else { + t.Fatalf("invalid request, expected *json.RawMesage got %T", requests[0].id) + } + + var arg int + args := []reflect.Type{reflect.TypeOf(arg), reflect.TypeOf(arg)} + + v, err := codec.ParseRequestArguments(args, requests[0].params) + if err != nil { + t.Fatalf("%v", err) + } + + if len(v) != 2 { + t.Fatalf("Expected 2 argument values, got %d", len(v)) + } + + if v[0].Int() != 11 || v[1].Int() != 22 { + t.Fatalf("expected %d == 11 && %d == 22", v[0].Int(), v[1].Int()) + } +} + +func TestJSONRequestParamsParsing(t *testing.T) { + + var ( + stringT = reflect.TypeOf("") + intT = reflect.TypeOf(0) + intPtrT = reflect.TypeOf(new(int)) + + stringV = reflect.ValueOf("abc") + i = 1 + intV = reflect.ValueOf(i) + intPtrV = reflect.ValueOf(&i) + ) + + var validTests = []struct { + input string + argTypes []reflect.Type + expected []reflect.Value + }{ + {`[]`, []reflect.Type{}, []reflect.Value{}}, + {`[]`, []reflect.Type{intPtrT}, []reflect.Value{intPtrV}}, + {`[1]`, []reflect.Type{intT}, []reflect.Value{intV}}, + {`[1,"abc"]`, []reflect.Type{intT, stringT}, []reflect.Value{intV, stringV}}, + {`[null]`, []reflect.Type{intPtrT}, []reflect.Value{intPtrV}}, + {`[null,"abc"]`, []reflect.Type{intPtrT, stringT, intPtrT}, []reflect.Value{intPtrV, stringV, intPtrV}}, + {`[null,"abc",null]`, []reflect.Type{intPtrT, stringT, intPtrT}, []reflect.Value{intPtrV, stringV, intPtrV}}, + } + + codec := jsonCodec{} + + for _, test := range validTests { + params := (json.RawMessage)([]byte(test.input)) + args, err := codec.ParseRequestArguments(test.argTypes, params) + + if err != nil { + t.Fatal(err) + } + + var match []interface{} + json.Unmarshal([]byte(test.input), &match) + + if len(args) != len(test.argTypes) { + t.Fatalf("expected %d parsed args, got %d", len(test.argTypes), len(args)) + } + + for i, arg := range args { + expected := test.expected[i] + + if arg.Kind() != expected.Kind() { + t.Errorf("expected type for param %d in %s", i, test.input) + } + + if arg.Kind() == reflect.Int && arg.Int() != expected.Int() { + t.Errorf("expected int(%d), got int(%d) in %s", expected.Int(), arg.Int(), test.input) + } + + if arg.Kind() == reflect.String && arg.String() != expected.String() { + t.Errorf("expected string(%s), got string(%s) in %s", expected.String(), arg.String(), test.input) + } + } + } + + var invalidTests = []struct { + input string + argTypes []reflect.Type + }{ + {`[]`, []reflect.Type{intT}}, + {`[null]`, []reflect.Type{intT}}, + {`[1]`, []reflect.Type{stringT}}, + {`[1,2]`, []reflect.Type{stringT}}, + {`["abc", null]`, []reflect.Type{stringT, intT}}, + } + + for i, test := range invalidTests { + if _, err := codec.ParseRequestArguments(test.argTypes, test.input); err == nil { + t.Errorf("expected test %d - %s to fail", i, test.input) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/server.go b/vendor/github.com/ethereum/go-ethereum/rpc/server.go new file mode 100644 index 0000000..996c637 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/server.go @@ -0,0 +1,452 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "fmt" + "reflect" + "runtime" + "sync/atomic" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" + "gopkg.in/fatih/set.v0" +) + +const ( + notificationBufferSize = 10000 // max buffered notifications before codec is closed + + MetadataApi = "rpc" + DefaultIPCApis = "admin,debug,eth,miner,net,personal,shh,txpool,web3" + DefaultHTTPApis = "eth,net,web3" +) + +// CodecOption specifies which type of messages this codec supports +type CodecOption int + +const ( + // OptionMethodInvocation is an indication that the codec supports RPC method calls + OptionMethodInvocation CodecOption = 1 << iota + + // OptionSubscriptions is an indication that the codec suports RPC notifications + OptionSubscriptions = 1 << iota // support pub sub +) + +// NewServer will create a new server instance with no registered handlers. +func NewServer() *Server { + server := &Server{ + services: make(serviceRegistry), + subscriptions: make(subscriptionRegistry), + codecs: set.New(), + run: 1, + } + + // register a default service which will provide meta information about the RPC service such as the services and + // methods it offers. + rpcService := &RPCService{server} + server.RegisterName(MetadataApi, rpcService) + + return server +} + +// RPCService gives meta information about the server. +// e.g. gives information about the loaded modules. +type RPCService struct { + server *Server +} + +// Modules returns the list of RPC services with their version number +func (s *RPCService) Modules() map[string]string { + modules := make(map[string]string) + for name := range s.server.services { + modules[name] = "1.0" + } + return modules +} + +// RegisterName will create a service for the given rcvr type under the given name. When no methods on the given rcvr +// match the criteria to be either a RPC method or a subscription an error is returned. Otherwise a new service is +// created and added to the service collection this server instance serves. +func (s *Server) RegisterName(name string, rcvr interface{}) error { + if s.services == nil { + s.services = make(serviceRegistry) + } + + svc := new(service) + svc.typ = reflect.TypeOf(rcvr) + rcvrVal := reflect.ValueOf(rcvr) + + if name == "" { + return fmt.Errorf("no service name for type %s", svc.typ.String()) + } + if !isExported(reflect.Indirect(rcvrVal).Type().Name()) { + return fmt.Errorf("%s is not exported", reflect.Indirect(rcvrVal).Type().Name()) + } + + // already a previous service register under given sname, merge methods/subscriptions + if regsvc, present := s.services[name]; present { + methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ) + if len(methods) == 0 && len(subscriptions) == 0 { + return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr) + } + + for _, m := range methods { + regsvc.callbacks[formatName(m.method.Name)] = m + } + for _, s := range subscriptions { + regsvc.subscriptions[formatName(s.method.Name)] = s + } + + return nil + } + + svc.name = name + svc.callbacks, svc.subscriptions = suitableCallbacks(rcvrVal, svc.typ) + + if len(svc.callbacks) == 0 && len(svc.subscriptions) == 0 { + return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr) + } + + s.services[svc.name] = svc + + return nil +} + +// hasOption returns true if option is included in options, otherwise false +func hasOption(option CodecOption, options []CodecOption) bool { + for _, o := range options { + if option == o { + return true + } + } + return false +} + +// serveRequest will reads requests from the codec, calls the RPC callback and +// writes the response to the given codec. +// +// If singleShot is true it will process a single request, otherwise it will handle +// requests until the codec returns an error when reading a request (in most cases +// an EOF). It executes requests in parallel when singleShot is false. +func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error { + defer func() { + if err := recover(); err != nil { + const size = 64 << 10 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + glog.Errorln(string(buf)) + } + + s.codecsMu.Lock() + s.codecs.Remove(codec) + s.codecsMu.Unlock() + + return + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // if the codec supports notification include a notifier that callbacks can use + // to send notification to clients. It is thight to the codec/connection. If the + // connection is closed the notifier will stop and cancels all active subscriptions. + if options&OptionSubscriptions == OptionSubscriptions { + ctx = context.WithValue(ctx, notifierKey{}, newNotifier(codec)) + } + s.codecsMu.Lock() + if atomic.LoadInt32(&s.run) != 1 { // server stopped + s.codecsMu.Unlock() + return &shutdownError{} + } + s.codecs.Add(codec) + s.codecsMu.Unlock() + + // test if the server is ordered to stop + for atomic.LoadInt32(&s.run) == 1 { + reqs, batch, err := s.readRequest(codec) + if err != nil { + glog.V(logger.Debug).Infof("read error %v\n", err) + codec.Write(codec.CreateErrorResponse(nil, err)) + return nil + } + + // check if server is ordered to shutdown and return an error + // telling the client that his request failed. + if atomic.LoadInt32(&s.run) != 1 { + err = &shutdownError{} + if batch { + resps := make([]interface{}, len(reqs)) + for i, r := range reqs { + resps[i] = codec.CreateErrorResponse(&r.id, err) + } + codec.Write(resps) + } else { + codec.Write(codec.CreateErrorResponse(&reqs[0].id, err)) + } + return nil + } + + if singleShot && batch { + s.execBatch(ctx, codec, reqs) + return nil + } else if singleShot && !batch { + s.exec(ctx, codec, reqs[0]) + return nil + } else if !singleShot && batch { + go s.execBatch(ctx, codec, reqs) + } else { + go s.exec(ctx, codec, reqs[0]) + } + } + + return nil +} + +// ServeCodec reads incoming requests from codec, calls the appropriate callback and writes the +// response back using the given codec. It will block until the codec is closed or the server is +// stopped. In either case the codec is closed. +func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { + defer codec.Close() + s.serveRequest(codec, false, options) +} + +// ServeSingleRequest reads and processes a single RPC request from the given codec. It will not +// close the codec unless a non-recoverable error has occurred. Note, this method will return after +// a single request has been processed! +func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption) { + s.serveRequest(codec, true, options) +} + +// Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish, +// close all codecs which will cancel pending requests/subscriptions. +func (s *Server) Stop() { + if atomic.CompareAndSwapInt32(&s.run, 1, 0) { + glog.V(logger.Debug).Infoln("RPC Server shutdown initiatied") + s.codecsMu.Lock() + defer s.codecsMu.Unlock() + s.codecs.Each(func(c interface{}) bool { + c.(ServerCodec).Close() + return true + }) + } +} + +// createSubscription will call the subscription callback and returns the subscription id or error. +func (s *Server) createSubscription(ctx context.Context, c ServerCodec, req *serverRequest) (ID, error) { + // subscription have as first argument the context following optional arguments + args := []reflect.Value{req.callb.rcvr, reflect.ValueOf(ctx)} + args = append(args, req.args...) + reply := req.callb.method.Func.Call(args) + + if !reply[1].IsNil() { // subscription creation failed + return "", reply[1].Interface().(error) + } + + return reply[0].Interface().(*Subscription).ID, nil +} + +// handle executes a request and returns the response from the callback. +func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverRequest) (interface{}, func()) { + if req.err != nil { + return codec.CreateErrorResponse(&req.id, req.err), nil + } + + if req.isUnsubscribe { // cancel subscription, first param must be the subscription id + if len(req.args) >= 1 && req.args[0].Kind() == reflect.String { + notifier, supported := NotifierFromContext(ctx) + if !supported { // interface doesn't support subscriptions (e.g. http) + return codec.CreateErrorResponse(&req.id, &callbackError{ErrNotificationsUnsupported.Error()}), nil + } + + subid := ID(req.args[0].String()) + if err := notifier.unsubscribe(subid); err != nil { + return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil + } + + return codec.CreateResponse(req.id, true), nil + } + return codec.CreateErrorResponse(&req.id, &invalidParamsError{"Expected subscription id as first argument"}), nil + } + + if req.callb.isSubscribe { + subid, err := s.createSubscription(ctx, codec, req) + if err != nil { + return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil + } + + // active the subscription after the sub id was successfully sent to the client + activateSub := func() { + notifier, _ := NotifierFromContext(ctx) + notifier.activate(subid) + } + + return codec.CreateResponse(req.id, subid), activateSub + } + + // regular RPC call, prepare arguments + if len(req.args) != len(req.callb.argTypes) { + rpcErr := &invalidParamsError{fmt.Sprintf("%s%s%s expects %d parameters, got %d", + req.svcname, serviceMethodSeparator, req.callb.method.Name, + len(req.callb.argTypes), len(req.args))} + return codec.CreateErrorResponse(&req.id, rpcErr), nil + } + + arguments := []reflect.Value{req.callb.rcvr} + if req.callb.hasCtx { + arguments = append(arguments, reflect.ValueOf(ctx)) + } + if len(req.args) > 0 { + arguments = append(arguments, req.args...) + } + + // execute RPC method and return result + reply := req.callb.method.Func.Call(arguments) + if len(reply) == 0 { + return codec.CreateResponse(req.id, nil), nil + } + + if req.callb.errPos >= 0 { // test if method returned an error + if !reply[req.callb.errPos].IsNil() { + e := reply[req.callb.errPos].Interface().(error) + res := codec.CreateErrorResponse(&req.id, &callbackError{e.Error()}) + return res, nil + } + } + return codec.CreateResponse(req.id, reply[0].Interface()), nil +} + +// exec executes the given request and writes the result back using the codec. +func (s *Server) exec(ctx context.Context, codec ServerCodec, req *serverRequest) { + var response interface{} + var callback func() + if req.err != nil { + response = codec.CreateErrorResponse(&req.id, req.err) + } else { + response, callback = s.handle(ctx, codec, req) + } + + if err := codec.Write(response); err != nil { + glog.V(logger.Error).Infof("%v\n", err) + codec.Close() + } + + // when request was a subscribe request this allows these subscriptions to be actived + if callback != nil { + callback() + } +} + +// execBatch executes the given requests and writes the result back using the codec. +// It will only write the response back when the last request is processed. +func (s *Server) execBatch(ctx context.Context, codec ServerCodec, requests []*serverRequest) { + responses := make([]interface{}, len(requests)) + var callbacks []func() + for i, req := range requests { + if req.err != nil { + responses[i] = codec.CreateErrorResponse(&req.id, req.err) + } else { + var callback func() + if responses[i], callback = s.handle(ctx, codec, req); callback != nil { + callbacks = append(callbacks, callback) + } + } + } + + if err := codec.Write(responses); err != nil { + glog.V(logger.Error).Infof("%v\n", err) + codec.Close() + } + + // when request holds one of more subscribe requests this allows these subscriptions to be actived + for _, c := range callbacks { + c() + } +} + +// readRequest requests the next (batch) request from the codec. It will return the collection +// of requests, an indication if the request was a batch, the invalid request identifier and an +// error when the request could not be read/parsed. +func (s *Server) readRequest(codec ServerCodec) ([]*serverRequest, bool, Error) { + reqs, batch, err := codec.ReadRequestHeaders() + if err != nil { + return nil, batch, err + } + + requests := make([]*serverRequest, len(reqs)) + + // verify requests + for i, r := range reqs { + var ok bool + var svc *service + + if r.err != nil { + requests[i] = &serverRequest{id: r.id, err: r.err} + continue + } + + if r.isPubSub && r.method == unsubscribeMethod { + requests[i] = &serverRequest{id: r.id, isUnsubscribe: true} + argTypes := []reflect.Type{reflect.TypeOf("")} // expect subscription id as first arg + if args, err := codec.ParseRequestArguments(argTypes, r.params); err == nil { + requests[i].args = args + } else { + requests[i].err = &invalidParamsError{err.Error()} + } + continue + } + + if svc, ok = s.services[r.service]; !ok { // rpc method isn't available + requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{r.service, r.method}} + continue + } + + if r.isPubSub { // eth_subscribe, r.method contains the subscription method name + if callb, ok := svc.subscriptions[r.method]; ok { + requests[i] = &serverRequest{id: r.id, svcname: svc.name, callb: callb} + if r.params != nil && len(callb.argTypes) > 0 { + argTypes := []reflect.Type{reflect.TypeOf("")} + argTypes = append(argTypes, callb.argTypes...) + if args, err := codec.ParseRequestArguments(argTypes, r.params); err == nil { + requests[i].args = args[1:] // first one is service.method name which isn't an actual argument + } else { + requests[i].err = &invalidParamsError{err.Error()} + } + } + } else { + requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{subscribeMethod, r.method}} + } + continue + } + + if callb, ok := svc.callbacks[r.method]; ok { // lookup RPC method + requests[i] = &serverRequest{id: r.id, svcname: svc.name, callb: callb} + if r.params != nil && len(callb.argTypes) > 0 { + if args, err := codec.ParseRequestArguments(callb.argTypes, r.params); err == nil { + requests[i].args = args + } else { + requests[i].err = &invalidParamsError{err.Error()} + } + } + continue + } + + requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{r.service, r.method}} + } + + return requests, batch, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/server_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/server_test.go new file mode 100644 index 0000000..c3c88fa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/server_test.go @@ -0,0 +1,163 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "encoding/json" + "net" + "reflect" + "testing" + "time" + + "golang.org/x/net/context" +) + +type Service struct{} + +type Args struct { + S string +} + +func (s *Service) NoArgsRets() { +} + +type Result struct { + String string + Int int + Args *Args +} + +func (s *Service) Echo(str string, i int, args *Args) Result { + return Result{str, i, args} +} + +func (s *Service) EchoWithCtx(ctx context.Context, str string, i int, args *Args) Result { + return Result{str, i, args} +} + +func (s *Service) Sleep(ctx context.Context, duration time.Duration) { + select { + case <-time.After(duration): + case <-ctx.Done(): + } +} + +func (s *Service) Rets() (string, error) { + return "", nil +} + +func (s *Service) InvalidRets1() (error, string) { + return nil, "" +} + +func (s *Service) InvalidRets2() (string, string) { + return "", "" +} + +func (s *Service) InvalidRets3() (string, string, error) { + return "", "", nil +} + +func (s *Service) Subscription(ctx context.Context) (*Subscription, error) { + return nil, nil +} + +func TestServerRegisterName(t *testing.T) { + server := NewServer() + service := new(Service) + + if err := server.RegisterName("calc", service); err != nil { + t.Fatalf("%v", err) + } + + if len(server.services) != 2 { + t.Fatalf("Expected 2 service entries, got %d", len(server.services)) + } + + svc, ok := server.services["calc"] + if !ok { + t.Fatalf("Expected service calc to be registered") + } + + if len(svc.callbacks) != 5 { + t.Errorf("Expected 5 callbacks for service 'calc', got %d", len(svc.callbacks)) + } + + if len(svc.subscriptions) != 1 { + t.Errorf("Expected 1 subscription for service 'calc', got %d", len(svc.subscriptions)) + } +} + +func testServerMethodExecution(t *testing.T, method string) { + server := NewServer() + service := new(Service) + + if err := server.RegisterName("test", service); err != nil { + t.Fatalf("%v", err) + } + + stringArg := "string arg" + intArg := 1122 + argsArg := &Args{"abcde"} + params := []interface{}{stringArg, intArg, argsArg} + + request := map[string]interface{}{ + "id": 12345, + "method": "test_" + method, + "version": "2.0", + "params": params, + } + + clientConn, serverConn := net.Pipe() + defer clientConn.Close() + + go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation) + + out := json.NewEncoder(clientConn) + in := json.NewDecoder(clientConn) + + if err := out.Encode(request); err != nil { + t.Fatal(err) + } + + response := jsonSuccessResponse{Result: &Result{}} + if err := in.Decode(&response); err != nil { + t.Fatal(err) + } + + if result, ok := response.Result.(*Result); ok { + if result.String != stringArg { + t.Errorf("expected %s, got : %s\n", stringArg, result.String) + } + if result.Int != intArg { + t.Errorf("expected %d, got %d\n", intArg, result.Int) + } + if !reflect.DeepEqual(result.Args, argsArg) { + t.Errorf("expected %v, got %v\n", argsArg, result) + } + } else { + t.Fatalf("invalid response: expected *Result - got: %T", response.Result) + } +} + +func TestServerMethodExecution(t *testing.T) { + testServerMethodExecution(t, "echo") +} + +func TestServerMethodWithCtx(t *testing.T) { + testServerMethodExecution(t, "echoWithCtx") +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/subscription.go b/vendor/github.com/ethereum/go-ethereum/rpc/subscription.go new file mode 100644 index 0000000..bcdc3cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/subscription.go @@ -0,0 +1,135 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "errors" + "sync" + + "golang.org/x/net/context" +) + +var ( + // ErrNotificationsUnsupported is returned when the connection doesn't support notifications + ErrNotificationsUnsupported = errors.New("notifications not supported") + // ErrNotificationNotFound is returned when the notification for the given id is not found + ErrSubscriptionNotFound = errors.New("subscription not found") +) + +// ID defines a pseudo random number that is used to identify RPC subscriptions. +type ID string + +// a Subscription is created by a notifier and tight to that notifier. The client can use +// this subscription to wait for an unsubscribe request for the client, see Err(). +type Subscription struct { + ID ID + err chan error // closed on unsubscribe +} + +// Err returns a channel that is closed when the client send an unsubscribe request. +func (s *Subscription) Err() <-chan error { + return s.err +} + +// notifierKey is used to store a notifier within the connection context. +type notifierKey struct{} + +// Notifier is tight to a RPC connection that supports subscriptions. +// Server callbacks use the notifier to send notifications. +type Notifier struct { + codec ServerCodec + subMu sync.RWMutex // guards active and inactive maps + stopped bool + active map[ID]*Subscription + inactive map[ID]*Subscription +} + +// newNotifier creates a new notifier that can be used to send subscription +// notifications to the client. +func newNotifier(codec ServerCodec) *Notifier { + return &Notifier{ + codec: codec, + active: make(map[ID]*Subscription), + inactive: make(map[ID]*Subscription), + } +} + +// NotifierFromContext returns the Notifier value stored in ctx, if any. +func NotifierFromContext(ctx context.Context) (*Notifier, bool) { + n, ok := ctx.Value(notifierKey{}).(*Notifier) + return n, ok +} + +// CreateSubscription returns a new subscription that is coupled to the +// RPC connection. By default subscriptions are inactive and notifications +// are dropped until the subscription is marked as active. This is done +// by the RPC server after the subscription ID is send to the client. +func (n *Notifier) CreateSubscription() *Subscription { + s := &Subscription{NewID(), make(chan error)} + n.subMu.Lock() + n.inactive[s.ID] = s + n.subMu.Unlock() + return s +} + +// Notify sends a notification to the client with the given data as payload. +// If an error occurs the RPC connection is closed and the error is returned. +func (n *Notifier) Notify(id ID, data interface{}) error { + n.subMu.RLock() + defer n.subMu.RUnlock() + + _, active := n.active[id] + if active { + notification := n.codec.CreateNotification(string(id), data) + if err := n.codec.Write(notification); err != nil { + n.codec.Close() + return err + } + } + return nil +} + +// Closed returns a channel that is closed when the RPC connection is closed. +func (n *Notifier) Closed() <-chan interface{} { + return n.codec.Closed() +} + +// unsubscribe a subscription. +// If the subscription could not be found ErrSubscriptionNotFound is returned. +func (n *Notifier) unsubscribe(id ID) error { + n.subMu.Lock() + defer n.subMu.Unlock() + if s, found := n.active[id]; found { + close(s.err) + delete(n.active, id) + return nil + } + return ErrSubscriptionNotFound +} + +// activate enables a subscription. Until a subscription is enabled all +// notifications are dropped. This method is called by the RPC server after +// the subscription ID was sent to client. This prevents notifications being +// send to the client before the subscription ID is send to the client. +func (n *Notifier) activate(id ID) { + n.subMu.Lock() + defer n.subMu.Unlock() + if sub, found := n.inactive[id]; found { + n.active[id] = sub + delete(n.inactive, id) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/subscription_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/subscription_test.go new file mode 100644 index 0000000..00c4e0e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/subscription_test.go @@ -0,0 +1,165 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "encoding/json" + "net" + "sync" + "testing" + "time" + + "golang.org/x/net/context" +) + +type NotificationTestService struct { + mu sync.Mutex + unsubscribed bool + + gotHangSubscriptionReq chan struct{} + unblockHangSubscription chan struct{} +} + +func (s *NotificationTestService) Echo(i int) int { + return i +} + +func (s *NotificationTestService) wasUnsubCallbackCalled() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.unsubscribed +} + +func (s *NotificationTestService) Unsubscribe(subid string) { + s.mu.Lock() + s.unsubscribed = true + s.mu.Unlock() +} + +func (s *NotificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { + notifier, supported := NotifierFromContext(ctx) + if !supported { + return nil, ErrNotificationsUnsupported + } + + // by explicitly creating an subscription we make sure that the subscription id is send back to the client + // before the first subscription.Notify is called. Otherwise the events might be send before the response + // for the eth_subscribe method. + subscription := notifier.CreateSubscription() + + go func() { + // test expects n events, if we begin sending event immediately some events + // will probably be dropped since the subscription ID might not be send to + // the client. + time.Sleep(5 * time.Second) + for i := 0; i < n; i++ { + if err := notifier.Notify(subscription.ID, val+i); err != nil { + return + } + } + + select { + case <-notifier.Closed(): + s.mu.Lock() + s.unsubscribed = true + s.mu.Unlock() + case <-subscription.Err(): + s.mu.Lock() + s.unsubscribed = true + s.mu.Unlock() + } + }() + + return subscription, nil +} + +// HangSubscription blocks on s.unblockHangSubscription before +// sending anything. +func (s *NotificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { + notifier, supported := NotifierFromContext(ctx) + if !supported { + return nil, ErrNotificationsUnsupported + } + + s.gotHangSubscriptionReq <- struct{}{} + <-s.unblockHangSubscription + subscription := notifier.CreateSubscription() + + go func() { + notifier.Notify(subscription.ID, val) + }() + return subscription, nil +} + +func TestNotifications(t *testing.T) { + server := NewServer() + service := &NotificationTestService{} + + if err := server.RegisterName("eth", service); err != nil { + t.Fatalf("unable to register test service %v", err) + } + + clientConn, serverConn := net.Pipe() + + go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions) + + out := json.NewEncoder(clientConn) + in := json.NewDecoder(clientConn) + + n := 5 + val := 12345 + request := map[string]interface{}{ + "id": 1, + "method": "eth_subscribe", + "version": "2.0", + "params": []interface{}{"someSubscription", n, val}, + } + + // create subscription + if err := out.Encode(request); err != nil { + t.Fatal(err) + } + + var subid string + response := jsonSuccessResponse{Result: subid} + if err := in.Decode(&response); err != nil { + t.Fatal(err) + } + + var ok bool + if _, ok = response.Result.(string); !ok { + t.Fatalf("expected subscription id, got %T", response.Result) + } + + for i := 0; i < n; i++ { + var notification jsonNotification + if err := in.Decode(¬ification); err != nil { + t.Fatalf("%v", err) + } + + if int(notification.Params.Result.(float64)) != val+i { + t.Fatalf("expected %d, got %d", val+i, notification.Params.Result) + } + } + + clientConn.Close() // causes notification unsubscribe callback to be called + time.Sleep(1 * time.Second) + + if !service.wasUnsubCallbackCalled() { + t.Error("unsubscribe callback not called after closing connection") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/types.go b/vendor/github.com/ethereum/go-ethereum/rpc/types.go new file mode 100644 index 0000000..d8d736e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/types.go @@ -0,0 +1,189 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "fmt" + "math" + "math/big" + "reflect" + "strings" + "sync" + + "gopkg.in/fatih/set.v0" +) + +// API describes the set of methods offered over the RPC interface +type API struct { + Namespace string // namespace under which the rpc methods of Service are exposed + Version string // api version for DApp's + Service interface{} // receiver instance which holds the methods + Public bool // indication if the methods must be considered safe for public use +} + +// callback is a method callback which was registered in the server +type callback struct { + rcvr reflect.Value // receiver of method + method reflect.Method // callback + argTypes []reflect.Type // input argument types + hasCtx bool // method's first argument is a context (not included in argTypes) + errPos int // err return idx, of -1 when method cannot return error + isSubscribe bool // indication if the callback is a subscription +} + +// service represents a registered object +type service struct { + name string // name for service + rcvr reflect.Value // receiver of methods for the service + typ reflect.Type // receiver type + callbacks callbacks // registered handlers + subscriptions subscriptions // available subscriptions/notifications +} + +// serverRequest is an incoming request +type serverRequest struct { + id interface{} + svcname string + rcvr reflect.Value + callb *callback + args []reflect.Value + isUnsubscribe bool + err Error +} + +type serviceRegistry map[string]*service // collection of services +type callbacks map[string]*callback // collection of RPC callbacks +type subscriptions map[string]*callback // collection of subscription callbacks +type subscriptionRegistry map[string]*callback // collection of subscription callbacks + +// Server represents a RPC server +type Server struct { + services serviceRegistry + muSubcriptions sync.Mutex // protects subscriptions + subscriptions subscriptionRegistry + + run int32 + codecsMu sync.Mutex + codecs *set.Set +} + +// rpcRequest represents a raw incoming RPC request +type rpcRequest struct { + service string + method string + id interface{} + isPubSub bool + params interface{} + err Error // invalid batch element +} + +// Error wraps RPC errors, which contain an error code in addition to the message. +type Error interface { + Error() string // returns the message + ErrorCode() int // returns the code +} + +// ServerCodec implements reading, parsing and writing RPC messages for the server side of +// a RPC session. Implementations must be go-routine safe since the codec can be called in +// multiple go-routines concurrently. +type ServerCodec interface { + // Read next request + ReadRequestHeaders() ([]rpcRequest, bool, Error) + // Parse request argument to the given types + ParseRequestArguments([]reflect.Type, interface{}) ([]reflect.Value, Error) + // Assemble success response, expects response id and payload + CreateResponse(interface{}, interface{}) interface{} + // Assemble error response, expects response id and error + CreateErrorResponse(interface{}, Error) interface{} + // Assemble error response with extra information about the error through info + CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{} + // Create notification response + CreateNotification(string, interface{}) interface{} + // Write msg to client. + Write(interface{}) error + // Close underlying data stream + Close() + // Closed when underlying connection is closed + Closed() <-chan interface{} +} + +var ( + pendingBlockNumber = big.NewInt(-2) + latestBlockNumber = big.NewInt(-1) + earliestBlockNumber = big.NewInt(0) + maxBlockNumber = big.NewInt(math.MaxInt64) +) + +type BlockNumber int64 + +const ( + PendingBlockNumber = BlockNumber(-2) + LatestBlockNumber = BlockNumber(-1) +) + +// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: +// - "latest", "earliest" or "pending" as string arguments +// - the block number +// Returned errors: +// - an invalid block number error when the given argument isn't a known strings +// - an out of range error when the given block number is either too little or too large +func (bn *BlockNumber) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + if len(input) == 0 { + *bn = BlockNumber(latestBlockNumber.Int64()) + return nil + } + + in := new(big.Int) + _, ok := in.SetString(input, 0) + + if !ok { // test if user supplied string tag + strBlockNumber := input + if strBlockNumber == "latest" { + *bn = BlockNumber(latestBlockNumber.Int64()) + return nil + } + + if strBlockNumber == "earliest" { + *bn = BlockNumber(earliestBlockNumber.Int64()) + return nil + } + + if strBlockNumber == "pending" { + *bn = BlockNumber(pendingBlockNumber.Int64()) + return nil + } + + return fmt.Errorf(`invalid blocknumber %s`, data) + } + + if in.Cmp(earliestBlockNumber) >= 0 && in.Cmp(maxBlockNumber) <= 0 { + *bn = BlockNumber(in.Int64()) + return nil + } + + return fmt.Errorf("blocknumber not in range [%d, %d]", earliestBlockNumber, maxBlockNumber) +} + +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/utils.go b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go new file mode 100644 index 0000000..c249e9b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/utils.go @@ -0,0 +1,263 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bufio" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "math/big" + "math/rand" + "reflect" + "strings" + "sync" + "time" + "unicode" + "unicode/utf8" + + "golang.org/x/net/context" +) + +var ( + subscriptionIDGenMu sync.Mutex + subscriptionIDGen = idGenerator() +) + +// Is this an exported - upper case - name? +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +// Is this type exported or a builtin? +func isExportedOrBuiltinType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + // PkgPath will be non-empty even for an exported type, + // so we need to check the type name as well. + return isExported(t.Name()) || t.PkgPath() == "" +} + +var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() + +// isContextType returns an indication if the given t is of context.Context or *context.Context type +func isContextType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t == contextType +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +// Implements this type the error interface +func isErrorType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t.Implements(errorType) +} + +var subscriptionType = reflect.TypeOf((*Subscription)(nil)).Elem() + +// isSubscriptionType returns an indication if the given t is of Subscription or *Subscription type +func isSubscriptionType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t == subscriptionType +} + +// isPubSub tests whether the given method has as as first argument a context.Context +// and returns the pair (Subscription, error) +func isPubSub(methodType reflect.Type) bool { + // numIn(0) is the receiver type + if methodType.NumIn() < 2 || methodType.NumOut() != 2 { + return false + } + + return isContextType(methodType.In(1)) && + isSubscriptionType(methodType.Out(0)) && + isErrorType(methodType.Out(1)) +} + +// formatName will convert to first character to lower case +func formatName(name string) string { + ret := []rune(name) + if len(ret) > 0 { + ret[0] = unicode.ToLower(ret[0]) + } + return string(ret) +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem() + +// Indication if this type should be serialized in hex +func isHexNum(t reflect.Type) bool { + if t == nil { + return false + } + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + return t == bigIntType +} + +var blockNumberType = reflect.TypeOf((*BlockNumber)(nil)).Elem() + +// Indication if the given block is a BlockNumber +func isBlockNumber(t reflect.Type) bool { + if t == nil { + return false + } + + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + return t == blockNumberType +} + +// suitableCallbacks iterates over the methods of the given type. It will determine if a method satisfies the criteria +// for a RPC callback or a subscription callback and adds it to the collection of callbacks or subscriptions. See server +// documentation for a summary of these criteria. +func suitableCallbacks(rcvr reflect.Value, typ reflect.Type) (callbacks, subscriptions) { + callbacks := make(callbacks) + subscriptions := make(subscriptions) + +METHODS: + for m := 0; m < typ.NumMethod(); m++ { + method := typ.Method(m) + mtype := method.Type + mname := formatName(method.Name) + if method.PkgPath != "" { // method must be exported + continue + } + + var h callback + h.isSubscribe = isPubSub(mtype) + h.rcvr = rcvr + h.method = method + h.errPos = -1 + + firstArg := 1 + numIn := mtype.NumIn() + if numIn >= 2 && mtype.In(1) == contextType { + h.hasCtx = true + firstArg = 2 + } + + if h.isSubscribe { + h.argTypes = make([]reflect.Type, numIn-firstArg) // skip rcvr type + for i := firstArg; i < numIn; i++ { + argType := mtype.In(i) + if isExportedOrBuiltinType(argType) { + h.argTypes[i-firstArg] = argType + } else { + continue METHODS + } + } + + subscriptions[mname] = &h + continue METHODS + } + + // determine method arguments, ignore first arg since it's the receiver type + // Arguments must be exported or builtin types + h.argTypes = make([]reflect.Type, numIn-firstArg) + for i := firstArg; i < numIn; i++ { + argType := mtype.In(i) + if !isExportedOrBuiltinType(argType) { + continue METHODS + } + h.argTypes[i-firstArg] = argType + } + + // check that all returned values are exported or builtin types + for i := 0; i < mtype.NumOut(); i++ { + if !isExportedOrBuiltinType(mtype.Out(i)) { + continue METHODS + } + } + + // when a method returns an error it must be the last returned value + h.errPos = -1 + for i := 0; i < mtype.NumOut(); i++ { + if isErrorType(mtype.Out(i)) { + h.errPos = i + break + } + } + + if h.errPos >= 0 && h.errPos != mtype.NumOut()-1 { + continue METHODS + } + + switch mtype.NumOut() { + case 0, 1: + break + case 2: + if h.errPos == -1 { // method must one return value and 1 error + continue METHODS + } + break + default: + continue METHODS + } + + callbacks[mname] = &h + } + + return callbacks, subscriptions +} + +// idGenerator helper utility that generates a (pseudo) random sequence of +// bytes that are used to generate identifiers. +func idGenerator() *rand.Rand { + if seed, err := binary.ReadVarint(bufio.NewReader(crand.Reader)); err == nil { + return rand.New(rand.NewSource(seed)) + } + return rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) +} + +// NewID generates a identifier that can be used as an identifier in the RPC interface. +// e.g. filter and subscription identifier. +func NewID() ID { + subscriptionIDGenMu.Lock() + defer subscriptionIDGenMu.Unlock() + + id := make([]byte, 16) + for i := 0; i < len(id); i += 7 { + val := subscriptionIDGen.Int63() + for j := 0; i+j < len(id) && j < 7; j++ { + id[i+j] = byte(val) + val >>= 8 + } + } + + rpcId := hex.EncodeToString(id) + // rpc ID's are RPC quantities, no leading zero's and 0 is 0x0 + rpcId = strings.TrimLeft(rpcId, "0") + if rpcId == "" { + rpcId = "0" + } + + return ID("0x" + rpcId) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/utils_test.go b/vendor/github.com/ethereum/go-ethereum/rpc/utils_test.go new file mode 100644 index 0000000..e0e063f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/utils_test.go @@ -0,0 +1,43 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "strings" + "testing" +) + +func TestNewID(t *testing.T) { + hexchars := "0123456789ABCDEFabcdef" + for i := 0; i < 100; i++ { + id := string(NewID()) + if !strings.HasPrefix(id, "0x") { + t.Fatalf("invalid ID prefix, want '0x...', got %s", id) + } + + id = id[2:] + if len(id) == 0 || len(id) > 32 { + t.Fatalf("invalid ID length, want len(id) > 0 && len(id) <= 32), got %d", len(id)) + } + + for i := 0; i < len(id); i++ { + if strings.IndexByte(hexchars, id[i]) == -1 { + t.Fatalf("unexpected byte, want any valid hex char, got %c", id[i]) + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/websocket.go b/vendor/github.com/ethereum/go-ethereum/rpc/websocket.go new file mode 100644 index 0000000..fc3cd07 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/websocket.go @@ -0,0 +1,152 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "crypto/tls" + "fmt" + "net" + "net/http" + "net/url" + "os" + "strings" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" + "golang.org/x/net/websocket" + "gopkg.in/fatih/set.v0" +) + +// WebsocketHandler returns a handler that serves JSON-RPC to WebSocket connections. +// +// allowedOrigins should be a comma-separated list of allowed origin URLs. +// To allow connections with any origin, pass "*". +func (srv *Server) WebsocketHandler(allowedOrigins string) http.Handler { + return websocket.Server{ + Handshake: wsHandshakeValidator(strings.Split(allowedOrigins, ",")), + Handler: func(conn *websocket.Conn) { + srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions) + }, + } +} + +// NewWSServer creates a new websocket RPC server around an API provider. +// +// Deprecated: use Server.WebsocketHandler +func NewWSServer(allowedOrigins string, srv *Server) *http.Server { + return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)} +} + +// wsHandshakeValidator returns a handler that verifies the origin during the +// websocket upgrade process. When a '*' is specified as an allowed origins all +// connections are accepted. +func wsHandshakeValidator(allowedOrigins []string) func(*websocket.Config, *http.Request) error { + origins := set.New() + allowAllOrigins := false + + for _, origin := range allowedOrigins { + if origin == "*" { + allowAllOrigins = true + } + if origin != "" { + origins.Add(strings.ToLower(origin)) + } + } + + // allow localhost if no allowedOrigins are specified. + if len(origins.List()) == 0 { + origins.Add("http://localhost") + if hostname, err := os.Hostname(); err == nil { + origins.Add("http://" + strings.ToLower(hostname)) + } + } + + glog.V(logger.Debug).Infof("Allowed origin(s) for WS RPC interface %v\n", origins.List()) + + f := func(cfg *websocket.Config, req *http.Request) error { + origin := strings.ToLower(req.Header.Get("Origin")) + if allowAllOrigins || origins.Has(origin) { + return nil + } + glog.V(logger.Debug).Infof("origin '%s' not allowed on WS-RPC interface\n", origin) + return fmt.Errorf("origin %s not allowed", origin) + } + + return f +} + +// DialWebsocket creates a new RPC client that communicates with a JSON-RPC server +// that is listening on the given endpoint. +// +// The context is used for the initial connection establishment. It does not +// affect subsequent interactions with the client. +func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { + if origin == "" { + var err error + if origin, err = os.Hostname(); err != nil { + return nil, err + } + if strings.HasPrefix(endpoint, "wss") { + origin = "https://" + strings.ToLower(origin) + } else { + origin = "http://" + strings.ToLower(origin) + } + } + config, err := websocket.NewConfig(endpoint, origin) + if err != nil { + return nil, err + } + + return newClient(ctx, func(ctx context.Context) (net.Conn, error) { + return wsDialContext(ctx, config) + }) +} + +func wsDialContext(ctx context.Context, config *websocket.Config) (*websocket.Conn, error) { + var conn net.Conn + var err error + switch config.Location.Scheme { + case "ws": + conn, err = dialContext(ctx, "tcp", wsDialAddress(config.Location)) + case "wss": + dialer := contextDialer(ctx) + conn, err = tls.DialWithDialer(dialer, "tcp", wsDialAddress(config.Location), config.TlsConfig) + default: + err = websocket.ErrBadScheme + } + if err != nil { + return nil, err + } + ws, err := websocket.NewClient(config, conn) + if err != nil { + conn.Close() + return nil, err + } + return ws, err +} + +var wsPortMap = map[string]string{"ws": "80", "wss": "443"} + +func wsDialAddress(location *url.URL) string { + if _, ok := wsPortMap[location.Scheme]; ok { + if _, _, err := net.SplitHostPort(location.Host); err != nil { + return net.JoinHostPort(location.Host, wsPortMap[location.Scheme]) + } + } + return location.Host +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go new file mode 100644 index 0000000..f92a146 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/api.go @@ -0,0 +1,205 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "fmt" + "io" + "net/http" + "regexp" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +var ( + hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}") + slashes = regexp.MustCompile("/+") + domainAndVersion = regexp.MustCompile("[@:;,]+") +) + +type Resolver interface { + Resolve(string) (common.Hash, error) +} + +/* +Api implements webserver/file system related content storage and retrieval +on top of the dpa +it is the public interface of the dpa which is included in the ethereum stack +*/ +type Api struct { + dpa *storage.DPA + dns Resolver +} + +//the api constructor initialises +func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { + self = &Api{ + dpa: dpa, + dns: dns, + } + return +} + +// DPA reader API +func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { + return self.dpa.Retrieve(key) +} + +func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { + return self.dpa.Store(data, size, wg, nil) +} + +type ErrResolve error + +// DNS Resolver +func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { + glog.V(logger.Detail).Infof("Resolving : %v", hostPort) + if hashMatcher.MatchString(hostPort) || self.dns == nil { + glog.V(logger.Detail).Infof("host is a contentHash: '%v'", hostPort) + return storage.Key(common.Hex2Bytes(hostPort)), nil + } + if !nameresolver { + return nil, fmt.Errorf("'%s' is not a content hash value.", hostPort) + } + contentHash, err := self.dns.Resolve(hostPort) + if err != nil { + err = ErrResolve(err) + glog.V(logger.Warn).Infof("DNS error : %v", err) + } + glog.V(logger.Detail).Infof("host lookup: %v -> %v", err) + return contentHash[:], err +} +func Parse(uri string) (hostPort, path string) { + if uri == "" { + return + } + parts := slashes.Split(uri, 3) + var i int + if len(parts) == 0 { + return + } + // beginning with slash is now optional + for len(parts[i]) == 0 { + i++ + } + hostPort = parts[i] + for i < len(parts)-1 { + i++ + if len(path) > 0 { + path = path + "/" + parts[i] + } else { + path = parts[i] + } + } + glog.V(logger.Debug).Infof("host: '%s', path '%s' requested.", hostPort, path) + return +} + +func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { + hostPort, path = Parse(uri) + //resolving host and port + contentHash, err := self.Resolve(hostPort, nameresolver) + glog.V(logger.Debug).Infof("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path) + return contentHash[:], hostPort, path, err +} + +// Put provides singleton manifest creation on top of dpa store +func (self *Api) Put(content, contentType string) (string, error) { + r := strings.NewReader(content) + wg := &sync.WaitGroup{} + key, err := self.dpa.Store(r, int64(len(content)), wg, nil) + if err != nil { + return "", err + } + manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) + r = strings.NewReader(manifest) + key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) + if err != nil { + return "", err + } + wg.Wait() + return key.String(), nil +} + +// Get uses iterative manifest retrieval and prefix matching +// to resolve path to content using dpa retrieve +// it returns a section reader, mimeType, status and an error +func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionReader, mimeType string, status int, err error) { + key, _, path, err := self.parseAndResolve(uri, nameresolver) + if err != nil { + return nil, "", 500, fmt.Errorf("can't resolve: %v", err) + } + + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("loadManifestTrie error: %v", err) + return + } + + glog.V(logger.Detail).Infof("getEntry(%s)", path) + + entry, _ := trie.getEntry(path) + + if entry != nil { + key = common.Hex2Bytes(entry.Hash) + status = entry.Status + mimeType = entry.ContentType + glog.V(logger.Detail).Infof("content lookup key: '%v' (%v)", key, mimeType) + reader = self.dpa.Retrieve(key) + } else { + status = http.StatusNotFound + err = fmt.Errorf("manifest entry for '%s' not found", path) + glog.V(logger.Warn).Infof("%v", err) + } + return +} + +func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) { + root, _, path, err := self.parseAndResolve(uri, nameresolver) + if err != nil { + return "", fmt.Errorf("can't resolve: %v", err) + } + + quitC := make(chan bool) + trie, err := loadManifest(self.dpa, root, quitC) + if err != nil { + return + } + + if contentHash != "" { + entry := &manifestTrieEntry{ + Path: path, + Hash: contentHash, + ContentType: contentType, + } + trie.addEntry(entry, quitC) + } else { + trie.deleteEntry(path, quitC) + } + + err = trie.recalcAndStore() + if err != nil { + return + } + return trie.hash.String(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/api_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/api_test.go new file mode 100644 index 0000000..b098119 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/api_test.go @@ -0,0 +1,117 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "io" + "io/ioutil" + "os" + "testing" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func testApi(t *testing.T, f func(*Api)) { + datadir, err := ioutil.TempDir("", "bzz-test") + if err != nil { + t.Fatalf("unable to create temp dir: %v", err) + } + os.RemoveAll(datadir) + defer os.RemoveAll(datadir) + dpa, err := storage.NewLocalDPA(datadir) + if err != nil { + return + } + api := NewApi(dpa, nil) + dpa.Start() + f(api) + dpa.Stop() +} + +type testResponse struct { + reader storage.LazySectionReader + *Response +} + +func checkResponse(t *testing.T, resp *testResponse, exp *Response) { + + if resp.MimeType != exp.MimeType { + t.Errorf("incorrect mimeType. expected '%s', got '%s'", exp.MimeType, resp.MimeType) + } + if resp.Status != exp.Status { + t.Errorf("incorrect status. expected '%d', got '%d'", exp.Status, resp.Status) + } + if resp.Size != exp.Size { + t.Errorf("incorrect size. expected '%d', got '%d'", exp.Size, resp.Size) + } + if resp.reader != nil { + content := make([]byte, resp.Size) + read, _ := resp.reader.Read(content) + if int64(read) != exp.Size { + t.Errorf("incorrect content length. expected '%d...', got '%d...'", read, exp.Size) + } + resp.Content = string(content) + } + if resp.Content != exp.Content { + // if !bytes.Equal(resp.Content, exp.Content) + t.Errorf("incorrect content. expected '%s...', got '%s...'", string(exp.Content), string(resp.Content)) + } +} + +// func expResponse(content []byte, mimeType string, status int) *Response { +func expResponse(content string, mimeType string, status int) *Response { + glog.V(logger.Detail).Infof("expected content (%v): %v ", len(content), content) + return &Response{mimeType, status, int64(len(content)), content} +} + +// func testGet(t *testing.T, api *Api, bzzhash string) *testResponse { +func testGet(t *testing.T, api *Api, bzzhash string) *testResponse { + reader, mimeType, status, err := api.Get(bzzhash, true) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + quitC := make(chan bool) + size, err := reader.Size(quitC) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + glog.V(logger.Detail).Infof("reader size: %v ", size) + s := make([]byte, size) + _, err = reader.Read(s) + if err != io.EOF { + t.Fatalf("unexpected error: %v", err) + } + reader.Seek(0, 0) + return &testResponse{reader, &Response{mimeType, status, size, string(s)}} + // return &testResponse{reader, &Response{mimeType, status, reader.Size(), nil}} +} + +func TestApiPut(t *testing.T) { + testApi(t, func(api *Api) { + content := "hello" + exp := expResponse(content, "text/plain", 0) + // exp := expResponse([]byte(content), "text/plain", 0) + bzzhash, err := api.Put(content, exp.MimeType) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + resp := testGet(t, api, bzzhash) + checkResponse(t, resp, exp) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go new file mode 100644 index 0000000..23a8555 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config.go @@ -0,0 +1,146 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + port = "8500" +) + +var ( + ensRootAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010") +) + +// separate bzz directories +// allow several bzz nodes running in parallel +type Config struct { + // serialised/persisted fields + *storage.StoreParams + *storage.ChunkerParams + *network.HiveParams + Swap *swap.SwapParams + *network.SyncParams + Path string + Port string + PublicKey string + BzzKey string + EnsRoot common.Address + NetworkId uint64 +} + +// config is agnostic to where private key is coming from +// so managing accounts is outside swarm and left to wrappers +func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, networkId uint64) (self *Config, err error) { + address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address + dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes())) + err = os.MkdirAll(dirpath, os.ModePerm) + if err != nil { + return + } + confpath := filepath.Join(dirpath, "config.json") + var data []byte + pubkey := crypto.FromECDSAPub(&prvKey.PublicKey) + pubkeyhex := common.ToHex(pubkey) + keyhex := crypto.Keccak256Hash(pubkey).Hex() + + self = &Config{ + SyncParams: network.NewSyncParams(dirpath), + HiveParams: network.NewHiveParams(dirpath), + ChunkerParams: storage.NewChunkerParams(), + StoreParams: storage.NewStoreParams(dirpath), + Port: port, + Path: dirpath, + Swap: swap.DefaultSwapParams(contract, prvKey), + PublicKey: pubkeyhex, + BzzKey: keyhex, + EnsRoot: ensRootAddress, + NetworkId: networkId, + } + data, err = ioutil.ReadFile(confpath) + + // if not set in function param, then set default for swarm network, will be overwritten by config file if present + if networkId == 0 { + self.NetworkId = network.NetworkId + } + + if err != nil { + if !os.IsNotExist(err) { + return + } + + // file does not exist + // write out config file + err = self.Save() + if err != nil { + err = fmt.Errorf("error writing config: %v", err) + } + return + } + + // file exists, deserialise + err = json.Unmarshal(data, self) + if err != nil { + return nil, fmt.Errorf("unable to parse config: %v", err) + } + // check public key + if pubkeyhex != self.PublicKey { + return nil, fmt.Errorf("public key does not match the one in the config file %v != %v", pubkeyhex, self.PublicKey) + } + if keyhex != self.BzzKey { + return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey) + } + + // if set in function param, replace id set from config file + if networkId != 0 { + self.NetworkId = networkId + } + + self.Swap.SetKey(prvKey) + + if (self.EnsRoot == common.Address{}) { + self.EnsRoot = ensRootAddress + } + + return +} + +func (self *Config) Save() error { + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = os.MkdirAll(self.Path, os.ModePerm) + if err != nil { + return err + } + confpath := filepath.Join(self.Path, "config.json") + return ioutil.WriteFile(confpath, data, os.ModePerm) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/config_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/config_test.go new file mode 100644 index 0000000..2f40098 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/config_test.go @@ -0,0 +1,121 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + hexprvkey = "65138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c" + defaultConfig = `{ + "ChunkDbPath": "` + filepath.Join("TMPDIR", "chunks") + `", + "DbCapacity": 5000000, + "CacheCapacity": 5000, + "Radius": 0, + "Branches": 128, + "Hash": "SHA3", + "CallInterval": 3000000000, + "KadDbPath": "` + filepath.Join("TMPDIR", "bzz-peers.json") + `", + "MaxProx": 8, + "ProxBinSize": 2, + "BucketSize": 4, + "PurgeInterval": 151200000000000, + "InitialRetryInterval": 42000000, + "MaxIdleInterval": 42000000000, + "ConnRetryExp": 2, + "Swap": { + "BuyAt": 20000000000, + "SellAt": 20000000000, + "PayAt": 100, + "DropAt": 10000, + "AutoCashInterval": 300000000000, + "AutoCashThreshold": 50000000000000, + "AutoDepositInterval": 300000000000, + "AutoDepositThreshold": 50000000000000, + "AutoDepositBuffer": 100000000000000, + "PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3", + "Contract": "0x0000000000000000000000000000000000000000", + "Beneficiary": "0x0d2f62485607cf38d9d795d93682a517661e513e" + }, + "RequestDbPath": "` + filepath.Join("TMPDIR", "requests") + `", + "RequestDbBatchSize": 512, + "KeyBufferSize": 1024, + "SyncBatchSize": 128, + "SyncBufferSize": 128, + "SyncCacheSize": 1024, + "SyncPriorities": [ + 2, + 1, + 1, + 0, + 0 + ], + "SyncModes": [ + true, + true, + true, + true, + false + ], + "Path": "TMPDIR", + "Port": "8500", + "PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3", + "BzzKey": "0xe861964402c0b78e2d44098329b8545726f215afa737d803714a4338552fcb81", + "EnsRoot": "0x112234455c3a32fd11230c42e7bccd4a84e02010", + "NetworkId": 323 +}` +) + +func TestConfigWriteRead(t *testing.T) { + tmp, err := ioutil.TempDir(os.TempDir(), "bzz-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + prvkey := crypto.ToECDSA(common.Hex2Bytes(hexprvkey)) + orig, err := NewConfig(tmp, common.Address{}, prvkey, 323) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + data, err := ioutil.ReadFile(filepath.Join(orig.Path, "config.json")) + if err != nil { + t.Fatalf("default config file cannot be read: %v", err) + } + exp := strings.Replace(defaultConfig, "TMPDIR", orig.Path, -1) + exp = strings.Replace(exp, "\\", "\\\\", -1) + if string(data) != exp { + t.Fatalf("default config mismatch:\nexpected: %v\ngot: %v", exp, string(data)) + } + + conf, err := NewConfig(tmp, common.Address{}, prvkey, 323) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if conf.Swap.Beneficiary.Hex() != orig.Swap.Beneficiary.Hex() { + t.Fatalf("expected beneficiary from loaded config %v to match original %v", conf.Swap.Beneficiary.Hex(), orig.Swap.Beneficiary.Hex()) + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go new file mode 100644 index 0000000..96aaf36 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem.go @@ -0,0 +1,285 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const maxParallelFiles = 5 + +type FileSystem struct { + api *Api +} + +func NewFileSystem(api *Api) *FileSystem { + return &FileSystem{api} +} + +// Upload replicates a local directory as a manifest file and uploads it +// using dpa store +// TODO: localpath should point to a manifest +func (self *FileSystem) Upload(lpath, index string) (string, error) { + var list []*manifestTrieEntry + localpath, err := filepath.Abs(filepath.Clean(lpath)) + if err != nil { + return "", err + } + + f, err := os.Open(localpath) + if err != nil { + return "", err + } + stat, err := f.Stat() + if err != nil { + return "", err + } + + var start int + if stat.IsDir() { + start = len(localpath) + glog.V(logger.Debug).Infof("uploading '%s'", localpath) + err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { + if (err == nil) && !info.IsDir() { + //fmt.Printf("lp %s path %s\n", localpath, path) + if len(path) <= start { + return fmt.Errorf("Path is too short") + } + if path[:start] != localpath { + return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(path), + } + list = append(list, entry) + } + return err + }) + if err != nil { + return "", err + } + } else { + dir := filepath.Dir(localpath) + start = len(dir) + if len(localpath) <= start { + return "", fmt.Errorf("Path is too short") + } + if localpath[:start] != dir { + return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) + } + entry := &manifestTrieEntry{ + Path: filepath.ToSlash(localpath), + } + list = append(list, entry) + } + + cnt := len(list) + errors := make([]error, cnt) + done := make(chan bool, maxParallelFiles) + dcnt := 0 + awg := &sync.WaitGroup{} + + for i, entry := range list { + if i >= dcnt+maxParallelFiles { + <-done + dcnt++ + } + awg.Add(1) + go func(i int, entry *manifestTrieEntry, done chan bool) { + f, err := os.Open(entry.Path) + if err == nil { + stat, _ := f.Stat() + var hash storage.Key + wg := &sync.WaitGroup{} + hash, err = self.api.dpa.Store(f, stat.Size(), wg, nil) + if hash != nil { + list[i].Hash = hash.String() + } + wg.Wait() + awg.Done() + if err == nil { + first512 := make([]byte, 512) + fread, _ := f.ReadAt(first512, 0) + if fread > 0 { + mimeType := http.DetectContentType(first512[:fread]) + if filepath.Ext(entry.Path) == ".css" { + mimeType = "text/css" + } + list[i].ContentType = mimeType + } + } + f.Close() + } + errors[i] = err + done <- true + }(i, entry, done) + } + for dcnt < cnt { + <-done + dcnt++ + } + + trie := &manifestTrie{ + dpa: self.api.dpa, + } + quitC := make(chan bool) + for i, entry := range list { + if errors[i] != nil { + return "", errors[i] + } + entry.Path = RegularSlashes(entry.Path[start:]) + if entry.Path == index { + ientry := &manifestTrieEntry{ + Path: "", + Hash: entry.Hash, + ContentType: entry.ContentType, + } + trie.addEntry(ientry, quitC) + } + trie.addEntry(entry, quitC) + } + + err2 := trie.recalcAndStore() + var hs string + if err2 == nil { + hs = trie.hash.String() + } + awg.Wait() + return hs, err2 +} + +// Download replicates the manifest path structure on the local filesystem +// under localpath +func (self *FileSystem) Download(bzzpath, localpath string) error { + lpath, err := filepath.Abs(filepath.Clean(localpath)) + if err != nil { + return err + } + err = os.MkdirAll(lpath, os.ModePerm) + if err != nil { + return err + } + + //resolving host and port + key, _, path, err := self.api.parseAndResolve(bzzpath, true) + if err != nil { + return err + } + + if len(path) > 0 { + path += "/" + } + + quitC := make(chan bool) + trie, err := loadManifest(self.api.dpa, key, quitC) + if err != nil { + glog.V(logger.Warn).Infof("fs.Download: loadManifestTrie error: %v", err) + return err + } + + type downloadListEntry struct { + key storage.Key + path string + } + + var list []*downloadListEntry + var mde error + + prevPath := lpath + err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { + glog.V(logger.Detail).Infof("fs.Download: %#v", entry) + + key = common.Hex2Bytes(entry.Hash) + path := lpath + "/" + suffix + dir := filepath.Dir(path) + if dir != prevPath { + mde = os.MkdirAll(dir, os.ModePerm) + prevPath = dir + } + if (mde == nil) && (path != dir+"/") { + list = append(list, &downloadListEntry{key: key, path: path}) + } + }) + if err != nil { + return err + } + + wg := sync.WaitGroup{} + errC := make(chan error) + done := make(chan bool, maxParallelFiles) + for i, entry := range list { + select { + case done <- true: + wg.Add(1) + case <-quitC: + return fmt.Errorf("aborted") + } + go func(i int, entry *downloadListEntry) { + defer wg.Done() + err := retrieveToFile(quitC, self.api.dpa, entry.key, entry.path) + if err != nil { + select { + case errC <- err: + case <-quitC: + } + return + } + <-done + }(i, entry) + } + go func() { + wg.Wait() + close(errC) + }() + select { + case err = <-errC: + return err + case <-quitC: + return fmt.Errorf("aborted") + } +} + +func retrieveToFile(quitC chan bool, dpa *storage.DPA, key storage.Key, path string) error { + f, err := os.Create(path) // TODO: path separators + if err != nil { + return err + } + reader := dpa.Retrieve(key) + writer := bufio.NewWriter(f) + size, err := reader.Size(quitC) + if err != nil { + return err + } + if _, err = io.CopyN(writer, reader, size); err != nil { + return err + } + if err := writer.Flush(); err != nil { + return err + } + return f.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem_test.go new file mode 100644 index 0000000..4a27cb1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/filesystem_test.go @@ -0,0 +1,188 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "sync" + "testing" +) + +var testDownloadDir, _ = ioutil.TempDir(os.TempDir(), "bzz-test") + +func testFileSystem(t *testing.T, f func(*FileSystem)) { + testApi(t, func(api *Api) { + f(NewFileSystem(api)) + }) +} + +func readPath(t *testing.T, parts ...string) string { + file := filepath.Join(parts...) + content, err := ioutil.ReadFile(file) + + if err != nil { + t.Fatalf("unexpected error reading '%v': %v", file, err) + } + return string(content) +} + +func TestApiDirUpload0(t *testing.T) { + testFileSystem(t, func(fs *FileSystem) { + api := fs.api + bzzhash, err := fs.Upload(filepath.Join("testdata", "test0"), "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + content := readPath(t, "testdata", "test0", "index.html") + resp := testGet(t, api, bzzhash+"/index.html") + exp := expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + + content = readPath(t, "testdata", "test0", "index.css") + resp = testGet(t, api, bzzhash+"/index.css") + exp = expResponse(content, "text/css", 0) + checkResponse(t, resp, exp) + + _, _, _, err = api.Get(bzzhash, true) + if err == nil { + t.Fatalf("expected error: %v", err) + } + + downloadDir := filepath.Join(testDownloadDir, "test0") + defer os.RemoveAll(downloadDir) + err = fs.Download(bzzhash, downloadDir) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + newbzzhash, err := fs.Upload(downloadDir, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if bzzhash != newbzzhash { + t.Fatalf("download %v reuploaded has incorrect hash, expected %v, got %v", downloadDir, bzzhash, newbzzhash) + } + }) +} + +func TestApiDirUploadModify(t *testing.T) { + testFileSystem(t, func(fs *FileSystem) { + api := fs.api + bzzhash, err := fs.Upload(filepath.Join("testdata", "test0"), "") + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + bzzhash, err = api.Modify(bzzhash+"/index.html", "", "", true) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + index, err := ioutil.ReadFile(filepath.Join("testdata", "test0", "index.html")) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + wg := &sync.WaitGroup{} + hash, err := api.Store(bytes.NewReader(index), int64(len(index)), wg) + wg.Wait() + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + bzzhash, err = api.Modify(bzzhash+"/index2.html", hash.Hex(), "text/html; charset=utf-8", true) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + bzzhash, err = api.Modify(bzzhash+"/img/logo.png", hash.Hex(), "text/html; charset=utf-8", true) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + content := readPath(t, "testdata", "test0", "index.html") + resp := testGet(t, api, bzzhash+"/index2.html") + exp := expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + + resp = testGet(t, api, bzzhash+"/img/logo.png") + exp = expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + + content = readPath(t, "testdata", "test0", "index.css") + resp = testGet(t, api, bzzhash+"/index.css") + exp = expResponse(content, "text/css", 0) + checkResponse(t, resp, exp) + + _, _, _, err = api.Get(bzzhash, true) + if err == nil { + t.Errorf("expected error: %v", err) + } + }) +} + +func TestApiDirUploadWithRootFile(t *testing.T) { + testFileSystem(t, func(fs *FileSystem) { + api := fs.api + bzzhash, err := fs.Upload(filepath.Join("testdata", "test0"), "index.html") + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + content := readPath(t, "testdata", "test0", "index.html") + resp := testGet(t, api, bzzhash) + exp := expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + }) +} + +func TestApiFileUpload(t *testing.T) { + testFileSystem(t, func(fs *FileSystem) { + api := fs.api + bzzhash, err := fs.Upload(filepath.Join("testdata", "test0", "index.html"), "") + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + content := readPath(t, "testdata", "test0", "index.html") + resp := testGet(t, api, bzzhash+"/index.html") + exp := expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + }) +} + +func TestApiFileUploadWithRootFile(t *testing.T) { + testFileSystem(t, func(fs *FileSystem) { + api := fs.api + bzzhash, err := fs.Upload(filepath.Join("testdata", "test0", "index.html"), "index.html") + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + content := readPath(t, "testdata", "test0", "index.html") + resp := testGet(t, api, bzzhash) + exp := expResponse(content, "text/html; charset=utf-8", 0) + checkResponse(t, resp, exp) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go new file mode 100644 index 0000000..7b5bbc8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper.go @@ -0,0 +1,67 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package http + +import ( + "fmt" + "net/http" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +http roundtripper to register for bzz url scheme +see https://github.com/ethereum/go-ethereum/issues/2040 +Usage: + +import ( + "github.com/ethereum/go-ethereum/common/httpclient" + "github.com/ethereum/go-ethereum/swarm/api/http" +) +client := httpclient.New() +// for (private) swarm proxy running locally +client.RegisterScheme("bzz", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzi", &http.RoundTripper{Port: port}) +client.RegisterScheme("bzzr", &http.RoundTripper{Port: port}) + +The port you give the Roundtripper is the port the swarm proxy is listening on. +If Host is left empty, localhost is assumed. + +Using a public gateway, the above few lines gives you the leanest +bzz-scheme aware read-only http client. You really only ever need this +if you need go-native swarm access to bzz addresses. +*/ + +type RoundTripper struct { + Host string + Port string +} + +func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { + host := self.Host + if len(host) == 0 { + host = "localhost" + } + url := fmt.Sprintf("http://%s:%s/%s:/%s/%s", host, self.Port, req.Proto, req.URL.Host, req.URL.Path) + glog.V(logger.Info).Infof("roundtripper: proxying request '%s' to '%s'", req.RequestURI, url) + reqProxy, err := http.NewRequest(req.Method, url, req.Body) + if err != nil { + return nil, err + } + return http.DefaultClient.Do(reqProxy) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper_test.go new file mode 100644 index 0000000..fc74f5d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/roundtripper_test.go @@ -0,0 +1,66 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package http + +import ( + "io/ioutil" + "net/http" + "strings" + "testing" + "time" +) + +const port = "3222" + +func TestRoundTripper(t *testing.T) { + serveMux := http.NewServeMux() + serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Unix(0, 0), strings.NewReader(r.RequestURI)) + } else { + http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + } + }) + go http.ListenAndServe(":"+port, serveMux) + + rt := &RoundTripper{Port: port} + trans := &http.Transport{} + trans.RegisterProtocol("bzz", rt) + client := &http.Client{Transport: trans} + resp, err := client.Get("bzz://test.com/path") + if err != nil { + t.Errorf("expected no error, got %v", err) + return + } + + defer func() { + if resp != nil { + resp.Body.Close() + } + }() + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("expected no error, got %v", err) + return + } + if string(content) != "/HTTP/1.1:/test.com/path" { + t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/HTTP/1.1:/test.com/path", string(content)) + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go new file mode 100644 index 0000000..afd867e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server.go @@ -0,0 +1,340 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* +A simple http server interface to Swarm +*/ +package http + +import ( + "bytes" + "io" + "net/http" + "regexp" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/rs/cors" +) + +const ( + rawType = "application/octet-stream" +) + +var ( + // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw) + bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+") + trailingSlashes = regexp.MustCompile("/+$") + rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$") + // forever = func() time.Time { return time.Unix(0, 0) } + forever = time.Now +) + +type sequentialReader struct { + reader io.Reader + pos int64 + ahead map[int64](chan bool) + lock sync.Mutex +} + +// Server is the basic configuration needs for the HTTP server and also +// includes CORS settings. +type Server struct { + Addr string + CorsString string +} + +// browser API for registering bzz url scheme handlers: +// https://developer.mozilla.org/en/docs/Web-based_protocol_handlers +// electron (chromium) api for registering bzz url scheme handlers: +// https://github.com/atom/electron/blob/master/docs/api/protocol.md + +// starts up http server +func StartHttpServer(api *api.Api, server *Server) { + serveMux := http.NewServeMux() + serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + handler(w, r, api) + }) + var allowedOrigins []string + for _, domain := range strings.Split(server.CorsString, ",") { + allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) + } + c := cors.New(cors.Options{ + AllowedOrigins: allowedOrigins, + AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"}, + MaxAge: 600, + }) + hdlr := c.Handler(serveMux) + + go http.ListenAndServe(server.Addr, hdlr) + glog.V(logger.Info).Infof("Swarm HTTP proxy started on localhost:%s", server.Addr) +} + +func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { + requestURL := r.URL + // This is wrong + // if requestURL.Host == "" { + // var err error + // requestURL, err = url.Parse(r.Referer() + requestURL.String()) + // if err != nil { + // http.Error(w, err.Error(), http.StatusBadRequest) + // return + // } + // } + glog.V(logger.Debug).Infof("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept")) + uri := requestURL.Path + var raw, nameresolver bool + var proto string + + // HTTP-based URL protocol handler + glog.V(logger.Debug).Infof("BZZ request URI: '%s'", uri) + + path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string { + proto = p + return "" + }) + + // protocol identification (ugly) + if proto == "" { + if glog.V(logger.Error) { + glog.Errorf( + "[BZZ] Swarm: Protocol error in request `%s`.", + uri, + ) + http.Error(w, "Invalid request URL: need access protocol (bzz:/, bzzr:/, bzzi:/) as first element in path.", http.StatusBadRequest) + return + } + } + if len(proto) > 4 { + raw = proto[1:5] == "bzzr" + nameresolver = proto[1:5] != "bzzi" + } + + glog.V(logger.Debug).Infof( + "[BZZ] Swarm: %s request over protocol %s '%s' received.", + r.Method, proto, path, + ) + + switch { + case r.Method == "POST" || r.Method == "PUT": + if r.Header.Get("content-length") == "" { + http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest) + return + } + key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil) + if err == nil { + glog.V(logger.Debug).Infof("Content for %v stored", key.Log()) + } else { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if r.Method == "POST" { + if raw { + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key)))) + } else { + http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest) + return + } + } else { + // PUT + if raw { + http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + mime := r.Header.Get("Content-Type") + // TODO proper root hash separation + glog.V(logger.Debug).Infof("Modify '%s' to store %v as '%s'.", path, key.Log(), mime) + newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest) + return + } + } + } + case r.Method == "DELETE": + if raw { + http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest) + return + } else { + path = api.RegularSlashes(path) + glog.V(logger.Debug).Infof("Delete '%s'.", path) + newKey, err := a.Modify(path, "", "", nameresolver) + if err == nil { + glog.V(logger.Debug).Infof("Swarm replaced manifest by '%s'", newKey) + w.Header().Set("Content-Type", "text/plain") + http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) + } else { + http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest) + return + } + } + case r.Method == "GET" || r.Method == "HEAD": + path = trailingSlashes.ReplaceAllString(path, "") + if path == "" { + http.Error(w, "Empty path not allowed", http.StatusBadRequest) + return + } + if raw { + var reader storage.LazySectionReader + parsedurl, _ := api.Parse(path) + + if parsedurl == path { + key, err := a.Resolve(parsedurl, nameresolver) + if err != nil { + glog.V(logger.Error).Infof("%v", err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + reader = a.Retrieve(key) + } else { + var status int + readertmp, _, status, err := a.Get(path, nameresolver) + if err != nil { + http.Error(w, err.Error(), status) + return + } + reader = readertmp + } + + // retrieving content + + quitC := make(chan bool) + size, err := reader.Size(quitC) + if err != nil { + glog.V(logger.Debug).Infof("Could not determine size: %v", err.Error()) + //An error on call to Size means we don't have the root chunk + http.Error(w, err.Error(), http.StatusNotFound) + return + } + glog.V(logger.Debug).Infof("Reading %d bytes.", size) + + // setting mime type + qv := requestURL.Query() + mimeType := qv.Get("content_type") + if mimeType == "" { + mimeType = rawType + } + + w.Header().Set("Content-Type", mimeType) + http.ServeContent(w, r, uri, forever(), reader) + glog.V(logger.Debug).Infof("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType) + + // retrieve path via manifest + } else { + glog.V(logger.Debug).Infof("Structured GET request '%s' received.", uri) + // add trailing slash, if missing + if rootDocumentUri.MatchString(uri) { + http.Redirect(w, r, path+"/", http.StatusFound) + return + } + reader, mimeType, status, err := a.Get(path, nameresolver) + if err != nil { + if _, ok := err.(api.ErrResolve); ok { + glog.V(logger.Debug).Infof("%v", err) + status = http.StatusBadRequest + } else { + glog.V(logger.Debug).Infof("error retrieving '%s': %v", uri, err) + status = http.StatusNotFound + } + http.Error(w, err.Error(), status) + return + } + // set mime type and status headers + w.Header().Set("Content-Type", mimeType) + if status > 0 { + w.WriteHeader(status) + } else { + status = 200 + } + quitC := make(chan bool) + size, err := reader.Size(quitC) + if err != nil { + glog.V(logger.Debug).Infof("Could not determine size: %v", err.Error()) + //An error on call to Size means we don't have the root chunk + http.Error(w, err.Error(), http.StatusNotFound) + return + } + glog.V(logger.Debug).Infof("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status) + + http.ServeContent(w, r, path, forever(), reader) + + } + default: + http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + } +} + +func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) { + self.lock.Lock() + // assert self.pos <= off + if self.pos > off { + glog.V(logger.Error).Infof("non-sequential read attempted from sequentialReader; %d > %d", + self.pos, off) + panic("Non-sequential read attempt") + } + if self.pos != off { + glog.V(logger.Debug).Infof("deferred read in POST at position %d, offset %d.", + self.pos, off) + wait := make(chan bool) + self.ahead[off] = wait + self.lock.Unlock() + if <-wait { + // failed read behind + n = 0 + err = io.ErrUnexpectedEOF + return + } + self.lock.Lock() + } + localPos := 0 + for localPos < len(target) { + n, err = self.reader.Read(target[localPos:]) + localPos += n + glog.V(logger.Debug).Infof("Read %d bytes into buffer size %d from POST, error %v.", + n, len(target), err) + if err != nil { + glog.V(logger.Debug).Infof("POST stream's reading terminated with %v.", err) + for i := range self.ahead { + self.ahead[i] <- true + delete(self.ahead, i) + } + self.lock.Unlock() + return localPos, err + } + self.pos += int64(n) + } + wait := self.ahead[self.pos] + if wait != nil { + glog.V(logger.Debug).Infof("deferred read in POST at position %d triggered.", + self.pos) + delete(self.ahead, self.pos) + close(wait) + } + self.lock.Unlock() + return localPos, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server_test.go new file mode 100644 index 0000000..88b49b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/http/server_test.go @@ -0,0 +1,136 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package http + +import ( + "bytes" + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func TestBzzrGetPath(t *testing.T) { + + var err error + + maxproxyattempts := 3 + + testmanifest := []string{ + `{"entries":[{"path":"a/","hash":"674af7073604ebfc0282a4ab21e5ef1a3c22913866879ebc0816f8a89896b2ed","contentType":"application/bzz-manifest+json","status":0}]}`, + `{"entries":[{"path":"a","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"b/","hash":"0a87b1c3e4bf013686cdf107ec58590f2004610ee58cc2240f26939f691215f5","contentType":"application/bzz-manifest+json","status":0}]}`, + `{"entries":[{"path":"b","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"c","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0}]}`, + } + + testrequests := make(map[string]int) + testrequests["/"] = 0 + testrequests["/a"] = 1 + testrequests["/a/b"] = 2 + testrequests["/x"] = 0 + testrequests[""] = 0 + + expectedfailrequests := []string{"", "/x"} + + reader := [3]*bytes.Reader{} + + key := [3]storage.Key{} + + dir, _ := ioutil.TempDir("", "bzz-storage-test") + + storeparams := &storage.StoreParams{ + ChunkDbPath: dir, + DbCapacity: 5000000, + CacheCapacity: 5000, + Radius: 0, + } + + localStore, err := storage.NewLocalStore(storage.MakeHashFunc("SHA3"), storeparams) + if err != nil { + t.Fatal(err) + } + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + dpa := &storage.DPA{ + Chunker: chunker, + ChunkStore: localStore, + } + dpa.Start() + defer dpa.Stop() + + wg := &sync.WaitGroup{} + + for i, mf := range testmanifest { + reader[i] = bytes.NewReader([]byte(mf)) + key[i], err = dpa.Store(reader[i], int64(len(mf)), wg, nil) + if err != nil { + t.Fatal(err) + } + wg.Wait() + } + + a := api.NewApi(dpa, nil) + + /// \todo iterate port numbers up if fail + StartHttpServer(a, &Server{Addr: "127.0.0.1:8504", CorsString: ""}) + // how to wait for ListenAndServe to have initialized? This is pretty cruuuude + // if we fix it we don't need maxproxyattempts anymore either + time.Sleep(1000 * time.Millisecond) + for i := 0; i <= maxproxyattempts; i++ { + _, err := http.Get("http://127.0.0.1:8504/bzzr:/" + common.ToHex(key[0])[2:] + "/a") + if i == maxproxyattempts { + t.Fatalf("Failed to connect to proxy after %v attempts: %v", i, err) + } else if err != nil { + time.Sleep(100 * time.Millisecond) + continue + } + break + } + + for k, v := range testrequests { + var resp *http.Response + var respbody []byte + + url := "http://127.0.0.1:8504/bzzr:/" + if k[:] != "" { + url += common.ToHex(key[0])[2:] + "/" + k[1:] + "?content_type=text/plain" + } + resp, err = http.Get(url) + if err != nil { + t.Fatalf("Request failed: %v", err) + } + defer resp.Body.Close() + respbody, err = ioutil.ReadAll(resp.Body) + + if string(respbody) != testmanifest[v] { + isexpectedfailrequest := false + + for _, r := range expectedfailrequests { + if k[:] == r { + isexpectedfailrequest = true + } + } + if isexpectedfailrequest == false { + t.Fatalf("Response body does not match, expected: %v, got %v", testmanifest[v], string(respbody)) + } + } + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go new file mode 100644 index 0000000..36c0b04 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest.go @@ -0,0 +1,340 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + manifestType = "application/bzz-manifest+json" +) + +type manifestTrie struct { + dpa *storage.DPA + entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry + hash storage.Key // if hash != nil, it is stored +} + +type manifestJSON struct { + Entries []*manifestTrieEntry `json:"entries"` +} + +type manifestTrieEntry struct { + Path string `json:"path"` + Hash string `json:"hash"` // for manifest content type, empty until subtrie is evaluated + ContentType string `json:"contentType"` + Status int `json:"status"` + subtrie *manifestTrie +} + +func loadManifest(dpa *storage.DPA, hash storage.Key, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + glog.V(logger.Detail).Infof("manifest lookup key: '%v'.", hash.Log()) + // retrieve manifest via DPA + manifestReader := dpa.Retrieve(hash) + return readManifest(manifestReader, hash, dpa, quitC) +} + +func readManifest(manifestReader storage.LazySectionReader, hash storage.Key, dpa *storage.DPA, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand + + // TODO check size for oversized manifests + size, err := manifestReader.Size(quitC) + if err != nil { // size == 0 + // can't determine size means we don't have the root chunk + err = fmt.Errorf("Manifest not Found") + return + } + manifestData := make([]byte, size) + read, err := manifestReader.Read(manifestData) + if int64(read) < size { + glog.V(logger.Detail).Infof("Manifest %v not found.", hash.Log()) + if err == nil { + err = fmt.Errorf("Manifest retrieval cut short: read %v, expect %v", read, size) + } + return + } + + glog.V(logger.Detail).Infof("Manifest %v retrieved", hash.Log()) + man := manifestJSON{} + err = json.Unmarshal(manifestData, &man) + if err != nil { + err = fmt.Errorf("Manifest %v is malformed: %v", hash.Log(), err) + glog.V(logger.Detail).Infof("%v", err) + return + } + + glog.V(logger.Detail).Infof("Manifest %v has %d entries.", hash.Log(), len(man.Entries)) + + trie = &manifestTrie{ + dpa: dpa, + } + for _, entry := range man.Entries { + trie.addEntry(entry, quitC) + } + return +} + +func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(entry.Path) == 0 { + self.entries[256] = entry + return + } + + b := byte(entry.Path[0]) + if (self.entries[b] == nil) || (self.entries[b].Path == entry.Path) { + self.entries[b] = entry + return + } + + oldentry := self.entries[b] + cpl := 0 + for (len(entry.Path) > cpl) && (len(oldentry.Path) > cpl) && (entry.Path[cpl] == oldentry.Path[cpl]) { + cpl++ + } + + if (oldentry.ContentType == manifestType) && (cpl == len(oldentry.Path)) { + if self.loadSubTrie(oldentry, quitC) != nil { + return + } + entry.Path = entry.Path[cpl:] + oldentry.subtrie.addEntry(entry, quitC) + oldentry.Hash = "" + return + } + + commonPrefix := entry.Path[:cpl] + + subtrie := &manifestTrie{ + dpa: self.dpa, + } + entry.Path = entry.Path[cpl:] + oldentry.Path = oldentry.Path[cpl:] + subtrie.addEntry(entry, quitC) + subtrie.addEntry(oldentry, quitC) + + self.entries[b] = &manifestTrieEntry{ + Path: commonPrefix, + Hash: "", + ContentType: manifestType, + subtrie: subtrie, + } +} + +func (self *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { + for _, e := range self.entries { + if e != nil { + cnt++ + entry = e + } + } + return +} + +func (self *manifestTrie) deleteEntry(path string, quitC chan bool) { + self.hash = nil // trie modified, hash needs to be re-calculated on demand + + if len(path) == 0 { + self.entries[256] = nil + return + } + + b := byte(path[0]) + entry := self.entries[b] + if entry == nil { + return + } + if entry.Path == path { + self.entries[b] = nil + return + } + + epl := len(entry.Path) + if (entry.ContentType == manifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { + if self.loadSubTrie(entry, quitC) != nil { + return + } + entry.subtrie.deleteEntry(path[epl:], quitC) + entry.Hash = "" + // remove subtree if it has less than 2 elements + cnt, lastentry := entry.subtrie.getCountLast() + if cnt < 2 { + if lastentry != nil { + lastentry.Path = entry.Path + lastentry.Path + } + self.entries[b] = lastentry + } + } +} + +func (self *manifestTrie) recalcAndStore() error { + if self.hash != nil { + return nil + } + + var buffer bytes.Buffer + buffer.WriteString(`{"entries":[`) + + list := &manifestJSON{} + for _, entry := range self.entries { + if entry != nil { + if entry.Hash == "" { // TODO: paralellize + err := entry.subtrie.recalcAndStore() + if err != nil { + return err + } + entry.Hash = entry.subtrie.hash.String() + } + list.Entries = append(list.Entries, entry) + } + } + + manifest, err := json.Marshal(list) + if err != nil { + return err + } + + sr := bytes.NewReader(manifest) + wg := &sync.WaitGroup{} + key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg, nil) + wg.Wait() + self.hash = key + return err2 +} + +func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) { + if entry.subtrie == nil { + hash := common.Hex2Bytes(entry.Hash) + entry.subtrie, err = loadManifest(self.dpa, hash, quitC) + entry.Hash = "" // might not match, should be recalculated + } + return +} + +func (self *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) error { + plen := len(prefix) + var start, stop int + if plen == 0 { + start = 0 + stop = 256 + } else { + start = int(prefix[0]) + stop = start + } + + for i := start; i <= stop; i++ { + select { + case <-quitC: + return fmt.Errorf("aborted") + default: + } + entry := self.entries[i] + if entry != nil { + epl := len(entry.Path) + if entry.ContentType == manifestType { + l := plen + if epl < l { + l = epl + } + if prefix[:l] == entry.Path[:l] { + err := self.loadSubTrie(entry, quitC) + if err != nil { + return err + } + err = entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], quitC, cb) + if err != nil { + return err + } + } + } else { + if (epl >= plen) && (prefix == entry.Path[:plen]) { + cb(entry, rp+entry.Path[plen:]) + } + } + } + } + return nil +} + +func (self *manifestTrie) listWithPrefix(prefix string, quitC chan bool, cb func(entry *manifestTrieEntry, suffix string)) (err error) { + return self.listWithPrefixInt(prefix, "", quitC, cb) +} + +func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *manifestTrieEntry, pos int) { + + glog.V(logger.Detail).Infof("findPrefixOf(%s)", path) + + if len(path) == 0 { + return self.entries[256], 0 + } + + b := byte(path[0]) + entry = self.entries[b] + if entry == nil { + return self.entries[256], 0 + } + epl := len(entry.Path) + glog.V(logger.Detail).Infof("path = %v entry.Path = %v epl = %v", path, entry.Path, epl) + if (len(path) >= epl) && (path[:epl] == entry.Path) { + glog.V(logger.Detail).Infof("entry.ContentType = %v", entry.ContentType) + if entry.ContentType == manifestType { + err := self.loadSubTrie(entry, quitC) + if err != nil { + return nil, 0 + } + entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC) + if entry != nil { + pos += epl + } + } else { + pos = epl + } + } + return +} + +// file system manifest always contains regularized paths +// no leading or trailing slashes, only single slashes inside +func RegularSlashes(path string) (res string) { + for i := 0; i < len(path); i++ { + if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) { + res = res + path[i:i+1] + } + } + if (len(res) > 0) && (res[len(res)-1] == '/') { + res = res[:len(res)-1] + } + return +} + +func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) { + path := RegularSlashes(spath) + var pos int + quitC := make(chan bool) + entry, pos = self.findPrefixOf(path, quitC) + return entry, path[:pos] +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest_test.go new file mode 100644 index 0000000..20b8117 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/manifest_test.go @@ -0,0 +1,80 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + // "encoding/json" + "fmt" + "io" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func manifest(paths ...string) (manifestReader storage.LazySectionReader) { + var entries []string + for _, path := range paths { + entry := fmt.Sprintf(`{"path":"%s"}`, path) + entries = append(entries, entry) + } + manifest := fmt.Sprintf(`{"entries":[%s]}`, strings.Join(entries, ",")) + return &storage.LazyTestSectionReader{ + SectionReader: io.NewSectionReader(strings.NewReader(manifest), 0, int64(len(manifest))), + } +} + +func testGetEntry(t *testing.T, path, match string, paths ...string) *manifestTrie { + quitC := make(chan bool) + trie, err := readManifest(manifest(paths...), nil, nil, quitC) + if err != nil { + t.Errorf("unexpected error making manifest: %v", err) + } + checkEntry(t, path, match, trie) + return trie +} + +func checkEntry(t *testing.T, path, match string, trie *manifestTrie) { + entry, fullpath := trie.getEntry(path) + if match == "-" && entry != nil { + t.Errorf("expected no match for '%s', got '%s'", path, fullpath) + } else if entry == nil { + if match != "-" { + t.Errorf("expected entry '%s' to match '%s', got no match", match, path) + } + } else if fullpath != match { + t.Errorf("incorrect entry retrieved for '%s'. expected path '%v', got '%s'", path, match, fullpath) + } +} + +func TestGetEntry(t *testing.T) { + // file system manifest always contains regularized paths + testGetEntry(t, "a", "a", "a") + testGetEntry(t, "b", "-", "a") + testGetEntry(t, "/a//", "a", "a") + // fallback + testGetEntry(t, "/a", "", "") + testGetEntry(t, "/a/b", "a/b", "a/b") + // longest/deepest math + testGetEntry(t, "a/b", "-", "a", "a/ba", "a/b/c") + testGetEntry(t, "a/b", "a/b", "a", "a/b", "a/bb", "a/b/c") + testGetEntry(t, "//a//b//", "a/b", "a", "a/b", "a/bb", "a/b/c") +} + +func TestDeleteEntry(t *testing.T) { + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go new file mode 100644 index 0000000..31b4846 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage.go @@ -0,0 +1,70 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +type Response struct { + MimeType string + Status int + Size int64 + // Content []byte + Content string +} + +// implements a service +type Storage struct { + api *Api +} + +func NewStorage(api *Api) *Storage { + return &Storage{api} +} + +// Put uploads the content to the swarm with a simple manifest speficying +// its content type +func (self *Storage) Put(content, contentType string) (string, error) { + return self.api.Put(content, contentType) +} + +// Get retrieves the content from bzzpath and reads the response in full +// It returns the Response object, which serialises containing the +// response body as the value of the Content field +// NOTE: if error is non-nil, sResponse may still have partial content +// the actual size of which is given in len(resp.Content), while the expected +// size is resp.Size +func (self *Storage) Get(bzzpath string) (*Response, error) { + reader, mimeType, status, err := self.api.Get(bzzpath, true) + if err != nil { + return nil, err + } + quitC := make(chan bool) + expsize, err := reader.Size(quitC) + if err != nil { + return nil, err + } + body := make([]byte, expsize) + size, err := reader.Read(body) + if int64(size) == expsize { + err = nil + } + return &Response{mimeType, status, expsize, string(body[:size])}, err +} + +// Modify(rootHash, path, contentHash, contentType) takes th e manifest trie rooted in rootHash, +// and merge on to it. creating an entry w conentType (mime) +func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) { + return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/storage_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage_test.go new file mode 100644 index 0000000..72caf52 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/storage_test.go @@ -0,0 +1,49 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "testing" +) + +func testStorage(t *testing.T, f func(*Storage)) { + testApi(t, func(api *Api) { + f(NewStorage(api)) + }) +} + +func TestStoragePutGet(t *testing.T) { + testStorage(t, func(api *Storage) { + content := "hello" + exp := expResponse(content, "text/plain", 0) + // exp := expResponse([]byte(content), "text/plain", 0) + bzzhash, err := api.Put(content, exp.MimeType) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // to check put against the Api#Get + resp0 := testGet(t, api.api, bzzhash) + checkResponse(t, resp0, exp) + + // check storage#Get + resp, err := api.Get(bzzhash) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + checkResponse(t, &testResponse{nil, resp}, exp) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go new file mode 100644 index 0000000..6631196 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/api/testapi.go @@ -0,0 +1,46 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package api + +import ( + "github.com/ethereum/go-ethereum/swarm/network" +) + +type Control struct { + api *Api + hive *network.Hive +} + +func NewControl(api *Api, hive *network.Hive) *Control { + return &Control{api, hive} +} + +func (self *Control) BlockNetworkRead(on bool) { + self.hive.BlockNetworkRead(on) +} + +func (self *Control) SyncEnabled(on bool) { + self.hive.SyncEnabled(on) +} + +func (self *Control) SwapEnabled(on bool) { + self.hive.SwapEnabled(on) +} + +func (self *Control) Hive() string { + return self.hive.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/api/testdata/test0/img/logo.png b/vendor/github.com/ethereum/go-ethereum/swarm/api/testdata/test0/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e0fb15ab33a59ae0324983a2cfc6cc3cea59ef61 GIT binary patch literal 18136 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 + + + + + +

Swarm Test

+ Ethereum logo + + \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go new file mode 100644 index 0000000..454a572 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/depo.go @@ -0,0 +1,217 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "bytes" + "encoding/binary" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Handler for storage/retrieval related protocol requests +// implements the StorageHandler interface used by the bzz protocol +type Depo struct { + hashfunc storage.Hasher + localStore storage.ChunkStore + netStore storage.ChunkStore +} + +func NewDepo(hash storage.Hasher, localStore, remoteStore storage.ChunkStore) *Depo { + return &Depo{ + hashfunc: hash, + localStore: localStore, + netStore: remoteStore, // entrypoint internal + } +} + +// Handles UnsyncedKeysMsg after msg decoding - unsynced hashes upto sync state +// * the remote sync state is just stored and handled in protocol +// * filters through the new syncRequests and send the ones missing +// * back immediately as a deliveryRequest message +// * empty message just pings back for more (is this needed?) +// * strict signed sync states may be needed. +func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error { + unsynced := req.Unsynced + var missing []*syncRequest + var chunk *storage.Chunk + var err error + for _, req := range unsynced { + // skip keys that are found, + chunk, err = self.localStore.Get(storage.Key(req.Key[:])) + if err != nil || chunk.SData == nil { + missing = append(missing, req) + } + } + glog.V(logger.Debug).Infof("Depo.HandleUnsyncedKeysMsg: received %v unsynced keys: %v missing. new state: %v", len(unsynced), len(missing), req.State) + glog.V(logger.Detail).Infof("Depo.HandleUnsyncedKeysMsg: received %v", unsynced) + // send delivery request with missing keys + err = p.deliveryRequest(missing) + if err != nil { + return err + } + // set peers state to persist + p.syncState = req.State + return nil +} + +// Handles deliveryRequestMsg +// * serves actual chunks asked by the remote peer +// by pushing to the delivery queue (sync db) of the correct priority +// (remote peer is free to reprioritize) +// * the message implies remote peer wants more, so trigger for +// * new outgoing unsynced keys message is fired +func (self *Depo) HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error { + deliver := req.Deliver + // queue the actual delivery of a chunk () + glog.V(logger.Detail).Infof("Depo.HandleDeliveryRequestMsg: received %v delivery requests: %v", len(deliver), deliver) + for _, sreq := range deliver { + // TODO: look up in cache here or in deliveries + // priorities are taken from the message so the remote party can + // reprioritise to at their leisure + // r = self.pullCached(sreq.Key) // pulls and deletes from cache + Push(p, sreq.Key, sreq.Priority) + } + + // sends it out as unsyncedKeysMsg + p.syncer.sendUnsyncedKeys() + return nil +} + +// the entrypoint for store requests coming from the bzz wire protocol +// if key found locally, return. otherwise +// remote is untrusted, so hash is verified and chunk passed on to NetStore +func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) { + var islocal bool + req.from = p + chunk, err := self.localStore.Get(req.Key) + switch { + case err != nil: + glog.V(logger.Detail).Infof("Depo.handleStoreRequest: %v not found locally. create new chunk/request", req.Key) + // not found in memory cache, ie., a genuine store request + // create chunk + chunk = storage.NewChunk(req.Key, nil) + + case chunk.SData == nil: + // found chunk in memory store, needs the data, validate now + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v. request entry found", req) + + default: + // data is found, store request ignored + // this should update access count? + glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v found locally. ignore.", req) + islocal = true + //return + } + + hasher := self.hashfunc() + hasher.Write(req.SData) + if !bytes.Equal(hasher.Sum(nil), req.Key) { + // data does not validate, ignore + // TODO: peer should be penalised/dropped? + glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req) + return + } + + if islocal { + return + } + // update chunk with size and data + chunk.SData = req.SData // protocol validates that SData is minimum 9 bytes long (int64 size + at least one byte of data) + chunk.Size = int64(binary.LittleEndian.Uint64(req.SData[0:8])) + glog.V(logger.Detail).Infof("delivery of %v from %v", chunk, p) + chunk.Source = p + self.netStore.Put(chunk) +} + +// entrypoint for retrieve requests coming from the bzz wire protocol +// checks swap balance - return if peer has no credit +func (self *Depo) HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) { + req.from = p + // swap - record credit for 1 request + // note that only charge actual reqsearches + var err error + if p.swap != nil { + err = p.swap.Add(1) + } + if err != nil { + glog.V(logger.Warn).Infof("Depo.HandleRetrieveRequest: %v - cannot process request: %v", req.Key.Log(), err) + return + } + + // call storage.NetStore#Get which + // blocks until local retrieval finished + // launches cloud retrieval + chunk, _ := self.netStore.Get(req.Key) + req = self.strategyUpdateRequest(chunk.Req, req) + // check if we can immediately deliver + if chunk.SData != nil { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, delivering...", req.Key.Log()) + + if req.MaxSize == 0 || int64(req.MaxSize) >= chunk.Size { + sreq := &storeRequestMsgData{ + Id: req.Id, + Key: chunk.Key, + SData: chunk.SData, + requestTimeout: req.timeout, // + } + p.syncer.addRequest(sreq, DeliverReq) + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content found, not wanted", req.Key.Log()) + } + } else { + glog.V(logger.Detail).Infof("Depo.HandleRetrieveRequest: %v - content not found locally. asked swarm for help. will get back", req.Key.Log()) + } +} + +// add peer request the chunk and decides the timeout for the response if still searching +func (self *Depo) strategyUpdateRequest(rs *storage.RequestStatus, origReq *retrieveRequestMsgData) (req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.strategyUpdateRequest: key %v", origReq.Key.Log()) + // we do not create an alternative one + req = origReq + if rs != nil { + self.addRequester(rs, req) + req.setTimeout(self.searchTimeout(rs, req)) + } + return +} + +// decides the timeout promise sent with the immediate peers response to a retrieve request +// if timeout is explicitly set and expired +func (self *Depo) searchTimeout(rs *storage.RequestStatus, req *retrieveRequestMsgData) (timeout *time.Time) { + reqt := req.getTimeout() + t := time.Now().Add(searchTimeout) + if reqt != nil && reqt.Before(t) { + return reqt + } else { + return &t + } +} + +/* +adds a new peer to an existing open request +only add if less than requesterCount peers forwarded the same request id so far +note this is done irrespective of status (searching or found) +*/ +func (self *Depo) addRequester(rs *storage.RequestStatus, req *retrieveRequestMsgData) { + glog.V(logger.Detail).Infof("Depo.addRequester: key %v - add peer to req.Id %v", req.Key.Log(), req.from, req.Id) + list := rs.Requesters[req.Id] + rs.Requesters[req.Id] = append(list, req) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go new file mode 100644 index 0000000..fef79c7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/forwarding.go @@ -0,0 +1,150 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const requesterCount = 3 + +/* +forwarder implements the CloudStore interface (use by storage.NetStore) +and serves as the cloud store backend orchestrating storage/retrieval/delivery +via the native bzz protocol +which uses an MSB logarithmic distance-based semi-permanent Kademlia table for +* recursive forwarding style routing for retrieval +* smart syncronisation +*/ + +type forwarder struct { + hive *Hive +} + +func NewForwarder(hive *Hive) *forwarder { + return &forwarder{hive: hive} +} + +// generate a unique id uint64 +func generateId() uint64 { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + return uint64(r.Int63()) +} + +var searchTimeout = 3 * time.Second + +// forwarding logic +// logic propagating retrieve requests to peers given by the kademlia hive +func (self *forwarder) Retrieve(chunk *storage.Chunk) { + peers := self.hive.getPeers(chunk.Key, 0) + glog.V(logger.Detail).Infof("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers)) +OUT: + for _, p := range peers { + glog.V(logger.Detail).Infof("forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p) + for _, recipients := range chunk.Req.Requesters { + for _, recipient := range recipients { + req := recipient.(*retrieveRequestMsgData) + if req.from.Addr() == p.Addr() { + continue OUT + } + } + } + req := &retrieveRequestMsgData{ + Key: chunk.Key, + Id: generateId(), + } + var err error + if p.swap != nil { + err = p.swap.Add(-1) + } + if err == nil { + p.retrieve(req) + break OUT + } + glog.V(logger.Warn).Infof("forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err) + } +} + +// requests to specific peers given by the kademlia hive +// except for peers that the store request came from (if any) +// delivery queueing taken care of by syncer +func (self *forwarder) Store(chunk *storage.Chunk) { + var n int + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var source *peer + if chunk.Source != nil { + source = chunk.Source.(*peer) + } + for _, p := range self.hive.getPeers(chunk.Key, 0) { + glog.V(logger.Detail).Infof("forwarder.Store: %v %v", p, chunk) + + if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) { + n++ + Deliver(p, msg, PropagateReq) + } + } + glog.V(logger.Detail).Infof("forwarder.Store: sent to %v peers (chunk = %v)", n, chunk) +} + +// once a chunk is found deliver it to its requesters unless timed out +func (self *forwarder) Deliver(chunk *storage.Chunk) { + // iterate over request entries + for id, requesters := range chunk.Req.Requesters { + counter := requesterCount + msg := &storeRequestMsgData{ + Key: chunk.Key, + SData: chunk.SData, + } + var n int + var req *retrieveRequestMsgData + // iterate over requesters with the same id + for id, r := range requesters { + req = r.(*retrieveRequestMsgData) + if req.timeout == nil || req.timeout.After(time.Now()) { + glog.V(logger.Detail).Infof("forwarder.Deliver: %v -> %v", req.Id, req.from) + msg.Id = uint64(id) + Deliver(req.from, msg, DeliverReq) + n++ + counter-- + if counter <= 0 { + break + } + } + } + glog.V(logger.Detail).Infof("forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n) + } +} + +// initiate delivery of a chunk to a particular peer via syncer#addRequest +// depending on syncer mode and priority settings and sync request type +// this either goes via confirmation roundtrip or queued or pushed directly +func Deliver(p *peer, req interface{}, ty int) { + p.syncer.addRequest(req, ty) +} + +// push chunk over to peer +func Push(p *peer, key storage.Key, priority uint) { + p.syncer.doDelivery(key, priority, p.syncer.quit) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go new file mode 100644 index 0000000..f81761b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/hive.go @@ -0,0 +1,388 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "fmt" + "math/rand" + "path/filepath" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/netutil" + "github.com/ethereum/go-ethereum/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// Hive is the logistic manager of the swarm +// it uses a generic kademlia nodetable to find best peer list +// for any target +// this is used by the netstore to search for content in the swarm +// the bzz protocol peersMsgData exchange is relayed to Kademlia +// for db storage and filtering +// connections and disconnections are reported and relayed +// to keep the nodetable uptodate + +type Hive struct { + listenAddr func() string + callInterval uint64 + id discover.NodeID + addr kademlia.Address + kad *kademlia.Kademlia + path string + quit chan bool + toggle chan bool + more chan bool + + // for testing only + swapEnabled bool + syncEnabled bool + blockRead bool + blockWrite bool +} + +const ( + callInterval = 3000000000 + // bucketSize = 3 + // maxProx = 8 + // proxBinSize = 4 +) + +type HiveParams struct { + CallInterval uint64 + KadDbPath string + *kademlia.KadParams +} + +func NewHiveParams(path string) *HiveParams { + kad := kademlia.NewKadParams() + // kad.BucketSize = bucketSize + // kad.MaxProx = maxProx + // kad.ProxBinSize = proxBinSize + + return &HiveParams{ + CallInterval: callInterval, + KadDbPath: filepath.Join(path, "bzz-peers.json"), + KadParams: kad, + } +} + +func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { + kad := kademlia.New(kademlia.Address(addr), params.KadParams) + return &Hive{ + callInterval: params.CallInterval, + kad: kad, + addr: kad.Addr(), + path: params.KadDbPath, + swapEnabled: swapEnabled, + syncEnabled: syncEnabled, + } +} + +func (self *Hive) SyncEnabled(on bool) { + self.syncEnabled = on +} + +func (self *Hive) SwapEnabled(on bool) { + self.swapEnabled = on +} + +func (self *Hive) BlockNetworkRead(on bool) { + self.blockRead = on +} + +func (self *Hive) BlockNetworkWrite(on bool) { + self.blockWrite = on +} + +// public accessor to the hive base address +func (self *Hive) Addr() kademlia.Address { + return self.addr +} + +// Start receives network info only at startup +// listedAddr is a function to retrieve listening address to advertise to peers +// connectPeer is a function to connect to a peer based on its NodeID or enode URL +// there are called on the p2p.Server which runs on the node +func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) { + self.toggle = make(chan bool) + self.more = make(chan bool) + self.quit = make(chan bool) + self.id = id + self.listenAddr = listenAddr + err = self.kad.Load(self.path, nil) + if err != nil { + glog.V(logger.Warn).Infof("Warning: error reading kaddb '%s' (skipping): %v", self.path, err) + err = nil + } + // this loop is doing bootstrapping and maintains a healthy table + go self.keepAlive() + go func() { + // whenever toggled ask kademlia about most preferred peer + for alive := range self.more { + if !alive { + // receiving false closes the loop while allowing parallel routines + // to attempt to write to more (remove Peer when shutting down) + return + } + node, need, proxLimit := self.kad.Suggest() + + if node != nil && len(node.Url) > 0 { + glog.V(logger.Detail).Infof("call known bee %v", node.Url) + // enode or any lower level connection address is unnecessary in future + // discovery table is used to look it up. + connectPeer(node.Url) + } + if need { + // a random peer is taken from the table + peers := self.kad.FindClosest(kademlia.RandomAddressAt(self.addr, rand.Intn(self.kad.MaxProx)), 1) + if len(peers) > 0 { + // a random address at prox bin 0 is sent for lookup + randAddr := kademlia.RandomAddressAt(self.addr, proxLimit) + req := &retrieveRequestMsgData{ + Key: storage.Key(randAddr[:]), + } + glog.V(logger.Detail).Infof("call any bee near %v (PO%03d) - messenger bee: %v", randAddr, proxLimit, peers[0]) + peers[0].(*peer).retrieve(req) + } else { + glog.V(logger.Warn).Infof("no peer") + } + glog.V(logger.Detail).Infof("buzz kept alive") + } else { + glog.V(logger.Info).Infof("no need for more bees") + } + select { + case self.toggle <- need: + case <-self.quit: + return + } + glog.V(logger.Debug).Infof("queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount()) + } + }() + return +} + +// keepAlive is a forever loop +// in its awake state it periodically triggers connection attempts +// by writing to self.more until Kademlia Table is saturated +// wake state is toggled by writing to self.toggle +// it restarts if the table becomes non-full again due to disconnections +func (self *Hive) keepAlive() { + alarm := time.NewTicker(time.Duration(self.callInterval)).C + for { + select { + case <-alarm: + if self.kad.DBCount() > 0 { + select { + case self.more <- true: + glog.V(logger.Debug).Infof("buzz wakeup") + default: + } + } + case need := <-self.toggle: + if alarm == nil && need { + alarm = time.NewTicker(time.Duration(self.callInterval)).C + } + if alarm != nil && !need { + alarm = nil + + } + case <-self.quit: + return + } + } +} + +func (self *Hive) Stop() error { + // closing toggle channel quits the updateloop + close(self.quit) + return self.kad.Save(self.path, saveSync) +} + +// called at the end of a successful protocol handshake +func (self *Hive) addPeer(p *peer) error { + defer func() { + select { + case self.more <- true: + default: + } + }() + glog.V(logger.Detail).Infof("hi new bee %v", p) + err := self.kad.On(p, loadSync) + if err != nil { + return err + } + // self lookup (can be encoded as nil/zero key since peers addr known) + no id () + // the most common way of saying hi in bzz is initiation of gossip + // let me know about anyone new from my hood , here is the storageradius + // to send the 6 byte self lookup + // we do not record as request or forward it, just reply with peers + p.retrieve(&retrieveRequestMsgData{}) + glog.V(logger.Detail).Infof("'whatsup wheresdaparty' sent to %v", p) + + return nil +} + +// called after peer disconnected +func (self *Hive) removePeer(p *peer) { + glog.V(logger.Debug).Infof("bee %v removed", p) + self.kad.Off(p, saveSync) + select { + case self.more <- true: + default: + } + if self.kad.Count() == 0 { + glog.V(logger.Debug).Infof("empty, all bees gone") + } +} + +// Retrieve a list of live peers that are closer to target than us +func (self *Hive) getPeers(target storage.Key, max int) (peers []*peer) { + var addr kademlia.Address + copy(addr[:], target[:]) + for _, node := range self.kad.FindClosest(addr, max) { + peers = append(peers, node.(*peer)) + } + return +} + +// disconnects all the peers +func (self *Hive) DropAll() { + glog.V(logger.Info).Infof("dropping all bees") + for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) { + node.Drop() + } +} + +// contructor for kademlia.NodeRecord based on peer address alone +// TODO: should go away and only addr passed to kademlia +func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord { + now := time.Now() + return &kademlia.NodeRecord{ + Addr: addr.Addr, + Url: addr.String(), + Seen: now, + After: now, + } +} + +// called by the protocol when receiving peerset (for target address) +// peersMsgData is converted to a slice of NodeRecords for Kademlia +// this is to store all thats needed +func (self *Hive) HandlePeersMsg(req *peersMsgData, from *peer) { + var nrs []*kademlia.NodeRecord + for _, p := range req.Peers { + if err := netutil.CheckRelayIP(from.remoteAddr.IP, p.IP); err != nil { + glog.V(logger.Detail).Infof("invalid peer IP %v from %v: %v", from.remoteAddr.IP, p.IP, err) + continue + } + nrs = append(nrs, newNodeRecord(p)) + } + self.kad.Add(nrs) +} + +// peer wraps the protocol instance to represent a connected peer +// it implements kademlia.Node interface +type peer struct { + *bzz // protocol instance running on peer connection +} + +// protocol instance implements kademlia.Node interface (embedded peer) +func (self *peer) Addr() kademlia.Address { + return self.remoteAddr.Addr +} + +func (self *peer) Url() string { + return self.remoteAddr.String() +} + +// TODO take into account traffic +func (self *peer) LastActive() time.Time { + return self.lastActive +} + +// reads the serialised form of sync state persisted as the 'Meta' attribute +// and sets the decoded syncState on the online node +func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error { + p, ok := node.(*peer) + if !ok { + return fmt.Errorf("invalid type") + } + if record.Meta == nil { + glog.V(logger.Debug).Infof("no sync state for node record %v setting default", record) + p.syncState = &syncState{DbSyncState: &storage.DbSyncState{}} + return nil + } + state, err := decodeSync(record.Meta) + if err != nil { + return fmt.Errorf("error decoding kddb record meta info into a sync state: %v", err) + } + glog.V(logger.Detail).Infof("sync state for node record %v read from Meta: %s", record, string(*(record.Meta))) + p.syncState = state + return err +} + +// callback when saving a sync state +func saveSync(record *kademlia.NodeRecord, node kademlia.Node) { + if p, ok := node.(*peer); ok { + meta, err := encodeSync(p.syncState) + if err != nil { + glog.V(logger.Warn).Infof("error saving sync state for %v: %v", node, err) + return + } + glog.V(logger.Detail).Infof("saved sync state for %v: %s", node, string(*meta)) + record.Meta = meta + } +} + +// the immediate response to a retrieve request, +// sends relevant peer data given by the kademlia hive to the requester +// TODO: remember peers sent for duration of the session, only new peers sent +func (self *Hive) peers(req *retrieveRequestMsgData) { + if req != nil && req.MaxPeers >= 0 { + var addrs []*peerAddr + if req.timeout == nil || time.Now().Before(*(req.timeout)) { + key := req.Key + // self lookup from remote peer + if storage.IsZeroKey(key) { + addr := req.from.Addr() + key = storage.Key(addr[:]) + req.Key = nil + } + // get peer addresses from hive + for _, peer := range self.getPeers(key, int(req.MaxPeers)) { + addrs = append(addrs, peer.remoteAddr) + } + glog.V(logger.Debug).Infof("Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %v", len(addrs), req.from, req.Id, req.Key.Log()) + + peersData := &peersMsgData{ + Peers: addrs, + Key: req.Key, + Id: req.Id, + } + peersData.setTimeout(req.timeout) + req.from.peers(peersData) + } + } +} + +func (self *Hive) String() string { + return self.kad.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go new file mode 100644 index 0000000..16c5ce5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "fmt" + "math/rand" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +type Address common.Hash + +func (a Address) String() string { + return fmt.Sprintf("%x", a[:]) +} + +func (a *Address) MarshalJSON() (out []byte, err error) { + return []byte(`"` + a.String() + `"`), nil +} + +func (a *Address) UnmarshalJSON(value []byte) error { + *a = Address(common.HexToHash(string(value[1 : len(value)-1]))) + return nil +} + +// the string form of the binary representation of an address (only first 8 bits) +func (a Address) Bin() string { + var bs []string + for _, b := range a[:] { + bs = append(bs, fmt.Sprintf("%08b", b)) + } + return strings.Join(bs, "") +} + +/* +Proximity(x, y) returns the proximity order of the MSB distance between x and y + +The distance metric MSB(x, y) of two equal length byte sequences x an y is the +value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed. +the binary cast is big endian: most significant bit first (=MSB). + +Proximity(x, y) is a discrete logarithmic scaling of the MSB distance. +It is defined as the reverse rank of the integer part of the base 2 +logarithm of the distance. +It is calculated by counting the number of common leading zeros in the (MSB) +binary representation of the x^y. + +(0 farthest, 255 closest, 256 self) +*/ +func proximity(one, other Address) (ret int) { + for i := 0; i < len(one); i++ { + oxo := one[i] ^ other[i] + for j := 0; j < 8; j++ { + if (uint8(oxo)>>uint8(7-j))&0x01 != 0 { + return i*8 + j + } + } + } + return len(one) * 8 +} + +// Address.ProxCmp compares the distances a->target and b->target. +// Returns -1 if a is closer to target, 1 if b is closer to target +// and 0 if they are equal. +func (target Address) ProxCmp(a, b Address) int { + for i := range target { + da := a[i] ^ target[i] + db := b[i] ^ target[i] + if da > db { + return 1 + } else if da < db { + return -1 + } + } + return 0 +} + +// randomAddressAt(address, prox) generates a random address +// at proximity order prox relative to address +// if prox is negative a random address is generated +func RandomAddressAt(self Address, prox int) (addr Address) { + addr = self + var pos int + if prox >= 0 { + pos = prox / 8 + trans := prox % 8 + transbytea := byte(0) + for j := 0; j <= trans; j++ { + transbytea |= 1 << uint8(7-j) + } + flipbyte := byte(1 << uint8(7-trans)) + transbyteb := transbytea ^ byte(255) + randbyte := byte(rand.Intn(255)) + addr[pos] = ((addr[pos] & transbytea) ^ flipbyte) | randbyte&transbyteb + } + for i := pos + 1; i < len(addr); i++ { + addr[i] = byte(rand.Intn(255)) + } + + return +} + +// KeyRange(a0, a1, proxLimit) returns the address inclusive address +// range that contain addresses closer to one than other +func KeyRange(one, other Address, proxLimit int) (start, stop Address) { + prox := proximity(one, other) + if prox >= proxLimit { + prox = proxLimit + } + start = CommonBitsAddrByte(one, other, byte(0x00), prox) + stop = CommonBitsAddrByte(one, other, byte(0xff), prox) + return +} + +func CommonBitsAddrF(self, other Address, f func() byte, p int) (addr Address) { + prox := proximity(self, other) + var pos int + if p <= prox { + prox = p + } + pos = prox / 8 + addr = self + trans := byte(prox % 8) + var transbytea byte + if p > prox { + transbytea = byte(0x7f) + } else { + transbytea = byte(0xff) + } + transbytea >>= trans + transbyteb := transbytea ^ byte(0xff) + addrpos := addr[pos] + addrpos &= transbyteb + if p > prox { + addrpos ^= byte(0x80 >> trans) + } + addrpos |= transbytea & f() + addr[pos] = addrpos + for i := pos + 1; i < len(addr); i++ { + addr[i] = f() + } + + return +} + +func CommonBitsAddr(self, other Address, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return byte(rand.Intn(255)) }, prox) +} + +func CommonBitsAddrByte(self, other Address, b byte, prox int) (addr Address) { + return CommonBitsAddrF(self, other, func() byte { return b }, prox) +} + +// randomAddressAt() generates a random address +func RandomAddress() Address { + return RandomAddressAt(Address{}, -1) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address_test.go new file mode 100644 index 0000000..c062c8e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/address_test.go @@ -0,0 +1,96 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "math/rand" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func (Address) Generate(rand *rand.Rand, size int) reflect.Value { + var id Address + for i := 0; i < len(id); i++ { + id[i] = byte(uint8(rand.Intn(255))) + } + return reflect.ValueOf(id) +} + +func TestCommonBitsAddrF(t *testing.T) { + a := Address(common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")) + b := Address(common.HexToHash("0x8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")) + c := Address(common.HexToHash("0x4123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")) + d := Address(common.HexToHash("0x0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")) + e := Address(common.HexToHash("0x01A3456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")) + ab := CommonBitsAddrF(a, b, func() byte { return byte(0x00) }, 10) + expab := Address(common.HexToHash("0x8000000000000000000000000000000000000000000000000000000000000000")) + + if ab != expab { + t.Fatalf("%v != %v", ab, expab) + } + ac := CommonBitsAddrF(a, c, func() byte { return byte(0x00) }, 10) + expac := Address(common.HexToHash("0x4000000000000000000000000000000000000000000000000000000000000000")) + + if ac != expac { + t.Fatalf("%v != %v", ac, expac) + } + ad := CommonBitsAddrF(a, d, func() byte { return byte(0x00) }, 10) + expad := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000")) + + if ad != expad { + t.Fatalf("%v != %v", ad, expad) + } + ae := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 10) + expae := Address(common.HexToHash("0x0180000000000000000000000000000000000000000000000000000000000000")) + + if ae != expae { + t.Fatalf("%v != %v", ae, expae) + } + acf := CommonBitsAddrF(a, c, func() byte { return byte(0xff) }, 10) + expacf := Address(common.HexToHash("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + + if acf != expacf { + t.Fatalf("%v != %v", acf, expacf) + } + aeo := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 2) + expaeo := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000")) + + if aeo != expaeo { + t.Fatalf("%v != %v", aeo, expaeo) + } + aep := CommonBitsAddrF(a, e, func() byte { return byte(0xff) }, 2) + expaep := Address(common.HexToHash("0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + + if aep != expaep { + t.Fatalf("%v != %v", aep, expaep) + } + +} + +func TestRandomAddressAt(t *testing.T) { + var a Address + for i := 0; i < 100; i++ { + a = RandomAddress() + prox := rand.Intn(255) + b := RandomAddressAt(a, prox) + if proximity(a, b) != prox { + t.Fatalf("incorrect address prox(%v, %v) == %v (expected %v)", a, b, proximity(a, b), prox) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go new file mode 100644 index 0000000..53a7db4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kaddb.go @@ -0,0 +1,351 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +type NodeData interface { + json.Marshaler + json.Unmarshaler +} + +// allow inactive peers under +type NodeRecord struct { + Addr Address // address of node + Url string // Url, used to connect to node + After time.Time // next call after time + Seen time.Time // last connected at time + Meta *json.RawMessage // arbitrary metadata saved for a peer + + node Node +} + +func (self *NodeRecord) setSeen() { + t := time.Now() + self.Seen = t + self.After = t +} + +func (self *NodeRecord) String() string { + return fmt.Sprintf("<%v>", self.Addr) +} + +// persisted node record database () +type KadDb struct { + Address Address + Nodes [][]*NodeRecord + index map[Address]*NodeRecord + cursors []int + lock sync.RWMutex + purgeInterval time.Duration + initialRetryInterval time.Duration + connRetryExp int +} + +func newKadDb(addr Address, params *KadParams) *KadDb { + return &KadDb{ + Address: addr, + Nodes: make([][]*NodeRecord, params.MaxProx+1), // overwritten by load + cursors: make([]int, params.MaxProx+1), + index: make(map[Address]*NodeRecord), + purgeInterval: params.PurgeInterval, + initialRetryInterval: params.InitialRetryInterval, + connRetryExp: params.ConnRetryExp, + } +} + +func (self *KadDb) findOrCreate(index int, a Address, url string) *NodeRecord { + defer self.lock.Unlock() + self.lock.Lock() + + record, found := self.index[a] + if !found { + record = &NodeRecord{ + Addr: a, + Url: url, + } + glog.V(logger.Info).Infof("add new record %v to kaddb", record) + // insert in kaddb + self.index[a] = record + self.Nodes[index] = append(self.Nodes[index], record) + } else { + glog.V(logger.Info).Infof("found record %v in kaddb", record) + } + // update last seen time + record.setSeen() + // update with url in case IP/port changes + record.Url = url + return record +} + +// add adds node records to kaddb (persisted node record db) +func (self *KadDb) add(nrs []*NodeRecord, proximityBin func(Address) int) { + defer self.lock.Unlock() + self.lock.Lock() + var n int + var nodes []*NodeRecord + for _, node := range nrs { + _, found := self.index[node.Addr] + if !found && node.Addr != self.Address { + node.setSeen() + self.index[node.Addr] = node + index := proximityBin(node.Addr) + dbcursor := self.cursors[index] + nodes = self.Nodes[index] + // this is inefficient for allocation, need to just append then shift + newnodes := make([]*NodeRecord, len(nodes)+1) + copy(newnodes[:], nodes[:dbcursor]) + newnodes[dbcursor] = node + copy(newnodes[dbcursor+1:], nodes[dbcursor:]) + glog.V(logger.Detail).Infof("new nodes: %v (keys: %v)\nnodes: %v", newnodes, nodes) + self.Nodes[index] = newnodes + n++ + } + } + if n > 0 { + glog.V(logger.Debug).Infof("%d/%d node records (new/known)", n, len(nrs)) + } +} + +/* +next return one node record with the highest priority for desired +connection. +This is used to pick candidates for live nodes that are most wanted for +a higly connected low centrality network structure for Swarm which best suits +for a Kademlia-style routing. + +* Starting as naive node with empty db, this implements Kademlia bootstrapping +* As a mature node, it fills short lines. All on demand. + +The candidate is chosen using the following strategy: +We check for missing online nodes in the buckets for 1 upto Max BucketSize rounds. +On each round we proceed from the low to high proximity order buckets. +If the number of active nodes (=connected peers) is < rounds, then start looking +for a known candidate. To determine if there is a candidate to recommend the +kaddb node record database row corresponding to the bucket is checked. + +If the row cursor is on position i, the ith element in the row is chosen. +If the record is scheduled not to be retried before NOW, the next element is taken. +If the record is scheduled to be retried, it is set as checked, scheduled for +checking and is returned. The time of the next check is in X (duration) such that +X = ConnRetryExp * delta where delta is the time past since the last check and +ConnRetryExp is constant obsoletion factor. (Note that when node records are added +from peer messages, they are marked as checked and placed at the cursor, ie. +given priority over older entries). Entries which were checked more than +purgeInterval ago are deleted from the kaddb row. If no candidate is found after +a full round of checking the next bucket up is considered. If no candidate is +found when we reach the maximum-proximity bucket, the next round starts. + +node record a is more favoured to b a > b iff a is a passive node (record of +offline past peer) +|proxBin(a)| < |proxBin(b)| +|| (proxBin(a) < proxBin(b) && |proxBin(a)| == |proxBin(b)|) +|| (proxBin(a) == proxBin(b) && lastChecked(a) < lastChecked(b)) + + +The second argument returned names the first missing slot found +*/ +func (self *KadDb) findBest(maxBinSize int, binSize func(int) int) (node *NodeRecord, need bool, proxLimit int) { + // return nil, proxLimit indicates that all buckets are filled + defer self.lock.Unlock() + self.lock.Lock() + + var interval time.Duration + var found bool + var purge []bool + var delta time.Duration + var cursor int + var count int + var after time.Time + + // iterate over columns maximum bucketsize times + for rounds := 1; rounds <= maxBinSize; rounds++ { + ROUND: + // iterate over rows from PO 0 upto MaxProx + for po, dbrow := range self.Nodes { + // if row has rounds connected peers, then take the next + if binSize(po) >= rounds { + continue ROUND + } + if !need { + // set proxlimit to the PO where the first missing slot is found + proxLimit = po + need = true + } + purge = make([]bool, len(dbrow)) + + // there is a missing slot - finding a node to connect to + // select a node record from the relavant kaddb row (of identical prox order) + ROW: + for cursor = self.cursors[po]; !found && count < len(dbrow); cursor = (cursor + 1) % len(dbrow) { + count++ + node = dbrow[cursor] + + // skip already connected nodes + if node.node != nil { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d/%d) already connected", node.Addr, po, cursor, len(dbrow)) + continue ROW + } + + // if node is scheduled to connect + if time.Time(node.After).After(time.Now()) { + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) skipped. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + continue ROW + } + + delta = time.Since(time.Time(node.Seen)) + if delta < self.initialRetryInterval { + delta = self.initialRetryInterval + } + if delta > self.purgeInterval { + // remove node + purge[cursor] = true + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) unreachable since %v. Removed", node.Addr, po, cursor, node.Seen) + continue ROW + } + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) ready to be tried. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) + + // scheduling next check + interval = time.Duration(delta * time.Duration(self.connRetryExp)) + after = time.Now().Add(interval) + + glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) selected as candidate connection %v. seen at %v (%v ago), selectable since %v, retry after %v (in %v)", node.Addr, po, cursor, rounds, node.Seen, delta, node.After, after, interval) + node.After = after + found = true + } // ROW + self.cursors[po] = cursor + self.delete(po, purge) + if found { + return node, need, proxLimit + } + } // ROUND + } // ROUNDS + + return nil, need, proxLimit +} + +// deletes the noderecords of a kaddb row corresponding to the indexes +// caller must hold the dblock +// the call is unsafe, no index checks +func (self *KadDb) delete(row int, purge []bool) { + var nodes []*NodeRecord + dbrow := self.Nodes[row] + for i, del := range purge { + if i == self.cursors[row] { + //reset cursor + self.cursors[row] = len(nodes) + } + // delete the entry to be purged + if del { + delete(self.index, dbrow[i].Addr) + continue + } + // otherwise append to new list + nodes = append(nodes, dbrow[i]) + } + self.Nodes[row] = nodes +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *KadDb) save(path string, cb func(*NodeRecord, Node)) error { + defer self.lock.Unlock() + self.lock.Lock() + + var n int + + for _, b := range self.Nodes { + for _, node := range b { + n++ + node.After = time.Now() + node.Seen = time.Now() + if cb != nil { + cb(node, node.node) + } + } + } + + data, err := json.MarshalIndent(self, "", " ") + if err != nil { + return err + } + err = ioutil.WriteFile(path, data, os.ModePerm) + if err != nil { + glog.V(logger.Warn).Infof("unable to save kaddb with %v nodes to %v: err", n, path, err) + } else { + glog.V(logger.Info).Infof("saved kaddb with %v nodes to %v", n, path) + } + return err +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err error) { + defer self.lock.Unlock() + self.lock.Lock() + + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return + } + + err = json.Unmarshal(data, self) + if err != nil { + return + } + var n int + var purge []bool + for po, b := range self.Nodes { + purge = make([]bool, len(b)) + ROW: + for i, node := range b { + if cb != nil { + err = cb(node, node.node) + if err != nil { + purge[i] = true + continue ROW + } + } + n++ + if (node.After == time.Time{}) { + node.After = time.Now() + } + self.index[node.Addr] = node + } + self.delete(po, purge) + } + glog.V(logger.Info).Infof("loaded kaddb with %v nodes from %v", n, path) + + return +} + +// accessor for KAD offline db count +func (self *KadDb) count() int { + defer self.lock.Unlock() + self.lock.Lock() + return len(self.index) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go new file mode 100644 index 0000000..87c57ce --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia.go @@ -0,0 +1,429 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "fmt" + "sort" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + bucketSize = 4 + proxBinSize = 2 + maxProx = 8 + connRetryExp = 2 + maxPeers = 100 +) + +var ( + purgeInterval = 42 * time.Hour + initialRetryInterval = 42 * time.Millisecond + maxIdleInterval = 42 * 1000 * time.Millisecond + // maxIdleInterval = 42 * 10 0 * time.Millisecond +) + +type KadParams struct { + // adjustable parameters + MaxProx int + ProxBinSize int + BucketSize int + PurgeInterval time.Duration + InitialRetryInterval time.Duration + MaxIdleInterval time.Duration + ConnRetryExp int +} + +func NewKadParams() *KadParams { + return &KadParams{ + MaxProx: maxProx, + ProxBinSize: proxBinSize, + BucketSize: bucketSize, + PurgeInterval: purgeInterval, + InitialRetryInterval: initialRetryInterval, + MaxIdleInterval: maxIdleInterval, + ConnRetryExp: connRetryExp, + } +} + +// Kademlia is a table of active nodes +type Kademlia struct { + addr Address // immutable baseaddress of the table + *KadParams // Kademlia configuration parameters + proxLimit int // state, the PO of the first row of the most proximate bin + proxSize int // state, the number of peers in the most proximate bin + count int // number of active peers (w live connection) + buckets [][]Node // the actual bins + db *KadDb // kaddb, node record database + lock sync.RWMutex // mutex to access buckets +} + +type Node interface { + Addr() Address + Url() string + LastActive() time.Time + Drop() +} + +// public constructor +// add is the base address of the table +// params is KadParams configuration +func New(addr Address, params *KadParams) *Kademlia { + buckets := make([][]Node, params.MaxProx+1) + return &Kademlia{ + addr: addr, + KadParams: params, + buckets: buckets, + db: newKadDb(addr, params), + } +} + +// accessor for KAD base address +func (self *Kademlia) Addr() Address { + return self.addr +} + +// accessor for KAD active node count +func (self *Kademlia) Count() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.count +} + +// accessor for KAD active node count +func (self *Kademlia) DBCount() int { + return self.db.count() +} + +// On is the entry point called when a new nodes is added +// unsafe in that node is not checked to be already active node (to be called once) +func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) { + glog.V(logger.Warn).Infof("%v", self) + defer self.lock.Unlock() + self.lock.Lock() + + index := self.proximityBin(node.Addr()) + record := self.db.findOrCreate(index, node.Addr(), node.Url()) + + if cb != nil { + err = cb(record, node) + glog.V(logger.Detail).Infof("cb(%v, %v) ->%v", record, node, err) + if err != nil { + return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err) + } + glog.V(logger.Debug).Infof("add node record %v with node %v", record, node) + } + + // insert in kademlia table of active nodes + bucket := self.buckets[index] + // if bucket is full insertion replaces the worst node + // TODO: give priority to peers with active traffic + if len(bucket) < self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation + self.buckets[index] = append(bucket, node) + glog.V(logger.Debug).Infof("add node %v to table", node) + self.setProxLimit(index, true) + record.node = node + self.count++ + return nil + } + + // always rotate peers + idle := self.MaxIdleInterval + var pos int + var replaced Node + for i, p := range bucket { + idleInt := time.Since(p.LastActive()) + if idleInt > idle { + idle = idleInt + pos = i + replaced = p + } + } + if replaced == nil { + glog.V(logger.Debug).Infof("all peers wanted, PO%03d bucket full", index) + return fmt.Errorf("bucket full") + } + glog.V(logger.Debug).Infof("node %v replaced by %v (idle for %v > %v)", replaced, node, idle, self.MaxIdleInterval) + replaced.Drop() + // actually replace in the row. When off(node) is called, the peer is no longer in the row + bucket[pos] = node + // there is no change in bucket cardinalities so no prox limit adjustment is needed + record.node = node + self.count++ + return nil + +} + +// Off is the called when a node is taken offline (from the protocol main loop exit) +func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) { + self.lock.Lock() + defer self.lock.Unlock() + + index := self.proximityBin(node.Addr()) + bucket := self.buckets[index] + for i := 0; i < len(bucket); i++ { + if node.Addr() == bucket[i].Addr() { + self.buckets[index] = append(bucket[:i], bucket[(i+1):]...) + self.setProxLimit(index, false) + break + } + } + + record := self.db.index[node.Addr()] + // callback on remove + if cb != nil { + cb(record, record.node) + } + record.node = nil + self.count-- + glog.V(logger.Debug).Infof("remove node %v from table, population now is %v", node, self.count) + + return +} + +// proxLimit is dynamically adjusted so that +// 1) there is no empty buckets in bin < proxLimit and +// 2) the sum of all items are the minimum possible but higher than ProxBinSize +// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes) +// caller holds the lock +func (self *Kademlia) setProxLimit(r int, on bool) { + // if the change is outside the core (PO lower) + // and the change does not leave a bucket empty then + // no adjustment needed + if r < self.proxLimit && len(self.buckets[r]) > 0 { + return + } + // if on=a node was added, then r must be within prox limit so increment cardinality + if on { + self.proxSize++ + curr := len(self.buckets[self.proxLimit]) + // if now core is big enough without the furthest bucket, then contract + // this can result in more than one bucket change + for self.proxSize >= self.ProxBinSize+curr && curr > 0 { + self.proxSize -= curr + self.proxLimit++ + curr = len(self.buckets[self.proxLimit]) + + glog.V(logger.Detail).Infof("proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } + return + } + // otherwise + if r >= self.proxLimit { + self.proxSize-- + } + // expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality + for (self.proxSize < self.ProxBinSize || r < self.proxLimit) && + self.proxLimit > 0 { + // + self.proxLimit-- + self.proxSize += len(self.buckets[self.proxLimit]) + glog.V(logger.Detail).Infof("proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r) + } +} + +/* +returns the list of nodes belonging to the same proximity bin +as the target. The most proximate bin will be the union of the bins between +proxLimit and MaxProx. +*/ +func (self *Kademlia) FindClosest(target Address, max int) []Node { + self.lock.Lock() + defer self.lock.Unlock() + + r := nodesByDistance{ + target: target, + } + + po := self.proximityBin(target) + index := po + step := 1 + glog.V(logger.Detail).Infof("serving %v nodes at %v (PO%02d)", max, index, po) + + // if max is set to 0, just want a full bucket, dynamic number + min := max + // set limit to max + limit := max + if max == 0 { + min = 1 + limit = maxPeers + } + + var n int + for index >= 0 { + // add entire bucket + for _, p := range self.buckets[index] { + r.push(p, limit) + n++ + } + // terminate if index reached the bottom or enough peers > min + glog.V(logger.Detail).Infof("add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po) + if n >= min && (step < 0 || max == 0) { + break + } + // reach top most non-empty PO bucket, turn around + if index == self.MaxProx { + index = po + step = -1 + } + index += step + } + glog.V(logger.Detail).Infof("serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po) + return r.nodes +} + +func (self *Kademlia) Suggest() (*NodeRecord, bool, int) { + defer self.lock.RUnlock() + self.lock.RLock() + return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) }) +} + +// adds node records to kaddb (persisted node record db) +func (self *Kademlia) Add(nrs []*NodeRecord) { + self.db.add(nrs, self.proximityBin) +} + +// nodesByDistance is a list of nodes, ordered by distance to target. +type nodesByDistance struct { + nodes []Node + target Address +} + +func sortedByDistanceTo(target Address, slice []Node) bool { + var last Address + for i, node := range slice { + if i > 0 { + if target.ProxCmp(node.Addr(), last) < 0 { + return false + } + } + last = node.Addr() + } + return true +} + +// push(node, max) adds the given node to the list, keeping the total size +// below max elements. +func (h *nodesByDistance) push(node Node, max int) { + // returns the firt index ix such that func(i) returns true + ix := sort.Search(len(h.nodes), func(i int) bool { + return h.target.ProxCmp(h.nodes[i].Addr(), node.Addr()) >= 0 + }) + + if len(h.nodes) < max { + h.nodes = append(h.nodes, node) + } + if ix < len(h.nodes) { + copy(h.nodes[ix+1:], h.nodes[ix:]) + h.nodes[ix] = node + } +} + +/* +Taking the proximity order relative to a fix point x classifies the points in +the space (n byte long byte sequences) into bins. Items in each are at +most half as distant from x as items in the previous bin. Given a sample of +uniformly distributed items (a hash function over arbitrary sequence) the +proximity scale maps onto series of subsets with cardinalities on a negative +exponential scale. + +It also has the property that any two item belonging to the same bin are at +most half as distant from each other as they are from x. + +If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local +decisions for graph traversal where the task is to find a route between two +points. Since in every hop, the finite distance halves, there is +a guaranteed constant maximum limit on the number of hops needed to reach one +node from the other. +*/ + +func (self *Kademlia) proximityBin(other Address) (ret int) { + ret = proximity(self.addr, other) + if ret > self.MaxProx { + ret = self.MaxProx + } + return +} + +// provides keyrange for chunk db iteration +func (self *Kademlia) KeyRange(other Address) (start, stop Address) { + defer self.lock.RUnlock() + self.lock.RLock() + return KeyRange(self.addr, other, self.proxLimit) +} + +// save persists kaddb on disk (written to file on path in json format. +func (self *Kademlia) Save(path string, cb func(*NodeRecord, Node)) error { + return self.db.save(path, cb) +} + +// Load(path) loads the node record database (kaddb) from file on path. +func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err error) { + return self.db.load(path, cb) +} + +// kademlia table + kaddb table displayed with ascii +func (self *Kademlia) String() string { + defer self.lock.RUnlock() + self.lock.RLock() + defer self.db.lock.RUnlock() + self.db.lock.RLock() + + var rows []string + rows = append(rows, "=========================================================================") + rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6])) + rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize)) + rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize)) + + for i, bucket := range self.buckets { + + if i == self.proxLimit { + rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) + } + row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))} + var k int + c := self.db.cursors[i] + for ; k < len(bucket); k++ { + p := bucket[(c+k)%len(bucket)] + row = append(row, p.Addr().String()[:6]) + if k == 4 { + break + } + } + for ; k < 4; k++ { + row = append(row, " ") + } + row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i])) + + for j, p := range self.db.Nodes[i] { + row = append(row, p.Addr.String()[:6]) + if j == 3 { + break + } + } + rows = append(rows, strings.Join(row, " ")) + if i == self.MaxProx { + } + } + rows = append(rows, "=========================================================================") + return strings.Join(rows, "\n") +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia_test.go new file mode 100644 index 0000000..417ccec --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/kademlia/kademlia_test.go @@ -0,0 +1,392 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package kademlia + +import ( + "fmt" + "math" + "math/rand" + "os" + "path/filepath" + "reflect" + "testing" + "testing/quick" + "time" +) + +var ( + quickrand = rand.New(rand.NewSource(time.Now().Unix())) + quickcfgFindClosest = &quick.Config{MaxCount: 50, Rand: quickrand} + quickcfgBootStrap = &quick.Config{MaxCount: 100, Rand: quickrand} +) + +type testNode struct { + addr Address +} + +func (n *testNode) String() string { + return fmt.Sprintf("%x", n.addr[:]) +} + +func (n *testNode) Addr() Address { + return n.addr +} + +func (n *testNode) Drop() { +} + +func (n *testNode) Url() string { + return "" +} + +func (n *testNode) LastActive() time.Time { + return time.Now() +} + +func TestOn(t *testing.T) { + addr, ok1 := gen(Address{}, quickrand).(Address) + other, ok2 := gen(Address{}, quickrand).(Address) + if !ok1 || !ok2 { + t.Errorf("oops") + } + kad := New(addr, NewKadParams()) + err := kad.On(&testNode{addr: other}, nil) + _ = err +} + +func TestBootstrap(t *testing.T) { + + test := func(test *bootstrapTest) bool { + // for any node kad.le, Target and N + params := NewKadParams() + params.MaxProx = test.MaxProx + params.BucketSize = test.BucketSize + params.ProxBinSize = test.BucketSize + kad := New(test.Self, params) + var err error + + for p := 0; p < 9; p++ { + var nrs []*NodeRecord + n := math.Pow(float64(2), float64(7-p)) + for i := 0; i < int(n); i++ { + addr := RandomAddressAt(test.Self, p) + nrs = append(nrs, &NodeRecord{ + Addr: addr, + }) + } + kad.Add(nrs) + } + + node := &testNode{test.Self} + + n := 0 + for n < 100 { + err = kad.On(node, nil) + if err != nil { + t.Fatalf("backend not accepting node: %v", err) + } + + record, need, _ := kad.Suggest() + if !need { + break + } + n++ + if record == nil { + continue + } + node = &testNode{record.Addr} + } + exp := test.BucketSize * (test.MaxProx + 1) + if kad.Count() != exp { + t.Errorf("incorrect number of peers, expected %d, got %d\n%v", exp, kad.Count(), kad) + return false + } + return true + } + if err := quick.Check(test, quickcfgBootStrap); err != nil { + t.Error(err) + } + +} + +func TestFindClosest(t *testing.T) { + + test := func(test *FindClosestTest) bool { + // for any node kad.le, Target and N + params := NewKadParams() + params.MaxProx = 7 + kad := New(test.Self, params) + var err error + for _, node := range test.All { + err = kad.On(node, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("backend not accepting node: %v", err) + } + } + + if len(test.All) == 0 || test.N == 0 { + return true + } + nodes := kad.FindClosest(test.Target, test.N) + + // check that the number of results is min(N, kad.len) + wantN := test.N + if tlen := kad.Count(); tlen < test.N { + wantN = tlen + } + + if len(nodes) != wantN { + t.Errorf("wrong number of nodes: got %d, want %d", len(nodes), wantN) + return false + } + + if hasDuplicates(nodes) { + t.Errorf("result contains duplicates") + return false + } + + if !sortedByDistanceTo(test.Target, nodes) { + t.Errorf("result is not sorted by distance to target") + return false + } + + // check that the result nodes have minimum distance to target. + farthestResult := nodes[len(nodes)-1].Addr() + for i, b := range kad.buckets { + for j, n := range b { + if contains(nodes, n.Addr()) { + continue // don't run the check below for nodes in result + } + if test.Target.ProxCmp(n.Addr(), farthestResult) < 0 { + _ = i * j + t.Errorf("kad.le contains node that is closer to target but it's not in result") + return false + } + } + } + return true + } + if err := quick.Check(test, quickcfgFindClosest); err != nil { + t.Error(err) + } +} + +type proxTest struct { + add bool + index int + addr Address +} + +var ( + addresses []Address +) + +func TestProxAdjust(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + self := gen(Address{}, r).(Address) + params := NewKadParams() + params.MaxProx = 7 + kad := New(self, params) + + var err error + for i := 0; i < 100; i++ { + a := gen(Address{}, r).(Address) + addresses = append(addresses, a) + err = kad.On(&testNode{addr: a}, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("backend not accepting node: %v", err) + } + if !kad.proxCheck(t) { + return + } + } + test := func(test *proxTest) bool { + node := &testNode{test.addr} + if test.add { + kad.On(node, nil) + } else { + kad.Off(node, nil) + } + return kad.proxCheck(t) + } + if err := quick.Check(test, quickcfgFindClosest); err != nil { + t.Error(err) + } +} + +func TestSaveLoad(t *testing.T) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + addresses := gen([]Address{}, r).([]Address) + self := RandomAddress() + params := NewKadParams() + params.MaxProx = 7 + kad := New(self, params) + + var err error + + for _, a := range addresses { + err = kad.On(&testNode{addr: a}, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("backend not accepting node: %v", err) + } + } + nodes := kad.FindClosest(self, 100) + + path := filepath.Join(os.TempDir(), "bzz-kad-test-save-load.peers") + err = kad.Save(path, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("unepected error saving kaddb: %v", err) + } + kad = New(self, params) + err = kad.Load(path, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("unepected error loading kaddb: %v", err) + } + for _, b := range kad.db.Nodes { + for _, node := range b { + err = kad.On(&testNode{node.Addr}, nil) + if err != nil && err.Error() != "bucket full" { + t.Fatalf("backend not accepting node: %v", err) + } + } + } + loadednodes := kad.FindClosest(self, 100) + for i, node := range loadednodes { + if nodes[i].Addr() != node.Addr() { + t.Errorf("node mismatch at %d/%d: %v != %v", i, len(nodes), nodes[i].Addr(), node.Addr()) + } + } +} + +func (self *Kademlia) proxCheck(t *testing.T) bool { + var sum int + for i, b := range self.buckets { + l := len(b) + // if we are in the high prox multibucket + if i >= self.proxLimit { + sum += l + } else if l == 0 { + t.Errorf("bucket %d empty, yet proxLimit is %d\n%v", len(b), self.proxLimit, self) + return false + } + } + // check if merged high prox bucket does not exceed size + if sum > 0 { + if sum != self.proxSize { + t.Errorf("proxSize incorrect, expected %v, got %v", sum, self.proxSize) + return false + } + last := len(self.buckets[self.proxLimit]) + if last > 0 && sum >= self.ProxBinSize+last { + t.Errorf("proxLimit %v incorrect, redundant non-empty bucket %d added to proxBin with %v (target %v)\n%v", self.proxLimit, last, sum-last, self.ProxBinSize, self) + return false + } + if self.proxLimit > 0 && sum < self.ProxBinSize { + t.Errorf("proxLimit %v incorrect. proxSize %v is less than target %v, yet there is more peers", self.proxLimit, sum, self.ProxBinSize) + return false + } + } + return true +} + +type bootstrapTest struct { + MaxProx int + BucketSize int + Self Address +} + +func (*bootstrapTest) Generate(rand *rand.Rand, size int) reflect.Value { + t := &bootstrapTest{ + Self: gen(Address{}, rand).(Address), + MaxProx: 5 + rand.Intn(2), + BucketSize: rand.Intn(3) + 1, + } + return reflect.ValueOf(t) +} + +type FindClosestTest struct { + Self Address + Target Address + All []Node + N int +} + +func (c FindClosestTest) String() string { + return fmt.Sprintf("A: %064x\nT: %064x\n(%d)\n", c.Self[:], c.Target[:], c.N) +} + +func (*FindClosestTest) Generate(rand *rand.Rand, size int) reflect.Value { + t := &FindClosestTest{ + Self: gen(Address{}, rand).(Address), + Target: gen(Address{}, rand).(Address), + N: rand.Intn(bucketSize), + } + for _, a := range gen([]Address{}, rand).([]Address) { + t.All = append(t.All, &testNode{addr: a}) + } + return reflect.ValueOf(t) +} + +func (*proxTest) Generate(rand *rand.Rand, size int) reflect.Value { + var add bool + if rand.Intn(1) == 0 { + add = true + } + var t *proxTest + if add { + t = &proxTest{ + addr: gen(Address{}, rand).(Address), + add: add, + } + } else { + t = &proxTest{ + index: rand.Intn(len(addresses)), + add: add, + } + } + return reflect.ValueOf(t) +} + +func hasDuplicates(slice []Node) bool { + seen := make(map[Address]bool) + for _, node := range slice { + if seen[node.Addr()] { + return true + } + seen[node.Addr()] = true + } + return false +} + +func contains(nodes []Node, addr Address) bool { + for _, n := range nodes { + if n.Addr() == addr { + return true + } + } + return false +} + +// gen wraps quick.Value so it's easier to use. +// it generates a random value of the given value's type. +func gen(typ interface{}, rand *rand.Rand) interface{} { + v, ok := quick.Value(reflect.TypeOf(typ), rand) + if !ok { + panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) + } + return v.Interface() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go new file mode 100644 index 0000000..d3858c4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/messages.go @@ -0,0 +1,317 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "fmt" + "net" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/swarm/network/kademlia" + "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +/* +BZZ protocol Message Types and Message Data Types +*/ + +// bzz protocol message codes +const ( + statusMsg = iota // 0x01 + storeRequestMsg // 0x02 + retrieveRequestMsg // 0x03 + peersMsg // 0x04 + syncRequestMsg // 0x05 + deliveryRequestMsg // 0x06 + unsyncedKeysMsg // 0x07 + paymentMsg // 0x08 +) + +/* + Handshake + +* Version: 8 byte integer version of the protocol +* ID: arbitrary byte sequence client identifier human readable +* Addr: the address advertised by the node, format similar to DEVp2p wire protocol +* Swap: info for the swarm accounting protocol +* NetworkID: 8 byte integer network identifier +* Caps: swarm-specific capabilities, format identical to devp2p +* SyncState: syncronisation state (db iterator key and address space etc) persisted about the peer + +*/ +type statusMsgData struct { + Version uint64 + ID string + Addr *peerAddr + Swap *swap.SwapProfile + NetworkId uint64 +} + +func (self *statusMsgData) String() string { + return fmt.Sprintf("Status: Version: %v, ID: %v, Addr: %v, Swap: %v, NetworkId: %v", self.Version, self.ID, self.Addr, self.Swap, self.NetworkId) +} + +/* + store requests are forwarded to the peers in their kademlia proximity bin + if they are distant + if they are within our storage radius or have any incentive to store it + then attach your nodeID to the metadata + if the storage request is sufficiently close (within our proxLimit, i. e., the + last row of the routing table) +*/ +type storeRequestMsgData struct { + Key storage.Key // hash of datasize | data + SData []byte // the actual chunk Data + // optional + Id uint64 // request ID. if delivery, the ID is retrieve request ID + requestTimeout *time.Time // expiry for forwarding - [not serialised][not currently used] + storageTimeout *time.Time // expiry of content - [not serialised][not currently used] + from *peer // [not serialised] protocol registers the requester +} + +func (self storeRequestMsgData) String() string { + var from string + if self.from == nil { + from = "self" + } else { + from = self.from.Addr().String() + } + end := len(self.SData) + if len(self.SData) > 10 { + end = 10 + } + return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:end]) +} + +/* +Retrieve request + +Timeout in milliseconds. Note that zero timeout retrieval requests do not request forwarding, but prompt for a peers message response. therefore they serve also +as messages to retrieve peers. + +MaxSize specifies the maximum size that the peer will accept. This is useful in +particular if we allow storage and delivery of multichunk payload representing +the entire or partial subtree unfolding from the requested root key. +So when only interested in limited part of a stream (infinite trees) or only +testing chunk availability etc etc, we can indicate it by limiting the size here. + +Request ID can be newly generated or kept from the request originator. +If request ID Is missing or zero, the request is handled as a lookup only +prompting a peers response but not launching a search. Lookup requests are meant +to be used to bootstrap kademlia tables. + +In the special case that the key is the zero value as well, the remote peer's +address is assumed (the message is to be handled as a self lookup request). +The response is a PeersMsg with the peers in the kademlia proximity bin +corresponding to the address. +*/ + +type retrieveRequestMsgData struct { + Key storage.Key // target Key address of chunk to be retrieved + Id uint64 // request id, request is a lookup if missing or zero + MaxSize uint64 // maximum size of delivery accepted + MaxPeers uint64 // maximum number of peers returned + Timeout uint64 // the longest time we are expecting a response + timeout *time.Time // [not serialied] + from *peer // +} + +func (self retrieveRequestMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, MaxSize: %v, MaxPeers: %d", from, target, self.Id, self.MaxSize, self.MaxPeers) +} + +// lookups are encoded by missing request ID +func (self retrieveRequestMsgData) isLookup() bool { + return self.Id == 0 +} + +// sets timeout fields +func (self retrieveRequestMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self retrieveRequestMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +// peerAddr is sent in StatusMsg as part of the handshake +type peerAddr struct { + IP net.IP + Port uint16 + ID []byte // the 64 byte NodeID (ECDSA Public Key) + Addr kademlia.Address +} + +// peerAddr pretty prints as enode +func (self peerAddr) String() string { + var nodeid discover.NodeID + copy(nodeid[:], self.ID) + return discover.NewNode(nodeid, self.IP, 0, self.Port).String() +} + +/* +peers Msg is one response to retrieval; it is always encouraged after a retrieval +request to respond with a list of peers in the same kademlia proximity bin. +The encoding of a peer is identical to that in the devp2p base protocol peers +messages: [IP, Port, NodeID] +note that a node's DPA address is not the NodeID but the hash of the NodeID. + +Timeout serves to indicate whether the responder is forwarding the query within +the timeout or not. + +NodeID serves as the owner of payment contracts and signer of proofs of transfer. + +The Key is the target (if response to a retrieval request) or missing (zero value) +peers address (hash of NodeID) if retrieval request was a self lookup. + +Peers message is requested by retrieval requests with a missing or zero value request ID +*/ +type peersMsgData struct { + Peers []*peerAddr // + Timeout uint64 // + timeout *time.Time // indicate whether responder is expected to deliver content + Key storage.Key // present if a response to a retrieval request + Id uint64 // present if a response to a retrieval request + from *peer +} + +// peers msg pretty printer +func (self peersMsgData) String() string { + var from string + if self.from == nil { + from = "ourselves" + } else { + from = self.from.Addr().String() + } + var target []byte + if len(self.Key) > 3 { + target = self.Key[:4] + } + return fmt.Sprintf("from: %v, Key: %x; ID: %v, Peers: %v", from, target, self.Id, self.Peers) +} + +func (self peersMsgData) setTimeout(t *time.Time) { + self.timeout = t + if t != nil { + self.Timeout = uint64(t.UnixNano()) + } else { + self.Timeout = 0 + } +} + +func (self peersMsgData) getTimeout() (t *time.Time) { + if self.Timeout > 0 && self.timeout == nil { + timeout := time.Unix(int64(self.Timeout), 0) + t = &timeout + self.timeout = t + } + return +} + +/* +syncRequest + +is sent after the handshake to initiate syncing +the syncState of the remote node is persisted in kaddb and set on the +peer/protocol instance when the node is registered by hive as online{ +*/ + +type syncRequestMsgData struct { + SyncState *syncState `rlp:"nil"` +} + +func (self *syncRequestMsgData) String() string { + return fmt.Sprintf("%v", self.SyncState) +} + +/* +deliveryRequest + +is sent once a batch of sync keys is filtered. The ones not found are +sent as a list of syncReuest (hash, priority) in the Deliver field. +When the source receives the sync request it continues to iterate +and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type deliveryRequestMsgData struct { + Deliver []*syncRequest +} + +func (self *deliveryRequestMsgData) String() string { + return fmt.Sprintf("sync request for new chunks\ndelivery request for %v chunks", len(self.Deliver)) +} + +/* +unsyncedKeys + +is sent first after the handshake if SyncState iterator brings up hundreds, thousands? +and subsequently sent as a response to deliveryRequestMsgData. + +Syncing is the iterative process of exchanging unsyncedKeys and deliveryRequestMsgs +both ways. + +State contains the sync state sent by the source. When the source receives the +sync state it continues to iterate and fetch at most N items as yet unsynced. +At the same time responds with deliveries of the items. +*/ +type unsyncedKeysMsgData struct { + Unsynced []*syncRequest + State *syncState +} + +func (self *unsyncedKeysMsgData) String() string { + return fmt.Sprintf("sync: keys of %d new chunks (state %v) => synced: %v", len(self.Unsynced), self.State, self.State.Synced) +} + +/* +payment + +is sent when the swap balance is tilted in favour of the remote peer +and in absolute units exceeds the PayAt parameter in the remote peer's profile +*/ + +type paymentMsgData struct { + Units uint // units actually paid for (checked against amount by swap) + Promise *chequebook.Cheque // payment with cheque +} + +func (self *paymentMsgData) String() string { + return fmt.Sprintf("payment for %d units: %v", self.Units, self.Promise) +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go new file mode 100644 index 0000000..763fb0b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol.go @@ -0,0 +1,551 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +/* +bzz implements the swarm wire protocol [bzz] (sister of eth and shh) +the protocol instance is launched on each peer by the network layer if the +bzz protocol handler is registered on the p2p server. + +The bzz protocol component speaks the bzz protocol +* handle the protocol handshake +* register peers in the KΛÐΞMLIΛ table via the hive logistic manager +* dispatch to hive for handling the DHT logic +* encode and decode requests for storage and retrieval +* handle sync protocol messages via the syncer +* talks the SWAP payment protocol (swap accounting is done within NetStore) +*/ + +import ( + "fmt" + "net" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/errs" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +const ( + Version = 0 + ProtocolLength = uint64(8) + ProtocolMaxMsgSize = 10 * 1024 * 1024 + NetworkId = 3 +) + +const ( + ErrMsgTooLarge = iota + ErrDecode + ErrInvalidMsgCode + ErrVersionMismatch + ErrNetworkIdMismatch + ErrNoStatusMsg + ErrExtraStatusMsg + ErrSwap + ErrSync + ErrUnwanted +) + +var errorToString = map[int]string{ + ErrMsgTooLarge: "Message too long", + ErrDecode: "Invalid message", + ErrInvalidMsgCode: "Invalid message code", + ErrVersionMismatch: "Protocol version mismatch", + ErrNetworkIdMismatch: "NetworkId mismatch", + ErrNoStatusMsg: "No status message", + ErrExtraStatusMsg: "Extra status message", + ErrSwap: "SWAP error", + ErrSync: "Sync error", + ErrUnwanted: "Unwanted peer", +} + +// bzz represents the swarm wire protocol +// an instance is running on each peer +type bzz struct { + selfID discover.NodeID // peer's node id used in peer advertising in handshake + key storage.Key // baseaddress as storage.Key + storage StorageHandler // handler storage/retrieval related requests coming via the bzz wire protocol + hive *Hive // the logistic manager, peerPool, routing service and peer handler + dbAccess *DbAccess // access to db storage counter and iterator for syncing + requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing + remoteAddr *peerAddr // remote peers address + peer *p2p.Peer // the p2p peer object + rw p2p.MsgReadWriter // messageReadWriter to send messages to + errors *errs.Errors // errors table + backend chequebook.Backend + lastActive time.Time + NetworkId uint64 + + swap *swap.Swap // swap instance for the peer connection + swapParams *bzzswap.SwapParams // swap settings both local and remote + swapEnabled bool // flag to enable SWAP (will be set via Caps in handshake) + syncEnabled bool // flag to enable SYNC (will be set via Caps in handshake) + syncer *syncer // syncer instance for the peer connection + syncParams *SyncParams // syncer params + syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter) +} + +// interface type for handler of storage/retrieval related requests coming +// via the bzz wire protocol +// messages: UnsyncedKeys, DeliveryRequest, StoreRequest, RetrieveRequest +type StorageHandler interface { + HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error + HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error + HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) + HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer) +} + +/* +main entrypoint, wrappers starting a server that will run the bzz protocol +use this constructor to attach the protocol ("class") to server caps +This is done by node.Node#Register(func(node.ServiceContext) (Service, error)) +Service implements Protocols() which is an array of protocol constructors +at node startup the protocols are initialised +the Dev p2p layer then calls Run(p *p2p.Peer, rw p2p.MsgReadWriter) error +on each peer connection +The Run function of the Bzz protocol class creates a bzz instance +which will represent the peer for the swarm hive and all peer-aware components +*/ +func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64) (p2p.Protocol, error) { + + // a single global request db is created for all peer connections + // this is to persist delivery backlog and aid syncronisation + requestDb, err := storage.NewLDBDatabase(sy.RequestDbPath) + if err != nil { + return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err) + } + if networkId == 0 { + networkId = NetworkId + } + return p2p.Protocol{ + Name: "bzz", + Version: Version, + Length: ProtocolLength, + Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { + return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, networkId, p, rw) + }, + }, nil +} + +/* +the main protocol loop that + * does the handshake by exchanging statusMsg + * if peer is valid and accepted, registers with the hive + * then enters into a forever loop handling incoming messages + * storage and retrieval related queries coming via bzz are dispatched to StorageHandler + * peer-related messages are dispatched to the hive + * payment related messages are relayed to SWAP service + * on disconnect, unregister the peer in the hive (note RemovePeer in the post-disconnect hook) + * whenever the loop terminates, the peer will disconnect with Subprotocol error + * whenever handlers return an error the loop terminates +*/ +func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) { + + self := &bzz{ + storage: depo, + backend: backend, + hive: hive, + dbAccess: dbaccess, + requestDb: requestDb, + peer: p, + rw: rw, + errors: &errs.Errors{ + Package: "BZZ", + Errors: errorToString, + }, + swapParams: sp, + syncParams: sy, + swapEnabled: hive.swapEnabled, + syncEnabled: true, + NetworkId: networkId, + } + + // handle handshake + err = self.handleStatus() + if err != nil { + return err + } + defer func() { + // if the handler loop exits, the peer is disconnecting + // deregister the peer in the hive + self.hive.removePeer(&peer{bzz: self}) + if self.syncer != nil { + self.syncer.stop() // quits request db and delivery loops, save requests + } + if self.swap != nil { + self.swap.Stop() // quits chequebox autocash etc + } + }() + + // the main forever loop that handles incoming requests + for { + if self.hive.blockRead { + glog.V(logger.Warn).Infof("Cannot read network") + time.Sleep(100 * time.Millisecond) + continue + } + err = self.handle() + if err != nil { + return + } + } +} + +// TODO: may need to implement protocol drop only? don't want to kick off the peer +// if they are useful for other protocols +func (self *bzz) Drop() { + self.peer.Disconnect(p2p.DiscSubprotocolError) +} + +// one cycle of the main forever loop that handles and dispatches incoming messages +func (self *bzz) handle() error { + msg, err := self.rw.ReadMsg() + glog.V(logger.Debug).Infof("<- %v", msg) + if err != nil { + return err + } + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + // make sure that the payload has been fully consumed + defer msg.Discard() + + switch msg.Code { + + case statusMsg: + // no extra status message allowed. The one needed already handled by + // handleStatus + glog.V(logger.Debug).Infof("Status message: %v", msg) + return self.protoError(ErrExtraStatusMsg, "") + + case storeRequestMsg: + // store requests are dispatched to netStore + var req storeRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + if len(req.SData) < 9 { + return self.protoError(ErrDecode, "<- %v: Data too short (%v)", msg) + } + // last Active time is set only when receiving chunks + self.lastActive = time.Now() + glog.V(logger.Detail).Infof("incoming store request: %s", req.String()) + // swap accounting is done within forwarding + self.storage.HandleStoreRequestMsg(&req, &peer{bzz: self}) + + case retrieveRequestMsg: + // retrieve Requests are dispatched to netStore + var req retrieveRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + // if request is lookup and not to be delivered + if req.isLookup() { + glog.V(logger.Detail).Infof("self lookup for %v: responding with peers only...", req.from) + } else if req.Key == nil { + return self.protoError(ErrDecode, "protocol handler: req.Key == nil || req.Timeout == nil") + } else { + // swap accounting is done within netStore + self.storage.HandleRetrieveRequestMsg(&req, &peer{bzz: self}) + } + // direct response with peers, TODO: sort this out + self.hive.peers(&req) + + case peersMsg: + // response to lookups and immediate response to retrieve requests + // dispatches new peer data to the hive that adds them to KADDB + var req peersMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + req.from = &peer{bzz: self} + glog.V(logger.Detail).Infof("<- peer addresses: %v", req) + self.hive.HandlePeersMsg(&req, &peer{bzz: self}) + + case syncRequestMsg: + var req syncRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- sync request: %v", req) + self.lastActive = time.Now() + self.sync(req.SyncState) + + case unsyncedKeysMsg: + // coming from parent node offering + var req unsyncedKeysMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- unsynced keys : %s", req.String()) + err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case deliveryRequestMsg: + // response to syncKeysMsg hashes filtered not existing in db + // also relays the last synced state to the source + var req deliveryRequestMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<-msg %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- delivery request: %s", req.String()) + err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self}) + self.lastActive = time.Now() + if err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + + case paymentMsg: + // swap protocol message for payment, Units paid for, Cheque paid with + if self.swapEnabled { + var req paymentMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "<- %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("<- payment: %s", req.String()) + self.swap.Receive(int(req.Units), req.Promise) + } + + default: + // no other message is allowed + return self.protoError(ErrInvalidMsgCode, "%v", msg.Code) + } + return nil +} + +func (self *bzz) handleStatus() (err error) { + + handshake := &statusMsgData{ + Version: uint64(Version), + ID: "honey", + Addr: self.selfAddr(), + NetworkId: uint64(self.NetworkId), + Swap: &bzzswap.SwapProfile{ + Profile: self.swapParams.Profile, + PayProfile: self.swapParams.PayProfile, + }, + } + + err = p2p.Send(self.rw, statusMsg, handshake) + if err != nil { + self.protoError(ErrNoStatusMsg, err.Error()) + } + + // read and handle remote status + var msg p2p.Msg + msg, err = self.rw.ReadMsg() + if err != nil { + return err + } + + if msg.Code != statusMsg { + self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, statusMsg) + } + + if msg.Size > ProtocolMaxMsgSize { + return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + + var status statusMsgData + if err := msg.Decode(&status); err != nil { + return self.protoError(ErrDecode, " %v: %v", msg, err) + } + + if status.NetworkId != self.NetworkId { + return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, self.NetworkId) + } + + if Version != status.Version { + return self.protoError(ErrVersionMismatch, "%d (!= %d)", status.Version, Version) + } + + self.remoteAddr = self.peerAddr(status.Addr) + glog.V(logger.Detail).Infof("self: advertised IP: %v, peer advertised: %v, local address: %v\npeer: advertised IP: %v, remote address: %v\n", self.selfAddr(), self.remoteAddr, self.peer.LocalAddr(), status.Addr.IP, self.peer.RemoteAddr()) + + if self.swapEnabled { + // set remote profile for accounting + self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self.backend, self) + if err != nil { + return self.protoError(ErrSwap, "%v", err) + } + } + + glog.V(logger.Info).Infof("Peer %08x is capable (%d/%d)", self.remoteAddr.Addr[:4], status.Version, status.NetworkId) + err = self.hive.addPeer(&peer{bzz: self}) + if err != nil { + return self.protoError(ErrUnwanted, "%v", err) + } + + // hive sets syncstate so sync should start after node added + glog.V(logger.Info).Infof("syncronisation request sent with %v", self.syncState) + self.syncRequest() + + return nil +} + +func (self *bzz) sync(state *syncState) error { + // syncer setup + if self.syncer != nil { + return self.protoError(ErrSync, "sync request can only be sent once") + } + + cnt := self.dbAccess.counter() + remoteaddr := self.remoteAddr.Addr + start, stop := self.hive.kad.KeyRange(remoteaddr) + + // an explicitly received nil syncstate disables syncronisation + if state == nil { + self.syncEnabled = false + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v", self) + state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true} + } else { + state.synced = make(chan bool) + state.SessionAt = cnt + if storage.IsZeroKey(state.Stop) && state.Synced { + state.Start = storage.Key(start[:]) + state.Stop = storage.Key(stop[:]) + } + glog.V(logger.Debug).Infof("syncronisation requested by peer %v at state %v", self, state) + } + var err error + self.syncer, err = newSyncer( + self.requestDb, + storage.Key(remoteaddr[:]), + self.dbAccess, + self.unsyncedKeys, self.store, + self.syncParams, state, func() bool { return self.syncEnabled }, + ) + if err != nil { + return self.protoError(ErrSync, "%v", err) + } + glog.V(logger.Detail).Infof("syncer set for peer %v", self) + return nil +} + +func (self *bzz) String() string { + return self.remoteAddr.String() +} + +// repair reported address if IP missing +func (self *bzz) peerAddr(base *peerAddr) *peerAddr { + if base.IP.IsUnspecified() { + host, _, _ := net.SplitHostPort(self.peer.RemoteAddr().String()) + base.IP = net.ParseIP(host) + } + return base +} + +// returns self advertised node connection info (listening address w enodes) +// IP will get repaired on the other end if missing +// or resolved via ID by discovery at dialout +func (self *bzz) selfAddr() *peerAddr { + id := self.hive.id + host, port, _ := net.SplitHostPort(self.hive.listenAddr()) + intport, _ := strconv.Atoi(port) + addr := &peerAddr{ + Addr: self.hive.addr, + ID: id[:], + IP: net.ParseIP(host), + Port: uint16(intport), + } + return addr +} + +// outgoing messages +// send retrieveRequestMsg +func (self *bzz) retrieve(req *retrieveRequestMsgData) error { + return self.send(retrieveRequestMsg, req) +} + +// send storeRequestMsg +func (self *bzz) store(req *storeRequestMsgData) error { + return self.send(storeRequestMsg, req) +} + +func (self *bzz) syncRequest() error { + req := &syncRequestMsgData{} + if self.hive.syncEnabled { + glog.V(logger.Debug).Infof("syncronisation request to peer %v at state %v", self, self.syncState) + req.SyncState = self.syncState + } + if self.syncState == nil { + glog.V(logger.Warn).Infof("syncronisation disabled for peer %v at state %v", self, self.syncState) + } + return self.send(syncRequestMsg, req) +} + +// queue storeRequestMsg in request db +func (self *bzz) deliveryRequest(reqs []*syncRequest) error { + req := &deliveryRequestMsgData{ + Deliver: reqs, + } + return self.send(deliveryRequestMsg, req) +} + +// batch of syncRequests to send off +func (self *bzz) unsyncedKeys(reqs []*syncRequest, state *syncState) error { + req := &unsyncedKeysMsgData{ + Unsynced: reqs, + State: state, + } + return self.send(unsyncedKeysMsg, req) +} + +// send paymentMsg +func (self *bzz) Pay(units int, promise swap.Promise) { + req := &paymentMsgData{uint(units), promise.(*chequebook.Cheque)} + self.payment(req) +} + +// send paymentMsg +func (self *bzz) payment(req *paymentMsgData) error { + return self.send(paymentMsg, req) +} + +// sends peersMsg +func (self *bzz) peers(req *peersMsgData) error { + return self.send(peersMsg, req) +} + +func (self *bzz) protoError(code int, format string, params ...interface{}) (err *errs.Error) { + err = self.errors.New(code, format, params...) + err.Log(glog.V(logger.Info)) + return +} + +func (self *bzz) send(msg uint64, data interface{}) error { + if self.hive.blockWrite { + return fmt.Errorf("network write blocked") + } + glog.V(logger.Detail).Infof("-> %v: %v (%T) to %v", msg, data, data, self) + err := p2p.Send(self.rw, msg, data) + if err != nil { + self.Drop() + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol_test.go new file mode 100644 index 0000000..988d0ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/protocol_test.go @@ -0,0 +1,17 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go new file mode 100644 index 0000000..cef3261 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb.go @@ -0,0 +1,390 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "encoding/binary" + "fmt" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const counterKeyPrefix = 0x01 + +/* +syncDb is a queueing service for outgoing deliveries. +One instance per priority queue for each peer + +a syncDb instance maintains an in-memory buffer (of capacity bufferSize) +once its in-memory buffer is full it switches to persisting in db +and dbRead iterator iterates through the items keeping their order +once the db read catches up (there is no more items in the db) then +it switches back to in-memory buffer. + +when syncdb is stopped all items in the buffer are saved to the db +*/ +type syncDb struct { + start []byte // this syncdb starting index in requestdb + key storage.Key // remote peers address key + counterKey []byte // db key to persist counter + priority uint // priotity High|Medium|Low + buffer chan interface{} // incoming request channel + db *storage.LDBDatabase // underlying db (TODO should be interface) + done chan bool // chan to signal goroutines finished quitting + quit chan bool // chan to signal quitting to goroutines + total, dbTotal int // counts for one session + batch chan chan int // channel for batch requests + dbBatchSize uint // number of items before batch is saved +} + +// constructor needs a shared request db (leveldb) +// priority is used in the index key +// uses a buffer and a leveldb for persistent storage +// bufferSize, dbBatchSize are config parameters +func newSyncDb(db *storage.LDBDatabase, key storage.Key, priority uint, bufferSize, dbBatchSize uint, deliver func(interface{}, chan bool) bool) *syncDb { + start := make([]byte, 42) + start[1] = byte(priorities - priority) + copy(start[2:34], key) + + counterKey := make([]byte, 34) + counterKey[0] = counterKeyPrefix + copy(counterKey[1:], start[1:34]) + + syncdb := &syncDb{ + start: start, + key: key, + counterKey: counterKey, + priority: priority, + buffer: make(chan interface{}, bufferSize), + db: db, + done: make(chan bool), + quit: make(chan bool), + batch: make(chan chan int), + dbBatchSize: dbBatchSize, + } + glog.V(logger.Detail).Infof("syncDb[peer: %v, priority: %v] - initialised", key.Log(), priority) + + // starts the main forever loop reading from buffer + go syncdb.bufferRead(deliver) + return syncdb +} + +/* +bufferRead is a forever iterator loop that takes care of delivering +outgoing store requests reads from incoming buffer + +its argument is the deliver function taking the item as first argument +and a quit channel as second. +Closing of this channel is supposed to abort all waiting for delivery +(typically network write) + +The iteration switches between 2 modes, +* buffer mode reads the in-memory buffer and delivers the items directly +* db mode reads from the buffer and writes to the db, parallelly another +routine is started that reads from the db and delivers items + +If there is buffer contention in buffer mode (slow network, high upload volume) +syncdb switches to db mode and starts dbRead +Once db backlog is delivered, it reverts back to in-memory buffer + +It is automatically started when syncdb is initialised. + +It saves the buffer to db upon receiving quit signal. syncDb#stop() +*/ +func (self *syncDb) bufferRead(deliver func(interface{}, chan bool) bool) { + var buffer, db chan interface{} // channels representing the two read modes + var more bool + var req interface{} + var entry *syncDbEntry + var inBatch, inDb int + batch := new(leveldb.Batch) + var dbSize chan int + quit := self.quit + counterValue := make([]byte, 8) + + // counter is used for keeping the items in order, persisted to db + // start counter where db was at, 0 if not found + data, err := self.db.Get(self.counterKey) + var counter uint64 + if err == nil { + counter = binary.BigEndian.Uint64(data) + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter read from db at %v", self.key.Log(), self.priority, counter) + } else { + glog.V(logger.Detail).Infof("syncDb[%v/%v] - counter starts at %v", self.key.Log(), self.priority, counter) + } + +LOOP: + for { + // waiting for item next in the buffer, or quit signal or batch request + select { + // buffer only closes when writing to db + case req = <-buffer: + // deliver request : this is blocking on network write so + // it is passed the quit channel as argument, so that it returns + // if syncdb is stopped. In this case we need to save the item to the db + more = deliver(req, self.quit) + if !more { + glog.V(logger.Debug).Infof("syncDb[%v/%v] quit: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // received quit signal, save request currently waiting delivery + // by switching to db mode and closing the buffer + buffer = nil + db = self.buffer + close(db) + quit = nil // needs to block the quit case in select + break // break from select, this item will be written to the db + } + self.total++ + glog.V(logger.Detail).Infof("syncDb[%v/%v] deliver (db/total): %v/%v", self.key.Log(), self.priority, self.dbTotal, self.total) + // by the time deliver returns, there were new writes to the buffer + // if buffer contention is detected, switch to db mode which drains + // the buffer so no process will block on pushing store requests + if len(buffer) == cap(buffer) { + glog.V(logger.Debug).Infof("syncDb[%v/%v] buffer full %v: switching to db. session tally (db/total): %v/%v", self.key.Log(), self.priority, cap(buffer), self.dbTotal, self.total) + buffer = nil + db = self.buffer + } + continue LOOP + + // incoming entry to put into db + case req, more = <-db: + if !more { + // only if quit is called, saved all the buffer + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) // persist counter in batch + self.writeSyncBatch(batch) // save batch + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save current batch to db", self.key.Log(), self.priority) + break LOOP + } + self.dbTotal++ + self.total++ + // otherwise break after select + case dbSize = <-self.batch: + // explicit request for batch + if inBatch == 0 && quit != nil { + // there was no writes since the last batch so db depleted + // switch to buffer mode + glog.V(logger.Debug).Infof("syncDb[%v/%v] empty db: switching to buffer", self.key.Log(), self.priority) + db = nil + buffer = self.buffer + dbSize <- 0 // indicates to 'caller' that batch has been written + inDb = 0 + continue LOOP + } + binary.BigEndian.PutUint64(counterValue, counter) + batch.Put(self.counterKey, counterValue) + glog.V(logger.Debug).Infof("syncDb[%v/%v] write batch %v/%v - %x - %x", self.key.Log(), self.priority, inBatch, counter, self.counterKey, counterValue) + batch = self.writeSyncBatch(batch) + dbSize <- inBatch // indicates to 'caller' that batch has been written + inBatch = 0 + continue LOOP + + // closing syncDb#quit channel is used to signal to all goroutines to quit + case <-quit: + // need to save backlog, so switch to db mode + db = self.buffer + buffer = nil + quit = nil + glog.V(logger.Detail).Infof("syncDb[%v/%v] quitting: save buffer to db", self.key.Log(), self.priority) + close(db) + continue LOOP + } + + // only get here if we put req into db + entry, err = self.newSyncDbEntry(req, counter) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving request %v (#%v/%v) failed: %v", self.key.Log(), self.priority, req, inBatch, inDb, err) + continue LOOP + } + batch.Put(entry.key, entry.val) + glog.V(logger.Detail).Infof("syncDb[%v/%v] to batch %v '%v' (#%v/%v/%v)", self.key.Log(), self.priority, req, entry, inBatch, inDb, counter) + // if just switched to db mode and not quitting, then launch dbRead + // in a parallel go routine to send deliveries from db + if inDb == 0 && quit != nil { + glog.V(logger.Detail).Infof("syncDb[%v/%v] start dbRead") + go self.dbRead(true, counter, deliver) + } + inDb++ + inBatch++ + counter++ + // need to save the batch if it gets too large (== dbBatchSize) + if inBatch%int(self.dbBatchSize) == 0 { + batch = self.writeSyncBatch(batch) + } + } + glog.V(logger.Info).Infof("syncDb[%v:%v]: saved %v keys (saved counter at %v)", self.key.Log(), self.priority, inBatch, counter) + close(self.done) +} + +// writes the batch to the db and returns a new batch object +func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch { + err := self.db.Write(batch) + if err != nil { + glog.V(logger.Warn).Infof("syncDb[%v/%v] saving batch to db failed: %v", self.key.Log(), self.priority, err) + return batch + } + return new(leveldb.Batch) +} + +// abstract type for db entries (TODO could be a feature of Receipts) +type syncDbEntry struct { + key, val []byte +} + +func (self syncDbEntry) String() string { + return fmt.Sprintf("key: %x, value: %x", self.key, self.val) +} + +/* + dbRead is iterating over store requests to be sent over to the peer + this is mainly to prevent crashes due to network output buffer contention (???) + as well as to make syncronisation resilient to disconnects + the messages are supposed to be sent in the p2p priority queue. + + the request DB is shared between peers, but domains for each syncdb + are disjoint. dbkeys (42 bytes) are structured: + * 0: 0x00 (0x01 reserved for counter key) + * 1: priorities - priority (so that high priority can be replayed first) + * 2-33: peers address + * 34-41: syncdb counter to preserve order (this field is missing for the counter key) + + values (40 bytes) are: + * 0-31: key + * 32-39: request id + +dbRead needs a boolean to indicate if on first round all the historical +record is synced. Second argument to indicate current db counter +The third is the function to apply +*/ +func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}, chan bool) bool) { + key := make([]byte, 42) + copy(key, self.start) + binary.BigEndian.PutUint64(key[34:], counter) + var batches, n, cnt, total int + var more bool + var entry *syncDbEntry + var it iterator.Iterator + var del *leveldb.Batch + batchSizes := make(chan int) + + for { + // if useBatches is false, cnt is not set + if useBatches { + // this could be called before all cnt items sent out + // so that loop is not blocking while delivering + // only relevant if cnt is large + select { + case self.batch <- batchSizes: + case <-self.quit: + return + } + // wait for the write to finish and get the item count in the next batch + cnt = <-batchSizes + batches++ + if cnt == 0 { + // empty + return + } + } + it = self.db.NewIterator() + it.Seek(key) + if !it.Valid() { + copy(key, self.start) + useBatches = true + continue + } + del = new(leveldb.Batch) + glog.V(logger.Detail).Infof("syncDb[%v/%v]: new iterator: %x (batch %v, count %v)", self.key.Log(), self.priority, key, batches, cnt) + + for n = 0; !useBatches || n < cnt; it.Next() { + copy(key, it.Key()) + if len(key) == 0 || key[0] != 0 { + copy(key, self.start) + useBatches = true + break + } + val := make([]byte, 40) + copy(val, it.Value()) + entry = &syncDbEntry{key, val} + // glog.V(logger.Detail).Infof("syncDb[%v/%v] - %v, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, self.key.Log(), batches, total, self.dbTotal, self.total) + more = fun(entry, self.quit) + if !more { + // quit received when waiting to deliver entry, the entry will not be deleted + glog.V(logger.Detail).Infof("syncDb[%v/%v] batch %v quit after %v/%v items", self.key.Log(), self.priority, batches, n, cnt) + break + } + // since subsequent batches of the same db session are indexed incrementally + // deleting earlier batches can be delayed and parallelised + // this could be batch delete when db is idle (but added complexity esp when quitting) + del.Delete(key) + n++ + total++ + } + glog.V(logger.Debug).Infof("syncDb[%v/%v] - db session closed, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, batches, total, self.dbTotal, self.total) + self.db.Write(del) // this could be async called only when db is idle + it.Release() + } +} + +// +func (self *syncDb) stop() { + close(self.quit) + <-self.done +} + +// calculate a dbkey for the request, for the db to work +// see syncdb for db key structure +// polimorphic: accepted types, see syncer#addRequest +func (self *syncDb) newSyncDbEntry(req interface{}, counter uint64) (entry *syncDbEntry, err error) { + var key storage.Key + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + + if key, ok = req.(storage.Key); ok { + id = generateId() + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + id = sreq.Id + } else if entry, ok = req.(*syncDbEntry); !ok { + return nil, fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + // order by peer > priority > seqid + // value is request id if exists + if entry == nil { + dbkey := make([]byte, 42) + dbval := make([]byte, 40) + + // encode key + copy(dbkey[:], self.start[:34]) // db peer + binary.BigEndian.PutUint64(dbkey[34:], counter) + // encode value + copy(dbval, key[:]) + binary.BigEndian.PutUint64(dbval[32:], id) + + entry = &syncDbEntry{dbkey, dbval} + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb_test.go new file mode 100644 index 0000000..21453a1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncdb_test.go @@ -0,0 +1,223 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func init() { + glog.SetV(0) + glog.SetToStderr(true) +} + +type testSyncDb struct { + *syncDb + c int + t *testing.T + fromDb chan bool + delivered [][]byte + sent []int + dbdir string + at int +} + +func newTestSyncDb(priority, bufferSize, batchSize int, dbdir string, t *testing.T) *testSyncDb { + if len(dbdir) == 0 { + tmp, err := ioutil.TempDir(os.TempDir(), "syncdb-test") + if err != nil { + t.Fatalf("unable to create temporary direcory %v: %v", tmp, err) + } + dbdir = tmp + } + db, err := storage.NewLDBDatabase(filepath.Join(dbdir, "requestdb")) + if err != nil { + t.Fatalf("unable to create db: %v", err) + } + self := &testSyncDb{ + fromDb: make(chan bool), + dbdir: dbdir, + t: t, + } + h := crypto.Keccak256Hash([]byte{0}) + key := storage.Key(h[:]) + self.syncDb = newSyncDb(db, key, uint(priority), uint(bufferSize), uint(batchSize), self.deliver) + // kick off db iterator right away, if no items on db this will allow + // reading from the buffer + return self + +} + +func (self *testSyncDb) close() { + self.db.Close() + os.RemoveAll(self.dbdir) +} + +func (self *testSyncDb) push(n int) { + for i := 0; i < n; i++ { + self.buffer <- storage.Key(crypto.Keccak256([]byte{byte(self.c)})) + self.sent = append(self.sent, self.c) + self.c++ + } + glog.V(logger.Debug).Infof("pushed %v requests", n) +} + +func (self *testSyncDb) draindb() { + it := self.db.NewIterator() + defer it.Release() + for { + it.Seek(self.start) + if !it.Valid() { + return + } + k := it.Key() + if len(k) == 0 || k[0] == 1 { + return + } + it.Release() + it = self.db.NewIterator() + } +} + +func (self *testSyncDb) deliver(req interface{}, quit chan bool) bool { + _, db := req.(*syncDbEntry) + key, _, _, _, err := parseRequest(req) + if err != nil { + self.t.Fatalf("unexpected error of key %v: %v", key, err) + } + self.delivered = append(self.delivered, key) + select { + case self.fromDb <- db: + return true + case <-quit: + return false + } +} + +func (self *testSyncDb) expect(n int, db bool) { + var ok bool + // for n items + for i := 0; i < n; i++ { + ok = <-self.fromDb + if self.at+1 > len(self.delivered) { + self.t.Fatalf("expected %v, got %v", self.at+1, len(self.delivered)) + } + if len(self.sent) > self.at && !bytes.Equal(crypto.Keccak256([]byte{byte(self.sent[self.at])}), self.delivered[self.at]) { + self.t.Fatalf("expected delivery %v/%v/%v to be hash of %v, from db: %v = %v", i, n, self.at, self.sent[self.at], ok, db) + glog.V(logger.Debug).Infof("%v/%v/%v to be hash of %v, from db: %v = %v", i, n, self.at, self.sent[self.at], ok, db) + } + if !ok && db { + self.t.Fatalf("expected delivery %v/%v/%v from db", i, n, self.at) + } + if ok && !db { + self.t.Fatalf("expected delivery %v/%v/%v from cache", i, n, self.at) + } + self.at++ + } +} + +func TestSyncDb(t *testing.T) { + t.Skip("fails randomly on all platforms") + + priority := High + bufferSize := 5 + batchSize := 2 * bufferSize + s := newTestSyncDb(priority, bufferSize, batchSize, "", t) + defer s.close() + defer s.stop() + s.dbRead(false, 0, s.deliver) + s.draindb() + + s.push(4) + s.expect(1, false) + // 3 in buffer + time.Sleep(100 * time.Millisecond) + s.push(3) + // push over limit + s.expect(1, false) + // one popped from the buffer, then contention detected + s.expect(4, true) + s.push(4) + s.expect(5, true) + // depleted db, switch back to buffer + s.draindb() + s.push(5) + s.expect(4, false) + s.push(3) + s.expect(4, false) + // buffer depleted + time.Sleep(100 * time.Millisecond) + s.push(6) + s.expect(1, false) + // push into buffer full, switch to db + s.expect(5, true) + s.draindb() + s.push(1) + s.expect(1, false) +} + +func TestSaveSyncDb(t *testing.T) { + amount := 30 + priority := High + bufferSize := amount + batchSize := 10 + s := newTestSyncDb(priority, bufferSize, batchSize, "", t) + go s.dbRead(false, 0, s.deliver) + s.push(amount) + s.stop() + s.db.Close() + + s = newTestSyncDb(priority, bufferSize, batchSize, s.dbdir, t) + go s.dbRead(false, 0, s.deliver) + s.expect(amount, true) + for i, key := range s.delivered { + expKey := crypto.Keccak256([]byte{byte(i)}) + if !bytes.Equal(key, expKey) { + t.Fatalf("delivery %v expected to be key %x, got %x", i, expKey, key) + } + } + s.push(amount) + s.expect(amount, false) + for i := amount; i < 2*amount; i++ { + key := s.delivered[i] + expKey := crypto.Keccak256([]byte{byte(i - amount)}) + if !bytes.Equal(key, expKey) { + t.Fatalf("delivery %v expected to be key %x, got %x", i, expKey, key) + } + } + s.stop() + s.db.Close() + + s = newTestSyncDb(priority, bufferSize, batchSize, s.dbdir, t) + defer s.close() + defer s.stop() + + go s.dbRead(false, 0, s.deliver) + s.push(1) + s.expect(1, false) + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go new file mode 100644 index 0000000..b6b1ea3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/network/syncer.go @@ -0,0 +1,778 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package network + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "path/filepath" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +// syncer parameters (global, not peer specific) default values +const ( + requestDbBatchSize = 512 // size of batch before written to request db + keyBufferSize = 1024 // size of buffer for unsynced keys + syncBatchSize = 128 // maximum batchsize for outgoing requests + syncBufferSize = 128 // size of buffer for delivery requests + syncCacheSize = 1024 // cache capacity to store request queue in memory +) + +// priorities +const ( + Low = iota // 0 + Medium // 1 + High // 2 + priorities // 3 number of priority levels +) + +// request types +const ( + DeliverReq = iota // 0 + PushReq // 1 + PropagateReq // 2 + HistoryReq // 3 + BacklogReq // 4 +) + +// json serialisable struct to record the syncronisation state between 2 peers +type syncState struct { + *storage.DbSyncState // embeds the following 4 fields: + // Start Key // lower limit of address space + // Stop Key // upper limit of address space + // First uint64 // counter taken from last sync state + // Last uint64 // counter of remote peer dbStore at the time of last connection + SessionAt uint64 // set at the time of connection + LastSeenAt uint64 // set at the time of connection + Latest storage.Key // cursor of dbstore when last (continuously set by syncer) + Synced bool // true iff Sync is done up to the last disconnect + synced chan bool // signal that sync stage finished +} + +// wrapper of db-s to provide mockable custom local chunk store access to syncer +type DbAccess struct { + db *storage.DbStore + loc *storage.LocalStore +} + +func NewDbAccess(loc *storage.LocalStore) *DbAccess { + return &DbAccess{loc.DbStore.(*storage.DbStore), loc} +} + +// to obtain the chunks from key or request db entry only +func (self *DbAccess) get(key storage.Key) (*storage.Chunk, error) { + return self.loc.Get(key) +} + +// current storage counter of chunk db +func (self *DbAccess) counter() uint64 { + return self.db.Counter() +} + +// implemented by dbStoreSyncIterator +type keyIterator interface { + Next() storage.Key +} + +// generator function for iteration by address range and storage counter +func (self *DbAccess) iterator(s *syncState) keyIterator { + it, err := self.db.NewSyncIterator(*(s.DbSyncState)) + if err != nil { + return nil + } + return keyIterator(it) +} + +func (self syncState) String() string { + if self.Synced { + return fmt.Sprintf( + "session started at: %v, last seen at: %v, latest key: %v", + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } else { + return fmt.Sprintf( + "address: %v-%v, index: %v-%v, session started at: %v, last seen at: %v, latest key: %v", + self.Start.Log(), self.Stop.Log(), + self.First, self.Last, + self.SessionAt, self.LastSeenAt, + self.Latest.Log(), + ) + } +} + +// syncer parameters (global, not peer specific) +type SyncParams struct { + RequestDbPath string // path for request db (leveldb) + RequestDbBatchSize uint // nuber of items before batch is saved to requestdb + KeyBufferSize uint // size of key buffer + SyncBatchSize uint // maximum batchsize for outgoing requests + SyncBufferSize uint // size of buffer for + SyncCacheSize uint // cache capacity to store request queue in memory + SyncPriorities []uint // list of priority levels for req types 0-3 + SyncModes []bool // list of sync modes for for req types 0-3 +} + +// constructor with default values +func NewSyncParams(bzzdir string) *SyncParams { + return &SyncParams{ + RequestDbPath: filepath.Join(bzzdir, "requests"), + RequestDbBatchSize: requestDbBatchSize, + KeyBufferSize: keyBufferSize, + SyncBufferSize: syncBufferSize, + SyncBatchSize: syncBatchSize, + SyncCacheSize: syncCacheSize, + SyncPriorities: []uint{High, Medium, Medium, Low, Low}, + SyncModes: []bool{true, true, true, true, false}, + } +} + +// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding +type syncer struct { + *SyncParams // sync parameters + syncF func() bool // if syncing is needed + key storage.Key // remote peers address key + state *syncState // sync state for our dbStore + syncStates chan *syncState // different stages of sync + deliveryRequest chan bool // one of two triggers needed to send unsyncedKeys + newUnsyncedKeys chan bool // one of two triggers needed to send unsynced keys + quit chan bool // signal to quit loops + + // DB related fields + dbAccess *DbAccess // access to dbStore + db *storage.LDBDatabase // delivery msg db + + // native fields + queues [priorities]*syncDb // in-memory cache / queues for sync reqs + keys [priorities]chan interface{} // buffer for unsynced keys + deliveries [priorities]chan *storeRequestMsgData // delivery + + // bzz protocol instance outgoing message callbacks (mockable for testing) + unsyncedKeys func([]*syncRequest, *syncState) error // send unsyncedKeysMsg + store func(*storeRequestMsgData) error // send storeRequestMsg +} + +// a syncer instance is linked to each peer connection +// constructor is called from protocol after successful handshake +// the returned instance is attached to the peer and can be called +// by the forwarder +func newSyncer( + db *storage.LDBDatabase, remotekey storage.Key, + dbAccess *DbAccess, + unsyncedKeys func([]*syncRequest, *syncState) error, + store func(*storeRequestMsgData) error, + params *SyncParams, + state *syncState, + syncF func() bool, +) (*syncer, error) { + + syncBufferSize := params.SyncBufferSize + keyBufferSize := params.KeyBufferSize + dbBatchSize := params.RequestDbBatchSize + + self := &syncer{ + syncF: syncF, + key: remotekey, + dbAccess: dbAccess, + syncStates: make(chan *syncState, 20), + deliveryRequest: make(chan bool, 1), + newUnsyncedKeys: make(chan bool, 1), + SyncParams: params, + state: state, + quit: make(chan bool), + unsyncedKeys: unsyncedKeys, + store: store, + } + + // initialising + for i := 0; i < priorities; i++ { + self.keys[i] = make(chan interface{}, keyBufferSize) + self.deliveries[i] = make(chan *storeRequestMsgData) + // initialise a syncdb instance for each priority queue + self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i))) + } + glog.V(logger.Info).Infof("syncer started: %v", state) + // launch chunk delivery service + go self.syncDeliveries() + // launch sync task manager + if self.syncF() { + go self.sync() + } + // process unsynced keys to broadcast + go self.syncUnsyncedKeys() + + return self, nil +} + +// metadata serialisation +func encodeSync(state *syncState) (*json.RawMessage, error) { + data, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + meta := json.RawMessage(data) + return &meta, nil +} + +func decodeSync(meta *json.RawMessage) (*syncState, error) { + if meta == nil { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + data := []byte(*(meta)) + if len(data) == 0 { + return nil, fmt.Errorf("unable to deserialise sync state from ") + } + state := &syncState{DbSyncState: &storage.DbSyncState{}} + err := json.Unmarshal(data, state) + return state, err +} + +/* + sync implements the syncing script + * first all items left in the request Db are replayed + * type = StaleSync + * Mode: by default once again via confirmation roundtrip + * Priority: the items are replayed as the proirity specified for StaleSync + * but within the order respects earlier priority level of request + * after all items are consumed for a priority level, the the respective + queue for delivery requests is open (this way new reqs not written to db) + (TODO: this should be checked) + * the sync state provided by the remote peer is used to sync history + * all the backlog from earlier (aborted) syncing is completed starting from latest + * if Last < LastSeenAt then all items in between then process all + backlog from upto last disconnect + * if Last > 0 && + + sync is called from the syncer constructor and is not supposed to be used externally +*/ +func (self *syncer) sync() { + state := self.state + // sync finished + defer close(self.syncStates) + + // 0. first replay stale requests from request db + if state.SessionAt == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: nothing to sync", self.key.Log()) + return + } + glog.V(logger.Debug).Infof("syncer[%v]: start replaying stale requests from request db", self.key.Log()) + for p := priorities - 1; p >= 0; p-- { + self.queues[p].dbRead(false, 0, self.replay()) + } + glog.V(logger.Debug).Infof("syncer[%v]: done replaying stale requests from request db", self.key.Log()) + + // unless peer is synced sync unfinished history beginning on + if !state.Synced { + start := state.Start + + if !storage.IsZeroKey(state.Latest) { + // 1. there is unfinished earlier sync + state.Start = state.Latest + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising backlog (unfinished sync: %v)", self.key.Log(), state) + // blocks while the entire history upto state is synced + self.syncState(state) + if state.Last < state.SessionAt { + state.First = state.Last + 1 + } + } + state.Latest = storage.ZeroKey + state.Start = start + // 2. sync up to last disconnect1 + if state.First < state.LastSeenAt { + state.Last = state.LastSeenAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history upto last disconnect at %v: %v", self.key.Log(), state.LastSeenAt, state) + self.syncState(state) + state.First = state.LastSeenAt + } + state.Latest = storage.ZeroKey + + } else { + // synchronisation starts at end of last session + state.First = state.LastSeenAt + } + + // 3. sync up to current session start + // if there have been new chunks since last session + if state.LastSeenAt < state.SessionAt { + state.Last = state.SessionAt + glog.V(logger.Debug).Infof("syncer[%v]: start syncronising history since last disconnect at %v up until session start at %v: %v", self.key.Log(), state.LastSeenAt, state.SessionAt, state) + // blocks until state syncing is finished + self.syncState(state) + } + glog.V(logger.Info).Infof("syncer[%v]: syncing all history complete", self.key.Log()) + +} + +// wait till syncronised block uptil state is synced +func (self *syncer) syncState(state *syncState) { + self.syncStates <- state + select { + case <-state.synced: + case <-self.quit: + } +} + +// stop quits both request processor and saves the request cache to disk +func (self *syncer) stop() { + close(self.quit) + glog.V(logger.Detail).Infof("syncer[%v]: stop and save sync request db backlog", self.key.Log()) + for _, db := range self.queues { + db.stop() + } +} + +// rlp serialisable sync request +type syncRequest struct { + Key storage.Key + Priority uint +} + +func (self *syncRequest) String() string { + return fmt.Sprintf("", self.Key.Log(), self.Priority) +} + +func (self *syncer) newSyncRequest(req interface{}, p int) (*syncRequest, error) { + key, _, _, _, err := parseRequest(req) + // TODO: if req has chunk, it should be put in a cache + // create + if err != nil { + return nil, err + } + return &syncRequest{key, uint(p)}, nil +} + +// serves historical items from the DB +// * read is on demand, blocking unless history channel is read +// * accepts sync requests (syncStates) to create new db iterator +// * closes the channel one iteration finishes +func (self *syncer) syncHistory(state *syncState) chan interface{} { + var n uint + history := make(chan interface{}) + glog.V(logger.Debug).Infof("syncer[%v]: syncing history between %v - %v for chunk addresses %v - %v", self.key.Log(), state.First, state.Last, state.Start, state.Stop) + it := self.dbAccess.iterator(state) + if it != nil { + go func() { + // signal end of the iteration ended + defer close(history) + IT: + for { + key := it.Next() + if key == nil { + break IT + } + select { + // blocking until history channel is read from + case history <- storage.Key(key): + n++ + glog.V(logger.Detail).Infof("syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n) + state.Latest = key + case <-self.quit: + return + } + } + glog.V(logger.Debug).Infof("syncer[%v]: finished syncing history between %v - %v for chunk addresses %v - %v (at %v) (chunks = %v)", self.key.Log(), state.First, state.Last, state.Start, state.Stop, state.Latest, n) + }() + } + return history +} + +// triggers key syncronisation +func (self *syncer) sendUnsyncedKeys() { + select { + case self.deliveryRequest <- true: + default: + } +} + +// assembles a new batch of unsynced keys +// * keys are drawn from the key buffers in order of priority queue +// * if the queues of priority for History (HistoryReq) or higher are depleted, +// historical data is used so historical items are lower priority within +// their priority group. +// * Order of historical data is unspecified +func (self *syncer) syncUnsyncedKeys() { + // send out new + var unsynced []*syncRequest + var more, justSynced bool + var keyCount, historyCnt int + var history chan interface{} + + priority := High + keys := self.keys[priority] + var newUnsyncedKeys, deliveryRequest chan bool + keyCounts := make([]int, priorities) + histPrior := self.SyncPriorities[HistoryReq] + syncStates := self.syncStates + state := self.state + +LOOP: + for { + + var req interface{} + // select the highest priority channel to read from + // keys channels are buffered so the highest priority ones + // are checked first - integrity can only be guaranteed if writing + // is locked while selecting + if priority != High || len(keys) == 0 { + // selection is not needed if the High priority queue has items + keys = nil + PRIORITIES: + for priority = High; priority >= 0; priority-- { + // the first priority channel that is non-empty will be assigned to keys + if len(self.keys[priority]) > 0 { + glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority) + keys = self.keys[priority] + break PRIORITIES + } + glog.V(logger.Detail).Infof("syncer[%v/%v]: queue: [%v, %v, %v]", self.key.Log(), priority, len(self.keys[High]), len(self.keys[Medium]), len(self.keys[Low])) + // if the input queue is empty on this level, resort to history if there is any + if uint(priority) == histPrior && history != nil { + glog.V(logger.Detail).Infof("syncer[%v]: reading history for %v", self.key.Log(), self.key) + keys = history + break PRIORITIES + } + } + } + + // if peer ready to receive but nothing to send + if keys == nil && deliveryRequest == nil { + // if no items left and switch to waiting mode + glog.V(logger.Detail).Infof("syncer[%v]: buffers consumed. Waiting", self.key.Log()) + newUnsyncedKeys = self.newUnsyncedKeys + } + + // send msg iff + // * peer is ready to receive keys AND ( + // * all queues and history are depleted OR + // * batch full OR + // * all history have been consumed, synced) + if deliveryRequest == nil && + (justSynced || + len(unsynced) > 0 && keys == nil || + len(unsynced) == int(self.SyncBatchSize)) { + justSynced = false + // listen to requests + deliveryRequest = self.deliveryRequest + newUnsyncedKeys = nil // not care about data until next req comes in + // set sync to current counter + // (all nonhistorical outgoing traffic sheduled and persisted + state.LastSeenAt = self.dbAccess.counter() + state.Latest = storage.ZeroKey + glog.V(logger.Detail).Infof("syncer[%v]: sending %v", self.key.Log(), unsynced) + // send the unsynced keys + stateCopy := *state + err := self.unsyncedKeys(unsynced, &stateCopy) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: unable to send unsynced keys: %v", err) + } + self.state = state + glog.V(logger.Debug).Infof("syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy) + unsynced = nil + keys = nil + } + + // process item and add it to the batch + select { + case <-self.quit: + break LOOP + case req, more = <-keys: + if keys == history && !more { + glog.V(logger.Detail).Infof("syncer[%v]: syncing history segment complete", self.key.Log()) + // history channel is closed, waiting for new state (called from sync()) + syncStates = self.syncStates + state.Synced = true // this signals that the current segment is complete + select { + case state.synced <- false: + case <-self.quit: + break LOOP + } + justSynced = true + history = nil + } + case <-deliveryRequest: + glog.V(logger.Detail).Infof("syncer[%v]: peer ready to receive", self.key.Log()) + + // this 1 cap channel can wake up the loop + // signaling that peer is ready to receive unsynced Keys + // the channel is set to nil any further writes will be ignored + deliveryRequest = nil + + case <-newUnsyncedKeys: + glog.V(logger.Detail).Infof("syncer[%v]: new unsynced keys available", self.key.Log()) + // this 1 cap channel can wake up the loop + // signals that data is available to send if peer is ready to receive + newUnsyncedKeys = nil + keys = self.keys[High] + + case state, more = <-syncStates: + // this resets the state + if !more { + state = self.state + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing complete upto %v)", self.key.Log(), priority, state) + state.Synced = true + syncStates = nil + } else { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) syncing history upto %v priority %v)", self.key.Log(), priority, state, histPrior) + state.Synced = false + history = self.syncHistory(state) + // only one history at a time, only allow another one once the + // history channel is closed + syncStates = nil + } + } + if req == nil { + continue LOOP + } + + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) added to unsynced keys: %v", self.key.Log(), priority, req) + keyCounts[priority]++ + keyCount++ + if keys == history { + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v) history item %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + historyCnt++ + } + if sreq, err := self.newSyncRequest(req, priority); err == nil { + // extract key from req + glog.V(logger.Detail).Infof("syncer[%v]: (priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced) + unsynced = append(unsynced, sreq) + } else { + glog.V(logger.Warn).Infof("syncer[%v]: (priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err) + } + + } +} + +// delivery loop +// takes into account priority, send store Requests with chunk (delivery) +// idle blocking if no new deliveries in any of the queues +func (self *syncer) syncDeliveries() { + var req *storeRequestMsgData + p := High + var deliveries chan *storeRequestMsgData + var msg *storeRequestMsgData + var err error + var c = [priorities]int{} + var n = [priorities]int{} + var total, success uint + + for { + deliveries = self.deliveries[p] + select { + case req = <-deliveries: + n[p]++ + c[p]++ + default: + if p == Low { + // blocking, depletion on all channels, no preference for priority + select { + case req = <-self.deliveries[High]: + n[High]++ + case req = <-self.deliveries[Medium]: + n[Medium]++ + case req = <-self.deliveries[Low]: + n[Low]++ + case <-self.quit: + return + } + p = High + } else { + p-- + continue + } + } + total++ + msg, err = self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to create store request for %v: %v", self.key.Log(), req, err) + } else { + err = self.store(msg) + if err != nil { + glog.V(logger.Warn).Infof("syncer[%v]: failed to deliver %v: %v", self.key.Log(), req, err) + } else { + success++ + glog.V(logger.Detail).Infof("syncer[%v]: %v successfully delivered", self.key.Log(), req) + } + } + if total%self.SyncBatchSize == 0 { + glog.V(logger.Debug).Infof("syncer[%v]: deliver Total: %v, Success: %v, High: %v/%v, Medium: %v/%v, Low %v/%v", self.key.Log(), total, success, c[High], n[High], c[Medium], n[Medium], c[Low], n[Low]) + } + } +} + +/* + addRequest handles requests for delivery + it accepts 4 types: + + * storeRequestMsgData: coming from netstore propagate response + * chunk: coming from forwarding (questionable: id?) + * key: from incoming syncRequest + * syncDbEntry: key,id encoded in db + + If sync mode is on for the type of request, then + it sends the request to the keys queue of the correct priority + channel buffered with capacity (SyncBufferSize) + + If sync mode is off then, requests are directly sent to deliveries +*/ +func (self *syncer) addRequest(req interface{}, ty int) { + // retrieve priority for request type name int8 + + priority := self.SyncPriorities[ty] + // sync mode for this type ON + if self.syncF() || ty == DeliverReq { + if self.SyncModes[ty] { + self.addKey(req, priority, self.quit) + } else { + self.addDelivery(req, priority, self.quit) + } + } +} + +// addKey queues sync request for sync confirmation with given priority +// ie the key will go out in an unsyncedKeys message +func (self *syncer) addKey(req interface{}, priority uint, quit chan bool) bool { + select { + case self.keys[priority] <- req: + // this wakes up the unsynced keys loop if idle + select { + case self.newUnsyncedKeys <- true: + default: + } + return true + case <-quit: + return false + } +} + +// addDelivery queues delivery request for with given priority +// ie the chunk will be delivered ASAP mod priority queueing handled by syncdb +// requests are persisted across sessions for correct sync +func (self *syncer) addDelivery(req interface{}, priority uint, quit chan bool) bool { + select { + case self.queues[priority].buffer <- req: + return true + case <-quit: + return false + } +} + +// doDelivery delivers the chunk for the request with given priority +// without queuing +func (self *syncer) doDelivery(req interface{}, priority uint, quit chan bool) bool { + msgdata, err := self.newStoreRequestMsgData(req) + if err != nil { + glog.V(logger.Warn).Infof("unable to deliver request %v: %v", msgdata, err) + return false + } + select { + case self.deliveries[priority] <- msgdata: + return true + case <-quit: + return false + } +} + +// returns the delivery function for given priority +// passed on to syncDb +func (self *syncer) deliver(priority uint) func(req interface{}, quit chan bool) bool { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } +} + +// returns the replay function passed on to syncDb +// depending on sync mode settings for BacklogReq, +// re play of request db backlog sends items via confirmation +// or directly delivers +func (self *syncer) replay() func(req interface{}, quit chan bool) bool { + sync := self.SyncModes[BacklogReq] + priority := self.SyncPriorities[BacklogReq] + // sync mode for this type ON + if sync { + return func(req interface{}, quit chan bool) bool { + return self.addKey(req, priority, quit) + } + } else { + return func(req interface{}, quit chan bool) bool { + return self.doDelivery(req, priority, quit) + } + + } +} + +// given a request, extends it to a full storeRequestMsgData +// polimorphic: see addRequest for the types accepted +func (self *syncer) newStoreRequestMsgData(req interface{}) (*storeRequestMsgData, error) { + + key, id, chunk, sreq, err := parseRequest(req) + if err != nil { + return nil, err + } + + if sreq == nil { + if chunk == nil { + var err error + chunk, err = self.dbAccess.get(key) + if err != nil { + return nil, err + } + } + + sreq = &storeRequestMsgData{ + Id: id, + Key: chunk.Key, + SData: chunk.SData, + } + } + + return sreq, nil +} + +// parse request types and extracts, key, id, chunk, request if available +// does not do chunk lookup ! +func parseRequest(req interface{}) (storage.Key, uint64, *storage.Chunk, *storeRequestMsgData, error) { + var key storage.Key + var entry *syncDbEntry + var chunk *storage.Chunk + var id uint64 + var ok bool + var sreq *storeRequestMsgData + var err error + + if key, ok = req.(storage.Key); ok { + id = generateId() + + } else if entry, ok = req.(*syncDbEntry); ok { + id = binary.BigEndian.Uint64(entry.val[32:]) + key = storage.Key(entry.val[:32]) + + } else if chunk, ok = req.(*storage.Chunk); ok { + key = chunk.Key + id = generateId() + + } else if sreq, ok = req.(*storeRequestMsgData); ok { + key = sreq.Key + } else { + err = fmt.Errorf("type not allowed: %v (%T)", req, req) + } + + return key, id, chunk, sreq, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go new file mode 100644 index 0000000..f72036d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap.go @@ -0,0 +1,283 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package swap + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "os" + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/chequebook/contract" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/services/swap/swap" + "golang.org/x/net/context" +) + +// SwAP Swarm Accounting Protocol with +// SWAP^2 Strategies of Withholding Automatic Payments +// SWAP^3 Accreditation: payment via credit SWAP +// using chequebook pkg for delayed payments +// default parameters + +var ( + autoCashInterval = 300 * time.Second // default interval for autocash + autoCashThreshold = big.NewInt(50000000000000) // threshold that triggers autocash (wei) + autoDepositInterval = 300 * time.Second // default interval for autocash + autoDepositThreshold = big.NewInt(50000000000000) // threshold that triggers autodeposit (wei) + autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei) + buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei) + sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei) + payAt = 100 // threshold that triggers payment {request} (units) + dropAt = 10000 // threshold that triggers disconnect (units) +) + +const ( + chequebookDeployRetries = 5 + chequebookDeployDelay = 1 * time.Second // delay between retries +) + +type SwapParams struct { + *swap.Params + *PayProfile +} + +type SwapProfile struct { + *swap.Profile + *PayProfile +} + +type PayProfile struct { + PublicKey string // check against signature of promise + Contract common.Address // address of chequebook contract + Beneficiary common.Address // recipient address for swarm sales revenue + privateKey *ecdsa.PrivateKey + publicKey *ecdsa.PublicKey + owner common.Address + chbook *chequebook.Chequebook + lock sync.RWMutex +} + +func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams { + pubkey := &prvkey.PublicKey + return &SwapParams{ + PayProfile: &PayProfile{ + PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)), + Contract: contract, + Beneficiary: crypto.PubkeyToAddress(*pubkey), + privateKey: prvkey, + publicKey: pubkey, + owner: crypto.PubkeyToAddress(*pubkey), + }, + Params: &swap.Params{ + Profile: &swap.Profile{ + BuyAt: buyAt, + SellAt: sellAt, + PayAt: uint(payAt), + DropAt: uint(dropAt), + }, + Strategy: &swap.Strategy{ + AutoCashInterval: autoCashInterval, + AutoCashThreshold: autoCashThreshold, + AutoDepositInterval: autoDepositInterval, + AutoDepositThreshold: autoDepositThreshold, + AutoDepositBuffer: autoDepositBuffer, + }, + }, + } +} + +// swap constructor, parameters +// * global chequebook, assume deployed service and +// * the balance is at buffer. +// swap.Add(n) called in netstore +// n > 0 called when sending chunks = receiving retrieve requests +// OR sending cheques. +// n < 0 called when receiving chunks = receiving delivery responses +// OR receiving cheques. + +func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend, proto swap.Protocol) (self *swap.Swap, err error) { + var ( + ctx = context.TODO() + ok bool + in *chequebook.Inbox + out *chequebook.Outbox + ) + + // check if remote chequebook is valid + // insolvent chequebooks suicide so will signal as invalid + // TODO: monitoring a chequebooks events + ok, err = chequebook.ValidateCode(ctx, backend, remote.Contract) + if !ok { + glog.V(logger.Info).Infof("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } else { + // remote contract valid, create inbox + in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err) + } + } + + // check if local chequebook contract is valid + ok, err = chequebook.ValidateCode(ctx, backend, local.Contract) + if !ok { + glog.V(logger.Warn).Infof("unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err) + } else { + out = chequebook.NewOutbox(local.Chequebook(), remote.Beneficiary) + } + + pm := swap.Payment{ + In: in, + Out: out, + Buys: out != nil, + Sells: in != nil, + } + self, err = swap.New(local.Params, pm, proto) + if err != nil { + return + } + // remote profile given (first) in handshake + self.SetRemote(remote.Profile) + var buy, sell string + if self.Buys { + buy = "purchase from peer enabled at " + remote.SellAt.String() + " wei/chunk" + } else { + buy = "purchase from peer disabled" + } + if self.Sells { + sell = "selling to peer enabled at " + local.SellAt.String() + " wei/chunk" + } else { + sell = "selling to peer disabled" + } + glog.V(logger.Warn).Infof("SWAP arrangement with <%v>: %v; %v)", proto, buy, sell) + + return +} + +func (self *SwapParams) Chequebook() *chequebook.Chequebook { + defer self.lock.Unlock() + self.lock.Lock() + return self.chbook +} + +func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey { + return self.privateKey +} + +// func (self *SwapParams) PublicKey() *ecdsa.PublicKey { +// return self.publicKey +// } + +func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) { + self.privateKey = prvkey + self.publicKey = &prvkey.PublicKey +} + +// setChequebook(path, backend) wraps the +// chequebook initialiser and sets up autoDeposit to cover spending. +func (self *SwapParams) SetChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + self.lock.Lock() + contract := self.Contract + self.lock.Unlock() + + valid, err := chequebook.ValidateCode(ctx, backend, contract) + if err != nil { + return err + } else if valid { + return self.newChequebookFromContract(path, backend) + } + return self.deployChequebook(ctx, backend, path) +} + +func (self *SwapParams) deployChequebook(ctx context.Context, backend chequebook.Backend, path string) error { + opts := bind.NewKeyedTransactor(self.privateKey) + opts.Value = self.AutoDepositBuffer + opts.Context = ctx + + glog.V(logger.Info).Infof("Deploying new chequebook (owner: %v)", opts.From.Hex()) + contract, err := deployChequebookLoop(opts, backend) + if err != nil { + glog.V(logger.Error).Infof("unable to deploy new chequebook: %v", err) + return err + } + glog.V(logger.Info).Infof("new chequebook deployed at %v (owner: %v)", contract.Hex(), opts.From.Hex()) + + // need to save config at this point + self.lock.Lock() + self.Contract = contract + err = self.newChequebookFromContract(path, backend) + self.lock.Unlock() + if err != nil { + glog.V(logger.Warn).Infof("error initialising cheque book (owner: %v): %v", opts.From.Hex(), err) + } + return err +} + +// repeatedly tries to deploy a chequebook. +func deployChequebookLoop(opts *bind.TransactOpts, backend chequebook.Backend) (addr common.Address, err error) { + var tx *types.Transaction + for try := 0; try < chequebookDeployRetries; try++ { + if try > 0 { + time.Sleep(chequebookDeployDelay) + } + if _, tx, _, err = contract.DeployChequebook(opts, backend); err != nil { + glog.V(logger.Warn).Infof("can't send chequebook deploy tx (try %d): %v", try, err) + continue + } + if addr, err = bind.WaitDeployed(opts.Context, backend, tx); err != nil { + glog.V(logger.Warn).Infof("chequebook deploy error (try %d): %v", try, err) + continue + } + return addr, nil + } + return addr, err +} + +// initialise the chequebook from a persisted json file or create a new one +// caller holds the lock +func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error { + hexkey := common.Bytes2Hex(self.Contract.Bytes()) + err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm) + if err != nil { + return fmt.Errorf("unable to create directory for chequebooks: %v", err) + } + + chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") + self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) + + if err != nil { + self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) + if err != nil { + glog.V(logger.Warn).Infof("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + return fmt.Errorf("unable to initialise chequebook (owner: %v): %v", self.owner.Hex(), err) + } + } + + self.chbook.AutoDeposit(self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + glog.V(logger.Info).Infof("auto deposit ON for %v -> %v: interval = %v, threshold = %v, buffer = %v)", crypto.PubkeyToAddress(*(self.publicKey)).Hex()[:8], self.Contract.Hex()[:8], self.AutoDepositInterval, self.AutoDepositThreshold, self.AutoDepositBuffer) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go new file mode 100644 index 0000000..9d5da7c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap.go @@ -0,0 +1,254 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package swap + +import ( + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// SwAP Swarm Accounting Protocol with +// Swift Automatic Payments +// a peer to peer micropayment system + +// public swap profile +// public parameters for SWAP, serializable config struct passed in handshake +type Profile struct { + BuyAt *big.Int // accepted max price for chunk + SellAt *big.Int // offered sale price for chunk + PayAt uint // threshold that triggers payment request + DropAt uint // threshold that triggers disconnect +} + +// Strategy encapsulates parameters relating to +// automatic deposit and automatic cashing +type Strategy struct { + AutoCashInterval time.Duration // default interval for autocash + AutoCashThreshold *big.Int // threshold that triggers autocash (wei) + AutoDepositInterval time.Duration // default interval for autocash + AutoDepositThreshold *big.Int // threshold that triggers autodeposit (wei) + AutoDepositBuffer *big.Int // buffer that is surplus for fork protection etc (wei) +} + +// Params extends the public profile with private parameters relating to +// automatic deposit and automatic cashing +type Params struct { + *Profile + *Strategy +} + +// Promise +// 3rd party Provable Promise of Payment +// issued by outPayment +// serialisable to send with Protocol +type Promise interface{} + +// interface for the peer protocol for testing or external alternative payment +type Protocol interface { + Pay(int, Promise) // units, payment proof + Drop() + String() string +} + +// interface for the (delayed) ougoing payment system with autodeposit +type OutPayment interface { + Issue(amount *big.Int) (promise Promise, err error) + AutoDeposit(interval time.Duration, threshold, buffer *big.Int) + Stop() +} + +// interface for the (delayed) incoming payment system with autocash +type InPayment interface { + Receive(promise Promise) (*big.Int, error) + AutoCash(cashInterval time.Duration, maxUncashed *big.Int) + Stop() +} + +// swap is the swarm accounting protocol instance +// * pairwise accounting and payments +type Swap struct { + lock sync.Mutex // mutex for balance access + balance int // units of chunk/retrieval request + local *Params // local peer's swap parameters + remote *Profile // remote peer's swap profile + proto Protocol // peer communication protocol + Payment +} + +type Payment struct { + Out OutPayment // outgoing payment handler + In InPayment // incoming payment handler + Buys, Sells bool +} + +// swap constructor +func New(local *Params, pm Payment, proto Protocol) (self *Swap, err error) { + + self = &Swap{ + local: local, + Payment: pm, + proto: proto, + } + + self.SetParams(local) + + return +} + +// entry point for setting remote swap profile (e.g from handshake or other message) +func (self *Swap) SetRemote(remote *Profile) { + defer self.lock.Unlock() + self.lock.Lock() + + self.remote = remote + if self.Sells && (remote.BuyAt.Cmp(common.Big0) <= 0 || self.local.SellAt.Cmp(common.Big0) <= 0 || remote.BuyAt.Cmp(self.local.SellAt) < 0) { + self.Out.Stop() + self.Sells = false + } + if self.Buys && (remote.SellAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(common.Big0) <= 0 || self.local.BuyAt.Cmp(self.remote.SellAt) < 0) { + self.In.Stop() + self.Buys = false + } + + glog.V(logger.Debug).Infof("<%v> remote profile set: pay at: %v, drop at: %v, buy at: %v, sell at: %v", self.proto, remote.PayAt, remote.DropAt, remote.BuyAt, remote.SellAt) + +} + +// to set strategy dynamically +func (self *Swap) SetParams(local *Params) { + defer self.lock.Unlock() + self.lock.Lock() + self.local = local + self.setParams(local) +} + +// caller holds the lock + +func (self *Swap) setParams(local *Params) { + + if self.Sells { + self.In.AutoCash(local.AutoCashInterval, local.AutoCashThreshold) + glog.V(logger.Info).Infof("<%v> set autocash to every %v, max uncashed limit: %v", self.proto, local.AutoCashInterval, local.AutoCashThreshold) + } else { + glog.V(logger.Info).Infof("<%v> autocash off (not selling)", self.proto) + } + if self.Buys { + self.Out.AutoDeposit(local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + glog.V(logger.Info).Infof("<%v> set autodeposit to every %v, pay at: %v, buffer: %v", self.proto, local.AutoDepositInterval, local.AutoDepositThreshold, local.AutoDepositBuffer) + } else { + glog.V(logger.Info).Infof("<%v> autodeposit off (not buying)", self.proto) + } +} + +// Add(n) +// n > 0 called when promised/provided n units of service +// n < 0 called when used/requested n units of service +func (self *Swap) Add(n int) error { + defer self.lock.Unlock() + self.lock.Lock() + self.balance += n + if !self.Sells && self.balance > 0 { + glog.V(logger.Detail).Infof("<%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance) + } + if !self.Buys && self.balance < 0 { + glog.V(logger.Detail).Infof("<%v> we cannot have debt (balance: %v)", self.proto, self.balance) + return fmt.Errorf("[SWAP] <%v> we cannot have debt (balance: %v)", self.proto, self.balance) + } + if self.balance >= int(self.local.DropAt) { + glog.V(logger.Detail).Infof("<%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + self.proto.Drop() + return fmt.Errorf("[SWAP] <%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt) + } else if self.balance <= -int(self.remote.PayAt) { + self.send() + } + return nil +} + +func (self *Swap) Balance() int { + defer self.lock.Unlock() + self.lock.Lock() + return self.balance +} + +// send(units) is called when payment is due +// In case of insolvency no promise is issued and sent, safe against fraud +// No return value: no error = payment is opportunistic = hang in till dropped +func (self *Swap) send() { + if self.local.BuyAt != nil && self.balance < 0 { + amount := big.NewInt(int64(-self.balance)) + amount.Mul(amount, self.remote.SellAt) + promise, err := self.Out.Issue(amount) + if err != nil { + glog.V(logger.Warn).Infof("<%v> cannot issue cheque (amount: %v, channel: %v): %v", self.proto, amount, self.Out, err) + } else { + glog.V(logger.Warn).Infof("<%v> cheque issued (amount: %v, channel: %v)", self.proto, amount, self.Out) + self.proto.Pay(-self.balance, promise) + self.balance = 0 + } + } +} + +// receive(units, promise) is called by the protocol when a payment msg is received +// returns error if promise is invalid. +func (self *Swap) Receive(units int, promise Promise) error { + if units <= 0 { + return fmt.Errorf("invalid units: %v <= 0", units) + } + + price := new(big.Int).SetInt64(int64(units)) + price.Mul(price, self.local.SellAt) + + amount, err := self.In.Receive(promise) + + if err != nil { + err = fmt.Errorf("invalid promise: %v", err) + } else if price.Cmp(amount) != 0 { + // verify amount = units * unit sale price + return fmt.Errorf("invalid amount: %v = %v * %v (units sent in msg * agreed sale unit price) != %v (signed in cheque)", price, units, self.local.SellAt, amount) + } + if err != nil { + glog.V(logger.Detail).Infof("<%v> invalid promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, err) + return err + } + + // credit remote peer with units + self.Add(-units) + glog.V(logger.Detail).Infof("<%v> received promise (amount: %v, channel: %v): %v", self.proto, amount, self.In, promise) + + return nil +} + +// stop() causes autocash loop to terminate. +// Called after protocol handle loop terminates. +func (self *Swap) Stop() { + defer self.lock.Unlock() + self.lock.Lock() + if self.Buys { + self.Out.Stop() + } + if self.Sells { + self.In.Stop() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap_test.go new file mode 100644 index 0000000..222e077 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/services/swap/swap/swap_test.go @@ -0,0 +1,194 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package swap + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" +) + +type testInPayment struct { + received []*testPromise + autocashInterval time.Duration + autocashLimit *big.Int +} + +type testPromise struct { + amount *big.Int +} + +func (self *testInPayment) Receive(promise Promise) (*big.Int, error) { + p := promise.(*testPromise) + self.received = append(self.received, p) + return p.amount, nil +} + +func (self *testInPayment) AutoCash(interval time.Duration, limit *big.Int) { + self.autocashInterval = interval + self.autocashLimit = limit +} + +func (self *testInPayment) Cash() (string, error) { return "", nil } + +func (self *testInPayment) Stop() {} + +type testOutPayment struct { + deposits []*big.Int + autodepositInterval time.Duration + autodepositThreshold *big.Int + autodepositBuffer *big.Int +} + +func (self *testOutPayment) Issue(amount *big.Int) (promise Promise, err error) { + return &testPromise{amount}, nil +} + +func (self *testOutPayment) Deposit(amount *big.Int) (string, error) { + self.deposits = append(self.deposits, amount) + return "", nil +} + +func (self *testOutPayment) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { + self.autodepositInterval = interval + self.autodepositThreshold = threshold + self.autodepositBuffer = buffer +} + +func (self *testOutPayment) Stop() {} + +type testProtocol struct { + drop bool + amounts []int + promises []*testPromise +} + +func (self *testProtocol) Drop() { + self.drop = true +} + +func (self *testProtocol) String() string { + return "" +} + +func (self *testProtocol) Pay(amount int, promise Promise) { + p := promise.(*testPromise) + self.promises = append(self.promises, p) + self.amounts = append(self.amounts, amount) +} + +func TestSwap(t *testing.T) { + + strategy := &Strategy{ + AutoCashInterval: 1 * time.Second, + AutoCashThreshold: big.NewInt(20), + AutoDepositInterval: 1 * time.Second, + AutoDepositThreshold: big.NewInt(20), + AutoDepositBuffer: big.NewInt(40), + } + + local := &Params{ + Profile: &Profile{ + PayAt: 5, + DropAt: 10, + BuyAt: common.Big3, + SellAt: common.Big2, + }, + Strategy: strategy, + } + + in := &testInPayment{} + out := &testOutPayment{} + proto := &testProtocol{} + + swap, _ := New(local, Payment{In: in, Out: out, Buys: true, Sells: true}, proto) + + if in.autocashInterval != strategy.AutoCashInterval { + t.Fatalf("autocash interval not properly set, expect %v, got %v", strategy.AutoCashInterval, in.autocashInterval) + } + if out.autodepositInterval != strategy.AutoDepositInterval { + t.Fatalf("autodeposit interval not properly set, expect %v, got %v", strategy.AutoDepositInterval, out.autodepositInterval) + } + + remote := &Profile{ + PayAt: 3, + DropAt: 10, + BuyAt: common.Big2, + SellAt: common.Big3, + } + swap.SetRemote(remote) + + swap.Add(9) + if proto.drop { + t.Fatalf("not expected peer to be dropped") + } + swap.Add(1) + if !proto.drop { + t.Fatalf("expected peer to be dropped") + } + if !proto.drop { + t.Fatalf("expected peer to be dropped") + } + proto.drop = false + + swap.Receive(10, &testPromise{big.NewInt(20)}) + if swap.balance != 0 { + t.Fatalf("expected zero balance, got %v", swap.balance) + } + + if len(proto.amounts) != 0 { + t.Fatalf("expected zero balance, got %v", swap.balance) + } + + swap.Add(-2) + if len(proto.amounts) > 0 { + t.Fatalf("expected no payments yet, got %v", proto.amounts) + } + + swap.Add(-1) + if len(proto.amounts) != 1 { + t.Fatalf("expected one payment, got %v", len(proto.amounts)) + } + + if proto.amounts[0] != 3 { + t.Fatalf("expected payment for %v units, got %v", proto.amounts[0], 3) + } + + exp := new(big.Int).Mul(big.NewInt(int64(proto.amounts[0])), remote.SellAt) + if proto.promises[0].amount.Cmp(exp) != 0 { + t.Fatalf("expected payment amount %v, got %v", exp, proto.promises[0].amount) + } + + swap.SetParams(&Params{ + Profile: &Profile{ + PayAt: 5, + DropAt: 10, + BuyAt: common.Big3, + SellAt: common.Big2, + }, + Strategy: &Strategy{ + AutoCashInterval: 2 * time.Second, + AutoCashThreshold: big.NewInt(40), + AutoDepositInterval: 2 * time.Second, + AutoDepositThreshold: big.NewInt(40), + AutoDepositBuffer: big.NewInt(60), + }, + }) + +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go new file mode 100644 index 0000000..d558753 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker.go @@ -0,0 +1,500 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "sync" +) + +/* +The distributed storage implemented in this package requires fix sized chunks of content. + +Chunker is the interface to a component that is responsible for disassembling and assembling larger data. + +TreeChunker implements a Chunker based on a tree structure defined as follows: + +1 each node in the tree including the root and other branching nodes are stored as a chunk. + +2 branching nodes encode data contents that includes the size of the dataslice covered by its entire subtree under the node as well as the hash keys of all its children : +data_{i} := size(subtree_{i}) || key_{j} || key_{j+1} .... || key_{j+n-1} + +3 Leaf nodes encode an actual subslice of the input data. + +4 if data size is not more than maximum chunksize, the data is stored in a single chunk + key = hash(int64(size) + data) + +5 if data size is more than chunksize*branches^l, but no more than chunksize* + branches^(l+1), the data vector is split into slices of chunksize* + branches^l length (except the last one). + key = hash(int64(size) + key(slice0) + key(slice1) + ...) + + The underlying hash function is configurable +*/ + +const ( + defaultHash = "SHA3" // http://golang.org/pkg/hash/#Hash + // defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash + defaultBranches int64 = 128 + // hashSize int64 = hasherfunc.New().Size() // hasher knows about its own length in bytes + // chunksize int64 = branches * hashSize // chunk is defined as this +) + +/* +Tree chunker is a concrete implementation of data chunking. +This chunker works in a simple way, it builds a tree out of the document so that each node either represents a chunk of real data or a chunk of data representing an branching non-leaf node of the tree. In particular each such non-leaf chunk will represent is a concatenation of the hash of its respective children. This scheme simultaneously guarantees data integrity as well as self addressing. Abstract nodes are transparent since their represented size component is strictly greater than their maximum data size, since they encode a subtree. + +If all is well it is possible to implement this by simply composing readers so that no extra allocation or buffering is necessary for the data splitting and joining. This means that in principle there can be direct IO between : memory, file system, network socket (bzz peers storage request is read from the socket). In practice there may be need for several stages of internal buffering. +The hashing itself does use extra copies and allocation though, since it does need it. +*/ + +type ChunkerParams struct { + Branches int64 + Hash string +} + +func NewChunkerParams() *ChunkerParams { + return &ChunkerParams{ + Branches: defaultBranches, + Hash: defaultHash, + } +} + +type TreeChunker struct { + branches int64 + hashFunc Hasher + // calculated + hashSize int64 // self.hashFunc.New().Size() + chunkSize int64 // hashSize* branches + workerCount int +} + +func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) { + self = &TreeChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +// func (self *TreeChunker) KeySize() int64 { +// return self.hashSize +// } + +// String() for pretty printing +func (self *Chunk) String() string { + return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData)) +} + +type hashJob struct { + key Key + chunk []byte + size int64 + parentWg *sync.WaitGroup +} + +func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + if self.chunkSize <= 0 { + panic("chunker must be initialised") + } + + jobC := make(chan *hashJob, 2*processors) + wg := &sync.WaitGroup{} + errC := make(chan error) + quitC := make(chan bool) + + // wwg = workers waitgroup keeps track of hashworkers spawned by this split call + if wwg != nil { + wwg.Add(1) + } + go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) + + depth := 0 + treeSize := self.chunkSize + + // takes lowest depth such that chunksize*HashCount^(depth+1) > size + // power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree. + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + + key := make([]byte, self.hashFunc().Size()) + // this waitgroup member is released after the root hash is calculated + wg.Add(1) + //launch actual recursive function passing the waitgroups + go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, quitC, wg, swg, wwg) + + // closes internal error channel if all subprocesses in the workgroup finished + go func() { + // waiting for all threads to finish + wg.Wait() + // if storage waitgroup is non-nil, we wait for storage to finish too + if swg != nil { + swg.Wait() + } + close(errC) + }() + + select { + case err := <-errC: + if err != nil { + close(quitC) + return nil, err + } + //TODO: add a timeout + } + return key, nil +} + +func (self *TreeChunker) split(depth int, treeSize int64, key Key, data io.Reader, size int64, jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, parentWg, swg, wwg *sync.WaitGroup) { + + for depth > 0 && size < treeSize { + treeSize /= self.branches + depth-- + } + + if depth == 0 { + // leaf nodes -> content chunks + chunkData := make([]byte, size+8) + binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size)) + var readBytes int64 + for readBytes < size { + n, err := data.Read(chunkData[8+readBytes:]) + readBytes += int64(n) + if err != nil && !(err == io.EOF && readBytes == size) { + errC <- err + return + } + } + select { + case jobC <- &hashJob{key, chunkData, size, parentWg}: + case <-quitC: + } + return + } + // dept > 0 + // intermediate chunk containing child nodes hashes + branchCnt := int64((size + treeSize - 1) / treeSize) + + var chunk []byte = make([]byte, branchCnt*self.hashSize+8) + var pos, i int64 + + binary.LittleEndian.PutUint64(chunk[0:8], uint64(size)) + + childrenWg := &sync.WaitGroup{} + var secSize int64 + for i < branchCnt { + // the last item can have shorter data + if size-pos < treeSize { + secSize = size - pos + } else { + secSize = treeSize + } + // the hash of that data + subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize] + + childrenWg.Add(1) + self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, quitC, childrenWg, swg, wwg) + + i++ + pos += treeSize + } + // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk + // parentWg.Add(1) + // go func() { + childrenWg.Wait() + if len(jobC) > self.workerCount && self.workerCount < processors { + if wwg != nil { + wwg.Add(1) + } + self.workerCount++ + go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) + } + select { + case jobC <- &hashJob{key, chunk, size, parentWg}: + case <-quitC: + } +} + +func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, swg, wwg *sync.WaitGroup) { + hasher := self.hashFunc() + if wwg != nil { + defer wwg.Done() + } + for { + select { + + case job, ok := <-jobC: + if !ok { + return + } + // now we got the hashes in the chunk, then hash the chunks + hasher.Reset() + self.hashChunk(hasher, job, chunkC, swg) + case <-quitC: + return + } + } +} + +// The treeChunkers own Hash hashes together +// - the size (of the subtree encoded in the Chunk) +// - the Chunk, ie. the contents read from the input reader +func (self *TreeChunker) hashChunk(hasher hash.Hash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) { + hasher.Write(job.chunk) + h := hasher.Sum(nil) + newChunk := &Chunk{ + Key: h, + SData: job.chunk, + Size: job.size, + wg: swg, + } + + // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk) + copy(job.key, h) + // send off new chunk to storage + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + } + job.parentWg.Done() + + if chunkC != nil { + chunkC <- newChunk + } +} + +// LazyChunkReader implements LazySectionReader +type LazyChunkReader struct { + key Key // root key + chunkC chan *Chunk // chunk channel to send retrieve requests on + chunk *Chunk // size of the entire subtree + off int64 // offset + chunkSize int64 // inherit from chunker + branches int64 // inherit from chunker + hashSize int64 // inherit from chunker +} + +// implements the Joiner interface +func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader { + + return &LazyChunkReader{ + key: key, + chunkC: chunkC, + chunkSize: self.chunkSize, + branches: self.branches, + hashSize: self.hashSize, + } +} + +// Size is meant to be called on the LazySectionReader +func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) { + if self.chunk != nil { + return self.chunk.Size, nil + } + chunk := retrieve(self.key, self.chunkC, quitC) + if chunk == nil { + select { + case <-quitC: + return 0, errors.New("aborted") + default: + return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex()) + } + } + self.chunk = chunk + return chunk.Size, nil +} + +// read at can be called numerous times +// concurrent reads are allowed +// Size() needs to be called synchronously on the LazyChunkReader first +func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) { + // this is correct, a swarm doc cannot be zero length, so no EOF is expected + if len(b) == 0 { + return 0, nil + } + quitC := make(chan bool) + size, err := self.Size(quitC) + if err != nil { + return 0, err + } + + errC := make(chan error) + + // } + var treeSize int64 + var depth int + // calculate depth and max treeSize + treeSize = self.chunkSize + for ; treeSize < size; treeSize *= self.branches { + depth++ + } + wg := sync.WaitGroup{} + wg.Add(1) + go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC) + go func() { + wg.Wait() + close(errC) + }() + + err = <-errC + if err != nil { + close(quitC) + + return 0, err + } + if off+int64(len(b)) >= size { + return len(b), io.EOF + } + return len(b), nil +} + +func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) { + defer parentWg.Done() + // return NewDPA(&LocalStore{}) + + // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + // find appropriate block level + for chunk.Size < treeSize && depth > 0 { + treeSize /= self.branches + depth-- + } + + // leaf chunk found + if depth == 0 { + extra := 8 + eoff - int64(len(chunk.SData)) + if extra > 0 { + eoff -= extra + } + copy(b, chunk.SData[8+off:8+eoff]) + return // simply give back the chunks reader for content chunks + } + + // subtree + start := off / treeSize + end := (eoff + treeSize - 1) / treeSize + + wg := &sync.WaitGroup{} + defer wg.Wait() + + for i := start; i < end; i++ { + soff := i * treeSize + roff := soff + seoff := soff + treeSize + + if soff < off { + soff = off + } + if seoff > eoff { + seoff = eoff + } + if depth > 1 { + wg.Wait() + } + wg.Add(1) + go func(j int64) { + childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize] + chunk := retrieve(childKey, self.chunkC, quitC) + if chunk == nil { + select { + case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize): + case <-quitC: + } + return + } + if soff < off { + soff = off + } + self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC) + }(i) + } //for +} + +// the helper method submits chunks for a key to a oueue (DPA) and +// block until they time out or arrive +// abort if quitC is readable +func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk { + chunk := &Chunk{ + Key: key, + C: make(chan bool), // close channel to signal data delivery + } + // submit chunk for retrieval + select { + case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally) + case <-quitC: + return nil + } + // waiting for the chunk retrieval + select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + case <-quitC: + // this is how we control process leakage (quitC is closed once join is finished (after timeout)) + return nil + case <-chunk.C: // bells are ringing, data have been delivered + } + if len(chunk.SData) == 0 { + return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + + } + return chunk +} + +// Read keeps a cursor so cannot be called simulateously, see ReadAt +func (self *LazyChunkReader) Read(b []byte) (read int, err error) { + read, err = self.ReadAt(b, self.off) + + self.off += int64(read) + return +} + +// completely analogous to standard SectionReader implementation +var errWhence = errors.New("Seek: invalid whence") +var errOffset = errors.New("Seek: invalid offset") + +func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case 0: + offset += 0 + case 1: + offset += s.off + case 2: + if s.chunk == nil { //seek from the end requires rootchunk for size. call Size first + _, err := s.Size(nil) + if err != nil { + return 0, fmt.Errorf("can't get size: %v", err) + } + } + offset += s.chunk.Size + } + + if offset < 0 { + return 0, errOffset + } + s.off = offset + return offset, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker_test.go new file mode 100644 index 0000000..40f8702 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/chunker_test.go @@ -0,0 +1,315 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "fmt" + "io" + "runtime" + "sync" + "testing" + "time" +) + +/* +Tests TreeChunker by splitting and joining a random byte slice +*/ + +type test interface { + Fatalf(string, ...interface{}) + Logf(string, ...interface{}) +} + +type chunkerTester struct { + inputs map[uint64][]byte + chunks map[string]*Chunk + t test +} + +func (self *chunkerTester) checkChunks(t *testing.T, want int) { + l := len(self.chunks) + if l != want { + t.Errorf("expected %v chunks, got %v", want, l) + } +} + +func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, chunkC chan *Chunk, swg *sync.WaitGroup, expectedError error) (key Key) { + // reset + self.chunks = make(map[string]*Chunk) + + if self.inputs == nil { + self.inputs = make(map[uint64][]byte) + } + + quitC := make(chan bool) + timeout := time.After(600 * time.Second) + if chunkC != nil { + go func() { + for { + select { + case <-timeout: + self.t.Fatalf("Join timeout error") + case <-quitC: + return + case chunk := <-chunkC: + // self.chunks = append(self.chunks, chunk) + self.chunks[chunk.Key.String()] = chunk + if chunk.wg != nil { + chunk.wg.Done() + } + } + } + }() + } + key, err := chunker.Split(data, size, chunkC, swg, nil) + if err != nil && expectedError == nil { + self.t.Fatalf("Split error: %v", err) + } else if expectedError != nil && (err == nil || err.Error() != expectedError.Error()) { + self.t.Fatalf("Not receiving the correct error! Expected %v, received %v", expectedError, err) + } + if chunkC != nil { + if swg != nil { + swg.Wait() + } + close(quitC) + } + return +} + +func (self *chunkerTester) Join(chunker Chunker, key Key, c int, chunkC chan *Chunk, quitC chan bool) LazySectionReader { + // reset but not the chunks + + reader := chunker.Join(key, chunkC) + + timeout := time.After(600 * time.Second) + i := 0 + go func() { + for { + select { + case <-timeout: + self.t.Fatalf("Join timeout error") + + case chunk, ok := <-chunkC: + if !ok { + close(quitC) + return + } + // this just mocks the behaviour of a chunk store retrieval + stored, success := self.chunks[chunk.Key.String()] + if !success { + self.t.Fatalf("not found") + return + } + chunk.SData = stored.SData + chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + close(chunk.C) + i++ + } + } + }() + return reader +} + +func testRandomBrokenData(splitter Splitter, n int, tester *chunkerTester) { + data := io.LimitReader(rand.Reader, int64(n)) + brokendata := brokenLimitReader(data, n, n/2) + + buf := make([]byte, n) + _, err := brokendata.Read(buf) + if err == nil || err.Error() != "Broken reader" { + tester.t.Fatalf("Broken reader is not broken, hence broken. Returns: %v", err) + } + + data = io.LimitReader(rand.Reader, int64(n)) + brokendata = brokenLimitReader(data, n, n/2) + + chunkC := make(chan *Chunk, 1000) + swg := &sync.WaitGroup{} + + key := tester.Split(splitter, brokendata, int64(n), chunkC, swg, fmt.Errorf("Broken reader")) + tester.t.Logf(" Key = %v\n", key) +} + +func testRandomData(splitter Splitter, n int, tester *chunkerTester) { + if tester.inputs == nil { + tester.inputs = make(map[uint64][]byte) + } + input, found := tester.inputs[uint64(n)] + var data io.Reader + if !found { + data, input = testDataReaderAndSlice(n) + tester.inputs[uint64(n)] = input + } else { + data = io.LimitReader(bytes.NewReader(input), int64(n)) + } + + chunkC := make(chan *Chunk, 1000) + swg := &sync.WaitGroup{} + + key := tester.Split(splitter, data, int64(n), chunkC, swg, nil) + tester.t.Logf(" Key = %v\n", key) + + chunkC = make(chan *Chunk, 1000) + quitC := make(chan bool) + + chunker := NewTreeChunker(NewChunkerParams()) + reader := tester.Join(chunker, key, 0, chunkC, quitC) + output := make([]byte, n) + r, err := reader.Read(output) + if r != n || err != io.EOF { + tester.t.Fatalf("read error read: %v n = %v err = %v\n", r, n, err) + } + if input != nil { + if !bytes.Equal(output, input) { + tester.t.Fatalf("input and output mismatch\n IN: %v\nOUT: %v\n", input, output) + } + } + close(chunkC) + <-quitC +} + +func TestRandomData(t *testing.T) { + // sizes := []int{123456} + sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 123456, 2345678} + tester := &chunkerTester{t: t} + chunker := NewTreeChunker(NewChunkerParams()) + for _, s := range sizes { + testRandomData(chunker, s, tester) + } + pyramid := NewPyramidChunker(NewChunkerParams()) + for _, s := range sizes { + testRandomData(pyramid, s, tester) + } +} + +func TestRandomBrokenData(t *testing.T) { + sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 123456, 2345678} + tester := &chunkerTester{t: t} + chunker := NewTreeChunker(NewChunkerParams()) + for _, s := range sizes { + testRandomBrokenData(chunker, s, tester) + t.Logf("done size: %v", s) + } +} + +func readAll(reader LazySectionReader, result []byte) { + size := int64(len(result)) + + var end int64 + for pos := int64(0); pos < size; pos += 1000 { + if pos+1000 > size { + end = size + } else { + end = pos + 1000 + } + reader.ReadAt(result[pos:end], pos) + } +} + +func benchReadAll(reader LazySectionReader) { + size, _ := reader.Size(nil) + output := make([]byte, 1000) + for pos := int64(0); pos < size; pos += 1000 { + reader.ReadAt(output, pos) + } +} + +func benchmarkJoin(n int, t *testing.B) { + t.ReportAllocs() + for i := 0; i < t.N; i++ { + chunker := NewTreeChunker(NewChunkerParams()) + tester := &chunkerTester{t: t} + data := testDataReader(n) + + chunkC := make(chan *Chunk, 1000) + swg := &sync.WaitGroup{} + + key := tester.Split(chunker, data, int64(n), chunkC, swg, nil) + // t.StartTimer() + chunkC = make(chan *Chunk, 1000) + quitC := make(chan bool) + reader := tester.Join(chunker, key, i, chunkC, quitC) + benchReadAll(reader) + close(chunkC) + <-quitC + // t.StopTimer() + } + stats := new(runtime.MemStats) + runtime.ReadMemStats(stats) + fmt.Println(stats.Sys) +} + +func benchmarkSplitTree(n int, t *testing.B) { + t.ReportAllocs() + for i := 0; i < t.N; i++ { + chunker := NewTreeChunker(NewChunkerParams()) + tester := &chunkerTester{t: t} + data := testDataReader(n) + tester.Split(chunker, data, int64(n), nil, nil, nil) + } + stats := new(runtime.MemStats) + runtime.ReadMemStats(stats) + fmt.Println(stats.Sys) +} + +func benchmarkSplitPyramid(n int, t *testing.B) { + t.ReportAllocs() + for i := 0; i < t.N; i++ { + splitter := NewPyramidChunker(NewChunkerParams()) + tester := &chunkerTester{t: t} + data := testDataReader(n) + tester.Split(splitter, data, int64(n), nil, nil, nil) + } + stats := new(runtime.MemStats) + runtime.ReadMemStats(stats) + fmt.Println(stats.Sys) +} + +func BenchmarkJoin_2(t *testing.B) { benchmarkJoin(100, t) } +func BenchmarkJoin_3(t *testing.B) { benchmarkJoin(1000, t) } +func BenchmarkJoin_4(t *testing.B) { benchmarkJoin(10000, t) } +func BenchmarkJoin_5(t *testing.B) { benchmarkJoin(100000, t) } +func BenchmarkJoin_6(t *testing.B) { benchmarkJoin(1000000, t) } +func BenchmarkJoin_7(t *testing.B) { benchmarkJoin(10000000, t) } +func BenchmarkJoin_8(t *testing.B) { benchmarkJoin(100000000, t) } + +func BenchmarkSplitTree_2(t *testing.B) { benchmarkSplitTree(100, t) } +func BenchmarkSplitTree_2h(t *testing.B) { benchmarkSplitTree(500, t) } +func BenchmarkSplitTree_3(t *testing.B) { benchmarkSplitTree(1000, t) } +func BenchmarkSplitTree_3h(t *testing.B) { benchmarkSplitTree(5000, t) } +func BenchmarkSplitTree_4(t *testing.B) { benchmarkSplitTree(10000, t) } +func BenchmarkSplitTree_4h(t *testing.B) { benchmarkSplitTree(50000, t) } +func BenchmarkSplitTree_5(t *testing.B) { benchmarkSplitTree(100000, t) } +func BenchmarkSplitTree_6(t *testing.B) { benchmarkSplitTree(1000000, t) } +func BenchmarkSplitTree_7(t *testing.B) { benchmarkSplitTree(10000000, t) } +func BenchmarkSplitTree_8(t *testing.B) { benchmarkSplitTree(100000000, t) } + +func BenchmarkSplitPyramid_2(t *testing.B) { benchmarkSplitPyramid(100, t) } +func BenchmarkSplitPyramid_2h(t *testing.B) { benchmarkSplitPyramid(500, t) } +func BenchmarkSplitPyramid_3(t *testing.B) { benchmarkSplitPyramid(1000, t) } +func BenchmarkSplitPyramid_3h(t *testing.B) { benchmarkSplitPyramid(5000, t) } +func BenchmarkSplitPyramid_4(t *testing.B) { benchmarkSplitPyramid(10000, t) } +func BenchmarkSplitPyramid_4h(t *testing.B) { benchmarkSplitPyramid(50000, t) } +func BenchmarkSplitPyramid_5(t *testing.B) { benchmarkSplitPyramid(100000, t) } +func BenchmarkSplitPyramid_6(t *testing.B) { benchmarkSplitPyramid(1000000, t) } +func BenchmarkSplitPyramid_7(t *testing.B) { benchmarkSplitPyramid(10000000, t) } +func BenchmarkSplitPyramid_8(t *testing.B) { benchmarkSplitPyramid(100000000, t) } + +// godep go test -bench ./swarm/storage -cpuprofile cpu.out -memprofile mem.out diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/common_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/common_test.go new file mode 100644 index 0000000..2a83f47 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/common_test.go @@ -0,0 +1,117 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +type brokenLimitedReader struct { + lr io.Reader + errAt int + off int + size int +} + +func brokenLimitReader(data io.Reader, size int, errAt int) *brokenLimitedReader { + return &brokenLimitedReader{ + lr: data, + errAt: errAt, + size: size, + } +} + +func testDataReader(l int) (r io.Reader) { + return io.LimitReader(rand.Reader, int64(l)) +} + +func (self *brokenLimitedReader) Read(buf []byte) (int, error) { + if self.off+len(buf) > self.errAt { + return 0, fmt.Errorf("Broken reader") + } + self.off += len(buf) + return self.lr.Read(buf) +} + +func testDataReaderAndSlice(l int) (r io.Reader, slice []byte) { + slice = make([]byte, l) + if _, err := rand.Read(slice); err != nil { + panic("rand error") + } + r = io.LimitReader(bytes.NewReader(slice), int64(l)) + return +} + +func testStore(m ChunkStore, l int64, branches int64, t *testing.T) { + + chunkC := make(chan *Chunk) + go func() { + for chunk := range chunkC { + m.Put(chunk) + if chunk.wg != nil { + chunk.wg.Done() + } + } + }() + chunker := NewTreeChunker(&ChunkerParams{ + Branches: branches, + Hash: defaultHash, + }) + swg := &sync.WaitGroup{} + key, _ := chunker.Split(rand.Reader, l, chunkC, swg, nil) + swg.Wait() + close(chunkC) + chunkC = make(chan *Chunk) + + quit := make(chan bool) + + go func() { + for ch := range chunkC { + go func(chunk *Chunk) { + storedChunk, err := m.Get(chunk.Key) + if err == notFound { + glog.V(logger.Detail).Infof("chunk '%v' not found", chunk.Key.Log()) + } else if err != nil { + glog.V(logger.Detail).Infof("error retrieving chunk %v: %v", chunk.Key.Log(), err) + } else { + chunk.SData = storedChunk.SData + chunk.Size = storedChunk.Size + } + glog.V(logger.Detail).Infof("chunk '%v' not found", chunk.Key.Log()) + close(chunk.C) + }(ch) + } + close(quit) + }() + r := chunker.Join(key, chunkC) + + b := make([]byte, l) + n, err := r.ReadAt(b, 0) + if err != io.EOF { + t.Fatalf("read error (%v/%v) %v", n, l, err) + } + close(chunkC) + <-quit +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go new file mode 100644 index 0000000..2532490 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/database.go @@ -0,0 +1,99 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +// this is a clone of an earlier state of the ethereum ethdb/database +// no need for queueing/caching + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/compression/rle" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +const openFileLimit = 128 + +type LDBDatabase struct { + db *leveldb.DB + comp bool +} + +func NewLDBDatabase(file string) (*LDBDatabase, error) { + // Open the db + db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit}) + if err != nil { + return nil, err + } + + database := &LDBDatabase{db: db, comp: false} + + return database, nil +} + +func (self *LDBDatabase) Put(key []byte, value []byte) { + if self.comp { + value = rle.Compress(value) + } + + err := self.db.Put(key, value, nil) + if err != nil { + fmt.Println("Error put", err) + } +} + +func (self *LDBDatabase) Get(key []byte) ([]byte, error) { + dat, err := self.db.Get(key, nil) + if err != nil { + return nil, err + } + + if self.comp { + return rle.Decompress(dat) + } + + return dat, nil +} + +func (self *LDBDatabase) Delete(key []byte) error { + return self.db.Delete(key, nil) +} + +func (self *LDBDatabase) LastKnownTD() []byte { + data, _ := self.Get([]byte("LTD")) + + if len(data) == 0 { + data = []byte{0x0} + } + + return data +} + +func (self *LDBDatabase) NewIterator() iterator.Iterator { + return self.db.NewIterator(nil, nil) +} + +func (self *LDBDatabase) Write(batch *leveldb.Batch) error { + return self.db.Write(batch, nil) +} + +func (self *LDBDatabase) Close() { + // Close the leveldb database + self.db.Close() +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go new file mode 100644 index 0000000..e320cd3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore.go @@ -0,0 +1,516 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// disk storage layer for the package bzz +// DbStore implements the ChunkStore interface and is used by the DPA as +// persistent storage of chunks +// it implements purging based on access count allowing for external control of +// max capacity + +package storage + +import ( + "bytes" + "encoding/binary" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" +) + +const ( + defaultDbCapacity = 5000000 + defaultRadius = 0 // not yet used + + gcArraySize = 10000 + gcArrayFreeRatio = 0.1 + + // key prefixes for leveldb storage + kpIndex = 0 + kpData = 1 +) + +var ( + keyAccessCnt = []byte{2} + keyEntryCnt = []byte{3} + keyDataIdx = []byte{4} + keyGCPos = []byte{5} +) + +type gcItem struct { + idx uint64 + value uint64 + idxKey []byte +} + +type DbStore struct { + db *LDBDatabase + + // this should be stored in db, accessed transactionally + entryCnt, accessCnt, dataIdx, capacity uint64 + + gcPos, gcStartPos []byte + gcArray []*gcItem + + hashfunc Hasher + + lock sync.Mutex +} + +func NewDbStore(path string, hash Hasher, capacity uint64, radius int) (s *DbStore, err error) { + s = new(DbStore) + + s.hashfunc = hash + + s.db, err = NewLDBDatabase(path) + if err != nil { + return + } + + s.setCapacity(capacity) + + s.gcStartPos = make([]byte, 1) + s.gcStartPos[0] = kpIndex + s.gcArray = make([]*gcItem, gcArraySize) + + data, _ := s.db.Get(keyEntryCnt) + s.entryCnt = BytesToU64(data) + data, _ = s.db.Get(keyAccessCnt) + s.accessCnt = BytesToU64(data) + data, _ = s.db.Get(keyDataIdx) + s.dataIdx = BytesToU64(data) + s.gcPos, _ = s.db.Get(keyGCPos) + if s.gcPos == nil { + s.gcPos = s.gcStartPos + } + return +} + +type dpaDBIndex struct { + Idx uint64 + Access uint64 +} + +func BytesToU64(data []byte) uint64 { + if len(data) < 8 { + return 0 + } + return binary.LittleEndian.Uint64(data) +} + +func U64ToBytes(val uint64) []byte { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, val) + return data +} + +func getIndexGCValue(index *dpaDBIndex) uint64 { + return index.Access +} + +func (s *DbStore) updateIndexAccess(index *dpaDBIndex) { + index.Access = s.accessCnt +} + +func getIndexKey(hash Key) []byte { + HashSize := len(hash) + key := make([]byte, HashSize+1) + key[0] = 0 + copy(key[1:], hash[:]) + return key +} + +func getDataKey(idx uint64) []byte { + key := make([]byte, 9) + key[0] = 1 + binary.BigEndian.PutUint64(key[1:9], idx) + + return key +} + +func encodeIndex(index *dpaDBIndex) []byte { + data, _ := rlp.EncodeToBytes(index) + return data +} + +func encodeData(chunk *Chunk) []byte { + return chunk.SData +} + +func decodeIndex(data []byte, index *dpaDBIndex) { + dec := rlp.NewStream(bytes.NewReader(data), 0) + dec.Decode(index) +} + +func decodeData(data []byte, chunk *Chunk) { + chunk.SData = data + chunk.Size = int64(binary.LittleEndian.Uint64(data[0:8])) +} + +func gcListPartition(list []*gcItem, left int, right int, pivotIndex int) int { + pivotValue := list[pivotIndex].value + dd := list[pivotIndex] + list[pivotIndex] = list[right] + list[right] = dd + storeIndex := left + for i := left; i < right; i++ { + if list[i].value < pivotValue { + dd = list[storeIndex] + list[storeIndex] = list[i] + list[i] = dd + storeIndex++ + } + } + dd = list[storeIndex] + list[storeIndex] = list[right] + list[right] = dd + return storeIndex +} + +func gcListSelect(list []*gcItem, left int, right int, n int) int { + if left == right { + return left + } + pivotIndex := (left + right) / 2 + pivotIndex = gcListPartition(list, left, right, pivotIndex) + if n == pivotIndex { + return n + } else { + if n < pivotIndex { + return gcListSelect(list, left, pivotIndex-1, n) + } else { + return gcListSelect(list, pivotIndex+1, right, n) + } + } +} + +func (s *DbStore) collectGarbage(ratio float32) { + it := s.db.NewIterator() + it.Seek(s.gcPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + gcnt := 0 + + for (gcnt < gcArraySize) && (uint64(gcnt) < s.entryCnt) { + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + it.Seek(s.gcStartPos) + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + + if (s.gcPos == nil) || (s.gcPos[0] != kpIndex) { + break + } + + gci := new(gcItem) + gci.idxKey = s.gcPos + var index dpaDBIndex + decodeIndex(it.Value(), &index) + gci.idx = index.Idx + // the smaller, the more likely to be gc'd + gci.value = getIndexGCValue(&index) + s.gcArray[gcnt] = gci + gcnt++ + it.Next() + if it.Valid() { + s.gcPos = it.Key() + } else { + s.gcPos = nil + } + } + it.Release() + + cutidx := gcListSelect(s.gcArray, 0, gcnt-1, int(float32(gcnt)*ratio)) + cutval := s.gcArray[cutidx].value + + // fmt.Print(gcnt, " ", s.entryCnt, " ") + + // actual gc + for i := 0; i < gcnt; i++ { + if s.gcArray[i].value <= cutval { + s.delete(s.gcArray[i].idx, s.gcArray[i].idxKey) + } + } + + // fmt.Println(s.entryCnt) + + s.db.Put(keyGCPos, s.gcPos) +} + +func (s *DbStore) Cleanup() { + //Iterates over the database and checks that there are no faulty chunks + it := s.db.NewIterator() + startPosition := []byte{kpIndex} + it.Seek(startPosition) + var key []byte + var errorsFound, total int + for it.Valid() { + key = it.Key() + if (key == nil) || (key[0] != kpIndex) { + break + } + total++ + var index dpaDBIndex + decodeIndex(it.Value(), &index) + + data, err := s.db.Get(getDataKey(index.Idx)) + if err != nil { + glog.V(logger.Warn).Infof("Chunk %x found but could not be accessed: %v", key[:], err) + s.delete(index.Idx, getIndexKey(key[1:])) + errorsFound++ + } else { + hasher := s.hashfunc() + hasher.Write(data) + hash := hasher.Sum(nil) + if !bytes.Equal(hash, key[1:]) { + glog.V(logger.Warn).Infof("Found invalid chunk. Hash mismatch. hash=%x, key=%x", hash, key[:]) + s.delete(index.Idx, getIndexKey(key[1:])) + errorsFound++ + } + } + it.Next() + } + it.Release() + glog.V(logger.Warn).Infof("Found %v errors out of %v entries", errorsFound, total) +} + +func (s *DbStore) delete(idx uint64, idxKey []byte) { + batch := new(leveldb.Batch) + batch.Delete(idxKey) + batch.Delete(getDataKey(idx)) + s.entryCnt-- + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.db.Write(batch) +} + +func (s *DbStore) Counter() uint64 { + s.lock.Lock() + defer s.lock.Unlock() + return s.dataIdx +} + +func (s *DbStore) Put(chunk *Chunk) { + s.lock.Lock() + defer s.lock.Unlock() + + ikey := getIndexKey(chunk.Key) + var index dpaDBIndex + + if s.tryAccessIdx(ikey, &index) { + if chunk.dbStored != nil { + close(chunk.dbStored) + } + glog.V(logger.Detail).Infof("Storing to DB: chunk already exists, only update access") + return // already exists, only update access + } + + data := encodeData(chunk) + //data := ethutil.Encode([]interface{}{entry}) + + if s.entryCnt >= s.capacity { + s.collectGarbage(gcArrayFreeRatio) + } + + batch := new(leveldb.Batch) + + batch.Put(getDataKey(s.dataIdx), data) + + index.Idx = s.dataIdx + s.updateIndexAccess(&index) + + idata := encodeIndex(&index) + batch.Put(ikey, idata) + + batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) + s.entryCnt++ + batch.Put(keyDataIdx, U64ToBytes(s.dataIdx)) + s.dataIdx++ + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + + s.db.Write(batch) + if chunk.dbStored != nil { + close(chunk.dbStored) + } + glog.V(logger.Detail).Infof("DbStore.Put: %v. db storage counter: %v ", chunk.Key.Log(), s.dataIdx) +} + +// try to find index; if found, update access cnt and return true +func (s *DbStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool { + idata, err := s.db.Get(ikey) + if err != nil { + return false + } + decodeIndex(idata, index) + + batch := new(leveldb.Batch) + + batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) + s.accessCnt++ + s.updateIndexAccess(index) + idata = encodeIndex(index) + batch.Put(ikey, idata) + + s.db.Write(batch) + + return true +} + +func (s *DbStore) Get(key Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + + if s.tryAccessIdx(getIndexKey(key), &index) { + var data []byte + data, err = s.db.Get(getDataKey(index.Idx)) + if err != nil { + glog.V(logger.Detail).Infof("DBStore: Chunk %v found but could not be accessed: %v", key.Log(), err) + s.delete(index.Idx, getIndexKey(key)) + return + } + + hasher := s.hashfunc() + hasher.Write(data) + hash := hasher.Sum(nil) + if !bytes.Equal(hash, key) { + s.delete(index.Idx, getIndexKey(key)) + panic("Invalid Chunk in Database. Please repair with command: 'swarm cleandb'") + } + + chunk = &Chunk{ + Key: key, + } + decodeData(data, chunk) + } else { + err = notFound + } + + return + +} + +func (s *DbStore) updateAccessCnt(key Key) { + + s.lock.Lock() + defer s.lock.Unlock() + + var index dpaDBIndex + s.tryAccessIdx(getIndexKey(key), &index) // result_chn == nil, only update access cnt + +} + +func (s *DbStore) setCapacity(c uint64) { + + s.lock.Lock() + defer s.lock.Unlock() + + s.capacity = c + + if s.entryCnt > c { + var ratio float32 + ratio = float32(1.01) - float32(c)/float32(s.entryCnt) + if ratio < gcArrayFreeRatio { + ratio = gcArrayFreeRatio + } + if ratio > 1 { + ratio = 1 + } + for s.entryCnt > c { + s.collectGarbage(ratio) + } + } +} + +func (s *DbStore) getEntryCnt() uint64 { + return s.entryCnt +} + +func (s *DbStore) Close() { + s.db.Close() +} + +// describes a section of the DbStore representing the unsynced +// domain relevant to a peer +// Start - Stop designate a continuous area Keys in an address space +// typically the addresses closer to us than to the peer but not closer +// another closer peer in between +// From - To designates a time interval typically from the last disconnect +// till the latest connection (real time traffic is relayed) +type DbSyncState struct { + Start, Stop Key + First, Last uint64 +} + +// implements the syncer iterator interface +// iterates by storage index (~ time of storage = first entry to db) +type dbSyncIterator struct { + it iterator.Iterator + DbSyncState +} + +// initialises a sync iterator from a syncToken (passed in with the handshake) +func (self *DbStore) NewSyncIterator(state DbSyncState) (si *dbSyncIterator, err error) { + if state.First > state.Last { + return nil, fmt.Errorf("no entries found") + } + si = &dbSyncIterator{ + it: self.db.NewIterator(), + DbSyncState: state, + } + si.it.Seek(getIndexKey(state.Start)) + return si, nil +} + +// walk the area from Start to Stop and returns items within time interval +// First to Last +func (self *dbSyncIterator) Next() (key Key) { + for self.it.Valid() { + dbkey := self.it.Key() + if dbkey[0] != 0 { + break + } + key = Key(make([]byte, len(dbkey)-1)) + copy(key[:], dbkey[1:]) + if bytes.Compare(key[:], self.Start) <= 0 { + self.it.Next() + continue + } + if bytes.Compare(key[:], self.Stop) > 0 { + break + } + var index dpaDBIndex + decodeIndex(self.it.Value(), &index) + self.it.Next() + if (index.Idx >= self.First) && (index.Idx < self.Last) { + return + } + } + self.it.Release() + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore_test.go new file mode 100644 index 0000000..ddce7cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dbstore_test.go @@ -0,0 +1,191 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func initDbStore(t *testing.T) *DbStore { + dir, err := ioutil.TempDir("", "bzz-storage-test") + if err != nil { + t.Fatal(err) + } + m, err := NewDbStore(dir, MakeHashFunc(defaultHash), defaultDbCapacity, defaultRadius) + if err != nil { + t.Fatal("can't create store:", err) + } + return m +} + +func testDbStore(l int64, branches int64, t *testing.T) { + m := initDbStore(t) + defer m.Close() + testStore(m, l, branches, t) +} + +func TestDbStore128_0x1000000(t *testing.T) { + testDbStore(0x1000000, 128, t) +} + +func TestDbStore128_10000_(t *testing.T) { + testDbStore(10000, 128, t) +} + +func TestDbStore128_1000_(t *testing.T) { + testDbStore(1000, 128, t) +} + +func TestDbStore128_100_(t *testing.T) { + testDbStore(100, 128, t) +} + +func TestDbStore2_100_(t *testing.T) { + testDbStore(100, 2, t) +} + +func TestDbStoreNotFound(t *testing.T) { + m := initDbStore(t) + defer m.Close() + _, err := m.Get(ZeroKey) + if err != notFound { + t.Errorf("Expected notFound, got %v", err) + } +} + +func TestDbStoreSyncIterator(t *testing.T) { + m := initDbStore(t) + defer m.Close() + keys := []Key{ + Key(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")), + Key(common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")), + Key(common.Hex2Bytes("5000000000000000000000000000000000000000000000000000000000000000")), + Key(common.Hex2Bytes("3000000000000000000000000000000000000000000000000000000000000000")), + Key(common.Hex2Bytes("2000000000000000000000000000000000000000000000000000000000000000")), + Key(common.Hex2Bytes("1000000000000000000000000000000000000000000000000000000000000000")), + } + for _, key := range keys { + m.Put(NewChunk(key, nil)) + } + it, err := m.NewSyncIterator(DbSyncState{ + Start: Key(common.Hex2Bytes("1000000000000000000000000000000000000000000000000000000000000000")), + Stop: Key(common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")), + First: 2, + Last: 4, + }) + if err != nil { + t.Fatalf("unexpected error creating NewSyncIterator") + } + + var chunk Key + var res []Key + for { + chunk = it.Next() + if chunk == nil { + break + } + res = append(res, chunk) + } + if len(res) != 1 { + t.Fatalf("Expected 1 chunk, got %v: %v", len(res), res) + } + if !bytes.Equal(res[0][:], keys[3]) { + t.Fatalf("Expected %v chunk, got %v", keys[3], res[0]) + } + + if err != nil { + t.Fatalf("unexpected error creating NewSyncIterator") + } + + it, err = m.NewSyncIterator(DbSyncState{ + Start: Key(common.Hex2Bytes("1000000000000000000000000000000000000000000000000000000000000000")), + Stop: Key(common.Hex2Bytes("5000000000000000000000000000000000000000000000000000000000000000")), + First: 2, + Last: 4, + }) + + res = nil + for { + chunk = it.Next() + if chunk == nil { + break + } + res = append(res, chunk) + } + if len(res) != 2 { + t.Fatalf("Expected 2 chunk, got %v: %v", len(res), res) + } + if !bytes.Equal(res[0][:], keys[3]) { + t.Fatalf("Expected %v chunk, got %v", keys[3], res[0]) + } + if !bytes.Equal(res[1][:], keys[2]) { + t.Fatalf("Expected %v chunk, got %v", keys[2], res[1]) + } + + if err != nil { + t.Fatalf("unexpected error creating NewSyncIterator") + } + + it, _ = m.NewSyncIterator(DbSyncState{ + Start: Key(common.Hex2Bytes("1000000000000000000000000000000000000000000000000000000000000000")), + Stop: Key(common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")), + First: 2, + Last: 5, + }) + res = nil + for { + chunk = it.Next() + if chunk == nil { + break + } + res = append(res, chunk) + } + if len(res) != 2 { + t.Fatalf("Expected 2 chunk, got %v", len(res)) + } + if !bytes.Equal(res[0][:], keys[4]) { + t.Fatalf("Expected %v chunk, got %v", keys[4], res[0]) + } + if !bytes.Equal(res[1][:], keys[3]) { + t.Fatalf("Expected %v chunk, got %v", keys[3], res[1]) + } + + it, _ = m.NewSyncIterator(DbSyncState{ + Start: Key(common.Hex2Bytes("2000000000000000000000000000000000000000000000000000000000000000")), + Stop: Key(common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")), + First: 2, + Last: 5, + }) + res = nil + for { + chunk = it.Next() + if chunk == nil { + break + } + res = append(res, chunk) + } + if len(res) != 1 { + t.Fatalf("Expected 1 chunk, got %v", len(res)) + } + if !bytes.Equal(res[0][:], keys[3]) { + t.Fatalf("Expected %v chunk, got %v", keys[3], res[0]) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go new file mode 100644 index 0000000..7b3e23c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa.go @@ -0,0 +1,244 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "errors" + "io" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +DPA provides the client API entrypoints Store and Retrieve to store and retrieve +It can store anything that has a byte slice representation, so files or serialised objects etc. + +Storage: DPA calls the Chunker to segment the input datastream of any size to a merkle hashed tree of chunks. The key of the root block is returned to the client. + +Retrieval: given the key of the root block, the DPA retrieves the block chunks and reconstructs the original data and passes it back as a lazy reader. A lazy reader is a reader with on-demand delayed processing, i.e. the chunks needed to reconstruct a large file are only fetched and processed if that particular part of the document is actually read. + +As the chunker produces chunks, DPA dispatches them to its own chunk store +implementation for storage or retrieval. +*/ + +const ( + storeChanCapacity = 100 + retrieveChanCapacity = 100 + singletonSwarmDbCapacity = 50000 + singletonSwarmCacheCapacity = 500 + maxStoreProcesses = 8 + maxRetrieveProcesses = 8 +) + +var ( + notFound = errors.New("not found") +) + +type DPA struct { + ChunkStore + storeC chan *Chunk + retrieveC chan *Chunk + Chunker Chunker + + lock sync.Mutex + running bool + wg *sync.WaitGroup + quitC chan bool +} + +// for testing locally +func NewLocalDPA(datadir string) (*DPA, error) { + + hash := MakeHashFunc("SHA256") + + dbStore, err := NewDbStore(datadir, hash, singletonSwarmDbCapacity, 0) + if err != nil { + return nil, err + } + + return NewDPA(&LocalStore{ + NewMemStore(dbStore, singletonSwarmCacheCapacity), + dbStore, + }, NewChunkerParams()), nil +} + +func NewDPA(store ChunkStore, params *ChunkerParams) *DPA { + chunker := NewTreeChunker(params) + return &DPA{ + Chunker: chunker, + ChunkStore: store, + } +} + +// Public API. Main entry point for document retrieval directly. Used by the +// FS-aware API and httpaccess +// Chunk retrieval blocks on netStore requests with a timeout so reader will +// report error if retrieval of chunks within requested range time out. +func (self *DPA) Retrieve(key Key) LazySectionReader { + return self.Chunker.Join(key, self.retrieveC) +} + +// Public API. Main entry point for document storage directly. Used by the +// FS-aware API and httpaccess +func (self *DPA) Store(data io.Reader, size int64, swg *sync.WaitGroup, wwg *sync.WaitGroup) (key Key, err error) { + return self.Chunker.Split(data, size, self.storeC, swg, wwg) +} + +func (self *DPA) Start() { + self.lock.Lock() + defer self.lock.Unlock() + if self.running { + return + } + self.running = true + self.retrieveC = make(chan *Chunk, retrieveChanCapacity) + self.storeC = make(chan *Chunk, storeChanCapacity) + self.quitC = make(chan bool) + self.storeLoop() + self.retrieveLoop() +} + +func (self *DPA) Stop() { + self.lock.Lock() + defer self.lock.Unlock() + if !self.running { + return + } + self.running = false + close(self.quitC) +} + +// retrieveLoop dispatches the parallel chunk retrieval requests received on the +// retrieve channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) retrieveLoop() { + for i := 0; i < maxRetrieveProcesses; i++ { + go self.retrieveWorker() + } + glog.V(logger.Detail).Infof("dpa: retrieve loop spawning %v workers", maxRetrieveProcesses) +} + +func (self *DPA) retrieveWorker() { + for chunk := range self.retrieveC { + glog.V(logger.Detail).Infof("dpa: retrieve loop : chunk %v", chunk.Key.Log()) + storedChunk, err := self.Get(chunk.Key) + if err == notFound { + glog.V(logger.Detail).Infof("chunk %v not found", chunk.Key.Log()) + } else if err != nil { + glog.V(logger.Detail).Infof("error retrieving chunk %v: %v", chunk.Key.Log(), err) + } else { + chunk.SData = storedChunk.SData + chunk.Size = storedChunk.Size + } + close(chunk.C) + + select { + case <-self.quitC: + return + default: + } + } +} + +// storeLoop dispatches the parallel chunk store request processors +// received on the store channel to its ChunkStore (NetStore or LocalStore) +func (self *DPA) storeLoop() { + for i := 0; i < maxStoreProcesses; i++ { + go self.storeWorker() + } + glog.V(logger.Detail).Infof("dpa: store spawning %v workers", maxStoreProcesses) +} + +func (self *DPA) storeWorker() { + + for chunk := range self.storeC { + self.Put(chunk) + if chunk.wg != nil { + glog.V(logger.Detail).Infof("dpa: store processor %v", chunk.Key.Log()) + chunk.wg.Done() + + } + select { + case <-self.quitC: + return + default: + } + } +} + +// DpaChunkStore implements the ChunkStore interface, +// this chunk access layer assumed 2 chunk stores +// local storage eg. LocalStore and network storage eg., NetStore +// access by calling network is blocking with a timeout + +type dpaChunkStore struct { + n int + localStore ChunkStore + netStore ChunkStore +} + +func NewDpaChunkStore(localStore, netStore ChunkStore) *dpaChunkStore { + return &dpaChunkStore{0, localStore, netStore} +} + +// Get is the entrypoint for local retrieve requests +// waits for response or times out +func (self *dpaChunkStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.netStore.Get(key) + // timeout := time.Now().Add(searchTimeout) + if chunk.SData != nil { + glog.V(logger.Detail).Infof("DPA.Get: %v found locally, %d bytes", key.Log(), len(chunk.SData)) + return + } + // TODO: use self.timer time.Timer and reset with defer disableTimer + timer := time.After(searchTimeout) + select { + case <-timer: + glog.V(logger.Detail).Infof("DPA.Get: %v request time out ", key.Log()) + err = notFound + case <-chunk.Req.C: + glog.V(logger.Detail).Infof("DPA.Get: %v retrieved, %d bytes (%p)", key.Log(), len(chunk.SData), chunk) + } + return +} + +// Put is the entrypoint for local store requests coming from storeLoop +func (self *dpaChunkStore) Put(entry *Chunk) { + chunk, err := self.localStore.Get(entry.Key) + if err != nil { + glog.V(logger.Detail).Infof("DPA.Put: %v new chunk. call netStore.Put", entry.Key.Log()) + chunk = entry + } else if chunk.SData == nil { + glog.V(logger.Detail).Infof("DPA.Put: %v request entry found", entry.Key.Log()) + chunk.SData = entry.SData + chunk.Size = entry.Size + } else { + glog.V(logger.Detail).Infof("DPA.Put: %v chunk already known", entry.Key.Log()) + return + } + // from this point on the storage logic is the same with network storage requests + glog.V(logger.Detail).Infof("DPA.Put %v: %v", self.n, chunk.Key.Log()) + self.n++ + self.netStore.Put(chunk) +} + +// Close chunk store +func (self *dpaChunkStore) Close() { + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa_test.go new file mode 100644 index 0000000..a23b9ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/dpa_test.go @@ -0,0 +1,143 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "sync" + "testing" +) + +const testDataSize = 0x1000000 + +func TestDPArandom(t *testing.T) { + dbStore := initDbStore(t) + dbStore.setCapacity(50000) + memStore := NewMemStore(dbStore, defaultCacheCapacity) + localStore := &LocalStore{ + memStore, + dbStore, + } + chunker := NewTreeChunker(NewChunkerParams()) + dpa := &DPA{ + Chunker: chunker, + ChunkStore: localStore, + } + dpa.Start() + defer dpa.Stop() + defer os.RemoveAll("/tmp/bzz") + + reader, slice := testDataReaderAndSlice(testDataSize) + wg := &sync.WaitGroup{} + key, err := dpa.Store(reader, testDataSize, wg, nil) + if err != nil { + t.Errorf("Store error: %v", err) + } + wg.Wait() + resultReader := dpa.Retrieve(key) + resultSlice := make([]byte, len(slice)) + n, err := resultReader.ReadAt(resultSlice, 0) + if err != io.EOF { + t.Errorf("Retrieve error: %v", err) + } + if n != len(slice) { + t.Errorf("Slice size error got %d, expected %d.", n, len(slice)) + } + if !bytes.Equal(slice, resultSlice) { + t.Errorf("Comparison error.") + } + ioutil.WriteFile("/tmp/slice.bzz.16M", slice, 0666) + ioutil.WriteFile("/tmp/result.bzz.16M", resultSlice, 0666) + localStore.memStore = NewMemStore(dbStore, defaultCacheCapacity) + resultReader = dpa.Retrieve(key) + for i := range resultSlice { + resultSlice[i] = 0 + } + n, err = resultReader.ReadAt(resultSlice, 0) + if err != io.EOF { + t.Errorf("Retrieve error after removing memStore: %v", err) + } + if n != len(slice) { + t.Errorf("Slice size error after removing memStore got %d, expected %d.", n, len(slice)) + } + if !bytes.Equal(slice, resultSlice) { + t.Errorf("Comparison error after removing memStore.") + } +} + +func TestDPA_capacity(t *testing.T) { + dbStore := initDbStore(t) + memStore := NewMemStore(dbStore, defaultCacheCapacity) + localStore := &LocalStore{ + memStore, + dbStore, + } + memStore.setCapacity(0) + chunker := NewTreeChunker(NewChunkerParams()) + dpa := &DPA{ + Chunker: chunker, + ChunkStore: localStore, + } + dpa.Start() + reader, slice := testDataReaderAndSlice(testDataSize) + wg := &sync.WaitGroup{} + key, err := dpa.Store(reader, testDataSize, wg, nil) + if err != nil { + t.Errorf("Store error: %v", err) + } + wg.Wait() + resultReader := dpa.Retrieve(key) + resultSlice := make([]byte, len(slice)) + n, err := resultReader.ReadAt(resultSlice, 0) + if err != io.EOF { + t.Errorf("Retrieve error: %v", err) + } + if n != len(slice) { + t.Errorf("Slice size error got %d, expected %d.", n, len(slice)) + } + if !bytes.Equal(slice, resultSlice) { + t.Errorf("Comparison error.") + } + // Clear memStore + memStore.setCapacity(0) + // check whether it is, indeed, empty + dpa.ChunkStore = memStore + resultReader = dpa.Retrieve(key) + if _, err = resultReader.ReadAt(resultSlice, 0); err == nil { + t.Errorf("Was able to read %d bytes from an empty memStore.", len(slice)) + } + // check how it works with localStore + dpa.ChunkStore = localStore + // localStore.dbStore.setCapacity(0) + resultReader = dpa.Retrieve(key) + for i := range resultSlice { + resultSlice[i] = 0 + } + n, err = resultReader.ReadAt(resultSlice, 0) + if err != io.EOF { + t.Errorf("Retrieve error after clearing memStore: %v", err) + } + if n != len(slice) { + t.Errorf("Slice size error after clearing memStore got %d, expected %d.", n, len(slice)) + } + if !bytes.Equal(slice, resultSlice) { + t.Errorf("Comparison error after clearing memStore.") + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go new file mode 100644 index 0000000..14827e3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/localstore.go @@ -0,0 +1,79 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" +) + +// LocalStore is a combination of inmemory db over a disk persisted db +// implements a Get/Put with fallback (caching) logic using any 2 ChunkStores +type LocalStore struct { + memStore ChunkStore + DbStore ChunkStore +} + +// This constructor uses MemStore and DbStore as components +func NewLocalStore(hash Hasher, params *StoreParams) (*LocalStore, error) { + dbStore, err := NewDbStore(params.ChunkDbPath, hash, params.DbCapacity, params.Radius) + if err != nil { + return nil, err + } + return &LocalStore{ + memStore: NewMemStore(dbStore, params.CacheCapacity), + DbStore: dbStore, + }, nil +} + +// LocalStore is itself a chunk store +// unsafe, in that the data is not integrity checked +func (self *LocalStore) Put(chunk *Chunk) { + chunk.dbStored = make(chan bool) + self.memStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Add(1) + } + go func() { + self.DbStore.Put(chunk) + if chunk.wg != nil { + chunk.wg.Done() + } + }() +} + +// Get(chunk *Chunk) looks up a chunk in the local stores +// This method is blocking until the chunk is retrieved +// so additional timeout may be needed to wrap this call if +// ChunkStores are remote and can have long latency +func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) { + chunk, err = self.memStore.Get(key) + if err == nil { + return + } + chunk, err = self.DbStore.Get(key) + if err != nil { + return + } + chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) + self.memStore.Put(chunk) + return +} + +// Close local store +func (self *LocalStore) Close() { + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go new file mode 100644 index 0000000..7903d33 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore.go @@ -0,0 +1,328 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// memory storage layer for the package blockhash + +package storage + +import ( + "sync" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +const ( + memTreeLW = 2 // log2(subtree count) of the subtrees + memTreeFLW = 14 // log2(subtree count) of the root layer + dbForceUpdateAccessCnt = 1000 + defaultCacheCapacity = 5000 +) + +type MemStore struct { + memtree *memTree + entryCnt, capacity uint // stored entries + accessCnt uint64 // access counter; oldest is thrown away when full + dbAccessCnt uint64 + dbStore *DbStore + lock sync.Mutex +} + +/* +a hash prefix subtree containing subtrees or one storage entry (but never both) + +- access[0] stores the smallest (oldest) access count value in this subtree +- if it contains more subtrees and its subtree count is at least 4, access[1:2] + stores the smallest access count in the first and second halves of subtrees + (so that access[0] = min(access[1], access[2]) +- likewise, if subtree count is at least 8, + access[1] = min(access[3], access[4]) + access[2] = min(access[5], access[6]) + (access[] is a binary tree inside the multi-bit leveled hash tree) +*/ + +func NewMemStore(d *DbStore, capacity uint) (m *MemStore) { + m = &MemStore{} + m.memtree = newMemTree(memTreeFLW, nil, 0) + m.dbStore = d + m.setCapacity(capacity) + return +} + +type memTree struct { + subtree []*memTree + parent *memTree + parentIdx uint + + bits uint // log2(subtree count) + width uint // subtree count + + entry *Chunk // if subtrees are present, entry should be nil + lastDBaccess uint64 + access []uint64 +} + +func newMemTree(b uint, parent *memTree, pidx uint) (node *memTree) { + node = new(memTree) + node.bits = b + node.width = 1 << uint(b) + node.subtree = make([]*memTree, node.width) + node.access = make([]uint64, node.width-1) + node.parent = parent + node.parentIdx = pidx + if parent != nil { + parent.subtree[pidx] = node + } + + return node +} + +func (node *memTree) updateAccess(a uint64) { + aidx := uint(0) + var aa uint64 + oa := node.access[0] + for node.access[aidx] == oa { + node.access[aidx] = a + if aidx > 0 { + aa = node.access[((aidx-1)^1)+1] + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + nn := node.subtree[pidx^1] + if nn != nil { + aa = nn.access[0] + } else { + aa = 0 + } + aidx = (node.width + pidx - 2) >> 1 + } + + if (aa != 0) && (aa < a) { + a = aa + } + } +} + +func (s *MemStore) setCapacity(c uint) { + s.lock.Lock() + defer s.lock.Unlock() + + for c < s.entryCnt { + s.removeOldest() + } + s.capacity = c +} + +func (s *MemStore) getEntryCnt() uint { + return s.entryCnt +} + +// entry (not its copy) is going to be in MemStore +func (s *MemStore) Put(entry *Chunk) { + if s.capacity == 0 { + return + } + + s.lock.Lock() + defer s.lock.Unlock() + + if s.entryCnt >= s.capacity { + s.removeOldest() + } + + s.accessCnt++ + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + bitpos += node.bits + node = st + break + } + bitpos += node.bits + node = st + } + + if node.entry != nil { + + if node.entry.Key.isEqual(entry.Key) { + node.updateAccess(s.accessCnt) + if entry.SData == nil { + entry.Size = node.entry.Size + entry.SData = node.entry.SData + } + if entry.Req == nil { + entry.Req = node.entry.Req + } + entry.C = node.entry.C + node.entry = entry + return + } + + for node.entry != nil { + + l := node.entry.Key.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + st.entry = node.entry + node.entry = nil + st.updateAccess(node.access[0]) + + l = entry.Key.bits(bitpos, node.bits) + st = node.subtree[l] + if st == nil { + st = newMemTree(memTreeLW, node, l) + } + bitpos += node.bits + node = st + + } + } + + node.entry = entry + node.lastDBaccess = s.dbAccessCnt + node.updateAccess(s.accessCnt) + s.entryCnt++ + + return +} + +func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) { + s.lock.Lock() + defer s.lock.Unlock() + + node := s.memtree + bitpos := uint(0) + for node.entry == nil { + l := hash.bits(bitpos, node.bits) + st := node.subtree[l] + if st == nil { + return nil, notFound + } + bitpos += node.bits + node = st + } + + if node.entry.Key.isEqual(hash) { + s.accessCnt++ + node.updateAccess(s.accessCnt) + chunk = node.entry + if s.dbAccessCnt-node.lastDBaccess > dbForceUpdateAccessCnt { + s.dbAccessCnt++ + node.lastDBaccess = s.dbAccessCnt + if s.dbStore != nil { + s.dbStore.updateAccessCnt(hash) + } + } + } else { + err = notFound + } + + return +} + +func (s *MemStore) removeOldest() { + node := s.memtree + + for node.entry == nil { + + aidx := uint(0) + av := node.access[aidx] + + for aidx < node.width/2-1 { + if av == node.access[aidx*2+1] { + node.access[aidx] = node.access[aidx*2+2] + aidx = aidx*2 + 1 + } else if av == node.access[aidx*2+2] { + node.access[aidx] = node.access[aidx*2+1] + aidx = aidx*2 + 2 + } else { + panic(nil) + } + } + pidx := aidx*2 + 2 - node.width + if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) { + if node.subtree[pidx+1] != nil { + node.access[aidx] = node.subtree[pidx+1].access[0] + } else { + node.access[aidx] = 0 + } + } else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) { + if node.subtree[pidx] != nil { + node.access[aidx] = node.subtree[pidx].access[0] + } else { + node.access[aidx] = 0 + } + pidx++ + } else { + panic(nil) + } + + //fmt.Println(pidx) + node = node.subtree[pidx] + + } + + if node.entry.dbStored != nil { + glog.V(logger.Detail).Infof("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log()) + <-node.entry.dbStored + glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log()) + } else { + glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v already in DB. Ready to delete.", node.entry.Key.Log()) + } + + if node.entry.SData != nil { + node.entry = nil + s.entryCnt-- + } + + node.access[0] = 0 + + //--- + + aidx := uint(0) + for { + aa := node.access[aidx] + if aidx > 0 { + aidx = (aidx - 1) >> 1 + } else { + pidx := node.parentIdx + node = node.parent + if node == nil { + return + } + aidx = (node.width + pidx - 2) >> 1 + } + if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) { + node.access[aidx] = aa + } + } +} + +// Close memstore +func (s *MemStore) Close() { + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore_test.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore_test.go new file mode 100644 index 0000000..2e0ab53 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/memstore_test.go @@ -0,0 +1,50 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "testing" +) + +func testMemStore(l int64, branches int64, t *testing.T) { + m := NewMemStore(nil, defaultCacheCapacity) + testStore(m, l, branches, t) +} + +func TestMemStore128_10000(t *testing.T) { + testMemStore(10000, 128, t) +} + +func TestMemStore128_1000(t *testing.T) { + testMemStore(1000, 128, t) +} + +func TestMemStore128_100(t *testing.T) { + testMemStore(100, 128, t) +} + +func TestMemStore2_100(t *testing.T) { + testMemStore(100, 2, t) +} + +func TestMemStoreNotFound(t *testing.T) { + m := NewMemStore(nil, defaultCacheCapacity) + _, err := m.Get(ZeroKey) + if err != notFound { + t.Errorf("Expected notFound, got %v", err) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go new file mode 100644 index 0000000..46479b5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/netstore.go @@ -0,0 +1,139 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "path/filepath" + "sync" + "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +/* +NetStore is a cloud storage access abstaction layer for swarm +it contains the shared logic of network served chunk store/retrieval requests +both local (coming from DPA api) and remote (coming from peers via bzz protocol) +it implements the ChunkStore interface and embeds LocalStore + +It is called by the bzz protocol instances via Depo (the store/retrieve request handler) +a protocol instance is running on each peer, so this is heavily parallelised. +NetStore falls back to a backend (CloudStorage interface) +implemented by bzz/network/forwarder. forwarder or IPFS or IPΞS +*/ +type NetStore struct { + hashfunc Hasher + localStore *LocalStore + cloud CloudStore + lock sync.Mutex +} + +// backend engine for cloud store +// It can be aggregate dispatching to several parallel implementations: +// bzz/network/forwarder. forwarder or IPFS or IPΞS +type CloudStore interface { + Store(*Chunk) + Deliver(*Chunk) + Retrieve(*Chunk) +} + +type StoreParams struct { + ChunkDbPath string + DbCapacity uint64 + CacheCapacity uint + Radius int +} + +func NewStoreParams(path string) (self *StoreParams) { + return &StoreParams{ + ChunkDbPath: filepath.Join(path, "chunks"), + DbCapacity: defaultDbCapacity, + CacheCapacity: defaultCacheCapacity, + Radius: defaultRadius, + } +} + +// netstore contructor, takes path argument that is used to initialise dbStore, +// the persistent (disk) storage component of LocalStore +// the second argument is the hive, the connection/logistics manager for the node +func NewNetStore(hash Hasher, lstore *LocalStore, cloud CloudStore, params *StoreParams) *NetStore { + return &NetStore{ + hashfunc: hash, + localStore: lstore, + cloud: cloud, + } +} + +const ( + // maximum number of peers that a retrieved message is delivered to + requesterCount = 3 +) + +var ( + // timeout interval before retrieval is timed out + searchTimeout = 3 * time.Second +) + +// store logic common to local and network chunk store requests +// ~ unsafe put in localdb no check if exists no extra copy no hash validation +// the chunk is forced to propagate (Cloud.Store) even if locally found! +// caller needs to make sure if that is wanted +func (self *NetStore) Put(entry *Chunk) { + self.localStore.Put(entry) + + // handle deliveries + if entry.Req != nil { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v hit existing request...delivering", entry.Key.Log()) + // closing C signals to other routines (local requests) + // that the chunk is has been retrieved + close(entry.Req.C) + // deliver the chunk to requesters upstream + go self.cloud.Deliver(entry) + } else { + glog.V(logger.Detail).Infof("NetStore.Put: localStore.Put %v stored locally", entry.Key.Log()) + // handle propagating store requests + // go self.cloud.Store(entry) + go self.cloud.Store(entry) + } +} + +// retrieve logic common for local and network chunk retrieval requests +func (self *NetStore) Get(key Key) (*Chunk, error) { + var err error + chunk, err := self.localStore.Get(key) + if err == nil { + if chunk.Req == nil { + glog.V(logger.Detail).Infof("NetStore.Get: %v found locally", key) + } else { + glog.V(logger.Detail).Infof("NetStore.Get: %v hit on an existing request", key) + // no need to launch again + } + return chunk, err + } + // no data and no request status + glog.V(logger.Detail).Infof("NetStore.Get: %v not found locally. open new request", key) + chunk = NewChunk(key, newRequestStatus(key)) + self.localStore.memStore.Put(chunk) + go self.cloud.Retrieve(chunk) + return chunk, nil +} + +// Close netstore +func (self *NetStore) Close() { + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go new file mode 100644 index 0000000..79e1927 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/pyramid.go @@ -0,0 +1,201 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +const ( + processors = 8 +) + +type Tree struct { + Chunks int64 + Levels []map[int64]*Node + Lock sync.RWMutex +} + +type Node struct { + Pending int64 + Size uint64 + Children []common.Hash + Last bool +} + +func (self *Node) String() string { + var children []string + for _, node := range self.Children { + children = append(children, node.Hex()) + } + return fmt.Sprintf("pending: %v, size: %v, last :%v, children: %v", self.Pending, self.Size, self.Last, strings.Join(children, ", ")) +} + +type Task struct { + Index int64 // Index of the chunk being processed + Size uint64 + Data []byte // Binary blob of the chunk + Last bool +} + +type PyramidChunker struct { + hashFunc Hasher + chunkSize int64 + hashSize int64 + branches int64 + workerCount int +} + +func NewPyramidChunker(params *ChunkerParams) (self *PyramidChunker) { + self = &PyramidChunker{} + self.hashFunc = MakeHashFunc(params.Hash) + self.branches = params.Branches + self.hashSize = int64(self.hashFunc().Size()) + self.chunkSize = self.hashSize * self.branches + self.workerCount = 1 + return +} + +func (self *PyramidChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { + + chunks := (size + self.chunkSize - 1) / self.chunkSize + depth := int(math.Ceil(math.Log(float64(chunks))/math.Log(float64(self.branches)))) + 1 + + results := Tree{ + Chunks: chunks, + Levels: make([]map[int64]*Node, depth), + } + for i := 0; i < depth; i++ { + results.Levels[i] = make(map[int64]*Node) + } + // Create a pool of workers to crunch through the file + tasks := make(chan *Task, 2*processors) + pend := new(sync.WaitGroup) + abortC := make(chan bool) + for i := 0; i < processors; i++ { + pend.Add(1) + go self.processor(pend, swg, tasks, chunkC, &results) + } + // Feed the chunks into the task pool + read := 0 + for index := 0; ; index++ { + buffer := make([]byte, self.chunkSize+8) + n, err := data.Read(buffer[8:]) + read += n + last := int64(read) == size || err == io.ErrUnexpectedEOF || err == io.EOF + if err != nil && !last { + close(abortC) + break + } + binary.LittleEndian.PutUint64(buffer[:8], uint64(n)) + pend.Add(1) + select { + case tasks <- &Task{Index: int64(index), Size: uint64(n), Data: buffer[:n+8], Last: last}: + case <-abortC: + return nil, err + } + if last { + break + } + } + // Wait for the workers and return + close(tasks) + pend.Wait() + + key := results.Levels[0][0].Children[0][:] + return key, nil +} + +func (self *PyramidChunker) processor(pend, swg *sync.WaitGroup, tasks chan *Task, chunkC chan *Chunk, results *Tree) { + defer pend.Done() + + // Start processing leaf chunks ad infinitum + hasher := self.hashFunc() + for task := range tasks { + depth, pow := len(results.Levels)-1, self.branches + size := task.Size + data := task.Data + var node *Node + for depth >= 0 { + // New chunk received, reset the hasher and start processing + hasher.Reset() + if node == nil { // Leaf node, hash the data chunk + hasher.Write(task.Data) + } else { // Internal node, hash the children + size = node.Size + data = make([]byte, hasher.Size()*len(node.Children)+8) + binary.LittleEndian.PutUint64(data[:8], size) + + hasher.Write(data[:8]) + for i, hash := range node.Children { + copy(data[i*hasher.Size()+8:], hash[:]) + hasher.Write(hash[:]) + } + } + hash := hasher.Sum(nil) + last := task.Last || (node != nil) && node.Last + // Insert the subresult into the memoization tree + results.Lock.Lock() + if node = results.Levels[depth][task.Index/pow]; node == nil { + // Figure out the pending tasks + pending := self.branches + if task.Index/pow == results.Chunks/pow { + pending = (results.Chunks + pow/self.branches - 1) / (pow / self.branches) % self.branches + } + node = &Node{pending, 0, make([]common.Hash, pending), last} + results.Levels[depth][task.Index/pow] = node + } + node.Pending-- + i := task.Index / (pow / self.branches) % self.branches + if last { + node.Last = true + } + copy(node.Children[i][:], hash) + node.Size += size + left := node.Pending + if chunkC != nil { + if swg != nil { + swg.Add(1) + } + select { + case chunkC <- &Chunk{Key: hash, SData: data, wg: swg}: + // case <- self.quitC + } + } + if depth+1 < len(results.Levels) { + delete(results.Levels[depth+1], task.Index/(pow/self.branches)) + } + + results.Lock.Unlock() + // If there's more work to be done, leave for others + if left > 0 { + break + } + // We're the last ones in this batch, merge the children together + depth-- + pow *= self.branches + } + pend.Done() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go new file mode 100644 index 0000000..cc5ded9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/swarm/storage/types.go @@ -0,0 +1,233 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package storage + +import ( + "bytes" + "crypto" + "fmt" + "hash" + "io" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" +) + +type Hasher func() hash.Hash + +// Peer is the recorded as Source on the chunk +// should probably not be here? but network should wrap chunk object +type Peer interface{} + +type Key []byte + +func (x Key) Size() uint { + return uint(len(x)) +} + +func (x Key) isEqual(y Key) bool { + return bytes.Equal(x, y) +} + +func (h Key) bits(i, j uint) uint { + ii := i >> 3 + jj := i & 7 + if ii >= h.Size() { + return 0 + } + + if jj+j <= 8 { + return uint((h[ii] >> jj) & ((1 << j) - 1)) + } + + res := uint(h[ii] >> jj) + jj = 8 - jj + j -= jj + for j != 0 { + ii++ + if j < 8 { + res += uint(h[ii]&((1<. + +package swarm + +import ( + "bytes" + "crypto/ecdsa" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/chequebook" + "github.com/ethereum/go-ethereum/contracts/ens" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/swarm/api" + httpapi "github.com/ethereum/go-ethereum/swarm/api/http" + "github.com/ethereum/go-ethereum/swarm/network" + "github.com/ethereum/go-ethereum/swarm/storage" + "golang.org/x/net/context" +) + +// the swarm stack +type Swarm struct { + config *api.Config // swarm configuration + api *api.Api // high level api layer (fs/manifest) + dns api.Resolver // DNS registrar + dbAccess *network.DbAccess // access to local chunk db iterator and storage counter + storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends + dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support + depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage + cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud) + hive *network.Hive // the logistic manager + backend chequebook.Backend // simple blockchain Backend + privateKey *ecdsa.PrivateKey + corsString string + swapEnabled bool + lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped +} + +type SwarmAPI struct { + Api *api.Api + Backend chequebook.Backend + PrvKey *ecdsa.PrivateKey +} + +func (self *Swarm) API() *SwarmAPI { + return &SwarmAPI{ + Api: self.api, + Backend: self.backend, + PrvKey: self.privateKey, + } +} + +// creates a new swarm service instance +// implements node.Service +func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool, cors string) (self *Swarm, err error) { + if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty public key") + } + if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroKey) { + return nil, fmt.Errorf("empty bzz key") + } + + self = &Swarm{ + config: config, + swapEnabled: swapEnabled, + backend: backend, + privateKey: config.Swap.PrivateKey(), + corsString: cors, + } + glog.V(logger.Debug).Infof("Setting up Swarm service components") + + hash := storage.MakeHashFunc(config.ChunkerParams.Hash) + self.lstore, err = storage.NewLocalStore(hash, config.StoreParams) + if err != nil { + return + } + + // setup local store + glog.V(logger.Debug).Infof("Set up local storage") + + self.dbAccess = network.NewDbAccess(self.lstore) + glog.V(logger.Debug).Infof("Set up local db access (iterator/counter)") + + // set up the kademlia hive + self.hive = network.NewHive( + common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address) + config.HiveParams, // configuration parameters + swapEnabled, // SWAP enabled + syncEnabled, // syncronisation enabled + ) + glog.V(logger.Debug).Infof("Set up swarm network with Kademlia hive") + + // setup cloud storage backend + cloud := network.NewForwarder(self.hive) + glog.V(logger.Debug).Infof("-> set swarm forwarder as cloud storage backend") + // setup cloud storage internal access layer + + self.storage = storage.NewNetStore(hash, self.lstore, cloud, config.StoreParams) + glog.V(logger.Debug).Infof("-> swarm net store shared access layer to Swarm Chunk Store") + + // set up Depo (storage handler = cloud storage access layer for incoming remote requests) + self.depo = network.NewDepo(hash, self.lstore, self.storage) + glog.V(logger.Debug).Infof("-> REmote Access to CHunks") + + // set up DPA, the cloud storage local access layer + dpaChunkStore := storage.NewDpaChunkStore(self.lstore, self.storage) + glog.V(logger.Debug).Infof("-> Local Access to Swarm") + // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage + self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams) + glog.V(logger.Debug).Infof("-> Content Store API") + + // set up high level api + transactOpts := bind.NewKeyedTransactor(self.privateKey) + + self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) + if err != nil { + return nil, err + } + glog.V(logger.Debug).Infof("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex()) + + self.api = api.NewApi(self.dpa, self.dns) + // Manifests for Smart Hosting + glog.V(logger.Debug).Infof("-> Web3 virtual server API") + + return self, nil +} + +/* +Start is called when the stack is started +* starts the network kademlia hive peer management +* (starts netStore level 0 api) +* starts DPA level 1 api (chunking -> store/retrieve requests) +* (starts level 2 api) +* starts http proxy server +* registers url scheme handlers for bzz, etc +* TODO: start subservices like sword, swear, swarmdns +*/ +// implements the node.Service interface +func (self *Swarm) Start(net *p2p.Server) error { + connectPeer := func(url string) error { + node, err := discover.ParseNode(url) + if err != nil { + return fmt.Errorf("invalid node URL: %v", err) + } + net.AddPeer(node) + return nil + } + // set chequebook + if self.swapEnabled { + ctx := context.Background() // The initial setup has no deadline. + err := self.SetChequebook(ctx) + if err != nil { + return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) + } + glog.V(logger.Debug).Infof("-> cheque book for SWAP: %v", self.config.Swap.Chequebook()) + } else { + glog.V(logger.Debug).Infof("SWAP disabled: no cheque book set") + } + + glog.V(logger.Warn).Infof("Starting Swarm service") + self.hive.Start( + discover.PubkeyID(&net.PrivateKey.PublicKey), + func() string { return net.ListenAddr }, + connectPeer, + ) + glog.V(logger.Info).Infof("Swarm network started on bzz address: %v", self.hive.Addr()) + + self.dpa.Start() + glog.V(logger.Debug).Infof("Swarm DPA started") + + // start swarm http proxy server + if self.config.Port != "" { + addr := ":" + self.config.Port + go httpapi.StartHttpServer(self.api, &httpapi.Server{Addr: addr, CorsString: self.corsString}) + } + + glog.V(logger.Debug).Infof("Swarm http proxy started on port: %v", self.config.Port) + + if self.corsString != "" { + glog.V(logger.Debug).Infof("Swarm http proxy started with corsdomain:", self.corsString) + } + + return nil +} + +// implements the node.Service interface +// stops all component services. +func (self *Swarm) Stop() error { + self.dpa.Stop() + self.hive.Stop() + if ch := self.config.Swap.Chequebook(); ch != nil { + ch.Stop() + ch.Save() + } + + if self.lstore != nil { + self.lstore.DbStore.Close() + } + + return self.config.Save() +} + +// implements the node.Service interface +func (self *Swarm) Protocols() []p2p.Protocol { + proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams, self.config.NetworkId) + if err != nil { + return nil + } + return []p2p.Protocol{proto} +} + +// implements node.Service +// Apis returns the RPC Api descriptors the Swarm implementation offers +func (self *Swarm) APIs() []rpc.API { + return []rpc.API{ + // public APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewStorage(self.api), + Public: true, + }, + { + Namespace: "bzz", + Version: "0.1", + Service: &Info{self.config, chequebook.ContractParams}, + Public: true, + }, + // admin APIs + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewFileSystem(self.api), + Public: false}, + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewControl(self.api, self.hive), + Public: false, + }, + { + Namespace: "chequebook", + Version: chequebook.Version, + Service: chequebook.NewApi(self.config.Swap.Chequebook), + Public: false, + }, + // {Namespace, Version, api.NewAdmin(self), false}, + } +} + +func (self *Swarm) Api() *api.Api { + return self.api +} + +// SetChequebook ensures that the local checquebook is set up on chain. +func (self *Swarm) SetChequebook(ctx context.Context) error { + err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) + if err != nil { + return err + } + glog.V(logger.Info).Infof("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex()) + self.config.Save() + self.hive.DropAll() + return nil +} + +// Local swarm without netStore +func NewLocalSwarm(datadir, port string) (self *Swarm, err error) { + + prvKey, err := crypto.GenerateKey() + if err != nil { + return + } + + config, err := api.NewConfig(datadir, common.Address{}, prvKey, network.NetworkId) + if err != nil { + return + } + config.Port = port + + dpa, err := storage.NewLocalDPA(datadir) + if err != nil { + return + } + + self = &Swarm{ + api: api.NewApi(dpa, nil), + config: config, + } + + return +} + +// serialisable info about swarm +type Info struct { + *api.Config + *chequebook.Params +} + +func (self *Info) Info() *Info { + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/encoding.go b/vendor/github.com/ethereum/go-ethereum/trie/encoding.go new file mode 100644 index 0000000..2037118 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/encoding.go @@ -0,0 +1,128 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +func compactEncode(hexSlice []byte) []byte { + terminator := byte(0) + if hexSlice[len(hexSlice)-1] == 16 { + terminator = 1 + hexSlice = hexSlice[:len(hexSlice)-1] + } + var ( + odd = byte(len(hexSlice) % 2) + buflen = len(hexSlice)/2 + 1 + bi, hi = 0, 0 // indices + hs = byte(0) // shift: flips between 0 and 4 + ) + if odd == 0 { + bi = 1 + hs = 4 + } + buf := make([]byte, buflen) + buf[0] = terminator<<5 | byte(odd)<<4 + for bi < len(buf) && hi < len(hexSlice) { + buf[bi] |= hexSlice[hi] << hs + if hs == 0 { + bi++ + } + hi, hs = hi+1, hs^(1<<2) + } + return buf +} + +func compactDecode(str []byte) []byte { + base := compactHexDecode(str) + base = base[:len(base)-1] + if base[0] >= 2 { + base = append(base, 16) + } + if base[0]%2 == 1 { + base = base[1:] + } else { + base = base[2:] + } + return base +} + +func compactHexDecode(str []byte) []byte { + l := len(str)*2 + 1 + var nibbles = make([]byte, l) + for i, b := range str { + nibbles[i*2] = b / 16 + nibbles[i*2+1] = b % 16 + } + nibbles[l-1] = 16 + return nibbles +} + +// compactHexEncode encodes a series of nibbles into a byte array +func compactHexEncode(nibbles []byte) []byte { + nl := len(nibbles) + if nl == 0 { + return nil + } + if nibbles[nl-1] == 16 { + nl-- + } + l := (nl + 1) / 2 + var str = make([]byte, l) + for i := range str { + b := nibbles[i*2] * 16 + if nl > i*2 { + b += nibbles[i*2+1] + } + str[i] = b + } + return str +} + +func decodeCompact(key []byte) []byte { + l := len(key) / 2 + var res = make([]byte, l) + for i := 0; i < l; i++ { + v1, v0 := key[2*i], key[2*i+1] + res[i] = v1*16 + v0 + } + return res +} + +// prefixLen returns the length of the common prefix of a and b. +func prefixLen(a, b []byte) int { + var i, length = 0, len(a) + if len(b) < length { + length = len(b) + } + for ; i < length; i++ { + if a[i] != b[i] { + break + } + } + return i +} + +func hasTerm(s []byte) bool { + return s[len(s)-1] == 16 +} + +func remTerm(s []byte) []byte { + if hasTerm(s) { + b := make([]byte, len(s)-1) + copy(b, s) + return b + } + return s +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/encoding_test.go b/vendor/github.com/ethereum/go-ethereum/trie/encoding_test.go new file mode 100644 index 0000000..2f125ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/encoding_test.go @@ -0,0 +1,129 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "encoding/hex" + "testing" + + checker "gopkg.in/check.v1" +) + +func TestEncoding(t *testing.T) { checker.TestingT(t) } + +type TrieEncodingSuite struct{} + +var _ = checker.Suite(&TrieEncodingSuite{}) + +func (s *TrieEncodingSuite) TestCompactEncode(c *checker.C) { + // even compact encode + test1 := []byte{1, 2, 3, 4, 5} + res1 := compactEncode(test1) + c.Assert(res1, checker.DeepEquals, []byte("\x11\x23\x45")) + + // odd compact encode + test2 := []byte{0, 1, 2, 3, 4, 5} + res2 := compactEncode(test2) + c.Assert(res2, checker.DeepEquals, []byte("\x00\x01\x23\x45")) + + //odd terminated compact encode + test3 := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16} + res3 := compactEncode(test3) + c.Assert(res3, checker.DeepEquals, []byte("\x20\x0f\x1c\xb8")) + + // even terminated compact encode + test4 := []byte{15, 1, 12, 11, 8 /*term*/, 16} + res4 := compactEncode(test4) + c.Assert(res4, checker.DeepEquals, []byte("\x3f\x1c\xb8")) +} + +func (s *TrieEncodingSuite) TestCompactHexDecode(c *checker.C) { + exp := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16} + res := compactHexDecode([]byte("verb")) + c.Assert(res, checker.DeepEquals, exp) +} + +func (s *TrieEncodingSuite) TestCompactHexEncode(c *checker.C) { + exp := []byte("verb") + res := compactHexEncode([]byte{7, 6, 6, 5, 7, 2, 6, 2, 16}) + c.Assert(res, checker.DeepEquals, exp) +} + +func (s *TrieEncodingSuite) TestCompactDecode(c *checker.C) { + // odd compact decode + exp := []byte{1, 2, 3, 4, 5} + res := compactDecode([]byte("\x11\x23\x45")) + c.Assert(res, checker.DeepEquals, exp) + + // even compact decode + exp = []byte{0, 1, 2, 3, 4, 5} + res = compactDecode([]byte("\x00\x01\x23\x45")) + c.Assert(res, checker.DeepEquals, exp) + + // even terminated compact decode + exp = []byte{0, 15, 1, 12, 11, 8 /*term*/, 16} + res = compactDecode([]byte("\x20\x0f\x1c\xb8")) + c.Assert(res, checker.DeepEquals, exp) + + // even terminated compact decode + exp = []byte{15, 1, 12, 11, 8 /*term*/, 16} + res = compactDecode([]byte("\x3f\x1c\xb8")) + c.Assert(res, checker.DeepEquals, exp) +} + +func (s *TrieEncodingSuite) TestDecodeCompact(c *checker.C) { + exp, _ := hex.DecodeString("012345") + res := decodeCompact([]byte{0, 1, 2, 3, 4, 5}) + c.Assert(res, checker.DeepEquals, exp) + + exp, _ = hex.DecodeString("012345") + res = decodeCompact([]byte{0, 1, 2, 3, 4, 5, 16}) + c.Assert(res, checker.DeepEquals, exp) + + exp, _ = hex.DecodeString("abcdef") + res = decodeCompact([]byte{10, 11, 12, 13, 14, 15}) + c.Assert(res, checker.DeepEquals, exp) +} + +func BenchmarkCompactEncode(b *testing.B) { + + testBytes := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16} + for i := 0; i < b.N; i++ { + compactEncode(testBytes) + } +} + +func BenchmarkCompactDecode(b *testing.B) { + testBytes := []byte{0, 15, 1, 12, 11, 8 /*term*/, 16} + for i := 0; i < b.N; i++ { + compactDecode(testBytes) + } +} + +func BenchmarkCompactHexDecode(b *testing.B) { + testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16} + for i := 0; i < b.N; i++ { + compactHexDecode(testBytes) + } +} + +func BenchmarkDecodeCompact(b *testing.B) { + testBytes := []byte{7, 6, 6, 5, 7, 2, 6, 2, 16} + for i := 0; i < b.N; i++ { + decodeCompact(testBytes) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/errors.go b/vendor/github.com/ethereum/go-ethereum/trie/errors.go new file mode 100644 index 0000000..76129a7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/errors.go @@ -0,0 +1,51 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" +) + +// MissingNodeError is returned by the trie functions (TryGet, TryUpdate, TryDelete) +// in the case where a trie node is not present in the local database. Contains +// information necessary for retrieving the missing node through an ODR service. +// +// NodeHash is the hash of the missing node +// +// RootHash is the original root of the trie that contains the node +// +// Key is a binary-encoded key that contains the prefix that leads to the first +// missing node and optionally a suffix that hints on which further nodes should +// also be retrieved +// +// PrefixLen is the nibble length of the key prefix that leads from the root to +// the missing node +// +// SuffixLen is the nibble length of the remaining part of the key that hints on +// which further nodes should also be retrieved (can be zero when there are no +// such hints in the error message) +type MissingNodeError struct { + RootHash, NodeHash common.Hash + Key []byte + PrefixLen, SuffixLen int +} + +func (err *MissingNodeError) Error() string { + return fmt.Sprintf("Missing trie node %064x", err.NodeHash) +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/hasher.go b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go new file mode 100644 index 0000000..98c3095 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/hasher.go @@ -0,0 +1,173 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "hash" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/rlp" +) + +type hasher struct { + tmp *bytes.Buffer + sha hash.Hash + cachegen, cachelimit uint16 +} + +// hashers live in a global pool. +var hasherPool = sync.Pool{ + New: func() interface{} { + return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} + }, +} + +func newHasher(cachegen, cachelimit uint16) *hasher { + h := hasherPool.Get().(*hasher) + h.cachegen, h.cachelimit = cachegen, cachelimit + return h +} + +func returnHasherToPool(h *hasher) { + hasherPool.Put(h) +} + +// hash collapses a node down into a hash node, also returning a copy of the +// original node initialzied with the computed hash to replace the original one. +func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { + // If we're not storing the node, just hashing, use available cached data + if hash, dirty := n.cache(); hash != nil { + if db == nil { + return hash, n, nil + } + if n.canUnload(h.cachegen, h.cachelimit) { + // Unload the node from cache. All of its subnodes will have a lower or equal + // cache generation number. + cacheUnloadCounter.Inc(1) + return hash, hash, nil + } + if !dirty { + return hash, n, nil + } + } + // Trie not processed yet or needs storage, walk the children + collapsed, cached, err := h.hashChildren(n, db) + if err != nil { + return hashNode{}, n, err + } + hashed, err := h.store(collapsed, db, force) + if err != nil { + return hashNode{}, n, err + } + // Cache the hash of the ndoe for later reuse and remove + // the dirty flag in commit mode. It's fine to assign these values directly + // without copying the node first because hashChildren copies it. + cachedHash, _ := hashed.(hashNode) + switch cn := cached.(type) { + case *shortNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false + } + case *fullNode: + cn.flags.hash = cachedHash + if db != nil { + cn.flags.dirty = false + } + } + return hashed, cached, nil +} + +// hashChildren replaces the children of a node with their hashes if the encoded +// size of the child is larger than a hash, returning the collapsed node as well +// as a replacement for the original node with the child hashes cached in. +func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { + var err error + + switch n := original.(type) { + case *shortNode: + // Hash the short node's child, caching the newly hashed subtree + collapsed, cached := n.copy(), n.copy() + collapsed.Key = compactEncode(n.Key) + cached.Key = common.CopyBytes(n.Key) + + if _, ok := n.Val.(valueNode); !ok { + collapsed.Val, cached.Val, err = h.hash(n.Val, db, false) + if err != nil { + return original, original, err + } + } + if collapsed.Val == nil { + collapsed.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + return collapsed, cached, nil + + case *fullNode: + // Hash the full node's children, caching the newly hashed subtrees + collapsed, cached := n.copy(), n.copy() + + for i := 0; i < 16; i++ { + if n.Children[i] != nil { + collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false) + if err != nil { + return original, original, err + } + } else { + collapsed.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings. + } + } + cached.Children[16] = n.Children[16] + if collapsed.Children[16] == nil { + collapsed.Children[16] = valueNode(nil) + } + return collapsed, cached, nil + + default: + // Value and hash nodes don't have children so they're left as were + return n, original, nil + } +} + +func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { + // Don't store hashes or empty nodes. + if _, isHash := n.(hashNode); n == nil || isHash { + return n, nil + } + // Generate the RLP encoding of the node + h.tmp.Reset() + if err := rlp.Encode(h.tmp, n); err != nil { + panic("encode error: " + err.Error()) + } + + if h.tmp.Len() < 32 && !force { + return n, nil // Nodes smaller than 32 bytes are stored inside their parent + } + // Larger nodes are replaced by their hash and stored in the database. + hash, _ := n.cache() + if hash == nil { + h.sha.Reset() + h.sha.Write(h.tmp.Bytes()) + hash = hashNode(h.sha.Sum(nil)) + } + if db != nil { + return hash, db.Put(hash, h.tmp.Bytes()) + } + return hash, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/iterator.go b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go new file mode 100644 index 0000000..234c49e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/iterator.go @@ -0,0 +1,365 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "github.com/ethereum/go-ethereum/common" +) + +// Iterator is a key-value trie iterator that traverses a Trie. +type Iterator struct { + nodeIt NodeIterator + + Key []byte // Current data key on which the iterator is positioned on + Value []byte // Current data value on which the iterator is positioned on +} + +// NewIterator creates a new key-value iterator. +func NewIterator(trie *Trie) *Iterator { + return &Iterator{ + nodeIt: NewNodeIterator(trie), + } +} + +// FromNodeIterator creates a new key-value iterator from a node iterator +func NewIteratorFromNodeIterator(it NodeIterator) *Iterator { + return &Iterator{ + nodeIt: it, + } +} + +// Next moves the iterator forward one key-value entry. +func (it *Iterator) Next() bool { + for it.nodeIt.Next(true) { + if it.nodeIt.Leaf() { + it.Key = decodeCompact(it.nodeIt.Path()) + it.Value = it.nodeIt.LeafBlob() + return true + } + } + it.Key = nil + it.Value = nil + return false +} + +// NodeIterator is an iterator to traverse the trie pre-order. +type NodeIterator interface { + // Hash returns the hash of the current node + Hash() common.Hash + // Parent returns the hash of the parent of the current node + Parent() common.Hash + // Leaf returns true iff the current node is a leaf node. + Leaf() bool + // LeafBlob returns the contents of the node, if it is a leaf. + // Callers must not retain references to the return value after calling Next() + LeafBlob() []byte + // Path returns the hex-encoded path to the current node. + // Callers must not retain references to the return value after calling Next() + Path() []byte + // Next moves the iterator to the next node. If the parameter is false, any child + // nodes will be skipped. + Next(bool) bool + // Error returns the error status of the iterator. + Error() error +} + +// nodeIteratorState represents the iteration state at one particular node of the +// trie, which can be resumed at a later invocation. +type nodeIteratorState struct { + hash common.Hash // Hash of the node being iterated (nil if not standalone) + node node // Trie node being iterated + parent common.Hash // Hash of the first full ancestor node (nil if current is the root) + child int // Child to be processed next + pathlen int // Length of the path to this node +} + +type nodeIterator struct { + trie *Trie // Trie being iterated + stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state + + err error // Failure set in case of an internal error in the iterator + + path []byte // Path to the current node +} + +// NewNodeIterator creates an post-order trie iterator. +func NewNodeIterator(trie *Trie) NodeIterator { + if trie.Hash() == emptyState { + return new(nodeIterator) + } + return &nodeIterator{trie: trie} +} + +// Hash returns the hash of the current node +func (it *nodeIterator) Hash() common.Hash { + if len(it.stack) == 0 { + return common.Hash{} + } + + return it.stack[len(it.stack)-1].hash +} + +// Parent returns the hash of the parent node +func (it *nodeIterator) Parent() common.Hash { + if len(it.stack) == 0 { + return common.Hash{} + } + + return it.stack[len(it.stack)-1].parent +} + +// Leaf returns true if the current node is a leaf +func (it *nodeIterator) Leaf() bool { + if len(it.stack) == 0 { + return false + } + + _, ok := it.stack[len(it.stack)-1].node.(valueNode) + return ok +} + +// LeafBlob returns the data for the current node, if it is a leaf +func (it *nodeIterator) LeafBlob() []byte { + if len(it.stack) == 0 { + return nil + } + + if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok { + return []byte(node) + } + return nil +} + +// Path returns the hex-encoded path to the current node +func (it *nodeIterator) Path() []byte { + return it.path +} + +// Error returns the error set in case of an internal error in the iterator +func (it *nodeIterator) Error() error { + return it.err +} + +// Next moves the iterator to the next node, returning whether there are any +// further nodes. In case of an internal error this method returns false and +// sets the Error field to the encountered failure. If `descend` is false, +// skips iterating over any subnodes of the current node. +func (it *nodeIterator) Next(descend bool) bool { + // If the iterator failed previously, don't do anything + if it.err != nil { + return false + } + // Otherwise step forward with the iterator and report any errors + if err := it.step(descend); err != nil { + it.err = err + return false + } + return it.trie != nil +} + +// step moves the iterator to the next node of the trie. +func (it *nodeIterator) step(descend bool) error { + if it.trie == nil { + // Abort if we reached the end of the iteration + return nil + } + if len(it.stack) == 0 { + // Initialize the iterator if we've just started. + root := it.trie.Hash() + state := &nodeIteratorState{node: it.trie.root, child: -1} + if root != emptyRoot { + state.hash = root + } + it.stack = append(it.stack, state) + return nil + } + + if !descend { + // If we're skipping children, pop the current node first + it.path = it.path[:it.stack[len(it.stack)-1].pathlen] + it.stack = it.stack[:len(it.stack)-1] + } + + // Continue iteration to the next child +outer: + for { + if len(it.stack) == 0 { + it.trie = nil + return nil + } + parent := it.stack[len(it.stack)-1] + ancestor := parent.hash + if (ancestor == common.Hash{}) { + ancestor = parent.parent + } + if node, ok := parent.node.(*fullNode); ok { + // Full node, iterate over children + for parent.child++; parent.child < len(node.Children); parent.child++ { + child := node.Children[parent.child] + if child != nil { + hash, _ := child.cache() + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(hash), + node: child, + parent: ancestor, + child: -1, + pathlen: len(it.path), + }) + it.path = append(it.path, byte(parent.child)) + break outer + } + } + } else if node, ok := parent.node.(*shortNode); ok { + // Short node, return the pointer singleton child + if parent.child < 0 { + parent.child++ + hash, _ := node.Val.cache() + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(hash), + node: node.Val, + parent: ancestor, + child: -1, + pathlen: len(it.path), + }) + if hasTerm(node.Key) { + it.path = append(it.path, node.Key[:len(node.Key)-1]...) + } else { + it.path = append(it.path, node.Key...) + } + break + } + } else if hash, ok := parent.node.(hashNode); ok { + // Hash node, resolve the hash child from the database + if parent.child < 0 { + parent.child++ + node, err := it.trie.resolveHash(hash, nil, nil) + if err != nil { + return err + } + it.stack = append(it.stack, &nodeIteratorState{ + hash: common.BytesToHash(hash), + node: node, + parent: ancestor, + child: -1, + pathlen: len(it.path), + }) + break + } + } + it.path = it.path[:parent.pathlen] + it.stack = it.stack[:len(it.stack)-1] + } + return nil +} + +type differenceIterator struct { + a, b NodeIterator // Nodes returned are those in b - a. + eof bool // Indicates a has run out of elements + count int // Number of nodes scanned on either trie +} + +// NewDifferenceIterator constructs a NodeIterator that iterates over elements in b that +// are not in a. Returns the iterator, and a pointer to an integer recording the number +// of nodes seen. +func NewDifferenceIterator(a, b NodeIterator) (NodeIterator, *int) { + a.Next(true) + it := &differenceIterator{ + a: a, + b: b, + } + return it, &it.count +} + +func (it *differenceIterator) Hash() common.Hash { + return it.b.Hash() +} + +func (it *differenceIterator) Parent() common.Hash { + return it.b.Parent() +} + +func (it *differenceIterator) Leaf() bool { + return it.b.Leaf() +} + +func (it *differenceIterator) LeafBlob() []byte { + return it.b.LeafBlob() +} + +func (it *differenceIterator) Path() []byte { + return it.b.Path() +} + +func (it *differenceIterator) Next(bool) bool { + // Invariants: + // - We always advance at least one element in b. + // - At the start of this function, a's path is lexically greater than b's. + if !it.b.Next(true) { + return false + } + it.count += 1 + + if it.eof { + // a has reached eof, so we just return all elements from b + return true + } + + for { + apath, bpath := it.a.Path(), it.b.Path() + switch bytes.Compare(apath, bpath) { + case -1: + // b jumped past a; advance a + if !it.a.Next(true) { + it.eof = true + return true + } + it.count += 1 + case 1: + // b is before a + return true + case 0: + if it.a.Hash() != it.b.Hash() || it.a.Leaf() != it.b.Leaf() { + // Keys are identical, but hashes or leaf status differs + return true + } + if it.a.Leaf() && it.b.Leaf() && !bytes.Equal(it.a.LeafBlob(), it.b.LeafBlob()) { + // Both are leaf nodes, but with different values + return true + } + + // a and b are identical; skip this whole subtree if the nodes have hashes + hasHash := it.a.Hash() == common.Hash{} + if !it.b.Next(hasHash) { + return false + } + it.count += 1 + if !it.a.Next(hasHash) { + it.eof = true + return true + } + it.count += 1 + } + } +} + +func (it *differenceIterator) Error() error { + if err := it.a.Error(); err != nil { + return err + } + return it.b.Error() +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/iterator_test.go b/vendor/github.com/ethereum/go-ethereum/trie/iterator_test.go new file mode 100644 index 0000000..0ad9711 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/iterator_test.go @@ -0,0 +1,175 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +func TestIterator(t *testing.T) { + trie := newEmpty() + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"dog", "puppy"}, + {"somethingveryoddindeedthis is", "myothernodedata"}, + } + all := make(map[string]string) + for _, val := range vals { + all[val.k] = val.v + trie.Update([]byte(val.k), []byte(val.v)) + } + trie.Commit() + + found := make(map[string]string) + it := NewIterator(trie) + for it.Next() { + found[string(it.Key)] = string(it.Value) + } + + for k, v := range all { + if found[k] != v { + t.Errorf("iterator value mismatch for %s: got %q want %q", k, found[k], v) + } + } +} + +type kv struct { + k, v []byte + t bool +} + +func TestIteratorLargeData(t *testing.T) { + trie := newEmpty() + vals := make(map[string]*kv) + + for i := byte(0); i < 255; i++ { + value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} + value2 := &kv{common.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false} + trie.Update(value.k, value.v) + trie.Update(value2.k, value2.v) + vals[string(value.k)] = value + vals[string(value2.k)] = value2 + } + + it := NewIterator(trie) + for it.Next() { + vals[string(it.Key)].t = true + } + + var untouched []*kv + for _, value := range vals { + if !value.t { + untouched = append(untouched, value) + } + } + + if len(untouched) > 0 { + t.Errorf("Missed %d nodes", len(untouched)) + for _, value := range untouched { + t.Error(value) + } + } +} + +// Tests that the node iterator indeed walks over the entire database contents. +func TestNodeIteratorCoverage(t *testing.T) { + // Create some arbitrary test trie to iterate + db, trie, _ := makeTestTrie() + + // Gather all the node hashes found by the iterator + hashes := make(map[common.Hash]struct{}) + for it := NewNodeIterator(trie); it.Next(true); { + if it.Hash() != (common.Hash{}) { + hashes[it.Hash()] = struct{}{} + } + } + // Cross check the hashes and the database itself + for hash := range hashes { + if _, err := db.Get(hash.Bytes()); err != nil { + t.Errorf("failed to retrieve reported node %x: %v", hash, err) + } + } + for _, key := range db.(*ethdb.MemDatabase).Keys() { + if _, ok := hashes[common.BytesToHash(key)]; !ok { + t.Errorf("state entry not reported %x", key) + } + } +} + +func TestDifferenceIterator(t *testing.T) { + triea := newEmpty() + valsa := []struct{ k, v string }{ + {"bar", "b"}, + {"barb", "ba"}, + {"bars", "bb"}, + {"bard", "bc"}, + {"fab", "z"}, + {"foo", "a"}, + {"food", "ab"}, + {"foos", "aa"}, + } + for _, val := range valsa { + triea.Update([]byte(val.k), []byte(val.v)) + } + triea.Commit() + + trieb := newEmpty() + valsb := []struct{ k, v string }{ + {"aardvark", "c"}, + {"bar", "b"}, + {"barb", "bd"}, + {"bars", "be"}, + {"fab", "z"}, + {"foo", "a"}, + {"foos", "aa"}, + {"food", "ab"}, + {"jars", "d"}, + } + for _, val := range valsb { + trieb.Update([]byte(val.k), []byte(val.v)) + } + trieb.Commit() + + found := make(map[string]string) + di, _ := NewDifferenceIterator(NewNodeIterator(triea), NewNodeIterator(trieb)) + it := NewIteratorFromNodeIterator(di) + for it.Next() { + found[string(it.Key)] = string(it.Value) + } + + all := []struct{ k, v string }{ + {"aardvark", "c"}, + {"barb", "bd"}, + {"bars", "be"}, + {"jars", "d"}, + } + for _, item := range all { + if found[item.k] != item.v { + t.Errorf("iterator value mismatch for %s: got %q want %q", item.k, found[item.k], item.v) + } + } + if len(found) != len(all) { + t.Errorf("iterator count mismatch: got %d values, want %d", len(found), len(all)) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/node.go b/vendor/github.com/ethereum/go-ethereum/trie/node.go new file mode 100644 index 0000000..4aa0cab --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/node.go @@ -0,0 +1,224 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "fmt" + "io" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "[17]"} + +type node interface { + fstring(string) string + cache() (hashNode, bool) + canUnload(cachegen, cachelimit uint16) bool +} + +type ( + fullNode struct { + Children [17]node // Actual trie node data to encode/decode (needs custom encoder) + flags nodeFlag + } + shortNode struct { + Key []byte + Val node + flags nodeFlag + } + hashNode []byte + valueNode []byte +) + +// EncodeRLP encodes a full node into the consensus RLP format. +func (n *fullNode) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, n.Children) +} + +func (n *fullNode) copy() *fullNode { copy := *n; return © } +func (n *shortNode) copy() *shortNode { copy := *n; return © } + +// nodeFlag contains caching-related metadata about a node. +type nodeFlag struct { + hash hashNode // cached hash of the node (may be nil) + gen uint16 // cache generation counter + dirty bool // whether the node has changes that must be written to the database +} + +// canUnload tells whether a node can be unloaded. +func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool { + return !n.dirty && cachegen-n.gen >= cachelimit +} + +func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } +func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } +func (n hashNode) canUnload(uint16, uint16) bool { return false } +func (n valueNode) canUnload(uint16, uint16) bool { return false } + +func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } +func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } +func (n hashNode) cache() (hashNode, bool) { return nil, true } +func (n valueNode) cache() (hashNode, bool) { return nil, true } + +// Pretty printing. +func (n *fullNode) String() string { return n.fstring("") } +func (n *shortNode) String() string { return n.fstring("") } +func (n hashNode) String() string { return n.fstring("") } +func (n valueNode) String() string { return n.fstring("") } + +func (n *fullNode) fstring(ind string) string { + resp := fmt.Sprintf("[\n%s ", ind) + for i, node := range n.Children { + if node == nil { + resp += fmt.Sprintf("%s: ", indices[i]) + } else { + resp += fmt.Sprintf("%s: %v", indices[i], node.fstring(ind+" ")) + } + } + return resp + fmt.Sprintf("\n%s] ", ind) +} +func (n *shortNode) fstring(ind string) string { + return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" ")) +} +func (n hashNode) fstring(ind string) string { + return fmt.Sprintf("<%x> ", []byte(n)) +} +func (n valueNode) fstring(ind string) string { + return fmt.Sprintf("%x ", []byte(n)) +} + +func mustDecodeNode(hash, buf []byte, cachegen uint16) node { + n, err := decodeNode(hash, buf, cachegen) + if err != nil { + panic(fmt.Sprintf("node %x: %v", hash, err)) + } + return n +} + +// decodeNode parses the RLP encoding of a trie node. +func decodeNode(hash, buf []byte, cachegen uint16) (node, error) { + if len(buf) == 0 { + return nil, io.ErrUnexpectedEOF + } + elems, _, err := rlp.SplitList(buf) + if err != nil { + return nil, fmt.Errorf("decode error: %v", err) + } + switch c, _ := rlp.CountValues(elems); c { + case 2: + n, err := decodeShort(hash, buf, elems, cachegen) + return n, wrapError(err, "short") + case 17: + n, err := decodeFull(hash, buf, elems, cachegen) + return n, wrapError(err, "full") + default: + return nil, fmt.Errorf("invalid number of list elements: %v", c) + } +} + +func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) { + kbuf, rest, err := rlp.SplitString(elems) + if err != nil { + return nil, err + } + flag := nodeFlag{hash: hash, gen: cachegen} + key := compactDecode(kbuf) + if key[len(key)-1] == 16 { + // value node + val, _, err := rlp.SplitString(rest) + if err != nil { + return nil, fmt.Errorf("invalid value node: %v", err) + } + return &shortNode{key, append(valueNode{}, val...), flag}, nil + } + r, _, err := decodeRef(rest, cachegen) + if err != nil { + return nil, wrapError(err, "val") + } + return &shortNode{key, r, flag}, nil +} + +func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) { + n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}} + for i := 0; i < 16; i++ { + cld, rest, err := decodeRef(elems, cachegen) + if err != nil { + return n, wrapError(err, fmt.Sprintf("[%d]", i)) + } + n.Children[i], elems = cld, rest + } + val, _, err := rlp.SplitString(elems) + if err != nil { + return n, err + } + if len(val) > 0 { + n.Children[16] = append(valueNode{}, val...) + } + return n, nil +} + +const hashLen = len(common.Hash{}) + +func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) { + kind, val, rest, err := rlp.Split(buf) + if err != nil { + return nil, buf, err + } + switch { + case kind == rlp.List: + // 'embedded' node reference. The encoding must be smaller + // than a hash in order to be valid. + if size := len(buf) - len(rest); size > hashLen { + err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen) + return nil, buf, err + } + n, err := decodeNode(nil, buf, cachegen) + return n, rest, err + case kind == rlp.String && len(val) == 0: + // empty node + return nil, rest, nil + case kind == rlp.String && len(val) == 32: + return append(hashNode{}, val...), rest, nil + default: + return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val)) + } +} + +// wraps a decoding error with information about the path to the +// invalid child node (for debugging encoding issues). +type decodeError struct { + what error + stack []string +} + +func wrapError(err error, ctx string) error { + if err == nil { + return nil + } + if decErr, ok := err.(*decodeError); ok { + decErr.stack = append(decErr.stack, ctx) + return decErr + } + return &decodeError{err, []string{ctx}} +} + +func (err *decodeError) Error() string { + return fmt.Sprintf("%v (decode path: %s)", err.what, strings.Join(err.stack, "<-")) +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/node_test.go b/vendor/github.com/ethereum/go-ethereum/trie/node_test.go new file mode 100644 index 0000000..7ad1ff9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/node_test.go @@ -0,0 +1,58 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import "testing" + +func TestCanUnload(t *testing.T) { + tests := []struct { + flag nodeFlag + cachegen, cachelimit uint16 + want bool + }{ + { + flag: nodeFlag{dirty: true, gen: 0}, + want: false, + }, + { + flag: nodeFlag{dirty: false, gen: 0}, + cachegen: 0, cachelimit: 0, + want: true, + }, + { + flag: nodeFlag{dirty: false, gen: 65534}, + cachegen: 65535, cachelimit: 1, + want: true, + }, + { + flag: nodeFlag{dirty: false, gen: 65534}, + cachegen: 0, cachelimit: 1, + want: true, + }, + { + flag: nodeFlag{dirty: false, gen: 1}, + cachegen: 65535, cachelimit: 1, + want: true, + }, + } + + for _, test := range tests { + if got := test.flag.canUnload(test.cachegen, test.cachelimit); got != test.want { + t.Errorf("%+v\n got %t, want %t", test, got, test.want) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/proof.go b/vendor/github.com/ethereum/go-ethereum/trie/proof.go new file mode 100644 index 0000000..bea5e5c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/proof.go @@ -0,0 +1,151 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/rlp" +) + +// Prove constructs a merkle proof for key. The result contains all +// encoded nodes on the path to the value at key. The value itself is +// also included in the last node and can be retrieved by verifying +// the proof. +// +// If the trie does not contain a value for key, the returned proof +// contains all nodes of the longest existing prefix of the key +// (at least the root node), ending with the node that proves the +// absence of the key. +func (t *Trie) Prove(key []byte) []rlp.RawValue { + // Collect all nodes on the path to key. + key = compactHexDecode(key) + nodes := []node{} + tn := t.root + for len(key) > 0 && tn != nil { + switch n := tn.(type) { + case *shortNode: + if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { + // The trie doesn't contain the key. + tn = nil + } else { + tn = n.Val + key = key[len(n.Key):] + } + nodes = append(nodes, n) + case *fullNode: + tn = n.Children[key[0]] + key = key[1:] + nodes = append(nodes, n) + case hashNode: + var err error + tn, err = t.resolveHash(n, nil, nil) + if err != nil { + if glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } + return nil + } + default: + panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) + } + } + hasher := newHasher(0, 0) + proof := make([]rlp.RawValue, 0, len(nodes)) + for i, n := range nodes { + // Don't bother checking for errors here since hasher panics + // if encoding doesn't work and we're not writing to any database. + n, _, _ = hasher.hashChildren(n, nil) + hn, _ := hasher.store(n, nil, false) + if _, ok := hn.(hashNode); ok || i == 0 { + // If the node's database encoding is a hash (or is the + // root node), it becomes a proof element. + enc, _ := rlp.EncodeToBytes(n) + proof = append(proof, enc) + } + } + return proof +} + +// VerifyProof checks merkle proofs. The given proof must contain the +// value for key in a trie with the given root hash. VerifyProof +// returns an error if the proof contains invalid trie nodes or the +// wrong value. +func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value []byte, err error) { + key = compactHexDecode(key) + sha := sha3.NewKeccak256() + wantHash := rootHash.Bytes() + for i, buf := range proof { + sha.Reset() + sha.Write(buf) + if !bytes.Equal(sha.Sum(nil), wantHash) { + return nil, fmt.Errorf("bad proof node %d: hash mismatch", i) + } + n, err := decodeNode(wantHash, buf, 0) + if err != nil { + return nil, fmt.Errorf("bad proof node %d: %v", i, err) + } + keyrest, cld := get(n, key) + switch cld := cld.(type) { + case nil: + if i != len(proof)-1 { + return nil, fmt.Errorf("key mismatch at proof node %d", i) + } else { + // The trie doesn't contain the key. + return nil, nil + } + case hashNode: + key = keyrest + wantHash = cld + case valueNode: + if i != len(proof)-1 { + return nil, errors.New("additional nodes at end of proof") + } + return cld, nil + } + } + return nil, errors.New("unexpected end of proof") +} + +func get(tn node, key []byte) ([]byte, node) { + for len(key) > 0 { + switch n := tn.(type) { + case *shortNode: + if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { + return nil, nil + } + tn = n.Val + key = key[len(n.Key):] + case *fullNode: + tn = n.Children[key[0]] + key = key[1:] + case hashNode: + return key, n + case nil: + return key, nil + default: + panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) + } + } + return nil, tn.(valueNode) +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/proof_test.go b/vendor/github.com/ethereum/go-ethereum/trie/proof_test.go new file mode 100644 index 0000000..91ebcd4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/proof_test.go @@ -0,0 +1,155 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + crand "crypto/rand" + mrand "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +func init() { + mrand.Seed(time.Now().Unix()) +} + +func TestProof(t *testing.T) { + trie, vals := randomTrie(500) + root := trie.Hash() + for _, kv := range vals { + proof := trie.Prove(kv.k) + if proof == nil { + t.Fatalf("missing key %x while constructing proof", kv.k) + } + val, err := VerifyProof(root, kv.k, proof) + if err != nil { + t.Fatalf("VerifyProof error for key %x: %v\nraw proof: %x", kv.k, err, proof) + } + if !bytes.Equal(val, kv.v) { + t.Fatalf("VerifyProof returned wrong value for key %x: got %x, want %x", kv.k, val, kv.v) + } + } +} + +func TestOneElementProof(t *testing.T) { + trie := new(Trie) + updateString(trie, "k", "v") + proof := trie.Prove([]byte("k")) + if proof == nil { + t.Fatal("nil proof") + } + if len(proof) != 1 { + t.Error("proof should have one element") + } + val, err := VerifyProof(trie.Hash(), []byte("k"), proof) + if err != nil { + t.Fatalf("VerifyProof error: %v\nraw proof: %x", err, proof) + } + if !bytes.Equal(val, []byte("v")) { + t.Fatalf("VerifyProof returned wrong value: got %x, want 'k'", val) + } +} + +func TestVerifyBadProof(t *testing.T) { + trie, vals := randomTrie(800) + root := trie.Hash() + for _, kv := range vals { + proof := trie.Prove(kv.k) + if proof == nil { + t.Fatal("nil proof") + } + mutateByte(proof[mrand.Intn(len(proof))]) + if _, err := VerifyProof(root, kv.k, proof); err == nil { + t.Fatalf("expected proof to fail for key %x", kv.k) + } + } +} + +// mutateByte changes one byte in b. +func mutateByte(b []byte) { + for r := mrand.Intn(len(b)); ; { + new := byte(mrand.Intn(255)) + if new != b[r] { + b[r] = new + break + } + } +} + +func BenchmarkProve(b *testing.B) { + trie, vals := randomTrie(100) + var keys []string + for k := range vals { + keys = append(keys, k) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + kv := vals[keys[i%len(keys)]] + if trie.Prove(kv.k) == nil { + b.Fatalf("nil proof for %x", kv.k) + } + } +} + +func BenchmarkVerifyProof(b *testing.B) { + trie, vals := randomTrie(100) + root := trie.Hash() + var keys []string + var proofs [][]rlp.RawValue + for k := range vals { + keys = append(keys, k) + proofs = append(proofs, trie.Prove([]byte(k))) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + im := i % len(keys) + if _, err := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil { + b.Fatalf("key %x: %v", keys[im], err) + } + } +} + +func randomTrie(n int) (*Trie, map[string]*kv) { + trie := new(Trie) + vals := make(map[string]*kv) + for i := byte(0); i < 100; i++ { + value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} + value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false} + trie.Update(value.k, value.v) + trie.Update(value2.k, value2.v) + vals[string(value.k)] = value + vals[string(value2.k)] = value2 + } + for i := 0; i < n; i++ { + value := &kv{randBytes(32), randBytes(20), false} + trie.Update(value.k, value.v) + vals[string(value.k)] = value + } + return trie, vals +} + +func randBytes(n int) []byte { + r := make([]byte, n) + crand.Read(r) + return r +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go new file mode 100644 index 0000000..8b90da0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie.go @@ -0,0 +1,214 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +var secureKeyPrefix = []byte("secure-key-") + +const secureKeyLength = 11 + 32 // Length of the above prefix + 32byte hash + +// SecureTrie wraps a trie with key hashing. In a secure trie, all +// access operations hash the key using keccak256. This prevents +// calling code from creating long chains of nodes that +// increase the access time. +// +// Contrary to a regular trie, a SecureTrie can only be created with +// New and must have an attached database. The database also stores +// the preimage of each key. +// +// SecureTrie is not safe for concurrent use. +type SecureTrie struct { + trie Trie + hashKeyBuf [secureKeyLength]byte + secKeyBuf [200]byte + secKeyCache map[string][]byte + secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch +} + +// NewSecure creates a trie with an existing root node from db. +// +// If root is the zero hash or the sha3 hash of an empty string, the +// trie is initially empty. Otherwise, New will panic if db is nil +// and returns MissingNodeError if the root node cannot be found. +// +// Accessing the trie loads nodes from db on demand. +// Loaded nodes are kept around until their 'cache generation' expires. +// A new cache generation is created by each call to Commit. +// cachelimit sets the number of past cache generations to keep. +func NewSecure(root common.Hash, db Database, cachelimit uint16) (*SecureTrie, error) { + if db == nil { + panic("NewSecure called with nil database") + } + trie, err := New(root, db) + if err != nil { + return nil, err + } + trie.SetCacheLimit(cachelimit) + return &SecureTrie{trie: *trie}, nil +} + +// Get returns the value for key stored in the trie. +// The value bytes must not be modified by the caller. +func (t *SecureTrie) Get(key []byte) []byte { + res, err := t.TryGet(key) + if err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } + return res +} + +// TryGet returns the value for key stored in the trie. +// The value bytes must not be modified by the caller. +// If a node was not found in the database, a MissingNodeError is returned. +func (t *SecureTrie) TryGet(key []byte) ([]byte, error) { + return t.trie.TryGet(t.hashKey(key)) +} + +// Update associates key with value in the trie. Subsequent calls to +// Get will return value. If value has length zero, any existing value +// is deleted from the trie and calls to Get will return nil. +// +// The value bytes must not be modified by the caller while they are +// stored in the trie. +func (t *SecureTrie) Update(key, value []byte) { + if err := t.TryUpdate(key, value); err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } +} + +// TryUpdate associates key with value in the trie. Subsequent calls to +// Get will return value. If value has length zero, any existing value +// is deleted from the trie and calls to Get will return nil. +// +// The value bytes must not be modified by the caller while they are +// stored in the trie. +// +// If a node was not found in the database, a MissingNodeError is returned. +func (t *SecureTrie) TryUpdate(key, value []byte) error { + hk := t.hashKey(key) + err := t.trie.TryUpdate(hk, value) + if err != nil { + return err + } + t.getSecKeyCache()[string(hk)] = common.CopyBytes(key) + return nil +} + +// Delete removes any existing value for key from the trie. +func (t *SecureTrie) Delete(key []byte) { + if err := t.TryDelete(key); err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } +} + +// TryDelete removes any existing value for key from the trie. +// If a node was not found in the database, a MissingNodeError is returned. +func (t *SecureTrie) TryDelete(key []byte) error { + hk := t.hashKey(key) + delete(t.getSecKeyCache(), string(hk)) + return t.trie.TryDelete(hk) +} + +// GetKey returns the sha3 preimage of a hashed key that was +// previously used to store a value. +func (t *SecureTrie) GetKey(shaKey []byte) []byte { + if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { + return key + } + key, _ := t.trie.db.Get(t.secKey(shaKey)) + return key +} + +// Commit writes all nodes and the secure hash pre-images to the trie's database. +// Nodes are stored with their sha3 hash as the key. +// +// Committing flushes nodes from memory. Subsequent Get calls will load nodes +// from the database. +func (t *SecureTrie) Commit() (root common.Hash, err error) { + return t.CommitTo(t.trie.db) +} + +func (t *SecureTrie) Hash() common.Hash { + return t.trie.Hash() +} + +func (t *SecureTrie) Root() []byte { + return t.trie.Root() +} + +func (t *SecureTrie) Iterator() *Iterator { + return t.trie.Iterator() +} + +func (t *SecureTrie) NodeIterator() NodeIterator { + return NewNodeIterator(&t.trie) +} + +// CommitTo writes all nodes and the secure hash pre-images to the given database. +// Nodes are stored with their sha3 hash as the key. +// +// Committing flushes nodes from memory. Subsequent Get calls will load nodes from +// the trie's database. Calling code must ensure that the changes made to db are +// written back to the trie's attached database before using the trie. +func (t *SecureTrie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { + if len(t.getSecKeyCache()) > 0 { + for hk, key := range t.secKeyCache { + if err := db.Put(t.secKey([]byte(hk)), key); err != nil { + return common.Hash{}, err + } + } + t.secKeyCache = make(map[string][]byte) + } + return t.trie.CommitTo(db) +} + +// secKey returns the database key for the preimage of key, as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. +func (t *SecureTrie) secKey(key []byte) []byte { + buf := append(t.secKeyBuf[:0], secureKeyPrefix...) + buf = append(buf, key...) + return buf +} + +// hashKey returns the hash of key as an ephemeral buffer. +// The caller must not hold onto the return value because it will become +// invalid on the next call to hashKey or secKey. +func (t *SecureTrie) hashKey(key []byte) []byte { + h := newHasher(0, 0) + h.sha.Reset() + h.sha.Write(key) + buf := h.sha.Sum(t.hashKeyBuf[:0]) + returnHasherToPool(h) + return buf +} + +// getSecKeyCache returns the current secure key cache, creating a new one if +// ownership changed (i.e. the current secure trie is a copy of another owning +// the actual cache). +func (t *SecureTrie) getSecKeyCache() map[string][]byte { + if t != t.secKeyCacheOwner { + t.secKeyCacheOwner = t + t.secKeyCache = make(map[string][]byte) + } + return t.secKeyCache +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/secure_trie_test.go b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie_test.go new file mode 100644 index 0000000..159640f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/secure_trie_test.go @@ -0,0 +1,145 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "runtime" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" +) + +func newEmptySecure() *SecureTrie { + db, _ := ethdb.NewMemDatabase() + trie, _ := NewSecure(common.Hash{}, db, 0) + return trie +} + +// makeTestSecureTrie creates a large enough secure trie for testing. +func makeTestSecureTrie() (ethdb.Database, *SecureTrie, map[string][]byte) { + // Create an empty trie + db, _ := ethdb.NewMemDatabase() + trie, _ := NewSecure(common.Hash{}, db, 0) + + // Fill it with some arbitrary data + content := make(map[string][]byte) + for i := byte(0); i < 255; i++ { + // Map the same data under multiple keys + key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i} + content[string(key)] = val + trie.Update(key, val) + + key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i} + content[string(key)] = val + trie.Update(key, val) + + // Add some other data to inflate th trie + for j := byte(3); j < 13; j++ { + key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i} + content[string(key)] = val + trie.Update(key, val) + } + } + trie.Commit() + + // Return the generated trie + return db, trie, content +} + +func TestSecureDelete(t *testing.T) { + trie := newEmptySecure() + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"ether", ""}, + {"dog", "puppy"}, + {"shaman", ""}, + } + for _, val := range vals { + if val.v != "" { + trie.Update([]byte(val.k), []byte(val.v)) + } else { + trie.Delete([]byte(val.k)) + } + } + hash := trie.Hash() + exp := common.HexToHash("29b235a58c3c25ab83010c327d5932bcf05324b7d6b1185e650798034783ca9d") + if hash != exp { + t.Errorf("expected %x got %x", exp, hash) + } +} + +func TestSecureGetKey(t *testing.T) { + trie := newEmptySecure() + trie.Update([]byte("foo"), []byte("bar")) + + key := []byte("foo") + value := []byte("bar") + seckey := crypto.Keccak256(key) + + if !bytes.Equal(trie.Get(key), value) { + t.Errorf("Get did not return bar") + } + if k := trie.GetKey(seckey); !bytes.Equal(k, key) { + t.Errorf("GetKey returned %q, want %q", k, key) + } +} + +func TestSecureTrieConcurrency(t *testing.T) { + // Create an initial trie and copy if for concurrent access + _, trie, _ := makeTestSecureTrie() + + threads := runtime.NumCPU() + tries := make([]*SecureTrie, threads) + for i := 0; i < threads; i++ { + cpy := *trie + tries[i] = &cpy + } + // Start a batch of goroutines interactng with the trie + pend := new(sync.WaitGroup) + pend.Add(threads) + for i := 0; i < threads; i++ { + go func(index int) { + defer pend.Done() + + for j := byte(0); j < 255; j++ { + // Map the same data under multiple keys + key, val := common.LeftPadBytes([]byte{byte(index), 1, j}, 32), []byte{j} + tries[index].Update(key, val) + + key, val = common.LeftPadBytes([]byte{byte(index), 2, j}, 32), []byte{j} + tries[index].Update(key, val) + + // Add some other data to inflate the trie + for k := byte(3); k < 13; k++ { + key, val = common.LeftPadBytes([]byte{byte(index), k, j}, 32), []byte{k, j} + tries[index].Update(key, val) + } + } + tries[index].Commit() + }(i) + } + // Wait for all threads to finish + pend.Wait() +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/sync.go b/vendor/github.com/ethereum/go-ethereum/trie/sync.go new file mode 100644 index 0000000..1685013 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/sync.go @@ -0,0 +1,285 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "gopkg.in/karalabe/cookiejar.v2/collections/prque" +) + +// ErrNotRequested is returned by the trie sync when it's requested to process a +// node it did not request. +var ErrNotRequested = errors.New("not requested") + +// request represents a scheduled or already in-flight state retrieval request. +type request struct { + hash common.Hash // Hash of the node data content to retrieve + data []byte // Data content of the node, cached until all subtrees complete + raw bool // Whether this is a raw entry (code) or a trie node + + parents []*request // Parent state nodes referencing this entry (notify all upon completion) + depth int // Depth level within the trie the node is located to prioritise DFS + deps int // Number of dependencies before allowed to commit this node + + callback TrieSyncLeafCallback // Callback to invoke if a leaf node it reached on this branch +} + +// SyncResult is a simple list to return missing nodes along with their request +// hashes. +type SyncResult struct { + Hash common.Hash // Hash of the originally unknown trie node + Data []byte // Data content of the retrieved node +} + +// TrieSyncLeafCallback is a callback type invoked when a trie sync reaches a +// leaf node. It's used by state syncing to check if the leaf node requires some +// further data syncing. +type TrieSyncLeafCallback func(leaf []byte, parent common.Hash) error + +// TrieSync is the main state trie synchronisation scheduler, which provides yet +// unknown trie hashes to retrieve, accepts node data associated with said hashes +// and reconstructs the trie step by step until all is done. +type TrieSync struct { + database DatabaseReader + requests map[common.Hash]*request // Pending requests pertaining to a key hash + queue *prque.Prque // Priority queue with the pending requests +} + +// NewTrieSync creates a new trie data download scheduler. +func NewTrieSync(root common.Hash, database DatabaseReader, callback TrieSyncLeafCallback) *TrieSync { + ts := &TrieSync{ + database: database, + requests: make(map[common.Hash]*request), + queue: prque.New(), + } + ts.AddSubTrie(root, 0, common.Hash{}, callback) + return ts +} + +// AddSubTrie registers a new trie to the sync code, rooted at the designated parent. +func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback TrieSyncLeafCallback) { + // Short circuit if the trie is empty or already known + if root == emptyRoot { + return + } + key := root.Bytes() + blob, _ := s.database.Get(key) + if local, err := decodeNode(key, blob, 0); local != nil && err == nil { + return + } + // Assemble the new sub-trie sync request + req := &request{ + hash: root, + depth: depth, + callback: callback, + } + // If this sub-trie has a designated parent, link them together + if parent != (common.Hash{}) { + ancestor := s.requests[parent] + if ancestor == nil { + panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent)) + } + ancestor.deps++ + req.parents = append(req.parents, ancestor) + } + s.schedule(req) +} + +// AddRawEntry schedules the direct retrieval of a state entry that should not be +// interpreted as a trie node, but rather accepted and stored into the database +// as is. This method's goal is to support misc state metadata retrievals (e.g. +// contract code). +func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) { + // Short circuit if the entry is empty or already known + if hash == emptyState { + return + } + if blob, _ := s.database.Get(hash.Bytes()); blob != nil { + return + } + // Assemble the new sub-trie sync request + req := &request{ + hash: hash, + raw: true, + depth: depth, + } + // If this sub-trie has a designated parent, link them together + if parent != (common.Hash{}) { + ancestor := s.requests[parent] + if ancestor == nil { + panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent)) + } + ancestor.deps++ + req.parents = append(req.parents, ancestor) + } + s.schedule(req) +} + +// Missing retrieves the known missing nodes from the trie for retrieval. +func (s *TrieSync) Missing(max int) []common.Hash { + requests := []common.Hash{} + for !s.queue.Empty() && (max == 0 || len(requests) < max) { + requests = append(requests, s.queue.PopItem().(common.Hash)) + } + return requests +} + +// Process injects a batch of retrieved trie nodes data, returning if something +// was committed to the database and also the index of an entry if processing of +// it failed. +func (s *TrieSync) Process(results []SyncResult, dbw DatabaseWriter) (bool, int, error) { + committed := false + + for i, item := range results { + // If the item was not requested, bail out + request := s.requests[item.Hash] + if request == nil { + return committed, i, ErrNotRequested + } + // If the item is a raw entry request, commit directly + if request.raw { + request.data = item.Data + s.commit(request, dbw) + committed = true + continue + } + // Decode the node data content and update the request + node, err := decodeNode(item.Hash[:], item.Data, 0) + if err != nil { + return committed, i, err + } + request.data = item.Data + + // Create and schedule a request for all the children nodes + requests, err := s.children(request, node) + if err != nil { + return committed, i, err + } + if len(requests) == 0 && request.deps == 0 { + s.commit(request, dbw) + committed = true + continue + } + request.deps += len(requests) + for _, child := range requests { + s.schedule(child) + } + } + return committed, 0, nil +} + +// Pending returns the number of state entries currently pending for download. +func (s *TrieSync) Pending() int { + return len(s.requests) +} + +// schedule inserts a new state retrieval request into the fetch queue. If there +// is already a pending request for this node, the new request will be discarded +// and only a parent reference added to the old one. +func (s *TrieSync) schedule(req *request) { + // If we're already requesting this node, add a new reference and stop + if old, ok := s.requests[req.hash]; ok { + old.parents = append(old.parents, req.parents...) + return + } + // Schedule the request for future retrieval + s.queue.Push(req.hash, float32(req.depth)) + s.requests[req.hash] = req +} + +// children retrieves all the missing children of a state trie entry for future +// retrieval scheduling. +func (s *TrieSync) children(req *request, object node) ([]*request, error) { + // Gather all the children of the node, irrelevant whether known or not + type child struct { + node node + depth int + } + children := []child{} + + switch node := (object).(type) { + case *shortNode: + children = []child{{ + node: node.Val, + depth: req.depth + len(node.Key), + }} + case *fullNode: + for i := 0; i < 17; i++ { + if node.Children[i] != nil { + children = append(children, child{ + node: node.Children[i], + depth: req.depth + 1, + }) + } + } + default: + panic(fmt.Sprintf("unknown node: %+v", node)) + } + // Iterate over the children, and request all unknown ones + requests := make([]*request, 0, len(children)) + for _, child := range children { + // Notify any external watcher of a new key/value node + if req.callback != nil { + if node, ok := (child.node).(valueNode); ok { + if err := req.callback(node, req.hash); err != nil { + return nil, err + } + } + } + // If the child references another node, resolve or schedule + if node, ok := (child.node).(hashNode); ok { + // Try to resolve the node from the local database + blob, _ := s.database.Get(node) + if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil { + continue + } + // Locally unknown node, schedule for retrieval + requests = append(requests, &request{ + hash: common.BytesToHash(node), + parents: []*request{req}, + depth: child.depth, + callback: req.callback, + }) + } + } + return requests, nil +} + +// commit finalizes a retrieval request and stores it into the database. If any +// of the referencing parent requests complete due to this commit, they are also +// committed themselves. +func (s *TrieSync) commit(req *request, dbw DatabaseWriter) (err error) { + // Write the node content to disk + if err := dbw.Put(req.hash[:], req.data); err != nil { + return err + } + delete(s.requests, req.hash) + + // Check all parents for completion + for _, parent := range req.parents { + parent.deps-- + if parent.deps == 0 { + if err := s.commit(parent, dbw); err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/sync_test.go b/vendor/github.com/ethereum/go-ethereum/trie/sync_test.go new file mode 100644 index 0000000..acae039 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/sync_test.go @@ -0,0 +1,333 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +// makeTestTrie create a sample test trie to test node-wise reconstruction. +func makeTestTrie() (ethdb.Database, *Trie, map[string][]byte) { + // Create an empty trie + db, _ := ethdb.NewMemDatabase() + trie, _ := New(common.Hash{}, db) + + // Fill it with some arbitrary data + content := make(map[string][]byte) + for i := byte(0); i < 255; i++ { + // Map the same data under multiple keys + key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i} + content[string(key)] = val + trie.Update(key, val) + + key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i} + content[string(key)] = val + trie.Update(key, val) + + // Add some other data to inflate th trie + for j := byte(3); j < 13; j++ { + key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i} + content[string(key)] = val + trie.Update(key, val) + } + } + trie.Commit() + + // Return the generated trie + return db, trie, content +} + +// checkTrieContents cross references a reconstructed trie with an expected data +// content map. +func checkTrieContents(t *testing.T, db Database, root []byte, content map[string][]byte) { + // Check root availability and trie contents + trie, err := New(common.BytesToHash(root), db) + if err != nil { + t.Fatalf("failed to create trie at %x: %v", root, err) + } + if err := checkTrieConsistency(db, common.BytesToHash(root)); err != nil { + t.Fatalf("inconsistent trie at %x: %v", root, err) + } + for key, val := range content { + if have := trie.Get([]byte(key)); !bytes.Equal(have, val) { + t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val) + } + } +} + +// checkTrieConsistency checks that all nodes in a trie are indeed present. +func checkTrieConsistency(db Database, root common.Hash) error { + // Create and iterate a trie rooted in a subnode + trie, err := New(root, db) + if err != nil { + return nil // // Consider a non existent state consistent + } + it := NewNodeIterator(trie) + for it.Next(true) { + } + return it.Error() +} + +// Tests that an empty trie is not scheduled for syncing. +func TestEmptyTrieSync(t *testing.T) { + emptyA, _ := New(common.Hash{}, nil) + emptyB, _ := New(emptyRoot, nil) + + for i, trie := range []*Trie{emptyA, emptyB} { + db, _ := ethdb.NewMemDatabase() + if req := NewTrieSync(common.BytesToHash(trie.Root()), db, nil).Missing(1); len(req) != 0 { + t.Errorf("test %d: content requested for empty trie: %v", i, req) + } + } +} + +// Tests that given a root hash, a trie can sync iteratively on a single thread, +// requesting retrieval tasks and returning all of them in one go. +func TestIterativeTrieSyncIndividual(t *testing.T) { testIterativeTrieSync(t, 1) } +func TestIterativeTrieSyncBatched(t *testing.T) { testIterativeTrieSync(t, 100) } + +func testIterativeTrieSync(t *testing.T, batch int) { + // Create a random trie to copy + srcDb, srcTrie, srcData := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + queue := append([]common.Hash{}, sched.Missing(batch)...) + for len(queue) > 0 { + results := make([]SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = SyncResult{hash, data} + } + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = append(queue[:0], sched.Missing(batch)...) + } + // Cross check that the two tries are in sync + checkTrieContents(t, dstDb, srcTrie.Root(), srcData) +} + +// Tests that the trie scheduler can correctly reconstruct the state even if only +// partial results are returned, and the others sent only later. +func TestIterativeDelayedTrieSync(t *testing.T) { + // Create a random trie to copy + srcDb, srcTrie, srcData := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + queue := append([]common.Hash{}, sched.Missing(10000)...) + for len(queue) > 0 { + // Sync only half of the scheduled nodes + results := make([]SyncResult, len(queue)/2+1) + for i, hash := range queue[:len(results)] { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = SyncResult{hash, data} + } + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = append(queue[len(results):], sched.Missing(10000)...) + } + // Cross check that the two tries are in sync + checkTrieContents(t, dstDb, srcTrie.Root(), srcData) +} + +// Tests that given a root hash, a trie can sync iteratively on a single thread, +// requesting retrieval tasks and returning all of them in one go, however in a +// random order. +func TestIterativeRandomTrieSyncIndividual(t *testing.T) { testIterativeRandomTrieSync(t, 1) } +func TestIterativeRandomTrieSyncBatched(t *testing.T) { testIterativeRandomTrieSync(t, 100) } + +func testIterativeRandomTrieSync(t *testing.T, batch int) { + // Create a random trie to copy + srcDb, srcTrie, srcData := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + queue := make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(batch) { + queue[hash] = struct{}{} + } + for len(queue) > 0 { + // Fetch all the queued nodes in a random order + results := make([]SyncResult, 0, len(queue)) + for hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results = append(results, SyncResult{hash, data}) + } + // Feed the retrieved results back and queue new tasks + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(batch) { + queue[hash] = struct{}{} + } + } + // Cross check that the two tries are in sync + checkTrieContents(t, dstDb, srcTrie.Root(), srcData) +} + +// Tests that the trie scheduler can correctly reconstruct the state even if only +// partial results are returned (Even those randomly), others sent only later. +func TestIterativeRandomDelayedTrieSync(t *testing.T) { + // Create a random trie to copy + srcDb, srcTrie, srcData := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + queue := make(map[common.Hash]struct{}) + for _, hash := range sched.Missing(10000) { + queue[hash] = struct{}{} + } + for len(queue) > 0 { + // Sync only half of the scheduled nodes, even those in random order + results := make([]SyncResult, 0, len(queue)/2+1) + for hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results = append(results, SyncResult{hash, data}) + + if len(results) >= cap(results) { + break + } + } + // Feed the retrieved results back and queue new tasks + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + for _, result := range results { + delete(queue, result.Hash) + } + for _, hash := range sched.Missing(10000) { + queue[hash] = struct{}{} + } + } + // Cross check that the two tries are in sync + checkTrieContents(t, dstDb, srcTrie.Root(), srcData) +} + +// Tests that a trie sync will not request nodes multiple times, even if they +// have such references. +func TestDuplicateAvoidanceTrieSync(t *testing.T) { + // Create a random trie to copy + srcDb, srcTrie, srcData := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + queue := append([]common.Hash{}, sched.Missing(0)...) + requested := make(map[common.Hash]struct{}) + + for len(queue) > 0 { + results := make([]SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + if _, ok := requested[hash]; ok { + t.Errorf("hash %x already requested once", hash) + } + requested[hash] = struct{}{} + + results[i] = SyncResult{hash, data} + } + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + queue = append(queue[:0], sched.Missing(0)...) + } + // Cross check that the two tries are in sync + checkTrieContents(t, dstDb, srcTrie.Root(), srcData) +} + +// Tests that at any point in time during a sync, only complete sub-tries are in +// the database. +func TestIncompleteTrieSync(t *testing.T) { + // Create a random trie to copy + srcDb, srcTrie, _ := makeTestTrie() + + // Create a destination trie and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewTrieSync(common.BytesToHash(srcTrie.Root()), dstDb, nil) + + added := []common.Hash{} + queue := append([]common.Hash{}, sched.Missing(1)...) + for len(queue) > 0 { + // Fetch a batch of trie nodes + results := make([]SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = SyncResult{hash, data} + } + // Process each of the trie nodes + if _, index, err := sched.Process(results, dstDb); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + for _, result := range results { + added = append(added, result.Hash) + } + // Check that all known sub-tries in the synced trie is complete + for _, root := range added { + if err := checkTrieConsistency(dstDb, root); err != nil { + t.Fatalf("trie inconsistent: %v", err) + } + } + // Fetch the next batch to retrieve + queue = append(queue[:0], sched.Missing(1)...) + } + // Sanity check that removing any node from the database is detected + for _, node := range added[1:] { + key := node.Bytes() + value, _ := dstDb.Get(key) + + dstDb.Delete(key) + if err := checkTrieConsistency(dstDb, added[0]); err == nil { + t.Fatalf("trie inconsistency not caught, missing: %x", key) + } + dstDb.Put(key, value) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/trie.go b/vendor/github.com/ethereum/go-ethereum/trie/trie.go new file mode 100644 index 0000000..cd9e20c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/trie.go @@ -0,0 +1,511 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package trie implements Merkle Patricia Tries. +package trie + +import ( + "bytes" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/rcrowley/go-metrics" +) + +var ( + // This is the known root hash of an empty trie. + emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + // This is the known hash of an empty state trie entry. + emptyState common.Hash +) + +var ( + cacheMissCounter = metrics.NewRegisteredCounter("trie/cachemiss", nil) + cacheUnloadCounter = metrics.NewRegisteredCounter("trie/cacheunload", nil) +) + +// CacheMisses retrieves a global counter measuring the number of cache misses +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheMisses() int64 { + return cacheMissCounter.Count() +} + +// CacheUnloads retrieves a global counter measuring the number of cache unloads +// the trie did since process startup. This isn't useful for anything apart from +// trie debugging purposes. +func CacheUnloads() int64 { + return cacheUnloadCounter.Count() +} + +func init() { + sha3.NewKeccak256().Sum(emptyState[:0]) +} + +// Database must be implemented by backing stores for the trie. +type Database interface { + DatabaseReader + DatabaseWriter +} + +// DatabaseReader wraps the Get method of a backing store for the trie. +type DatabaseReader interface { + Get(key []byte) (value []byte, err error) +} + +// DatabaseWriter wraps the Put method of a backing store for the trie. +type DatabaseWriter interface { + // Put stores the mapping key->value in the database. + // Implementations must not hold onto the value bytes, the trie + // will reuse the slice across calls to Put. + Put(key, value []byte) error +} + +// Trie is a Merkle Patricia Trie. +// The zero value is an empty trie with no database. +// Use New to create a trie that sits on top of a database. +// +// Trie is not safe for concurrent use. +type Trie struct { + root node + db Database + originalRoot common.Hash + + // Cache generation values. + // cachegen increase by one with each commit operation. + // new nodes are tagged with the current generation and unloaded + // when their generation is older than than cachegen-cachelimit. + cachegen, cachelimit uint16 +} + +// SetCacheLimit sets the number of 'cache generations' to keep. +// A cache generations is created by a call to Commit. +func (t *Trie) SetCacheLimit(l uint16) { + t.cachelimit = l +} + +// newFlag returns the cache flag value for a newly created node. +func (t *Trie) newFlag() nodeFlag { + return nodeFlag{dirty: true, gen: t.cachegen} +} + +// New creates a trie with an existing root node from db. +// +// If root is the zero hash or the sha3 hash of an empty string, the +// trie is initially empty and does not require a database. Otherwise, +// New will panic if db is nil and returns a MissingNodeError if root does +// not exist in the database. Accessing the trie loads nodes from db on demand. +func New(root common.Hash, db Database) (*Trie, error) { + trie := &Trie{db: db, originalRoot: root} + if (root != common.Hash{}) && root != emptyRoot { + if db == nil { + panic("trie.New: cannot use existing root without a database") + } + rootnode, err := trie.resolveHash(root[:], nil, nil) + if err != nil { + return nil, err + } + trie.root = rootnode + } + return trie, nil +} + +// Iterator returns an iterator over all mappings in the trie. +func (t *Trie) Iterator() *Iterator { + return NewIterator(t) +} + +// Get returns the value for key stored in the trie. +// The value bytes must not be modified by the caller. +func (t *Trie) Get(key []byte) []byte { + res, err := t.TryGet(key) + if err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } + return res +} + +// TryGet returns the value for key stored in the trie. +// The value bytes must not be modified by the caller. +// If a node was not found in the database, a MissingNodeError is returned. +func (t *Trie) TryGet(key []byte) ([]byte, error) { + key = compactHexDecode(key) + value, newroot, didResolve, err := t.tryGet(t.root, key, 0) + if err == nil && didResolve { + t.root = newroot + } + return value, err +} + +func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) { + switch n := (origNode).(type) { + case nil: + return nil, nil, false, nil + case valueNode: + return n, n, false, nil + case *shortNode: + if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { + // key not found in trie + return nil, n, false, nil + } + value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key)) + if err == nil && didResolve { + n = n.copy() + n.Val = newnode + n.flags.gen = t.cachegen + } + return value, n, didResolve, err + case *fullNode: + value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1) + if err == nil && didResolve { + n = n.copy() + n.flags.gen = t.cachegen + n.Children[key[pos]] = newnode + } + return value, n, didResolve, err + case hashNode: + child, err := t.resolveHash(n, key[:pos], key[pos:]) + if err != nil { + return nil, n, true, err + } + value, newnode, _, err := t.tryGet(child, key, pos) + return value, newnode, true, err + default: + panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode)) + } +} + +// Update associates key with value in the trie. Subsequent calls to +// Get will return value. If value has length zero, any existing value +// is deleted from the trie and calls to Get will return nil. +// +// The value bytes must not be modified by the caller while they are +// stored in the trie. +func (t *Trie) Update(key, value []byte) { + if err := t.TryUpdate(key, value); err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } +} + +// TryUpdate associates key with value in the trie. Subsequent calls to +// Get will return value. If value has length zero, any existing value +// is deleted from the trie and calls to Get will return nil. +// +// The value bytes must not be modified by the caller while they are +// stored in the trie. +// +// If a node was not found in the database, a MissingNodeError is returned. +func (t *Trie) TryUpdate(key, value []byte) error { + k := compactHexDecode(key) + if len(value) != 0 { + _, n, err := t.insert(t.root, nil, k, valueNode(value)) + if err != nil { + return err + } + t.root = n + } else { + _, n, err := t.delete(t.root, nil, k) + if err != nil { + return err + } + t.root = n + } + return nil +} + +func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) { + if len(key) == 0 { + if v, ok := n.(valueNode); ok { + return !bytes.Equal(v, value.(valueNode)), value, nil + } + return true, value, nil + } + switch n := n.(type) { + case *shortNode: + matchlen := prefixLen(key, n.Key) + // If the whole key matches, keep this short node as is + // and only update the value. + if matchlen == len(n.Key) { + dirty, nn, err := t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value) + if !dirty || err != nil { + return false, n, err + } + return true, &shortNode{n.Key, nn, t.newFlag()}, nil + } + // Otherwise branch out at the index where they differ. + branch := &fullNode{flags: t.newFlag()} + var err error + _, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val) + if err != nil { + return false, nil, err + } + _, branch.Children[key[matchlen]], err = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value) + if err != nil { + return false, nil, err + } + // Replace this shortNode with the branch if it occurs at index 0. + if matchlen == 0 { + return true, branch, nil + } + // Otherwise, replace it with a short node leading up to the branch. + return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil + + case *fullNode: + dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value) + if !dirty || err != nil { + return false, n, err + } + n = n.copy() + n.flags = t.newFlag() + n.Children[key[0]] = nn + return true, n, nil + + case nil: + return true, &shortNode{key, value, t.newFlag()}, nil + + case hashNode: + // We've hit a part of the trie that isn't loaded yet. Load + // the node and insert into it. This leaves all child nodes on + // the path to the value in the trie. + rn, err := t.resolveHash(n, prefix, key) + if err != nil { + return false, nil, err + } + dirty, nn, err := t.insert(rn, prefix, key, value) + if !dirty || err != nil { + return false, rn, err + } + return true, nn, nil + + default: + panic(fmt.Sprintf("%T: invalid node: %v", n, n)) + } +} + +// Delete removes any existing value for key from the trie. +func (t *Trie) Delete(key []byte) { + if err := t.TryDelete(key); err != nil && glog.V(logger.Error) { + glog.Errorf("Unhandled trie error: %v", err) + } +} + +// TryDelete removes any existing value for key from the trie. +// If a node was not found in the database, a MissingNodeError is returned. +func (t *Trie) TryDelete(key []byte) error { + k := compactHexDecode(key) + _, n, err := t.delete(t.root, nil, k) + if err != nil { + return err + } + t.root = n + return nil +} + +// delete returns the new root of the trie with key deleted. +// It reduces the trie to minimal form by simplifying +// nodes on the way up after deleting recursively. +func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { + switch n := n.(type) { + case *shortNode: + matchlen := prefixLen(key, n.Key) + if matchlen < len(n.Key) { + return false, n, nil // don't replace n on mismatch + } + if matchlen == len(key) { + return true, nil, nil // remove n entirely for whole matches + } + // The key is longer than n.Key. Remove the remaining suffix + // from the subtrie. Child can never be nil here since the + // subtrie must contain at least two other values with keys + // longer than n.Key. + dirty, child, err := t.delete(n.Val, append(prefix, key[:len(n.Key)]...), key[len(n.Key):]) + if !dirty || err != nil { + return false, n, err + } + switch child := child.(type) { + case *shortNode: + // Deleting from the subtrie reduced it to another + // short node. Merge the nodes to avoid creating a + // shortNode{..., shortNode{...}}. Use concat (which + // always creates a new slice) instead of append to + // avoid modifying n.Key since it might be shared with + // other nodes. + return true, &shortNode{concat(n.Key, child.Key...), child.Val, t.newFlag()}, nil + default: + return true, &shortNode{n.Key, child, t.newFlag()}, nil + } + + case *fullNode: + dirty, nn, err := t.delete(n.Children[key[0]], append(prefix, key[0]), key[1:]) + if !dirty || err != nil { + return false, n, err + } + n = n.copy() + n.flags = t.newFlag() + n.Children[key[0]] = nn + + // Check how many non-nil entries are left after deleting and + // reduce the full node to a short node if only one entry is + // left. Since n must've contained at least two children + // before deletion (otherwise it would not be a full node) n + // can never be reduced to nil. + // + // When the loop is done, pos contains the index of the single + // value that is left in n or -2 if n contains at least two + // values. + pos := -1 + for i, cld := range n.Children { + if cld != nil { + if pos == -1 { + pos = i + } else { + pos = -2 + break + } + } + } + if pos >= 0 { + if pos != 16 { + // If the remaining entry is a short node, it replaces + // n and its key gets the missing nibble tacked to the + // front. This avoids creating an invalid + // shortNode{..., shortNode{...}}. Since the entry + // might not be loaded yet, resolve it just for this + // check. + cnode, err := t.resolve(n.Children[pos], prefix, []byte{byte(pos)}) + if err != nil { + return false, nil, err + } + if cnode, ok := cnode.(*shortNode); ok { + k := append([]byte{byte(pos)}, cnode.Key...) + return true, &shortNode{k, cnode.Val, t.newFlag()}, nil + } + } + // Otherwise, n is replaced by a one-nibble short node + // containing the child. + return true, &shortNode{[]byte{byte(pos)}, n.Children[pos], t.newFlag()}, nil + } + // n still contains at least two values and cannot be reduced. + return true, n, nil + + case valueNode: + return true, nil, nil + + case nil: + return false, nil, nil + + case hashNode: + // We've hit a part of the trie that isn't loaded yet. Load + // the node and delete from it. This leaves all child nodes on + // the path to the value in the trie. + rn, err := t.resolveHash(n, prefix, key) + if err != nil { + return false, nil, err + } + dirty, nn, err := t.delete(rn, prefix, key) + if !dirty || err != nil { + return false, rn, err + } + return true, nn, nil + + default: + panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key)) + } +} + +func concat(s1 []byte, s2 ...byte) []byte { + r := make([]byte, len(s1)+len(s2)) + copy(r, s1) + copy(r[len(s1):], s2) + return r +} + +func (t *Trie) resolve(n node, prefix, suffix []byte) (node, error) { + if n, ok := n.(hashNode); ok { + return t.resolveHash(n, prefix, suffix) + } + return n, nil +} + +func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { + cacheMissCounter.Inc(1) + + enc, err := t.db.Get(n) + if err != nil || enc == nil { + return nil, &MissingNodeError{ + RootHash: t.originalRoot, + NodeHash: common.BytesToHash(n), + Key: compactHexEncode(append(prefix, suffix...)), + PrefixLen: len(prefix), + SuffixLen: len(suffix), + } + } + dec := mustDecodeNode(n, enc, t.cachegen) + return dec, nil +} + +// Root returns the root hash of the trie. +// Deprecated: use Hash instead. +func (t *Trie) Root() []byte { return t.Hash().Bytes() } + +// Hash returns the root hash of the trie. It does not write to the +// database and can be used even if the trie doesn't have one. +func (t *Trie) Hash() common.Hash { + hash, cached, _ := t.hashRoot(nil) + t.root = cached + return common.BytesToHash(hash.(hashNode)) +} + +// Commit writes all nodes to the trie's database. +// Nodes are stored with their sha3 hash as the key. +// +// Committing flushes nodes from memory. +// Subsequent Get calls will load nodes from the database. +func (t *Trie) Commit() (root common.Hash, err error) { + if t.db == nil { + panic("Commit called on trie with nil database") + } + return t.CommitTo(t.db) +} + +// CommitTo writes all nodes to the given database. +// Nodes are stored with their sha3 hash as the key. +// +// Committing flushes nodes from memory. Subsequent Get calls will +// load nodes from the trie's database. Calling code must ensure that +// the changes made to db are written back to the trie's attached +// database before using the trie. +func (t *Trie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { + hash, cached, err := t.hashRoot(db) + if err != nil { + return (common.Hash{}), err + } + t.root = cached + t.cachegen++ + return common.BytesToHash(hash.(hashNode)), nil +} + +func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) { + if t.root == nil { + return hashNode(emptyRoot.Bytes()), nil, nil + } + h := newHasher(t.cachegen, t.cachelimit) + defer returnHasherToPool(h) + return h.hash(t.root, db, true) +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/trie_test.go b/vendor/github.com/ethereum/go-ethereum/trie/trie_test.go new file mode 100644 index 0000000..60307db --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/trie/trie_test.go @@ -0,0 +1,599 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package trie + +import ( + "bytes" + "encoding/binary" + "fmt" + "io/ioutil" + "math/rand" + "os" + "reflect" + "testing" + "testing/quick" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +func init() { + spew.Config.Indent = " " + spew.Config.DisableMethods = true +} + +// Used for testing +func newEmpty() *Trie { + db, _ := ethdb.NewMemDatabase() + trie, _ := New(common.Hash{}, db) + return trie +} + +func TestEmptyTrie(t *testing.T) { + var trie Trie + res := trie.Hash() + exp := emptyRoot + if res != common.Hash(exp) { + t.Errorf("expected %x got %x", exp, res) + } +} + +func TestNull(t *testing.T) { + var trie Trie + key := make([]byte, 32) + value := []byte("test") + trie.Update(key, value) + if !bytes.Equal(trie.Get(key), value) { + t.Fatal("wrong value") + } +} + +func TestMissingRoot(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), db) + if trie != nil { + t.Error("New returned non-nil trie for invalid root") + } + if _, ok := err.(*MissingNodeError); !ok { + t.Errorf("New returned wrong error: %v", err) + } +} + +func TestMissingNode(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + trie, _ := New(common.Hash{}, db) + updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") + updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") + root, _ := trie.Commit() + + trie, _ = New(root, db) + _, err := trie.TryGet([]byte("120000")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + trie, _ = New(root, db) + _, err = trie.TryGet([]byte("120099")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + trie, _ = New(root, db) + _, err = trie.TryGet([]byte("123456")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + trie, _ = New(root, db) + err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + trie, _ = New(root, db) + err = trie.TryDelete([]byte("123456")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + db.Delete(common.FromHex("e1d943cc8f061a0c0b98162830b970395ac9315654824bf21b73b891365262f9")) + + trie, _ = New(root, db) + _, err = trie.TryGet([]byte("120000")) + if _, ok := err.(*MissingNodeError); !ok { + t.Errorf("Wrong error: %v", err) + } + + trie, _ = New(root, db) + _, err = trie.TryGet([]byte("120099")) + if _, ok := err.(*MissingNodeError); !ok { + t.Errorf("Wrong error: %v", err) + } + + trie, _ = New(root, db) + _, err = trie.TryGet([]byte("123456")) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + trie, _ = New(root, db) + err = trie.TryUpdate([]byte("120099"), []byte("zxcv")) + if _, ok := err.(*MissingNodeError); !ok { + t.Errorf("Wrong error: %v", err) + } + + trie, _ = New(root, db) + err = trie.TryDelete([]byte("123456")) + if _, ok := err.(*MissingNodeError); !ok { + t.Errorf("Wrong error: %v", err) + } +} + +func TestInsert(t *testing.T) { + trie := newEmpty() + + updateString(trie, "doe", "reindeer") + updateString(trie, "dog", "puppy") + updateString(trie, "dogglesworth", "cat") + + exp := common.HexToHash("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3") + root := trie.Hash() + if root != exp { + t.Errorf("exp %x got %x", exp, root) + } + + trie = newEmpty() + updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + + exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") + root, err := trie.Commit() + if err != nil { + t.Fatalf("commit error: %v", err) + } + if root != exp { + t.Errorf("exp %x got %x", exp, root) + } +} + +func TestGet(t *testing.T) { + trie := newEmpty() + updateString(trie, "doe", "reindeer") + updateString(trie, "dog", "puppy") + updateString(trie, "dogglesworth", "cat") + + for i := 0; i < 2; i++ { + res := getString(trie, "dog") + if !bytes.Equal(res, []byte("puppy")) { + t.Errorf("expected puppy got %x", res) + } + + unknown := getString(trie, "unknown") + if unknown != nil { + t.Errorf("expected nil got %x", unknown) + } + + if i == 1 { + return + } + trie.Commit() + } +} + +func TestDelete(t *testing.T) { + trie := newEmpty() + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"ether", ""}, + {"dog", "puppy"}, + {"shaman", ""}, + } + for _, val := range vals { + if val.v != "" { + updateString(trie, val.k, val.v) + } else { + deleteString(trie, val.k) + } + } + + hash := trie.Hash() + exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84") + if hash != exp { + t.Errorf("expected %x got %x", exp, hash) + } +} + +func TestEmptyValues(t *testing.T) { + trie := newEmpty() + + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"ether", ""}, + {"dog", "puppy"}, + {"shaman", ""}, + } + for _, val := range vals { + updateString(trie, val.k, val.v) + } + + hash := trie.Hash() + exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84") + if hash != exp { + t.Errorf("expected %x got %x", exp, hash) + } +} + +func TestReplication(t *testing.T) { + trie := newEmpty() + vals := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + {"shaman", "horse"}, + {"doge", "coin"}, + {"dog", "puppy"}, + {"somethingveryoddindeedthis is", "myothernodedata"}, + } + for _, val := range vals { + updateString(trie, val.k, val.v) + } + exp, err := trie.Commit() + if err != nil { + t.Fatalf("commit error: %v", err) + } + + // create a new trie on top of the database and check that lookups work. + trie2, err := New(exp, trie.db) + if err != nil { + t.Fatalf("can't recreate trie at %x: %v", exp, err) + } + for _, kv := range vals { + if string(getString(trie2, kv.k)) != kv.v { + t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v) + } + } + hash, err := trie2.Commit() + if err != nil { + t.Fatalf("commit error: %v", err) + } + if hash != exp { + t.Errorf("root failure. expected %x got %x", exp, hash) + } + + // perform some insertions on the new trie. + vals2 := []struct{ k, v string }{ + {"do", "verb"}, + {"ether", "wookiedoo"}, + {"horse", "stallion"}, + // {"shaman", "horse"}, + // {"doge", "coin"}, + // {"ether", ""}, + // {"dog", "puppy"}, + // {"somethingveryoddindeedthis is", "myothernodedata"}, + // {"shaman", ""}, + } + for _, val := range vals2 { + updateString(trie2, val.k, val.v) + } + if hash := trie2.Hash(); hash != exp { + t.Errorf("root failure. expected %x got %x", exp, hash) + } +} + +func TestLargeValue(t *testing.T) { + trie := newEmpty() + trie.Update([]byte("key1"), []byte{99, 99, 99, 99}) + trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32)) + trie.Hash() +} + +type countingDB struct { + Database + gets map[string]int +} + +func (db *countingDB) Get(key []byte) ([]byte, error) { + db.gets[string(key)]++ + return db.Database.Get(key) +} + +// TestCacheUnload checks that decoded nodes are unloaded after a +// certain number of commit operations. +func TestCacheUnload(t *testing.T) { + // Create test trie with two branches. + trie := newEmpty() + key1 := "---------------------------------" + key2 := "---some other branch" + updateString(trie, key1, "this is the branch of key1.") + updateString(trie, key2, "this is the branch of key2.") + root, _ := trie.Commit() + + // Commit the trie repeatedly and access key1. + // The branch containing it is loaded from DB exactly two times: + // in the 0th and 6th iteration. + db := &countingDB{Database: trie.db, gets: make(map[string]int)} + trie, _ = New(root, db) + trie.SetCacheLimit(5) + for i := 0; i < 12; i++ { + getString(trie, key1) + trie.Commit() + } + + // Check that it got loaded two times. + for dbkey, count := range db.gets { + if count != 2 { + t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 2) + } + } +} + +// randTest performs random trie operations. +// Instances of this test are created by Generate. +type randTest []randTestStep + +type randTestStep struct { + op int + key []byte // for opUpdate, opDelete, opGet + value []byte // for opUpdate +} + +const ( + opUpdate = iota + opDelete + opGet + opCommit + opHash + opReset + opItercheckhash + opCheckCacheInvariant + opMax // boundary value, not an actual op +) + +func (randTest) Generate(r *rand.Rand, size int) reflect.Value { + var allKeys [][]byte + genKey := func() []byte { + if len(allKeys) < 2 || r.Intn(100) < 10 { + // new key + key := make([]byte, r.Intn(50)) + randRead(r, key) + allKeys = append(allKeys, key) + return key + } + // use existing key + return allKeys[r.Intn(len(allKeys))] + } + + var steps randTest + for i := 0; i < size; i++ { + step := randTestStep{op: r.Intn(opMax)} + switch step.op { + case opUpdate: + step.key = genKey() + step.value = make([]byte, 8) + binary.BigEndian.PutUint64(step.value, uint64(i)) + case opGet, opDelete: + step.key = genKey() + } + steps = append(steps, step) + } + return reflect.ValueOf(steps) +} + +// rand.Rand provides a Read method in Go 1.7 and later, but +// we can't use it yet. +func randRead(r *rand.Rand, b []byte) { + pos := 0 + val := 0 + for n := 0; n < len(b); n++ { + if pos == 0 { + val = r.Int() + pos = 7 + } + b[n] = byte(val) + val >>= 8 + pos-- + } +} + +func runRandTest(rt randTest) bool { + db, _ := ethdb.NewMemDatabase() + tr, _ := New(common.Hash{}, db) + values := make(map[string]string) // tracks content of the trie + + for _, step := range rt { + switch step.op { + case opUpdate: + tr.Update(step.key, step.value) + values[string(step.key)] = string(step.value) + case opDelete: + tr.Delete(step.key) + delete(values, string(step.key)) + case opGet: + v := tr.Get(step.key) + want := values[string(step.key)] + if string(v) != want { + fmt.Printf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want) + return false + } + case opCommit: + if _, err := tr.Commit(); err != nil { + panic(err) + } + case opHash: + tr.Hash() + case opReset: + hash, err := tr.Commit() + if err != nil { + panic(err) + } + newtr, err := New(hash, db) + if err != nil { + panic(err) + } + tr = newtr + case opItercheckhash: + checktr, _ := New(common.Hash{}, nil) + it := tr.Iterator() + for it.Next() { + checktr.Update(it.Key, it.Value) + } + if tr.Hash() != checktr.Hash() { + fmt.Println("hashes not equal") + return false + } + case opCheckCacheInvariant: + return checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0) + } + } + return true +} + +func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) bool { + var children []node + var flag nodeFlag + switch n := n.(type) { + case *shortNode: + flag = n.flags + children = []node{n.Val} + case *fullNode: + flag = n.flags + children = n.Children[:] + default: + return true + } + + showerror := func() { + fmt.Printf("at depth %d node %s", depth, spew.Sdump(n)) + fmt.Printf("parent: %s", spew.Sdump(parent)) + } + if flag.gen > parentCachegen { + fmt.Printf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen) + showerror() + return false + } + if depth > 0 && !parentDirty && flag.dirty { + fmt.Printf("cache invariant violation: child is dirty but parent isn't\n") + showerror() + return false + } + for _, child := range children { + if !checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1) { + return false + } + } + return true +} + +func TestRandom(t *testing.T) { + if err := quick.Check(runRandTest, nil); err != nil { + t.Fatal(err) + } +} + +func BenchmarkGet(b *testing.B) { benchGet(b, false) } +func BenchmarkGetDB(b *testing.B) { benchGet(b, true) } +func BenchmarkUpdateBE(b *testing.B) { benchUpdate(b, binary.BigEndian) } +func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) } +func BenchmarkHashBE(b *testing.B) { benchHash(b, binary.BigEndian) } +func BenchmarkHashLE(b *testing.B) { benchHash(b, binary.LittleEndian) } + +const benchElemCount = 20000 + +func benchGet(b *testing.B, commit bool) { + trie := new(Trie) + if commit { + _, tmpdb := tempDB() + trie, _ = New(common.Hash{}, tmpdb) + } + k := make([]byte, 32) + for i := 0; i < benchElemCount; i++ { + binary.LittleEndian.PutUint64(k, uint64(i)) + trie.Update(k, k) + } + binary.LittleEndian.PutUint64(k, benchElemCount/2) + if commit { + trie.Commit() + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + trie.Get(k) + } + b.StopTimer() + + if commit { + ldb := trie.db.(*ethdb.LDBDatabase) + ldb.Close() + os.RemoveAll(ldb.Path()) + } +} + +func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie { + trie := newEmpty() + k := make([]byte, 32) + for i := 0; i < b.N; i++ { + e.PutUint64(k, uint64(i)) + trie.Update(k, k) + } + return trie +} + +func benchHash(b *testing.B, e binary.ByteOrder) { + trie := newEmpty() + k := make([]byte, 32) + for i := 0; i < benchElemCount; i++ { + e.PutUint64(k, uint64(i)) + trie.Update(k, k) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + trie.Hash() + } +} + +func tempDB() (string, Database) { + dir, err := ioutil.TempDir("", "trie-bench") + if err != nil { + panic(fmt.Sprintf("can't create temporary directory: %v", err)) + } + db, err := ethdb.NewLDBDatabase(dir, 256, 0) + if err != nil { + panic(fmt.Sprintf("can't create temporary database: %v", err)) + } + return dir, db +} + +func getString(trie *Trie, k string) []byte { + return trie.Get([]byte(k)) +} + +func updateString(trie *Trie, k, v string) { + trie.Update([]byte(k), []byte(v)) +} + +func deleteString(trie *Trie, k string) { + trie.Delete([]byte(k)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/CHANGELOG.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/CHANGELOG.md new file mode 100644 index 0000000..13a0ca4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/CHANGELOG.md @@ -0,0 +1,280 @@ +# CHANGELOG + +----- + +## `v6.0.0-beta` + +| api | version | note | +|:-------------------------------|:-------------------|:-----------------------------------| +| arm/authorization | no change | code refactoring | +| arm/batch | no change | code refactoring | +| arm/compute | no change | code refactoring | +| arm/containerservice | 2016-03-30 | return | +| arm/datalake-analytics/account | 2015-10-01-preview | new | +| arm/datalake-store/filesystem | no change | moved to datalake-store/filesystem | +| arm/eventhub | no change | code refactoring | +| arm/intune | no change | code refactoring | +| arm/iothub | no change | code refactoring | +| arm/keyvault | no change | code refactoring | +| arm/mediaservices | no change | code refactoring | +| arm/network | no change | code refactoring | +| arm/notificationhubs | no change | code refactoring | +| arm/redis | no change | code refactoring | +| arm/resources/resources | no change | code refactoring | +| arm/resources/links | 2016-09-01 | new | +| arm/resources/locks | 2016-09-01 | updated | +| arm/resources/policy | no change | code refactoring | +| arm/resources/resources | 2016-09-01 | updated | +| arm/servermanagement | 2016-07-01-preview | updated | +| arm/web | no change | code refactoring | + +- storage: Added blob lease functionality and tests + +## `v5.0.0-beta` + +| api | version | note | +|:------------------------------|:--------------------|:-----------------| +| arm/network | 2016-09-01 | updated | +| arm/servermanagement | 2015-07-01-preview | new | +| arm/eventhub | 2015-08-01 | new | +| arm/containerservice | -- | removed | +| arm/resources/subscriptions | no change | code refactoring | +| arm/resources/features | no change | code refactoring | +| arm/resources/resources | no change | code refactoring | +| arm/datalake-store/accounts | no change | code refactoring | +| arm/datalake-store/filesystem | no change | code refactoring | +| arm/notificationhubs | no change | code refactoring | +| arm/redis | no change | code refactoring | + +- storage: Add more file storage share operations. +- azure-rest-api-specs/commit/b8cdc2c50a0872fc0039f20c2b6b33aa0c2af4bf +- Uses go-autorest v7.2.1 + +## `v4.0.0-beta` + +- arm/logic: breaking change in package logic. +- arm: parameter validation code added in all arm packages. +- Uses go-autorest v7.2.0. + + +## `v3.2.0-beta` + +| api | version | note | +|:----------------------------|:--------------------|:----------| +| arm/mediaservices | 2015-10-01 | new | +| arm/keyvault | 2015-06-01 | new | +| arm/iothub | 2016-02-03 | new | +| arm/datalake-store | 2015-12-01 | new | +| arm/network | 2016-06-01 | updated | +| arm/resources/resources | 2016-07-01 | updated | +| arm/resources/policy | 2016-04-01 | updated | +| arm/servicebus | 2015-08-01 | updated | + +- arm: uses go-autorest version v7.1.0. +- storage: fix for operating on blobs names containing special characters. +- storage: add SetBlobProperties(), update BlobProperties response fields. +- storage: make storage client work correctly with read-only secondary account. +- storage: add Azure Storage Emulator support. + + +## `v3.1.0-beta` + +- Added a new arm/compute/containerservice (2016-03-30) package +- Reintroduced NewxxClientWithBaseURI method. +- Uses go-autorest version - v7.0.7. + + +## `v3.0.0-beta` + +This release brings the Go SDK ARM packages up-to-date with Azure ARM Swagger files for most +services. Since the underlying [Swagger files](https://github.com/Azure/azure-rest-api-specs) +continue to change substantially, the ARM packages are still in *beta* status. + +The ARM packages now align with the following API versions (*highlighted* packages are new or +updated in this release): + +| api | version | note | +|:----------------------------|:--------------------|:----------| +| arm/authorization | 2015-07-01 | no change | +| arm/intune | 2015-01-14-preview | no change | +| arm/notificationhubs | 2014-09-01 | no change | +| arm/resources/features | 2015-12-01 | no change | +| arm/resources/subscriptions | 2015-11-01 | no change | +| arm/web | 2015-08-01 | no change | +| arm/cdn | 2016-04-02 | updated | +| arm/compute | 2016-03-30 | updated | +| arm/dns | 2016-04-01 | updated | +| arm/logic | 2015-08-01-preview | updated | +| arm/network | 2016-03-30 | updated | +| arm/redis | 2016-04-01 | updated | +| arm/resources/resources | 2016-02-01 | updated | +| arm/resources/policy | 2015-10-01-preview | updated | +| arm/resources/locks | 2015-01-01 | updated (resources/authorization earlier)| +| arm/scheduler | 2016-03-01 | updated | +| arm/storage | 2016-01-01 | updated | +| arm/search | 2015-02-28 | updated | +| arm/batch | 2015-12-01 | new | +| arm/cognitiveservices | 2016-02-01-preview | new | +| arm/devtestlabs | 2016-05-15 | new | +| arm/machinelearning | 2016-05-01-preview | new | +| arm/powerbiembedded | 2016-01-29 | new | +| arm/mobileengagement | 2014-12-01 | new | +| arm/servicebus | 2014-09-01 | new | +| arm/sql | 2015-05-01 | new | +| arm/trafficmanager | 2015-11-01 | new | + + +Below are some design changes. +- Removed Api version from method arguments. +- Removed New...ClientWithBaseURI() method in all clients. BaseURI value is set in client.go. +- Uses go-autorest version v7.0.6. + + +## `v2.2.0-beta` + +- Uses go-autorest version v7.0.5. +- Update version of pacakges "jwt-go" and "crypto" in glide.lock. + + +## `v2.1.1-beta` + +- arm: Better error messages for long running operation failures (Uses go-autorest version v7.0.4). + + +## `v2.1.0-beta` + +- arm: Uses go-autorest v7.0.3 (polling related updates). +- arm: Cancel channel argument added in long-running calls. +- storage: Allow caller to provide headers for DeleteBlob methods. +- storage: Enables connection sharing with http keepalive. +- storage: Add BlobPrefixes and Delimiter to BlobListResponse + + +## `v2.0.0-beta` + +- Uses go-autorest v6.0.0 (Polling and Asynchronous requests related changes). + + +## `v0.5.0-beta` + +Updated following packages to new API versions: +- arm/resources/features 2015-12-01 +- arm/resources/resources 2015-11-01 +- arm/resources/subscriptions 2015-11-01 + + +### Changes + + - SDK now uses go-autorest v3.0.0. + + + +## `v0.4.0-beta` + +This release brings the Go SDK ARM packages up-to-date with Azure ARM Swagger files for most +services. Since the underlying [Swagger files](https://github.com/Azure/azure-rest-api-specs) +continue to change substantially, the ARM packages are still in *beta* status. + +The ARM packages now align with the following API versions (*highlighted* packages are new or +updated in this release): + +- *arm/authorization 2015-07-01* +- *arm/cdn 2015-06-01* +- arm/compute 2015-06-15 +- arm/dns 2015-05-04-preview +- *arm/intune 2015-01-14-preview* +- arm/logic 2015-02-01-preview +- *arm/network 2015-06-15* +- *arm/notificationhubs 2014-09-01* +- arm/redis 2015-08-01 +- *arm/resources/authorization 2015-01-01* +- *arm/resources/features 2014-08-01-preview* +- *arm/resources/resources 2014-04-01-preview* +- *arm/resources/subscriptions 2014-04-01-preview* +- *arm/scheduler 2016-01-01* +- arm/storage 2015-06-15 +- arm/web 2015-08-01 + +### Changes + +- Moved the arm/authorization, arm/features, arm/resources, and arm/subscriptions packages under a new, resources, package (to reflect the corresponding Swagger structure) +- Added a new arm/authoriation (2015-07-01) package +- Added a new arm/cdn (2015-06-01) package +- Added a new arm/intune (2015-01-14-preview) package +- Udated arm/network (2015-06-01) +- Added a new arm/notificationhubs (2014-09-01) package +- Updated arm/scheduler (2016-01-01) package + + +----- + +## `v0.3.0-beta` + +- Corrected unintentional struct field renaming and client renaming in v0.2.0-beta + +----- + +## `v0.2.0-beta` + +- Added support for DNS, Redis, and Web site services +- Updated Storage service to API version 2015-06-15 +- Updated Network to include routing table support +- Address https://github.com/Azure/azure-sdk-for-go/issues/232 +- Address https://github.com/Azure/azure-sdk-for-go/issues/231 +- Address https://github.com/Azure/azure-sdk-for-go/issues/230 +- Address https://github.com/Azure/azure-sdk-for-go/issues/224 +- Address https://github.com/Azure/azure-sdk-for-go/issues/184 +- Address https://github.com/Azure/azure-sdk-for-go/issues/183 + +------ + +## `v0.1.1-beta` + +- Improves the UserAgent string to disambiguate arm packages from others in the SDK +- Improves setting the http.Response into generated results (reduces likelihood of a nil reference) +- Adds gofmt, golint, and govet to Travis CI for the arm packages + +##### Fixed Issues + +- https://github.com/Azure/azure-sdk-for-go/issues/196 +- https://github.com/Azure/azure-sdk-for-go/issues/213 + +------ + +## v0.1.0-beta + +This release addresses the issues raised against the alpha release and adds more features. Most +notably, to address the challenges of encoding JSON +(see the [comments](https://github.com/Azure/go-autorest#handling-empty-values) in the +[go-autorest](https://github.com/Azure/go-autorest) package) by using pointers for *all* structure +fields (with the exception of enumerations). The +[go-autorest/autorest/to](https://github.com/Azure/go-autorest/tree/master/autorest/to) package +provides helpers to convert to / from pointers. The examples demonstrate their usage. + +Additionally, the packages now align with Go coding standards and pass both `golint` and `govet`. +Accomplishing this required renaming various fields and parameters (such as changing Url to URL). + +##### Changes + +- Changed request / response structures to use pointer fields. +- Changed methods to return `error` instead of `autorest.Error`. +- Re-divided methods to ease asynchronous requests. +- Added paged results support. +- Added a UserAgent string. +- Added changes necessary to pass golint and govet. +- Updated README.md with details on asynchronous requests and paging. +- Saved package dependencies through Godep (for the entire SDK). + +##### Fixed Issues: + +- https://github.com/Azure/azure-sdk-for-go/issues/205 +- https://github.com/Azure/azure-sdk-for-go/issues/206 +- https://github.com/Azure/azure-sdk-for-go/issues/211 +- https://github.com/Azure/azure-sdk-for-go/issues/212 + +----- + +## v0.1.0-alpha + +This release introduces the Azure Resource Manager packages generated from the corresponding +[Swagger API](http://swagger.io) [definitions](https://github.com/Azure/azure-rest-api-specs). \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/LICENSE new file mode 100644 index 0000000..af39a91 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Microsoft Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/README.md new file mode 100644 index 0000000..6a1aaf4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/README.md @@ -0,0 +1,102 @@ +# Microsoft Azure SDK for Go + +This project provides various Go packages to perform operations +on Microsoft Azure REST APIs. + +[![GoDoc](https://godoc.org/github.com/Azure/azure-sdk-for-go?status.svg)](https://godoc.org/github.com/Azure/azure-sdk-for-go) [![Build Status](https://travis-ci.org/Azure/azure-sdk-for-go.svg?branch=master)](https://travis-ci.org/Azure/azure-sdk-for-go) + +> **NOTE:** This repository is under heavy ongoing development and +is likely to break over time. We currently do not have any releases +yet. If you are planning to use the repository, please consider vendoring +the packages in your project and update them when a stable tag is out. + +# Packages + +## Azure Resource Manager (ARM) + +[About ARM](/arm/README.md) + +- [authorization](/arm/authorization) +- [batch](/arm/batch) +- [cdn](/arm/cdn) +- [cognitiveservices](/arm/cognitiveservices) +- [compute](/arm/compute) +- [containerservice](/arm/containerservice) +- [datalake-store](/arm/datalake-store) +- [devtestlabs](/arm/devtestlabs) +- [dns](/arm/dns) +- [intune](/arm/intune) +- [iothub](/arm/iothub) +- [keyvault](/arm/keyvault) +- [logic](/arm/logic) +- [machinelearning](/arm/machinelearning) +- [mediaservices](/arm/mediaservices) +- [mobileengagement](/arm/mobileengagement) +- [network](/arm/network) +- [notificationhubs](/arm/notificationhubs) +- [powerbiembedded](/arm/powerbiembedded) +- [redis](/arm/redis) +- [resources](/arm/resources) +- [scheduler](/arm/scheduler) +- [search](/arm/search) +- [servicebus](/arm/servicebus) +- [sql](/arm/sql) +- [storage](/arm/storage) +- [trafficmanager](/arm/trafficmanager) +- [web](/arm/web) + +## Azure Service Management (ASM), aka classic deployment + +[About ASM](/management/README.md) + +- [affinitygroup](/management/affinitygroup) +- [hostedservice](/management/hostedservice) +- [location](/management/location) +- [networksecuritygroup](/management/networksecuritygroup) +- [osimage](/management/osimage) +- [sql](/management/sql) +- [storageservice](/management/storageservice) +- [virtualmachine](/management/virtualmachine) +- [virtualmachinedisk](/management/virtualmachinedisk) +- [virtualmachineimage](/management/virtualmachineimage) +- [virtualnetwork](/management/virtualnetwork) +- [vmutils](/management/vmutils) + +## Azure Storage SDK for Go + +[About Storage](/storage/README.md) + +- [storage](/storage) + +# Installation + +- [Install Go 1.7](https://golang.org/dl/). + +- Go get the SDK: + +``` +$ go get -d github.com/Azure/azure-sdk-for-go +``` + +> **IMPORTANT:** We highly suggest vendoring Azure SDK for Go as a dependency. For vendoring dependencies, Azure SDK for Go uses [glide](https://github.com/Masterminds/glide). If you haven't already, install glide. Navigate to your project directory and install the dependencies. + +``` +$ cd your/project +$ glide create +$ glide install +``` + +# Documentation + +Read the Godoc of the repository at [Godoc.org](http://godoc.org/github.com/Azure/azure-sdk-for-go/). + +# Contribute + +If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines/). + +# License + +This project is published under [Apache 2.0 License](LICENSE). + +----- +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.lock b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.lock new file mode 100644 index 0000000..31f3207 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.lock @@ -0,0 +1,53 @@ +hash: 7407050cee9bb9ce89e23ef26bce4051cce63d558338a4937f027a18b789e3a1 +updated: 2016-10-25T11:34:48.4987356-07:00 +imports: +- name: github.com/Azure/go-autorest + version: 0781901f19f1e7db3034d97ec57af753db0bf808 + subpackages: + - autorest + - autorest/azure + - autorest/date + - autorest/to + - autorest/validation +- name: github.com/dgrijalva/jwt-go + version: 24c63f56522a87ec5339cc3567883f1039378fdb +- name: github.com/howeyc/gopass + version: f5387c492211eb133053880d23dfae62aa14123d +- name: github.com/mattn/go-colorable + version: 6c903ff4aa50920ca86087a280590b36b3152b9c +- name: github.com/mattn/go-isatty + version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 +- name: github.com/mgutz/ansi + version: c286dcecd19ff979eeb73ea444e479b903f2cfcb +- name: github.com/mgutz/minimist + version: 39eb8cf573ca29344bd7d7e6ba4d7febdebd37a9 +- name: github.com/mgutz/str + version: 968bf66e3da857419e4f6e71b2d5c9ae95682dc4 +- name: github.com/mgutz/to + version: 2a0bcba0661696e339461f5efb2273f4459dd1b9 +- name: github.com/MichaelTJones/walk + version: 3af09438b0ab0e8f296410bfa646a7e635ea1fc0 +- name: github.com/nozzle/throttler + version: d9b45f19996c645d38c9266d1f5cf1990e930119 +- name: github.com/satori/uuid + version: b061729afc07e77a8aa4fad0a2fd840958f1942a +- name: golang.org/x/crypto + version: 84e98f45760e87786b7f24603b8166a6fa09811d + subpackages: + - pkcs12 + - pkcs12/internal/rc2 + - ssh/terminal +- name: golang.org/x/sys + version: c200b10b5d5e122be351b67af224adc6128af5bf + subpackages: + - unix +- name: gopkg.in/check.v1 + version: 4f90aeace3a26ad7021961c297b22c42160c7b25 +- name: gopkg.in/godo.v2 + version: b5fd2f0bef1ebe832e628cfad18ab1cc707f65a1 + subpackages: + - glob + - util + - watcher + - watcher/fswatch +testImports: [] diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.yaml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.yaml new file mode 100644 index 0000000..90f54fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/glide.yaml @@ -0,0 +1,12 @@ +package: github.com/Azure/azure-sdk-for-go +import: +- package: github.com/Azure/go-autorest + subpackages: + - /autorest + - autorest/azure + - autorest/date + - autorest/to +- package: golang.org/x/crypto + subpackages: + - /pkcs12 +- package: gopkg.in/check.v1 \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md new file mode 100644 index 0000000..0ab0998 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md @@ -0,0 +1,5 @@ +# Azure Storage SDK for Go + +The `github.com/Azure/azure-sdk-for-go/storage` package is used to perform operations in Azure Storage Service. To manage your storage accounts (Azure Resource Manager / ARM), use the [github.com/Azure/azure-sdk-for-go/arm/storage](../arm/storage) package. For your classic storage accounts (Azure Service Management / ASM), use [github.com/Azure/azure-sdk-for-go/management/storageservice](../management/storageservice) package. + +This package includes support for [Azure Storage Emulator](https://azure.microsoft.com/documentation/articles/storage-use-emulator/) \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go new file mode 100644 index 0000000..3176203 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go @@ -0,0 +1,1278 @@ +package storage + +import ( + "bytes" + "encoding/xml" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +// BlobStorageClient contains operations for Microsoft Azure Blob Storage +// Service. +type BlobStorageClient struct { + client Client +} + +// A Container is an entry in ContainerListResponse. +type Container struct { + Name string `xml:"Name"` + Properties ContainerProperties `xml:"Properties"` + // TODO (ahmetalpbalkan) Metadata +} + +// ContainerProperties contains various properties of a container returned from +// various endpoints like ListContainers. +type ContainerProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` + LeaseStatus string `xml:"LeaseStatus"` + LeaseState string `xml:"LeaseState"` + LeaseDuration string `xml:"LeaseDuration"` + // TODO (ahmetalpbalkan) remaining fields +} + +// ContainerListResponse contains the response fields from +// ListContainers call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +type ContainerListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Containers []Container `xml:"Containers>Container"` +} + +// A Blob is an entry in BlobListResponse. +type Blob struct { + Name string `xml:"Name"` + Properties BlobProperties `xml:"Properties"` + Metadata BlobMetadata `xml:"Metadata"` +} + +// BlobMetadata is a set of custom name/value pairs. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179404.aspx +type BlobMetadata map[string]string + +type blobMetadataEntries struct { + Entries []blobMetadataEntry `xml:",any"` +} +type blobMetadataEntry struct { + XMLName xml.Name + Value string `xml:",chardata"` +} + +// UnmarshalXML converts the xml:Metadata into Metadata map +func (bm *BlobMetadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var entries blobMetadataEntries + if err := d.DecodeElement(&entries, &start); err != nil { + return err + } + for _, entry := range entries.Entries { + if *bm == nil { + *bm = make(BlobMetadata) + } + (*bm)[strings.ToLower(entry.XMLName.Local)] = entry.Value + } + return nil +} + +// MarshalXML implements the xml.Marshaler interface. It encodes +// metadata name/value pairs as they would appear in an Azure +// ListBlobs response. +func (bm BlobMetadata) MarshalXML(enc *xml.Encoder, start xml.StartElement) error { + entries := make([]blobMetadataEntry, 0, len(bm)) + for k, v := range bm { + entries = append(entries, blobMetadataEntry{ + XMLName: xml.Name{Local: http.CanonicalHeaderKey(k)}, + Value: v, + }) + } + return enc.EncodeElement(blobMetadataEntries{ + Entries: entries, + }, start) +} + +// BlobProperties contains various properties of a blob +// returned in various endpoints like ListBlobs or GetBlobProperties. +type BlobProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` + ContentMD5 string `xml:"Content-MD5"` + ContentLength int64 `xml:"Content-Length"` + ContentType string `xml:"Content-Type"` + ContentEncoding string `xml:"Content-Encoding"` + CacheControl string `xml:"Cache-Control"` + ContentLanguage string `xml:"Cache-Language"` + BlobType BlobType `xml:"x-ms-blob-blob-type"` + SequenceNumber int64 `xml:"x-ms-blob-sequence-number"` + CopyID string `xml:"CopyId"` + CopyStatus string `xml:"CopyStatus"` + CopySource string `xml:"CopySource"` + CopyProgress string `xml:"CopyProgress"` + CopyCompletionTime string `xml:"CopyCompletionTime"` + CopyStatusDescription string `xml:"CopyStatusDescription"` + LeaseStatus string `xml:"LeaseStatus"` +} + +// BlobHeaders contains various properties of a blob and is an entry +// in SetBlobProperties +type BlobHeaders struct { + ContentMD5 string `header:"x-ms-blob-content-md5"` + ContentLanguage string `header:"x-ms-blob-content-language"` + ContentEncoding string `header:"x-ms-blob-content-encoding"` + ContentType string `header:"x-ms-blob-content-type"` + CacheControl string `header:"x-ms-blob-cache-control"` +} + +// BlobListResponse contains the response fields from ListBlobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +type BlobListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Blobs []Blob `xml:"Blobs>Blob"` + + // BlobPrefix is used to traverse blobs as if it were a file system. + // It is returned if ListBlobsParameters.Delimiter is specified. + // The list here can be thought of as "folders" that may contain + // other folders or blobs. + BlobPrefixes []string `xml:"Blobs>BlobPrefix>Name"` + + // Delimiter is used to traverse blobs as if it were a file system. + // It is returned if ListBlobsParameters.Delimiter is specified. + Delimiter string `xml:"Delimiter"` +} + +// ListContainersParameters defines the set of customizable parameters to make a +// List Containers call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +type ListContainersParameters struct { + Prefix string + Marker string + Include string + MaxResults uint + Timeout uint +} + +func (p ListContainersParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// ListBlobsParameters defines the set of customizable +// parameters to make a List Blobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +type ListBlobsParameters struct { + Prefix string + Delimiter string + Marker string + Include string + MaxResults uint + Timeout uint +} + +func (p ListBlobsParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Delimiter != "" { + out.Set("delimiter", p.Delimiter) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// BlobType defines the type of the Azure Blob. +type BlobType string + +// Types of page blobs +const ( + BlobTypeBlock BlobType = "BlockBlob" + BlobTypePage BlobType = "PageBlob" + BlobTypeAppend BlobType = "AppendBlob" +) + +// PageWriteType defines the type updates that are going to be +// done on the page blob. +type PageWriteType string + +// Types of operations on page blobs +const ( + PageWriteTypeUpdate PageWriteType = "update" + PageWriteTypeClear PageWriteType = "clear" +) + +const ( + blobCopyStatusPending = "pending" + blobCopyStatusSuccess = "success" + blobCopyStatusAborted = "aborted" + blobCopyStatusFailed = "failed" +) + +// lease constants. +const ( + leaseHeaderPrefix = "x-ms-lease-" + leaseID = "x-ms-lease-id" + leaseAction = "x-ms-lease-action" + leaseBreakPeriod = "x-ms-lease-break-period" + leaseDuration = "x-ms-lease-duration" + leaseProposedID = "x-ms-proposed-lease-id" + leaseTime = "x-ms-lease-time" + + acquireLease = "acquire" + renewLease = "renew" + changeLease = "change" + releaseLease = "release" + breakLease = "break" +) + +// BlockListType is used to filter out types of blocks in a Get Blocks List call +// for a block blob. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all +// block types. +type BlockListType string + +// Filters for listing blocks in block blobs +const ( + BlockListTypeAll BlockListType = "all" + BlockListTypeCommitted BlockListType = "committed" + BlockListTypeUncommitted BlockListType = "uncommitted" +) + +// ContainerAccessType defines the access level to the container from a public +// request. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx and "x-ms- +// blob-public-access" header. +type ContainerAccessType string + +// Access options for containers +const ( + ContainerAccessTypePrivate ContainerAccessType = "" + ContainerAccessTypeBlob ContainerAccessType = "blob" + ContainerAccessTypeContainer ContainerAccessType = "container" +) + +// Maximum sizes (per REST API) for various concepts +const ( + MaxBlobBlockSize = 4 * 1024 * 1024 + MaxBlobPageSize = 4 * 1024 * 1024 +) + +// BlockStatus defines states a block for a block blob can +// be in. +type BlockStatus string + +// List of statuses that can be used to refer to a block in a block list +const ( + BlockStatusUncommitted BlockStatus = "Uncommitted" + BlockStatusCommitted BlockStatus = "Committed" + BlockStatusLatest BlockStatus = "Latest" +) + +// Block is used to create Block entities for Put Block List +// call. +type Block struct { + ID string + Status BlockStatus +} + +// BlockListResponse contains the response fields from Get Block List call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx +type BlockListResponse struct { + XMLName xml.Name `xml:"BlockList"` + CommittedBlocks []BlockResponse `xml:"CommittedBlocks>Block"` + UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"` +} + +// BlockResponse contains the block information returned +// in the GetBlockListCall. +type BlockResponse struct { + Name string `xml:"Name"` + Size int64 `xml:"Size"` +} + +// GetPageRangesResponse contains the reponse fields from +// Get Page Ranges call. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx +type GetPageRangesResponse struct { + XMLName xml.Name `xml:"PageList"` + PageList []PageRange `xml:"PageRange"` +} + +// PageRange contains information about a page of a page blob from +// Get Pages Range call. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx +type PageRange struct { + Start int64 `xml:"Start"` + End int64 `xml:"End"` +} + +var ( + errBlobCopyAborted = errors.New("storage: blob copy is aborted") + errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch") +) + +// ListContainers returns the list of containers in a storage account along with +// pagination token and other response details. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +func (b BlobStorageClient) ListContainers(params ListContainersParameters) (ContainerListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) + uri := b.client.getEndpoint(blobServiceName, "", q) + headers := b.client.getStandardHeaders() + + var out ContainerListResponse + resp, err := b.client.exec("GET", uri, headers, nil) + if err != nil { + return out, err + } + defer resp.body.Close() + + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +// CreateContainer creates a blob container within the storage account +// with given name and access level. Returns error if container already exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx +func (b BlobStorageClient) CreateContainer(name string, access ContainerAccessType) error { + resp, err := b.createContainer(name, access) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// CreateContainerIfNotExists creates a blob container if it does not exist. Returns +// true if container is newly created or false if container already exists. +func (b BlobStorageClient) CreateContainerIfNotExists(name string, access ContainerAccessType) (bool, error) { + resp, err := b.createContainer(name, access) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { + return resp.statusCode == http.StatusCreated, nil + } + } + return false, err +} + +func (b BlobStorageClient) createContainer(name string, access ContainerAccessType) (*storageResponse, error) { + verb := "PUT" + uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) + + headers := b.client.getStandardHeaders() + if access != "" { + headers["x-ms-blob-public-access"] = string(access) + } + return b.client.exec(verb, uri, headers, nil) +} + +// ContainerExists returns true if a container with given name exists +// on the storage account, otherwise returns false. +func (b BlobStorageClient) ContainerExists(name string) (bool, error) { + verb := "HEAD" + uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) + headers := b.client.getStandardHeaders() + + resp, err := b.client.exec(verb, uri, headers, nil) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusOK, nil + } + } + return false, err +} + +// DeleteContainer deletes the container with given name on the storage +// account. If the container does not exist returns error. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx +func (b BlobStorageClient) DeleteContainer(name string) error { + resp, err := b.deleteContainer(name) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} + +// DeleteContainerIfExists deletes the container with given name on the storage +// account if it exists. Returns true if container is deleted with this call, or +// false if the container did not exist at the time of the Delete Container +// operation. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx +func (b BlobStorageClient) DeleteContainerIfExists(name string) (bool, error) { + resp, err := b.deleteContainer(name) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +func (b BlobStorageClient) deleteContainer(name string) (*storageResponse, error) { + verb := "DELETE" + uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) + + headers := b.client.getStandardHeaders() + return b.client.exec(verb, uri, headers, nil) +} + +// ListBlobs returns an object that contains list of blobs in the container, +// pagination token and other information in the response of List Blobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +func (b BlobStorageClient) ListBlobs(container string, params ListBlobsParameters) (BlobListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{ + "restype": {"container"}, + "comp": {"list"}}) + uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), q) + headers := b.client.getStandardHeaders() + + var out BlobListResponse + resp, err := b.client.exec("GET", uri, headers, nil) + if err != nil { + return out, err + } + defer resp.body.Close() + + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +// BlobExists returns true if a blob with given name exists on the specified +// container of the storage account. +func (b BlobStorageClient) BlobExists(container, name string) (bool, error) { + verb := "HEAD" + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + headers := b.client.getStandardHeaders() + resp, err := b.client.exec(verb, uri, headers, nil) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusOK, nil + } + } + return false, err +} + +// GetBlobURL gets the canonical URL to the blob with the specified name in the +// specified container. This method does not create a publicly accessible URL if +// the blob or container is private and this method does not check if the blob +// exists. +func (b BlobStorageClient) GetBlobURL(container, name string) string { + if container == "" { + container = "$root" + } + return b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) +} + +// GetBlob returns a stream to read the blob. Caller must call Close() the +// reader to close on the underlying connection. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179440.aspx +func (b BlobStorageClient) GetBlob(container, name string) (io.ReadCloser, error) { + resp, err := b.getBlobRange(container, name, "", nil) + if err != nil { + return nil, err + } + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + return resp.body, nil +} + +// GetBlobRange reads the specified range of a blob to a stream. The bytesRange +// string must be in a format like "0-", "10-100" as defined in HTTP 1.1 spec. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179440.aspx +func (b BlobStorageClient) GetBlobRange(container, name, bytesRange string, extraHeaders map[string]string) (io.ReadCloser, error) { + resp, err := b.getBlobRange(container, name, bytesRange, extraHeaders) + if err != nil { + return nil, err + } + + if err := checkRespCode(resp.statusCode, []int{http.StatusPartialContent}); err != nil { + return nil, err + } + return resp.body, nil +} + +func (b BlobStorageClient) getBlobRange(container, name, bytesRange string, extraHeaders map[string]string) (*storageResponse, error) { + verb := "GET" + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + + headers := b.client.getStandardHeaders() + if bytesRange != "" { + headers["Range"] = fmt.Sprintf("bytes=%s", bytesRange) + } + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec(verb, uri, headers, nil) + if err != nil { + return nil, err + } + return resp, err +} + +// leasePut is common PUT code for the various aquire/release/break etc functions. +func (b BlobStorageClient) leaseCommonPut(container string, name string, headers map[string]string, expectedStatus int) (http.Header, error) { + params := url.Values{"comp": {"lease"}} + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{expectedStatus}); err != nil { + return nil, err + } + + return resp.headers, nil +} + +// AcquireLease creates a lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +// returns leaseID acquired +func (b BlobStorageClient) AcquireLease(container string, name string, leaseTimeInSeconds int, proposedLeaseID string) (returnedLeaseID string, err error) { + headers := b.client.getStandardHeaders() + headers[leaseAction] = acquireLease + headers[leaseProposedID] = proposedLeaseID + headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds) + + respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusCreated) + if err != nil { + return "", err + } + + returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(leaseID)) + + if returnedLeaseID != "" { + return returnedLeaseID, nil + } + + // what should we return in case of HTTP 201 but no lease ID? + // or it just cant happen? (brave words) + return "", errors.New("LeaseID not returned") +} + +// BreakLease breaks the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +// Returns the timeout remaining in the lease in seconds +func (b BlobStorageClient) BreakLease(container string, name string) (breakTimeout int, err error) { + headers := b.client.getStandardHeaders() + headers[leaseAction] = breakLease + return b.breakLeaseCommon(container, name, headers) +} + +// BreakLeaseWithBreakPeriod breaks the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +// breakPeriodInSeconds is used to determine how long until new lease can be created. +// Returns the timeout remaining in the lease in seconds +func (b BlobStorageClient) BreakLeaseWithBreakPeriod(container string, name string, breakPeriodInSeconds int) (breakTimeout int, err error) { + headers := b.client.getStandardHeaders() + headers[leaseAction] = breakLease + headers[leaseBreakPeriod] = strconv.Itoa(breakPeriodInSeconds) + return b.breakLeaseCommon(container, name, headers) +} + +// breakLeaseCommon is common code for both version of BreakLease (with and without break period) +func (b BlobStorageClient) breakLeaseCommon(container string, name string, headers map[string]string) (breakTimeout int, err error) { + + respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusAccepted) + if err != nil { + return 0, err + } + + breakTimeoutStr := respHeaders.Get(http.CanonicalHeaderKey(leaseTime)) + if breakTimeoutStr != "" { + breakTimeout, err = strconv.Atoi(breakTimeoutStr) + if err != nil { + return 0, err + } + } + + return breakTimeout, nil +} + +// ChangeLease changes a lease ID for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +// Returns the new LeaseID acquired +func (b BlobStorageClient) ChangeLease(container string, name string, currentLeaseID string, proposedLeaseID string) (newLeaseID string, err error) { + headers := b.client.getStandardHeaders() + headers[leaseAction] = changeLease + headers[leaseID] = currentLeaseID + headers[leaseProposedID] = proposedLeaseID + + respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusOK) + if err != nil { + return "", err + } + + newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(leaseID)) + if newLeaseID != "" { + return newLeaseID, nil + } + + return "", errors.New("LeaseID not returned") +} + +// ReleaseLease releases the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +func (b BlobStorageClient) ReleaseLease(container string, name string, currentLeaseID string) error { + headers := b.client.getStandardHeaders() + headers[leaseAction] = releaseLease + headers[leaseID] = currentLeaseID + + _, err := b.leaseCommonPut(container, name, headers, http.StatusOK) + if err != nil { + return err + } + + return nil +} + +// RenewLease renews the lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx +func (b BlobStorageClient) RenewLease(container string, name string, currentLeaseID string) error { + headers := b.client.getStandardHeaders() + headers[leaseAction] = renewLease + headers[leaseID] = currentLeaseID + + _, err := b.leaseCommonPut(container, name, headers, http.StatusOK) + if err != nil { + return err + } + + return nil +} + +// GetBlobProperties provides various information about the specified +// blob. See https://msdn.microsoft.com/en-us/library/azure/dd179394.aspx +func (b BlobStorageClient) GetBlobProperties(container, name string) (*BlobProperties, error) { + verb := "HEAD" + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + + headers := b.client.getStandardHeaders() + resp, err := b.client.exec(verb, uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + var contentLength int64 + contentLengthStr := resp.headers.Get("Content-Length") + if contentLengthStr != "" { + contentLength, err = strconv.ParseInt(contentLengthStr, 0, 64) + if err != nil { + return nil, err + } + } + + var sequenceNum int64 + sequenceNumStr := resp.headers.Get("x-ms-blob-sequence-number") + if sequenceNumStr != "" { + sequenceNum, err = strconv.ParseInt(sequenceNumStr, 0, 64) + if err != nil { + return nil, err + } + } + + return &BlobProperties{ + LastModified: resp.headers.Get("Last-Modified"), + Etag: resp.headers.Get("Etag"), + ContentMD5: resp.headers.Get("Content-MD5"), + ContentLength: contentLength, + ContentEncoding: resp.headers.Get("Content-Encoding"), + ContentType: resp.headers.Get("Content-Type"), + CacheControl: resp.headers.Get("Cache-Control"), + ContentLanguage: resp.headers.Get("Content-Language"), + SequenceNumber: sequenceNum, + CopyCompletionTime: resp.headers.Get("x-ms-copy-completion-time"), + CopyStatusDescription: resp.headers.Get("x-ms-copy-status-description"), + CopyID: resp.headers.Get("x-ms-copy-id"), + CopyProgress: resp.headers.Get("x-ms-copy-progress"), + CopySource: resp.headers.Get("x-ms-copy-source"), + CopyStatus: resp.headers.Get("x-ms-copy-status"), + BlobType: BlobType(resp.headers.Get("x-ms-blob-type")), + LeaseStatus: resp.headers.Get("x-ms-lease-status"), + }, nil +} + +// SetBlobProperties replaces the BlobHeaders for the specified blob. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetBlobProperties. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee691966.aspx +func (b BlobStorageClient) SetBlobProperties(container, name string, blobHeaders BlobHeaders) error { + params := url.Values{"comp": {"properties"}} + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + headers := b.client.getStandardHeaders() + + extraHeaders := headersFromStruct(blobHeaders) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// SetBlobMetadata replaces the metadata for the specified blob. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetBlobMetadata. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx +func (b BlobStorageClient) SetBlobMetadata(container, name string, metadata map[string]string, extraHeaders map[string]string) error { + params := url.Values{"comp": {"metadata"}} + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + headers := b.client.getStandardHeaders() + for k, v := range metadata { + headers[userDefinedMetadataHeaderPrefix+k] = v + } + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// GetBlobMetadata returns all user-defined metadata for the specified blob. +// +// All metadata keys will be returned in lower case. (HTTP header +// names are case-insensitive.) +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx +func (b BlobStorageClient) GetBlobMetadata(container, name string) (map[string]string, error) { + params := url.Values{"comp": {"metadata"}} + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + headers := b.client.getStandardHeaders() + + resp, err := b.client.exec("GET", uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + metadata := make(map[string]string) + for k, v := range resp.headers { + // Can't trust CanonicalHeaderKey() to munge case + // reliably. "_" is allowed in identifiers: + // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx + // https://msdn.microsoft.com/library/aa664670(VS.71).aspx + // http://tools.ietf.org/html/rfc7230#section-3.2 + // ...but "_" is considered invalid by + // CanonicalMIMEHeaderKey in + // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542 + // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar". + k = strings.ToLower(k) + if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) { + continue + } + // metadata["foo"] = content of the last X-Ms-Meta-Foo header + k = k[len(userDefinedMetadataHeaderPrefix):] + metadata[k] = v[len(v)-1] + } + return metadata, nil +} + +// CreateBlockBlob initializes an empty block blob with no blocks. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx +func (b BlobStorageClient) CreateBlockBlob(container, name string) error { + return b.CreateBlockBlobFromReader(container, name, 0, nil, nil) +} + +// CreateBlockBlobFromReader initializes a block blob using data from +// reader. Size must be the number of bytes read from reader. To +// create an empty blob, use size==0 and reader==nil. +// +// The API rejects requests with size > 64 MiB (but this limit is not +// checked by the SDK). To write a larger blob, use CreateBlockBlob, +// PutBlock, and PutBlockList. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx +func (b BlobStorageClient) CreateBlockBlobFromReader(container, name string, size uint64, blob io.Reader, extraHeaders map[string]string) error { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypeBlock) + headers["Content-Length"] = fmt.Sprintf("%d", size) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, blob) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// PutBlock saves the given data chunk to the specified block blob with +// given ID. +// +// The API rejects chunks larger than 4 MiB (but this limit is not +// checked by the SDK). +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx +func (b BlobStorageClient) PutBlock(container, name, blockID string, chunk []byte) error { + return b.PutBlockWithLength(container, name, blockID, uint64(len(chunk)), bytes.NewReader(chunk), nil) +} + +// PutBlockWithLength saves the given data stream of exactly specified size to +// the block blob with given ID. It is an alternative to PutBlocks where data +// comes as stream but the length is known in advance. +// +// The API rejects requests with size > 4 MiB (but this limit is not +// checked by the SDK). +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx +func (b BlobStorageClient) PutBlockWithLength(container, name, blockID string, size uint64, blob io.Reader, extraHeaders map[string]string) error { + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{"comp": {"block"}, "blockid": {blockID}}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypeBlock) + headers["Content-Length"] = fmt.Sprintf("%v", size) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, blob) + if err != nil { + return err + } + + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// PutBlockList saves list of blocks to the specified block blob. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179467.aspx +func (b BlobStorageClient) PutBlockList(container, name string, blocks []Block) error { + blockListXML := prepareBlockListRequest(blocks) + + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{"comp": {"blocklist"}}) + headers := b.client.getStandardHeaders() + headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML)) + + resp, err := b.client.exec("PUT", uri, headers, strings.NewReader(blockListXML)) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// GetBlockList retrieves list of blocks in the specified block blob. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx +func (b BlobStorageClient) GetBlockList(container, name string, blockType BlockListType) (BlockListResponse, error) { + params := url.Values{"comp": {"blocklist"}, "blocklisttype": {string(blockType)}} + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + headers := b.client.getStandardHeaders() + + var out BlockListResponse + resp, err := b.client.exec("GET", uri, headers, nil) + if err != nil { + return out, err + } + defer resp.body.Close() + + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +// PutPageBlob initializes an empty page blob with specified name and maximum +// size in bytes (size must be aligned to a 512-byte boundary). A page blob must +// be created using this method before writing pages. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx +func (b BlobStorageClient) PutPageBlob(container, name string, size int64, extraHeaders map[string]string) error { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypePage) + headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", size) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// PutPage writes a range of pages to a page blob or clears the given range. +// In case of 'clear' writes, given chunk is discarded. Ranges must be aligned +// with 512-byte boundaries and chunk must be of size multiplies by 512. +// +// See https://msdn.microsoft.com/en-us/library/ee691975.aspx +func (b BlobStorageClient) PutPage(container, name string, startByte, endByte int64, writeType PageWriteType, chunk []byte, extraHeaders map[string]string) error { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"page"}}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypePage) + headers["x-ms-page-write"] = string(writeType) + headers["x-ms-range"] = fmt.Sprintf("bytes=%v-%v", startByte, endByte) + for k, v := range extraHeaders { + headers[k] = v + } + var contentLength int64 + var data io.Reader + if writeType == PageWriteTypeClear { + contentLength = 0 + data = bytes.NewReader([]byte{}) + } else { + contentLength = int64(len(chunk)) + data = bytes.NewReader(chunk) + } + headers["Content-Length"] = fmt.Sprintf("%v", contentLength) + + resp, err := b.client.exec("PUT", uri, headers, data) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// GetPageRanges returns the list of valid page ranges for a page blob. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx +func (b BlobStorageClient) GetPageRanges(container, name string) (GetPageRangesResponse, error) { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"pagelist"}}) + headers := b.client.getStandardHeaders() + + var out GetPageRangesResponse + resp, err := b.client.exec("GET", uri, headers, nil) + if err != nil { + return out, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return out, err + } + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +// PutAppendBlob initializes an empty append blob with specified name. An +// append blob must be created using this method before appending blocks. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx +func (b BlobStorageClient) PutAppendBlob(container, name string, extraHeaders map[string]string) error { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypeAppend) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// AppendBlock appends a block to an append blob. +// +// See https://msdn.microsoft.com/en-us/library/azure/mt427365.aspx +func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, extraHeaders map[string]string) error { + path := fmt.Sprintf("%s/%s", container, name) + uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"appendblock"}}) + headers := b.client.getStandardHeaders() + headers["x-ms-blob-type"] = string(BlobTypeAppend) + headers["Content-Length"] = fmt.Sprintf("%v", len(chunk)) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := b.client.exec("PUT", uri, headers, bytes.NewReader(chunk)) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// CopyBlob starts a blob copy operation and waits for the operation to +// complete. sourceBlob parameter must be a canonical URL to the blob (can be +// obtained using GetBlobURL method.) There is no SLA on blob copy and therefore +// this helper method works faster on smaller files. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx +func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error { + copyID, err := b.startBlobCopy(container, name, sourceBlob) + if err != nil { + return err + } + + return b.waitForBlobCopy(container, name, copyID) +} + +func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (string, error) { + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + + headers := b.client.getStandardHeaders() + headers["x-ms-copy-source"] = sourceBlob + + resp, err := b.client.exec("PUT", uri, headers, nil) + if err != nil { + return "", err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusAccepted, http.StatusCreated}); err != nil { + return "", err + } + + copyID := resp.headers.Get("x-ms-copy-id") + if copyID == "" { + return "", errors.New("Got empty copy id header") + } + return copyID, nil +} + +func (b BlobStorageClient) waitForBlobCopy(container, name, copyID string) error { + for { + props, err := b.GetBlobProperties(container, name) + if err != nil { + return err + } + + if props.CopyID != copyID { + return errBlobCopyIDMismatch + } + + switch props.CopyStatus { + case blobCopyStatusSuccess: + return nil + case blobCopyStatusPending: + continue + case blobCopyStatusAborted: + return errBlobCopyAborted + case blobCopyStatusFailed: + return fmt.Errorf("storage: blob copy failed. Id=%s Description=%s", props.CopyID, props.CopyStatusDescription) + default: + return fmt.Errorf("storage: unhandled blob copy status: '%s'", props.CopyStatus) + } + } +} + +// DeleteBlob deletes the given blob from the specified container. +// If the blob does not exists at the time of the Delete Blob operation, it +// returns error. See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx +func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[string]string) error { + resp, err := b.deleteBlob(container, name, extraHeaders) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} + +// DeleteBlobIfExists deletes the given blob from the specified container If the +// blob is deleted with this call, returns true. Otherwise returns false. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx +func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) { + resp, err := b.deleteBlob(container, name, extraHeaders) + if resp != nil && (resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound) { + return resp.statusCode == http.StatusAccepted, nil + } + defer resp.body.Close() + return false, err +} + +func (b BlobStorageClient) deleteBlob(container, name string, extraHeaders map[string]string) (*storageResponse, error) { + verb := "DELETE" + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + headers := b.client.getStandardHeaders() + for k, v := range extraHeaders { + headers[k] = v + } + + return b.client.exec(verb, uri, headers, nil) +} + +// helper method to construct the path to a container given its name +func pathForContainer(name string) string { + return fmt.Sprintf("/%s", name) +} + +// helper method to construct the path to a blob given its container and blob +// name +func pathForBlob(container, name string) string { + return fmt.Sprintf("/%s/%s", container, name) +} + +// GetBlobSASURI creates an URL to the specified blob which contains the Shared +// Access Signature with specified permissions and expiration time. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx +func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) { + var ( + signedPermissions = permissions + blobURL = b.GetBlobURL(container, name) + ) + canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL) + + if err != nil { + return "", err + } + + // "The canonicalizedresouce portion of the string is a canonical path to the signed resource. + // It must include the service name (blob, table, queue or file) for version 2015-02-21 or + // later, the storage account name, and the resource name, and must be URL-decoded. + // -- https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx + + // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component). + canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1) + + canonicalizedResource, err = url.QueryUnescape(canonicalizedResource) + if err != nil { + return "", err + } + + signedExpiry := expiry.UTC().Format(time.RFC3339) + signedResource := "b" + + stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions) + if err != nil { + return "", err + } + + sig := b.client.computeHmac256(stringToSign) + sasParams := url.Values{ + "sv": {b.client.apiVersion}, + "se": {signedExpiry}, + "sr": {signedResource}, + "sp": {signedPermissions}, + "sig": {sig}, + } + + sasURL, err := url.Parse(blobURL) + if err != nil { + return "", err + } + sasURL.RawQuery = sasParams.Encode() + return sasURL.String(), nil +} + +func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string) (string, error) { + var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string + + if signedVersion >= "2015-02-21" { + canonicalizedResource = "/blob" + canonicalizedResource + } + + // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx + if signedVersion >= "2013-08-15" { + return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil + } + return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15") +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go new file mode 100644 index 0000000..2816e03 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go @@ -0,0 +1,551 @@ +// Package storage provides clients for Microsoft Azure Storage Services. +package storage + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "sort" + "strconv" + "strings" +) + +const ( + // DefaultBaseURL is the domain name used for storage requests when a + // default client is created. + DefaultBaseURL = "core.windows.net" + + // DefaultAPIVersion is the Azure Storage API version string used when a + // basic client is created. + DefaultAPIVersion = "2015-02-21" + + defaultUseHTTPS = true + + // StorageEmulatorAccountName is the fixed storage account used by Azure Storage Emulator + StorageEmulatorAccountName = "devstoreaccount1" + + // StorageEmulatorAccountKey is the the fixed storage account used by Azure Storage Emulator + StorageEmulatorAccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" + + blobServiceName = "blob" + tableServiceName = "table" + queueServiceName = "queue" + fileServiceName = "file" + + storageEmulatorBlob = "127.0.0.1:10000" + storageEmulatorTable = "127.0.0.1:10002" + storageEmulatorQueue = "127.0.0.1:10001" +) + +// Client is the object that needs to be constructed to perform +// operations on the storage account. +type Client struct { + // HTTPClient is the http.Client used to initiate API + // requests. If it is nil, http.DefaultClient is used. + HTTPClient *http.Client + + accountName string + accountKey []byte + useHTTPS bool + baseURL string + apiVersion string +} + +type storageResponse struct { + statusCode int + headers http.Header + body io.ReadCloser +} + +type odataResponse struct { + storageResponse + odata odataErrorMessage +} + +// AzureStorageServiceError contains fields of the error response from +// Azure Storage Service REST API. See https://msdn.microsoft.com/en-us/library/azure/dd179382.aspx +// Some fields might be specific to certain calls. +type AzureStorageServiceError struct { + Code string `xml:"Code"` + Message string `xml:"Message"` + AuthenticationErrorDetail string `xml:"AuthenticationErrorDetail"` + QueryParameterName string `xml:"QueryParameterName"` + QueryParameterValue string `xml:"QueryParameterValue"` + Reason string `xml:"Reason"` + StatusCode int + RequestID string +} + +type odataErrorMessageMessage struct { + Lang string `json:"lang"` + Value string `json:"value"` +} + +type odataErrorMessageInternal struct { + Code string `json:"code"` + Message odataErrorMessageMessage `json:"message"` +} + +type odataErrorMessage struct { + Err odataErrorMessageInternal `json:"odata.error"` +} + +// UnexpectedStatusCodeError is returned when a storage service responds with neither an error +// nor with an HTTP status code indicating success. +type UnexpectedStatusCodeError struct { + allowed []int + got int +} + +func (e UnexpectedStatusCodeError) Error() string { + s := func(i int) string { return fmt.Sprintf("%d %s", i, http.StatusText(i)) } + + got := s(e.got) + expected := []string{} + for _, v := range e.allowed { + expected = append(expected, s(v)) + } + return fmt.Sprintf("storage: status code from service response is %s; was expecting %s", got, strings.Join(expected, " or ")) +} + +// Got is the actual status code returned by Azure. +func (e UnexpectedStatusCodeError) Got() int { + return e.got +} + +// NewBasicClient constructs a Client with given storage service name and +// key. +func NewBasicClient(accountName, accountKey string) (Client, error) { + if accountName == StorageEmulatorAccountName { + return NewEmulatorClient() + } + return NewClient(accountName, accountKey, DefaultBaseURL, DefaultAPIVersion, defaultUseHTTPS) +} + +//NewEmulatorClient contructs a Client intended to only work with Azure +//Storage Emulator +func NewEmulatorClient() (Client, error) { + return NewClient(StorageEmulatorAccountName, StorageEmulatorAccountKey, DefaultBaseURL, DefaultAPIVersion, false) +} + +// NewClient constructs a Client. This should be used if the caller wants +// to specify whether to use HTTPS, a specific REST API version or a custom +// storage endpoint than Azure Public Cloud. +func NewClient(accountName, accountKey, blobServiceBaseURL, apiVersion string, useHTTPS bool) (Client, error) { + var c Client + if accountName == "" { + return c, fmt.Errorf("azure: account name required") + } else if accountKey == "" { + return c, fmt.Errorf("azure: account key required") + } else if blobServiceBaseURL == "" { + return c, fmt.Errorf("azure: base storage service url required") + } + + key, err := base64.StdEncoding.DecodeString(accountKey) + if err != nil { + return c, fmt.Errorf("azure: malformed storage account key: %v", err) + } + + return Client{ + accountName: accountName, + accountKey: key, + useHTTPS: useHTTPS, + baseURL: blobServiceBaseURL, + apiVersion: apiVersion, + }, nil +} + +func (c Client) getBaseURL(service string) string { + scheme := "http" + if c.useHTTPS { + scheme = "https" + } + host := "" + if c.accountName == StorageEmulatorAccountName { + switch service { + case blobServiceName: + host = storageEmulatorBlob + case tableServiceName: + host = storageEmulatorTable + case queueServiceName: + host = storageEmulatorQueue + } + } else { + host = fmt.Sprintf("%s.%s.%s", c.accountName, service, c.baseURL) + } + + u := &url.URL{ + Scheme: scheme, + Host: host} + return u.String() +} + +func (c Client) getEndpoint(service, path string, params url.Values) string { + u, err := url.Parse(c.getBaseURL(service)) + if err != nil { + // really should not be happening + panic(err) + } + + // API doesn't accept path segments not starting with '/' + if !strings.HasPrefix(path, "/") { + path = fmt.Sprintf("/%v", path) + } + + if c.accountName == StorageEmulatorAccountName { + path = fmt.Sprintf("/%v%v", StorageEmulatorAccountName, path) + } + + u.Path = path + u.RawQuery = params.Encode() + return u.String() +} + +// GetBlobService returns a BlobStorageClient which can operate on the blob +// service of the storage account. +func (c Client) GetBlobService() BlobStorageClient { + return BlobStorageClient{c} +} + +// GetQueueService returns a QueueServiceClient which can operate on the queue +// service of the storage account. +func (c Client) GetQueueService() QueueServiceClient { + return QueueServiceClient{c} +} + +// GetTableService returns a TableServiceClient which can operate on the table +// service of the storage account. +func (c Client) GetTableService() TableServiceClient { + return TableServiceClient{c} +} + +// GetFileService returns a FileServiceClient which can operate on the file +// service of the storage account. +func (c Client) GetFileService() FileServiceClient { + return FileServiceClient{c} +} + +func (c Client) createAuthorizationHeader(canonicalizedString string) string { + signature := c.computeHmac256(canonicalizedString) + return fmt.Sprintf("%s %s:%s", "SharedKey", c.getCanonicalizedAccountName(), signature) +} + +func (c Client) getAuthorizationHeader(verb, url string, headers map[string]string) (string, error) { + canonicalizedResource, err := c.buildCanonicalizedResource(url) + if err != nil { + return "", err + } + + canonicalizedString := c.buildCanonicalizedString(verb, headers, canonicalizedResource) + return c.createAuthorizationHeader(canonicalizedString), nil +} + +func (c Client) getStandardHeaders() map[string]string { + return map[string]string{ + "x-ms-version": c.apiVersion, + "x-ms-date": currentTimeRfc1123Formatted(), + } +} + +func (c Client) getCanonicalizedAccountName() string { + // since we may be trying to access a secondary storage account, we need to + // remove the -secondary part of the storage name + return strings.TrimSuffix(c.accountName, "-secondary") +} + +func (c Client) buildCanonicalizedHeader(headers map[string]string) string { + cm := make(map[string]string) + + for k, v := range headers { + headerName := strings.TrimSpace(strings.ToLower(k)) + match, _ := regexp.MatchString("x-ms-", headerName) + if match { + cm[headerName] = v + } + } + + if len(cm) == 0 { + return "" + } + + keys := make([]string, 0, len(cm)) + for key := range cm { + keys = append(keys, key) + } + + sort.Strings(keys) + + ch := "" + + for i, key := range keys { + if i == len(keys)-1 { + ch += fmt.Sprintf("%s:%s", key, cm[key]) + } else { + ch += fmt.Sprintf("%s:%s\n", key, cm[key]) + } + } + return ch +} + +func (c Client) buildCanonicalizedResourceTable(uri string) (string, error) { + errMsg := "buildCanonicalizedResourceTable error: %s" + u, err := url.Parse(uri) + if err != nil { + return "", fmt.Errorf(errMsg, err.Error()) + } + + cr := "/" + c.getCanonicalizedAccountName() + + if len(u.Path) > 0 { + cr += u.Path + } + + return cr, nil +} + +func (c Client) buildCanonicalizedResource(uri string) (string, error) { + errMsg := "buildCanonicalizedResource error: %s" + u, err := url.Parse(uri) + if err != nil { + return "", fmt.Errorf(errMsg, err.Error()) + } + + cr := "/" + c.getCanonicalizedAccountName() + + if len(u.Path) > 0 { + // Any portion of the CanonicalizedResource string that is derived from + // the resource's URI should be encoded exactly as it is in the URI. + // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx + cr += u.EscapedPath() + } + + params, err := url.ParseQuery(u.RawQuery) + if err != nil { + return "", fmt.Errorf(errMsg, err.Error()) + } + + if len(params) > 0 { + cr += "\n" + keys := make([]string, 0, len(params)) + for key := range params { + keys = append(keys, key) + } + + sort.Strings(keys) + + for i, key := range keys { + if len(params[key]) > 1 { + sort.Strings(params[key]) + } + + if i == len(keys)-1 { + cr += fmt.Sprintf("%s:%s", key, strings.Join(params[key], ",")) + } else { + cr += fmt.Sprintf("%s:%s\n", key, strings.Join(params[key], ",")) + } + } + } + + return cr, nil +} + +func (c Client) buildCanonicalizedString(verb string, headers map[string]string, canonicalizedResource string) string { + contentLength := headers["Content-Length"] + if contentLength == "0" { + contentLength = "" + } + canonicalizedString := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", + verb, + headers["Content-Encoding"], + headers["Content-Language"], + contentLength, + headers["Content-MD5"], + headers["Content-Type"], + headers["Date"], + headers["If-Modified-Since"], + headers["If-Match"], + headers["If-None-Match"], + headers["If-Unmodified-Since"], + headers["Range"], + c.buildCanonicalizedHeader(headers), + canonicalizedResource) + + return canonicalizedString +} + +func (c Client) exec(verb, url string, headers map[string]string, body io.Reader) (*storageResponse, error) { + authHeader, err := c.getAuthorizationHeader(verb, url, headers) + if err != nil { + return nil, err + } + headers["Authorization"] = authHeader + if err != nil { + return nil, err + } + + req, err := http.NewRequest(verb, url, body) + if err != nil { + return nil, errors.New("azure/storage: error creating request: " + err.Error()) + } + + if clstr, ok := headers["Content-Length"]; ok { + // content length header is being signed, but completely ignored by golang. + // instead we have to use the ContentLength property on the request struct + // (see https://golang.org/src/net/http/request.go?s=18140:18370#L536 and + // https://golang.org/src/net/http/transfer.go?s=1739:2467#L49) + req.ContentLength, err = strconv.ParseInt(clstr, 10, 64) + if err != nil { + return nil, err + } + } + for k, v := range headers { + req.Header.Add(k, v) + } + + httpClient := c.HTTPClient + if httpClient == nil { + httpClient = http.DefaultClient + } + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + + statusCode := resp.StatusCode + if statusCode >= 400 && statusCode <= 505 { + var respBody []byte + respBody, err = readResponseBody(resp) + if err != nil { + return nil, err + } + + if len(respBody) == 0 { + // no error in response body + err = fmt.Errorf("storage: service returned without a response body (%s)", resp.Status) + } else { + // response contains storage service error object, unmarshal + storageErr, errIn := serviceErrFromXML(respBody, resp.StatusCode, resp.Header.Get("x-ms-request-id")) + if err != nil { // error unmarshaling the error response + err = errIn + } + err = storageErr + } + return &storageResponse{ + statusCode: resp.StatusCode, + headers: resp.Header, + body: ioutil.NopCloser(bytes.NewReader(respBody)), /* restore the body */ + }, err + } + + return &storageResponse{ + statusCode: resp.StatusCode, + headers: resp.Header, + body: resp.Body}, nil +} + +func (c Client) execInternalJSON(verb, url string, headers map[string]string, body io.Reader) (*odataResponse, error) { + req, err := http.NewRequest(verb, url, body) + for k, v := range headers { + req.Header.Add(k, v) + } + + httpClient := c.HTTPClient + if httpClient == nil { + httpClient = http.DefaultClient + } + + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + + respToRet := &odataResponse{} + respToRet.body = resp.Body + respToRet.statusCode = resp.StatusCode + respToRet.headers = resp.Header + + statusCode := resp.StatusCode + if statusCode >= 400 && statusCode <= 505 { + var respBody []byte + respBody, err = readResponseBody(resp) + if err != nil { + return nil, err + } + + if len(respBody) == 0 { + // no error in response body + err = fmt.Errorf("storage: service returned without a response body (%d)", resp.StatusCode) + return respToRet, err + } + // try unmarshal as odata.error json + err = json.Unmarshal(respBody, &respToRet.odata) + return respToRet, err + } + + return respToRet, nil +} + +func (c Client) createSharedKeyLite(url string, headers map[string]string) (string, error) { + can, err := c.buildCanonicalizedResourceTable(url) + + if err != nil { + return "", err + } + strToSign := headers["x-ms-date"] + "\n" + can + + hmac := c.computeHmac256(strToSign) + return fmt.Sprintf("SharedKeyLite %s:%s", c.accountName, hmac), nil +} + +func (c Client) execTable(verb, url string, headers map[string]string, body io.Reader) (*odataResponse, error) { + var err error + headers["Authorization"], err = c.createSharedKeyLite(url, headers) + if err != nil { + return nil, err + } + + return c.execInternalJSON(verb, url, headers, body) +} + +func readResponseBody(resp *http.Response) ([]byte, error) { + defer resp.Body.Close() + out, err := ioutil.ReadAll(resp.Body) + if err == io.EOF { + err = nil + } + return out, err +} + +func serviceErrFromXML(body []byte, statusCode int, requestID string) (AzureStorageServiceError, error) { + var storageErr AzureStorageServiceError + if err := xml.Unmarshal(body, &storageErr); err != nil { + return storageErr, err + } + storageErr.StatusCode = statusCode + storageErr.RequestID = requestID + return storageErr, nil +} + +func (e AzureStorageServiceError) Error() string { + return fmt.Sprintf("storage: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s, QueryParameterName=%s, QueryParameterValue=%s", + e.StatusCode, e.Code, e.Message, e.RequestID, e.QueryParameterName, e.QueryParameterValue) +} + +// checkRespCode returns UnexpectedStatusError if the given response code is not +// one of the allowed status codes; otherwise nil. +func checkRespCode(respCode int, allowed []int) error { + for _, v := range allowed { + if respCode == v { + return nil + } + } + return UnexpectedStatusCodeError{allowed, respCode} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go new file mode 100644 index 0000000..2397587 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go @@ -0,0 +1,352 @@ +package storage + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + "strings" +) + +// FileServiceClient contains operations for Microsoft Azure File Service. +type FileServiceClient struct { + client Client +} + +// A Share is an entry in ShareListResponse. +type Share struct { + Name string `xml:"Name"` + Properties ShareProperties `xml:"Properties"` +} + +// ShareProperties contains various properties of a share returned from +// various endpoints like ListShares. +type ShareProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` + Quota string `xml:"Quota"` +} + +// ShareListResponse contains the response fields from +// ListShares call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx +type ShareListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Shares []Share `xml:"Shares>Share"` +} + +// ListSharesParameters defines the set of customizable parameters to make a +// List Shares call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx +type ListSharesParameters struct { + Prefix string + Marker string + Include string + MaxResults uint + Timeout uint +} + +// ShareHeaders contains various properties of a file and is an entry +// in SetShareProperties +type ShareHeaders struct { + Quota string `header:"x-ms-share-quota"` +} + +func (p ListSharesParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// pathForFileShare returns the URL path segment for a File Share resource +func pathForFileShare(name string) string { + return fmt.Sprintf("/%s", name) +} + +// ListShares returns the list of shares in a storage account along with +// pagination token and other response details. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +func (f FileServiceClient) ListShares(params ListSharesParameters) (ShareListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) + uri := f.client.getEndpoint(fileServiceName, "", q) + headers := f.client.getStandardHeaders() + + var out ShareListResponse + resp, err := f.client.exec("GET", uri, headers, nil) + if err != nil { + return out, err + } + defer resp.body.Close() + + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +// CreateShare operation creates a new share under the specified account. If the +// share with the same name already exists, the operation fails. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx +func (f FileServiceClient) CreateShare(name string) error { + resp, err := f.createShare(name) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// ShareExists returns true if a share with given name exists +// on the storage account, otherwise returns false. +func (f FileServiceClient) ShareExists(name string) (bool, error) { + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) + headers := f.client.getStandardHeaders() + + resp, err := f.client.exec("HEAD", uri, headers, nil) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusOK, nil + } + } + return false, err +} + +// GetShareURL gets the canonical URL to the share with the specified name in the +// specified container. This method does not create a publicly accessible URL if +// the file is private and this method does not check if the file +// exists. +func (f FileServiceClient) GetShareURL(name string) string { + return f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{}) +} + +// CreateShareIfNotExists creates a new share under the specified account if +// it does not exist. Returns true if container is newly created or false if +// container already exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx +func (f FileServiceClient) CreateShareIfNotExists(name string) (bool, error) { + resp, err := f.createShare(name) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { + return resp.statusCode == http.StatusCreated, nil + } + } + return false, err +} + +// CreateShare creates a Azure File Share and returns its response +func (f FileServiceClient) createShare(name string) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) + headers := f.client.getStandardHeaders() + return f.client.exec("PUT", uri, headers, nil) +} + +// GetShareProperties provides various information about the specified +// file. See https://msdn.microsoft.com/en-us/library/azure/dn689099.aspx +func (f FileServiceClient) GetShareProperties(name string) (*ShareProperties, error) { + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) + + headers := f.client.getStandardHeaders() + resp, err := f.client.exec("HEAD", uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + return &ShareProperties{ + LastModified: resp.headers.Get("Last-Modified"), + Etag: resp.headers.Get("Etag"), + Quota: resp.headers.Get("x-ms-share-quota"), + }, nil +} + +// SetShareProperties replaces the ShareHeaders for the specified file. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by SetShareProperties. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/mt427368.aspx +func (f FileServiceClient) SetShareProperties(name string, shareHeaders ShareHeaders) error { + params := url.Values{} + params.Set("restype", "share") + params.Set("comp", "properties") + + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) + headers := f.client.getStandardHeaders() + + extraHeaders := headersFromStruct(shareHeaders) + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := f.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// DeleteShare operation marks the specified share for deletion. The share +// and any files contained within it are later deleted during garbage +// collection. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx +func (f FileServiceClient) DeleteShare(name string) error { + resp, err := f.deleteShare(name) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} + +// DeleteShareIfExists operation marks the specified share for deletion if it +// exists. The share and any files contained within it are later deleted during +// garbage collection. Returns true if share existed and deleted with this call, +// false otherwise. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx +func (f FileServiceClient) DeleteShareIfExists(name string) (bool, error) { + resp, err := f.deleteShare(name) + if resp != nil { + defer resp.body.Close() + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +// deleteShare makes the call to Delete Share operation endpoint and returns +// the response +func (f FileServiceClient) deleteShare(name string) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) + return f.client.exec("DELETE", uri, f.client.getStandardHeaders(), nil) +} + +// SetShareMetadata replaces the metadata for the specified Share. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetShareMetadata. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx +func (f FileServiceClient) SetShareMetadata(name string, metadata map[string]string, extraHeaders map[string]string) error { + params := url.Values{} + params.Set("restype", "share") + params.Set("comp", "metadata") + + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) + headers := f.client.getStandardHeaders() + for k, v := range metadata { + headers[userDefinedMetadataHeaderPrefix+k] = v + } + + for k, v := range extraHeaders { + headers[k] = v + } + + resp, err := f.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// GetShareMetadata returns all user-defined metadata for the specified share. +// +// All metadata keys will be returned in lower case. (HTTP header +// names are case-insensitive.) +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx +func (f FileServiceClient) GetShareMetadata(name string) (map[string]string, error) { + params := url.Values{} + params.Set("restype", "share") + params.Set("comp", "metadata") + + uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) + headers := f.client.getStandardHeaders() + + resp, err := f.client.exec("GET", uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + metadata := make(map[string]string) + for k, v := range resp.headers { + // Can't trust CanonicalHeaderKey() to munge case + // reliably. "_" is allowed in identifiers: + // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx + // https://msdn.microsoft.com/library/aa664670(VS.71).aspx + // http://tools.ietf.org/html/rfc7230#section-3.2 + // ...but "_" is considered invalid by + // CanonicalMIMEHeaderKey in + // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542 + // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar". + k = strings.ToLower(k) + if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) { + continue + } + // metadata["foo"] = content of the last X-Ms-Meta-Foo header + k = k[len(userDefinedMetadataHeaderPrefix):] + metadata[k] = v[len(v)-1] + } + return metadata, nil +} + +//checkForStorageEmulator determines if the client is setup for use with +//Azure Storage Emulator, and returns a relevant error +func (f FileServiceClient) checkForStorageEmulator() error { + if f.client.accountName == StorageEmulatorAccountName { + return fmt.Errorf("Error: File service is not currently supported by Azure Storage Emulator") + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go new file mode 100644 index 0000000..3ecf4ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go @@ -0,0 +1,306 @@ +package storage + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" +) + +const ( + // casing is per Golang's http.Header canonicalizing the header names. + approximateMessagesCountHeader = "X-Ms-Approximate-Messages-Count" + userDefinedMetadataHeaderPrefix = "X-Ms-Meta-" +) + +// QueueServiceClient contains operations for Microsoft Azure Queue Storage +// Service. +type QueueServiceClient struct { + client Client +} + +func pathForQueue(queue string) string { return fmt.Sprintf("/%s", queue) } +func pathForQueueMessages(queue string) string { return fmt.Sprintf("/%s/messages", queue) } +func pathForMessage(queue, name string) string { return fmt.Sprintf("/%s/messages/%s", queue, name) } + +type putMessageRequest struct { + XMLName xml.Name `xml:"QueueMessage"` + MessageText string `xml:"MessageText"` +} + +// PutMessageParameters is the set of options can be specified for Put Messsage +// operation. A zero struct does not use any preferences for the request. +type PutMessageParameters struct { + VisibilityTimeout int + MessageTTL int +} + +func (p PutMessageParameters) getParameters() url.Values { + out := url.Values{} + if p.VisibilityTimeout != 0 { + out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout)) + } + if p.MessageTTL != 0 { + out.Set("messagettl", strconv.Itoa(p.MessageTTL)) + } + return out +} + +// GetMessagesParameters is the set of options can be specified for Get +// Messsages operation. A zero struct does not use any preferences for the +// request. +type GetMessagesParameters struct { + NumOfMessages int + VisibilityTimeout int +} + +func (p GetMessagesParameters) getParameters() url.Values { + out := url.Values{} + if p.NumOfMessages != 0 { + out.Set("numofmessages", strconv.Itoa(p.NumOfMessages)) + } + if p.VisibilityTimeout != 0 { + out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout)) + } + return out +} + +// PeekMessagesParameters is the set of options can be specified for Peek +// Messsage operation. A zero struct does not use any preferences for the +// request. +type PeekMessagesParameters struct { + NumOfMessages int +} + +func (p PeekMessagesParameters) getParameters() url.Values { + out := url.Values{"peekonly": {"true"}} // Required for peek operation + if p.NumOfMessages != 0 { + out.Set("numofmessages", strconv.Itoa(p.NumOfMessages)) + } + return out +} + +// GetMessagesResponse represents a response returned from Get Messages +// operation. +type GetMessagesResponse struct { + XMLName xml.Name `xml:"QueueMessagesList"` + QueueMessagesList []GetMessageResponse `xml:"QueueMessage"` +} + +// GetMessageResponse represents a QueueMessage object returned from Get +// Messages operation response. +type GetMessageResponse struct { + MessageID string `xml:"MessageId"` + InsertionTime string `xml:"InsertionTime"` + ExpirationTime string `xml:"ExpirationTime"` + PopReceipt string `xml:"PopReceipt"` + TimeNextVisible string `xml:"TimeNextVisible"` + DequeueCount int `xml:"DequeueCount"` + MessageText string `xml:"MessageText"` +} + +// PeekMessagesResponse represents a response returned from Get Messages +// operation. +type PeekMessagesResponse struct { + XMLName xml.Name `xml:"QueueMessagesList"` + QueueMessagesList []PeekMessageResponse `xml:"QueueMessage"` +} + +// PeekMessageResponse represents a QueueMessage object returned from Peek +// Messages operation response. +type PeekMessageResponse struct { + MessageID string `xml:"MessageId"` + InsertionTime string `xml:"InsertionTime"` + ExpirationTime string `xml:"ExpirationTime"` + DequeueCount int `xml:"DequeueCount"` + MessageText string `xml:"MessageText"` +} + +// QueueMetadataResponse represents user defined metadata and queue +// properties on a specific queue. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179384.aspx +type QueueMetadataResponse struct { + ApproximateMessageCount int + UserDefinedMetadata map[string]string +} + +// SetMetadata operation sets user-defined metadata on the specified queue. +// Metadata is associated with the queue as name-value pairs. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179348.aspx +func (c QueueServiceClient) SetMetadata(name string, metadata map[string]string) error { + uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}}) + headers := c.client.getStandardHeaders() + for k, v := range metadata { + headers[userDefinedMetadataHeaderPrefix+k] = v + } + + resp, err := c.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + + return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) +} + +// GetMetadata operation retrieves user-defined metadata and queue +// properties on the specified queue. Metadata is associated with +// the queue as name-values pairs. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179384.aspx +// +// Because the way Golang's http client (and http.Header in particular) +// canonicalize header names, the returned metadata names would always +// be all lower case. +func (c QueueServiceClient) GetMetadata(name string) (QueueMetadataResponse, error) { + qm := QueueMetadataResponse{} + qm.UserDefinedMetadata = make(map[string]string) + uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}}) + headers := c.client.getStandardHeaders() + resp, err := c.client.exec("GET", uri, headers, nil) + if err != nil { + return qm, err + } + defer resp.body.Close() + + for k, v := range resp.headers { + if len(v) != 1 { + return qm, fmt.Errorf("Unexpected number of values (%d) in response header '%s'", len(v), k) + } + + value := v[0] + + if k == approximateMessagesCountHeader { + qm.ApproximateMessageCount, err = strconv.Atoi(value) + if err != nil { + return qm, fmt.Errorf("Unexpected value in response header '%s': '%s' ", k, value) + } + } else if strings.HasPrefix(k, userDefinedMetadataHeaderPrefix) { + name := strings.TrimPrefix(k, userDefinedMetadataHeaderPrefix) + qm.UserDefinedMetadata[strings.ToLower(name)] = value + } + } + + return qm, checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// CreateQueue operation creates a queue under the given account. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179342.aspx +func (c QueueServiceClient) CreateQueue(name string) error { + uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{}) + headers := c.client.getStandardHeaders() + resp, err := c.client.exec("PUT", uri, headers, nil) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// DeleteQueue operation permanently deletes the specified queue. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179436.aspx +func (c QueueServiceClient) DeleteQueue(name string) error { + uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{}) + resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) +} + +// QueueExists returns true if a queue with given name exists. +func (c QueueServiceClient) QueueExists(name string) (bool, error) { + uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": {"metadata"}}) + resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + if resp != nil && (resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound) { + return resp.statusCode == http.StatusOK, nil + } + + return false, err +} + +// PutMessage operation adds a new message to the back of the message queue. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179346.aspx +func (c QueueServiceClient) PutMessage(queue string, message string, params PutMessageParameters) error { + uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters()) + req := putMessageRequest{MessageText: message} + body, nn, err := xmlMarshal(req) + if err != nil { + return err + } + headers := c.client.getStandardHeaders() + headers["Content-Length"] = strconv.Itoa(nn) + resp, err := c.client.exec("POST", uri, headers, body) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// ClearMessages operation deletes all messages from the specified queue. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179454.aspx +func (c QueueServiceClient) ClearMessages(queue string) error { + uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), url.Values{}) + resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) +} + +// GetMessages operation retrieves one or more messages from the front of the +// queue. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179474.aspx +func (c QueueServiceClient) GetMessages(queue string, params GetMessagesParameters) (GetMessagesResponse, error) { + var r GetMessagesResponse + uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters()) + resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + if err != nil { + return r, err + } + defer resp.body.Close() + err = xmlUnmarshal(resp.body, &r) + return r, err +} + +// PeekMessages retrieves one or more messages from the front of the queue, but +// does not alter the visibility of the message. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179472.aspx +func (c QueueServiceClient) PeekMessages(queue string, params PeekMessagesParameters) (PeekMessagesResponse, error) { + var r PeekMessagesResponse + uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters()) + resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + if err != nil { + return r, err + } + defer resp.body.Close() + err = xmlUnmarshal(resp.body, &r) + return r, err +} + +// DeleteMessage operation deletes the specified message. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179347.aspx +func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) error { + uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), url.Values{ + "popreceipt": {popReceipt}}) + resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + if err != nil { + return err + } + defer resp.body.Close() + return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go new file mode 100644 index 0000000..39e9975 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go @@ -0,0 +1,129 @@ +package storage + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" +) + +// TableServiceClient contains operations for Microsoft Azure Table Storage +// Service. +type TableServiceClient struct { + client Client +} + +// AzureTable is the typedef of the Azure Table name +type AzureTable string + +const ( + tablesURIPath = "/Tables" +) + +type createTableRequest struct { + TableName string `json:"TableName"` +} + +func pathForTable(table AzureTable) string { return fmt.Sprintf("%s", table) } + +func (c *TableServiceClient) getStandardHeaders() map[string]string { + return map[string]string{ + "x-ms-version": "2015-02-21", + "x-ms-date": currentTimeRfc1123Formatted(), + "Accept": "application/json;odata=nometadata", + "Accept-Charset": "UTF-8", + "Content-Type": "application/json", + } +} + +// QueryTables returns the tables created in the +// *TableServiceClient storage account. +func (c *TableServiceClient) QueryTables() ([]AzureTable, error) { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + + headers := c.getStandardHeaders() + headers["Content-Length"] = "0" + + resp, err := c.client.execTable("GET", uri, headers, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + buf.ReadFrom(resp.body) + + var respArray queryTablesResponse + if err := json.Unmarshal(buf.Bytes(), &respArray); err != nil { + return nil, err + } + + s := make([]AzureTable, len(respArray.TableName)) + for i, elem := range respArray.TableName { + s[i] = AzureTable(elem.TableName) + } + + return s, nil +} + +// CreateTable creates the table given the specific +// name. This function fails if the name is not compliant +// with the specification or the tables already exists. +func (c *TableServiceClient) CreateTable(table AzureTable) error { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + + headers := c.getStandardHeaders() + + req := createTableRequest{TableName: string(table)} + buf := new(bytes.Buffer) + + if err := json.NewEncoder(buf).Encode(req); err != nil { + return err + } + + headers["Content-Length"] = fmt.Sprintf("%d", buf.Len()) + + resp, err := c.client.execTable("POST", uri, headers, buf) + + if err != nil { + return err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil { + return err + } + + return nil +} + +// DeleteTable deletes the table given the specific +// name. This function fails if the table is not present. +// Be advised: DeleteTable deletes all the entries +// that may be present. +func (c *TableServiceClient) DeleteTable(table AzureTable) error { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + uri += fmt.Sprintf("('%s')", string(table)) + + headers := c.getStandardHeaders() + + headers["Content-Length"] = "0" + + resp, err := c.client.execTable("DELETE", uri, headers, nil) + + if err != nil { + return err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { + return err + + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go new file mode 100644 index 0000000..1b5919c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go @@ -0,0 +1,355 @@ +package storage + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "reflect" +) + +const ( + partitionKeyNode = "PartitionKey" + rowKeyNode = "RowKey" + tag = "table" + tagIgnore = "-" + continuationTokenPartitionKeyHeader = "X-Ms-Continuation-Nextpartitionkey" + continuationTokenRowHeader = "X-Ms-Continuation-Nextrowkey" + maxTopParameter = 1000 +) + +type queryTablesResponse struct { + TableName []struct { + TableName string `json:"TableName"` + } `json:"value"` +} + +const ( + tableOperationTypeInsert = iota + tableOperationTypeUpdate = iota + tableOperationTypeMerge = iota + tableOperationTypeInsertOrReplace = iota + tableOperationTypeInsertOrMerge = iota +) + +type tableOperation int + +// TableEntity interface specifies +// the functions needed to support +// marshaling and unmarshaling into +// Azure Tables. The struct must only contain +// simple types because Azure Tables do not +// support hierarchy. +type TableEntity interface { + PartitionKey() string + RowKey() string + SetPartitionKey(string) error + SetRowKey(string) error +} + +// ContinuationToken is an opaque (ie not useful to inspect) +// struct that Get... methods can return if there are more +// entries to be returned than the ones already +// returned. Just pass it to the same function to continue +// receiving the remaining entries. +type ContinuationToken struct { + NextPartitionKey string + NextRowKey string +} + +type getTableEntriesResponse struct { + Elements []map[string]interface{} `json:"value"` +} + +// QueryTableEntities queries the specified table and returns the unmarshaled +// entities of type retType. +// top parameter limits the returned entries up to top. Maximum top +// allowed by Azure API is 1000. In case there are more than top entries to be +// returned the function will return a non nil *ContinuationToken. You can call the +// same function again passing the received ContinuationToken as previousContToken +// parameter in order to get the following entries. The query parameter +// is the odata query. To retrieve all the entries pass the empty string. +// The function returns a pointer to a TableEntity slice, the *ContinuationToken +// if there are more entries to be returned and an error in case something went +// wrong. +// +// Example: +// entities, cToken, err = tSvc.QueryTableEntities("table", cToken, reflect.TypeOf(entity), 20, "") +func (c *TableServiceClient) QueryTableEntities(tableName AzureTable, previousContToken *ContinuationToken, retType reflect.Type, top int, query string) ([]TableEntity, *ContinuationToken, error) { + if top > maxTopParameter { + return nil, nil, fmt.Errorf("top accepts at maximum %d elements. Requested %d instead", maxTopParameter, top) + } + + uri := c.client.getEndpoint(tableServiceName, pathForTable(tableName), url.Values{}) + uri += fmt.Sprintf("?$top=%d", top) + if query != "" { + uri += fmt.Sprintf("&$filter=%s", url.QueryEscape(query)) + } + + if previousContToken != nil { + uri += fmt.Sprintf("&NextPartitionKey=%s&NextRowKey=%s", previousContToken.NextPartitionKey, previousContToken.NextRowKey) + } + + headers := c.getStandardHeaders() + + headers["Content-Length"] = "0" + + resp, err := c.client.execTable("GET", uri, headers, nil) + + if err != nil { + return nil, nil, err + } + + contToken := extractContinuationTokenFromHeaders(resp.headers) + + if err != nil { + return nil, contToken, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, contToken, err + } + + retEntries, err := deserializeEntity(retType, resp.body) + if err != nil { + return nil, contToken, err + } + + return retEntries, contToken, nil +} + +// InsertEntity inserts an entity in the specified table. +// The function fails if there is an entity with the same +// PartitionKey and RowKey in the table. +func (c *TableServiceClient) InsertEntity(table AzureTable, entity TableEntity) error { + var err error + + if sc, err := c.execTable(table, entity, false, "POST"); err != nil { + return checkRespCode(sc, []int{http.StatusCreated}) + } + + return err +} + +func (c *TableServiceClient) execTable(table AzureTable, entity TableEntity, specifyKeysInURL bool, method string) (int, error) { + uri := c.client.getEndpoint(tableServiceName, pathForTable(table), url.Values{}) + if specifyKeysInURL { + uri += fmt.Sprintf("(PartitionKey='%s',RowKey='%s')", url.QueryEscape(entity.PartitionKey()), url.QueryEscape(entity.RowKey())) + } + + headers := c.getStandardHeaders() + + var buf bytes.Buffer + + if err := injectPartitionAndRowKeys(entity, &buf); err != nil { + return 0, err + } + + headers["Content-Length"] = fmt.Sprintf("%d", buf.Len()) + + var err error + var resp *odataResponse + + resp, err = c.client.execTable(method, uri, headers, &buf) + + if err != nil { + return 0, err + } + + defer resp.body.Close() + + return resp.statusCode, nil +} + +// UpdateEntity updates the contents of an entity with the +// one passed as parameter. The function fails if there is no entity +// with the same PartitionKey and RowKey in the table. +func (c *TableServiceClient) UpdateEntity(table AzureTable, entity TableEntity) error { + var err error + + if sc, err := c.execTable(table, entity, true, "PUT"); err != nil { + return checkRespCode(sc, []int{http.StatusNoContent}) + } + return err +} + +// MergeEntity merges the contents of an entity with the +// one passed as parameter. +// The function fails if there is no entity +// with the same PartitionKey and RowKey in the table. +func (c *TableServiceClient) MergeEntity(table AzureTable, entity TableEntity) error { + var err error + + if sc, err := c.execTable(table, entity, true, "MERGE"); err != nil { + return checkRespCode(sc, []int{http.StatusNoContent}) + } + return err +} + +// DeleteEntityWithoutCheck deletes the entity matching by +// PartitionKey and RowKey. There is no check on IfMatch +// parameter so the entity is always deleted. +// The function fails if there is no entity +// with the same PartitionKey and RowKey in the table. +func (c *TableServiceClient) DeleteEntityWithoutCheck(table AzureTable, entity TableEntity) error { + return c.DeleteEntity(table, entity, "*") +} + +// DeleteEntity deletes the entity matching by +// PartitionKey, RowKey and ifMatch field. +// The function fails if there is no entity +// with the same PartitionKey and RowKey in the table or +// the ifMatch is different. +func (c *TableServiceClient) DeleteEntity(table AzureTable, entity TableEntity, ifMatch string) error { + uri := c.client.getEndpoint(tableServiceName, pathForTable(table), url.Values{}) + uri += fmt.Sprintf("(PartitionKey='%s',RowKey='%s')", url.QueryEscape(entity.PartitionKey()), url.QueryEscape(entity.RowKey())) + + headers := c.getStandardHeaders() + + headers["Content-Length"] = "0" + headers["If-Match"] = ifMatch + + resp, err := c.client.execTable("DELETE", uri, headers, nil) + + if err != nil { + return err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { + return err + } + + return nil +} + +// InsertOrReplaceEntity inserts an entity in the specified table +// or replaced the existing one. +func (c *TableServiceClient) InsertOrReplaceEntity(table AzureTable, entity TableEntity) error { + var err error + + if sc, err := c.execTable(table, entity, true, "PUT"); err != nil { + return checkRespCode(sc, []int{http.StatusNoContent}) + } + return err +} + +// InsertOrMergeEntity inserts an entity in the specified table +// or merges the existing one. +func (c *TableServiceClient) InsertOrMergeEntity(table AzureTable, entity TableEntity) error { + var err error + + if sc, err := c.execTable(table, entity, true, "MERGE"); err != nil { + return checkRespCode(sc, []int{http.StatusNoContent}) + } + return err +} + +func injectPartitionAndRowKeys(entity TableEntity, buf *bytes.Buffer) error { + if err := json.NewEncoder(buf).Encode(entity); err != nil { + return err + } + + dec := make(map[string]interface{}) + if err := json.NewDecoder(buf).Decode(&dec); err != nil { + return err + } + + // Inject PartitionKey and RowKey + dec[partitionKeyNode] = entity.PartitionKey() + dec[rowKeyNode] = entity.RowKey() + + // Remove tagged fields + // The tag is defined in the const section + // This is useful to avoid storing the PartitionKey and RowKey twice. + numFields := reflect.ValueOf(entity).Elem().NumField() + for i := 0; i < numFields; i++ { + f := reflect.ValueOf(entity).Elem().Type().Field(i) + + if f.Tag.Get(tag) == tagIgnore { + // we must look for its JSON name in the dictionary + // as the user can rename it using a tag + jsonName := f.Name + if f.Tag.Get("json") != "" { + jsonName = f.Tag.Get("json") + } + delete(dec, jsonName) + } + } + + buf.Reset() + + if err := json.NewEncoder(buf).Encode(&dec); err != nil { + return err + } + + return nil +} + +func deserializeEntity(retType reflect.Type, reader io.Reader) ([]TableEntity, error) { + buf := new(bytes.Buffer) + + var ret getTableEntriesResponse + if err := json.NewDecoder(reader).Decode(&ret); err != nil { + return nil, err + } + + tEntries := make([]TableEntity, len(ret.Elements)) + + for i, entry := range ret.Elements { + + buf.Reset() + if err := json.NewEncoder(buf).Encode(entry); err != nil { + return nil, err + } + + dec := make(map[string]interface{}) + if err := json.NewDecoder(buf).Decode(&dec); err != nil { + return nil, err + } + + var pKey, rKey string + // strip pk and rk + for key, val := range dec { + switch key { + case partitionKeyNode: + pKey = val.(string) + case rowKeyNode: + rKey = val.(string) + } + } + + delete(dec, partitionKeyNode) + delete(dec, rowKeyNode) + + buf.Reset() + if err := json.NewEncoder(buf).Encode(dec); err != nil { + return nil, err + } + + // Create a empty retType instance + tEntries[i] = reflect.New(retType.Elem()).Interface().(TableEntity) + // Popolate it with the values + if err := json.NewDecoder(buf).Decode(&tEntries[i]); err != nil { + return nil, err + } + + // Reset PartitionKey and RowKey + tEntries[i].SetPartitionKey(pKey) + tEntries[i].SetRowKey(rKey) + } + + return tEntries, nil +} + +func extractContinuationTokenFromHeaders(h http.Header) *ContinuationToken { + ct := ContinuationToken{h.Get(continuationTokenPartitionKeyHeader), h.Get(continuationTokenRowHeader)} + + if ct.NextPartitionKey != "" && ct.NextRowKey != "" { + return &ct + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go new file mode 100644 index 0000000..d71c6ce --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go @@ -0,0 +1,85 @@ +package storage + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "time" +) + +func (c Client) computeHmac256(message string) string { + h := hmac.New(sha256.New, c.accountKey) + h.Write([]byte(message)) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func currentTimeRfc1123Formatted() string { + return timeRfc1123Formatted(time.Now().UTC()) +} + +func timeRfc1123Formatted(t time.Time) string { + return t.Format(http.TimeFormat) +} + +func mergeParams(v1, v2 url.Values) url.Values { + out := url.Values{} + for k, v := range v1 { + out[k] = v + } + for k, v := range v2 { + vals, ok := out[k] + if ok { + vals = append(vals, v...) + out[k] = vals + } else { + out[k] = v + } + } + return out +} + +func prepareBlockListRequest(blocks []Block) string { + s := `` + for _, v := range blocks { + s += fmt.Sprintf("<%s>%s", v.Status, v.ID, v.Status) + } + s += `` + return s +} + +func xmlUnmarshal(body io.Reader, v interface{}) error { + data, err := ioutil.ReadAll(body) + if err != nil { + return err + } + return xml.Unmarshal(data, v) +} + +func xmlMarshal(v interface{}) (io.Reader, int, error) { + b, err := xml.Marshal(v) + if err != nil { + return nil, 0, err + } + return bytes.NewReader(b), len(b), nil +} + +func headersFromStruct(v interface{}) map[string]string { + headers := make(map[string]string) + value := reflect.ValueOf(v) + for i := 0; i < value.NumField(); i++ { + key := value.Type().Field(i).Tag.Get("header") + val := value.Field(i).String() + if val != "" { + headers[key] = val + } + } + return headers +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/.travis.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/.travis.yml new file mode 100644 index 0000000..1f34efa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/.travis.yml @@ -0,0 +1,16 @@ +language: go +go: +- 1.7.3 +- tip +before_install: +- go get -v github.com/golang/lint/golint +- go get -v -t -d ./... +after_success: +- make coverdata +- bash <(curl -s https://codecov.io/bash) +script: +- make -j4 check GOTEST_FLAGS=-v +notifications: + slack: + secure: MO/3LqbyALbi9vAY3pZetp/LfRuKEPAYEUya7XKmTWA3OFHYkTGqJWNosVkFJd6eSKwnc3HP4jlKADEBNVxADHzcA3uMPUQi1mIcNk/Ps1WWMNDv1liE2XOoOmHSHZ/8ksk6TNq83x+d17ZffYq8KAH6iKNKvllO1JzQPgJJdf+cNXQQlg6uPSe+ggMpjqVLkKcHqA4L3/BWo6fNcyvkqaN3uXcEzYPi7Nb2q9tl0ja6ToyZV4H6SinwitZmpedN3RkBcm4fKmGyw5ikzH93ycA5SvWrnXTh1dJvq6DU0FV7iwI6oqPTbAUc3FE5g7aEkK0qVR21s2j+KNaOLnuX10ZGQFwj2r3SW2REHq4j+qqFla/2EmSFZJt3GXYS+plmGCxqCgyjSw6tTi7LaGZ/mWBJEA9/EaXG1NkwlQYx5tdUMeGj77OczjXClynpb2hJ7MM2b32Rnp0JmNaXAh01SmClo+8nDWuksAsIdPtWsbF0/XHmEJiqpu8ojvVXOQIbPt43bjG7PS1t5jaRAU/N1n56SiCGgCSGd3Ui5eX5vmgWdpZMl8NG05G4LFsgmkdphRT5fru0C2PrhNZYRDGWs63XKapBxsvfqGzdHxTtYuaDjHjrI+9w0BC/8kEzSWoPmabQ5ci4wf4DeplcIay4tDMgMSo8pGAf52vrne4rmUo= + on_success: change diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/AUTHORS new file mode 100644 index 0000000..5bb93cb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/AUTHORS @@ -0,0 +1,25 @@ +All contributors are required to sign a "Contributor License Agreement" at + + +The following organizations and people have contributed code to this library. +(Please keep both lists sorted alphabetically.) + + +Arista Networks, Inc. + + +Benoit Sigoure +Fabrice Rabaute + + + +The list of individual contributors for code currently in HEAD can be obtained +at any time with the following script: + +find . -type f \ +| while read i; do \ + git blame -t $i 2>/dev/null; \ + done \ +| sed 's/^[0-9a-f]\{8\} [^(]*(\([^)]*\) [-+0-9 ]\{14,\}).*/\1/;s/ *$//' \ +| awk '{a[$0]++; t++} END{for(n in a) print n}' \ +| sort diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/COPYING b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/COPYING new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/COPYING @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Dockerfile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Dockerfile new file mode 100644 index 0000000..0322e56 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Dockerfile @@ -0,0 +1,14 @@ +# Copyright (C) 2016 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the COPYING file. + +# TODO: move this to cmd/ockafka (https://github.com/docker/hub-feedback/issues/292) +FROM golang:1.7.3 + +RUN mkdir -p /go/src/github.com/aristanetworks/goarista/cmd +WORKDIR /go/src/github.com/aristanetworks/goarista +COPY ./ . +RUN go get -d ./cmd/ockafka/... \ + && go install ./cmd/ockafka + +ENTRYPOINT ["/go/bin/ockafka"] diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Makefile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Makefile new file mode 100644 index 0000000..4ef2468 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/Makefile @@ -0,0 +1,58 @@ +# Copyright (C) 2015 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the COPYING file. + +GO := go +TEST_TIMEOUT := 30s +GOTEST_FLAGS := + +DEFAULT_GOPATH := $${GOPATH%%:*} +GOPATH_BIN := $(DEFAULT_GOPATH)/bin +GOPATH_PKG := $(DEFAULT_GOPATH)/pkg +GOLINT := $(GOPATH_BIN)/golint +GOFOLDERS := find . -type d ! -path "./.git/*" + +all: install + +install: + $(GO) install ./... + +check: vet test fmtcheck lint + +COVER_PKGS := key test +COVER_MODE := count +coverdata: + echo 'mode: $(COVER_MODE)' >coverage.out + for dir in $(COVER_PKGS); do \ + $(GO) test -covermode=$(COVER_MODE) -coverprofile=cov.out-t ./$$dir || exit; \ + tail -n +2 cov.out-t >> coverage.out && \ + rm cov.out-t; \ + done; + +coverage: coverdata + $(GO) tool cover -html=coverage.out + rm -f coverage.out + +fmtcheck: + errors=`gofmt -l .`; if test -n "$$errors"; then echo Check these files for style errors:; echo "$$errors"; exit 1; fi + find . -name '*.go' ! -name '*.pb.go' -exec ./check_line_len.awk {} + + +vet: + $(GO) vet ./... + +lint: + lint=`$(GOFOLDERS) | xargs -L 1 $(GOLINT) | fgrep -v .pb.go`; if test -n "$$lint"; then echo "$$lint"; exit 1; fi +# The above is ugly, but unfortunately golint doesn't exit 1 when it finds +# lint. See https://github.com/golang/lint/issues/65 + +test: + $(GO) test $(GOTEST_FLAGS) -timeout=$(TEST_TIMEOUT) ./... + +docker: + docker build -f cmd/occlient/Dockerfile . + +clean: + rm -rf $(GOPATH_PKG)/*/github.com/aristanetworks/goarista + $(GO) clean ./... + +.PHONY: all check coverage coverdata docker fmtcheck install lint test vet diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/README.md new file mode 100644 index 0000000..316d711 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/README.md @@ -0,0 +1,62 @@ +# Arista Go library [![Build Status](https://travis-ci.org/aristanetworks/goarista.svg?branch=master)](https://travis-ci.org/aristanetworks/goarista) [![codecov.io](http://codecov.io/github/aristanetworks/goarista/coverage.svg?branch=master)](http://codecov.io/github/aristanetworks/goarista?branch=master) [![GoDoc](https://godoc.org/github.com/aristanetworks/goarista?status.png)](https://godoc.org/github.com/aristanetworks/goarista) [![Go Report Card](https://goreportcard.com/badge/github.com/aristanetworks/goarista)](https://goreportcard.com/report/github.com/aristanetworks/goarista) + +## areflect + +Helper functions to work with the `reflect` package. Contains +`ForceExport()`, which bypasses the check in `reflect.Value` that +prevents accessing unexported attributes. + +## monotime + +Provides access to a fast monotonic clock source, to fill in the gap in the +[Go standard library, which lacks one](https://github.com/golang/go/issues/12914). +Don't use `time.Now()` in code that needs to time things or otherwise assume +that time passes at a constant rate, instead use `monotime.Now()`. + +## cmd + +See the [cmd](cmd) directory. + +## dscp + +Provides `ListenTCPWithTOS()`, which is a replacement for `net.ListenTCP()` +that allows specifying the ToS (Type of Service), to specify DSCP / ECN / +class of service flags to use for incoming connections. + +## key + +Provides a common type used across various Arista projects, named `key.Key`, +which is used to work around the fact that Go can't let one +use a non-hashable type as a key to a `map`, and we sometimes need to use +a `map[string]interface{}` (or something containing one) as a key to maps. +As a result, we frequently use `map[key.Key]interface{}` instead of just +`map[interface{}]interface{}` when we need a generic key-value collection. + +## lanz +A client for [LANZ](https://eos.arista.com/latency-analyzer-lanz-architectures-and-configuration/) +streaming servers. It connects to a LANZ streaming server, +listens for notifications, decodes them and sends the LANZ protobuf on the +provided channel. + +## monitor + +A library to help expose monitoring metrics on top of the +[`expvar`](https://golang.org/pkg/expvar/) infrastructure. + +## netns + +`netns.Do(namespace, cb)` provides a handy mechanism to execute the given +callback `cb` in the given [network namespace](https://lwn.net/Articles/580893/). + +## pathmap + +A datastructure for mapping keys of type string slice to values. It +allows for some fuzzy matching. + +## test + +This is a [Go](http://golang.org/) library to help in writing unit tests. + +## Examples + +TBD diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/check_line_len.awk b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/check_line_len.awk new file mode 100755 index 0000000..a9db535 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/check_line_len.awk @@ -0,0 +1,25 @@ +#!/usr/bin/awk -f +# Copyright (C) 2015 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the COPYING file. + +BEGIN { + max = 100; +} + +# Expand tabs to 4 spaces. +{ + gsub(/\t/, " "); +} + +length() > max { + errors++; + print FILENAME ":" FNR ": Line too long (" length() "/" max ")"; +} + +END { + if (errors >= 125) { + errors = 125; + } + exit errors; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/iptables.sh b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/iptables.sh new file mode 100755 index 0000000..f118ff4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/iptables.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +DEFAULT_PORT=6042 + +set -e + +if [ "$#" -lt 1 ] +then + echo "usage: $0 []" + exit 1 +fi + +host=$1 +port=$DEFAULT_PORT +if [ "$#" -gt 1 ] +then + port=$2 +fi +iptables="bash sudo iptables -A INPUT -p tcp --dport $port -j ACCEPT" +ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $host "$iptables" +echo "opened TCP port $port on $host" diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s new file mode 100644 index 0000000..66109f4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/issue15006.s @@ -0,0 +1,6 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// This file is intentionally empty. +// It's a workaround for https://github.com/golang/go/issues/15006 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go new file mode 100644 index 0000000..5f5fbc7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/monotime/nanotime.go @@ -0,0 +1,31 @@ +// Copyright (C) 2016 Arista Networks, Inc. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the COPYING file. + +// Package monotime provides a fast monotonic clock source. +package monotime + +import ( + "time" + _ "unsafe" // required to use //go:linkname +) + +//go:noescape +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +// Now returns the current time in nanoseconds from a monotonic clock. +// The time returned is based on some arbitrary platform-specific point in the +// past. The time returned is guaranteed to increase monotonically at a +// constant rate, unlike time.Now() from the Go standard library, which may +// slow down, speed up, jump forward or backward, due to NTP activity or leap +// seconds. +func Now() uint64 { + return uint64(nanotime()) +} + +// Since returns the amount of time that has elapsed since t. t should be +// the result of a call to Now() on the same machine. +func Since(t uint64) time.Duration { + return time.Duration(Now() - t) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/rpmbuild.sh b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/rpmbuild.sh new file mode 100755 index 0000000..52b691b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/aristanetworks/goarista/rpmbuild.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Copyright (C) 2016 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the COPYING file. + +if [ "$#" -lt 1 ] +then + echo "usage: $0 " + exit 1 +fi +binary=$1 + +if [ -z "$GOPATH" ] || [ -z "$GOOS" ] || [ -z "$GOARCH" ] +then + echo "Please set \$GOPATH, \$GOOS and \$GOARCH" + exit 1 +fi + +set -e + +version=$(git rev-parse --short=7 HEAD) +pwd=$(pwd) +cd $GOPATH/bin +if [ -d $GOOS_$GOARCH ] +then + cd $GOOS_GOARCH +fi +os=$GOOS +arch=$GOARCH +if [ "$arch" == "386" ] +then + arch="i686" +fi +cmd="fpm -n $binary -v $version -s dir -t rpm --rpm-os $os -a $arch --epoch 0 --prefix /usr/bin $binary" +echo $cmd +$cmd +mv $binary-$version-1.$arch.rpm $pwd diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/LICENSE new file mode 100644 index 0000000..53ba0c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2013-2017 The btcsuite developers +Copyright (c) 2015-2016 The Decred developers + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/README.md new file mode 100644 index 0000000..5875dfc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/README.md @@ -0,0 +1,74 @@ +btcec +===== + +[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)] +(https://travis-ci.org/btcsuite/btcec) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![GoDoc](https://godoc.org/github.com/btcsuite/btcd/btcec?status.png)] +(http://godoc.org/github.com/btcsuite/btcd/btcec) + +Package btcec implements elliptic curve cryptography needed for working with +Bitcoin (secp256k1 only for now). It is designed so that it may be used with the +standard crypto/ecdsa packages provided with go. A comprehensive suite of test +is provided to ensure proper functionality. Package btcec was originally based +on work from ThePiachu which is licensed under the same terms as Go, but it has +signficantly diverged since then. The btcsuite developers original is licensed +under the liberal ISC license. + +Although this package was primarily written for btcd, it has intentionally been +designed so it can be used as a standalone package for any projects needing to +use secp256k1 elliptic curve cryptography. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcd/btcec +``` + +## Examples + +* [Sign Message] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage) + Demonstrates signing a message with a secp256k1 private key that is first + parsed form raw bytes and serializing the generated signature. + +* [Verify Signature] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature) + Demonstrates verifying a secp256k1 signature against a public key that is + first parsed from raw bytes. The signature is also parsed from raw bytes. + +* [Encryption] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage) + Demonstrates encrypting a message for a public key that is first parsed from + raw bytes, then decrypting it using the corresponding private key. + +* [Decryption] + (http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage) + Demonstrates decrypting a message using a private key that is first parsed + from raw bytes. + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from the btcsuite developers. To +verify the signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package btcec is licensed under the [copyfree](http://copyfree.org) ISC License +except for btcec.go and btcec_test.go which is under the same license as Go. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/btcec.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/btcec.go new file mode 100644 index 0000000..98d7b14 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/btcec.go @@ -0,0 +1,956 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// References: +// [SECG]: Recommended Elliptic Curve Domain Parameters +// http://www.secg.org/sec2-v2.pdf +// +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +// This package operates, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1) +// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole +// calculation can be performed within the transform (as in ScalarMult and +// ScalarBaseMult). But even for Add and Double, it's faster to apply and +// reverse the transform than to operate in affine coordinates. + +import ( + "crypto/elliptic" + "math/big" + "sync" +) + +var ( + // fieldOne is simply the integer 1 in field representation. It is + // used to avoid needing to create it multiple times during the internal + // arithmetic. + fieldOne = new(fieldVal).SetInt(1) +) + +// KoblitzCurve supports a koblitz curve implementation that fits the ECC Curve +// interface from crypto/elliptic. +type KoblitzCurve struct { + *elliptic.CurveParams + q *big.Int + H int // cofactor of the curve. + + // byteSize is simply the bit size / 8 and is provided for convenience + // since it is calculated repeatedly. + byteSize int + + // bytePoints + bytePoints *[32][256][3]fieldVal + + // The next 6 values are used specifically for endomorphism + // optimizations in ScalarMult. + + // lambda must fulfill lambda^3 = 1 mod N where N is the order of G. + lambda *big.Int + + // beta must fulfill beta^3 = 1 mod P where P is the prime field of the + // curve. + beta *fieldVal + + // See the EndomorphismVectors in gensecp256k1.go to see how these are + // derived. + a1 *big.Int + b1 *big.Int + a2 *big.Int + b2 *big.Int +} + +// Params returns the parameters for the curve. +func (curve *KoblitzCurve) Params() *elliptic.CurveParams { + return curve.CurveParams +} + +// bigAffineToField takes an affine point (x, y) as big integers and converts +// it to an affine point as field values. +func (curve *KoblitzCurve) bigAffineToField(x, y *big.Int) (*fieldVal, *fieldVal) { + x3, y3 := new(fieldVal), new(fieldVal) + x3.SetByteSlice(x.Bytes()) + y3.SetByteSlice(y.Bytes()) + + return x3, y3 +} + +// fieldJacobianToBigAffine takes a Jacobian point (x, y, z) as field values and +// converts it to an affine point as big integers. +func (curve *KoblitzCurve) fieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) { + // Inversions are expensive and both point addition and point doubling + // are faster when working with points that have a z value of one. So, + // if the point needs to be converted to affine, go ahead and normalize + // the point itself at the same time as the calculation is the same. + var zInv, tempZ fieldVal + zInv.Set(z).Inverse() // zInv = Z^-1 + tempZ.SquareVal(&zInv) // tempZ = Z^-2 + x.Mul(&tempZ) // X = X/Z^2 (mag: 1) + y.Mul(tempZ.Mul(&zInv)) // Y = Y/Z^3 (mag: 1) + z.SetInt(1) // Z = 1 (mag: 1) + + // Normalize the x and y values. + x.Normalize() + y.Normalize() + + // Convert the field values for the now affine point to big.Ints. + x3, y3 := new(big.Int), new(big.Int) + x3.SetBytes(x.Bytes()[:]) + y3.SetBytes(y.Bytes()[:]) + return x3, y3 +} + +// IsOnCurve returns boolean if the point (x,y) is on the curve. +// Part of the elliptic.Curve interface. This function differs from the +// crypto/elliptic algorithm since a = 0 not -3. +func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool { + // Convert big ints to field values for faster arithmetic. + fx, fy := curve.bigAffineToField(x, y) + + // Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7 + y2 := new(fieldVal).SquareVal(fy).Normalize() + result := new(fieldVal).SquareVal(fx).Mul(fx).AddInt(7).Normalize() + return y2.Equals(result) +} + +// addZ1AndZ2EqualsOne adds two Jacobian points that are already known to have +// z values of 1 and stores the result in (x3, y3, z3). That is to say +// (x1, y1, 1) + (x2, y2, 1) = (x3, y3, z3). It performs faster addition than +// the generic add routine since less arithmetic is needed due to the ability to +// avoid the z value multiplications. +func (curve *KoblitzCurve) addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl + // + // In particular it performs the calculations using the following: + // H = X2-X1, HH = H^2, I = 4*HH, J = H*I, r = 2*(Y2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = 2*H + // + // This results in a cost of 4 field multiplications, 2 field squarings, + // 6 field additions, and 5 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + x1.Normalize() + y1.Normalize() + x2.Normalize() + y2.Normalize() + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, v fieldVal + var negJ, neg2V, negX3 fieldVal + h.Set(x1).Negate(1).Add(x2) // H = X2-X1 (mag: 3) + i.SquareVal(&h).MulInt(4) // I = 4*H^2 (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + r.Set(y1).Negate(1).Add(y2).MulInt(2) // r = 2*(Y2-Y1) (mag: 6) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + negJ.Set(&j).Negate(1) // negJ = -J (mag: 2) + neg2V.Set(&v).MulInt(2).Negate(2) // neg2V = -(2*V) (mag: 3) + x3.Set(&r).Square().Add(&negJ).Add(&neg2V) // X3 = r^2-J-2*V (mag: 6) + negX3.Set(x3).Negate(6) // negX3 = -X3 (mag: 7) + j.Mul(y1).MulInt(2).Negate(2) // J = -(2*Y1*J) (mag: 3) + y3.Set(&v).Add(&negX3).Mul(&r).Add(&j) // Y3 = r*(V-X3)-2*Y1*J (mag: 4) + z3.Set(&h).MulInt(2) // Z3 = 2*H (mag: 6) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addZ1EqualsZ2 adds two Jacobian points that are already known to have the +// same z value and stores the result in (x3, y3, z3). That is to say +// (x1, y1, z1) + (x2, y2, z1) = (x3, y3, z3). It performs faster addition than +// the generic add routine since less arithmetic is needed due to the known +// equivalence. +func (curve *KoblitzCurve) addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using a slightly modified version + // of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl + // + // In particular it performs the calculations using the following: + // A = X2-X1, B = A^2, C=Y2-Y1, D = C^2, E = X1*B, F = X2*B + // X3 = D-E-F, Y3 = C*(E-X3)-Y1*(F-E), Z3 = Z1*A + // + // This results in a cost of 5 field multiplications, 2 field squarings, + // 9 field additions, and 0 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. + x1.Normalize() + y1.Normalize() + x2.Normalize() + y2.Normalize() + if x1.Equals(x2) { + if y1.Equals(y2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var a, b, c, d, e, f fieldVal + var negX1, negY1, negE, negX3 fieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + a.Set(&negX1).Add(x2) // A = X2-X1 (mag: 3) + b.SquareVal(&a) // B = A^2 (mag: 1) + c.Set(&negY1).Add(y2) // C = Y2-Y1 (mag: 3) + d.SquareVal(&c) // D = C^2 (mag: 1) + e.Mul2(x1, &b) // E = X1*B (mag: 1) + negE.Set(&e).Negate(1) // negE = -E (mag: 2) + f.Mul2(x2, &b) // F = X2*B (mag: 1) + x3.Add2(&e, &f).Negate(3).Add(&d) // X3 = D-E-F (mag: 5) + negX3.Set(x3).Negate(5).Normalize() // negX3 = -X3 (mag: 1) + y3.Set(y1).Mul(f.Add(&negE)).Negate(3) // Y3 = -(Y1*(F-E)) (mag: 4) + y3.Add(e.Add(&negX3).Mul(&c)) // Y3 = C*(E-X3)+Y3 (mag: 5) + z3.Mul2(z1, &a) // Z3 = Z1*A (mag: 1) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() +} + +// addZ2EqualsOne adds two Jacobian points when the second point is already +// known to have a z value of 1 (and the z value for the first point is not 1) +// and stores the result in (x3, y3, z3). That is to say (x1, y1, z1) + +// (x2, y2, 1) = (x3, y3, z3). It performs faster addition than the generic +// add routine since less arithmetic is needed due to the ability to avoid +// multiplications by the second point's z value. +func (curve *KoblitzCurve) addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, U2 = X2*Z1Z1, S2 = Y2*Z1*Z1Z1, H = U2-X1, HH = H^2, + // I = 4*HH, J = H*I, r = 2*(S2-Y1), V = X1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = (Z1+H)^2-Z1Z1-HH + // + // This results in a cost of 7 field multiplications, 4 field squarings, + // 9 field additions, and 4 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity per the group law for elliptic curve cryptography. Since + // any number of Jacobian coordinates can represent the same affine + // point, the x and y values need to be converted to like terms. Due to + // the assumption made for this function that the second point has a z + // value of 1 (z2=1), the first point is already "converted". + var z1z1, u2, s2 fieldVal + x1.Normalize() + y1.Normalize() + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if x1.Equals(&u2) { + if y1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, hh, i, j, r, rr, v fieldVal + var negX1, negY1, negX3 fieldVal + negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2) + h.Add2(&u2, &negX1) // H = U2-X1 (mag: 3) + hh.SquareVal(&h) // HH = H^2 (mag: 1) + i.Set(&hh).MulInt(4) // I = 4 * HH (mag: 4) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2) + r.Set(&s2).Add(&negY1).MulInt(2) // r = 2*(S2-Y1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(x1, &i) // V = X1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Set(y1).Mul(&j).MulInt(2).Negate(2) // Y3 = -(2*Y1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, &h).Square() // Z3 = (Z1+H)^2 (mag: 1) + z3.Add(z1z1.Add(&hh).Negate(2)) // Z3 = Z3-(Z1Z1+HH) (mag: 4) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// addGeneric adds two Jacobian points (x1, y1, z1) and (x2, y2, z2) without any +// assumptions about the z values of the two points and stores the result in +// (x3, y3, z3). That is to say (x1, y1, z1) + (x2, y2, z2) = (x3, y3, z3). It +// is the slowest of the add routines due to requiring the most arithmetic. +func (curve *KoblitzCurve) addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + // To compute the point addition efficiently, this implementation splits + // the equation into intermediate elements which are used to minimize + // the number of field multiplications using the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + // + // In particular it performs the calculations using the following: + // Z1Z1 = Z1^2, Z2Z2 = Z2^2, U1 = X1*Z2Z2, U2 = X2*Z1Z1, S1 = Y1*Z2*Z2Z2 + // S2 = Y2*Z1*Z1Z1, H = U2-U1, I = (2*H)^2, J = H*I, r = 2*(S2-S1) + // V = U1*I + // X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*S1*J, Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H + // + // This results in a cost of 11 field multiplications, 5 field squarings, + // 9 field additions, and 4 integer multiplications. + + // When the x coordinates are the same for two points on the curve, the + // y coordinates either must be the same, in which case it is point + // doubling, or they are opposite and the result is the point at + // infinity. Since any number of Jacobian coordinates can represent the + // same affine point, the x and y values need to be converted to like + // terms. + var z1z1, z2z2, u1, u2, s1, s2 fieldVal + z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1) + z2z2.SquareVal(z2) // Z2Z2 = Z2^2 (mag: 1) + u1.Set(x1).Mul(&z2z2).Normalize() // U1 = X1*Z2Z2 (mag: 1) + u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1) + s1.Set(y1).Mul(&z2z2).Mul(z2).Normalize() // S1 = Y1*Z2*Z2Z2 (mag: 1) + s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1) + if u1.Equals(&u2) { + if s1.Equals(&s2) { + // Since x1 == x2 and y1 == y2, point doubling must be + // done, otherwise the addition would end up dividing + // by zero. + curve.doubleJacobian(x1, y1, z1, x3, y3, z3) + return + } + + // Since x1 == x2 and y1 == -y2, the sum is the point at + // infinity per the group law. + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Calculate X3, Y3, and Z3 according to the intermediate elements + // breakdown above. + var h, i, j, r, rr, v fieldVal + var negU1, negS1, negX3 fieldVal + negU1.Set(&u1).Negate(1) // negU1 = -U1 (mag: 2) + h.Add2(&u2, &negU1) // H = U2-U1 (mag: 3) + i.Set(&h).MulInt(2).Square() // I = (2*H)^2 (mag: 2) + j.Mul2(&h, &i) // J = H*I (mag: 1) + negS1.Set(&s1).Negate(1) // negS1 = -S1 (mag: 2) + r.Set(&s2).Add(&negS1).MulInt(2) // r = 2*(S2-S1) (mag: 6) + rr.SquareVal(&r) // rr = r^2 (mag: 1) + v.Mul2(&u1, &i) // V = U1*I (mag: 1) + x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4) + x3.Add(&rr) // X3 = r^2+X3 (mag: 5) + negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6) + y3.Mul2(&s1, &j).MulInt(2).Negate(2) // Y3 = -(2*S1*J) (mag: 3) + y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4) + z3.Add2(z1, z2).Square() // Z3 = (Z1+Z2)^2 (mag: 1) + z3.Add(z1z1.Add(&z2z2).Negate(2)) // Z3 = Z3-(Z1Z1+Z2Z2) (mag: 4) + z3.Mul(&h) // Z3 = Z3*H (mag: 1) + + // Normalize the resulting field values to a magnitude of 1 as needed. + x3.Normalize() + y3.Normalize() +} + +// addJacobian adds the passed Jacobian points (x1, y1, z1) and (x2, y2, z2) +// together and stores the result in (x3, y3, z3). +func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) { + // A point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if (x1.IsZero() && y1.IsZero()) || z1.IsZero() { + x3.Set(x2) + y3.Set(y2) + z3.Set(z2) + return + } + if (x2.IsZero() && y2.IsZero()) || z2.IsZero() { + x3.Set(x1) + y3.Set(y1) + z3.Set(z1) + return + } + + // Faster point addition can be achieved when certain assumptions are + // met. For example, when both points have the same z value, arithmetic + // on the z values can be avoided. This section thus checks for these + // conditions and calls an appropriate add function which is accelerated + // by using those assumptions. + z1.Normalize() + z2.Normalize() + isZ1One := z1.Equals(fieldOne) + isZ2One := z2.Equals(fieldOne) + switch { + case isZ1One && isZ2One: + curve.addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3) + return + case z1.Equals(z2): + curve.addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3) + return + case isZ2One: + curve.addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3) + return + } + + // None of the above assumptions are true, so fall back to generic + // point addition. + curve.addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3) +} + +// Add returns the sum of (x1,y1) and (x2,y2). Part of the elliptic.Curve +// interface. +func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + // A point at infinity is the identity according to the group law for + // elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P. + if x1.Sign() == 0 && y1.Sign() == 0 { + return x2, y2 + } + if x2.Sign() == 0 && y2.Sign() == 0 { + return x1, y1 + } + + // Convert the affine coordinates from big integers to field values + // and do the point addition in Jacobian projective space. + fx1, fy1 := curve.bigAffineToField(x1, y1) + fx2, fy2 := curve.bigAffineToField(x2, y2) + fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal) + fOne := new(fieldVal).SetInt(1) + curve.addJacobian(fx1, fy1, fOne, fx2, fy2, fOne, fx3, fy3, fz3) + + // Convert the Jacobian coordinate field values back to affine big + // integers. + return curve.fieldJacobianToBigAffine(fx3, fy3, fz3) +} + +// doubleZ1EqualsOne performs point doubling on the passed Jacobian point +// when the point is already known to have a z value of 1 and stores +// the result in (x3, y3, z3). That is to say (x3, y3, z3) = 2*(x1, y1, 1). It +// performs faster point doubling than the generic routine since less arithmetic +// is needed due to the ability to avoid multiplication by the z value. +func (curve *KoblitzCurve) doubleZ1EqualsOne(x1, y1, x3, y3, z3 *fieldVal) { + // This function uses the assumptions that z1 is 1, thus the point + // doubling formulas reduce to: + // + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + var a, b, c, d, e, f fieldVal + z3.Set(y1).MulInt(2) // Z3 = 2*Y1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + e.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&e) // F = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = F+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10) + + // Normalize the field values back to a magnitude of 1. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// doubleGeneric performs point doubling on the passed Jacobian point without +// any assumptions about the z value and stores the result in (x3, y3, z3). +// That is to say (x3, y3, z3) = 2*(x1, y1, z1). It is the slowest of the point +// doubling routines due to requiring the most arithmetic. +func (curve *KoblitzCurve) doubleGeneric(x1, y1, z1, x3, y3, z3 *fieldVal) { + // Point doubling formula for Jacobian coordinates for the secp256k1 + // curve: + // X3 = (3*X1^2)^2 - 8*X1*Y1^2 + // Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4 + // Z3 = 2*Y1*Z1 + // + // To compute the above efficiently, this implementation splits the + // equation into intermediate elements which are used to minimize the + // number of field multiplications in favor of field squarings which + // are roughly 35% faster than field multiplications with the current + // implementation at the time this was written. + // + // This uses a slightly modified version of the method shown at: + // http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // + // In particular it performs the calculations using the following: + // A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C) + // E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C + // Z3 = 2*Y1*Z1 + // + // This results in a cost of 1 field multiplication, 5 field squarings, + // 6 field additions, and 5 integer multiplications. + var a, b, c, d, e, f fieldVal + z3.Mul2(y1, z1).MulInt(2) // Z3 = 2*Y1*Z1 (mag: 2) + a.SquareVal(x1) // A = X1^2 (mag: 1) + b.SquareVal(y1) // B = Y1^2 (mag: 1) + c.SquareVal(&b) // C = B^2 (mag: 1) + b.Add(x1).Square() // B = (X1+B)^2 (mag: 1) + d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3) + d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8) + e.Set(&a).MulInt(3) // E = 3*A (mag: 3) + f.SquareVal(&e) // F = E^2 (mag: 1) + x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17) + x3.Add(&f) // X3 = F+X3 (mag: 18) + f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1) + y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9) + y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10) + + // Normalize the field values back to a magnitude of 1. + x3.Normalize() + y3.Normalize() + z3.Normalize() +} + +// doubleJacobian doubles the passed Jacobian point (x1, y1, z1) and stores the +// result in (x3, y3, z3). +func (curve *KoblitzCurve) doubleJacobian(x1, y1, z1, x3, y3, z3 *fieldVal) { + // Doubling a point at infinity is still infinity. + if y1.IsZero() || z1.IsZero() { + x3.SetInt(0) + y3.SetInt(0) + z3.SetInt(0) + return + } + + // Slightly faster point doubling can be achieved when the z value is 1 + // by avoiding the multiplication on the z value. This section calls + // a point doubling function which is accelerated by using that + // assumption when possible. + if z1.Normalize().Equals(fieldOne) { + curve.doubleZ1EqualsOne(x1, y1, x3, y3, z3) + return + } + + // Fall back to generic point doubling which works with arbitrary z + // values. + curve.doubleGeneric(x1, y1, z1, x3, y3, z3) +} + +// Double returns 2*(x1,y1). Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + if y1.Sign() == 0 { + return new(big.Int), new(big.Int) + } + + // Convert the affine coordinates from big integers to field values + // and do the point doubling in Jacobian projective space. + fx1, fy1 := curve.bigAffineToField(x1, y1) + fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal) + fOne := new(fieldVal).SetInt(1) + curve.doubleJacobian(fx1, fy1, fOne, fx3, fy3, fz3) + + // Convert the Jacobian coordinate field values back to affine big + // integers. + return curve.fieldJacobianToBigAffine(fx3, fy3, fz3) +} + +// splitK returns a balanced length-two representation of k and their signs. +// This is algorithm 3.74 from [GECC]. +// +// One thing of note about this algorithm is that no matter what c1 and c2 are, +// the final equation of k = k1 + k2 * lambda (mod n) will hold. This is +// provable mathematically due to how a1/b1/a2/b2 are computed. +// +// c1 and c2 are chosen to minimize the max(k1,k2). +func (curve *KoblitzCurve) splitK(k []byte) ([]byte, []byte, int, int) { + // All math here is done with big.Int, which is slow. + // At some point, it might be useful to write something similar to + // fieldVal but for N instead of P as the prime field if this ends up + // being a bottleneck. + bigIntK := new(big.Int) + c1, c2 := new(big.Int), new(big.Int) + tmp1, tmp2 := new(big.Int), new(big.Int) + k1, k2 := new(big.Int), new(big.Int) + + bigIntK.SetBytes(k) + // c1 = round(b2 * k / n) from step 4. + // Rounding isn't really necessary and costs too much, hence skipped + c1.Mul(curve.b2, bigIntK) + c1.Div(c1, curve.N) + // c2 = round(b1 * k / n) from step 4 (sign reversed to optimize one step) + // Rounding isn't really necessary and costs too much, hence skipped + c2.Mul(curve.b1, bigIntK) + c2.Div(c2, curve.N) + // k1 = k - c1 * a1 - c2 * a2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, curve.a1) + tmp2.Mul(c2, curve.a2) + k1.Sub(bigIntK, tmp1) + k1.Add(k1, tmp2) + // k2 = - c1 * b1 - c2 * b2 from step 5 (note c2's sign is reversed) + tmp1.Mul(c1, curve.b1) + tmp2.Mul(c2, curve.b2) + k2.Sub(tmp2, tmp1) + + // Note Bytes() throws out the sign of k1 and k2. This matters + // since k1 and/or k2 can be negative. Hence, we pass that + // back separately. + return k1.Bytes(), k2.Bytes(), k1.Sign(), k2.Sign() +} + +// moduloReduce reduces k from more than 32 bytes to 32 bytes and under. This +// is done by doing a simple modulo curve.N. We can do this since G^N = 1 and +// thus any other valid point on the elliptic curve has the same order. +func (curve *KoblitzCurve) moduloReduce(k []byte) []byte { + // Since the order of G is curve.N, we can use a much smaller number + // by doing modulo curve.N + if len(k) > curve.byteSize { + // Reduce k by performing modulo curve.N. + tmpK := new(big.Int).SetBytes(k) + tmpK.Mod(tmpK, curve.N) + return tmpK.Bytes() + } + + return k +} + +// NAF takes a positive integer k and returns the Non-Adjacent Form (NAF) as two +// byte slices. The first is where 1s will be. The second is where -1s will +// be. NAF is convenient in that on average, only 1/3rd of its values are +// non-zero. This is algorithm 3.30 from [GECC]. +// +// Essentially, this makes it possible to minimize the number of operations +// since the resulting ints returned will be at least 50% 0s. +func NAF(k []byte) ([]byte, []byte) { + // The essence of this algorithm is that whenever we have consecutive 1s + // in the binary, we want to put a -1 in the lowest bit and get a bunch + // of 0s up to the highest bit of consecutive 1s. This is due to this + // identity: + // 2^n + 2^(n-1) + 2^(n-2) + ... + 2^(n-k) = 2^(n+1) - 2^(n-k) + // + // The algorithm thus may need to go 1 more bit than the length of the + // bits we actually have, hence bits being 1 bit longer than was + // necessary. Since we need to know whether adding will cause a carry, + // we go from right-to-left in this addition. + var carry, curIsOne, nextIsOne bool + // these default to zero + retPos := make([]byte, len(k)+1) + retNeg := make([]byte, len(k)+1) + for i := len(k) - 1; i >= 0; i-- { + curByte := k[i] + for j := uint(0); j < 8; j++ { + curIsOne = curByte&1 == 1 + if j == 7 { + if i == 0 { + nextIsOne = false + } else { + nextIsOne = k[i-1]&1 == 1 + } + } else { + nextIsOne = curByte&2 == 2 + } + if carry { + if curIsOne { + // This bit is 1, so continue to carry + // and don't need to do anything. + } else { + // We've hit a 0 after some number of + // 1s. + if nextIsOne { + // Start carrying again since + // a new sequence of 1s is + // starting. + retNeg[i+1] += 1 << j + } else { + // Stop carrying since 1s have + // stopped. + carry = false + retPos[i+1] += 1 << j + } + } + } else if curIsOne { + if nextIsOne { + // If this is the start of at least 2 + // consecutive 1s, set the current one + // to -1 and start carrying. + retNeg[i+1] += 1 << j + carry = true + } else { + // This is a singleton, not consecutive + // 1s. + retPos[i+1] += 1 << j + } + } + curByte >>= 1 + } + } + if carry { + retPos[0] = 1 + } + + return retPos, retNeg +} + +// ScalarMult returns k*(Bx, By) where k is a big endian integer. +// Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { + // Point Q = ∞ (point at infinity). + qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal) + + // Decompose K into k1 and k2 in order to halve the number of EC ops. + // See Algorithm 3.74 in [GECC]. + k1, k2, signK1, signK2 := curve.splitK(curve.moduloReduce(k)) + + // The main equation here to remember is: + // k * P = k1 * P + k2 * ϕ(P) + // + // P1 below is P in the equation, P2 below is ϕ(P) in the equation + p1x, p1y := curve.bigAffineToField(Bx, By) + p1yNeg := new(fieldVal).NegateVal(p1y, 1) + p1z := new(fieldVal).SetInt(1) + + // NOTE: ϕ(x,y) = (βx,y). The Jacobian z coordinate is 1, so this math + // goes through. + p2x := new(fieldVal).Mul2(p1x, curve.beta) + p2y := new(fieldVal).Set(p1y) + p2yNeg := new(fieldVal).NegateVal(p2y, 1) + p2z := new(fieldVal).SetInt(1) + + // Flip the positive and negative values of the points as needed + // depending on the signs of k1 and k2. As mentioned in the equation + // above, each of k1 and k2 are multiplied by the respective point. + // Since -k * P is the same thing as k * -P, and the group law for + // elliptic curves states that P(x, y) = -P(x, -y), it's faster and + // simplifies the code to just make the point negative. + if signK1 == -1 { + p1y, p1yNeg = p1yNeg, p1y + } + if signK2 == -1 { + p2y, p2yNeg = p2yNeg, p2y + } + + // NAF versions of k1 and k2 should have a lot more zeros. + // + // The Pos version of the bytes contain the +1s and the Neg versions + // contain the -1s. + k1PosNAF, k1NegNAF := NAF(k1) + k2PosNAF, k2NegNAF := NAF(k2) + k1Len := len(k1PosNAF) + k2Len := len(k2PosNAF) + + m := k1Len + if m < k2Len { + m = k2Len + } + + // Add left-to-right using the NAF optimization. See algorithm 3.77 + // from [GECC]. This should be faster overall since there will be a lot + // more instances of 0, hence reducing the number of Jacobian additions + // at the cost of 1 possible extra doubling. + var k1BytePos, k1ByteNeg, k2BytePos, k2ByteNeg byte + for i := 0; i < m; i++ { + // Since we're going left-to-right, pad the front with 0s. + if i < m-k1Len { + k1BytePos = 0 + k1ByteNeg = 0 + } else { + k1BytePos = k1PosNAF[i-(m-k1Len)] + k1ByteNeg = k1NegNAF[i-(m-k1Len)] + } + if i < m-k2Len { + k2BytePos = 0 + k2ByteNeg = 0 + } else { + k2BytePos = k2PosNAF[i-(m-k2Len)] + k2ByteNeg = k2NegNAF[i-(m-k2Len)] + } + + for j := 7; j >= 0; j-- { + // Q = 2 * Q + curve.doubleJacobian(qx, qy, qz, qx, qy, qz) + + if k1BytePos&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p1x, p1y, p1z, + qx, qy, qz) + } else if k1ByteNeg&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p1x, p1yNeg, p1z, + qx, qy, qz) + } + + if k2BytePos&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p2x, p2y, p2z, + qx, qy, qz) + } else if k2ByteNeg&0x80 == 0x80 { + curve.addJacobian(qx, qy, qz, p2x, p2yNeg, p2z, + qx, qy, qz) + } + k1BytePos <<= 1 + k1ByteNeg <<= 1 + k2BytePos <<= 1 + k2ByteNeg <<= 1 + } + } + + // Convert the Jacobian coordinate field values back to affine big.Ints. + return curve.fieldJacobianToBigAffine(qx, qy, qz) +} + +// ScalarBaseMult returns k*G where G is the base point of the group and k is a +// big endian integer. +// Part of the elliptic.Curve interface. +func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + newK := curve.moduloReduce(k) + diff := len(curve.bytePoints) - len(newK) + + // Point Q = ∞ (point at infinity). + qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal) + + // curve.bytePoints has all 256 byte points for each 8-bit window. The + // strategy is to add up the byte points. This is best understood by + // expressing k in base-256 which it already sort of is. + // Each "digit" in the 8-bit window can be looked up using bytePoints + // and added together. + for i, byteVal := range newK { + p := curve.bytePoints[diff+i][byteVal] + curve.addJacobian(qx, qy, qz, &p[0], &p[1], &p[2], qx, qy, qz) + } + return curve.fieldJacobianToBigAffine(qx, qy, qz) +} + +// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating +// square roots via exponention. +func (curve *KoblitzCurve) QPlus1Div4() *big.Int { + return curve.q +} + +var initonce sync.Once +var secp256k1 KoblitzCurve + +func initAll() { + initS256() +} + +// fromHex converts the passed hex string into a big integer pointer and will +// panic is there is an error. This is only provided for the hard-coded +// constants so errors in the source code can bet detected. It will only (and +// must only) be called for initialization purposes. +func fromHex(s string) *big.Int { + r, ok := new(big.Int).SetString(s, 16) + if !ok { + panic("invalid hex in source file: " + s) + } + return r +} + +func initS256() { + // Curve parameters taken from [SECG] section 2.4.1. + secp256k1.CurveParams = new(elliptic.CurveParams) + secp256k1.P = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F") + secp256k1.N = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141") + secp256k1.B = fromHex("0000000000000000000000000000000000000000000000000000000000000007") + secp256k1.Gx = fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798") + secp256k1.Gy = fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8") + secp256k1.BitSize = 256 + secp256k1.H = 1 + secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P, + big.NewInt(1)), big.NewInt(4)) + + // Provided for convenience since this gets computed repeatedly. + secp256k1.byteSize = secp256k1.BitSize / 8 + + // Deserialize and set the pre-computed table used to accelerate scalar + // base multiplication. This is hard-coded data, so any errors are + // panics because it means something is wrong in the source code. + if err := loadS256BytePoints(); err != nil { + panic(err) + } + + // Next 6 constants are from Hal Finney's bitcointalk.org post: + // https://bitcointalk.org/index.php?topic=3238.msg45565#msg45565 + // May he rest in peace. + // + // They have also been independently derived from the code in the + // EndomorphismVectors function in gensecp256k1.go. + secp256k1.lambda = fromHex("5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72") + secp256k1.beta = new(fieldVal).SetHex("7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE") + secp256k1.a1 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + secp256k1.b1 = fromHex("-E4437ED6010E88286F547FA90ABFE4C3") + secp256k1.a2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8") + secp256k1.b2 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + + // Alternatively, we can use the parameters below, however, they seem + // to be about 8% slower. + // secp256k1.lambda = fromHex("AC9C52B33FA3CF1F5AD9E3FD77ED9BA4A880B9FC8EC739C2E0CFC810B51283CE") + // secp256k1.beta = new(fieldVal).SetHex("851695D49A83F8EF919BB86153CBCB16630FB68AED0A766A3EC693D68E6AFA40") + // secp256k1.a1 = fromHex("E4437ED6010E88286F547FA90ABFE4C3") + // secp256k1.b1 = fromHex("-3086D221A7D46BCDE86C90E49284EB15") + // secp256k1.a2 = fromHex("3086D221A7D46BCDE86C90E49284EB15") + // secp256k1.b2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8") +} + +// S256 returns a Curve which implements secp256k1. +func S256() *KoblitzCurve { + initonce.Do(initAll) + return &secp256k1 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/ciphering.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/ciphering.go new file mode 100644 index 0000000..b18c9b7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/ciphering.go @@ -0,0 +1,216 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "errors" + "io" +) + +var ( + // ErrInvalidMAC occurs when Message Authentication Check (MAC) fails + // during decryption. This happens because of either invalid private key or + // corrupt ciphertext. + ErrInvalidMAC = errors.New("invalid mac hash") + + // errInputTooShort occurs when the input ciphertext to the Decrypt + // function is less than 134 bytes long. + errInputTooShort = errors.New("ciphertext too short") + + // errUnsupportedCurve occurs when the first two bytes of the encrypted + // text aren't 0x02CA (= 712 = secp256k1, from OpenSSL). + errUnsupportedCurve = errors.New("unsupported curve") + + errInvalidXLength = errors.New("invalid X length, must be 32") + errInvalidYLength = errors.New("invalid Y length, must be 32") + errInvalidPadding = errors.New("invalid PKCS#7 padding") + + // 0x02CA = 714 + ciphCurveBytes = [2]byte{0x02, 0xCA} + // 0x20 = 32 + ciphCoordLength = [2]byte{0x00, 0x20} +) + +// GenerateSharedSecret generates a shared secret based on a private key and a +// public key using Diffie-Hellman key exchange (ECDH) (RFC 4753). +// RFC5903 Section 9 states we should only return x. +func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte { + x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) + return x.Bytes() +} + +// Encrypt encrypts data for the target public key using AES-256-CBC. It also +// generates a private key (the pubkey of which is also in the output). The only +// supported curve is secp256k1. The `structure' that it encodes everything into +// is: +// +// struct { +// // Initialization Vector used for AES-256-CBC +// IV [16]byte +// // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX + +// // len_of_pubkeyY(2) + pubkeyY (curve = 714) +// PublicKey [70]byte +// // Cipher text +// Data []byte +// // HMAC-SHA-256 Message Authentication Code +// HMAC [32]byte +// } +// +// The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer +// to section 5.8.1 of ANSI X9.63 for rationale on this format. +func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) { + ephemeral, err := NewPrivateKey(S256()) + if err != nil { + return nil, err + } + ecdhKey := GenerateSharedSecret(ephemeral, pubkey) + derivedKey := sha512.Sum512(ecdhKey) + keyE := derivedKey[:32] + keyM := derivedKey[32:] + + paddedIn := addPKCSPadding(in) + // IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256 + out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size) + iv := out[:aes.BlockSize] + if _, err = io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + // start writing public key + pb := ephemeral.PubKey().SerializeUncompressed() + offset := aes.BlockSize + + // curve and X length + copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...)) + offset += 4 + // X + copy(out[offset:offset+32], pb[1:33]) + offset += 32 + // Y length + copy(out[offset:offset+2], ciphCoordLength[:]) + offset += 2 + // Y + copy(out[offset:offset+32], pb[33:]) + offset += 32 + + // start encryption + block, err := aes.NewCipher(keyE) + if err != nil { + return nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn) + + // start HMAC-SHA-256 + hm := hmac.New(sha256.New, keyM) + hm.Write(out[:len(out)-sha256.Size]) // everything is hashed + copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum + + return out, nil +} + +// Decrypt decrypts data that was encrypted using the Encrypt function. +func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) { + // IV + Curve params/X/Y + 1 block + HMAC-256 + if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size { + return nil, errInputTooShort + } + + // read iv + iv := in[:aes.BlockSize] + offset := aes.BlockSize + + // start reading pubkey + if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) { + return nil, errUnsupportedCurve + } + offset += 2 + + if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { + return nil, errInvalidXLength + } + offset += 2 + + xBytes := in[offset : offset+32] + offset += 32 + + if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { + return nil, errInvalidYLength + } + offset += 2 + + yBytes := in[offset : offset+32] + offset += 32 + + pb := make([]byte, 65) + pb[0] = byte(0x04) // uncompressed + copy(pb[1:33], xBytes) + copy(pb[33:], yBytes) + // check if (X, Y) lies on the curve and create a Pubkey if it does + pubkey, err := ParsePubKey(pb, S256()) + if err != nil { + return nil, err + } + + // check for cipher text length + if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 { + return nil, errInvalidPadding // not padded to 16 bytes + } + + // read hmac + messageMAC := in[len(in)-sha256.Size:] + + // generate shared secret + ecdhKey := GenerateSharedSecret(priv, pubkey) + derivedKey := sha512.Sum512(ecdhKey) + keyE := derivedKey[:32] + keyM := derivedKey[32:] + + // verify mac + hm := hmac.New(sha256.New, keyM) + hm.Write(in[:len(in)-sha256.Size]) // everything is hashed + expectedMAC := hm.Sum(nil) + if !hmac.Equal(messageMAC, expectedMAC) { + return nil, ErrInvalidMAC + } + + // start decryption + block, err := aes.NewCipher(keyE) + if err != nil { + return nil, err + } + mode := cipher.NewCBCDecrypter(block, iv) + // same length as ciphertext + plaintext := make([]byte, len(in)-offset-sha256.Size) + mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size]) + + return removePKCSPadding(plaintext) +} + +// Implement PKCS#7 padding with block size of 16 (AES block size). + +// addPKCSPadding adds padding to a block of data +func addPKCSPadding(src []byte) []byte { + padding := aes.BlockSize - len(src)%aes.BlockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +// removePKCSPadding removes padding from data that was added with addPKCSPadding +func removePKCSPadding(src []byte) ([]byte, error) { + length := len(src) + padLength := int(src[length-1]) + if padLength > aes.BlockSize || length < aes.BlockSize { + return nil, errInvalidPadding + } + + return src[:length-padLength], nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/doc.go new file mode 100644 index 0000000..fa8346a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/doc.go @@ -0,0 +1,21 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package btcec implements support for the elliptic curves needed for bitcoin. + +Bitcoin uses elliptic curve cryptography using koblitz curves +(specifically secp256k1) for cryptographic functions. See +http://www.secg.org/collateral/sec2_final.pdf for details on the +standard. + +This package provides the data structures and functions implementing the +crypto/elliptic Curve interface in order to permit using these curves +with the standard crypto/ecdsa package provided with go. Helper +functionality is provided to parse signatures and public keys from +standard formats. It was designed for use with btcd, but should be +general enough for other uses of elliptic curve crypto. It was originally based +on some initial work by ThePiachu, but has significantly diverged since then. +*/ +package btcec diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/field.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/field.go new file mode 100644 index 0000000..c2c6a64 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/field.go @@ -0,0 +1,1262 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2016 Dave Collins +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// References: +// [HAC]: Handbook of Applied Cryptography Menezes, van Oorschot, Vanstone. +// http://cacr.uwaterloo.ca/hac/ + +// All elliptic curve operations for secp256k1 are done in a finite field +// characterized by a 256-bit prime. Given this precision is larger than the +// biggest available native type, obviously some form of bignum math is needed. +// This package implements specialized fixed-precision field arithmetic rather +// than relying on an arbitrary-precision arithmetic package such as math/big +// for dealing with the field math since the size is known. As a result, rather +// large performance gains are achieved by taking advantage of many +// optimizations not available to arbitrary-precision arithmetic and generic +// modular arithmetic algorithms. +// +// There are various ways to internally represent each finite field element. +// For example, the most obvious representation would be to use an array of 4 +// uint64s (64 bits * 4 = 256 bits). However, that representation suffers from +// a couple of issues. First, there is no native Go type large enough to handle +// the intermediate results while adding or multiplying two 64-bit numbers, and +// second there is no space left for overflows when performing the intermediate +// arithmetic between each array element which would lead to expensive carry +// propagation. +// +// Given the above, this implementation represents the the field elements as +// 10 uint32s with each word (array entry) treated as base 2^26. This was +// chosen for the following reasons: +// 1) Most systems at the current time are 64-bit (or at least have 64-bit +// registers available for specialized purposes such as MMX) so the +// intermediate results can typically be done using a native register (and +// using uint64s to avoid the need for additional half-word arithmetic) +// 2) In order to allow addition of the internal words without having to +// propagate the the carry, the max normalized value for each register must +// be less than the number of bits available in the register +// 3) Since we're dealing with 32-bit values, 64-bits of overflow is a +// reasonable choice for #2 +// 4) Given the need for 256-bits of precision and the properties stated in #1, +// #2, and #3, the representation which best accommodates this is 10 uint32s +// with base 2^26 (26 bits * 10 = 260 bits, so the final word only needs 22 +// bits) which leaves the desired 64 bits (32 * 10 = 320, 320 - 256 = 64) for +// overflow +// +// Since it is so important that the field arithmetic is extremely fast for +// high performance crypto, this package does not perform any validation where +// it ordinarily would. For example, some functions only give the correct +// result is the field is normalized and there is no checking to ensure it is. +// While I typically prefer to ensure all state and input is valid for most +// packages, this code is really only used internally and every extra check +// counts. + +import ( + "encoding/hex" +) + +// Constants used to make the code more readable. +const ( + twoBitsMask = 0x3 + fourBitsMask = 0xf + sixBitsMask = 0x3f + eightBitsMask = 0xff +) + +// Constants related to the field representation. +const ( + // fieldWords is the number of words used to internally represent the + // 256-bit value. + fieldWords = 10 + + // fieldBase is the exponent used to form the numeric base of each word. + // 2^(fieldBase*i) where i is the word position. + fieldBase = 26 + + // fieldOverflowBits is the minimum number of "overflow" bits for each + // word in the field value. + fieldOverflowBits = 32 - fieldBase + + // fieldBaseMask is the mask for the bits in each word needed to + // represent the numeric base of each word (except the most significant + // word). + fieldBaseMask = (1 << fieldBase) - 1 + + // fieldMSBBits is the number of bits in the most significant word used + // to represent the value. + fieldMSBBits = 256 - (fieldBase * (fieldWords - 1)) + + // fieldMSBMask is the mask for the bits in the most significant word + // needed to represent the value. + fieldMSBMask = (1 << fieldMSBBits) - 1 + + // fieldPrimeWordZero is word zero of the secp256k1 prime in the + // internal field representation. It is used during negation. + fieldPrimeWordZero = 0x3fffc2f + + // fieldPrimeWordOne is word one of the secp256k1 prime in the + // internal field representation. It is used during negation. + fieldPrimeWordOne = 0x3ffffbf + + // primeLowBits is the lower 2*fieldBase bits of the secp256k1 prime in + // its standard normalized form. It is used during modular reduction. + primeLowBits = 0xffffefffffc2f +) + +// fieldVal implements optimized fixed-precision arithmetic over the +// secp256k1 finite field. This means all arithmetic is performed modulo +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f. It +// represents each 256-bit value as 10 32-bit integers in base 2^26. This +// provides 6 bits of overflow in each word (10 bits in the most significant +// word) for a total of 64 bits of overflow (9*6 + 10 = 64). It only implements +// the arithmetic needed for elliptic curve operations. +// +// The following depicts the internal representation: +// ----------------------------------------------------------------- +// | n[9] | n[8] | ... | n[0] | +// | 32 bits available | 32 bits available | ... | 32 bits available | +// | 22 bits for value | 26 bits for value | ... | 26 bits for value | +// | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow | +// | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) | +// ----------------------------------------------------------------- +// +// For example, consider the number 2^49 + 1. It would be represented as: +// n[0] = 1 +// n[1] = 2^23 +// n[2..9] = 0 +// +// The full 256-bit value is then calculated by looping i from 9..0 and +// doing sum(n[i] * 2^(26i)) like so: +// n[9] * 2^(26*9) = 0 * 2^234 = 0 +// n[8] * 2^(26*8) = 0 * 2^208 = 0 +// ... +// n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49 +// n[0] * 2^(26*0) = 1 * 2^0 = 1 +// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1 +type fieldVal struct { + n [10]uint32 +} + +// String returns the field value as a human-readable hex string. +func (f fieldVal) String() string { + t := new(fieldVal).Set(&f).Normalize() + return hex.EncodeToString(t.Bytes()[:]) +} + +// Zero sets the field value to zero. A newly created field value is already +// set to zero. This function can be useful to clear an existing field value +// for reuse. +func (f *fieldVal) Zero() { + f.n[0] = 0 + f.n[1] = 0 + f.n[2] = 0 + f.n[3] = 0 + f.n[4] = 0 + f.n[5] = 0 + f.n[6] = 0 + f.n[7] = 0 + f.n[8] = 0 + f.n[9] = 0 +} + +// Set sets the field value equal to the passed value. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).Set(f2).Add(1) so that f = f2 + 1 where f2 is not +// modified. +func (f *fieldVal) Set(val *fieldVal) *fieldVal { + *f = *val + return f +} + +// SetInt sets the field value to the passed integer. This is a convenience +// function since it is fairly common to perform some arithemetic with small +// native integers. +// +// The field value is returned to support chaining. This enables syntax such +// as f := new(fieldVal).SetInt(2).Mul(f2) so that f = 2 * f2. +func (f *fieldVal) SetInt(ui uint) *fieldVal { + f.Zero() + f.n[0] = uint32(ui) + return f +} + +// SetBytes packs the passed 32-byte big-endian value into the internal field +// value representation. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetBytes(byteArray).Mul(f2) so that f = ba * f2. +func (f *fieldVal) SetBytes(b *[32]byte) *fieldVal { + // Pack the 256 total bits across the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is significantly faster. Benchmarks show + // this is about 34 times faster than the variant which uses loops. + f.n[0] = uint32(b[31]) | uint32(b[30])<<8 | uint32(b[29])<<16 | + (uint32(b[28])&twoBitsMask)<<24 + f.n[1] = uint32(b[28])>>2 | uint32(b[27])<<6 | uint32(b[26])<<14 | + (uint32(b[25])&fourBitsMask)<<22 + f.n[2] = uint32(b[25])>>4 | uint32(b[24])<<4 | uint32(b[23])<<12 | + (uint32(b[22])&sixBitsMask)<<20 + f.n[3] = uint32(b[22])>>6 | uint32(b[21])<<2 | uint32(b[20])<<10 | + uint32(b[19])<<18 + f.n[4] = uint32(b[18]) | uint32(b[17])<<8 | uint32(b[16])<<16 | + (uint32(b[15])&twoBitsMask)<<24 + f.n[5] = uint32(b[15])>>2 | uint32(b[14])<<6 | uint32(b[13])<<14 | + (uint32(b[12])&fourBitsMask)<<22 + f.n[6] = uint32(b[12])>>4 | uint32(b[11])<<4 | uint32(b[10])<<12 | + (uint32(b[9])&sixBitsMask)<<20 + f.n[7] = uint32(b[9])>>6 | uint32(b[8])<<2 | uint32(b[7])<<10 | + uint32(b[6])<<18 + f.n[8] = uint32(b[5]) | uint32(b[4])<<8 | uint32(b[3])<<16 | + (uint32(b[2])&twoBitsMask)<<24 + f.n[9] = uint32(b[2])>>2 | uint32(b[1])<<6 | uint32(b[0])<<14 + return f +} + +// SetByteSlice packs the passed big-endian value into the internal field value +// representation. Only the first 32-bytes are used. As a result, it is up to +// the caller to ensure numbers of the appropriate size are used or the value +// will be truncated. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetByteSlice(byteSlice) +func (f *fieldVal) SetByteSlice(b []byte) *fieldVal { + var b32 [32]byte + for i := 0; i < len(b); i++ { + if i < 32 { + b32[i+(32-len(b))] = b[i] + } + } + return f.SetBytes(&b32) +} + +// SetHex decodes the passed big-endian hex string into the internal field value +// representation. Only the first 32-bytes are used. +// +// The field value is returned to support chaining. This enables syntax like: +// f := new(fieldVal).SetHex("0abc").Add(1) so that f = 0x0abc + 1 +func (f *fieldVal) SetHex(hexString string) *fieldVal { + if len(hexString)%2 != 0 { + hexString = "0" + hexString + } + bytes, _ := hex.DecodeString(hexString) + return f.SetByteSlice(bytes) +} + +// Normalize normalizes the internal field words into the desired range and +// performs fast modular reduction over the secp256k1 prime by making use of the +// special form of the prime. +func (f *fieldVal) Normalize() *fieldVal { + // The field representation leaves 6 bits of overflow in each + // word so intermediate calculations can be performed without needing + // to propagate the carry to each higher word during the calculations. + // In order to normalize, first we need to "compact" the full 256-bit + // value to the right and treat the additional 64 leftmost bits as + // the magnitude. + m := f.n[0] + t0 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[1] + t1 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[2] + t2 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[3] + t3 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[4] + t4 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[5] + t5 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[6] + t6 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[7] + t7 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[8] + t8 := m & fieldBaseMask + m = (m >> fieldBase) + f.n[9] + t9 := m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. Since this field + // is doing arithmetic modulo the secp256k1 prime, we need to perform + // modular reduction over the prime. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // The algorithm presented in the referenced section typically repeats + // until the quotient is zero. However, due to our field representation + // we already know at least how many times we would need to repeat as + // it's the value currently in m. Thus we can simply multiply the + // magnitude by the field representation of the prime and do a single + // iteration. Notice that nothing will be changed when the magnitude is + // zero, so we could skip this in that case, however always running + // regardless allows it to run in constant time. + r := t0 + m*977 + t0 = r & fieldBaseMask + r = (r >> fieldBase) + t1 + m*64 + t1 = r & fieldBaseMask + r = (r >> fieldBase) + t2 + t2 = r & fieldBaseMask + r = (r >> fieldBase) + t3 + t3 = r & fieldBaseMask + r = (r >> fieldBase) + t4 + t4 = r & fieldBaseMask + r = (r >> fieldBase) + t5 + t5 = r & fieldBaseMask + r = (r >> fieldBase) + t6 + t6 = r & fieldBaseMask + r = (r >> fieldBase) + t7 + t7 = r & fieldBaseMask + r = (r >> fieldBase) + t8 + t8 = r & fieldBaseMask + r = (r >> fieldBase) + t9 + t9 = r & fieldMSBMask + + // At this point, the result will be in the range 0 <= result <= + // prime + (2^64 - c). Therefore, one more subtraction of the prime + // might be needed if the current result is greater than or equal to the + // prime. The following does the final reduction in constant time. + // Note that the if/else here intentionally does the bitwise OR with + // zero even though it won't change the value to ensure constant time + // between the branches. + var mask int32 + lowBits := uint64(t1)<> fieldBase) & fieldBaseMask) + t2 = t2 & uint32(mask) + t3 = t3 & uint32(mask) + t4 = t4 & uint32(mask) + t5 = t5 & uint32(mask) + t6 = t6 & uint32(mask) + t7 = t7 & uint32(mask) + t8 = t8 & uint32(mask) + t9 = t9 & uint32(mask) + + // Finally, set the normalized and reduced words. + f.n[0] = t0 + f.n[1] = t1 + f.n[2] = t2 + f.n[3] = t3 + f.n[4] = t4 + f.n[5] = t5 + f.n[6] = t6 + f.n[7] = t7 + f.n[8] = t8 + f.n[9] = t9 + return f +} + +// PutBytes unpacks the field value to a 32-byte big-endian value using the +// passed byte array. There is a similar function, Bytes, which unpacks the +// field value into a new array and returns that. This version is provided +// since it can be useful to cut down on the number of allocations by allowing +// the caller to reuse a buffer. +// +// The field value must be normalized for this function to return the correct +// result. +func (f *fieldVal) PutBytes(b *[32]byte) { + // Unpack the 256 total bits from the 10 uint32 words with a max of + // 26-bits per word. This could be done with a couple of for loops, + // but this unrolled version is a bit faster. Benchmarks show this is + // about 10 times faster than the variant which uses loops. + b[31] = byte(f.n[0] & eightBitsMask) + b[30] = byte((f.n[0] >> 8) & eightBitsMask) + b[29] = byte((f.n[0] >> 16) & eightBitsMask) + b[28] = byte((f.n[0]>>24)&twoBitsMask | (f.n[1]&sixBitsMask)<<2) + b[27] = byte((f.n[1] >> 6) & eightBitsMask) + b[26] = byte((f.n[1] >> 14) & eightBitsMask) + b[25] = byte((f.n[1]>>22)&fourBitsMask | (f.n[2]&fourBitsMask)<<4) + b[24] = byte((f.n[2] >> 4) & eightBitsMask) + b[23] = byte((f.n[2] >> 12) & eightBitsMask) + b[22] = byte((f.n[2]>>20)&sixBitsMask | (f.n[3]&twoBitsMask)<<6) + b[21] = byte((f.n[3] >> 2) & eightBitsMask) + b[20] = byte((f.n[3] >> 10) & eightBitsMask) + b[19] = byte((f.n[3] >> 18) & eightBitsMask) + b[18] = byte(f.n[4] & eightBitsMask) + b[17] = byte((f.n[4] >> 8) & eightBitsMask) + b[16] = byte((f.n[4] >> 16) & eightBitsMask) + b[15] = byte((f.n[4]>>24)&twoBitsMask | (f.n[5]&sixBitsMask)<<2) + b[14] = byte((f.n[5] >> 6) & eightBitsMask) + b[13] = byte((f.n[5] >> 14) & eightBitsMask) + b[12] = byte((f.n[5]>>22)&fourBitsMask | (f.n[6]&fourBitsMask)<<4) + b[11] = byte((f.n[6] >> 4) & eightBitsMask) + b[10] = byte((f.n[6] >> 12) & eightBitsMask) + b[9] = byte((f.n[6]>>20)&sixBitsMask | (f.n[7]&twoBitsMask)<<6) + b[8] = byte((f.n[7] >> 2) & eightBitsMask) + b[7] = byte((f.n[7] >> 10) & eightBitsMask) + b[6] = byte((f.n[7] >> 18) & eightBitsMask) + b[5] = byte(f.n[8] & eightBitsMask) + b[4] = byte((f.n[8] >> 8) & eightBitsMask) + b[3] = byte((f.n[8] >> 16) & eightBitsMask) + b[2] = byte((f.n[8]>>24)&twoBitsMask | (f.n[9]&sixBitsMask)<<2) + b[1] = byte((f.n[9] >> 6) & eightBitsMask) + b[0] = byte((f.n[9] >> 14) & eightBitsMask) +} + +// Bytes unpacks the field value to a 32-byte big-endian value. See PutBytes +// for a variant that allows the a buffer to be passed which can be useful to +// to cut down on the number of allocations by allowing the caller to reuse a +// buffer. +// +// The field value must be normalized for this function to return correct +// result. +func (f *fieldVal) Bytes() *[32]byte { + b := new([32]byte) + f.PutBytes(b) + return b +} + +// IsZero returns whether or not the field value is equal to zero. +func (f *fieldVal) IsZero() bool { + // The value can only be zero if no bits are set in any of the words. + // This is a constant time implementation. + bits := f.n[0] | f.n[1] | f.n[2] | f.n[3] | f.n[4] | + f.n[5] | f.n[6] | f.n[7] | f.n[8] | f.n[9] + + return bits == 0 +} + +// IsOdd returns whether or not the field value is an odd number. +// +// The field value must be normalized for this function to return correct +// result. +func (f *fieldVal) IsOdd() bool { + // Only odd numbers have the bottom bit set. + return f.n[0]&1 == 1 +} + +// Equals returns whether or not the two field values are the same. Both +// field values being compared must be normalized for this function to return +// the correct result. +func (f *fieldVal) Equals(val *fieldVal) bool { + // Xor only sets bits when they are different, so the two field values + // can only be the same if no bits are set after xoring each word. + // This is a constant time implementation. + bits := (f.n[0] ^ val.n[0]) | (f.n[1] ^ val.n[1]) | (f.n[2] ^ val.n[2]) | + (f.n[3] ^ val.n[3]) | (f.n[4] ^ val.n[4]) | (f.n[5] ^ val.n[5]) | + (f.n[6] ^ val.n[6]) | (f.n[7] ^ val.n[7]) | (f.n[8] ^ val.n[8]) | + (f.n[9] ^ val.n[9]) + + return bits == 0 +} + +// NegateVal negates the passed value and stores the result in f. The caller +// must provide the magnitude of the passed value for a correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.NegateVal(f2).AddInt(1) so that f = -f2 + 1. +func (f *fieldVal) NegateVal(val *fieldVal, magnitude uint32) *fieldVal { + // Negation in the field is just the prime minus the value. However, + // in order to allow negation against a field value without having to + // normalize/reduce it first, multiply by the magnitude (that is how + // "far" away it is from the normalized value) to adjust. Also, since + // negating a value pushes it one more order of magnitude away from the + // normalized range, add 1 to compensate. + // + // For some intuition here, imagine you're performing mod 12 arithmetic + // (picture a clock) and you are negating the number 7. So you start at + // 12 (which is of course 0 under mod 12) and count backwards (left on + // the clock) 7 times to arrive at 5. Notice this is just 12-7 = 5. + // Now, assume you're starting with 19, which is a number that is + // already larger than the modulus and congruent to 7 (mod 12). When a + // value is already in the desired range, its magnitude is 1. Since 19 + // is an additional "step", its magnitude (mod 12) is 2. Since any + // multiple of the modulus is conguent to zero (mod m), the answer can + // be shortcut by simply mulplying the magnitude by the modulus and + // subtracting. Keeping with the example, this would be (2*12)-19 = 5. + f.n[0] = (magnitude+1)*fieldPrimeWordZero - val.n[0] + f.n[1] = (magnitude+1)*fieldPrimeWordOne - val.n[1] + f.n[2] = (magnitude+1)*fieldBaseMask - val.n[2] + f.n[3] = (magnitude+1)*fieldBaseMask - val.n[3] + f.n[4] = (magnitude+1)*fieldBaseMask - val.n[4] + f.n[5] = (magnitude+1)*fieldBaseMask - val.n[5] + f.n[6] = (magnitude+1)*fieldBaseMask - val.n[6] + f.n[7] = (magnitude+1)*fieldBaseMask - val.n[7] + f.n[8] = (magnitude+1)*fieldBaseMask - val.n[8] + f.n[9] = (magnitude+1)*fieldMSBMask - val.n[9] + + return f +} + +// Negate negates the field value. The existing field value is modified. The +// caller must provide the magnitude of the field value for a correct result. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Negate().AddInt(1) so that f = -f + 1. +func (f *fieldVal) Negate(magnitude uint32) *fieldVal { + return f.NegateVal(f, magnitude) +} + +// AddInt adds the passed integer to the existing field value and stores the +// result in f. This is a convenience function since it is fairly common to +// perform some arithemetic with small native integers. +// +// The field value is returned to support chaining. This enables syntax like: +// f.AddInt(1).Add(f2) so that f = f + 1 + f2. +func (f *fieldVal) AddInt(ui uint) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // the word and will be normalized out. + f.n[0] += uint32(ui) + + return f +} + +// Add adds the passed value to the existing field value and stores the result +// in f. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Add(f2).AddInt(1) so that f = f + f2 + 1. +func (f *fieldVal) Add(val *fieldVal) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] += val.n[0] + f.n[1] += val.n[1] + f.n[2] += val.n[2] + f.n[3] += val.n[3] + f.n[4] += val.n[4] + f.n[5] += val.n[5] + f.n[6] += val.n[6] + f.n[7] += val.n[7] + f.n[8] += val.n[8] + f.n[9] += val.n[9] + + return f +} + +// Add2 adds the passed two field values together and stores the result in f. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Add2(f, f2).AddInt(1) so that f3 = f + f2 + 1. +func (f *fieldVal) Add2(val *fieldVal, val2 *fieldVal) *fieldVal { + // Since the field representation intentionally provides overflow bits, + // it's ok to use carryless addition as the carry bit is safely part of + // each word and will be normalized out. This could obviously be done + // in a loop, but the unrolled version is faster. + f.n[0] = val.n[0] + val2.n[0] + f.n[1] = val.n[1] + val2.n[1] + f.n[2] = val.n[2] + val2.n[2] + f.n[3] = val.n[3] + val2.n[3] + f.n[4] = val.n[4] + val2.n[4] + f.n[5] = val.n[5] + val2.n[5] + f.n[6] = val.n[6] + val2.n[6] + f.n[7] = val.n[7] + val2.n[7] + f.n[8] = val.n[8] + val2.n[8] + f.n[9] = val.n[9] + val2.n[9] + + return f +} + +// MulInt multiplies the field value by the passed int and stores the result in +// f. Note that this function can overflow if multiplying the value by any of +// the individual words exceeds a max uint32. Therefore it is important that +// the caller ensures no overflows will occur before using this function. +// +// The field value is returned to support chaining. This enables syntax like: +// f.MulInt(2).Add(f2) so that f = 2 * f + f2. +func (f *fieldVal) MulInt(val uint) *fieldVal { + // Since each word of the field representation can hold up to + // fieldOverflowBits extra bits which will be normalized out, it's safe + // to multiply each word without using a larger type or carry + // propagation so long as the values won't overflow a uint32. This + // could obviously be done in a loop, but the unrolled version is + // faster. + ui := uint32(val) + f.n[0] *= ui + f.n[1] *= ui + f.n[2] *= ui + f.n[3] *= ui + f.n[4] *= ui + f.n[5] *= ui + f.n[6] *= ui + f.n[7] *= ui + f.n[8] *= ui + f.n[9] *= ui + + return f +} + +// Mul multiplies the passed value to the existing field value and stores the +// result in f. Note that this function can overflow if multiplying any +// of the individual words exceeds a max uint32. In practice, this means the +// magnitude of either value involved in the multiplication must be a max of +// 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Mul(f2).AddInt(1) so that f = (f * f2) + 1. +func (f *fieldVal) Mul(val *fieldVal) *fieldVal { + return f.Mul2(f, val) +} + +// Mul2 multiplies the passed two field values together and stores the result +// result in f. Note that this function can overflow if multiplying any of +// the individual words exceeds a max uint32. In practice, this means the +// magnitude of either value involved in the multiplication must be a max of +// 8. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.Mul2(f, f2).AddInt(1) so that f3 = (f * f2) + 1. +func (f *fieldVal) Mul2(val *fieldVal, val2 *fieldVal) *fieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val2.n[0]) + t0 := m & fieldBaseMask + + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[1]) + + uint64(val.n[1])*uint64(val2.n[0]) + t1 := m & fieldBaseMask + + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[2]) + + uint64(val.n[1])*uint64(val2.n[1]) + + uint64(val.n[2])*uint64(val2.n[0]) + t2 := m & fieldBaseMask + + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[3]) + + uint64(val.n[1])*uint64(val2.n[2]) + + uint64(val.n[2])*uint64(val2.n[1]) + + uint64(val.n[3])*uint64(val2.n[0]) + t3 := m & fieldBaseMask + + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[4]) + + uint64(val.n[1])*uint64(val2.n[3]) + + uint64(val.n[2])*uint64(val2.n[2]) + + uint64(val.n[3])*uint64(val2.n[1]) + + uint64(val.n[4])*uint64(val2.n[0]) + t4 := m & fieldBaseMask + + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[5]) + + uint64(val.n[1])*uint64(val2.n[4]) + + uint64(val.n[2])*uint64(val2.n[3]) + + uint64(val.n[3])*uint64(val2.n[2]) + + uint64(val.n[4])*uint64(val2.n[1]) + + uint64(val.n[5])*uint64(val2.n[0]) + t5 := m & fieldBaseMask + + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[6]) + + uint64(val.n[1])*uint64(val2.n[5]) + + uint64(val.n[2])*uint64(val2.n[4]) + + uint64(val.n[3])*uint64(val2.n[3]) + + uint64(val.n[4])*uint64(val2.n[2]) + + uint64(val.n[5])*uint64(val2.n[1]) + + uint64(val.n[6])*uint64(val2.n[0]) + t6 := m & fieldBaseMask + + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[7]) + + uint64(val.n[1])*uint64(val2.n[6]) + + uint64(val.n[2])*uint64(val2.n[5]) + + uint64(val.n[3])*uint64(val2.n[4]) + + uint64(val.n[4])*uint64(val2.n[3]) + + uint64(val.n[5])*uint64(val2.n[2]) + + uint64(val.n[6])*uint64(val2.n[1]) + + uint64(val.n[7])*uint64(val2.n[0]) + t7 := m & fieldBaseMask + + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[8]) + + uint64(val.n[1])*uint64(val2.n[7]) + + uint64(val.n[2])*uint64(val2.n[6]) + + uint64(val.n[3])*uint64(val2.n[5]) + + uint64(val.n[4])*uint64(val2.n[4]) + + uint64(val.n[5])*uint64(val2.n[3]) + + uint64(val.n[6])*uint64(val2.n[2]) + + uint64(val.n[7])*uint64(val2.n[1]) + + uint64(val.n[8])*uint64(val2.n[0]) + t8 := m & fieldBaseMask + + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + uint64(val.n[0])*uint64(val2.n[9]) + + uint64(val.n[1])*uint64(val2.n[8]) + + uint64(val.n[2])*uint64(val2.n[7]) + + uint64(val.n[3])*uint64(val2.n[6]) + + uint64(val.n[4])*uint64(val2.n[5]) + + uint64(val.n[5])*uint64(val2.n[4]) + + uint64(val.n[6])*uint64(val2.n[3]) + + uint64(val.n[7])*uint64(val2.n[2]) + + uint64(val.n[8])*uint64(val2.n[1]) + + uint64(val.n[9])*uint64(val2.n[0]) + t9 := m & fieldBaseMask + + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + uint64(val.n[1])*uint64(val2.n[9]) + + uint64(val.n[2])*uint64(val2.n[8]) + + uint64(val.n[3])*uint64(val2.n[7]) + + uint64(val.n[4])*uint64(val2.n[6]) + + uint64(val.n[5])*uint64(val2.n[5]) + + uint64(val.n[6])*uint64(val2.n[4]) + + uint64(val.n[7])*uint64(val2.n[3]) + + uint64(val.n[8])*uint64(val2.n[2]) + + uint64(val.n[9])*uint64(val2.n[1]) + t10 := m & fieldBaseMask + + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + uint64(val.n[2])*uint64(val2.n[9]) + + uint64(val.n[3])*uint64(val2.n[8]) + + uint64(val.n[4])*uint64(val2.n[7]) + + uint64(val.n[5])*uint64(val2.n[6]) + + uint64(val.n[6])*uint64(val2.n[5]) + + uint64(val.n[7])*uint64(val2.n[4]) + + uint64(val.n[8])*uint64(val2.n[3]) + + uint64(val.n[9])*uint64(val2.n[2]) + t11 := m & fieldBaseMask + + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + uint64(val.n[3])*uint64(val2.n[9]) + + uint64(val.n[4])*uint64(val2.n[8]) + + uint64(val.n[5])*uint64(val2.n[7]) + + uint64(val.n[6])*uint64(val2.n[6]) + + uint64(val.n[7])*uint64(val2.n[5]) + + uint64(val.n[8])*uint64(val2.n[4]) + + uint64(val.n[9])*uint64(val2.n[3]) + t12 := m & fieldBaseMask + + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + uint64(val.n[4])*uint64(val2.n[9]) + + uint64(val.n[5])*uint64(val2.n[8]) + + uint64(val.n[6])*uint64(val2.n[7]) + + uint64(val.n[7])*uint64(val2.n[6]) + + uint64(val.n[8])*uint64(val2.n[5]) + + uint64(val.n[9])*uint64(val2.n[4]) + t13 := m & fieldBaseMask + + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + uint64(val.n[5])*uint64(val2.n[9]) + + uint64(val.n[6])*uint64(val2.n[8]) + + uint64(val.n[7])*uint64(val2.n[7]) + + uint64(val.n[8])*uint64(val2.n[6]) + + uint64(val.n[9])*uint64(val2.n[5]) + t14 := m & fieldBaseMask + + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + uint64(val.n[6])*uint64(val2.n[9]) + + uint64(val.n[7])*uint64(val2.n[8]) + + uint64(val.n[8])*uint64(val2.n[7]) + + uint64(val.n[9])*uint64(val2.n[6]) + t15 := m & fieldBaseMask + + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + uint64(val.n[7])*uint64(val2.n[9]) + + uint64(val.n[8])*uint64(val2.n[8]) + + uint64(val.n[9])*uint64(val2.n[7]) + t16 := m & fieldBaseMask + + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + + uint64(val.n[8])*uint64(val2.n[9]) + + uint64(val.n[9])*uint64(val2.n[8]) + t17 := m & fieldBaseMask + + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val2.n[9]) + t18 := m & fieldBaseMask + + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + d := t0 + m*977 + f.n[0] = uint32(d & fieldBaseMask) + d = (d >> fieldBase) + t1 + m*64 + f.n[1] = uint32(d & fieldBaseMask) + f.n[2] = uint32((d >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + + return f +} + +// Square squares the field value. The existing field value is modified. Note +// that this function can overflow if multiplying any of the individual words +// exceeds a max uint32. In practice, this means the magnitude of the field +// must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Square().Mul(f2) so that f = f^2 * f2. +func (f *fieldVal) Square() *fieldVal { + return f.SquareVal(f) +} + +// SquareVal squares the passed value and stores the result in f. Note that +// this function can overflow if multiplying any of the individual words +// exceeds a max uint32. In practice, this means the magnitude of the field +// being squred must be a max of 8 to prevent overflow. +// +// The field value is returned to support chaining. This enables syntax like: +// f3.SquareVal(f).Mul(f) so that f3 = f^2 * f = f^3. +func (f *fieldVal) SquareVal(val *fieldVal) *fieldVal { + // This could be done with a couple of for loops and an array to store + // the intermediate terms, but this unrolled version is significantly + // faster. + + // Terms for 2^(fieldBase*0). + m := uint64(val.n[0]) * uint64(val.n[0]) + t0 := m & fieldBaseMask + + // Terms for 2^(fieldBase*1). + m = (m >> fieldBase) + 2*uint64(val.n[0])*uint64(val.n[1]) + t1 := m & fieldBaseMask + + // Terms for 2^(fieldBase*2). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[2]) + + uint64(val.n[1])*uint64(val.n[1]) + t2 := m & fieldBaseMask + + // Terms for 2^(fieldBase*3). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[3]) + + 2*uint64(val.n[1])*uint64(val.n[2]) + t3 := m & fieldBaseMask + + // Terms for 2^(fieldBase*4). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[4]) + + 2*uint64(val.n[1])*uint64(val.n[3]) + + uint64(val.n[2])*uint64(val.n[2]) + t4 := m & fieldBaseMask + + // Terms for 2^(fieldBase*5). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[5]) + + 2*uint64(val.n[1])*uint64(val.n[4]) + + 2*uint64(val.n[2])*uint64(val.n[3]) + t5 := m & fieldBaseMask + + // Terms for 2^(fieldBase*6). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[6]) + + 2*uint64(val.n[1])*uint64(val.n[5]) + + 2*uint64(val.n[2])*uint64(val.n[4]) + + uint64(val.n[3])*uint64(val.n[3]) + t6 := m & fieldBaseMask + + // Terms for 2^(fieldBase*7). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[7]) + + 2*uint64(val.n[1])*uint64(val.n[6]) + + 2*uint64(val.n[2])*uint64(val.n[5]) + + 2*uint64(val.n[3])*uint64(val.n[4]) + t7 := m & fieldBaseMask + + // Terms for 2^(fieldBase*8). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[8]) + + 2*uint64(val.n[1])*uint64(val.n[7]) + + 2*uint64(val.n[2])*uint64(val.n[6]) + + 2*uint64(val.n[3])*uint64(val.n[5]) + + uint64(val.n[4])*uint64(val.n[4]) + t8 := m & fieldBaseMask + + // Terms for 2^(fieldBase*9). + m = (m >> fieldBase) + + 2*uint64(val.n[0])*uint64(val.n[9]) + + 2*uint64(val.n[1])*uint64(val.n[8]) + + 2*uint64(val.n[2])*uint64(val.n[7]) + + 2*uint64(val.n[3])*uint64(val.n[6]) + + 2*uint64(val.n[4])*uint64(val.n[5]) + t9 := m & fieldBaseMask + + // Terms for 2^(fieldBase*10). + m = (m >> fieldBase) + + 2*uint64(val.n[1])*uint64(val.n[9]) + + 2*uint64(val.n[2])*uint64(val.n[8]) + + 2*uint64(val.n[3])*uint64(val.n[7]) + + 2*uint64(val.n[4])*uint64(val.n[6]) + + uint64(val.n[5])*uint64(val.n[5]) + t10 := m & fieldBaseMask + + // Terms for 2^(fieldBase*11). + m = (m >> fieldBase) + + 2*uint64(val.n[2])*uint64(val.n[9]) + + 2*uint64(val.n[3])*uint64(val.n[8]) + + 2*uint64(val.n[4])*uint64(val.n[7]) + + 2*uint64(val.n[5])*uint64(val.n[6]) + t11 := m & fieldBaseMask + + // Terms for 2^(fieldBase*12). + m = (m >> fieldBase) + + 2*uint64(val.n[3])*uint64(val.n[9]) + + 2*uint64(val.n[4])*uint64(val.n[8]) + + 2*uint64(val.n[5])*uint64(val.n[7]) + + uint64(val.n[6])*uint64(val.n[6]) + t12 := m & fieldBaseMask + + // Terms for 2^(fieldBase*13). + m = (m >> fieldBase) + + 2*uint64(val.n[4])*uint64(val.n[9]) + + 2*uint64(val.n[5])*uint64(val.n[8]) + + 2*uint64(val.n[6])*uint64(val.n[7]) + t13 := m & fieldBaseMask + + // Terms for 2^(fieldBase*14). + m = (m >> fieldBase) + + 2*uint64(val.n[5])*uint64(val.n[9]) + + 2*uint64(val.n[6])*uint64(val.n[8]) + + uint64(val.n[7])*uint64(val.n[7]) + t14 := m & fieldBaseMask + + // Terms for 2^(fieldBase*15). + m = (m >> fieldBase) + + 2*uint64(val.n[6])*uint64(val.n[9]) + + 2*uint64(val.n[7])*uint64(val.n[8]) + t15 := m & fieldBaseMask + + // Terms for 2^(fieldBase*16). + m = (m >> fieldBase) + + 2*uint64(val.n[7])*uint64(val.n[9]) + + uint64(val.n[8])*uint64(val.n[8]) + t16 := m & fieldBaseMask + + // Terms for 2^(fieldBase*17). + m = (m >> fieldBase) + 2*uint64(val.n[8])*uint64(val.n[9]) + t17 := m & fieldBaseMask + + // Terms for 2^(fieldBase*18). + m = (m >> fieldBase) + uint64(val.n[9])*uint64(val.n[9]) + t18 := m & fieldBaseMask + + // What's left is for 2^(fieldBase*19). + t19 := m >> fieldBase + + // At this point, all of the terms are grouped into their respective + // base. + // + // Per [HAC] section 14.3.4: Reduction method of moduli of special form, + // when the modulus is of the special form m = b^t - c, highly efficient + // reduction can be achieved per the provided algorithm. + // + // The secp256k1 prime is equivalent to 2^256 - 4294968273, so it fits + // this criteria. + // + // 4294968273 in field representation (base 2^26) is: + // n[0] = 977 + // n[1] = 64 + // That is to say (2^26 * 64) + 977 = 4294968273 + // + // Since each word is in base 26, the upper terms (t10 and up) start + // at 260 bits (versus the final desired range of 256 bits), so the + // field representation of 'c' from above needs to be adjusted for the + // extra 4 bits by multiplying it by 2^4 = 16. 4294968273 * 16 = + // 68719492368. Thus, the adjusted field representation of 'c' is: + // n[0] = 977 * 16 = 15632 + // n[1] = 64 * 16 = 1024 + // That is to say (2^26 * 1024) + 15632 = 68719492368 + // + // To reduce the final term, t19, the entire 'c' value is needed instead + // of only n[0] because there are no more terms left to handle n[1]. + // This means there might be some magnitude left in the upper bits that + // is handled below. + m = t0 + t10*15632 + t0 = m & fieldBaseMask + m = (m >> fieldBase) + t1 + t10*1024 + t11*15632 + t1 = m & fieldBaseMask + m = (m >> fieldBase) + t2 + t11*1024 + t12*15632 + t2 = m & fieldBaseMask + m = (m >> fieldBase) + t3 + t12*1024 + t13*15632 + t3 = m & fieldBaseMask + m = (m >> fieldBase) + t4 + t13*1024 + t14*15632 + t4 = m & fieldBaseMask + m = (m >> fieldBase) + t5 + t14*1024 + t15*15632 + t5 = m & fieldBaseMask + m = (m >> fieldBase) + t6 + t15*1024 + t16*15632 + t6 = m & fieldBaseMask + m = (m >> fieldBase) + t7 + t16*1024 + t17*15632 + t7 = m & fieldBaseMask + m = (m >> fieldBase) + t8 + t17*1024 + t18*15632 + t8 = m & fieldBaseMask + m = (m >> fieldBase) + t9 + t18*1024 + t19*68719492368 + t9 = m & fieldMSBMask + m = m >> fieldMSBBits + + // At this point, if the magnitude is greater than 0, the overall value + // is greater than the max possible 256-bit value. In particular, it is + // "how many times larger" than the max value it is. + // + // The algorithm presented in [HAC] section 14.3.4 repeats until the + // quotient is zero. However, due to the above, we already know at + // least how many times we would need to repeat as it's the value + // currently in m. Thus we can simply multiply the magnitude by the + // field representation of the prime and do a single iteration. Notice + // that nothing will be changed when the magnitude is zero, so we could + // skip this in that case, however always running regardless allows it + // to run in constant time. The final result will be in the range + // 0 <= result <= prime + (2^64 - c), so it is guaranteed to have a + // magnitude of 1, but it is denormalized. + n := t0 + m*977 + f.n[0] = uint32(n & fieldBaseMask) + n = (n >> fieldBase) + t1 + m*64 + f.n[1] = uint32(n & fieldBaseMask) + f.n[2] = uint32((n >> fieldBase) + t2) + f.n[3] = uint32(t3) + f.n[4] = uint32(t4) + f.n[5] = uint32(t5) + f.n[6] = uint32(t6) + f.n[7] = uint32(t7) + f.n[8] = uint32(t8) + f.n[9] = uint32(t9) + + return f +} + +// Inverse finds the modular multiplicative inverse of the field value. The +// existing field value is modified. +// +// The field value is returned to support chaining. This enables syntax like: +// f.Inverse().Mul(f2) so that f = f^-1 * f2. +func (f *fieldVal) Inverse() *fieldVal { + // Fermat's little theorem states that for a nonzero number a and prime + // prime p, a^(p-1) = 1 (mod p). Since the multipliciative inverse is + // a*b = 1 (mod p), it follows that b = a*a^(p-2) = a^(p-1) = 1 (mod p). + // Thus, a^(p-2) is the multiplicative inverse. + // + // In order to efficiently compute a^(p-2), p-2 needs to be split into + // a sequence of squares and multipications that minimizes the number of + // multiplications needed (since they are more costly than squarings). + // Intermediate results are saved and reused as well. + // + // The secp256k1 prime - 2 is 2^256 - 4294968275. + // + // This has a cost of 258 field squarings and 33 field multiplications. + var a2, a3, a4, a10, a11, a21, a42, a45, a63, a1019, a1023 fieldVal + a2.SquareVal(f) + a3.Mul2(&a2, f) + a4.SquareVal(&a2) + a10.SquareVal(&a4).Mul(&a2) + a11.Mul2(&a10, f) + a21.Mul2(&a10, &a11) + a42.SquareVal(&a21) + a45.Mul2(&a42, &a3) + a63.Mul2(&a42, &a21) + a1019.SquareVal(&a63).Square().Square().Square().Mul(&a11) + a1023.Mul2(&a1019, &a4) + f.Set(&a63) // f = a^(2^6 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^11 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^16 - 1024) + f.Mul(&a1023) // f = a^(2^16 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^21 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^26 - 1024) + f.Mul(&a1023) // f = a^(2^26 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^31 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^36 - 1024) + f.Mul(&a1023) // f = a^(2^36 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^41 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^46 - 1024) + f.Mul(&a1023) // f = a^(2^46 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^51 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^56 - 1024) + f.Mul(&a1023) // f = a^(2^56 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^61 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^66 - 1024) + f.Mul(&a1023) // f = a^(2^66 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^71 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^76 - 1024) + f.Mul(&a1023) // f = a^(2^76 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^81 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^86 - 1024) + f.Mul(&a1023) // f = a^(2^86 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^91 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^96 - 1024) + f.Mul(&a1023) // f = a^(2^96 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^101 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^106 - 1024) + f.Mul(&a1023) // f = a^(2^106 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^111 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^116 - 1024) + f.Mul(&a1023) // f = a^(2^116 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^121 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^126 - 1024) + f.Mul(&a1023) // f = a^(2^126 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^131 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^136 - 1024) + f.Mul(&a1023) // f = a^(2^136 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^141 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^146 - 1024) + f.Mul(&a1023) // f = a^(2^146 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^151 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^156 - 1024) + f.Mul(&a1023) // f = a^(2^156 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^161 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^166 - 1024) + f.Mul(&a1023) // f = a^(2^166 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^171 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^176 - 1024) + f.Mul(&a1023) // f = a^(2^176 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^181 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^186 - 1024) + f.Mul(&a1023) // f = a^(2^186 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^191 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^196 - 1024) + f.Mul(&a1023) // f = a^(2^196 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^201 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^206 - 1024) + f.Mul(&a1023) // f = a^(2^206 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^211 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^216 - 1024) + f.Mul(&a1023) // f = a^(2^216 - 1) + f.Square().Square().Square().Square().Square() // f = a^(2^221 - 32) + f.Square().Square().Square().Square().Square() // f = a^(2^226 - 1024) + f.Mul(&a1019) // f = a^(2^226 - 5) + f.Square().Square().Square().Square().Square() // f = a^(2^231 - 160) + f.Square().Square().Square().Square().Square() // f = a^(2^236 - 5120) + f.Mul(&a1023) // f = a^(2^236 - 4097) + f.Square().Square().Square().Square().Square() // f = a^(2^241 - 131104) + f.Square().Square().Square().Square().Square() // f = a^(2^246 - 4195328) + f.Mul(&a1023) // f = a^(2^246 - 4194305) + f.Square().Square().Square().Square().Square() // f = a^(2^251 - 134217760) + f.Square().Square().Square().Square().Square() // f = a^(2^256 - 4294968320) + return f.Mul(&a45) // f = a^(2^256 - 4294968275) = a^(p-2) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go new file mode 100644 index 0000000..d4a9c1b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/genprecomps.go @@ -0,0 +1,63 @@ +// Copyright 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is ignored during the regular build due to the following build tag. +// It is called by go generate and used to automatically generate pre-computed +// tables used to accelerate operations. +// +build ignore + +package main + +import ( + "bytes" + "compress/zlib" + "encoding/base64" + "fmt" + "log" + "os" + + "github.com/btcsuite/btcd/btcec" +) + +func main() { + fi, err := os.Create("secp256k1.go") + if err != nil { + log.Fatal(err) + } + defer fi.Close() + + // Compress the serialized byte points. + serialized := btcec.S256().SerializedBytePoints() + var compressed bytes.Buffer + w := zlib.NewWriter(&compressed) + if _, err := w.Write(serialized); err != nil { + fmt.Println(err) + os.Exit(1) + } + w.Close() + + // Encode the compressed byte points with base64. + encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len())) + base64.StdEncoding.Encode(encoded, compressed.Bytes()) + + fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers") + fmt.Fprintln(fi, "// Use of this source code is governed by an ISC") + fmt.Fprintln(fi, "// license that can be found in the LICENSE file.") + fmt.Fprintln(fi) + fmt.Fprintln(fi, "package btcec") + fmt.Fprintln(fi) + fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)") + fmt.Fprintln(fi, "// DO NOT EDIT") + fmt.Fprintln(fi) + fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded)) + + a1, b1, a2, b2 := btcec.S256().EndomorphismVectors() + fmt.Println("The following values are the computed linearly " + + "independent vectors needed to make use of the secp256k1 " + + "endomorphism:") + fmt.Printf("a1: %x\n", a1) + fmt.Printf("b1: %x\n", b1) + fmt.Printf("a2: %x\n", a2) + fmt.Printf("b2: %x\n", b2) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go new file mode 100644 index 0000000..1928702 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/gensecp256k1.go @@ -0,0 +1,203 @@ +// Copyright (c) 2014-2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// This file is ignored during the regular build due to the following build tag. +// This build tag is set during go generate. +// +build gensecp256k1 + +package btcec + +// References: +// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) + +import ( + "encoding/binary" + "math/big" +) + +// secp256k1BytePoints are dummy points used so the code which generates the +// real values can compile. +var secp256k1BytePoints = "" + +// getDoublingPoints returns all the possible G^(2^i) for i in +// 0..n-1 where n is the curve's bit size (256 in the case of secp256k1) +// the coordinates are recorded as Jacobian coordinates. +func (curve *KoblitzCurve) getDoublingPoints() [][3]fieldVal { + doublingPoints := make([][3]fieldVal, curve.BitSize) + + // initialize px, py, pz to the Jacobian coordinates for the base point + px, py := curve.bigAffineToField(curve.Gx, curve.Gy) + pz := new(fieldVal).SetInt(1) + for i := 0; i < curve.BitSize; i++ { + doublingPoints[i] = [3]fieldVal{*px, *py, *pz} + // P = 2*P + curve.doubleJacobian(px, py, pz, px, py, pz) + } + return doublingPoints +} + +// SerializedBytePoints returns a serialized byte slice which contains all of +// the possible points per 8-bit window. This is used to when generating +// secp256k1.go. +func (curve *KoblitzCurve) SerializedBytePoints() []byte { + doublingPoints := curve.getDoublingPoints() + + // Segregate the bits into byte-sized windows + serialized := make([]byte, curve.byteSize*256*3*10*4) + offset := 0 + for byteNum := 0; byteNum < curve.byteSize; byteNum++ { + // Grab the 8 bits that make up this byte from doublingPoints. + startingBit := 8 * (curve.byteSize - byteNum - 1) + computingPoints := doublingPoints[startingBit : startingBit+8] + + // Compute all points in this window and serialize them. + for i := 0; i < 256; i++ { + px, py, pz := new(fieldVal), new(fieldVal), new(fieldVal) + for j := 0; j < 8; j++ { + if i>>uint(j)&1 == 1 { + curve.addJacobian(px, py, pz, &computingPoints[j][0], + &computingPoints[j][1], &computingPoints[j][2], px, py, pz) + } + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], px.n[i]) + offset += 4 + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], py.n[i]) + offset += 4 + } + for i := 0; i < 10; i++ { + binary.LittleEndian.PutUint32(serialized[offset:], pz.n[i]) + offset += 4 + } + } + } + + return serialized +} + +// sqrt returns the square root of the provided big integer using Newton's +// method. It's only compiled and used during generation of pre-computed +// values, so speed is not a huge concern. +func sqrt(n *big.Int) *big.Int { + // Initial guess = 2^(log_2(n)/2) + guess := big.NewInt(2) + guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil) + + // Now refine using Newton's method. + big2 := big.NewInt(2) + prevGuess := big.NewInt(0) + for { + prevGuess.Set(guess) + guess.Add(guess, new(big.Int).Div(n, guess)) + guess.Div(guess, big2) + if guess.Cmp(prevGuess) == 0 { + break + } + } + return guess +} + +// EndomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to +// generate the linearly independent vectors needed to generate a balanced +// length-two representation of a multiplier such that k = k1 + k2λ (mod N) and +// returns them. Since the values will always be the same given the fact that N +// and λ are fixed, the final results can be accelerated by storing the +// precomputed values with the curve. +func (curve *KoblitzCurve) EndomorphismVectors() (a1, b1, a2, b2 *big.Int) { + bigMinus1 := big.NewInt(-1) + + // This section uses an extended Euclidean algorithm to generate a + // sequence of equations: + // s[i] * N + t[i] * λ = r[i] + + nSqrt := sqrt(curve.N) + u, v := new(big.Int).Set(curve.N), new(big.Int).Set(curve.lambda) + x1, y1 := big.NewInt(1), big.NewInt(0) + x2, y2 := big.NewInt(0), big.NewInt(1) + q, r := new(big.Int), new(big.Int) + qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int) + s, t := new(big.Int), new(big.Int) + ri, ti := new(big.Int), new(big.Int) + a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int) + found, oneMore := false, false + for u.Sign() != 0 { + // q = v/u + q.Div(v, u) + + // r = v - q*u + qu.Mul(q, u) + r.Sub(v, qu) + + // s = x2 - q*x1 + qx1.Mul(q, x1) + s.Sub(x2, qx1) + + // t = y2 - q*y1 + qy1.Mul(q, y1) + t.Sub(y2, qy1) + + // v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t + v.Set(u) + u.Set(r) + x2.Set(x1) + x1.Set(s) + y2.Set(y1) + y1.Set(t) + + // As soon as the remainder is less than the sqrt of n, the + // values of a1 and b1 are known. + if !found && r.Cmp(nSqrt) < 0 { + // When this condition executes ri and ti represent the + // r[i] and t[i] values such that i is the greatest + // index for which r >= sqrt(n). Meanwhile, the current + // r and t values are r[i+1] and t[i+1], respectively. + + // a1 = r[i+1], b1 = -t[i+1] + a1.Set(r) + b1.Mul(t, bigMinus1) + found = true + oneMore = true + + // Skip to the next iteration so ri and ti are not + // modified. + continue + + } else if oneMore { + // When this condition executes ri and ti still + // represent the r[i] and t[i] values while the current + // r and t are r[i+2] and t[i+2], respectively. + + // sum1 = r[i]^2 + t[i]^2 + rSquared := new(big.Int).Mul(ri, ri) + tSquared := new(big.Int).Mul(ti, ti) + sum1 := new(big.Int).Add(rSquared, tSquared) + + // sum2 = r[i+2]^2 + t[i+2]^2 + r2Squared := new(big.Int).Mul(r, r) + t2Squared := new(big.Int).Mul(t, t) + sum2 := new(big.Int).Add(r2Squared, t2Squared) + + // if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2) + if sum1.Cmp(sum2) <= 0 { + // a2 = r[i], b2 = -t[i] + a2.Set(ri) + b2.Mul(ti, bigMinus1) + } else { + // a2 = r[i+2], b2 = -t[i+2] + a2.Set(r) + b2.Mul(t, bigMinus1) + } + + // All done. + break + } + + ri.Set(r) + ti.Set(t) + } + + return a1, b1, a2, b2 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/precompute.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/precompute.go new file mode 100644 index 0000000..034cd55 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/precompute.go @@ -0,0 +1,67 @@ +// Copyright 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "compress/zlib" + "encoding/base64" + "encoding/binary" + "io/ioutil" + "strings" +) + +//go:generate go run -tags gensecp256k1 genprecomps.go + +// loadS256BytePoints decompresses and deserializes the pre-computed byte points +// used to accelerate scalar base multiplication for the secp256k1 curve. This +// approach is used since it allows the compile to use significantly less ram +// and be performed much faster than it is with hard-coding the final in-memory +// data structure. At the same time, it is quite fast to generate the in-memory +// data structure at init time with this approach versus computing the table. +func loadS256BytePoints() error { + // There will be no byte points to load when generating them. + bp := secp256k1BytePoints + if len(bp) == 0 { + return nil + } + + // Decompress the pre-computed table used to accelerate scalar base + // multiplication. + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp)) + r, err := zlib.NewReader(decoder) + if err != nil { + return err + } + serialized, err := ioutil.ReadAll(r) + if err != nil { + return err + } + + // Deserialize the precomputed byte points and set the curve to them. + offset := 0 + var bytePoints [32][256][3]fieldVal + for byteNum := 0; byteNum < 32; byteNum++ { + // All points in this window. + for i := 0; i < 256; i++ { + px := &bytePoints[byteNum][i][0] + py := &bytePoints[byteNum][i][1] + pz := &bytePoints[byteNum][i][2] + for i := 0; i < 10; i++ { + px.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + for i := 0; i < 10; i++ { + py.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + for i := 0; i < 10; i++ { + pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) + offset += 4 + } + } + } + secp256k1.bytePoints = &bytePoints + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/privkey.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/privkey.go new file mode 100644 index 0000000..676a8c3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/privkey.go @@ -0,0 +1,73 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "math/big" +) + +// PrivateKey wraps an ecdsa.PrivateKey as a convenience mainly for signing +// things with the the private key without having to directly import the ecdsa +// package. +type PrivateKey ecdsa.PrivateKey + +// PrivKeyFromBytes returns a private and public key for `curve' based on the +// private key passed as an argument as a byte slice. +func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey, + *PublicKey) { + x, y := curve.ScalarBaseMult(pk) + + priv := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: curve, + X: x, + Y: y, + }, + D: new(big.Int).SetBytes(pk), + } + + return (*PrivateKey)(priv), (*PublicKey)(&priv.PublicKey) +} + +// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey +// instead of the normal ecdsa.PrivateKey. +func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) { + key, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, err + } + return (*PrivateKey)(key), nil +} + +// PubKey returns the PublicKey corresponding to this private key. +func (p *PrivateKey) PubKey() *PublicKey { + return (*PublicKey)(&p.PublicKey) +} + +// ToECDSA returns the private key as a *ecdsa.PrivateKey. +func (p *PrivateKey) ToECDSA() *ecdsa.PrivateKey { + return (*ecdsa.PrivateKey)(p) +} + +// Sign generates an ECDSA signature for the provided hash (which should be the result +// of hashing a larger message) using the private key. Produced signature +// is deterministic (same message and same key yield the same signature) and canonical +// in accordance with RFC6979 and BIP0062. +func (p *PrivateKey) Sign(hash []byte) (*Signature, error) { + return signRFC6979(p, hash) +} + +// PrivKeyBytesLen defines the length in bytes of a serialized private key. +const PrivKeyBytesLen = 32 + +// Serialize returns the private key number d as a big-endian binary-encoded +// number, padded to a length of 32 bytes. +func (p *PrivateKey) Serialize() []byte { + b := make([]byte, 0, PrivKeyBytesLen) + return paddedAppend(PrivKeyBytesLen, b, p.ToECDSA().D.Bytes()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/pubkey.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/pubkey.go new file mode 100644 index 0000000..f145414 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/pubkey.go @@ -0,0 +1,172 @@ +// Copyright (c) 2013-2014 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" +) + +// These constants define the lengths of serialized public keys. +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 +) + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +// decompressPoint decompresses a point on the given curve given the X point and +// the solution to use. +func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) { + // TODO: This will probably only work for secp256k1 due to + // optimizations. + + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, curve.Params().B) + + // now calculate sqrt mod p of x2 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P) + + if ybit != isOdd(y) { + y.Sub(curve.Params().P, y) + } + if ybit != isOdd(y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + return y, nil +} + +const ( + pubkeyCompressed byte = 0x2 // y_bit + x coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord +) + +// ParsePubKey parses a public key for a koblitz curve from a bytestring into a +// ecdsa.Publickey, verifying that it is valid. It supports compressed, +// uncompressed and hybrid signature formats. +func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) { + pubkey := PublicKey{} + pubkey.Curve = curve + + if len(pubKeyStr) == 0 { + return nil, errors.New("pubkey string is empty") + } + + format := pubKeyStr[0] + ybit := (format & 0x1) == 0x1 + format &= ^byte(0x1) + + switch len(pubKeyStr) { + case PubKeyBytesLenUncompressed: + if format != pubkeyUncompressed && format != pubkeyHybrid { + return nil, fmt.Errorf("invalid magic in pubkey str: "+ + "%d", pubKeyStr[0]) + } + + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) + // hybrid keys have extra information, make use of it. + if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + case PubKeyBytesLenCompressed: + // format is 0x2 | solution, + // solution determines which solution of the curve we use. + /// y^2 = x^3 + Curve.B + if format != pubkeyCompressed { + return nil, fmt.Errorf("invalid magic in compressed "+ + "pubkey string: %d", pubKeyStr[0]) + } + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit) + if err != nil { + return nil, err + } + default: // wrong! + return nil, fmt.Errorf("invalid pub key length %d", + len(pubKeyStr)) + } + + if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey X parameter is >= to P") + } + if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey Y parameter is >= to P") + } + if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { + return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") + } + return &pubkey, nil +} + +// PublicKey is an ecdsa.PublicKey with additional functions to +// serialize in uncompressed, compressed, and hybrid formats. +type PublicKey ecdsa.PublicKey + +// ToECDSA returns the public key as a *ecdsa.PublicKey. +func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { + return (*ecdsa.PublicKey)(p) +} + +// SerializeUncompressed serializes a public key in a 65-byte uncompressed +// format. +func (p *PublicKey) SerializeUncompressed() []byte { + b := make([]byte, 0, PubKeyBytesLenUncompressed) + b = append(b, pubkeyUncompressed) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) +} + +// SerializeCompressed serializes a public key in a 33-byte compressed format. +func (p *PublicKey) SerializeCompressed() []byte { + b := make([]byte, 0, PubKeyBytesLenCompressed) + format := pubkeyCompressed + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + return paddedAppend(32, b, p.X.Bytes()) +} + +// SerializeHybrid serializes a public key in a 65-byte hybrid format. +func (p *PublicKey) SerializeHybrid() []byte { + b := make([]byte, 0, PubKeyBytesLenHybrid) + format := pubkeyHybrid + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + b = paddedAppend(32, b, p.X.Bytes()) + return paddedAppend(32, b, p.Y.Bytes()) +} + +// IsEqual compares this PublicKey instance to the one passed, returning true if +// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they +// both have the same X and Y coordinate. +func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { + return p.X.Cmp(otherPubKey.X) == 0 && + p.Y.Cmp(otherPubKey.Y) == 0 +} + +// paddedAppend appends the src byte slice to dst, returning the new slice. +// If the length of the source is smaller than the passed size, leading zero +// bytes are appended to the dst slice before appending src. +func paddedAppend(size uint, dst, src []byte) []byte { + for i := 0; i < int(size)-len(src); i++ { + dst = append(dst, 0) + } + return append(dst, src...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go new file mode 100644 index 0000000..5e7bffe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/secp256k1.go @@ -0,0 +1,10 @@ +// Copyright (c) 2015 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +// Auto-generated file (see genprecomps.go) +// DO NOT EDIT + +var secp256k1BytePoints = "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" diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/signature.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/signature.go new file mode 100644 index 0000000..9b27652 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/btcsuite/btcd/btcec/signature.go @@ -0,0 +1,539 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcec + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/sha256" + "errors" + "fmt" + "hash" + "math/big" +) + +// Errors returned by canonicalPadding. +var ( + errNegativeValue = errors.New("value may be interpreted as negative") + errExcessivelyPaddedValue = errors.New("value is excessively padded") +) + +// Signature is a type representing an ecdsa signature. +type Signature struct { + R *big.Int + S *big.Int +} + +var ( + // Curve order and halforder, used to tame ECDSA malleability (see BIP-0062) + order = new(big.Int).Set(S256().N) + halforder = new(big.Int).Rsh(order, 1) + + // Used in RFC6979 implementation when testing the nonce for correctness + one = big.NewInt(1) + + // oneInitializer is used to fill a byte slice with byte 0x01. It is provided + // here to avoid the need to create it multiple times. + oneInitializer = []byte{0x01} +) + +// Serialize returns the ECDSA signature in the more strict DER format. Note +// that the serialized bytes returned do not include the appended hash type +// used in Bitcoin signature scripts. +// +// encoding/asn1 is broken so we hand roll this output: +// +// 0x30 0x02 r 0x02 s +func (sig *Signature) Serialize() []byte { + // low 'S' malleability breaker + sigS := sig.S + if sigS.Cmp(halforder) == 1 { + sigS = new(big.Int).Sub(order, sigS) + } + // Ensure the encoded bytes for the r and s values are canonical and + // thus suitable for DER encoding. + rb := canonicalizeInt(sig.R) + sb := canonicalizeInt(sigS) + + // total length of returned signature is 1 byte for each magic and + // length (6 total), plus lengths of r and s + length := 6 + len(rb) + len(sb) + b := make([]byte, length, length) + + b[0] = 0x30 + b[1] = byte(length - 2) + b[2] = 0x02 + b[3] = byte(len(rb)) + offset := copy(b[4:], rb) + 4 + b[offset] = 0x02 + b[offset+1] = byte(len(sb)) + copy(b[offset+2:], sb) + return b +} + +// Verify calls ecdsa.Verify to verify the signature of hash using the public +// key. It returns true if the signature is valid, false otherwise. +func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool { + return ecdsa.Verify(pubKey.ToECDSA(), hash, sig.R, sig.S) +} + +// IsEqual compares this Signature instance to the one passed, returning true +// if both Signatures are equivalent. A signature is equivalent to another, if +// they both have the same scalar value for R and S. +func (sig *Signature) IsEqual(otherSig *Signature) bool { + return sig.R.Cmp(otherSig.R) == 0 && + sig.S.Cmp(otherSig.S) == 0 +} + +func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) { + // Originally this code used encoding/asn1 in order to parse the + // signature, but a number of problems were found with this approach. + // Despite the fact that signatures are stored as DER, the difference + // between go's idea of a bignum (and that they have sign) doesn't agree + // with the openssl one (where they do not). The above is true as of + // Go 1.1. In the end it was simpler to rewrite the code to explicitly + // understand the format which is this: + // 0x30 <0x02> 0x2 + // . + + signature := &Signature{} + + // minimal message is when both numbers are 1 bytes. adding up to: + // 0x30 + len + 0x02 + 0x01 + + 0x2 + 0x01 + + if len(sigStr) < 8 { + return nil, errors.New("malformed signature: too short") + } + // 0x30 + index := 0 + if sigStr[index] != 0x30 { + return nil, errors.New("malformed signature: no header magic") + } + index++ + // length of remaining message + siglen := sigStr[index] + index++ + if int(siglen+2) > len(sigStr) { + return nil, errors.New("malformed signature: bad length") + } + // trim the slice we're working on so we only look at what matters. + sigStr = sigStr[:siglen+2] + + // 0x02 + if sigStr[index] != 0x02 { + return nil, + errors.New("malformed signature: no 1st int marker") + } + index++ + + // Length of signature R. + rLen := int(sigStr[index]) + // must be positive, must be able to fit in another 0x2, + // hence the -3. We assume that the length must be at least one byte. + index++ + if rLen <= 0 || rLen > len(sigStr)-index-3 { + return nil, errors.New("malformed signature: bogus R length") + } + + // Then R itself. + rBytes := sigStr[index : index+rLen] + if der { + switch err := canonicalPadding(rBytes); err { + case errNegativeValue: + return nil, errors.New("signature R is negative") + case errExcessivelyPaddedValue: + return nil, errors.New("signature R is excessively padded") + } + } + signature.R = new(big.Int).SetBytes(rBytes) + index += rLen + // 0x02. length already checked in previous if. + if sigStr[index] != 0x02 { + return nil, errors.New("malformed signature: no 2nd int marker") + } + index++ + + // Length of signature S. + sLen := int(sigStr[index]) + index++ + // S should be the rest of the string. + if sLen <= 0 || sLen > len(sigStr)-index { + return nil, errors.New("malformed signature: bogus S length") + } + + // Then S itself. + sBytes := sigStr[index : index+sLen] + if der { + switch err := canonicalPadding(sBytes); err { + case errNegativeValue: + return nil, errors.New("signature S is negative") + case errExcessivelyPaddedValue: + return nil, errors.New("signature S is excessively padded") + } + } + signature.S = new(big.Int).SetBytes(sBytes) + index += sLen + + // sanity check length parsing + if index != len(sigStr) { + return nil, fmt.Errorf("malformed signature: bad final length %v != %v", + index, len(sigStr)) + } + + // Verify also checks this, but we can be more sure that we parsed + // correctly if we verify here too. + // FWIW the ecdsa spec states that R and S must be | 1, N - 1 | + // but crypto/ecdsa only checks for Sign != 0. Mirror that. + if signature.R.Sign() != 1 { + return nil, errors.New("signature R isn't 1 or more") + } + if signature.S.Sign() != 1 { + return nil, errors.New("signature S isn't 1 or more") + } + if signature.R.Cmp(curve.Params().N) >= 0 { + return nil, errors.New("signature R is >= curve.N") + } + if signature.S.Cmp(curve.Params().N) >= 0 { + return nil, errors.New("signature S is >= curve.N") + } + + return signature, nil +} + +// ParseSignature parses a signature in BER format for the curve type `curve' +// into a Signature type, perfoming some basic sanity checks. If parsing +// according to the more strict DER format is needed, use ParseDERSignature. +func ParseSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) { + return parseSig(sigStr, curve, false) +} + +// ParseDERSignature parses a signature in DER format for the curve type +// `curve` into a Signature type. If parsing according to the less strict +// BER format is needed, use ParseSignature. +func ParseDERSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) { + return parseSig(sigStr, curve, true) +} + +// canonicalizeInt returns the bytes for the passed big integer adjusted as +// necessary to ensure that a big-endian encoded integer can't possibly be +// misinterpreted as a negative number. This can happen when the most +// significant bit is set, so it is padded by a leading zero byte in this case. +// Also, the returned bytes will have at least a single byte when the passed +// value is 0. This is required for DER encoding. +func canonicalizeInt(val *big.Int) []byte { + b := val.Bytes() + if len(b) == 0 { + b = []byte{0x00} + } + if b[0]&0x80 != 0 { + paddedBytes := make([]byte, len(b)+1) + copy(paddedBytes[1:], b) + b = paddedBytes + } + return b +} + +// canonicalPadding checks whether a big-endian encoded integer could +// possibly be misinterpreted as a negative number (even though OpenSSL +// treats all numbers as unsigned), or if there is any unnecessary +// leading zero padding. +func canonicalPadding(b []byte) error { + switch { + case b[0]&0x80 == 0x80: + return errNegativeValue + case len(b) > 1 && b[0] == 0x00 && b[1]&0x80 != 0x80: + return errExcessivelyPaddedValue + default: + return nil + } +} + +// hashToInt converts a hash value to an integer. There is some disagreement +// about how this is done. [NSA] suggests that this is done in the obvious +// manner, but [SECG] truncates the hash to the bit-length of the curve order +// first. We follow [SECG] because that's what OpenSSL does. Additionally, +// OpenSSL right shifts excess bits from the number if the hash is too large +// and we mirror that too. +// This is borrowed from crypto/ecdsa. +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() + orderBytes := (orderBits + 7) / 8 + if len(hash) > orderBytes { + hash = hash[:orderBytes] + } + + ret := new(big.Int).SetBytes(hash) + excess := len(hash)*8 - orderBits + if excess > 0 { + ret.Rsh(ret, uint(excess)) + } + return ret +} + +// recoverKeyFromSignature recoves a public key from the signature "sig" on the +// given message hash "msg". Based on the algorithm found in section 5.1.5 of +// SEC 1 Ver 2.0, page 47-48 (53 and 54 in the pdf). This performs the details +// in the inner loop in Step 1. The counter provided is actually the j parameter +// of the loop * 2 - on the first iteration of j we do the R case, else the -R +// case in step 1.6. This counter is used in the bitcoin compressed signature +// format and thus we match bitcoind's behaviour here. +func recoverKeyFromSignature(curve *KoblitzCurve, sig *Signature, msg []byte, + iter int, doChecks bool) (*PublicKey, error) { + // 1.1 x = (n * i) + r + Rx := new(big.Int).Mul(curve.Params().N, + new(big.Int).SetInt64(int64(iter/2))) + Rx.Add(Rx, sig.R) + if Rx.Cmp(curve.Params().P) != -1 { + return nil, errors.New("calculated Rx is larger than curve P") + } + + // convert 02 to point R. (step 1.2 and 1.3). If we are on an odd + // iteration then 1.6 will be done with -R, so we calculate the other + // term when uncompressing the point. + Ry, err := decompressPoint(curve, Rx, iter%2 == 1) + if err != nil { + return nil, err + } + + // 1.4 Check n*R is point at infinity + if doChecks { + nRx, nRy := curve.ScalarMult(Rx, Ry, curve.Params().N.Bytes()) + if nRx.Sign() != 0 || nRy.Sign() != 0 { + return nil, errors.New("n*R does not equal the point at infinity") + } + } + + // 1.5 calculate e from message using the same algorithm as ecdsa + // signature calculation. + e := hashToInt(msg, curve) + + // Step 1.6.1: + // We calculate the two terms sR and eG separately multiplied by the + // inverse of r (from the signature). We then add them to calculate + // Q = r^-1(sR-eG) + invr := new(big.Int).ModInverse(sig.R, curve.Params().N) + + // first term. + invrS := new(big.Int).Mul(invr, sig.S) + invrS.Mod(invrS, curve.Params().N) + sRx, sRy := curve.ScalarMult(Rx, Ry, invrS.Bytes()) + + // second term. + e.Neg(e) + e.Mod(e, curve.Params().N) + e.Mul(e, invr) + e.Mod(e, curve.Params().N) + minuseGx, minuseGy := curve.ScalarBaseMult(e.Bytes()) + + // TODO: this would be faster if we did a mult and add in one + // step to prevent the jacobian conversion back and forth. + Qx, Qy := curve.Add(sRx, sRy, minuseGx, minuseGy) + + return &PublicKey{ + Curve: curve, + X: Qx, + Y: Qy, + }, nil +} + +// SignCompact produces a compact signature of the data in hash with the given +// private key on the given koblitz curve. The isCompressed parameter should +// be used to detail if the given signature should reference a compressed +// public key or not. If successful the bytes of the compact signature will be +// returned in the format: +// <(byte of 27+public key solution)+4 if compressed >< padded bytes for signature R> +// where the R and S parameters are padde up to the bitlengh of the curve. +func SignCompact(curve *KoblitzCurve, key *PrivateKey, + hash []byte, isCompressedKey bool) ([]byte, error) { + sig, err := key.Sign(hash) + if err != nil { + return nil, err + } + + // bitcoind checks the bit length of R and S here. The ecdsa signature + // algorithm returns R and S mod N therefore they will be the bitsize of + // the curve, and thus correctly sized. + for i := 0; i < (curve.H+1)*2; i++ { + pk, err := recoverKeyFromSignature(curve, sig, hash, i, true) + if err == nil && pk.X.Cmp(key.X) == 0 && pk.Y.Cmp(key.Y) == 0 { + result := make([]byte, 1, 2*curve.byteSize+1) + result[0] = 27 + byte(i) + if isCompressedKey { + result[0] += 4 + } + // Not sure this needs rounding but safer to do so. + curvelen := (curve.BitSize + 7) / 8 + + // Pad R and S to curvelen if needed. + bytelen := (sig.R.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, + make([]byte, curvelen-bytelen)...) + } + result = append(result, sig.R.Bytes()...) + + bytelen = (sig.S.BitLen() + 7) / 8 + if bytelen < curvelen { + result = append(result, + make([]byte, curvelen-bytelen)...) + } + result = append(result, sig.S.Bytes()...) + + return result, nil + } + } + + return nil, errors.New("no valid solution for pubkey found") +} + +// RecoverCompact verifies the compact signature "signature" of "hash" for the +// Koblitz curve in "curve". If the signature matches then the recovered public +// key will be returned as well as a boolen if the original key was compressed +// or not, else an error will be returned. +func RecoverCompact(curve *KoblitzCurve, signature, + hash []byte) (*PublicKey, bool, error) { + bitlen := (curve.BitSize + 7) / 8 + if len(signature) != 1+bitlen*2 { + return nil, false, errors.New("invalid compact signature size") + } + + iteration := int((signature[0] - 27) & ^byte(4)) + + // format is
+ sig := &Signature{ + R: new(big.Int).SetBytes(signature[1 : bitlen+1]), + S: new(big.Int).SetBytes(signature[bitlen+1:]), + } + // The iteration used here was encoded + key, err := recoverKeyFromSignature(curve, sig, hash, iteration, false) + if err != nil { + return nil, false, err + } + + return key, ((signature[0] - 27) & 4) == 4, nil +} + +// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979 and BIP 62. +func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) { + + privkey := privateKey.ToECDSA() + N := order + k := nonceRFC6979(privkey.D, hash) + inv := new(big.Int).ModInverse(k, N) + r, _ := privkey.Curve.ScalarBaseMult(k.Bytes()) + if r.Cmp(N) == 1 { + r.Sub(r, N) + } + + if r.Sign() == 0 { + return nil, errors.New("calculated R is zero") + } + + e := hashToInt(hash, privkey.Curve) + s := new(big.Int).Mul(privkey.D, r) + s.Add(s, e) + s.Mul(s, inv) + s.Mod(s, N) + + if s.Cmp(halforder) == 1 { + s.Sub(N, s) + } + if s.Sign() == 0 { + return nil, errors.New("calculated S is zero") + } + return &Signature{R: r, S: s}, nil +} + +// nonceRFC6979 generates an ECDSA nonce (`k`) deterministically according to RFC 6979. +// It takes a 32-byte hash as an input and returns 32-byte nonce to be used in ECDSA algorithm. +func nonceRFC6979(privkey *big.Int, hash []byte) *big.Int { + + curve := S256() + q := curve.Params().N + x := privkey + alg := sha256.New + + qlen := q.BitLen() + holen := alg().Size() + rolen := (qlen + 7) >> 3 + bx := append(int2octets(x, rolen), bits2octets(hash, curve, rolen)...) + + // Step B + v := bytes.Repeat(oneInitializer, holen) + + // Step C (Go zeroes the all allocated memory) + k := make([]byte, holen) + + // Step D + k = mac(alg, k, append(append(v, 0x00), bx...)) + + // Step E + v = mac(alg, k, v) + + // Step F + k = mac(alg, k, append(append(v, 0x01), bx...)) + + // Step G + v = mac(alg, k, v) + + // Step H + for { + // Step H1 + var t []byte + + // Step H2 + for len(t)*8 < qlen { + v = mac(alg, k, v) + t = append(t, v...) + } + + // Step H3 + secret := hashToInt(t, curve) + if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 { + return secret + } + k = mac(alg, k, append(v, 0x00)) + v = mac(alg, k, v) + } +} + +// mac returns an HMAC of the given key and message. +func mac(alg func() hash.Hash, k, m []byte) []byte { + h := hmac.New(alg, k) + h.Write(m) + return h.Sum(nil) +} + +// https://tools.ietf.org/html/rfc6979#section-2.3.3 +func int2octets(v *big.Int, rolen int) []byte { + out := v.Bytes() + + // left pad with zeros if it's too short + if len(out) < rolen { + out2 := make([]byte, rolen) + copy(out2[rolen-len(out):], out) + return out2 + } + + // drop most significant bytes if it's too long + if len(out) > rolen { + out2 := make([]byte, rolen) + copy(out2, out[len(out)-rolen:]) + return out2 + } + + return out +} + +// https://tools.ietf.org/html/rfc6979#section-2.3.4 +func bits2octets(in []byte, curve elliptic.Curve, rolen int) []byte { + z1 := hashToInt(in, curve) + z2 := new(big.Int).Sub(z1, curve.Params().N) + if z2.Sign() < 0 { + return int2octets(z1, rolen) + } + return int2octets(z2, rolen) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/LICENSE.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/LICENSE.txt new file mode 100644 index 0000000..70da676 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2015 Caleb Spare + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/README.md new file mode 100644 index 0000000..577701f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/README.md @@ -0,0 +1,9 @@ +# cp + +[![GoDoc](https://godoc.org/github.com/cespare/cp?status.svg)](https://godoc.org/github.com/cespare/cp) + +cp is a small Go package for copying files and directories. + +The API may change because I want to add some options in the future (for merging with existing dirs). + +It does not currently handle Windows specifically (I think it may require some special treatment). diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/cp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/cp.go new file mode 100644 index 0000000..d71dbb4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/cespare/cp/cp.go @@ -0,0 +1,58 @@ +// Package cp offers simple file and directory copying for Go. +package cp + +import ( + "errors" + "io" + "os" + "path/filepath" + "strings" +) + +var errCopyFileWithDir = errors.New("dir argument to CopyFile") + +// CopyFile copies the file with path src to dst. The new file must not exist. +// It is created with the same permissions as src. +func CopyFile(dst, src string) error { + rf, err := os.Open(src) + if err != nil { + return err + } + defer rf.Close() + rstat, err := rf.Stat() + if err != nil { + return err + } + if rstat.IsDir() { + return errCopyFileWithDir + } + + wf, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, rstat.Mode()) + if err != nil { + return err + } + if _, err := io.Copy(wf, rf); err != nil { + wf.Close() + return err + } + return wf.Close() +} + +// CopyAll copies the file or (recursively) the directory at src to dst. +// Permissions are preserved. dst must not already exist. +func CopyAll(dst, src string) error { + return filepath.Walk(src, makeWalkFn(dst, src)) +} + +func makeWalkFn(dst, src string) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + dstPath := filepath.Join(dst, strings.TrimPrefix(path, src)) + if info.IsDir() { + return os.Mkdir(dstPath, info.Mode()) + } + return CopyFile(dstPath, path) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.gitignore b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.travis.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.travis.yml new file mode 100644 index 0000000..984e073 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/.travis.yml @@ -0,0 +1,14 @@ +language: go +go: + - 1.5.4 + - 1.6.3 + - 1.7 +install: + - go get -v golang.org/x/tools/cmd/cover +script: + - go test -v -tags=safe ./spew + - go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov +after_success: + - go get -v github.com/mattn/goveralls + - export PATH=$PATH:$HOME/gopath/bin + - goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 0000000..c836416 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/README.md new file mode 100644 index 0000000..2624304 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/README.md @@ -0,0 +1,205 @@ +go-spew +======= + +[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)] +(https://travis-ci.org/davecgh/go-spew) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status] +(https://img.shields.io/coveralls/davecgh/go-spew.svg)] +(https://coveralls.io/r/davecgh/go-spew?branch=master) + + +Go-spew implements a deep pretty printer for Go data structures to aid in +debugging. A comprehensive suite of tests with 100% test coverage is provided +to ensure proper functionality. See `test_coverage.txt` for the gocov coverage +report. Go-spew is licensed under the liberal ISC license, so it may be used in +open source or commercial projects. + +If you're interested in reading about how this package came to life and some +of the challenges involved in providing a deep pretty printer, there is a blog +post about it +[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/). + +## Documentation + +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)] +(http://godoc.org/github.com/davecgh/go-spew/spew) + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the excellent GoDoc site here: +http://godoc.org/github.com/davecgh/go-spew/spew + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/davecgh/go-spew/spew + +## Installation + +```bash +$ go get -u github.com/davecgh/go-spew/spew +``` + +## Quick Start + +Add this import line to the file you're working in: + +```Go +import "github.com/davecgh/go-spew/spew" +``` + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + +```Go +spew.Dump(myVar1, myVar2, ...) +spew.Fdump(someWriter, myVar1, myVar2, ...) +str := spew.Sdump(myVar1, myVar2, ...) +``` + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most +compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types +and pointer addresses): + +```Go +spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) +spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) +spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) +spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) +``` + +## Debugging a Web Application Example + +Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production. + +```Go +package main + +import ( + "fmt" + "html" + "net/http" + + "github.com/davecgh/go-spew/spew" +) + +func handler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:]) + fmt.Fprintf(w, "") +} + +func main() { + http.HandleFunc("/", handler) + http.ListenAndServe(":8080", nil) +} +``` + +## Sample Dump Output + +``` +(main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) { + (string) "one": (bool) true + } +} +([]uint8) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| +} +``` + +## Sample Formatter Output + +Double pointer to a uint8: +``` + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 +``` + +Pointer to circular struct with a uint8 field and a pointer to itself: +``` + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} +``` + +## Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available via the +spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +``` +* Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + +* MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + +* DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + +* DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. This option + relies on access to the unsafe package, so it will not have any effect when + running in environments without access to the unsafe package such as Google + App Engine or with the "safe" build tag specified. + Pointer method invocation is enabled by default. + +* DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + +* DisableCapacities + DisableCapacities specifies whether to disable the printing of capacities + for arrays, slices, maps and channels. This is useful when diffing data + structures in tests. + +* ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + +* SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are supported, + with other types sorted according to the reflect.Value.String() output + which guarantees display stability. Natural map order is used by + default. + +* SpewKeys + SpewKeys specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only considered + if SortKeys is true. + +``` + +## Unsafe Package Dependency + +This package relies on the unsafe package to perform some of the more advanced +features, however it also supports a "limited" mode which allows it to work in +environments where the unsafe package is not available. By default, it will +operate in this mode on Google App Engine and when compiled with GopherJS. The +"safe" build tag may also be specified to force the package to build without +using the unsafe package. + +## License + +Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/cov_report.sh b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/cov_report.sh new file mode 100644 index 0000000..9579497 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/cov_report.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +if ! type gocov >/dev/null 2>&1; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi + +# Only run the cgo tests if gcc is installed. +if type gcc >/dev/null 2>&1; then + (cd spew && gocov test -tags testcgo | gocov report) +else + (cd spew && gocov test | gocov report) +fi diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypass.go new file mode 100644 index 0000000..8a4a658 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -0,0 +1,152 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build !js,!appengine,!safe,!disableunsafe + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +var ( + // offsetPtr, offsetScalar, and offsetFlag are the offsets for the + // internal reflect.Value fields. These values are valid before golang + // commit ecccf07e7f9d which changed the format. The are also valid + // after commit 82f48826c6c7 which changed the format again to mirror + // the original format. Code in the init function updates these offsets + // as necessary. + offsetPtr = uintptr(ptrSize) + offsetScalar = uintptr(0) + offsetFlag = uintptr(ptrSize * 2) + + // flagKindWidth and flagKindShift indicate various bits that the + // reflect package uses internally to track kind information. + // + // flagRO indicates whether or not the value field of a reflect.Value is + // read-only. + // + // flagIndir indicates whether the value field of a reflect.Value is + // the actual data or a pointer to the data. + // + // These values are valid before golang commit 90a7c3c86944 which + // changed their positions. Code in the init function updates these + // flags as necessary. + flagKindWidth = uintptr(5) + flagKindShift = uintptr(flagKindWidth - 1) + flagRO = uintptr(1 << 0) + flagIndir = uintptr(1 << 1) +) + +func init() { + // Older versions of reflect.Value stored small integers directly in the + // ptr field (which is named val in the older versions). Versions + // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named + // scalar for this purpose which unfortunately came before the flag + // field, so the offset of the flag field is different for those + // versions. + // + // This code constructs a new reflect.Value from a known small integer + // and checks if the size of the reflect.Value struct indicates it has + // the scalar field. When it does, the offsets are updated accordingly. + vv := reflect.ValueOf(0xf00) + if unsafe.Sizeof(vv) == (ptrSize * 4) { + offsetScalar = ptrSize * 2 + offsetFlag = ptrSize * 3 + } + + // Commit 90a7c3c86944 changed the flag positions such that the low + // order bits are the kind. This code extracts the kind from the flags + // field and ensures it's the correct type. When it's not, the flag + // order has been changed to the newer format, so the flags are updated + // accordingly. + upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) + upfv := *(*uintptr)(upf) + flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { + flagKindShift = 0 + flagRO = 1 << 5 + flagIndir = 1 << 6 + + // Commit adf9b30e5594 modified the flags to separate the + // flagRO flag into two bits which specifies whether or not the + // field is embedded. This causes flagIndir to move over a bit + // and means that flagRO is the combination of either of the + // original flagRO bit and the new bit. + // + // This code detects the change by extracting what used to be + // the indirect bit to ensure it's set. When it's not, the flag + // order has been changed to the newer format, so the flags are + // updated accordingly. + if upfv&flagIndir == 0 { + flagRO = 3 << 5 + flagIndir = 1 << 7 + } + } +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { + indirects := 1 + vt := v.Type() + upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) + rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) + if rvf&flagIndir != 0 { + vt = reflect.PtrTo(v.Type()) + indirects++ + } else if offsetScalar != 0 { + // The value is in the scalar field when it's not one of the + // reference types. + switch vt.Kind() { + case reflect.Uintptr: + case reflect.Chan: + case reflect.Func: + case reflect.Map: + case reflect.Ptr: + case reflect.UnsafePointer: + default: + upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + + offsetScalar) + } + } + + pv := reflect.NewAt(vt, upv) + rv = pv + for i := 0; i < indirects; i++ { + rv = rv.Elem() + } + return rv +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go new file mode 100644 index 0000000..1fe3cf3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/common.go new file mode 100644 index 0000000..7c519ff --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/config.go new file mode 100644 index 0000000..2e3d22f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/doc.go new file mode 100644 index 0000000..aacaac6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 0000000..df1d582 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound == true: + d.w.Write(nilAngleBytes) + + case cycleFound == true: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/format.go new file mode 100644 index 0000000..c49875b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound == true: + f.fs.Write(nilAngleBytes) + + case cycleFound == true: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/spew.go new file mode 100644 index 0000000..32c0e33 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/test_coverage.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/test_coverage.txt new file mode 100644 index 0000000..2cd087a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/davecgh/go-spew/test_coverage.txt @@ -0,0 +1,61 @@ + +github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88) +github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82) +github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52) +github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44) +github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39) +github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30) +github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18) +github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13) +github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12) +github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11) +github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11) +github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10) +github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8) +github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7) +github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5) +github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4) +github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4) +github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4) +github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4) +github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3) +github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3) +github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3) +github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3) +github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3) +github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1) +github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1) +github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1) +github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1) +github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1) +github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1) +github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1) +github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505) + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/CMakeLists.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/CMakeLists.txt new file mode 100644 index 0000000..807c43e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8.7) +project(ethash) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") +set(ETHHASH_LIBS ethash) + +if (WIN32 AND WANT_CRYPTOPP) + add_subdirectory(cryptopp) +endif() + +add_subdirectory(src/libethash) + +add_subdirectory(src/benchmark EXCLUDE_FROM_ALL) +add_subdirectory(test/c) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/MANIFEST.in b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/MANIFEST.in new file mode 100644 index 0000000..74e73c8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/MANIFEST.in @@ -0,0 +1,17 @@ +include setup.py + +# C sources +include src/libethash/internal.c +include src/libethash/sha3.c +include src/libethash/util.c +include src/python/core.c + +# Headers +include src/libethash/compiler.h +include src/libethash/data_sizes.h +include src/libethash/endian.h +include src/libethash/ethash.h +include src/libethash/fnv.h +include src/libethash/internal.h +include src/libethash/sha3.h +include src/libethash/util.h diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Makefile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Makefile new file mode 100644 index 0000000..741d3b5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Makefile @@ -0,0 +1,6 @@ +.PHONY: clean test +test: + ./test/test.sh + +clean: + rm -rf *.so pyethash.egg-info/ build/ test/python/python-virtual-env/ test/c/build/ pyethash.so test/python/*.pyc dist/ MANIFEST diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/README.md new file mode 100644 index 0000000..2b2c3b5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/README.md @@ -0,0 +1,22 @@ +[![Build Status](https://travis-ci.org/ethereum/ethash.svg?branch=master)](https://travis-ci.org/ethereum/ethash) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/debris/ethash?branch=master&svg=true)](https://ci.appveyor.com/project/debris/ethash-nr37r/branch/master) + +# Ethash + +For details on this project, please see the Ethereum wiki: +https://github.com/ethereum/wiki/wiki/Ethash + +### Coding Style for C++ code: + +Follow the same exact style as in [cpp-ethereum](https://github.com/ethereum/cpp-ethereum/blob/develop/CodingStandards.txt) + +### Coding Style for C code: + +The main thing above all is code consistency. + +- Tabs for indentation. A tab is 4 spaces +- Try to stick to the [K&R](http://en.wikipedia.org/wiki/Indent_style#K.26R_style), + especially for the C code. +- Keep the line lengths reasonable. No hard limit on 80 characters but don't go further + than 110. Some people work with multiple buffers next to each other. + Make them like you :) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Vagrantfile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Vagrantfile new file mode 100644 index 0000000..0389165 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/Vagrantfile @@ -0,0 +1,7 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure(2) do |config| + config.vm.box = "Ubuntu 12.04" + config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box" +end diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/appveyor.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/appveyor.yml new file mode 100644 index 0000000..ac36a06 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/appveyor.yml @@ -0,0 +1,43 @@ +version: 1.0.0.{build} + +environment: + BOOST_ROOT: "c:/projects/ethash/deps/boost" + +branches: + only: + - master + - develop + +os: Windows Server 2012 R2 + +clone_folder: c:\projects\ethash + +#platform: Any CPU +#configuration: Debug + +install: + # by default, all script lines are interpreted as batch + +# scripts to run before build +before_build: + - echo "Downloading boost..." + - mkdir c:\projects\ethash\deps + - cd c:\projects\ethash\deps + - curl -O https://build.ethdev.com/builds/windows-precompiled/boost.tar.gz + - echo "Unzipping boost..." + - 7z x boost.tar.gz > nul + - 7z x boost.tar > nul + - ls + - echo "Running cmake..." + - cd c:\projects\ethash + - cmake . + +build: + project: ALL_BUILD.vcxproj # path to Visual Studio solution or project + +after_build: + - echo "Running tests..." + - cd c:\projects\ethash\test\c\Debug + - Test.exe + - echo "Finished!" + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethash.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethash.go new file mode 100644 index 0000000..2a31aaf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethash.go @@ -0,0 +1,441 @@ +// Copyright 2015 The go-ethereum Authors +// Copyright 2015 Lefteris Karapetsas +// Copyright 2015 Matthew Wampler-Doty +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethash + +/* +#include "src/libethash/internal.h" + +int ethashGoCallback_cgo(unsigned); +*/ +import "C" + +import ( + "errors" + "fmt" + "io/ioutil" + "math/big" + "math/rand" + "os" + "os/user" + "path/filepath" + "runtime" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/pow" +) + +var ( + maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) + sharedLight = new(Light) +) + +const ( + epochLength uint64 = 30000 + cacheSizeForTesting C.uint64_t = 1024 + dagSizeForTesting C.uint64_t = 1024 * 32 +) + +var DefaultDir = defaultDir() + +func defaultDir() string { + home := os.Getenv("HOME") + if user, err := user.Current(); err == nil { + home = user.HomeDir + } + if runtime.GOOS == "windows" { + return filepath.Join(home, "AppData", "Ethash") + } + return filepath.Join(home, ".ethash") +} + +// cache wraps an ethash_light_t with some metadata +// and automatic memory management. +type cache struct { + epoch uint64 + used time.Time + test bool + + gen sync.Once // ensures cache is only generated once. + ptr *C.struct_ethash_light +} + +// generate creates the actual cache. it can be called from multiple +// goroutines. the first call will generate the cache, subsequent +// calls wait until it is generated. +func (cache *cache) generate() { + cache.gen.Do(func() { + started := time.Now() + seedHash := makeSeedHash(cache.epoch) + glog.V(logger.Debug).Infof("Generating cache for epoch %d (%x)", cache.epoch, seedHash) + size := C.ethash_get_cachesize(C.uint64_t(cache.epoch * epochLength)) + if cache.test { + size = cacheSizeForTesting + } + cache.ptr = C.ethash_light_new_internal(size, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0]))) + runtime.SetFinalizer(cache, freeCache) + glog.V(logger.Debug).Infof("Done generating cache for epoch %d, it took %v", cache.epoch, time.Since(started)) + }) +} + +func freeCache(cache *cache) { + C.ethash_light_delete(cache.ptr) + cache.ptr = nil +} + +func (cache *cache) compute(dagSize uint64, hash common.Hash, nonce uint64) (ok bool, mixDigest, result common.Hash) { + ret := C.ethash_light_compute_internal(cache.ptr, C.uint64_t(dagSize), hashToH256(hash), C.uint64_t(nonce)) + // Make sure cache is live until after the C call. + // This is important because a GC might happen and execute + // the finalizer before the call completes. + _ = cache + return bool(ret.success), h256ToHash(ret.mix_hash), h256ToHash(ret.result) +} + +// Light implements the Verify half of the proof of work. It uses a few small +// in-memory caches to verify the nonces found by Full. +type Light struct { + test bool // If set, use a smaller cache size + + mu sync.Mutex // Protects the per-epoch map of verification caches + caches map[uint64]*cache // Currently maintained verification caches + future *cache // Pre-generated cache for the estimated future DAG + + NumCaches int // Maximum number of caches to keep before eviction (only init, don't modify) +} + +// Verify checks whether the block's nonce is valid. +func (l *Light) Verify(block pow.Block) bool { + // TODO: do ethash_quick_verify before getCache in order + // to prevent DOS attacks. + blockNum := block.NumberU64() + if blockNum >= epochLength*2048 { + glog.V(logger.Debug).Infof("block number %d too high, limit is %d", epochLength*2048) + return false + } + + difficulty := block.Difficulty() + /* Cannot happen if block header diff is validated prior to PoW, but can + happen if PoW is checked first due to parallel PoW checking. + We could check the minimum valid difficulty but for SoC we avoid (duplicating) + Ethereum protocol consensus rules here which are not in scope of Ethash + */ + if difficulty.Cmp(common.Big0) == 0 { + glog.V(logger.Debug).Infof("invalid block difficulty") + return false + } + + cache := l.getCache(blockNum) + dagSize := C.ethash_get_datasize(C.uint64_t(blockNum)) + if l.test { + dagSize = dagSizeForTesting + } + // Recompute the hash using the cache. + ok, mixDigest, result := cache.compute(uint64(dagSize), block.HashNoNonce(), block.Nonce()) + if !ok { + return false + } + + // avoid mixdigest malleability as it's not included in a block's "hashNononce" + if block.MixDigest() != mixDigest { + return false + } + + // The actual check. + target := new(big.Int).Div(maxUint256, difficulty) + return result.Big().Cmp(target) <= 0 +} + +func h256ToHash(in C.ethash_h256_t) common.Hash { + return *(*common.Hash)(unsafe.Pointer(&in.b)) +} + +func hashToH256(in common.Hash) C.ethash_h256_t { + return C.ethash_h256_t{b: *(*[32]C.uint8_t)(unsafe.Pointer(&in[0]))} +} + +func (l *Light) getCache(blockNum uint64) *cache { + var c *cache + epoch := blockNum / epochLength + + // If we have a PoW for that epoch, use that + l.mu.Lock() + if l.caches == nil { + l.caches = make(map[uint64]*cache) + } + if l.NumCaches == 0 { + l.NumCaches = 3 + } + c = l.caches[epoch] + if c == nil { + // No cached DAG, evict the oldest if the cache limit was reached + if len(l.caches) >= l.NumCaches { + var evict *cache + for _, cache := range l.caches { + if evict == nil || evict.used.After(cache.used) { + evict = cache + } + } + glog.V(logger.Debug).Infof("Evicting DAG for epoch %d in favour of epoch %d", evict.epoch, epoch) + delete(l.caches, evict.epoch) + } + // If we have the new DAG pre-generated, use that, otherwise create a new one + if l.future != nil && l.future.epoch == epoch { + glog.V(logger.Debug).Infof("Using pre-generated DAG for epoch %d", epoch) + c, l.future = l.future, nil + } else { + glog.V(logger.Debug).Infof("No pre-generated DAG available, creating new for epoch %d", epoch) + c = &cache{epoch: epoch, test: l.test} + } + l.caches[epoch] = c + + // If we just used up the future cache, or need a refresh, regenerate + if l.future == nil || l.future.epoch <= epoch { + glog.V(logger.Debug).Infof("Pre-generating DAG for epoch %d", epoch+1) + l.future = &cache{epoch: epoch + 1, test: l.test} + go l.future.generate() + } + } + c.used = time.Now() + l.mu.Unlock() + + // Wait for generation finish and return the cache + c.generate() + return c +} + +// dag wraps an ethash_full_t with some metadata +// and automatic memory management. +type dag struct { + epoch uint64 + test bool + dir string + + gen sync.Once // ensures DAG is only generated once. + ptr *C.struct_ethash_full +} + +// generate creates the actual DAG. it can be called from multiple +// goroutines. the first call will generate the DAG, subsequent +// calls wait until it is generated. +func (d *dag) generate() { + d.gen.Do(func() { + var ( + started = time.Now() + seedHash = makeSeedHash(d.epoch) + blockNum = C.uint64_t(d.epoch * epochLength) + cacheSize = C.ethash_get_cachesize(blockNum) + dagSize = C.ethash_get_datasize(blockNum) + ) + if d.test { + cacheSize = cacheSizeForTesting + dagSize = dagSizeForTesting + } + if d.dir == "" { + d.dir = DefaultDir + } + glog.V(logger.Info).Infof("Generating DAG for epoch %d (size %d) (%x)", d.epoch, dagSize, seedHash) + // Generate a temporary cache. + // TODO: this could share the cache with Light + cache := C.ethash_light_new_internal(cacheSize, (*C.ethash_h256_t)(unsafe.Pointer(&seedHash[0]))) + defer C.ethash_light_delete(cache) + // Generate the actual DAG. + d.ptr = C.ethash_full_new_internal( + C.CString(d.dir), + hashToH256(seedHash), + dagSize, + cache, + (C.ethash_callback_t)(unsafe.Pointer(C.ethashGoCallback_cgo)), + ) + if d.ptr == nil { + panic("ethash_full_new IO or memory error") + } + runtime.SetFinalizer(d, freeDAG) + glog.V(logger.Info).Infof("Done generating DAG for epoch %d, it took %v", d.epoch, time.Since(started)) + }) +} + +func freeDAG(d *dag) { + C.ethash_full_delete(d.ptr) + d.ptr = nil +} + +func (d *dag) Ptr() unsafe.Pointer { + return unsafe.Pointer(d.ptr.data) +} + +//export ethashGoCallback +func ethashGoCallback(percent C.unsigned) C.int { + glog.V(logger.Info).Infof("Generating DAG: %d%%", percent) + return 0 +} + +// MakeDAG pre-generates a DAG file for the given block number in the +// given directory. If dir is the empty string, the default directory +// is used. +func MakeDAG(blockNum uint64, dir string) error { + d := &dag{epoch: blockNum / epochLength, dir: dir} + if blockNum >= epochLength*2048 { + return fmt.Errorf("block number too high, limit is %d", epochLength*2048) + } + d.generate() + if d.ptr == nil { + return errors.New("failed") + } + return nil +} + +// Full implements the Search half of the proof of work. +type Full struct { + Dir string // use this to specify a non-default DAG directory + + test bool // if set use a smaller DAG size + turbo bool + hashRate int32 + + mu sync.Mutex // protects dag + current *dag // current full DAG +} + +func (pow *Full) getDAG(blockNum uint64) (d *dag) { + epoch := blockNum / epochLength + pow.mu.Lock() + if pow.current != nil && pow.current.epoch == epoch { + d = pow.current + } else { + d = &dag{epoch: epoch, test: pow.test, dir: pow.Dir} + pow.current = d + } + pow.mu.Unlock() + // wait for it to finish generating. + d.generate() + return d +} + +func (pow *Full) Search(block pow.Block, stop <-chan struct{}, index int) (nonce uint64, mixDigest []byte) { + dag := pow.getDAG(block.NumberU64()) + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + diff := block.Difficulty() + + i := int64(0) + starti := i + start := time.Now().UnixNano() + previousHashrate := int32(0) + + nonce = uint64(r.Int63()) + hash := hashToH256(block.HashNoNonce()) + target := new(big.Int).Div(maxUint256, diff) + for { + select { + case <-stop: + atomic.AddInt32(&pow.hashRate, -previousHashrate) + return 0, nil + default: + i++ + + // we don't have to update hash rate on every nonce, so update after + // first nonce check and then after 2^X nonces + if i == 2 || ((i % (1 << 16)) == 0) { + elapsed := time.Now().UnixNano() - start + hashes := (float64(1e9) / float64(elapsed)) * float64(i-starti) + hashrateDiff := int32(hashes) - previousHashrate + previousHashrate = int32(hashes) + atomic.AddInt32(&pow.hashRate, hashrateDiff) + } + + ret := C.ethash_full_compute(dag.ptr, hash, C.uint64_t(nonce)) + result := h256ToHash(ret.result).Big() + + // TODO: disagrees with the spec https://github.com/ethereum/wiki/wiki/Ethash#mining + if ret.success && result.Cmp(target) <= 0 { + mixDigest = C.GoBytes(unsafe.Pointer(&ret.mix_hash), C.int(32)) + atomic.AddInt32(&pow.hashRate, -previousHashrate) + return nonce, mixDigest + } + nonce += 1 + } + + if !pow.turbo { + time.Sleep(20 * time.Microsecond) + } + } +} + +func (pow *Full) GetHashrate() int64 { + return int64(atomic.LoadInt32(&pow.hashRate)) +} + +func (pow *Full) Turbo(on bool) { + // TODO: this needs to use an atomic operation. + pow.turbo = on +} + +// Ethash combines block verification with Light and +// nonce searching with Full into a single proof of work. +type Ethash struct { + *Light + *Full +} + +// New creates an instance of the proof of work. +func New() *Ethash { + return &Ethash{new(Light), &Full{turbo: true}} +} + +// NewShared creates an instance of the proof of work., where a single instance +// of the Light cache is shared across all instances created with NewShared. +func NewShared() *Ethash { + return &Ethash{sharedLight, &Full{turbo: true}} +} + +// NewForTesting creates a proof of work for use in unit tests. +// It uses a smaller DAG and cache size to keep test times low. +// DAG files are stored in a temporary directory. +// +// Nonces found by a testing instance are not verifiable with a +// regular-size cache. +func NewForTesting() (*Ethash, error) { + dir, err := ioutil.TempDir("", "ethash-test") + if err != nil { + return nil, err + } + return &Ethash{&Light{test: true}, &Full{Dir: dir, test: true}}, nil +} + +func GetSeedHash(blockNum uint64) ([]byte, error) { + if blockNum >= epochLength*2048 { + return nil, fmt.Errorf("block number too high, limit is %d", epochLength*2048) + } + sh := makeSeedHash(blockNum / epochLength) + return sh[:], nil +} + +func makeSeedHash(epoch uint64) (sh common.Hash) { + for ; epoch > 0; epoch-- { + sh = crypto.Sha3Hash(sh[:]) + } + return sh +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethashc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethashc.go new file mode 100644 index 0000000..1d2ba16 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/ethashc.go @@ -0,0 +1,51 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethash + +/* + -mno-stack-arg-probe disables stack probing which avoids the function + __chkstk_ms being linked. this avoids a clash of this symbol as we also + separately link the secp256k1 lib which ends up defining this symbol + + 1. https://gcc.gnu.org/onlinedocs/gccint/Stack-Checking.html + 2. https://groups.google.com/forum/#!msg/golang-dev/v1bziURSQ4k/88fXuJ24e-gJ + 3. https://groups.google.com/forum/#!topic/golang-nuts/VNP6Mwz_B6o + +*/ + +/* +#cgo CFLAGS: -std=gnu99 -Wall +#cgo windows CFLAGS: -mno-stack-arg-probe +#cgo LDFLAGS: -lm + +#include "src/libethash/internal.c" +#include "src/libethash/sha3.c" +#include "src/libethash/io.c" + +#ifdef _WIN32 +# include "src/libethash/io_win32.c" +# include "src/libethash/mmap_win32.c" +#else +# include "src/libethash/io_posix.c" +#endif + +// 'gateway function' for calling back into go. +extern int ethashGoCallback(unsigned); +int ethashGoCallback_cgo(unsigned percent) { return ethashGoCallback(percent); } + +*/ +import "C" diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/setup.py b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/setup.py new file mode 100755 index 0000000..18aa20f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +import os +from distutils.core import setup, Extension +sources = [ + 'src/python/core.c', + 'src/libethash/io.c', + 'src/libethash/internal.c', + 'src/libethash/sha3.c'] +if os.name == 'nt': + sources += [ + 'src/libethash/util_win32.c', + 'src/libethash/io_win32.c', + 'src/libethash/mmap_win32.c', + ] +else: + sources += [ + 'src/libethash/io_posix.c' + ] +depends = [ + 'src/libethash/ethash.h', + 'src/libethash/compiler.h', + 'src/libethash/data_sizes.h', + 'src/libethash/endian.h', + 'src/libethash/ethash.h', + 'src/libethash/io.h', + 'src/libethash/fnv.h', + 'src/libethash/internal.h', + 'src/libethash/sha3.h', + 'src/libethash/util.h', +] +pyethash = Extension('pyethash', + sources=sources, + depends=depends, + extra_compile_args=["-Isrc/", "-std=gnu99", "-Wall"]) + +setup( + name='pyethash', + author="Matthew Wampler-Doty", + author_email="matthew.wampler.doty@gmail.com", + license='GPL', + version='0.1.23', + url='https://github.com/ethereum/ethash', + download_url='https://github.com/ethereum/ethash/tarball/v23', + description=('Python wrappers for ethash, the ethereum proof of work' + 'hashing function'), + ext_modules=[pyethash], +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/CMakeLists.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/CMakeLists.txt new file mode 100644 index 0000000..a65621c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/CMakeLists.txt @@ -0,0 +1,44 @@ +set(LIBRARY ethash) + +if (CPPETHEREUM) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +endif () + +set(CMAKE_BUILD_TYPE Release) + +if (NOT MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +endif() + +set(FILES util.h + io.c + internal.c + ethash.h + endian.h + compiler.h + fnv.h + data_sizes.h) + +if (MSVC) + list(APPEND FILES util_win32.c io_win32.c mmap_win32.c) +else() + list(APPEND FILES io_posix.c) +endif() + +if (NOT CRYPTOPP_FOUND) + find_package(CryptoPP 5.6.2) +endif() + +if (CRYPTOPP_FOUND) + add_definitions(-DWITH_CRYPTOPP) + include_directories( ${CRYPTOPP_INCLUDE_DIRS} ) + list(APPEND FILES sha3_cryptopp.cpp sha3_cryptopp.h) +else() + list(APPEND FILES sha3.c sha3.h) +endif() + +add_library(${LIBRARY} ${FILES}) + +if (CRYPTOPP_FOUND) + TARGET_LINK_LIBRARIES(${LIBRARY} ${CRYPTOPP_LIBRARIES}) +endif() diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/compiler.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/compiler.h new file mode 100644 index 0000000..9695871 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/compiler.h @@ -0,0 +1,33 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file compiler.h + * @date 2014 + */ +#pragma once + +// Visual Studio doesn't support the inline keyword in C mode +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +// pretend restrict is a standard keyword +#if defined(_MSC_VER) +#define restrict __restrict +#else +#define restrict __restrict__ +#endif + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/data_sizes.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/data_sizes.h new file mode 100644 index 0000000..83cc30b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/data_sizes.h @@ -0,0 +1,812 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software FoundationUUU,either version 3 of the LicenseUUU,or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be usefulU, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If notUUU,see . +*/ + +/** @file data_sizes.h +* @author Matthew Wampler-Doty +* @date 2015 +*/ + +#pragma once + +#include +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// 2048 Epochs (~20 years) worth of tabulated DAG sizes + +// Generated with the following Mathematica Code: + +// GetCacheSizes[n_] := Module[{ +// CacheSizeBytesInit = 2^24, +// CacheGrowth = 2^17, +// HashBytes = 64, +// j = 0}, +// Reap[ +// While[j < n, +// Module[{i = +// Floor[(CacheSizeBytesInit + CacheGrowth * j) / HashBytes]}, +// While[! PrimeQ[i], i--]; +// Sow[i*HashBytes]; j++]]]][[2]][[1]] + + +static const uint64_t dag_sizes[2048] = { + 1073739904U, 1082130304U, 1090514816U, 1098906752U, 1107293056U, + 1115684224U, 1124070016U, 1132461952U, 1140849536U, 1149232768U, + 1157627776U, 1166013824U, 1174404736U, 1182786944U, 1191180416U, + 1199568512U, 1207958912U, 1216345216U, 1224732032U, 1233124736U, + 1241513344U, 1249902464U, 1258290304U, 1266673792U, 1275067264U, + 1283453312U, 1291844992U, 1300234112U, 1308619904U, 1317010048U, + 1325397376U, 1333787776U, 1342176128U, 1350561664U, 1358954368U, + 1367339392U, 1375731584U, 1384118144U, 1392507008U, 1400897408U, + 1409284736U, 1417673344U, 1426062464U, 1434451072U, 1442839168U, + 1451229056U, 1459615616U, 1468006016U, 1476394112U, 1484782976U, + 1493171584U, 1501559168U, 1509948032U, 1518337664U, 1526726528U, + 1535114624U, 1543503488U, 1551892096U, 1560278656U, 1568669056U, + 1577056384U, 1585446272U, 1593831296U, 1602219392U, 1610610304U, + 1619000192U, 1627386752U, 1635773824U, 1644164224U, 1652555648U, + 1660943488U, 1669332608U, 1677721216U, 1686109312U, 1694497664U, + 1702886272U, 1711274624U, 1719661184U, 1728047744U, 1736434816U, + 1744829056U, 1753218944U, 1761606272U, 1769995904U, 1778382464U, + 1786772864U, 1795157888U, 1803550592U, 1811937664U, 1820327552U, + 1828711552U, 1837102976U, 1845488768U, 1853879936U, 1862269312U, + 1870656896U, 1879048064U, 1887431552U, 1895825024U, 1904212096U, + 1912601216U, 1920988544U, 1929379456U, 1937765504U, 1946156672U, + 1954543232U, 1962932096U, 1971321728U, 1979707264U, 1988093056U, + 1996487552U, 2004874624U, 2013262208U, 2021653888U, 2030039936U, + 2038430848U, 2046819968U, 2055208576U, 2063596672U, 2071981952U, + 2080373632U, 2088762752U, 2097149056U, 2105539712U, 2113928576U, + 2122315136U, 2130700672U, 2139092608U, 2147483264U, 2155872128U, + 2164257664U, 2172642176U, 2181035392U, 2189426048U, 2197814912U, + 2206203008U, 2214587264U, 2222979712U, 2231367808U, 2239758208U, + 2248145024U, 2256527744U, 2264922752U, 2273312128U, 2281701248U, + 2290086272U, 2298476672U, 2306867072U, 2315251072U, 2323639168U, + 2332032128U, 2340420224U, 2348808064U, 2357196416U, 2365580416U, + 2373966976U, 2382363008U, 2390748544U, 2399139968U, 2407530368U, + 2415918976U, 2424307328U, 2432695424U, 2441084288U, 2449472384U, + 2457861248U, 2466247808U, 2474637184U, 2483026816U, 2491414144U, + 2499803776U, 2508191872U, 2516582272U, 2524970368U, 2533359232U, + 2541743488U, 2550134144U, 2558525056U, 2566913408U, 2575301504U, + 2583686528U, 2592073856U, 2600467328U, 2608856192U, 2617240448U, + 2625631616U, 2634022016U, 2642407552U, 2650796416U, 2659188352U, + 2667574912U, 2675965312U, 2684352896U, 2692738688U, 2701130624U, + 2709518464U, 2717907328U, 2726293376U, 2734685056U, 2743073152U, + 2751462016U, 2759851648U, 2768232832U, 2776625536U, 2785017728U, + 2793401984U, 2801794432U, 2810182016U, 2818571648U, 2826959488U, + 2835349376U, 2843734144U, 2852121472U, 2860514432U, 2868900992U, + 2877286784U, 2885676928U, 2894069632U, 2902451584U, 2910843008U, + 2919234688U, 2927622784U, 2936011648U, 2944400768U, 2952789376U, + 2961177728U, 2969565568U, 2977951616U, 2986338944U, 2994731392U, + 3003120256U, 3011508352U, 3019895936U, 3028287104U, 3036675968U, + 3045063808U, 3053452928U, 3061837696U, 3070228352U, 3078615424U, + 3087003776U, 3095394944U, 3103782272U, 3112173184U, 3120562048U, + 3128944768U, 3137339264U, 3145725056U, 3154109312U, 3162505088U, + 3170893184U, 3179280256U, 3187669376U, 3196056704U, 3204445568U, + 3212836736U, 3221224064U, 3229612928U, 3238002304U, 3246391168U, + 3254778496U, 3263165824U, 3271556224U, 3279944576U, 3288332416U, + 3296719232U, 3305110912U, 3313500032U, 3321887104U, 3330273152U, + 3338658944U, 3347053184U, 3355440512U, 3363827072U, 3372220288U, + 3380608384U, 3388997504U, 3397384576U, 3405774208U, 3414163072U, + 3422551936U, 3430937984U, 3439328384U, 3447714176U, 3456104576U, + 3464493952U, 3472883584U, 3481268864U, 3489655168U, 3498048896U, + 3506434432U, 3514826368U, 3523213952U, 3531603584U, 3539987072U, + 3548380288U, 3556763264U, 3565157248U, 3573545344U, 3581934464U, + 3590324096U, 3598712704U, 3607098752U, 3615488384U, 3623877248U, + 3632265856U, 3640646528U, 3649043584U, 3657430144U, 3665821568U, + 3674207872U, 3682597504U, 3690984832U, 3699367808U, 3707764352U, + 3716152448U, 3724541056U, 3732925568U, 3741318016U, 3749706368U, + 3758091136U, 3766481536U, 3774872704U, 3783260032U, 3791650432U, + 3800036224U, 3808427648U, 3816815488U, 3825204608U, 3833592704U, + 3841981568U, 3850370432U, 3858755968U, 3867147904U, 3875536256U, + 3883920512U, 3892313728U, 3900702592U, 3909087872U, 3917478784U, + 3925868416U, 3934256512U, 3942645376U, 3951032192U, 3959422336U, + 3967809152U, 3976200064U, 3984588416U, 3992974976U, 4001363584U, + 4009751168U, 4018141312U, 4026530432U, 4034911616U, 4043308928U, + 4051695488U, 4060084352U, 4068472448U, 4076862848U, 4085249408U, + 4093640576U, 4102028416U, 4110413696U, 4118805632U, 4127194496U, + 4135583104U, 4143971968U, 4152360832U, 4160746112U, 4169135744U, + 4177525888U, 4185912704U, 4194303616U, 4202691968U, 4211076736U, + 4219463552U, 4227855488U, 4236246656U, 4244633728U, 4253022848U, + 4261412224U, 4269799808U, 4278184832U, 4286578048U, 4294962304U, + 4303349632U, 4311743104U, 4320130432U, 4328521088U, 4336909184U, + 4345295488U, 4353687424U, 4362073472U, 4370458496U, 4378852736U, + 4387238528U, 4395630208U, 4404019072U, 4412407424U, 4420790656U, + 4429182848U, 4437571456U, 4445962112U, 4454344064U, 4462738048U, + 4471119232U, 4479516544U, 4487904128U, 4496289664U, 4504682368U, + 4513068416U, 4521459584U, 4529846144U, 4538232704U, 4546619776U, + 4555010176U, 4563402112U, 4571790208U, 4580174464U, 4588567936U, + 4596957056U, 4605344896U, 4613734016U, 4622119808U, 4630511488U, + 4638898816U, 4647287936U, 4655675264U, 4664065664U, 4672451968U, + 4680842624U, 4689231488U, 4697620352U, 4706007424U, 4714397056U, + 4722786176U, 4731173248U, 4739562368U, 4747951744U, 4756340608U, + 4764727936U, 4773114496U, 4781504384U, 4789894784U, 4798283648U, + 4806667648U, 4815059584U, 4823449472U, 4831835776U, 4840226176U, + 4848612224U, 4857003392U, 4865391488U, 4873780096U, 4882169728U, + 4890557312U, 4898946944U, 4907333248U, 4915722368U, 4924110976U, + 4932499328U, 4940889728U, 4949276032U, 4957666432U, 4966054784U, + 4974438016U, 4982831488U, 4991221376U, 4999607168U, 5007998848U, + 5016386432U, 5024763776U, 5033164672U, 5041544576U, 5049941888U, + 5058329728U, 5066717056U, 5075107456U, 5083494272U, 5091883904U, + 5100273536U, 5108662144U, 5117048192U, 5125436032U, 5133827456U, + 5142215296U, 5150605184U, 5158993024U, 5167382144U, 5175769472U, + 5184157568U, 5192543872U, 5200936064U, 5209324928U, 5217711232U, + 5226102656U, 5234490496U, 5242877312U, 5251263872U, 5259654016U, + 5268040832U, 5276434304U, 5284819328U, 5293209728U, 5301598592U, + 5309986688U, 5318374784U, 5326764416U, 5335151488U, 5343542144U, + 5351929472U, 5360319872U, 5368706944U, 5377096576U, 5385484928U, + 5393871232U, 5402263424U, 5410650496U, 5419040384U, 5427426944U, + 5435816576U, 5444205952U, 5452594816U, 5460981376U, 5469367936U, + 5477760896U, 5486148736U, 5494536832U, 5502925952U, 5511315328U, + 5519703424U, 5528089984U, 5536481152U, 5544869504U, 5553256064U, + 5561645696U, 5570032768U, 5578423936U, 5586811264U, 5595193216U, + 5603585408U, 5611972736U, 5620366208U, 5628750464U, 5637143936U, + 5645528192U, 5653921408U, 5662310272U, 5670694784U, 5679082624U, + 5687474048U, 5695864448U, 5704251008U, 5712641408U, 5721030272U, + 5729416832U, 5737806208U, 5746194304U, 5754583936U, 5762969984U, + 5771358592U, 5779748224U, 5788137856U, 5796527488U, 5804911232U, + 5813300608U, 5821692544U, 5830082176U, 5838468992U, 5846855552U, + 5855247488U, 5863636096U, 5872024448U, 5880411008U, 5888799872U, + 5897186432U, 5905576832U, 5913966976U, 5922352768U, 5930744704U, + 5939132288U, 5947522432U, 5955911296U, 5964299392U, 5972688256U, + 5981074304U, 5989465472U, 5997851008U, 6006241408U, 6014627968U, + 6023015552U, 6031408256U, 6039796096U, 6048185216U, 6056574848U, + 6064963456U, 6073351808U, 6081736064U, 6090128768U, 6098517632U, + 6106906496U, 6115289216U, 6123680896U, 6132070016U, 6140459648U, + 6148849024U, 6157237376U, 6165624704U, 6174009728U, 6182403712U, + 6190792064U, 6199176064U, 6207569792U, 6215952256U, 6224345216U, + 6232732544U, 6241124224U, 6249510272U, 6257899136U, 6266287744U, + 6274676864U, 6283065728U, 6291454336U, 6299843456U, 6308232064U, + 6316620928U, 6325006208U, 6333395584U, 6341784704U, 6350174848U, + 6358562176U, 6366951296U, 6375337856U, 6383729536U, 6392119168U, + 6400504192U, 6408895616U, 6417283456U, 6425673344U, 6434059136U, + 6442444672U, 6450837376U, 6459223424U, 6467613056U, 6476004224U, + 6484393088U, 6492781952U, 6501170048U, 6509555072U, 6517947008U, + 6526336384U, 6534725504U, 6543112832U, 6551500672U, 6559888768U, + 6568278656U, 6576662912U, 6585055616U, 6593443456U, 6601834112U, + 6610219648U, 6618610304U, 6626999168U, 6635385472U, 6643777408U, + 6652164224U, 6660552832U, 6668941952U, 6677330048U, 6685719424U, + 6694107776U, 6702493568U, 6710882176U, 6719274112U, 6727662976U, + 6736052096U, 6744437632U, 6752825984U, 6761213824U, 6769604224U, + 6777993856U, 6786383488U, 6794770816U, 6803158144U, 6811549312U, + 6819937664U, 6828326528U, 6836706176U, 6845101696U, 6853491328U, + 6861880448U, 6870269312U, 6878655104U, 6887046272U, 6895433344U, + 6903822208U, 6912212864U, 6920596864U, 6928988288U, 6937377152U, + 6945764992U, 6954149248U, 6962544256U, 6970928768U, 6979317376U, + 6987709312U, 6996093824U, 7004487296U, 7012875392U, 7021258624U, + 7029652352U, 7038038912U, 7046427776U, 7054818944U, 7063207808U, + 7071595136U, 7079980928U, 7088372608U, 7096759424U, 7105149824U, + 7113536896U, 7121928064U, 7130315392U, 7138699648U, 7147092352U, + 7155479168U, 7163865728U, 7172249984U, 7180648064U, 7189036672U, + 7197424768U, 7205810816U, 7214196608U, 7222589824U, 7230975104U, + 7239367552U, 7247755904U, 7256145536U, 7264533376U, 7272921472U, + 7281308032U, 7289694848U, 7298088832U, 7306471808U, 7314864512U, + 7323253888U, 7331643008U, 7340029568U, 7348419712U, 7356808832U, + 7365196672U, 7373585792U, 7381973888U, 7390362752U, 7398750592U, + 7407138944U, 7415528576U, 7423915648U, 7432302208U, 7440690304U, + 7449080192U, 7457472128U, 7465860992U, 7474249088U, 7482635648U, + 7491023744U, 7499412608U, 7507803008U, 7516192384U, 7524579968U, + 7532967296U, 7541358464U, 7549745792U, 7558134656U, 7566524032U, + 7574912896U, 7583300992U, 7591690112U, 7600075136U, 7608466816U, + 7616854912U, 7625244544U, 7633629824U, 7642020992U, 7650410368U, + 7658794112U, 7667187328U, 7675574912U, 7683961984U, 7692349568U, + 7700739712U, 7709130368U, 7717519232U, 7725905536U, 7734295424U, + 7742683264U, 7751069056U, 7759457408U, 7767849088U, 7776238208U, + 7784626816U, 7793014912U, 7801405312U, 7809792128U, 7818179968U, + 7826571136U, 7834957184U, 7843347328U, 7851732352U, 7860124544U, + 7868512384U, 7876902016U, 7885287808U, 7893679744U, 7902067072U, + 7910455936U, 7918844288U, 7927230848U, 7935622784U, 7944009344U, + 7952400256U, 7960786048U, 7969176704U, 7977565312U, 7985953408U, + 7994339968U, 8002730368U, 8011119488U, 8019508096U, 8027896192U, + 8036285056U, 8044674688U, 8053062272U, 8061448832U, 8069838464U, + 8078227328U, 8086616704U, 8095006592U, 8103393664U, 8111783552U, + 8120171392U, 8128560256U, 8136949376U, 8145336704U, 8153726848U, + 8162114944U, 8170503296U, 8178891904U, 8187280768U, 8195669632U, + 8204058496U, 8212444544U, 8220834176U, 8229222272U, 8237612672U, + 8246000768U, 8254389376U, 8262775168U, 8271167104U, 8279553664U, + 8287944064U, 8296333184U, 8304715136U, 8313108352U, 8321497984U, + 8329885568U, 8338274432U, 8346663296U, 8355052928U, 8363441536U, + 8371828352U, 8380217984U, 8388606592U, 8396996224U, 8405384576U, + 8413772672U, 8422161536U, 8430549376U, 8438939008U, 8447326592U, + 8455715456U, 8464104832U, 8472492928U, 8480882048U, 8489270656U, + 8497659776U, 8506045312U, 8514434944U, 8522823808U, 8531208832U, + 8539602304U, 8547990656U, 8556378752U, 8564768384U, 8573154176U, + 8581542784U, 8589933952U, 8598322816U, 8606705024U, 8615099264U, + 8623487872U, 8631876992U, 8640264064U, 8648653952U, 8657040256U, + 8665430656U, 8673820544U, 8682209152U, 8690592128U, 8698977152U, + 8707374464U, 8715763328U, 8724151424U, 8732540032U, 8740928384U, + 8749315712U, 8757704576U, 8766089344U, 8774480768U, 8782871936U, + 8791260032U, 8799645824U, 8808034432U, 8816426368U, 8824812928U, + 8833199488U, 8841591424U, 8849976448U, 8858366336U, 8866757248U, + 8875147136U, 8883532928U, 8891923328U, 8900306816U, 8908700288U, + 8917088384U, 8925478784U, 8933867392U, 8942250368U, 8950644608U, + 8959032704U, 8967420544U, 8975809664U, 8984197504U, 8992584064U, + 9000976256U, 9009362048U, 9017752448U, 9026141312U, 9034530688U, + 9042917504U, 9051307904U, 9059694208U, 9068084864U, 9076471424U, + 9084861824U, 9093250688U, 9101638528U, 9110027648U, 9118416512U, + 9126803584U, 9135188096U, 9143581312U, 9151969664U, 9160356224U, + 9168747136U, 9177134464U, 9185525632U, 9193910144U, 9202302848U, + 9210690688U, 9219079552U, 9227465344U, 9235854464U, 9244244864U, + 9252633472U, 9261021824U, 9269411456U, 9277799296U, 9286188928U, + 9294574208U, 9302965888U, 9311351936U, 9319740032U, 9328131968U, + 9336516736U, 9344907392U, 9353296768U, 9361685888U, 9370074752U, + 9378463616U, 9386849408U, 9395239808U, 9403629184U, 9412016512U, + 9420405376U, 9428795008U, 9437181568U, 9445570688U, 9453960832U, + 9462346624U, 9470738048U, 9479121536U, 9487515008U, 9495903616U, + 9504289664U, 9512678528U, 9521067904U, 9529456256U, 9537843584U, + 9546233728U, 9554621312U, 9563011456U, 9571398784U, 9579788672U, + 9588178304U, 9596567168U, 9604954496U, 9613343104U, 9621732992U, + 9630121856U, 9638508416U, 9646898816U, 9655283584U, 9663675776U, + 9672061312U, 9680449664U, 9688840064U, 9697230464U, 9705617536U, + 9714003584U, 9722393984U, 9730772608U, 9739172224U, 9747561088U, + 9755945344U, 9764338816U, 9772726144U, 9781116544U, 9789503872U, + 9797892992U, 9806282624U, 9814670464U, 9823056512U, 9831439232U, + 9839833984U, 9848224384U, 9856613504U, 9865000576U, 9873391232U, + 9881772416U, 9890162816U, 9898556288U, 9906940544U, 9915333248U, + 9923721088U, 9932108672U, 9940496512U, 9948888448U, 9957276544U, + 9965666176U, 9974048384U, 9982441088U, 9990830464U, 9999219584U, + 10007602816U, 10015996544U, 10024385152U, 10032774016U, 10041163648U, + 10049548928U, 10057940096U, 10066329472U, 10074717824U, 10083105152U, + 10091495296U, 10099878784U, 10108272256U, 10116660608U, 10125049216U, + 10133437312U, 10141825664U, 10150213504U, 10158601088U, 10166991232U, + 10175378816U, 10183766144U, 10192157312U, 10200545408U, 10208935552U, + 10217322112U, 10225712768U, 10234099328U, 10242489472U, 10250876032U, + 10259264896U, 10267656064U, 10276042624U, 10284429184U, 10292820352U, + 10301209472U, 10309598848U, 10317987712U, 10326375296U, 10334763392U, + 10343153536U, 10351541632U, 10359930752U, 10368318592U, 10376707456U, + 10385096576U, 10393484672U, 10401867136U, 10410262144U, 10418647424U, + 10427039104U, 10435425664U, 10443810176U, 10452203648U, 10460589952U, + 10468982144U, 10477369472U, 10485759104U, 10494147712U, 10502533504U, + 10510923392U, 10519313536U, 10527702656U, 10536091264U, 10544478592U, + 10552867712U, 10561255808U, 10569642368U, 10578032768U, 10586423168U, + 10594805632U, 10603200128U, 10611588992U, 10619976064U, 10628361344U, + 10636754048U, 10645143424U, 10653531776U, 10661920384U, 10670307968U, + 10678696832U, 10687086464U, 10695475072U, 10703863168U, 10712246144U, + 10720639616U, 10729026688U, 10737414784U, 10745806208U, 10754190976U, + 10762581376U, 10770971264U, 10779356288U, 10787747456U, 10796135552U, + 10804525184U, 10812915584U, 10821301888U, 10829692288U, 10838078336U, + 10846469248U, 10854858368U, 10863247232U, 10871631488U, 10880023424U, + 10888412032U, 10896799616U, 10905188992U, 10913574016U, 10921964672U, + 10930352768U, 10938742912U, 10947132544U, 10955518592U, 10963909504U, + 10972298368U, 10980687488U, 10989074816U, 10997462912U, 11005851776U, + 11014241152U, 11022627712U, 11031017344U, 11039403904U, 11047793024U, + 11056184704U, 11064570752U, 11072960896U, 11081343872U, 11089737856U, + 11098128256U, 11106514816U, 11114904448U, 11123293568U, 11131680128U, + 11140065152U, 11148458368U, 11156845696U, 11165236864U, 11173624192U, + 11182013824U, 11190402688U, 11198790784U, 11207179136U, 11215568768U, + 11223957376U, 11232345728U, 11240734592U, 11249122688U, 11257511296U, + 11265899648U, 11274285952U, 11282675584U, 11291065472U, 11299452544U, + 11307842432U, 11316231296U, 11324616832U, 11333009024U, 11341395584U, + 11349782656U, 11358172288U, 11366560384U, 11374950016U, 11383339648U, + 11391721856U, 11400117376U, 11408504192U, 11416893568U, 11425283456U, + 11433671552U, 11442061184U, 11450444672U, 11458837888U, 11467226752U, + 11475611776U, 11484003968U, 11492392064U, 11500780672U, 11509169024U, + 11517550976U, 11525944448U, 11534335616U, 11542724224U, 11551111808U, + 11559500672U, 11567890304U, 11576277376U, 11584667008U, 11593056128U, + 11601443456U, 11609830016U, 11618221952U, 11626607488U, 11634995072U, + 11643387776U, 11651775104U, 11660161664U, 11668552576U, 11676940928U, + 11685330304U, 11693718656U, 11702106496U, 11710496128U, 11718882688U, + 11727273088U, 11735660416U, 11744050048U, 11752437376U, 11760824704U, + 11769216128U, 11777604736U, 11785991296U, 11794381952U, 11802770048U, + 11811157888U, 11819548544U, 11827932544U, 11836324736U, 11844713344U, + 11853100928U, 11861486464U, 11869879936U, 11878268032U, 11886656896U, + 11895044992U, 11903433088U, 11911822976U, 11920210816U, 11928600448U, + 11936987264U, 11945375872U, 11953761152U, 11962151296U, 11970543488U, + 11978928512U, 11987320448U, 11995708288U, 12004095104U, 12012486272U, + 12020875136U, 12029255552U, 12037652096U, 12046039168U, 12054429568U, + 12062813824U, 12071206528U, 12079594624U, 12087983744U, 12096371072U, + 12104759936U, 12113147264U, 12121534592U, 12129924992U, 12138314624U, + 12146703232U, 12155091584U, 12163481216U, 12171864704U, 12180255872U, + 12188643968U, 12197034112U, 12205424512U, 12213811328U, 12222199424U, + 12230590336U, 12238977664U, 12247365248U, 12255755392U, 12264143488U, + 12272531584U, 12280920448U, 12289309568U, 12297694592U, 12306086528U, + 12314475392U, 12322865024U, 12331253632U, 12339640448U, 12348029312U, + 12356418944U, 12364805248U, 12373196672U, 12381580928U, 12389969024U, + 12398357632U, 12406750592U, 12415138432U, 12423527552U, 12431916416U, + 12440304512U, 12448692352U, 12457081216U, 12465467776U, 12473859968U, + 12482245504U, 12490636672U, 12499025536U, 12507411584U, 12515801728U, + 12524190592U, 12532577152U, 12540966272U, 12549354368U, 12557743232U, + 12566129536U, 12574523264U, 12582911872U, 12591299456U, 12599688064U, + 12608074624U, 12616463488U, 12624845696U, 12633239936U, 12641631616U, + 12650019968U, 12658407296U, 12666795136U, 12675183232U, 12683574656U, + 12691960192U, 12700350592U, 12708740224U, 12717128576U, 12725515904U, + 12733906816U, 12742295168U, 12750680192U, 12759071872U, 12767460736U, + 12775848832U, 12784236928U, 12792626816U, 12801014656U, 12809404288U, + 12817789312U, 12826181504U, 12834568832U, 12842954624U, 12851345792U, + 12859732352U, 12868122496U, 12876512128U, 12884901248U, 12893289088U, + 12901672832U, 12910067584U, 12918455168U, 12926842496U, 12935232896U, + 12943620736U, 12952009856U, 12960396928U, 12968786816U, 12977176192U, + 12985563776U, 12993951104U, 13002341504U, 13010730368U, 13019115392U, + 13027506304U, 13035895168U, 13044272512U, 13052673152U, 13061062528U, + 13069446272U, 13077838976U, 13086227072U, 13094613632U, 13103000192U, + 13111393664U, 13119782528U, 13128157568U, 13136559232U, 13144945024U, + 13153329536U, 13161724288U, 13170111872U, 13178502784U, 13186884736U, + 13195279744U, 13203667072U, 13212057472U, 13220445824U, 13228832128U, + 13237221248U, 13245610624U, 13254000512U, 13262388352U, 13270777472U, + 13279166336U, 13287553408U, 13295943296U, 13304331904U, 13312719488U, + 13321108096U, 13329494656U, 13337885824U, 13346274944U, 13354663808U, + 13363051136U, 13371439232U, 13379825024U, 13388210816U, 13396605056U, + 13404995456U, 13413380224U, 13421771392U, 13430159744U, 13438546048U, + 13446937216U, 13455326848U, 13463708288U, 13472103808U, 13480492672U, + 13488875648U, 13497269888U, 13505657728U, 13514045312U, 13522435712U, + 13530824576U, 13539210112U, 13547599232U, 13555989376U, 13564379008U, + 13572766336U, 13581154432U, 13589544832U, 13597932928U, 13606320512U, + 13614710656U, 13623097472U, 13631477632U, 13639874944U, 13648264064U, + 13656652928U, 13665041792U, 13673430656U, 13681818496U, 13690207616U, + 13698595712U, 13706982272U, 13715373184U, 13723762048U, 13732150144U, + 13740536704U, 13748926592U, 13757316224U, 13765700992U, 13774090112U, + 13782477952U, 13790869376U, 13799259008U, 13807647872U, 13816036736U, + 13824425344U, 13832814208U, 13841202304U, 13849591424U, 13857978752U, + 13866368896U, 13874754688U, 13883145344U, 13891533184U, 13899919232U, + 13908311168U, 13916692096U, 13925085056U, 13933473152U, 13941866368U, + 13950253696U, 13958643584U, 13967032192U, 13975417216U, 13983807616U, + 13992197504U, 14000582272U, 14008973696U, 14017363072U, 14025752192U, + 14034137984U, 14042528384U, 14050918016U, 14059301504U, 14067691648U, + 14076083584U, 14084470144U, 14092852352U, 14101249664U, 14109635968U, + 14118024832U, 14126407552U, 14134804352U, 14143188608U, 14151577984U, + 14159968384U, 14168357248U, 14176741504U, 14185127296U, 14193521024U, + 14201911424U, 14210301824U, 14218685056U, 14227067264U, 14235467392U, + 14243855488U, 14252243072U, 14260630144U, 14269021568U, 14277409408U, + 14285799296U, 14294187904U, 14302571392U, 14310961792U, 14319353728U, + 14327738752U, 14336130944U, 14344518784U, 14352906368U, 14361296512U, + 14369685376U, 14378071424U, 14386462592U, 14394848128U, 14403230848U, + 14411627392U, 14420013952U, 14428402304U, 14436793472U, 14445181568U, + 14453569664U, 14461959808U, 14470347904U, 14478737024U, 14487122816U, + 14495511424U, 14503901824U, 14512291712U, 14520677504U, 14529064832U, + 14537456768U, 14545845632U, 14554234496U, 14562618496U, 14571011456U, + 14579398784U, 14587789184U, 14596172672U, 14604564608U, 14612953984U, + 14621341312U, 14629724288U, 14638120832U, 14646503296U, 14654897536U, + 14663284864U, 14671675264U, 14680061056U, 14688447616U, 14696835968U, + 14705228416U, 14713616768U, 14722003328U, 14730392192U, 14738784128U, + 14747172736U, 14755561088U, 14763947648U, 14772336512U, 14780725376U, + 14789110144U, 14797499776U, 14805892736U, 14814276992U, 14822670208U, + 14831056256U, 14839444352U, 14847836032U, 14856222848U, 14864612992U, + 14872997504U, 14881388672U, 14889775744U, 14898165376U, 14906553472U, + 14914944896U, 14923329664U, 14931721856U, 14940109696U, 14948497024U, + 14956887424U, 14965276544U, 14973663616U, 14982053248U, 14990439808U, + 14998830976U, 15007216768U, 15015605888U, 15023995264U, 15032385152U, + 15040768384U, 15049154944U, 15057549184U, 15065939072U, 15074328448U, + 15082715008U, 15091104128U, 15099493504U, 15107879296U, 15116269184U, + 15124659584U, 15133042304U, 15141431936U, 15149824384U, 15158214272U, + 15166602368U, 15174991232U, 15183378304U, 15191760512U, 15200154496U, + 15208542592U, 15216931712U, 15225323392U, 15233708416U, 15242098048U, + 15250489216U, 15258875264U, 15267265408U, 15275654528U, 15284043136U, + 15292431488U, 15300819584U, 15309208192U, 15317596544U, 15325986176U, + 15334374784U, 15342763648U, 15351151744U, 15359540608U, 15367929728U, + 15376318336U, 15384706432U, 15393092992U, 15401481856U, 15409869952U, + 15418258816U, 15426649984U, 15435037568U, 15443425664U, 15451815296U, + 15460203392U, 15468589184U, 15476979328U, 15485369216U, 15493755776U, + 15502146944U, 15510534272U, 15518924416U, 15527311232U, 15535699072U, + 15544089472U, 15552478336U, 15560866688U, 15569254528U, 15577642624U, + 15586031488U, 15594419072U, 15602809472U, 15611199104U, 15619586432U, + 15627975296U, 15636364928U, 15644753792U, 15653141888U, 15661529216U, + 15669918848U, 15678305152U, 15686696576U, 15695083136U, 15703474048U, + 15711861632U, 15720251264U, 15728636288U, 15737027456U, 15745417088U, + 15753804928U, 15762194048U, 15770582656U, 15778971008U, 15787358336U, + 15795747712U, 15804132224U, 15812523392U, 15820909696U, 15829300096U, + 15837691264U, 15846071936U, 15854466944U, 15862855808U, 15871244672U, + 15879634816U, 15888020608U, 15896409728U, 15904799104U, 15913185152U, + 15921577088U, 15929966464U, 15938354816U, 15946743424U, 15955129472U, + 15963519872U, 15971907968U, 15980296064U, 15988684928U, 15997073024U, + 16005460864U, 16013851264U, 16022241152U, 16030629248U, 16039012736U, + 16047406976U, 16055794816U, 16064181376U, 16072571264U, 16080957824U, + 16089346688U, 16097737856U, 16106125184U, 16114514816U, 16122904192U, + 16131292544U, 16139678848U, 16148066944U, 16156453504U, 16164839552U, + 16173236096U, 16181623424U, 16190012032U, 16198401152U, 16206790528U, + 16215177344U, 16223567744U, 16231956352U, 16240344704U, 16248731008U, + 16257117824U, 16265504384U, 16273898624U, 16282281856U, 16290668672U, + 16299064192U, 16307449216U, 16315842176U, 16324230016U, 16332613504U, + 16341006464U, 16349394304U, 16357783168U, 16366172288U, 16374561664U, + 16382951296U, 16391337856U, 16399726208U, 16408116352U, 16416505472U, + 16424892032U, 16433282176U, 16441668224U, 16450058624U, 16458448768U, + 16466836864U, 16475224448U, 16483613056U, 16492001408U, 16500391808U, + 16508779648U, 16517166976U, 16525555328U, 16533944192U, 16542330752U, + 16550719616U, 16559110528U, 16567497088U, 16575888512U, 16584274816U, + 16592665472U, 16601051008U, 16609442944U, 16617832064U, 16626218624U, + 16634607488U, 16642996096U, 16651385728U, 16659773824U, 16668163712U, + 16676552576U, 16684938112U, 16693328768U, 16701718144U, 16710095488U, + 16718492288U, 16726883968U, 16735272832U, 16743661184U, 16752049792U, + 16760436608U, 16768827008U, 16777214336U, 16785599104U, 16793992832U, + 16802381696U, 16810768768U, 16819151744U, 16827542656U, 16835934848U, + 16844323712U, 16852711552U, 16861101952U, 16869489536U, 16877876864U, + 16886265728U, 16894653056U, 16903044736U, 16911431296U, 16919821696U, + 16928207488U, 16936592768U, 16944987776U, 16953375616U, 16961763968U, + 16970152832U, 16978540928U, 16986929536U, 16995319168U, 17003704448U, + 17012096896U, 17020481152U, 17028870784U, 17037262208U, 17045649536U, + 17054039936U, 17062426496U, 17070814336U, 17079205504U, 17087592064U, + 17095978112U, 17104369024U, 17112759424U, 17121147776U, 17129536384U, + 17137926016U, 17146314368U, 17154700928U, 17163089792U, 17171480192U, + 17179864192U, 17188256896U, 17196644992U, 17205033856U, 17213423488U, + 17221811072U, 17230198912U, 17238588032U, 17246976896U, 17255360384U, + 17263754624U, 17272143232U, 17280530048U, 17288918912U, 17297309312U, + 17305696384U, 17314085504U, 17322475136U, 17330863744U, 17339252096U, + 17347640192U, 17356026496U, 17364413824U, 17372796544U, 17381190016U, + 17389583488U, 17397972608U, 17406360704U, 17414748544U, 17423135872U, + 17431527296U, 17439915904U, 17448303232U, 17456691584U, 17465081728U, + 17473468288U, 17481857408U, 17490247552U, 17498635904U, 17507022464U, + 17515409024U, 17523801728U, 17532189824U, 17540577664U, 17548966016U, + 17557353344U, 17565741184U, 17574131584U, 17582519168U, 17590907008U, + 17599296128U, 17607687808U, 17616076672U, 17624455808U, 17632852352U, + 17641238656U, 17649630848U, 17658018944U, 17666403968U, 17674794112U, + 17683178368U, 17691573376U, 17699962496U, 17708350592U, 17716739968U, + 17725126528U, 17733517184U, 17741898112U, 17750293888U, 17758673024U, + 17767070336U, 17775458432U, 17783848832U, 17792236928U, 17800625536U, + 17809012352U, 17817402752U, 17825785984U, 17834178944U, 17842563968U, + 17850955648U, 17859344512U, 17867732864U, 17876119424U, 17884511872U, + 17892900224U, 17901287296U, 17909677696U, 17918058112U, 17926451072U, + 17934843776U, 17943230848U, 17951609216U, 17960008576U, 17968397696U, + 17976784256U, 17985175424U, 17993564032U, 18001952128U, 18010339712U, + 18018728576U, 18027116672U, 18035503232U, 18043894144U, 18052283264U, + 18060672128U, 18069056384U, 18077449856U, 18085837184U, 18094225792U, + 18102613376U, 18111004544U, 18119388544U, 18127781248U, 18136170368U, + 18144558976U, 18152947328U, 18161336192U, 18169724288U, 18178108544U, + 18186498944U, 18194886784U, 18203275648U, 18211666048U, 18220048768U, + 18228444544U, 18236833408U, 18245220736U +}; + + +// Generated with the following Mathematica Code: + +// GetCacheSizes[n_] := Module[{ +// DataSetSizeBytesInit = 2^30, +// MixBytes = 128, +// DataSetGrowth = 2^23, +// HashBytes = 64, +// CacheMultiplier = 1024, +// j = 0}, +// Reap[ +// While[j < n, +// Module[{i = Floor[(DataSetSizeBytesInit + DataSetGrowth * j) / (CacheMultiplier * HashBytes)]}, +// While[! PrimeQ[i], i--]; +// Sow[i*HashBytes]; j++]]]][[2]][[1]] + +const uint64_t cache_sizes[2048] = { + 16776896U, 16907456U, 17039296U, 17170112U, 17301056U, 17432512U, 17563072U, + 17693888U, 17824192U, 17955904U, 18087488U, 18218176U, 18349504U, 18481088U, + 18611392U, 18742336U, 18874304U, 19004224U, 19135936U, 19267264U, 19398208U, + 19529408U, 19660096U, 19791424U, 19922752U, 20053952U, 20184896U, 20315968U, + 20446912U, 20576576U, 20709184U, 20840384U, 20971072U, 21102272U, 21233216U, + 21364544U, 21494848U, 21626816U, 21757376U, 21887552U, 22019392U, 22151104U, + 22281536U, 22412224U, 22543936U, 22675264U, 22806464U, 22935872U, 23068096U, + 23198272U, 23330752U, 23459008U, 23592512U, 23723968U, 23854912U, 23986112U, + 24116672U, 24247616U, 24378688U, 24509504U, 24640832U, 24772544U, 24903488U, + 25034432U, 25165376U, 25296704U, 25427392U, 25558592U, 25690048U, 25820096U, + 25951936U, 26081728U, 26214208U, 26345024U, 26476096U, 26606656U, 26737472U, + 26869184U, 26998208U, 27131584U, 27262528U, 27393728U, 27523904U, 27655744U, + 27786688U, 27917888U, 28049344U, 28179904U, 28311488U, 28441792U, 28573504U, + 28700864U, 28835648U, 28966208U, 29096768U, 29228608U, 29359808U, 29490752U, + 29621824U, 29752256U, 29882816U, 30014912U, 30144448U, 30273728U, 30406976U, + 30538432U, 30670784U, 30799936U, 30932672U, 31063744U, 31195072U, 31325248U, + 31456192U, 31588288U, 31719232U, 31850432U, 31981504U, 32110784U, 32243392U, + 32372672U, 32505664U, 32636608U, 32767808U, 32897344U, 33029824U, 33160768U, + 33289664U, 33423296U, 33554368U, 33683648U, 33816512U, 33947456U, 34076992U, + 34208704U, 34340032U, 34471744U, 34600256U, 34734016U, 34864576U, 34993984U, + 35127104U, 35258176U, 35386688U, 35518528U, 35650624U, 35782336U, 35910976U, + 36044608U, 36175808U, 36305728U, 36436672U, 36568384U, 36699968U, 36830656U, + 36961984U, 37093312U, 37223488U, 37355072U, 37486528U, 37617472U, 37747904U, + 37879232U, 38009792U, 38141888U, 38272448U, 38403392U, 38535104U, 38660672U, + 38795584U, 38925632U, 39059264U, 39190336U, 39320768U, 39452096U, 39581632U, + 39713984U, 39844928U, 39974848U, 40107968U, 40238144U, 40367168U, 40500032U, + 40631744U, 40762816U, 40894144U, 41023552U, 41155904U, 41286208U, 41418304U, + 41547712U, 41680448U, 41811904U, 41942848U, 42073792U, 42204992U, 42334912U, + 42467008U, 42597824U, 42729152U, 42860096U, 42991552U, 43122368U, 43253696U, + 43382848U, 43515712U, 43646912U, 43777088U, 43907648U, 44039104U, 44170432U, + 44302144U, 44433344U, 44564288U, 44694976U, 44825152U, 44956864U, 45088448U, + 45219008U, 45350464U, 45481024U, 45612608U, 45744064U, 45874496U, 46006208U, + 46136768U, 46267712U, 46399424U, 46529344U, 46660672U, 46791488U, 46923328U, + 47053504U, 47185856U, 47316928U, 47447872U, 47579072U, 47710144U, 47839936U, + 47971648U, 48103232U, 48234176U, 48365248U, 48496192U, 48627136U, 48757312U, + 48889664U, 49020736U, 49149248U, 49283008U, 49413824U, 49545152U, 49675712U, + 49807168U, 49938368U, 50069056U, 50200256U, 50331584U, 50462656U, 50593472U, + 50724032U, 50853952U, 50986048U, 51117632U, 51248576U, 51379904U, 51510848U, + 51641792U, 51773248U, 51903296U, 52035136U, 52164032U, 52297664U, 52427968U, + 52557376U, 52690112U, 52821952U, 52952896U, 53081536U, 53213504U, 53344576U, + 53475776U, 53608384U, 53738816U, 53870528U, 54000832U, 54131776U, 54263744U, + 54394688U, 54525248U, 54655936U, 54787904U, 54918592U, 55049152U, 55181248U, + 55312064U, 55442752U, 55574336U, 55705024U, 55836224U, 55967168U, 56097856U, + 56228672U, 56358592U, 56490176U, 56621888U, 56753728U, 56884928U, 57015488U, + 57146816U, 57278272U, 57409216U, 57540416U, 57671104U, 57802432U, 57933632U, + 58064576U, 58195264U, 58326976U, 58457408U, 58588864U, 58720192U, 58849984U, + 58981696U, 59113024U, 59243456U, 59375552U, 59506624U, 59637568U, 59768512U, + 59897792U, 60030016U, 60161984U, 60293056U, 60423872U, 60554432U, 60683968U, + 60817216U, 60948032U, 61079488U, 61209664U, 61341376U, 61471936U, 61602752U, + 61733696U, 61865792U, 61996736U, 62127808U, 62259136U, 62389568U, 62520512U, + 62651584U, 62781632U, 62910784U, 63045056U, 63176128U, 63307072U, 63438656U, + 63569216U, 63700928U, 63831616U, 63960896U, 64093888U, 64225088U, 64355392U, + 64486976U, 64617664U, 64748608U, 64879424U, 65009216U, 65142464U, 65273792U, + 65402816U, 65535424U, 65666752U, 65797696U, 65927744U, 66060224U, 66191296U, + 66321344U, 66453056U, 66584384U, 66715328U, 66846656U, 66977728U, 67108672U, + 67239104U, 67370432U, 67501888U, 67631296U, 67763776U, 67895104U, 68026304U, + 68157248U, 68287936U, 68419264U, 68548288U, 68681408U, 68811968U, 68942912U, + 69074624U, 69205568U, 69337024U, 69467584U, 69599168U, 69729472U, 69861184U, + 69989824U, 70122944U, 70253888U, 70385344U, 70515904U, 70647232U, 70778816U, + 70907968U, 71040832U, 71171648U, 71303104U, 71432512U, 71564992U, 71695168U, + 71826368U, 71958464U, 72089536U, 72219712U, 72350144U, 72482624U, 72613568U, + 72744512U, 72875584U, 73006144U, 73138112U, 73268672U, 73400128U, 73530944U, + 73662272U, 73793344U, 73924544U, 74055104U, 74185792U, 74316992U, 74448832U, + 74579392U, 74710976U, 74841664U, 74972864U, 75102784U, 75233344U, 75364544U, + 75497024U, 75627584U, 75759296U, 75890624U, 76021696U, 76152256U, 76283072U, + 76414144U, 76545856U, 76676672U, 76806976U, 76937792U, 77070016U, 77200832U, + 77331392U, 77462464U, 77593664U, 77725376U, 77856448U, 77987776U, 78118336U, + 78249664U, 78380992U, 78511424U, 78642496U, 78773056U, 78905152U, 79033664U, + 79166656U, 79297472U, 79429568U, 79560512U, 79690816U, 79822784U, 79953472U, + 80084672U, 80214208U, 80346944U, 80477632U, 80608576U, 80740288U, 80870848U, + 81002048U, 81133504U, 81264448U, 81395648U, 81525952U, 81657536U, 81786304U, + 81919808U, 82050112U, 82181312U, 82311616U, 82443968U, 82573376U, 82705984U, + 82835776U, 82967744U, 83096768U, 83230528U, 83359552U, 83491264U, 83622464U, + 83753536U, 83886016U, 84015296U, 84147776U, 84277184U, 84409792U, 84540608U, + 84672064U, 84803008U, 84934336U, 85065152U, 85193792U, 85326784U, 85458496U, + 85589312U, 85721024U, 85851968U, 85982656U, 86112448U, 86244416U, 86370112U, + 86506688U, 86637632U, 86769344U, 86900672U, 87031744U, 87162304U, 87293632U, + 87424576U, 87555392U, 87687104U, 87816896U, 87947968U, 88079168U, 88211264U, + 88341824U, 88473152U, 88603712U, 88735424U, 88862912U, 88996672U, 89128384U, + 89259712U, 89390272U, 89521984U, 89652544U, 89783872U, 89914816U, 90045376U, + 90177088U, 90307904U, 90438848U, 90569152U, 90700096U, 90832832U, 90963776U, + 91093696U, 91223744U, 91356992U, 91486784U, 91618496U, 91749824U, 91880384U, + 92012224U, 92143552U, 92273344U, 92405696U, 92536768U, 92666432U, 92798912U, + 92926016U, 93060544U, 93192128U, 93322816U, 93453632U, 93583936U, 93715136U, + 93845056U, 93977792U, 94109504U, 94240448U, 94371776U, 94501184U, 94632896U, + 94764224U, 94895552U, 95023424U, 95158208U, 95287744U, 95420224U, 95550016U, + 95681216U, 95811904U, 95943872U, 96075328U, 96203584U, 96337856U, 96468544U, + 96599744U, 96731072U, 96860992U, 96992576U, 97124288U, 97254848U, 97385536U, + 97517248U, 97647808U, 97779392U, 97910464U, 98041408U, 98172608U, 98303168U, + 98434496U, 98565568U, 98696768U, 98827328U, 98958784U, 99089728U, 99220928U, + 99352384U, 99482816U, 99614272U, 99745472U, 99876416U, 100007104U, + 100138048U, 100267072U, 100401088U, 100529984U, 100662592U, 100791872U, + 100925248U, 101056064U, 101187392U, 101317952U, 101449408U, 101580608U, + 101711296U, 101841728U, 101973824U, 102104896U, 102235712U, 102366016U, + 102498112U, 102628672U, 102760384U, 102890432U, 103021888U, 103153472U, + 103284032U, 103415744U, 103545152U, 103677248U, 103808576U, 103939648U, + 104070976U, 104201792U, 104332736U, 104462528U, 104594752U, 104725952U, + 104854592U, 104988608U, 105118912U, 105247808U, 105381184U, 105511232U, + 105643072U, 105774784U, 105903296U, 106037056U, 106167872U, 106298944U, + 106429504U, 106561472U, 106691392U, 106822592U, 106954304U, 107085376U, + 107216576U, 107346368U, 107478464U, 107609792U, 107739712U, 107872192U, + 108003136U, 108131392U, 108265408U, 108396224U, 108527168U, 108657344U, + 108789568U, 108920384U, 109049792U, 109182272U, 109312576U, 109444928U, + 109572928U, 109706944U, 109837888U, 109969088U, 110099648U, 110230976U, + 110362432U, 110492992U, 110624704U, 110755264U, 110886208U, 111017408U, + 111148864U, 111279296U, 111410752U, 111541952U, 111673024U, 111803456U, + 111933632U, 112066496U, 112196416U, 112328512U, 112457792U, 112590784U, + 112715968U, 112852672U, 112983616U, 113114944U, 113244224U, 113376448U, + 113505472U, 113639104U, 113770304U, 113901376U, 114031552U, 114163264U, + 114294592U, 114425536U, 114556864U, 114687424U, 114818624U, 114948544U, + 115080512U, 115212224U, 115343296U, 115473472U, 115605184U, 115736128U, + 115867072U, 115997248U, 116128576U, 116260288U, 116391488U, 116522944U, + 116652992U, 116784704U, 116915648U, 117046208U, 117178304U, 117308608U, + 117440192U, 117569728U, 117701824U, 117833024U, 117964096U, 118094656U, + 118225984U, 118357312U, 118489024U, 118617536U, 118749632U, 118882112U, + 119012416U, 119144384U, 119275328U, 119406016U, 119537344U, 119668672U, + 119798464U, 119928896U, 120061376U, 120192832U, 120321728U, 120454336U, + 120584512U, 120716608U, 120848192U, 120979136U, 121109056U, 121241408U, + 121372352U, 121502912U, 121634752U, 121764416U, 121895744U, 122027072U, + 122157632U, 122289088U, 122421184U, 122550592U, 122682944U, 122813888U, + 122945344U, 123075776U, 123207488U, 123338048U, 123468736U, 123600704U, + 123731264U, 123861952U, 123993664U, 124124608U, 124256192U, 124386368U, + 124518208U, 124649024U, 124778048U, 124911296U, 125041088U, 125173696U, + 125303744U, 125432896U, 125566912U, 125696576U, 125829056U, 125958592U, + 126090304U, 126221248U, 126352832U, 126483776U, 126615232U, 126746432U, + 126876608U, 127008704U, 127139392U, 127270336U, 127401152U, 127532224U, + 127663552U, 127794752U, 127925696U, 128055232U, 128188096U, 128319424U, + 128449856U, 128581312U, 128712256U, 128843584U, 128973632U, 129103808U, + 129236288U, 129365696U, 129498944U, 129629888U, 129760832U, 129892288U, + 130023104U, 130154048U, 130283968U, 130416448U, 130547008U, 130678336U, + 130807616U, 130939456U, 131071552U, 131202112U, 131331776U, 131464384U, + 131594048U, 131727296U, 131858368U, 131987392U, 132120256U, 132250816U, + 132382528U, 132513728U, 132644672U, 132774976U, 132905792U, 133038016U, + 133168832U, 133299392U, 133429312U, 133562048U, 133692992U, 133823296U, + 133954624U, 134086336U, 134217152U, 134348608U, 134479808U, 134607296U, + 134741056U, 134872384U, 135002944U, 135134144U, 135265472U, 135396544U, + 135527872U, 135659072U, 135787712U, 135921472U, 136052416U, 136182848U, + 136313792U, 136444864U, 136576448U, 136707904U, 136837952U, 136970048U, + 137099584U, 137232064U, 137363392U, 137494208U, 137625536U, 137755712U, + 137887424U, 138018368U, 138149824U, 138280256U, 138411584U, 138539584U, + 138672832U, 138804928U, 138936128U, 139066688U, 139196864U, 139328704U, + 139460032U, 139590208U, 139721024U, 139852864U, 139984576U, 140115776U, + 140245696U, 140376512U, 140508352U, 140640064U, 140769856U, 140902336U, + 141032768U, 141162688U, 141294016U, 141426496U, 141556544U, 141687488U, + 141819584U, 141949888U, 142080448U, 142212544U, 142342336U, 142474432U, + 142606144U, 142736192U, 142868288U, 142997824U, 143129408U, 143258944U, + 143392448U, 143523136U, 143653696U, 143785024U, 143916992U, 144045632U, + 144177856U, 144309184U, 144440768U, 144570688U, 144701888U, 144832448U, + 144965056U, 145096384U, 145227584U, 145358656U, 145489856U, 145620928U, + 145751488U, 145883072U, 146011456U, 146144704U, 146275264U, 146407232U, + 146538176U, 146668736U, 146800448U, 146931392U, 147062336U, 147193664U, + 147324224U, 147455936U, 147586624U, 147717056U, 147848768U, 147979456U, + 148110784U, 148242368U, 148373312U, 148503232U, 148635584U, 148766144U, + 148897088U, 149028416U, 149159488U, 149290688U, 149420224U, 149551552U, + 149683136U, 149814976U, 149943616U, 150076352U, 150208064U, 150338624U, + 150470464U, 150600256U, 150732224U, 150862784U, 150993088U, 151125952U, + 151254976U, 151388096U, 151519168U, 151649728U, 151778752U, 151911104U, + 152042944U, 152174144U, 152304704U, 152435648U, 152567488U, 152698816U, + 152828992U, 152960576U, 153091648U, 153222976U, 153353792U, 153484096U, + 153616192U, 153747008U, 153878336U, 154008256U, 154139968U, 154270912U, + 154402624U, 154533824U, 154663616U, 154795712U, 154926272U, 155057984U, + 155188928U, 155319872U, 155450816U, 155580608U, 155712064U, 155843392U, + 155971136U, 156106688U, 156237376U, 156367424U, 156499264U, 156630976U, + 156761536U, 156892352U, 157024064U, 157155008U, 157284416U, 157415872U, + 157545536U, 157677248U, 157810496U, 157938112U, 158071744U, 158203328U, + 158334656U, 158464832U, 158596288U, 158727616U, 158858048U, 158988992U, + 159121216U, 159252416U, 159381568U, 159513152U, 159645632U, 159776192U, + 159906496U, 160038464U, 160169536U, 160300352U, 160430656U, 160563008U, + 160693952U, 160822208U, 160956352U, 161086784U, 161217344U, 161349184U, + 161480512U, 161611456U, 161742272U, 161873216U, 162002752U, 162135872U, + 162266432U, 162397888U, 162529216U, 162660032U, 162790976U, 162922048U, + 163052096U, 163184576U, 163314752U, 163446592U, 163577408U, 163707968U, + 163839296U, 163969984U, 164100928U, 164233024U, 164364224U, 164494912U, + 164625856U, 164756672U, 164887616U, 165019072U, 165150016U, 165280064U, + 165412672U, 165543104U, 165674944U, 165805888U, 165936832U, 166067648U, + 166198336U, 166330048U, 166461248U, 166591552U, 166722496U, 166854208U, + 166985408U, 167116736U, 167246656U, 167378368U, 167508416U, 167641024U, + 167771584U, 167903168U, 168034112U, 168164032U, 168295744U, 168427456U, + 168557632U, 168688448U, 168819136U, 168951616U, 169082176U, 169213504U, + 169344832U, 169475648U, 169605952U, 169738048U, 169866304U, 169999552U, + 170131264U, 170262464U, 170393536U, 170524352U, 170655424U, 170782016U, + 170917696U, 171048896U, 171179072U, 171310784U, 171439936U, 171573184U, + 171702976U, 171835072U, 171966272U, 172097216U, 172228288U, 172359232U, + 172489664U, 172621376U, 172747712U, 172883264U, 173014208U, 173144512U, + 173275072U, 173407424U, 173539136U, 173669696U, 173800768U, 173931712U, + 174063424U, 174193472U, 174325696U, 174455744U, 174586816U, 174718912U, + 174849728U, 174977728U, 175109696U, 175242688U, 175374272U, 175504832U, + 175636288U, 175765696U, 175898432U, 176028992U, 176159936U, 176291264U, + 176422592U, 176552512U, 176684864U, 176815424U, 176946496U, 177076544U, + 177209152U, 177340096U, 177470528U, 177600704U, 177731648U, 177864256U, + 177994816U, 178126528U, 178257472U, 178387648U, 178518464U, 178650176U, + 178781888U, 178912064U, 179044288U, 179174848U, 179305024U, 179436736U, + 179568448U, 179698496U, 179830208U, 179960512U, 180092608U, 180223808U, + 180354752U, 180485696U, 180617152U, 180748096U, 180877504U, 181009984U, + 181139264U, 181272512U, 181402688U, 181532608U, 181663168U, 181795136U, + 181926592U, 182057536U, 182190016U, 182320192U, 182451904U, 182582336U, + 182713792U, 182843072U, 182976064U, 183107264U, 183237056U, 183368384U, + 183494848U, 183631424U, 183762752U, 183893824U, 184024768U, 184154816U, + 184286656U, 184417984U, 184548928U, 184680128U, 184810816U, 184941248U, + 185072704U, 185203904U, 185335616U, 185465408U, 185596352U, 185727296U, + 185859904U, 185989696U, 186121664U, 186252992U, 186383552U, 186514112U, + 186645952U, 186777152U, 186907328U, 187037504U, 187170112U, 187301824U, + 187429184U, 187562048U, 187693504U, 187825472U, 187957184U, 188087104U, + 188218304U, 188349376U, 188481344U, 188609728U, 188743616U, 188874304U, + 189005248U, 189136448U, 189265088U, 189396544U, 189528128U, 189660992U, + 189791936U, 189923264U, 190054208U, 190182848U, 190315072U, 190447424U, + 190577984U, 190709312U, 190840768U, 190971328U, 191102656U, 191233472U, + 191364032U, 191495872U, 191626816U, 191758016U, 191888192U, 192020288U, + 192148928U, 192282176U, 192413504U, 192542528U, 192674752U, 192805952U, + 192937792U, 193068608U, 193198912U, 193330496U, 193462208U, 193592384U, + 193723456U, 193854272U, 193985984U, 194116672U, 194247232U, 194379712U, + 194508352U, 194641856U, 194772544U, 194900672U, 195035072U, 195166016U, + 195296704U, 195428032U, 195558592U, 195690304U, 195818176U, 195952576U, + 196083392U, 196214336U, 196345792U, 196476736U, 196607552U, 196739008U, + 196869952U, 197000768U, 197130688U, 197262784U, 197394368U, 197523904U, + 197656384U, 197787584U, 197916608U, 198049472U, 198180544U, 198310208U, + 198442432U, 198573632U, 198705088U, 198834368U, 198967232U, 199097792U, + 199228352U, 199360192U, 199491392U, 199621696U, 199751744U, 199883968U, + 200014016U, 200146624U, 200276672U, 200408128U, 200540096U, 200671168U, + 200801984U, 200933312U, 201062464U, 201194944U, 201326144U, 201457472U, + 201588544U, 201719744U, 201850816U, 201981632U, 202111552U, 202244032U, + 202374464U, 202505152U, 202636352U, 202767808U, 202898368U, 203030336U, + 203159872U, 203292608U, 203423296U, 203553472U, 203685824U, 203816896U, + 203947712U, 204078272U, 204208192U, 204341056U, 204472256U, 204603328U, + 204733888U, 204864448U, 204996544U, 205125568U, 205258304U, 205388864U, + 205517632U, 205650112U, 205782208U, 205913536U, 206044736U, 206176192U, + 206307008U, 206434496U, 206569024U, 206700224U, 206831168U, 206961856U, + 207093056U, 207223616U, 207355328U, 207486784U, 207616832U, 207749056U, + 207879104U, 208010048U, 208141888U, 208273216U, 208404032U, 208534336U, + 208666048U, 208796864U, 208927424U, 209059264U, 209189824U, 209321792U, + 209451584U, 209582656U, 209715136U, 209845568U, 209976896U, 210106432U, + 210239296U, 210370112U, 210501568U, 210630976U, 210763712U, 210894272U, + 211024832U, 211156672U, 211287616U, 211418176U, 211549376U, 211679296U, + 211812032U, 211942592U, 212074432U, 212204864U, 212334016U, 212467648U, + 212597824U, 212727616U, 212860352U, 212991424U, 213120832U, 213253952U, + 213385024U, 213515584U, 213645632U, 213777728U, 213909184U, 214040128U, + 214170688U, 214302656U, 214433728U, 214564544U, 214695232U, 214826048U, + 214956992U, 215089088U, 215219776U, 215350592U, 215482304U, 215613248U, + 215743552U, 215874752U, 216005312U, 216137024U, 216267328U, 216399296U, + 216530752U, 216661696U, 216790592U, 216923968U, 217054528U, 217183168U, + 217316672U, 217448128U, 217579072U, 217709504U, 217838912U, 217972672U, + 218102848U, 218233024U, 218364736U, 218496832U, 218627776U, 218759104U, + 218888896U, 219021248U, 219151936U, 219281728U, 219413056U, 219545024U, + 219675968U, 219807296U, 219938624U, 220069312U, 220200128U, 220331456U, + 220461632U, 220592704U, 220725184U, 220855744U, 220987072U, 221117888U, + 221249216U, 221378368U, 221510336U, 221642048U, 221772736U, 221904832U, + 222031808U, 222166976U, 222297536U, 222428992U, 222559936U, 222690368U, + 222820672U, 222953152U, 223083968U, 223213376U, 223345984U, 223476928U, + 223608512U, 223738688U, 223869376U, 224001472U, 224132672U, 224262848U, + 224394944U, 224524864U, 224657344U, 224788288U, 224919488U, 225050432U, + 225181504U, 225312704U, 225443776U, 225574592U, 225704768U, 225834176U, + 225966784U, 226097216U, 226229824U, 226360384U, 226491712U, 226623424U, + 226754368U, 226885312U, 227015104U, 227147456U, 227278528U, 227409472U, + 227539904U, 227669696U, 227802944U, 227932352U, 228065216U, 228196288U, + 228326464U, 228457792U, 228588736U, 228720064U, 228850112U, 228981056U, + 229113152U, 229243328U, 229375936U, 229505344U, 229636928U, 229769152U, + 229894976U, 230030272U, 230162368U, 230292416U, 230424512U, 230553152U, + 230684864U, 230816704U, 230948416U, 231079616U, 231210944U, 231342016U, + 231472448U, 231603776U, 231733952U, 231866176U, 231996736U, 232127296U, + 232259392U, 232388672U, 232521664U, 232652608U, 232782272U, 232914496U, + 233043904U, 233175616U, 233306816U, 233438528U, 233569984U, 233699776U, + 233830592U, 233962688U, 234092224U, 234221888U, 234353984U, 234485312U, + 234618304U, 234749888U, 234880832U, 235011776U, 235142464U, 235274048U, + 235403456U, 235535936U, 235667392U, 235797568U, 235928768U, 236057152U, + 236190272U, 236322752U, 236453312U, 236583616U, 236715712U, 236846528U, + 236976448U, 237108544U, 237239104U, 237371072U, 237501632U, 237630784U, + 237764416U, 237895232U, 238026688U, 238157632U, 238286912U, 238419392U, + 238548032U, 238681024U, 238812608U, 238941632U, 239075008U, 239206336U, + 239335232U, 239466944U, 239599168U, 239730496U, 239861312U, 239992384U, + 240122816U, 240254656U, 240385856U, 240516928U, 240647872U, 240779072U, + 240909632U, 241040704U, 241171904U, 241302848U, 241433408U, 241565248U, + 241696192U, 241825984U, 241958848U, 242088256U, 242220224U, 242352064U, + 242481856U, 242611648U, 242744896U, 242876224U, 243005632U, 243138496U, + 243268672U, 243400384U, 243531712U, 243662656U, 243793856U, 243924544U, + 244054592U, 244187072U, 244316608U, 244448704U, 244580032U, 244710976U, + 244841536U, 244972864U, 245104448U, 245233984U, 245365312U, 245497792U, + 245628736U, 245759936U, 245889856U, 246021056U, 246152512U, 246284224U, + 246415168U, 246545344U, 246675904U, 246808384U, 246939584U, 247070144U, + 247199552U, 247331648U, 247463872U, 247593536U, 247726016U, 247857088U, + 247987648U, 248116928U, 248249536U, 248380736U, 248512064U, 248643008U, + 248773312U, 248901056U, 249036608U, 249167552U, 249298624U, 249429184U, + 249560512U, 249692096U, 249822784U, 249954112U, 250085312U, 250215488U, + 250345792U, 250478528U, 250608704U, 250739264U, 250870976U, 251002816U, + 251133632U, 251263552U, 251395136U, 251523904U, 251657792U, 251789248U, + 251919424U, 252051392U, 252182464U, 252313408U, 252444224U, 252575552U, + 252706624U, 252836032U, 252968512U, 253099712U, 253227584U, 253361728U, + 253493056U, 253623488U, 253754432U, 253885504U, 254017216U, 254148032U, + 254279488U, 254410432U, 254541376U, 254672576U, 254803264U, 254933824U, + 255065792U, 255196736U, 255326528U, 255458752U, 255589952U, 255721408U, + 255851072U, 255983296U, 256114624U, 256244416U, 256374208U, 256507712U, + 256636096U, 256768832U, 256900544U, 257031616U, 257162176U, 257294272U, + 257424448U, 257555776U, 257686976U, 257818432U, 257949632U, 258079552U, + 258211136U, 258342464U, 258473408U, 258603712U, 258734656U, 258867008U, + 258996544U, 259127744U, 259260224U, 259391296U, 259522112U, 259651904U, + 259784384U, 259915328U, 260045888U, 260175424U, 260308544U, 260438336U, + 260570944U, 260700992U, 260832448U, 260963776U, 261092672U, 261226304U, + 261356864U, 261487936U, 261619648U, 261750592U, 261879872U, 262011968U, + 262143424U, 262274752U, 262404416U, 262537024U, 262667968U, 262799296U, + 262928704U, 263061184U, 263191744U, 263322944U, 263454656U, 263585216U, + 263716672U, 263847872U, 263978944U, 264108608U, 264241088U, 264371648U, + 264501184U, 264632768U, 264764096U, 264895936U, 265024576U, 265158464U, + 265287488U, 265418432U, 265550528U, 265681216U, 265813312U, 265943488U, + 266075968U, 266206144U, 266337728U, 266468032U, 266600384U, 266731072U, + 266862272U, 266993344U, 267124288U, 267255616U, 267386432U, 267516992U, + 267648704U, 267777728U, 267910592U, 268040512U, 268172096U, 268302784U, + 268435264U, 268566208U, 268696256U, 268828096U, 268959296U, 269090368U, + 269221312U, 269352256U, 269482688U, 269614784U, 269745856U, 269876416U, + 270007616U, 270139328U, 270270272U, 270401216U, 270531904U, 270663616U, + 270791744U, 270924736U, 271056832U, 271186112U, 271317184U, 271449536U, + 271580992U, 271711936U, 271843136U, 271973056U, 272105408U, 272236352U, + 272367296U, 272498368U, 272629568U, 272759488U, 272891456U, 273022784U, + 273153856U, 273284672U, 273415616U, 273547072U, 273677632U, 273808448U, + 273937088U, 274071488U, 274200896U, 274332992U, 274463296U, 274595392U, + 274726208U, 274857536U, 274988992U, 275118656U, 275250496U, 275382208U, + 275513024U, 275643968U, 275775296U, 275906368U, 276037184U, 276167872U, + 276297664U, 276429376U, 276560576U, 276692672U, 276822976U, 276955072U, + 277085632U, 277216832U, 277347008U, 277478848U, 277609664U, 277740992U, + 277868608U, 278002624U, 278134336U, 278265536U, 278395328U, 278526784U, + 278657728U, 278789824U, 278921152U, 279052096U, 279182912U, 279313088U, + 279443776U, 279576256U, 279706048U, 279838528U, 279969728U, 280099648U, + 280230976U, 280361408U, 280493632U, 280622528U, 280755392U, 280887104U, + 281018176U, 281147968U, 281278912U, 281411392U, 281542592U, 281673152U, + 281803712U, 281935552U, 282066496U, 282197312U, 282329024U, 282458816U, + 282590272U, 282720832U, 282853184U, 282983744U, 283115072U, 283246144U, + 283377344U, 283508416U, 283639744U, 283770304U, 283901504U, 284032576U, + 284163136U, 284294848U, 284426176U, 284556992U, 284687296U, 284819264U, + 284950208U, 285081536U +}; + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/endian.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/endian.h new file mode 100644 index 0000000..5b8abf0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/endian.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include "compiler.h" + +#if defined(__MINGW32__) || defined(_WIN32) + # define LITTLE_ENDIAN 1234 + # define BYTE_ORDER LITTLE_ENDIAN +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) + # include +#elif defined(__OpenBSD__) || defined(__SVR4) + # include +#elif defined(__APPLE__) +# include +#elif defined( BSD ) && (BSD >= 199103) + # include +#elif defined( __QNXNTO__ ) && defined( __LITTLEENDIAN__ ) + # define LITTLE_ENDIAN 1234 + # define BYTE_ORDER LITTLE_ENDIAN +#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ ) + # define BIG_ENDIAN 1234 + # define BYTE_ORDER BIG_ENDIAN +#else +# include +#endif + +#if defined(_WIN32) +#include +#define ethash_swap_u32(input_) _byteswap_ulong(input_) +#define ethash_swap_u64(input_) _byteswap_uint64(input_) +#elif defined(__APPLE__) +#include +#define ethash_swap_u32(input_) OSSwapInt32(input_) +#define ethash_swap_u64(input_) OSSwapInt64(input_) +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) +#define ethash_swap_u32(input_) bswap32(input_) +#define ethash_swap_u64(input_) bswap64(input_) +#elif defined(__OpenBSD__) +#include +#define ethash_swap_u32(input_) swap32(input_) +#define ethash_swap_u64(input_) swap64(input_) +#else // posix +#include +#define ethash_swap_u32(input_) bswap_32(input_) +#define ethash_swap_u64(input_) bswap_64(input_) +#endif + + +#if LITTLE_ENDIAN == BYTE_ORDER + +#define fix_endian32(dst_ ,src_) dst_ = src_ +#define fix_endian32_same(val_) +#define fix_endian64(dst_, src_) dst_ = src_ +#define fix_endian64_same(val_) +#define fix_endian_arr32(arr_, size_) +#define fix_endian_arr64(arr_, size_) + +#elif BIG_ENDIAN == BYTE_ORDER + +#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_) +#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_) +#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_) +#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_) +#define fix_endian_arr32(arr_, size_) \ + do { \ + for (unsigned i_ = 0; i_ < (size_); ++i_) { \ + arr_[i_] = ethash_swap_u32(arr_[i_]); \ + } \ + } while (0) +#define fix_endian_arr64(arr_, size_) \ + do { \ + for (unsigned i_ = 0; i_ < (size_); ++i_) { \ + arr_[i_] = ethash_swap_u64(arr_[i_]); \ + } \ + } while (0) +#else +# error "endian not supported" +#endif // BYTE_ORDER diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/ethash.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/ethash.h new file mode 100644 index 0000000..0c6a1f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/ethash.h @@ -0,0 +1,147 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ + +/** @file ethash.h +* @date 2015 +*/ +#pragma once + +#include +#include +#include +#include +#include "compiler.h" + +#define ETHASH_REVISION 23 +#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30 +#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23 +#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24 +#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17 +#define ETHASH_EPOCH_LENGTH 30000U +#define ETHASH_MIX_BYTES 128 +#define ETHASH_HASH_BYTES 64 +#define ETHASH_DATASET_PARENTS 256 +#define ETHASH_CACHE_ROUNDS 3 +#define ETHASH_ACCESSES 64 +#define ETHASH_DAG_MAGIC_NUM_SIZE 8 +#define ETHASH_DAG_MAGIC_NUM 0xFEE1DEADBADDCAFE + +#ifdef __cplusplus +extern "C" { +#endif + +/// Type of a seedhash/blockhash e.t.c. +typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t; + +// convenience macro to statically initialize an h256_t +// usage: +// ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... ) +// have to provide all 32 values. If you don't provide all the rest +// will simply be unitialized (not guranteed to be 0) +#define ethash_h256_static_init(...) \ + { {__VA_ARGS__} } + +struct ethash_light; +typedef struct ethash_light* ethash_light_t; +struct ethash_full; +typedef struct ethash_full* ethash_full_t; +typedef int(*ethash_callback_t)(unsigned); + +typedef struct ethash_return_value { + ethash_h256_t result; + ethash_h256_t mix_hash; + bool success; +} ethash_return_value_t; + +/** + * Allocate and initialize a new ethash_light handler + * + * @param block_number The block number for which to create the handler + * @return Newly allocated ethash_light handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() + */ +ethash_light_t ethash_light_new(uint64_t block_number); +/** + * Frees a previously allocated ethash_light handler + * @param light The light handler to free + */ +void ethash_light_delete(ethash_light_t light); +/** + * Calculate the light client data + * + * @param light The light client handler + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return an object of ethash_return_value_t holding the return values + */ +ethash_return_value_t ethash_light_compute( + ethash_light_t light, + ethash_h256_t const header_hash, + uint64_t nonce +); + +/** + * Allocate and initialize a new ethash_full handler + * + * @param light The light handler containing the cache. + * @param callback A callback function with signature of @ref ethash_callback_t + * It accepts an unsigned with which a progress of DAG calculation + * can be displayed. If all goes well the callback should return 0. + * If a non-zero value is returned then DAG generation will stop. + * Be advised. A progress value of 100 means that DAG creation is + * almost complete and that this function will soon return succesfully. + * It does not mean that the function has already had a succesfull return. + * @return Newly allocated ethash_full handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() + */ +ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback); + +/** + * Frees a previously allocated ethash_full handler + * @param full The light handler to free + */ +void ethash_full_delete(ethash_full_t full); +/** + * Calculate the full client data + * + * @param full The full client handler + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return An object of ethash_return_value to hold the return value + */ +ethash_return_value_t ethash_full_compute( + ethash_full_t full, + ethash_h256_t const header_hash, + uint64_t nonce +); +/** + * Get a pointer to the full DAG data + */ +void const* ethash_full_dag(ethash_full_t full); +/** + * Get the size of the DAG data + */ +uint64_t ethash_full_dag_size(ethash_full_t full); + +/** + * Calculate the seedhash for a given block number + */ +ethash_h256_t ethash_get_seedhash(uint64_t block_number); + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/fnv.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/fnv.h new file mode 100644 index 0000000..82cd655 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/fnv.h @@ -0,0 +1,43 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file fnv.h +* @author Matthew Wampler-Doty +* @date 2015 +*/ + +#pragma once +#include +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FNV_PRIME 0x01000193 + +/* The FNV-1 spec multiplies the prime with the input one byte (octet) in turn. + We instead multiply it with the full 32-bit input. + This gives a different result compared to a canonical FNV-1 implementation. +*/ +static inline uint32_t fnv_hash(uint32_t const x, uint32_t const y) +{ + return x * FNV_PRIME ^ y; +} + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.c new file mode 100644 index 0000000..0a830fc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.c @@ -0,0 +1,507 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file internal.c +* @author Tim Hughes +* @author Matthew Wampler-Doty +* @date 2015 +*/ + +#include +#include +#include +#include +#include +#include "mmap.h" +#include "ethash.h" +#include "fnv.h" +#include "endian.h" +#include "internal.h" +#include "data_sizes.h" +#include "io.h" + +#ifdef WITH_CRYPTOPP + +#include "sha3_cryptopp.h" + +#else +#include "sha3.h" +#endif // WITH_CRYPTOPP + +uint64_t ethash_get_datasize(uint64_t const block_number) +{ + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return dag_sizes[block_number / ETHASH_EPOCH_LENGTH]; +} + +uint64_t ethash_get_cachesize(uint64_t const block_number) +{ + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return cache_sizes[block_number / ETHASH_EPOCH_LENGTH]; +} + +// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) +// https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf +// SeqMemoHash(s, R, N) +bool static ethash_compute_cache_nodes( + node* const nodes, + uint64_t cache_size, + ethash_h256_t const* seed +) +{ + if (cache_size % sizeof(node) != 0) { + return false; + } + uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node)); + + SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32); + + for (uint32_t i = 1; i != num_nodes; ++i) { + SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); + } + + for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) { + for (uint32_t i = 0; i != num_nodes; i++) { + uint32_t const idx = nodes[i].words[0] % num_nodes; + node data; + data = nodes[(num_nodes - 1 + i) % num_nodes]; + for (uint32_t w = 0; w != NODE_WORDS; ++w) { + data.words[w] ^= nodes[idx].words[w]; + } + SHA3_512(nodes[i].bytes, data.bytes, sizeof(data)); + } + } + + // now perform endian conversion + fix_endian_arr32(nodes->words, num_nodes * NODE_WORDS); + return true; +} + +void ethash_calculate_dag_item( + node* const ret, + uint32_t node_index, + ethash_light_t const light +) +{ + uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node)); + node const* cache_nodes = (node const *) light->cache; + node const* init = &cache_nodes[node_index % num_parent_nodes]; + memcpy(ret, init, sizeof(node)); + ret->words[0] ^= node_index; + SHA3_512(ret->bytes, ret->bytes, sizeof(node)); +#if defined(_M_X64) && ENABLE_SSE + __m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME); + __m128i xmm0 = ret->xmm[0]; + __m128i xmm1 = ret->xmm[1]; + __m128i xmm2 = ret->xmm[2]; + __m128i xmm3 = ret->xmm[3]; +#endif + + for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) { + uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes; + node const *parent = &cache_nodes[parent_index]; + +#if defined(_M_X64) && ENABLE_SSE + { + xmm0 = _mm_mullo_epi32(xmm0, fnv_prime); + xmm1 = _mm_mullo_epi32(xmm1, fnv_prime); + xmm2 = _mm_mullo_epi32(xmm2, fnv_prime); + xmm3 = _mm_mullo_epi32(xmm3, fnv_prime); + xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]); + xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]); + xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]); + xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]); + + // have to write to ret as values are used to compute index + ret->xmm[0] = xmm0; + ret->xmm[1] = xmm1; + ret->xmm[2] = xmm2; + ret->xmm[3] = xmm3; + } + #else + { + for (unsigned w = 0; w != NODE_WORDS; ++w) { + ret->words[w] = fnv_hash(ret->words[w], parent->words[w]); + } + } +#endif + } + SHA3_512(ret->bytes, ret->bytes, sizeof(node)); +} + +bool ethash_compute_full_data( + void* mem, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback +) +{ + if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 || + (full_size % sizeof(node)) != 0) { + return false; + } + uint32_t const max_n = (uint32_t)(full_size / sizeof(node)); + node* full_nodes = mem; + double const progress_change = 1.0f / max_n; + double progress = 0.0f; + // now compute full nodes + for (uint32_t n = 0; n != max_n; ++n) { + if (callback && + n % (max_n / 100) == 0 && + callback((unsigned int)(ceil(progress * 100.0f))) != 0) { + + return false; + } + progress += progress_change; + ethash_calculate_dag_item(&(full_nodes[n]), n, light); + } + return true; +} + +static bool ethash_hash( + ethash_return_value_t* ret, + node const* full_nodes, + ethash_light_t const light, + uint64_t full_size, + ethash_h256_t const header_hash, + uint64_t const nonce +) +{ + if (full_size % MIX_WORDS != 0) { + return false; + } + + // pack hash and nonce together into first 40 bytes of s_mix + assert(sizeof(node) * 8 == 512); + node s_mix[MIX_NODES + 1]; + memcpy(s_mix[0].bytes, &header_hash, 32); + fix_endian64(s_mix[0].double_words[4], nonce); + + // compute sha3-512 hash and replicate across mix + SHA3_512(s_mix->bytes, s_mix->bytes, 40); + fix_endian_arr32(s_mix[0].words, 16); + + node* const mix = s_mix + 1; + for (uint32_t w = 0; w != MIX_WORDS; ++w) { + mix->words[w] = s_mix[0].words[w % NODE_WORDS]; + } + + unsigned const page_size = sizeof(uint32_t) * MIX_WORDS; + unsigned const num_full_pages = (unsigned) (full_size / page_size); + + for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { + uint32_t const index = fnv_hash(s_mix->words[0] ^ i, mix->words[i % MIX_WORDS]) % num_full_pages; + + for (unsigned n = 0; n != MIX_NODES; ++n) { + node const* dag_node; + if (full_nodes) { + dag_node = &full_nodes[MIX_NODES * index + n]; + } else { + node tmp_node; + ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light); + dag_node = &tmp_node; + } + +#if defined(_M_X64) && ENABLE_SSE + { + __m128i fnv_prime = _mm_set1_epi32(FNV_PRIME); + __m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]); + __m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]); + __m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]); + __m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]); + mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]); + mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]); + mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]); + mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]); + } + #else + { + for (unsigned w = 0; w != NODE_WORDS; ++w) { + mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]); + } + } +#endif + } + + } + + // compress mix + for (uint32_t w = 0; w != MIX_WORDS; w += 4) { + uint32_t reduction = mix->words[w + 0]; + reduction = reduction * FNV_PRIME ^ mix->words[w + 1]; + reduction = reduction * FNV_PRIME ^ mix->words[w + 2]; + reduction = reduction * FNV_PRIME ^ mix->words[w + 3]; + mix->words[w / 4] = reduction; + } + + fix_endian_arr32(mix->words, MIX_WORDS / 4); + memcpy(&ret->mix_hash, mix->bytes, 32); + // final Keccak hash + SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix) + return true; +} + +void ethash_quick_hash( + ethash_h256_t* return_hash, + ethash_h256_t const* header_hash, + uint64_t nonce, + ethash_h256_t const* mix_hash +) +{ + uint8_t buf[64 + 32]; + memcpy(buf, header_hash, 32); + fix_endian64_same(nonce); + memcpy(&(buf[32]), &nonce, 8); + SHA3_512(buf, buf, 40); + memcpy(&(buf[64]), mix_hash, 32); + SHA3_256(return_hash, buf, 64 + 32); +} + +ethash_h256_t ethash_get_seedhash(uint64_t block_number) +{ + ethash_h256_t ret; + ethash_h256_reset(&ret); + uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH; + for (uint32_t i = 0; i < epochs; ++i) + SHA3_256(&ret, (uint8_t*)&ret, 32); + return ret; +} + +bool ethash_quick_check_difficulty( + ethash_h256_t const* header_hash, + uint64_t const nonce, + ethash_h256_t const* mix_hash, + ethash_h256_t const* boundary +) +{ + + ethash_h256_t return_hash; + ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash); + return ethash_check_difficulty(&return_hash, boundary); +} + +ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed) +{ + struct ethash_light *ret; + ret = calloc(sizeof(*ret), 1); + if (!ret) { + return NULL; + } + ret->cache = malloc((size_t)cache_size); + if (!ret->cache) { + goto fail_free_light; + } + node* nodes = (node*)ret->cache; + if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) { + goto fail_free_cache_mem; + } + ret->cache_size = cache_size; + return ret; + +fail_free_cache_mem: + free(ret->cache); +fail_free_light: + free(ret); + return NULL; +} + +ethash_light_t ethash_light_new(uint64_t block_number) +{ + ethash_h256_t seedhash = ethash_get_seedhash(block_number); + ethash_light_t ret; + ret = ethash_light_new_internal(ethash_get_cachesize(block_number), &seedhash); + ret->block_number = block_number; + return ret; +} + +void ethash_light_delete(ethash_light_t light) +{ + if (light->cache) { + free(light->cache); + } + free(light); +} + +ethash_return_value_t ethash_light_compute_internal( + ethash_light_t light, + uint64_t full_size, + ethash_h256_t const header_hash, + uint64_t nonce +) +{ + ethash_return_value_t ret; + ret.success = true; + if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) { + ret.success = false; + } + return ret; +} + +ethash_return_value_t ethash_light_compute( + ethash_light_t light, + ethash_h256_t const header_hash, + uint64_t nonce +) +{ + uint64_t full_size = ethash_get_datasize(light->block_number); + return ethash_light_compute_internal(light, full_size, header_hash, nonce); +} + +static bool ethash_mmap(struct ethash_full* ret, FILE* f) +{ + int fd; + char* mmapped_data; + errno = 0; + ret->file = f; + if ((fd = ethash_fileno(ret->file)) == -1) { + return false; + } + mmapped_data= mmap( + NULL, + (size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0 + ); + if (mmapped_data == MAP_FAILED) { + return false; + } + ret->data = (node*)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE); + return true; +} + +ethash_full_t ethash_full_new_internal( + char const* dirname, + ethash_h256_t const seed_hash, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback +) +{ + struct ethash_full* ret; + FILE *f = NULL; + ret = calloc(sizeof(*ret), 1); + if (!ret) { + return NULL; + } + ret->file_size = (size_t)full_size; + switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) { + case ETHASH_IO_FAIL: + // ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case + goto fail_free_full; + case ETHASH_IO_MEMO_MATCH: + if (!ethash_mmap(ret, f)) { + ETHASH_CRITICAL("mmap failure()"); + goto fail_close_file; + } + return ret; + case ETHASH_IO_MEMO_SIZE_MISMATCH: + // if a DAG of same filename but unexpected size is found, silently force new file creation + if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) { + ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size."); + goto fail_free_full; + } + // fallthrough to the mismatch case here, DO NOT go through match + case ETHASH_IO_MEMO_MISMATCH: + if (!ethash_mmap(ret, f)) { + ETHASH_CRITICAL("mmap failure()"); + goto fail_close_file; + } + break; + } + + if (!ethash_compute_full_data(ret->data, full_size, light, callback)) { + ETHASH_CRITICAL("Failure at computing DAG data."); + goto fail_free_full_data; + } + + // after the DAG has been filled then we finalize it by writting the magic number at the beginning + if (fseek(f, 0, SEEK_SET) != 0) { + ETHASH_CRITICAL("Could not seek to DAG file start to write magic number."); + goto fail_free_full_data; + } + uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM; + if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) { + ETHASH_CRITICAL("Could not write magic number to DAG's beginning."); + goto fail_free_full_data; + } + if (fflush(f) != 0) {// make sure the magic number IS there + ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?"); + goto fail_free_full_data; + } + return ret; + +fail_free_full_data: + // could check that munmap(..) == 0 but even if it did not can't really do anything here + munmap(ret->data, (size_t)full_size); +fail_close_file: + fclose(ret->file); +fail_free_full: + free(ret); + return NULL; +} + +ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback) +{ + char strbuf[256]; + if (!ethash_get_default_dirname(strbuf, 256)) { + return NULL; + } + uint64_t full_size = ethash_get_datasize(light->block_number); + ethash_h256_t seedhash = ethash_get_seedhash(light->block_number); + return ethash_full_new_internal(strbuf, seedhash, full_size, light, callback); +} + +void ethash_full_delete(ethash_full_t full) +{ + // could check that munmap(..) == 0 but even if it did not can't really do anything here + munmap(full->data, (size_t)full->file_size); + if (full->file) { + fclose(full->file); + } + free(full); +} + +ethash_return_value_t ethash_full_compute( + ethash_full_t full, + ethash_h256_t const header_hash, + uint64_t nonce +) +{ + ethash_return_value_t ret; + ret.success = true; + if (!ethash_hash( + &ret, + (node const*)full->data, + NULL, + full->file_size, + header_hash, + nonce)) { + ret.success = false; + } + return ret; +} + +void const* ethash_full_dag(ethash_full_t full) +{ + return full->data; +} + +uint64_t ethash_full_dag_size(ethash_full_t full) +{ + return full->file_size; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.h new file mode 100644 index 0000000..26c395a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/internal.h @@ -0,0 +1,179 @@ +#pragma once +#include "compiler.h" +#include "endian.h" +#include "ethash.h" +#include + +#define ENABLE_SSE 0 + +#if defined(_M_X64) && ENABLE_SSE +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// compile time settings +#define NODE_WORDS (64/4) +#define MIX_WORDS (ETHASH_MIX_BYTES/4) +#define MIX_NODES (MIX_WORDS / NODE_WORDS) +#include + +typedef union node { + uint8_t bytes[NODE_WORDS * 4]; + uint32_t words[NODE_WORDS]; + uint64_t double_words[NODE_WORDS / 2]; + +#if defined(_M_X64) && ENABLE_SSE + __m128i xmm[NODE_WORDS/4]; +#endif + +} node; + +static inline uint8_t ethash_h256_get(ethash_h256_t const* hash, unsigned int i) +{ + return hash->b[i]; +} + +static inline void ethash_h256_set(ethash_h256_t* hash, unsigned int i, uint8_t v) +{ + hash->b[i] = v; +} + +static inline void ethash_h256_reset(ethash_h256_t* hash) +{ + memset(hash, 0, 32); +} + +// Returns if hash is less than or equal to boundary (2^256/difficulty) +static inline bool ethash_check_difficulty( + ethash_h256_t const* hash, + ethash_h256_t const* boundary +) +{ + // Boundary is big endian + for (int i = 0; i < 32; i++) { + if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) { + continue; + } + return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i); + } + return true; +} + +/** + * Difficulty quick check for POW preverification + * + * @param header_hash The hash of the header + * @param nonce The block's nonce + * @param mix_hash The mix digest hash + * @param boundary The boundary is defined as (2^256 / difficulty) + * @return true for succesful pre-verification and false otherwise + */ +bool ethash_quick_check_difficulty( + ethash_h256_t const* header_hash, + uint64_t const nonce, + ethash_h256_t const* mix_hash, + ethash_h256_t const* boundary +); + +struct ethash_light { + void* cache; + uint64_t cache_size; + uint64_t block_number; +}; + +/** + * Allocate and initialize a new ethash_light handler. Internal version + * + * @param cache_size The size of the cache in bytes + * @param seed Block seedhash to be used during the computation of the + * cache nodes + * @return Newly allocated ethash_light handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() + */ +ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed); + +/** + * Calculate the light client data. Internal version. + * + * @param light The light client handler + * @param full_size The size of the full data in bytes. + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return The resulting hash. + */ +ethash_return_value_t ethash_light_compute_internal( + ethash_light_t light, + uint64_t full_size, + ethash_h256_t const header_hash, + uint64_t nonce +); + +struct ethash_full { + FILE* file; + uint64_t file_size; + node* data; +}; + +/** + * Allocate and initialize a new ethash_full handler. Internal version. + * + * @param dirname The directory in which to put the DAG file. + * @param seedhash The seed hash of the block. Used in the DAG file naming. + * @param full_size The size of the full data in bytes. + * @param cache A cache object to use that was allocated with @ref ethash_cache_new(). + * Iff this function succeeds the ethash_full_t will take memory + * memory ownership of the cache and free it at deletion. If + * not then the user still has to handle freeing of the cache himself. + * @param callback A callback function with signature of @ref ethash_callback_t + * It accepts an unsigned with which a progress of DAG calculation + * can be displayed. If all goes well the callback should return 0. + * If a non-zero value is returned then DAG generation will stop. + * @return Newly allocated ethash_full handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() + */ +ethash_full_t ethash_full_new_internal( + char const* dirname, + ethash_h256_t const seed_hash, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback +); + +void ethash_calculate_dag_item( + node* const ret, + uint32_t node_index, + ethash_light_t const cache +); + +void ethash_quick_hash( + ethash_h256_t* return_hash, + ethash_h256_t const* header_hash, + const uint64_t nonce, + ethash_h256_t const* mix_hash +); + +uint64_t ethash_get_datasize(uint64_t const block_number); +uint64_t ethash_get_cachesize(uint64_t const block_number); + +/** + * Compute the memory data for a full node's memory + * + * @param mem A pointer to an ethash full's memory + * @param full_size The size of the full data in bytes + * @param cache A cache object to use in the calculation + * @param callback The callback function. Check @ref ethash_full_new() for details. + * @return true if all went fine and false for invalid parameters + */ +bool ethash_compute_full_data( + void* mem, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback +); + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.c new file mode 100644 index 0000000..f4db477 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.c @@ -0,0 +1,119 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io.c + * @author Lefteris Karapetsas + * @date 2015 + */ +#include "io.h" +#include +#include +#include + +enum ethash_io_rc ethash_io_prepare( + char const* dirname, + ethash_h256_t const seedhash, + FILE** output_file, + uint64_t file_size, + bool force_create +) +{ + char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE]; + enum ethash_io_rc ret = ETHASH_IO_FAIL; + // reset errno before io calls + errno = 0; + + // assert directory exists + if (!ethash_mkdir(dirname)) { + ETHASH_CRITICAL("Could not create the ethash directory"); + goto end; + } + + ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name); + char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name)); + if (!tmpfile) { + ETHASH_CRITICAL("Could not create the full DAG pathname"); + goto end; + } + + FILE *f; + if (!force_create) { + // try to open the file + f = ethash_fopen(tmpfile, "rb+"); + if (f) { + size_t found_size; + if (!ethash_file_size(f, &found_size)) { + fclose(f); + ETHASH_CRITICAL("Could not query size of DAG file: \"%s\"", tmpfile); + goto free_memo; + } + if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) { + fclose(f); + ret = ETHASH_IO_MEMO_SIZE_MISMATCH; + goto free_memo; + } + // compare the magic number, no need to care about endianess since it's local + uint64_t magic_num; + if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) { + // I/O error + fclose(f); + ETHASH_CRITICAL("Could not read from DAG file: \"%s\"", tmpfile); + ret = ETHASH_IO_MEMO_SIZE_MISMATCH; + goto free_memo; + } + if (magic_num != ETHASH_DAG_MAGIC_NUM) { + fclose(f); + ret = ETHASH_IO_MEMO_SIZE_MISMATCH; + goto free_memo; + } + ret = ETHASH_IO_MEMO_MATCH; + goto set_file; + } + } + + // file does not exist, will need to be created + f = ethash_fopen(tmpfile, "wb+"); + if (!f) { + ETHASH_CRITICAL("Could not create DAG file: \"%s\"", tmpfile); + goto free_memo; + } + // make sure it's of the proper size + if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) { + fclose(f); + ETHASH_CRITICAL("Could not seek to the end of DAG file: \"%s\". Insufficient space?", tmpfile); + goto free_memo; + } + if (fputc('\n', f) == EOF) { + fclose(f); + ETHASH_CRITICAL("Could not write in the end of DAG file: \"%s\". Insufficient space?", tmpfile); + goto free_memo; + } + if (fflush(f) != 0) { + fclose(f); + ETHASH_CRITICAL("Could not flush at end of DAG file: \"%s\". Insufficient space?", tmpfile); + goto free_memo; + } + ret = ETHASH_IO_MEMO_MISMATCH; + goto set_file; + + ret = ETHASH_IO_MEMO_MATCH; +set_file: + *output_file = f; +free_memo: + free(tmpfile); +end: + return ret; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.h new file mode 100644 index 0000000..7a27089 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io.h @@ -0,0 +1,202 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io.h + * @author Lefteris Karapetsas + * @date 2015 + */ +#pragma once +#include +#include +#include +#include +#ifdef __cplusplus +#define __STDC_FORMAT_MACROS 1 +#endif +#include +#include "endian.h" +#include "ethash.h" + +#ifdef __cplusplus +extern "C" { +#endif +// Maximum size for mutable part of DAG file name +// 6 is for "full-R", the suffix of the filename +// 10 is for maximum number of digits of a uint32_t (for REVISION) +// 1 is for - and 16 is for the first 16 hex digits for first 8 bytes of +// the seedhash and last 1 is for the null terminating character +// Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG +#define DAG_MUTABLE_NAME_MAX_SIZE (6 + 10 + 1 + 16 + 1) +/// Possible return values of @see ethash_io_prepare +enum ethash_io_rc { + ETHASH_IO_FAIL = 0, ///< There has been an IO failure + ETHASH_IO_MEMO_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong. + ETHASH_IO_MEMO_MISMATCH, ///< The DAG file did not exist or there was revision/hash mismatch + ETHASH_IO_MEMO_MATCH, ///< DAG file existed and revision/hash matched. No need to do anything +}; + +// small hack for windows. I don't feel I should use va_args and forward just +// to have this one function properly cross-platform abstracted +#if defined(_WIN32) && !defined(__GNUC__) +#define snprintf(...) sprintf_s(__VA_ARGS__) +#endif + +/** + * Logs a critical error in important parts of ethash. Should mostly help + * figure out what kind of problem (I/O, memory e.t.c.) causes a NULL + * ethash_full_t + */ +#ifdef ETHASH_PRINT_CRITICAL_OUTPUT +#define ETHASH_CRITICAL(...) \ + do \ + { \ + printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +#define ETHASH_CRITICAL(...) +#endif + +/** + * Prepares io for ethash + * + * Create the DAG directory and the DAG file if they don't exist. + * + * @param[in] dirname A null terminated c-string of the path of the ethash + * data directory. If it does not exist it's created. + * @param[in] seedhash The seedhash of the current block number, used in the + * naming of the file as can be seen from the spec at: + * https://github.com/ethereum/wiki/wiki/Ethash-DAG + * @param[out] output_file If there was no failure then this will point to an open + * file descriptor. User is responsible for closing it. + * In the case of memo match then the file is open on read + * mode, while on the case of mismatch a new file is created + * on write mode + * @param[in] file_size The size that the DAG file should have on disk + * @param[out] force_create If true then there is no check to see if the file + * already exists + * @return For possible return values @see enum ethash_io_rc + */ +enum ethash_io_rc ethash_io_prepare( + char const* dirname, + ethash_h256_t const seedhash, + FILE** output_file, + uint64_t file_size, + bool force_create +); + +/** + * An fopen wrapper for no-warnings crossplatform fopen. + * + * Msvc compiler considers fopen to be insecure and suggests to use their + * alternative. This is a wrapper for this alternative. Another way is to + * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does + * not sound like a good idea. + * + * @param file_name The path to the file to open + * @param mode Opening mode. Check fopen() + * @return The FILE* or NULL in failure + */ +FILE* ethash_fopen(char const* file_name, char const* mode); + +/** + * An strncat wrapper for no-warnings crossplatform strncat. + * + * Msvc compiler considers strncat to be insecure and suggests to use their + * alternative. This is a wrapper for this alternative. Another way is to + * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does + * not sound like a good idea. + * + * @param des Destination buffer + * @param dest_size Maximum size of the destination buffer. This is the + * extra argument for the MSVC secure strncat + * @param src Souce buffer + * @param count Number of bytes to copy from source + * @return If all is well returns the dest buffer. If there is an + * error returns NULL + */ +char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count); + +/** + * A cross-platform mkdir wrapper to create a directory or assert it's there + * + * @param dirname The full path of the directory to create + * @return true if the directory was created or if it already + * existed + */ +bool ethash_mkdir(char const* dirname); + +/** + * Get a file's size + * + * @param[in] f The open file stream whose size to get + * @param[out] size Pass a size_t by reference to contain the file size + * @return true in success and false if there was a failure + */ +bool ethash_file_size(FILE* f, size_t* ret_size); + +/** + * Get a file descriptor number from a FILE stream + * + * @param f The file stream whose fd to get + * @return Platform specific fd handler + */ +int ethash_fileno(FILE* f); + +/** + * Create the filename for the DAG. + * + * @param dirname The directory name in which the DAG file should reside + * If it does not end with a directory separator it is appended. + * @param filename The actual name of the file + * @param filename_length The length of the filename in bytes + * @return A char* containing the full name. User must deallocate. + */ +char* ethash_io_create_filename( + char const* dirname, + char const* filename, + size_t filename_length +); + +/** + * Gets the default directory name for the DAG depending on the system + * + * The spec defining this directory is here: https://github.com/ethereum/wiki/wiki/Ethash-DAG + * + * @param[out] strbuf A string buffer of sufficient size to keep the + * null termninated string of the directory name + * @param[in] buffsize Size of @a strbuf in bytes + * @return true for success and false otherwise + */ +bool ethash_get_default_dirname(char* strbuf, size_t buffsize); + +static inline bool ethash_io_mutable_name( + uint32_t revision, + ethash_h256_t const* seed_hash, + char* output +) +{ + uint64_t hash = *((uint64_t*)seed_hash); +#if LITTLE_ENDIAN == BYTE_ORDER + hash = ethash_swap_u64(hash); +#endif + return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "full-R%u-%016" PRIx64, revision, hash) >= 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_posix.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_posix.c new file mode 100644 index 0000000..c9a17d8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_posix.c @@ -0,0 +1,111 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io_posix.c + * @author Lefteris Karapetsas + * @date 2015 + */ + +#include "io.h" +#include +#include +#include +#include +#include +#include +#include +#include + +FILE* ethash_fopen(char const* file_name, char const* mode) +{ + return fopen(file_name, mode); +} + +char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count) +{ + return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL; +} + +bool ethash_mkdir(char const* dirname) +{ + int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + return rc != -1 || errno == EEXIST; +} + +int ethash_fileno(FILE *f) +{ + return fileno(f); +} + +char* ethash_io_create_filename( + char const* dirname, + char const* filename, + size_t filename_length +) +{ + size_t dirlen = strlen(dirname); + size_t dest_size = dirlen + filename_length + 1; + if (dirname[dirlen] != '/') { + dest_size += 1; + } + char* name = malloc(dest_size); + if (!name) { + return NULL; + } + + name[0] = '\0'; + ethash_strncat(name, dest_size, dirname, dirlen); + if (dirname[dirlen] != '/') { + ethash_strncat(name, dest_size, "/", 1); + } + ethash_strncat(name, dest_size, filename, filename_length); + return name; +} + +bool ethash_file_size(FILE* f, size_t* ret_size) +{ + struct stat st; + int fd; + if ((fd = fileno(f)) == -1 || fstat(fd, &st) != 0) { + return false; + } + *ret_size = st.st_size; + return true; +} + +bool ethash_get_default_dirname(char* strbuf, size_t buffsize) +{ + static const char dir_suffix[] = ".ethash/"; + strbuf[0] = '\0'; + char* home_dir = getenv("HOME"); + if (!home_dir || strlen(home_dir) == 0) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) + home_dir = pwd->pw_dir; + } + + size_t len = strlen(home_dir); + if (!ethash_strncat(strbuf, buffsize, home_dir, len)) { + return false; + } + if (home_dir[len] != '/') { + if (!ethash_strncat(strbuf, buffsize, "/", 1)) { + return false; + } + } + return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix)); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_win32.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_win32.c new file mode 100644 index 0000000..34f1aaa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/io_win32.c @@ -0,0 +1,100 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io_win32.c + * @author Lefteris Karapetsas + * @date 2015 + */ + +#include "io.h" +#include +#include +#include +#include +#include +#include + +FILE* ethash_fopen(char const* file_name, char const* mode) +{ + FILE* f; + return fopen_s(&f, file_name, mode) == 0 ? f : NULL; +} + +char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count) +{ + return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL; +} + +bool ethash_mkdir(char const* dirname) +{ + int rc = _mkdir(dirname); + return rc != -1 || errno == EEXIST; +} + +int ethash_fileno(FILE* f) +{ + return _fileno(f); +} + +char* ethash_io_create_filename( + char const* dirname, + char const* filename, + size_t filename_length +) +{ + size_t dirlen = strlen(dirname); + size_t dest_size = dirlen + filename_length + 1; + if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') { + dest_size += 1; + } + char* name = malloc(dest_size); + if (!name) { + return NULL; + } + + name[0] = '\0'; + ethash_strncat(name, dest_size, dirname, dirlen); + if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') { + ethash_strncat(name, dest_size, "\\", 1); + } + ethash_strncat(name, dest_size, filename, filename_length); + return name; +} + +bool ethash_file_size(FILE* f, size_t* ret_size) +{ + struct _stat st; + int fd; + if ((fd = _fileno(f)) == -1 || _fstat(fd, &st) != 0) { + return false; + } + *ret_size = st.st_size; + return true; +} + +bool ethash_get_default_dirname(char* strbuf, size_t buffsize) +{ + static const char dir_suffix[] = "Ethash\\"; + strbuf[0] = '\0'; + if (!SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, (CHAR*)strbuf))) { + return false; + } + if (!ethash_strncat(strbuf, buffsize, "\\", 1)) { + return false; + } + + return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix)); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap.h new file mode 100644 index 0000000..1e226e8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap.h @@ -0,0 +1,47 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file mmap.h + * @author Lefteris Karapetsas + * @date 2015 + */ +#pragma once +#if defined(__MINGW32__) || defined(_WIN32) +#include + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE +#define PROT_EXEC 0x4 +#else +#define PROT_EXEC 0x0 +#define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset); +void munmap(void* addr, size_t length); +#else // posix, yay! ^_^ +#include +#endif + + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap_win32.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap_win32.c new file mode 100644 index 0000000..42968b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/mmap_win32.c @@ -0,0 +1,84 @@ +/* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#include +#include +#include "mmap.h" + +#ifdef __USE_FILE_OFFSET64 +# define DWORD_HI(x) (x >> 32) +# define DWORD_LO(x) ((x) & 0xffffffff) +#else +# define DWORD_HI(x) (0) +# define DWORD_LO(x) (x) +#endif + +void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + ret = MAP_FAILED; + } + // since we are handling the file ourselves with fd, close the Windows Handle here + CloseHandle(h); + return ret; +} + +void munmap(void* addr, size_t length) +{ + UnmapViewOfFile(addr); +} + +#undef DWORD_HI +#undef DWORD_LO diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.c new file mode 100644 index 0000000..e72fe10 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.c @@ -0,0 +1,151 @@ +/** libkeccak-tiny +* +* A single-file implementation of SHA-3 and SHAKE. +* +* Implementor: David Leon Gil +* License: CC0, attribution kindly requested. Blame taken too, +* but not liability. +*/ +#include "sha3.h" + +#include +#include +#include +#include + +/******** The Keccak-f[1600] permutation ********/ + +/*** Constants. ***/ +static const uint8_t rho[24] = \ + { 1, 3, 6, 10, 15, 21, + 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, + 62, 18, 39, 61, 20, 44}; +static const uint8_t pi[24] = \ + {10, 7, 11, 17, 18, 3, + 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, + 20, 14, 22, 9, 6, 1}; +static const uint64_t RC[24] = \ + {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, + 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; + +/*** Helper macros to unroll the permutation. ***/ +#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) +#define REPEAT6(e) e e e e e e +#define REPEAT24(e) REPEAT6(e e e e) +#define REPEAT5(e) e e e e e +#define FOR5(v, s, e) \ + v = 0; \ + REPEAT5(e; v += s;) + +/*** Keccak-f[1600] ***/ +static inline void keccakf(void* state) { + uint64_t* a = (uint64_t*)state; + uint64_t b[5] = {0}; + uint64_t t = 0; + uint8_t x, y; + + for (int i = 0; i < 24; i++) { + // Theta + FOR5(x, 1, + b[x] = 0; + FOR5(y, 5, + b[x] ^= a[x + y]; )) + FOR5(x, 1, + FOR5(y, 5, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) + // Rho and pi + t = a[1]; + x = 0; + REPEAT24(b[0] = a[pi[x]]; + a[pi[x]] = rol(t, rho[x]); + t = b[0]; + x++; ) + // Chi + FOR5(y, + 5, + FOR5(x, 1, + b[x] = a[y + x];) + FOR5(x, 1, + a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) + // Iota + a[0] ^= RC[i]; + } +} + +/******** The FIPS202-defined functions. ********/ + +/*** Some helper macros. ***/ + +#define _(S) do { S } while (0) +#define FOR(i, ST, L, S) \ + _(for (size_t i = 0; i < L; i += ST) { S; }) +#define mkapply_ds(NAME, S) \ + static inline void NAME(uint8_t* dst, \ + const uint8_t* src, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } +#define mkapply_sd(NAME, S) \ + static inline void NAME(const uint8_t* src, \ + uint8_t* dst, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } + +mkapply_ds(xorin, dst[i] ^= src[i]) // xorin +mkapply_sd(setout, dst[i] = src[i]) // setout + +#define P keccakf +#define Plen 200 + +// Fold P*F over the full blocks of an input. +#define foldP(I, L, F) \ + while (L >= rate) { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } + +/** The sponge-based hash construction. **/ +static inline int hash(uint8_t* out, size_t outlen, + const uint8_t* in, size_t inlen, + size_t rate, uint8_t delim) { + if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { + return -1; + } + uint8_t a[Plen] = {0}; + // Absorb input. + foldP(in, inlen, xorin); + // Xor in the DS and pad frame. + a[inlen] ^= delim; + a[rate - 1] ^= 0x80; + // Xor in the last block. + xorin(a, in, inlen); + // Apply P + P(a); + // Squeeze output. + foldP(out, outlen, setout); + setout(a, out, outlen); + memset(a, 0, 200); + return 0; +} + +#define defsha3(bits) \ + int sha3_##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + if (outlen > (bits/8)) { \ + return -1; \ + } \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ + } + +/*** FIPS202 SHA3 FOFs ***/ +defsha3(256) +defsha3(512) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.h new file mode 100644 index 0000000..a380062 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3.h @@ -0,0 +1,31 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "compiler.h" +#include +#include + +struct ethash_h256; + +#define decsha3(bits) \ + int sha3_##bits(uint8_t*, size_t, uint8_t const*, size_t); + +decsha3(256) +decsha3(512) + +static inline void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t const size) +{ + sha3_256((uint8_t*)ret, 32, data, size); +} + +static inline void SHA3_512(uint8_t* ret, uint8_t const* data, size_t const size) +{ + sha3_512(ret, 64, data, size); +} + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.cpp b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.cpp new file mode 100644 index 0000000..2a7c026 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.cpp @@ -0,0 +1,37 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ + +/** @file sha3.cpp +* @author Tim Hughes +* @date 2015 +*/ +#include +#include + +extern "C" { +struct ethash_h256; +typedef struct ethash_h256 ethash_h256_t; +void SHA3_256(ethash_h256_t const* ret, uint8_t const* data, size_t size) +{ + CryptoPP::SHA3_256().CalculateDigest((uint8_t*)ret, data, size); +} + +void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size) +{ + CryptoPP::SHA3_512().CalculateDigest(ret, data, size); +} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.h new file mode 100644 index 0000000..9edc407 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/sha3_cryptopp.h @@ -0,0 +1,18 @@ +#pragma once + +#include "compiler.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ethash_h256; + +void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t size); +void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util.h new file mode 100644 index 0000000..c5fc6e5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util.h @@ -0,0 +1,47 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file util.h + * @author Tim Hughes + * @date 2015 + */ +#pragma once +#include +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +void debugf(char const* str, ...); +#else +#define debugf printf +#endif + +static inline uint32_t min_u32(uint32_t a, uint32_t b) +{ + return a < b ? a : b; +} + +static inline uint32_t clamp_u32(uint32_t x, uint32_t min_, uint32_t max_) +{ + return x < min_ ? min_ : (x > max_ ? max_ : x); +} + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util_win32.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util_win32.c new file mode 100644 index 0000000..268e6db --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/ethereum/ethash/src/libethash/util_win32.c @@ -0,0 +1,38 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file util.c + * @author Tim Hughes + * @date 2015 + */ +#include +#include +#include "util.h" + + +// foward declare without all of Windows.h +__declspec(dllimport) void __stdcall OutputDebugStringA(char const* lpOutputString); + +void debugf(char const* str, ...) +{ + va_list args; + va_start(args, str); + + char buf[1<<16]; + _vsnprintf_s(buf, sizeof(buf), sizeof(buf), str, args); + buf[sizeof(buf)-1] = '\0'; + OutputDebugStringA(buf); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000..25fdaf6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/README.md new file mode 100644 index 0000000..25abbca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/README.md @@ -0,0 +1,175 @@ +# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color) + + + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + + + +![Color](http://i.imgur.com/c1JI0lA.png) + + +## Install + +```bash +go get github.com/fatih/color +``` + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable color + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + +```go + +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/color.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/color.go new file mode 100644 index 0000000..34cd8e4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/color.go @@ -0,0 +1,525 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. This is a global option and affects all colors. For more control + // over each color block use the methods DisableColor() individually. + NoColor = !isatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("TERM") == "dumb" + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{params: make([]Attribute, 0)} + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user setted action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is an convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is an convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is an convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is an convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is an convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is an convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is an convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is an convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is an convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is an convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is an convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is an convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is an convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is an convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is an convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is an convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/doc.go new file mode 100644 index 0000000..1e57812 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,128 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions works as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/LICENSE new file mode 100644 index 0000000..311ccc7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Zack Guo + +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. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/README.md new file mode 100644 index 0000000..d5f3d9a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/README.md @@ -0,0 +1,151 @@ +# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui) + +demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.) + +`termui` is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard. It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go. + +Now version v2 has arrived! It brings new event system, new theme system, new `Buffer` interface and specific colour text rendering. (some docs are missing, but it will be completed soon!) + +## Installation + +`master` mirrors v2 branch, to install: + + go get -u github.com/gizak/termui + +It is recommanded to use locked deps by using [glide](https://glide.sh): move to `termui` src directory then run `glide up`. + +For the compatible reason, you can choose to install the legacy version of `termui`: + + go get gopkg.in/gizak/termui.v1 + +## Usage + +### Layout + +To use `termui`, the very first thing you may want to know is how to manage layout. `termui` offers two ways of doing this, known as absolute layout and grid layout. + +__Absolute layout__ + +Each widget has an underlying block structure which basically is a box model. It has border, label and padding properties. A border of a widget can be chosen to hide or display (with its border label), you can pick a different front/back colour for the border as well. To display such a widget at a specific location in terminal window, you need to assign `.X`, `.Y`, `.Height`, `.Width` values for each widget before sending it to `.Render`. Let's demonstrate these by a code snippet: + +`````go + import ui "github.com/gizak/termui" // <- ui shortcut, optional + + func main() { + err := ui.Init() + if err != nil { + panic(err) + } + defer ui.Close() + + p := ui.NewPar(":PRESS q TO QUIT DEMO") + p.Height = 3 + p.Width = 50 + p.TextFgColor = ui.ColorWhite + p.BorderLabel = "Text Box" + p.BorderFg = ui.ColorCyan + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.Height = 3 + g.Y = 11 + g.BorderLabel = "Gauge" + g.BarColor = ui.ColorRed + g.BorderFg = ui.ColorWhite + g.BorderLabelFg = ui.ColorCyan + + ui.Render(p, g) // feel free to call Render, it's async and non-block + + // event handler... + } +````` + +Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right). + +__Grid layout:__ + +grid + +Grid layout uses [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) with expressive syntax. To use `Grid`, all we need to do is build a widget tree consisting of `Row`s and `Col`s (Actually a `Col` is also a `Row` but with a widget endpoint attached). + +```go + import ui "github.com/gizak/termui" + // init and create widgets... + + // build + ui.Body.AddRows( + ui.NewRow( + ui.NewCol(6, 0, widget0), + ui.NewCol(6, 0, widget1)), + ui.NewRow( + ui.NewCol(3, 0, widget2), + ui.NewCol(3, 0, widget30, widget31, widget32), + ui.NewCol(6, 0, widget4))) + + // calculate layout + ui.Body.Align() + + ui.Render(ui.Body) +``` + +### Events + +`termui` ships with a http-like event mux handling system. All events are channeled up from different sources (typing, click, windows resize, custom event) and then encoded as universal `Event` object. `Event.Path` indicates the event type and `Event.Data` stores the event data struct. Add a handler to a certain event is easy as below: + +```go + // handle key q pressing + ui.Handle("/sys/kbd/q", func(ui.Event) { + // press q to quit + ui.StopLoop() + }) + + ui.Handle("/sys/kbd/C-x", func(ui.Event) { + // handle Ctrl + x combination + }) + + ui.Handle("/sys/kbd", func(ui.Event) { + // handle all other key pressing + }) + + // handle a 1s timer + ui.Handle("/timer/1s", func(e ui.Event) { + t := e.Data.(ui.EvtTimer) + // t is a EvtTimer + if t.Count%2 ==0 { + // do something + } + }) + + ui.Loop() // block until StopLoop is called +``` + +### Widgets + +Click image to see the corresponding demo codes. + +[par](https://github.com/gizak/termui/blob/master/_example/par.go) +[list](https://github.com/gizak/termui/blob/master/_example/list.go) +[gauge](https://github.com/gizak/termui/blob/master/_example/gauge.go) +[linechart](https://github.com/gizak/termui/blob/master/_example/linechart.go) +[barchart](https://github.com/gizak/termui/blob/master/_example/barchart.go) +[barchart](https://github.com/gizak/termui/blob/master/_example/mbarchart.go) +[sparklines](https://github.com/gizak/termui/blob/master/_example/sparklines.go) +[table](https://github.com/gizak/termui/blob/master/_example/table.go) + +## GoDoc + +[godoc](https://godoc.org/github.com/gizak/termui) + +## TODO + +- [x] Grid layout +- [x] Event system +- [x] Canvas widget +- [x] Refine APIs +- [ ] Focusable widgets + +## Changelog + +## License +This library is under the [MIT License](http://opensource.org/licenses/MIT) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/barchart.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/barchart.go new file mode 100644 index 0000000..6560c8b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/barchart.go @@ -0,0 +1,149 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "fmt" + +// BarChart creates multiple bars in a widget: +/* + bc := termui.NewBarChart() + data := []int{3, 2, 5, 3, 9, 5} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.BorderLabel = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow +*/ +type BarChart struct { + Block + BarColor Attribute + TextColor Attribute + NumColor Attribute + Data []int + DataLabels []string + BarWidth int + BarGap int + CellChar rune + labels [][]rune + dataNum [][]rune + numBar int + scale float64 + max int +} + +// NewBarChart returns a new *BarChart with current theme. +func NewBarChart() *BarChart { + bc := &BarChart{Block: *NewBlock()} + bc.BarColor = ThemeAttr("barchart.bar.bg") + bc.NumColor = ThemeAttr("barchart.num.fg") + bc.TextColor = ThemeAttr("barchart.text.fg") + bc.BarGap = 1 + bc.BarWidth = 3 + bc.CellChar = ' ' + return bc +} + +func (bc *BarChart) layout() { + bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth) + bc.labels = make([][]rune, bc.numBar) + bc.dataNum = make([][]rune, len(bc.Data)) + + for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ { + bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) + n := bc.Data[i] + s := fmt.Sprint(n) + bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth) + } + + //bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range + // Asign a negative value to get maxvalue auto-populates + if bc.max == 0 { + bc.max = -1 + } + for i := 0; i < len(bc.Data); i++ { + if bc.max < bc.Data[i] { + bc.max = bc.Data[i] + } + } + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1) +} + +func (bc *BarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *BarChart) Buffer() Buffer { + buf := bc.Block.Buffer() + bc.layout() + + for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ { + h := int(float64(bc.Data[i]) / bc.scale) + oftX := i * (bc.BarWidth + bc.BarGap) + + barBg := bc.Bg + barFg := bc.BarColor + + if bc.CellChar == ' ' { + barBg = bc.BarColor + barFg = ColorDefault + if bc.BarColor == ColorDefault { // the same as above + barBg |= AttrReverse + } + } + + // plot bar + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + c := Cell{ + Ch: bc.CellChar, + Bg: barBg, + Fg: barFg, + } + + x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k + buf.Set(x, y, c) + } + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + c := Cell{ + Ch: bc.labels[i][j], + Bg: bc.Bg, + Fg: bc.TextColor, + } + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1 + x := bc.innerArea.Min.X + oftX + k + buf.Set(x, y, c) + k += w + } + // plot num + for j := 0; j < len(bc.dataNum[i]); j++ { + c := Cell{ + Ch: bc.dataNum[i][j], + Fg: bc.NumColor, + Bg: barBg, + } + + if h == 0 { + c.Bg = bc.Bg + } + x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 + buf.Set(x, y, c) + } + } + + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block.go new file mode 100644 index 0000000..3e8571b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block.go @@ -0,0 +1,240 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Hline is a horizontal line. +type Hline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute +} + +// Vline is a vertical line. +type Vline struct { + X int + Y int + Len int + Fg Attribute + Bg Attribute +} + +// Buffer draws a horizontal line. +func (l Hline) Buffer() Buffer { + if l.Len <= 0 { + return NewBuffer() + } + return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg) +} + +// Buffer draws a vertical line. +func (l Vline) Buffer() Buffer { + if l.Len <= 0 { + return NewBuffer() + } + return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg) +} + +// Buffer draws a box border. +func (b Block) drawBorder(buf Buffer) { + if !b.Border { + return + } + + min := b.area.Min + max := b.area.Max + + x0 := min.X + y0 := min.Y + x1 := max.X - 1 + y1 := max.Y - 1 + + // draw lines + if b.BorderTop { + buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderBottom { + buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderLeft { + buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) + } + if b.BorderRight { + buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) + } + + // draw corners + if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 { + buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg}) + } + if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 { + buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg}) + } + if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 { + buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg}) + } + if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 { + buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg}) + } +} + +func (b Block) drawBorderLabel(buf Buffer) { + maxTxtW := b.area.Dx() - 2 + tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW) + + for i, w := 0, 0; i < len(tx); i++ { + buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i]) + w += tx[i].Width() + } +} + +// Block is a base struct for all other upper level widgets, +// consider it as css: display:block. +// Normally you do not need to create it manually. +type Block struct { + area image.Rectangle + innerArea image.Rectangle + X int + Y int + Border bool + BorderFg Attribute + BorderBg Attribute + BorderLeft bool + BorderRight bool + BorderTop bool + BorderBottom bool + BorderLabel string + BorderLabelFg Attribute + BorderLabelBg Attribute + Display bool + Bg Attribute + Width int + Height int + PaddingTop int + PaddingBottom int + PaddingLeft int + PaddingRight int + id string + Float Align +} + +// NewBlock returns a *Block which inherits styles from current theme. +func NewBlock() *Block { + b := Block{} + b.Display = true + b.Border = true + b.BorderLeft = true + b.BorderRight = true + b.BorderTop = true + b.BorderBottom = true + b.BorderBg = ThemeAttr("border.bg") + b.BorderFg = ThemeAttr("border.fg") + b.BorderLabelBg = ThemeAttr("label.bg") + b.BorderLabelFg = ThemeAttr("label.fg") + b.Bg = ThemeAttr("block.bg") + b.Width = 2 + b.Height = 2 + b.id = GenId() + b.Float = AlignNone + return &b +} + +func (b Block) Id() string { + return b.id +} + +// Align computes box model +func (b *Block) Align() { + // outer + b.area.Min.X = 0 + b.area.Min.Y = 0 + b.area.Max.X = b.Width + b.area.Max.Y = b.Height + + // float + b.area = AlignArea(TermRect(), b.area, b.Float) + b.area = MoveArea(b.area, b.X, b.Y) + + // inner + b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft + b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop + b.innerArea.Max.X = b.area.Max.X - b.PaddingRight + b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom + + if b.Border { + if b.BorderLeft { + b.innerArea.Min.X++ + } + if b.BorderRight { + b.innerArea.Max.X-- + } + if b.BorderTop { + b.innerArea.Min.Y++ + } + if b.BorderBottom { + b.innerArea.Max.Y-- + } + } +} + +// InnerBounds returns the internal bounds of the block after aligning and +// calculating the padding and border, if any. +func (b *Block) InnerBounds() image.Rectangle { + b.Align() + return b.innerArea +} + +// Buffer implements Bufferer interface. +// Draw background and border (if any). +func (b *Block) Buffer() Buffer { + b.Align() + + buf := NewBuffer() + buf.SetArea(b.area) + buf.Fill(' ', ColorDefault, b.Bg) + + b.drawBorder(buf) + b.drawBorderLabel(buf) + + return buf +} + +// GetHeight implements GridBufferer. +// It returns current height of the block. +func (b Block) GetHeight() int { + return b.Height +} + +// SetX implements GridBufferer interface, which sets block's x position. +func (b *Block) SetX(x int) { + b.X = x +} + +// SetY implements GridBufferer interface, it sets y position for block. +func (b *Block) SetY(y int) { + b.Y = y +} + +// SetWidth implements GridBuffer interface, it sets block's width. +func (b *Block) SetWidth(w int) { + b.Width = w +} + +func (b Block) InnerWidth() int { + return b.innerArea.Dx() +} + +func (b Block) InnerHeight() int { + return b.innerArea.Dy() +} + +func (b Block) InnerX() int { + return b.innerArea.Min.X +} + +func (b Block) InnerY() int { return b.innerArea.Min.Y } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_common.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_common.go new file mode 100644 index 0000000..6438bf2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_common.go @@ -0,0 +1,20 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +// +build !windows + +package termui + +const TOP_RIGHT = '┐' +const VERTICAL_LINE = '│' +const HORIZONTAL_LINE = '─' +const TOP_LEFT = '┌' +const BOTTOM_RIGHT = '┘' +const BOTTOM_LEFT = '└' +const VERTICAL_LEFT = '┤' +const VERTICAL_RIGHT = '├' +const HORIZONTAL_DOWN = '┬' +const HORIZONTAL_UP = '┴' +const QUOTA_LEFT = '«' +const QUOTA_RIGHT = '»' diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_windows.go new file mode 100644 index 0000000..a4fba77 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/block_windows.go @@ -0,0 +1,14 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +// +build windows + +package termui + +const TOP_RIGHT = '+' +const VERTICAL_LINE = '|' +const HORIZONTAL_LINE = '-' +const TOP_LEFT = '+' +const BOTTOM_RIGHT = '+' +const BOTTOM_LEFT = '+' diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/buffer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/buffer.go new file mode 100644 index 0000000..9e3a973 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/buffer.go @@ -0,0 +1,106 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Cell is a rune with assigned Fg and Bg +type Cell struct { + Ch rune + Fg Attribute + Bg Attribute +} + +// Buffer is a renderable rectangle cell data container. +type Buffer struct { + Area image.Rectangle // selected drawing area + CellMap map[image.Point]Cell +} + +// At returns the cell at (x,y). +func (b Buffer) At(x, y int) Cell { + return b.CellMap[image.Pt(x, y)] +} + +// Set assigns a char to (x,y) +func (b Buffer) Set(x, y int, c Cell) { + b.CellMap[image.Pt(x, y)] = c +} + +// Bounds returns the domain for which At can return non-zero color. +func (b Buffer) Bounds() image.Rectangle { + x0, y0, x1, y1 := 0, 0, 0, 0 + for p := range b.CellMap { + if p.X > x1 { + x1 = p.X + } + if p.X < x0 { + x0 = p.X + } + if p.Y > y1 { + y1 = p.Y + } + if p.Y < y0 { + y0 = p.Y + } + } + return image.Rect(x0, y0, x1+1, y1+1) +} + +// SetArea assigns a new rect area to Buffer b. +func (b *Buffer) SetArea(r image.Rectangle) { + b.Area.Max = r.Max + b.Area.Min = r.Min +} + +// Sync sets drawing area to the buffer's bound +func (b *Buffer) Sync() { + b.SetArea(b.Bounds()) +} + +// NewCell returns a new cell +func NewCell(ch rune, fg, bg Attribute) Cell { + return Cell{ch, fg, bg} +} + +// Merge merges bs Buffers onto b +func (b *Buffer) Merge(bs ...Buffer) { + for _, buf := range bs { + for p, v := range buf.CellMap { + b.Set(p.X, p.Y, v) + } + b.SetArea(b.Area.Union(buf.Area)) + } +} + +// NewBuffer returns a new Buffer +func NewBuffer() Buffer { + return Buffer{ + CellMap: make(map[image.Point]Cell), + Area: image.Rectangle{}} +} + +// Fill fills the Buffer b with ch,fg and bg. +func (b Buffer) Fill(ch rune, fg, bg Attribute) { + for x := b.Area.Min.X; x < b.Area.Max.X; x++ { + for y := b.Area.Min.Y; y < b.Area.Max.Y; y++ { + b.Set(x, y, Cell{ch, fg, bg}) + } + } +} + +// NewFilledBuffer returns a new Buffer filled with ch, fb and bg. +func NewFilledBuffer(x0, y0, x1, y1 int, ch rune, fg, bg Attribute) Buffer { + buf := NewBuffer() + buf.Area.Min = image.Pt(x0, y0) + buf.Area.Max = image.Pt(x1, y1) + + for x := buf.Area.Min.X; x < buf.Area.Max.X; x++ { + for y := buf.Area.Min.Y; y < buf.Area.Max.Y; y++ { + buf.Set(x, y, Cell{ch, fg, bg}) + } + } + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/canvas.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/canvas.go new file mode 100644 index 0000000..6d2513e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/canvas.go @@ -0,0 +1,72 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +/* +dots: + ,___, + |1 4| + |2 5| + |3 6| + |7 8| + ````` +*/ + +var brailleBase = '\u2800' + +var brailleOftMap = [4][2]rune{ + {'\u0001', '\u0008'}, + {'\u0002', '\u0010'}, + {'\u0004', '\u0020'}, + {'\u0040', '\u0080'}} + +// Canvas contains drawing map: i,j -> rune +type Canvas map[[2]int]rune + +// NewCanvas returns an empty Canvas +func NewCanvas() Canvas { + return make(map[[2]int]rune) +} + +func chOft(x, y int) rune { + return brailleOftMap[y%4][x%2] +} + +func (c Canvas) rawCh(x, y int) rune { + if ch, ok := c[[2]int{x, y}]; ok { + return ch + } + return '\u0000' //brailleOffset +} + +// return coordinate in terminal +func chPos(x, y int) (int, int) { + return y / 4, x / 2 +} + +// Set sets a point (x,y) in the virtual coordinate +func (c Canvas) Set(x, y int) { + i, j := chPos(x, y) + ch := c.rawCh(i, j) + ch |= chOft(x, y) + c[[2]int{i, j}] = ch +} + +// Unset removes point (x,y) +func (c Canvas) Unset(x, y int) { + i, j := chPos(x, y) + ch := c.rawCh(i, j) + ch &= ^chOft(x, y) + c[[2]int{i, j}] = ch +} + +// Buffer returns un-styled points +func (c Canvas) Buffer() Buffer { + buf := NewBuffer() + for k, v := range c { + buf.Set(k[0], k[1], Cell{Ch: v + brailleBase}) + } + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/config.py b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/config.py new file mode 100644 index 0000000..30fadcf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/config.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import re +import os +import io + +copyright = """// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +""" + +exclude_dirs = [".git", "_docs"] +exclude_files = [] +include_dirs = [".", "debug", "extra", "test", "_example"] + + +def is_target(fpath): + if os.path.splitext(fpath)[-1] == ".go": + return True + return False + + +def update_copyright(fpath): + print("processing " + fpath) + f = io.open(fpath, 'r', encoding='utf-8') + fstr = f.read() + f.close() + + # remove old + m = re.search('^// Copyright .+?\r?\n\r?\n', fstr, re.MULTILINE|re.DOTALL) + if m: + fstr = fstr[m.end():] + + # add new + fstr = copyright + fstr + f = io.open(fpath, 'w',encoding='utf-8') + f.write(fstr) + f.close() + + +def main(): + for d in include_dirs: + files = [ + os.path.join(d, f) for f in os.listdir(d) + if os.path.isfile(os.path.join(d, f)) + ] + for f in files: + if is_target(f): + update_copyright(f) + + +if __name__ == '__main__': + main() diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/doc.go new file mode 100644 index 0000000..13924eb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/doc.go @@ -0,0 +1,29 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +/* +Package termui is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui + +A simplest example: + package main + + import ui "github.com/gizak/termui" + + func main() { + if err:=ui.Init(); err != nil { + panic(err) + } + defer ui.Close() + + g := ui.NewGauge() + g.Percent = 50 + g.Width = 50 + g.BorderLabel = "Gauge" + + ui.Render(g) + + ui.Loop() + } +*/ +package termui diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/events.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/events.go new file mode 100644 index 0000000..16d9bd9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/events.go @@ -0,0 +1,324 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "path" + "strconv" + "sync" + "time" + + "github.com/nsf/termbox-go" +) + +type Event struct { + Type string + Path string + From string + To string + Data interface{} + Time int64 +} + +var sysEvtChs []chan Event + +type EvtKbd struct { + KeyStr string +} + +func evtKbd(e termbox.Event) EvtKbd { + ek := EvtKbd{} + + k := string(e.Ch) + pre := "" + mod := "" + + if e.Mod == termbox.ModAlt { + mod = "M-" + } + if e.Ch == 0 { + if e.Key > 0xFFFF-12 { + k = "" + } else if e.Key > 0xFFFF-25 { + ks := []string{"", "", "", "", "", "", "", "", "", ""} + k = ks[0xFFFF-int(e.Key)-12] + } + + if e.Key <= 0x7F { + pre = "C-" + k = string('a' - 1 + int(e.Key)) + kmap := map[termbox.Key][2]string{ + termbox.KeyCtrlSpace: {"C-", ""}, + termbox.KeyBackspace: {"", ""}, + termbox.KeyTab: {"", ""}, + termbox.KeyEnter: {"", ""}, + termbox.KeyEsc: {"", ""}, + termbox.KeyCtrlBackslash: {"C-", "\\"}, + termbox.KeyCtrlSlash: {"C-", "/"}, + termbox.KeySpace: {"", ""}, + termbox.KeyCtrl8: {"C-", "8"}, + } + if sk, ok := kmap[e.Key]; ok { + pre = sk[0] + k = sk[1] + } + } + } + + ek.KeyStr = pre + mod + k + return ek +} + +func crtTermboxEvt(e termbox.Event) Event { + systypemap := map[termbox.EventType]string{ + termbox.EventKey: "keyboard", + termbox.EventResize: "window", + termbox.EventMouse: "mouse", + termbox.EventError: "error", + termbox.EventInterrupt: "interrupt", + } + ne := Event{From: "/sys", Time: time.Now().Unix()} + typ := e.Type + ne.Type = systypemap[typ] + + switch typ { + case termbox.EventKey: + kbd := evtKbd(e) + ne.Path = "/sys/kbd/" + kbd.KeyStr + ne.Data = kbd + case termbox.EventResize: + wnd := EvtWnd{} + wnd.Width = e.Width + wnd.Height = e.Height + ne.Path = "/sys/wnd/resize" + ne.Data = wnd + case termbox.EventError: + err := EvtErr(e.Err) + ne.Path = "/sys/err" + ne.Data = err + case termbox.EventMouse: + m := EvtMouse{} + m.X = e.MouseX + m.Y = e.MouseY + ne.Path = "/sys/mouse" + ne.Data = m + } + return ne +} + +type EvtWnd struct { + Width int + Height int +} + +type EvtMouse struct { + X int + Y int + Press string +} + +type EvtErr error + +func hookTermboxEvt() { + for { + e := termbox.PollEvent() + + for _, c := range sysEvtChs { + go func(ch chan Event) { + ch <- crtTermboxEvt(e) + }(c) + } + } +} + +func NewSysEvtCh() chan Event { + ec := make(chan Event) + sysEvtChs = append(sysEvtChs, ec) + return ec +} + +var DefaultEvtStream = NewEvtStream() + +type EvtStream struct { + sync.RWMutex + srcMap map[string]chan Event + stream chan Event + wg sync.WaitGroup + sigStopLoop chan Event + Handlers map[string]func(Event) + hook func(Event) +} + +func NewEvtStream() *EvtStream { + return &EvtStream{ + srcMap: make(map[string]chan Event), + stream: make(chan Event), + Handlers: make(map[string]func(Event)), + sigStopLoop: make(chan Event), + } +} + +func (es *EvtStream) Init() { + es.Merge("internal", es.sigStopLoop) + go func() { + es.wg.Wait() + close(es.stream) + }() +} + +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + return path.Clean(p) +} + +func isPathMatch(pattern, path string) bool { + if len(pattern) == 0 { + return false + } + n := len(pattern) + return len(path) >= n && path[0:n] == pattern +} + +func (es *EvtStream) Merge(name string, ec chan Event) { + es.Lock() + defer es.Unlock() + + es.wg.Add(1) + es.srcMap[name] = ec + + go func(a chan Event) { + for n := range a { + n.From = name + es.stream <- n + } + es.wg.Done() + }(ec) +} + +func (es *EvtStream) Handle(path string, handler func(Event)) { + es.Handlers[cleanPath(path)] = handler +} + +func findMatch(mux map[string]func(Event), path string) string { + n := -1 + pattern := "" + for m := range mux { + if !isPathMatch(m, path) { + continue + } + if len(m) > n { + pattern = m + n = len(m) + } + } + return pattern + +} + +// Remove all existing defined Handlers from the map +func (es *EvtStream) ResetHandlers() { + for Path, _ := range es.Handlers { + delete(es.Handlers, Path) + } + return +} + +func (es *EvtStream) match(path string) string { + return findMatch(es.Handlers, path) +} + +func (es *EvtStream) Hook(f func(Event)) { + es.hook = f +} + +func (es *EvtStream) Loop() { + for e := range es.stream { + switch e.Path { + case "/sig/stoploop": + return + } + go func(a Event) { + es.RLock() + defer es.RUnlock() + if pattern := es.match(a.Path); pattern != "" { + es.Handlers[pattern](a) + } + }(e) + if es.hook != nil { + es.hook(e) + } + } +} + +func (es *EvtStream) StopLoop() { + go func() { + e := Event{ + Path: "/sig/stoploop", + } + es.sigStopLoop <- e + }() +} + +func Merge(name string, ec chan Event) { + DefaultEvtStream.Merge(name, ec) +} + +func Handle(path string, handler func(Event)) { + DefaultEvtStream.Handle(path, handler) +} + +func Loop() { + DefaultEvtStream.Loop() +} + +func StopLoop() { + DefaultEvtStream.StopLoop() +} + +type EvtTimer struct { + Duration time.Duration + Count uint64 +} + +func NewTimerCh(du time.Duration) chan Event { + t := make(chan Event) + + go func(a chan Event) { + n := uint64(0) + for { + n++ + time.Sleep(du) + e := Event{} + e.Type = "timer" + e.Path = "/timer/" + du.String() + e.Time = time.Now().Unix() + e.Data = EvtTimer{ + Duration: du, + Count: n, + } + t <- e + + } + }(t) + return t +} + +var DefaultHandler = func(e Event) { +} + +var usrEvtCh = make(chan Event) + +func SendCustomEvt(path string, data interface{}) { + e := Event{} + e.Path = path + e.Data = data + e.Time = time.Now().Unix() + usrEvtCh <- e +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/gauge.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/gauge.go new file mode 100644 index 0000000..9f6ce3a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/gauge.go @@ -0,0 +1,109 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "strconv" + "strings" +) + +// Gauge is a progress bar like widget. +// A simple example: +/* + g := termui.NewGauge() + g.Percent = 40 + g.Width = 50 + g.Height = 3 + g.BorderLabel = "Slim Gauge" + g.BarColor = termui.ColorRed + g.PercentColor = termui.ColorBlue +*/ + +const ColorUndef Attribute = Attribute(^uint16(0)) + +type Gauge struct { + Block + Percent int + BarColor Attribute + PercentColor Attribute + PercentColorHighlighted Attribute + Label string + LabelAlign Align +} + +// NewGauge return a new gauge with current theme. +func NewGauge() *Gauge { + g := &Gauge{ + Block: *NewBlock(), + PercentColor: ThemeAttr("gauge.percent.fg"), + BarColor: ThemeAttr("gauge.bar.bg"), + Label: "{{percent}}%", + LabelAlign: AlignCenter, + PercentColorHighlighted: ColorUndef, + } + + g.Width = 12 + g.Height = 5 + return g +} + +// Buffer implements Bufferer interface. +func (g *Gauge) Buffer() Buffer { + buf := g.Block.Buffer() + + // plot bar + w := g.Percent * g.innerArea.Dx() / 100 + for i := 0; i < g.innerArea.Dy(); i++ { + for j := 0; j < w; j++ { + c := Cell{} + c.Ch = ' ' + c.Bg = g.BarColor + if c.Bg == ColorDefault { + c.Bg |= AttrReverse + } + buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c) + } + } + + // plot percentage + s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) + pry := g.innerArea.Min.Y + g.innerArea.Dy()/2 + rs := str2runes(s) + var pos int + switch g.LabelAlign { + case AlignLeft: + pos = 0 + + case AlignCenter: + pos = (g.innerArea.Dx() - strWidth(s)) / 2 + + case AlignRight: + pos = g.innerArea.Dx() - strWidth(s) - 1 + } + pos += g.innerArea.Min.X + + for i, v := range rs { + c := Cell{ + Ch: v, + Fg: g.PercentColor, + } + + if w+g.innerArea.Min.X > pos+i { + c.Bg = g.BarColor + if c.Bg == ColorDefault { + c.Bg |= AttrReverse + } + + if g.PercentColorHighlighted != ColorUndef { + c.Fg = g.PercentColorHighlighted + } + } else { + c.Bg = g.Block.Bg + } + + buf.Set(1+pos+i, pry, c) + } + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.lock b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.lock new file mode 100644 index 0000000..be5952d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.lock @@ -0,0 +1,30 @@ +hash: 7a754ba100256404a978b2fc8738aee337beb822458e4b6060399fb89ebd215c +updated: 2016-11-03T17:39:24.323773674-04:00 +imports: +- name: github.com/maruel/panicparse + version: ad661195ed0e88491e0f14be6613304e3b1141d6 + subpackages: + - stack +- name: github.com/mattn/go-runewidth + version: 737072b4e32b7a5018b4a7125da8d12de90e8045 +- name: github.com/mitchellh/go-wordwrap + version: ad45545899c7b13c020ea92b2072220eefad42b8 +- name: github.com/nsf/termbox-go + version: b6acae516ace002cb8105a89024544a1480655a5 +- name: golang.org/x/net + version: 569280fa63be4e201b975e5411e30a92178f0118 + subpackages: + - websocket +testImports: +- name: github.com/davecgh/go-spew + version: 346938d642f2ec3594ed81d874461961cd0faa76 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 976c720a22c8eb4eb6a0b4348ad85ad12491a506 + subpackages: + - assert diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.yaml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.yaml new file mode 100644 index 0000000..a681231 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/glide.yaml @@ -0,0 +1,9 @@ +package: github.com/gizak/termui +import: +- package: github.com/mattn/go-runewidth +- package: github.com/mitchellh/go-wordwrap +- package: github.com/nsf/termbox-go +- package: golang.org/x/net + subpackages: + - websocket +- package: github.com/maruel/panicparse diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/grid.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/grid.go new file mode 100644 index 0000000..a950232 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/grid.go @@ -0,0 +1,279 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +// GridBufferer introduces a Bufferer that can be manipulated by Grid. +type GridBufferer interface { + Bufferer + GetHeight() int + SetWidth(int) + SetX(int) + SetY(int) +} + +// Row builds a layout tree +type Row struct { + Cols []*Row //children + Widget GridBufferer // root + X int + Y int + Width int + Height int + Span int + Offset int +} + +// calculate and set the underlying layout tree's x, y, height and width. +func (r *Row) calcLayout() { + r.assignWidth(r.Width) + r.Height = r.solveHeight() + r.assignX(r.X) + r.assignY(r.Y) +} + +// tell if the node is leaf in the tree. +func (r *Row) isLeaf() bool { + return r.Cols == nil || len(r.Cols) == 0 +} + +func (r *Row) isRenderableLeaf() bool { + return r.isLeaf() && r.Widget != nil +} + +// assign widgets' (and their parent rows') width recursively. +func (r *Row) assignWidth(w int) { + r.SetWidth(w) + + accW := 0 // acc span and offset + calcW := make([]int, len(r.Cols)) // calculated width + calcOftX := make([]int, len(r.Cols)) // computated start position of x + + for i, c := range r.Cols { + accW += c.Span + c.Offset + cw := int(float64(c.Span*r.Width) / 12.0) + + if i >= 1 { + calcOftX[i] = calcOftX[i-1] + + calcW[i-1] + + int(float64(r.Cols[i-1].Offset*r.Width)/12.0) + } + + // use up the space if it is the last col + if i == len(r.Cols)-1 && accW == 12 { + cw = r.Width - calcOftX[i] + } + calcW[i] = cw + r.Cols[i].assignWidth(cw) + } +} + +// bottom up calc and set rows' (and their widgets') height, +// return r's total height. +func (r *Row) solveHeight() int { + if r.isRenderableLeaf() { + r.Height = r.Widget.GetHeight() + return r.Widget.GetHeight() + } + + maxh := 0 + if !r.isLeaf() { + for _, c := range r.Cols { + nh := c.solveHeight() + // when embed rows in Cols, row widgets stack up + if r.Widget != nil { + nh += r.Widget.GetHeight() + } + if nh > maxh { + maxh = nh + } + } + } + + r.Height = maxh + return maxh +} + +// recursively assign x position for r tree. +func (r *Row) assignX(x int) { + r.SetX(x) + + if !r.isLeaf() { + acc := 0 + for i, c := range r.Cols { + if c.Offset != 0 { + acc += int(float64(c.Offset*r.Width) / 12.0) + } + r.Cols[i].assignX(x + acc) + acc += c.Width + } + } +} + +// recursively assign y position to r. +func (r *Row) assignY(y int) { + r.SetY(y) + + if r.isLeaf() { + return + } + + for i := range r.Cols { + acc := 0 + if r.Widget != nil { + acc = r.Widget.GetHeight() + } + r.Cols[i].assignY(y + acc) + } + +} + +// GetHeight implements GridBufferer interface. +func (r Row) GetHeight() int { + return r.Height +} + +// SetX implements GridBufferer interface. +func (r *Row) SetX(x int) { + r.X = x + if r.Widget != nil { + r.Widget.SetX(x) + } +} + +// SetY implements GridBufferer interface. +func (r *Row) SetY(y int) { + r.Y = y + if r.Widget != nil { + r.Widget.SetY(y) + } +} + +// SetWidth implements GridBufferer interface. +func (r *Row) SetWidth(w int) { + r.Width = w + if r.Widget != nil { + r.Widget.SetWidth(w) + } +} + +// Buffer implements Bufferer interface, +// recursively merge all widgets buffer +func (r *Row) Buffer() Buffer { + merged := NewBuffer() + + if r.isRenderableLeaf() { + return r.Widget.Buffer() + } + + // for those are not leaves but have a renderable widget + if r.Widget != nil { + merged.Merge(r.Widget.Buffer()) + } + + // collect buffer from children + if !r.isLeaf() { + for _, c := range r.Cols { + merged.Merge(c.Buffer()) + } + } + + return merged +} + +// Grid implements 12 columns system. +// A simple example: +/* + import ui "github.com/gizak/termui" + // init and create widgets... + + // build + ui.Body.AddRows( + ui.NewRow( + ui.NewCol(6, 0, widget0), + ui.NewCol(6, 0, widget1)), + ui.NewRow( + ui.NewCol(3, 0, widget2), + ui.NewCol(3, 0, widget30, widget31, widget32), + ui.NewCol(6, 0, widget4))) + + // calculate layout + ui.Body.Align() + + ui.Render(ui.Body) +*/ +type Grid struct { + Rows []*Row + Width int + X int + Y int + BgColor Attribute +} + +// NewGrid returns *Grid with given rows. +func NewGrid(rows ...*Row) *Grid { + return &Grid{Rows: rows} +} + +// AddRows appends given rows to Grid. +func (g *Grid) AddRows(rs ...*Row) { + g.Rows = append(g.Rows, rs...) +} + +// NewRow creates a new row out of given columns. +func NewRow(cols ...*Row) *Row { + rs := &Row{Span: 12, Cols: cols} + return rs +} + +// NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow. +// Note that if multiple widgets are provided, they will stack up in the col. +func NewCol(span, offset int, widgets ...GridBufferer) *Row { + r := &Row{Span: span, Offset: offset} + + if widgets != nil && len(widgets) == 1 { + wgt := widgets[0] + nw, isRow := wgt.(*Row) + if isRow { + r.Cols = nw.Cols + } else { + r.Widget = wgt + } + return r + } + + r.Cols = []*Row{} + ir := r + for _, w := range widgets { + nr := &Row{Span: 12, Widget: w} + ir.Cols = []*Row{nr} + ir = nr + } + + return r +} + +// Align calculate each rows' layout. +func (g *Grid) Align() { + h := 0 + for _, r := range g.Rows { + r.SetWidth(g.Width) + r.SetX(g.X) + r.SetY(g.Y + h) + r.calcLayout() + h += r.GetHeight() + } +} + +// Buffer implments Bufferer interface. +func (g Grid) Buffer() Buffer { + buf := NewBuffer() + + for _, r := range g.Rows { + buf.Merge(r.Buffer()) + } + return buf +} + +var Body *Grid diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/helper.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/helper.go new file mode 100644 index 0000000..18a6770 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/helper.go @@ -0,0 +1,222 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "regexp" + "strings" + + tm "github.com/nsf/termbox-go" +) +import rw "github.com/mattn/go-runewidth" + +/* ---------------Port from termbox-go --------------------- */ + +// Attribute is printable cell's color and style. +type Attribute uint16 + +// 8 basic clolrs +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +//Have a constant that defines number of colors +const NumberofColors = 8 + +// Text style +const ( + AttrBold Attribute = 1 << (iota + 9) + AttrUnderline + AttrReverse +) + +var ( + dot = "…" + dotw = rw.StringWidth(dot) +) + +/* ----------------------- End ----------------------------- */ + +func toTmAttr(x Attribute) tm.Attribute { + return tm.Attribute(x) +} + +func str2runes(s string) []rune { + return []rune(s) +} + +// Here for backwards-compatibility. +func trimStr2Runes(s string, w int) []rune { + return TrimStr2Runes(s, w) +} + +// TrimStr2Runes trims string to w[-1 rune], appends …, and returns the runes +// of that string if string is grather then n. If string is small then w, +// return the runes. +func TrimStr2Runes(s string, w int) []rune { + if w <= 0 { + return []rune{} + } + + sw := rw.StringWidth(s) + if sw > w { + return []rune(rw.Truncate(s, w, dot)) + } + return str2runes(s) +} + +// TrimStrIfAppropriate trim string to "s[:-1] + …" +// if string > width otherwise return string +func TrimStrIfAppropriate(s string, w int) string { + if w <= 0 { + return "" + } + + sw := rw.StringWidth(s) + if sw > w { + return rw.Truncate(s, w, dot) + } + + return s +} + +func strWidth(s string) int { + return rw.StringWidth(s) +} + +func charWidth(ch rune) int { + return rw.RuneWidth(ch) +} + +var whiteSpaceRegex = regexp.MustCompile(`\s`) + +// StringToAttribute converts text to a termui attribute. You may specifiy more +// then one attribute like that: "BLACK, BOLD, ...". All whitespaces +// are ignored. +func StringToAttribute(text string) Attribute { + text = whiteSpaceRegex.ReplaceAllString(strings.ToLower(text), "") + attributes := strings.Split(text, ",") + result := Attribute(0) + + for _, theAttribute := range attributes { + var match Attribute + switch theAttribute { + case "reset", "default": + match = ColorDefault + + case "black": + match = ColorBlack + + case "red": + match = ColorRed + + case "green": + match = ColorGreen + + case "yellow": + match = ColorYellow + + case "blue": + match = ColorBlue + + case "magenta": + match = ColorMagenta + + case "cyan": + match = ColorCyan + + case "white": + match = ColorWhite + + case "bold": + match = AttrBold + + case "underline": + match = AttrUnderline + + case "reverse": + match = AttrReverse + } + + result |= match + } + + return result +} + +// TextCells returns a coloured text cells []Cell +func TextCells(s string, fg, bg Attribute) []Cell { + cs := make([]Cell, 0, len(s)) + + // sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg) + // runes := []rune(sequence.NormalizedText) + runes := str2runes(s) + + for n := range runes { + // point, _ := sequence.PointAt(n, 0, 0) + // cs = append(cs, Cell{point.Ch, point.Fg, point.Bg}) + cs = append(cs, Cell{runes[n], fg, bg}) + } + return cs +} + +// Width returns the actual screen space the cell takes (usually 1 or 2). +func (c Cell) Width() int { + return charWidth(c.Ch) +} + +// Copy return a copy of c +func (c Cell) Copy() Cell { + return c +} + +// TrimTxCells trims the overflowed text cells sequence. +func TrimTxCells(cs []Cell, w int) []Cell { + if len(cs) <= w { + return cs + } + return cs[:w] +} + +// DTrimTxCls trims the overflowed text cells sequence and append dots at the end. +func DTrimTxCls(cs []Cell, w int) []Cell { + l := len(cs) + if l <= 0 { + return []Cell{} + } + + rt := make([]Cell, 0, w) + csw := 0 + for i := 0; i < l && csw <= w; i++ { + c := cs[i] + cw := c.Width() + + if cw+csw < w { + rt = append(rt, c) + csw += cw + } else { + rt = append(rt, Cell{'…', c.Fg, c.Bg}) + break + } + } + + return rt +} + +func CellsToStr(cs []Cell) string { + str := "" + for _, c := range cs { + str += string(c.Ch) + } + return str +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart.go new file mode 100644 index 0000000..f7eea28 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart.go @@ -0,0 +1,331 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "fmt" + "math" +) + +// only 16 possible combinations, why bother +var braillePatterns = map[[2]int]rune{ + [2]int{0, 0}: '⣀', + [2]int{0, 1}: '⡠', + [2]int{0, 2}: '⡐', + [2]int{0, 3}: '⡈', + + [2]int{1, 0}: '⢄', + [2]int{1, 1}: '⠤', + [2]int{1, 2}: '⠔', + [2]int{1, 3}: '⠌', + + [2]int{2, 0}: '⢂', + [2]int{2, 1}: '⠢', + [2]int{2, 2}: '⠒', + [2]int{2, 3}: '⠊', + + [2]int{3, 0}: '⢁', + [2]int{3, 1}: '⠡', + [2]int{3, 2}: '⠑', + [2]int{3, 3}: '⠉', +} + +var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'} +var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'} + +// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode, +// because one braille char can represent two data points. +/* + lc := termui.NewLineChart() + lc.BorderLabel = "braille-mode Line Chart" + lc.Data = [1.2, 1.3, 1.5, 1.7, 1.5, 1.6, 1.8, 2.0] + lc.Width = 50 + lc.Height = 12 + lc.AxesColor = termui.ColorWhite + lc.LineColor = termui.ColorGreen | termui.AttrBold + // termui.Render(lc)... +*/ +type LineChart struct { + Block + Data []float64 + DataLabels []string // if unset, the data indices will be used + Mode string // braille | dot + DotStyle rune + LineColor Attribute + scale float64 // data span per cell on y-axis + AxesColor Attribute + drawingX int + drawingY int + axisYHeight int + axisXWidth int + axisYLabelGap int + axisXLabelGap int + topValue float64 + bottomValue float64 + labelX [][]rune + labelY [][]rune + labelYSpace int + maxY float64 + minY float64 + autoLabels bool +} + +// NewLineChart returns a new LineChart with current theme. +func NewLineChart() *LineChart { + lc := &LineChart{Block: *NewBlock()} + lc.AxesColor = ThemeAttr("linechart.axes.fg") + lc.LineColor = ThemeAttr("linechart.line.fg") + lc.Mode = "braille" + lc.DotStyle = '•' + lc.axisXLabelGap = 2 + lc.axisYLabelGap = 1 + lc.bottomValue = math.Inf(1) + lc.topValue = math.Inf(-1) + return lc +} + +// one cell contains two data points +// so the capicity is 2x as dot-mode +func (lc *LineChart) renderBraille() Buffer { + buf := NewBuffer() + + // return: b -> which cell should the point be in + // m -> in the cell, divided into 4 equal height levels, which subcell? + getPos := func(d float64) (b, m int) { + cnt4 := int((d-lc.bottomValue)/(lc.scale/4) + 0.5) + b = cnt4 / 4 + m = cnt4 % 4 + return + } + // plot points + for i := 0; 2*i+1 < len(lc.Data) && i < lc.axisXWidth; i++ { + b0, m0 := getPos(lc.Data[2*i]) + b1, m1 := getPos(lc.Data[2*i+1]) + + if b0 == b1 { + c := Cell{ + Ch: braillePatterns[[2]int{m0, m1}], + Bg: lc.Bg, + Fg: lc.LineColor, + } + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0 + x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + buf.Set(x, y, c) + } else { + c0 := Cell{Ch: lSingleBraille[m0], + Fg: lc.LineColor, + Bg: lc.Bg} + x0 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y0 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0 + buf.Set(x0, y0, c0) + + c1 := Cell{Ch: rSingleBraille[m1], + Fg: lc.LineColor, + Bg: lc.Bg} + x1 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y1 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b1 + buf.Set(x1, y1, c1) + } + + } + return buf +} + +func (lc *LineChart) renderDot() Buffer { + buf := NewBuffer() + for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ { + c := Cell{ + Ch: lc.DotStyle, + Fg: lc.LineColor, + Bg: lc.Bg, + } + x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5) + buf.Set(x, y, c) + } + + return buf +} + +func (lc *LineChart) calcLabelX() { + lc.labelX = [][]rune{} + + for i, l := 0, 0; i < len(lc.DataLabels) && l < lc.axisXWidth; i++ { + if lc.Mode == "dot" { + if l >= len(lc.DataLabels) { + break + } + + s := str2runes(lc.DataLabels[l]) + w := strWidth(lc.DataLabels[l]) + if l+w <= lc.axisXWidth { + lc.labelX = append(lc.labelX, s) + } + l += w + lc.axisXLabelGap + } else { // braille + if 2*l >= len(lc.DataLabels) { + break + } + + s := str2runes(lc.DataLabels[2*l]) + w := strWidth(lc.DataLabels[2*l]) + if l+w <= lc.axisXWidth { + lc.labelX = append(lc.labelX, s) + } + l += w + lc.axisXLabelGap + + } + } +} + +func shortenFloatVal(x float64) string { + s := fmt.Sprintf("%.2f", x) + if len(s)-3 > 3 { + s = fmt.Sprintf("%.2e", x) + } + + if x < 0 { + s = fmt.Sprintf("%.2f", x) + } + return s +} + +func (lc *LineChart) calcLabelY() { + span := lc.topValue - lc.bottomValue + lc.scale = span / float64(lc.axisYHeight) + + n := (1 + lc.axisYHeight) / (lc.axisYLabelGap + 1) + lc.labelY = make([][]rune, n) + maxLen := 0 + for i := 0; i < n; i++ { + s := str2runes(shortenFloatVal(lc.bottomValue + float64(i)*span/float64(n))) + if len(s) > maxLen { + maxLen = len(s) + } + lc.labelY[i] = s + } + + lc.labelYSpace = maxLen +} + +func (lc *LineChart) calcLayout() { + // set datalabels if it is not provided + if (lc.DataLabels == nil || len(lc.DataLabels) == 0) || lc.autoLabels { + lc.autoLabels = true + lc.DataLabels = make([]string, len(lc.Data)) + for i := range lc.Data { + lc.DataLabels[i] = fmt.Sprint(i) + } + } + + // lazy increase, to avoid y shaking frequently + // update bound Y when drawing is gonna overflow + lc.minY = lc.Data[0] + lc.maxY = lc.Data[0] + + // valid visible range + vrange := lc.innerArea.Dx() + if lc.Mode == "braille" { + vrange = 2 * lc.innerArea.Dx() + } + if vrange > len(lc.Data) { + vrange = len(lc.Data) + } + + for _, v := range lc.Data[:vrange] { + if v > lc.maxY { + lc.maxY = v + } + if v < lc.minY { + lc.minY = v + } + } + + span := lc.maxY - lc.minY + + if lc.minY < lc.bottomValue { + lc.bottomValue = lc.minY - 0.2*span + } + + if lc.maxY > lc.topValue { + lc.topValue = lc.maxY + 0.2*span + } + + lc.axisYHeight = lc.innerArea.Dy() - 2 + lc.calcLabelY() + + lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace + lc.calcLabelX() + + lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace + lc.drawingY = lc.innerArea.Min.Y +} + +func (lc *LineChart) plotAxes() Buffer { + buf := NewBuffer() + + origY := lc.innerArea.Min.Y + lc.innerArea.Dy() - 2 + origX := lc.innerArea.Min.X + lc.labelYSpace + + buf.Set(origX, origY, Cell{Ch: ORIGIN, Fg: lc.AxesColor, Bg: lc.Bg}) + + for x := origX + 1; x < origX+lc.axisXWidth; x++ { + buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg}) + } + + for dy := 1; dy <= lc.axisYHeight; dy++ { + buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg}) + } + + // x label + oft := 0 + for _, rs := range lc.labelX { + if oft+len(rs) > lc.axisXWidth { + break + } + for j, r := range rs { + c := Cell{ + Ch: r, + Fg: lc.AxesColor, + Bg: lc.Bg, + } + x := origX + oft + j + y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1 + buf.Set(x, y, c) + } + oft += len(rs) + lc.axisXLabelGap + } + + // y labels + for i, rs := range lc.labelY { + for j, r := range rs { + buf.Set( + lc.innerArea.Min.X+j, + origY-i*(lc.axisYLabelGap+1), + Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg}) + } + } + + return buf +} + +// Buffer implements Bufferer interface. +func (lc *LineChart) Buffer() Buffer { + buf := lc.Block.Buffer() + + if lc.Data == nil || len(lc.Data) == 0 { + return buf + } + lc.calcLayout() + buf.Merge(lc.plotAxes()) + + if lc.Mode == "dot" { + buf.Merge(lc.renderDot()) + } else { + buf.Merge(lc.renderBraille()) + } + + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_others.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_others.go new file mode 100644 index 0000000..14897ea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_others.go @@ -0,0 +1,11 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +// +build !windows + +package termui + +const VDASH = '┊' +const HDASH = '┈' +const ORIGIN = '└' diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_windows.go new file mode 100644 index 0000000..994d3e5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/linechart_windows.go @@ -0,0 +1,11 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +// +build windows + +package termui + +const VDASH = '|' +const HDASH = '-' +const ORIGIN = '+' diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/list.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/list.go new file mode 100644 index 0000000..ea6635e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/list.go @@ -0,0 +1,89 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "strings" + +// List displays []string as its items, +// it has a Overflow option (default is "hidden"), when set to "hidden", +// the item exceeding List's width is truncated, but when set to "wrap", +// the overflowed text breaks into next line. +/* + strs := []string{ + "[0] github.com/gizak/termui", + "[1] editbox.go", + "[2] iterrupt.go", + "[3] keyboard.go", + "[4] output.go", + "[5] random_out.go", + "[6] dashboard.go", + "[7] nsf/termbox-go"} + + ls := termui.NewList() + ls.Items = strs + ls.ItemFgColor = termui.ColorYellow + ls.BorderLabel = "List" + ls.Height = 7 + ls.Width = 25 + ls.Y = 0 +*/ +type List struct { + Block + Items []string + Overflow string + ItemFgColor Attribute + ItemBgColor Attribute +} + +// NewList returns a new *List with current theme. +func NewList() *List { + l := &List{Block: *NewBlock()} + l.Overflow = "hidden" + l.ItemFgColor = ThemeAttr("list.item.fg") + l.ItemBgColor = ThemeAttr("list.item.bg") + return l +} + +// Buffer implements Bufferer interface. +func (l *List) Buffer() Buffer { + buf := l.Block.Buffer() + + switch l.Overflow { + case "wrap": + cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor) + i, j, k := 0, 0, 0 + for i < l.innerArea.Dy() && k < len(cs) { + w := cs[k].Width() + if cs[k].Ch == '\n' || j+w > l.innerArea.Dx() { + i++ + j = 0 + if cs[k].Ch == '\n' { + k++ + } + continue + } + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k]) + + k++ + j++ + } + + case "hidden": + trimItems := l.Items + if len(trimItems) > l.innerArea.Dy() { + trimItems = trimItems[:l.innerArea.Dy()] + } + for i, v := range trimItems { + cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx()) + j := 0 + for _, vv := range cs { + w := vv.Width() + buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv) + j += w + } + } + } + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mbarchart.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mbarchart.go new file mode 100644 index 0000000..0f91e97 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mbarchart.go @@ -0,0 +1,242 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "fmt" +) + +// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go +// Multi-Colored-BarChart creates multiple bars in a widget: +/* + bc := termui.NewMBarChart() + data := make([][]int, 2) + data[0] := []int{3, 2, 5, 7, 9, 4} + data[1] := []int{7, 8, 5, 3, 1, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.BorderLabel = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow +*/ +type MBarChart struct { + Block + BarColor [NumberofColors]Attribute + TextColor Attribute + NumColor [NumberofColors]Attribute + Data [NumberofColors][]int + DataLabels []string + BarWidth int + BarGap int + labels [][]rune + dataNum [NumberofColors][][]rune + numBar int + scale float64 + max int + minDataLen int + numStack int + ShowScale bool + maxScale []rune +} + +// NewBarChart returns a new *BarChart with current theme. +func NewMBarChart() *MBarChart { + bc := &MBarChart{Block: *NewBlock()} + bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg") + bc.NumColor[0] = ThemeAttr("mbarchart.num.fg") + bc.TextColor = ThemeAttr("mbarchart.text.fg") + bc.BarGap = 1 + bc.BarWidth = 3 + return bc +} + +func (bc *MBarChart) layout() { + bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth) + bc.labels = make([][]rune, bc.numBar) + DataLen := 0 + LabelLen := len(bc.DataLabels) + bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length + + // We need to know how many stack/data array data[0] , data[1] are there + for i := 0; i < len(bc.Data); i++ { + if bc.Data[i] == nil { + break + } + DataLen++ + } + bc.numStack = DataLen + + //We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs + + for i := 0; i < DataLen; i++ { + if bc.minDataLen > len(bc.Data[i]) { + bc.minDataLen = len(bc.Data[i]) + } + } + + if LabelLen > bc.minDataLen { + LabelLen = bc.minDataLen + } + + for i := 0; i < LabelLen && i < bc.numBar; i++ { + bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) + } + + for i := 0; i < bc.numStack; i++ { + bc.dataNum[i] = make([][]rune, len(bc.Data[i])) + //For each stack of bar calcualte the rune + for j := 0; j < LabelLen && i < bc.numBar; j++ { + n := bc.Data[i][j] + s := fmt.Sprint(n) + bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth) + } + //If color is not defined by default then populate a color that is different from the prevous bar + if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault { + if i == 0 { + bc.BarColor[i] = ColorBlack + } else { + bc.BarColor[i] = bc.BarColor[i-1] + 1 + if bc.BarColor[i] > NumberofColors { + bc.BarColor[i] = ColorBlack + } + } + bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility + } + } + + //If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n])) + + if bc.max == 0 { + bc.max = -1 + } + for i := 0; i < bc.minDataLen && i < LabelLen; i++ { + var dsum int + for j := 0; j < bc.numStack; j++ { + dsum += bc.Data[j][i] + } + if dsum > bc.max { + bc.max = dsum + } + } + + //Finally Calculate max sale + if bc.ShowScale { + s := fmt.Sprintf("%d", bc.max) + bc.maxScale = trimStr2Runes(s, len(s)) + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2) + } else { + bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1) + } + +} + +func (bc *MBarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *MBarChart) Buffer() Buffer { + buf := bc.Block.Buffer() + bc.layout() + var oftX int + + for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ { + ph := 0 //Previous Height to stack up + oftX = i * (bc.BarWidth + bc.BarGap) + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + // plot bars + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + c := Cell{ + Ch: ' ', + Bg: bc.BarColor[i1], + } + if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent! + c.Bg |= AttrReverse + } + x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph + buf.Set(x, y, c) + + } + } + ph += h + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + c := Cell{ + Ch: bc.labels[i][j], + Bg: bc.Bg, + Fg: bc.TextColor, + } + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1 + x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k + buf.Set(x, y, c) + k += w + } + // plot num + ph = 0 //re-initialize previous height + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ { + c := Cell{ + Ch: bc.dataNum[i1][i][j], + Fg: bc.NumColor[i1], + Bg: bc.BarColor[i1], + } + if bc.BarColor[i1] == ColorDefault { // the same as above + c.Bg |= AttrReverse + } + if h == 0 { + c.Bg = bc.Bg + } + x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph + buf.Set(x, y, c) + } + ph += h + } + } + + if bc.ShowScale { + //Currently bar graph only supprts data range from 0 to MAX + //Plot 0 + c := Cell{ + Ch: '0', + Bg: bc.Bg, + Fg: bc.TextColor, + } + + y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 + x := bc.X + buf.Set(x, y, c) + + //Plot the maximum sacle value + for i := 0; i < len(bc.maxScale); i++ { + c := Cell{ + Ch: bc.maxScale[i], + Bg: bc.Bg, + Fg: bc.TextColor, + } + + y := bc.innerArea.Min.Y + x := bc.X + i + + buf.Set(x, y, c) + } + + } + + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mkdocs.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mkdocs.yml new file mode 100644 index 0000000..2ab45f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/mkdocs.yml @@ -0,0 +1,28 @@ +pages: +- Home: 'index.md' +- Quickstart: 'quickstart.md' +- Recipes: 'recipes.md' +- References: + - Layouts: 'layouts.md' + - Components: 'components.md' + - Events: 'events.md' + - Themes: 'themes.md' +- Versions: 'versions.md' +- About: 'about.md' + +site_name: termui +repo_url: https://github.com/gizak/termui/ +site_description: 'termui user guide' +site_author: gizak + +docs_dir: '_docs' + +theme: readthedocs + +markdown_extensions: + - smarty + - admonition + - toc + +extra: + version: 1.0 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/par.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/par.go new file mode 100644 index 0000000..29b6d46 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/par.go @@ -0,0 +1,73 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +// Par displays a paragraph. +/* + par := termui.NewPar("Simple Text") + par.Height = 3 + par.Width = 17 + par.BorderLabel = "Label" +*/ +type Par struct { + Block + Text string + TextFgColor Attribute + TextBgColor Attribute + WrapLength int // words wrap limit. Note it may not work properly with multi-width char +} + +// NewPar returns a new *Par with given text as its content. +func NewPar(s string) *Par { + return &Par{ + Block: *NewBlock(), + Text: s, + TextFgColor: ThemeAttr("par.text.fg"), + TextBgColor: ThemeAttr("par.text.bg"), + WrapLength: 0, + } +} + +// Buffer implements Bufferer interface. +func (p *Par) Buffer() Buffer { + buf := p.Block.Buffer() + + fg, bg := p.TextFgColor, p.TextBgColor + cs := DefaultTxBuilder.Build(p.Text, fg, bg) + + // wrap if WrapLength set + if p.WrapLength < 0 { + cs = wrapTx(cs, p.Width-2) + } else if p.WrapLength > 0 { + cs = wrapTx(cs, p.WrapLength) + } + + y, x, n := 0, 0, 0 + for y < p.innerArea.Dy() && n < len(cs) { + w := cs[n].Width() + if cs[n].Ch == '\n' || x+w > p.innerArea.Dx() { + y++ + x = 0 // set x = 0 + if cs[n].Ch == '\n' { + n++ + } + + if y >= p.innerArea.Dy() { + buf.Set(p.innerArea.Min.X+p.innerArea.Dx()-1, + p.innerArea.Min.Y+p.innerArea.Dy()-1, + Cell{Ch: '…', Fg: p.TextFgColor, Bg: p.TextBgColor}) + break + } + continue + } + + buf.Set(p.innerArea.Min.X+x, p.innerArea.Min.Y+y, cs[n]) + + n++ + x += w + } + + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/pos.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/pos.go new file mode 100644 index 0000000..c7d647f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/pos.go @@ -0,0 +1,78 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "image" + +// Align is the position of the gauge's label. +type Align uint + +// All supported positions. +const ( + AlignNone Align = 0 + AlignLeft Align = 1 << iota + AlignRight + AlignBottom + AlignTop + AlignCenterVertical + AlignCenterHorizontal + AlignCenter = AlignCenterVertical | AlignCenterHorizontal +) + +func AlignArea(parent, child image.Rectangle, a Align) image.Rectangle { + w, h := child.Dx(), child.Dy() + + // parent center + pcx, pcy := parent.Min.X+parent.Dx()/2, parent.Min.Y+parent.Dy()/2 + // child center + ccx, ccy := child.Min.X+child.Dx()/2, child.Min.Y+child.Dy()/2 + + if a&AlignLeft == AlignLeft { + child.Min.X = parent.Min.X + child.Max.X = child.Min.X + w + } + + if a&AlignRight == AlignRight { + child.Max.X = parent.Max.X + child.Min.X = child.Max.X - w + } + + if a&AlignBottom == AlignBottom { + child.Max.Y = parent.Max.Y + child.Min.Y = child.Max.Y - h + } + + if a&AlignTop == AlignRight { + child.Min.Y = parent.Min.Y + child.Max.Y = child.Min.Y + h + } + + if a&AlignCenterHorizontal == AlignCenterHorizontal { + child.Min.X += pcx - ccx + child.Max.X = child.Min.X + w + } + + if a&AlignCenterVertical == AlignCenterVertical { + child.Min.Y += pcy - ccy + child.Max.Y = child.Min.Y + h + } + + return child +} + +func MoveArea(a image.Rectangle, dx, dy int) image.Rectangle { + a.Min.X += dx + a.Max.X += dx + a.Min.Y += dy + a.Max.Y += dy + return a +} + +var termWidth int +var termHeight int + +func TermRect() image.Rectangle { + return image.Rect(0, 0, termWidth, termHeight) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/render.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/render.go new file mode 100644 index 0000000..b9d37d9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/render.go @@ -0,0 +1,164 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "image" + "io" + "sync" + "time" + + "fmt" + + "os" + + "runtime/debug" + + "bytes" + + "github.com/maruel/panicparse/stack" + tm "github.com/nsf/termbox-go" +) + +// Bufferer should be implemented by all renderable components. +type Bufferer interface { + Buffer() Buffer +} + +// Init initializes termui library. This function should be called before any others. +// After initialization, the library must be finalized by 'Close' function. +func Init() error { + if err := tm.Init(); err != nil { + return err + } + + sysEvtChs = make([]chan Event, 0) + go hookTermboxEvt() + + renderJobs = make(chan []Bufferer) + //renderLock = new(sync.RWMutex) + + Body = NewGrid() + Body.X = 0 + Body.Y = 0 + Body.BgColor = ThemeAttr("bg") + Body.Width = TermWidth() + + DefaultEvtStream.Init() + DefaultEvtStream.Merge("termbox", NewSysEvtCh()) + DefaultEvtStream.Merge("timer", NewTimerCh(time.Second)) + DefaultEvtStream.Merge("custom", usrEvtCh) + + DefaultEvtStream.Handle("/", DefaultHandler) + DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) { + w := e.Data.(EvtWnd) + Body.Width = w.Width + }) + + DefaultWgtMgr = NewWgtMgr() + DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook()) + + go func() { + for bs := range renderJobs { + render(bs...) + } + }() + + return nil +} + +// Close finalizes termui library, +// should be called after successful initialization when termui's functionality isn't required anymore. +func Close() { + tm.Close() +} + +var renderLock sync.Mutex + +func termSync() { + renderLock.Lock() + tm.Sync() + termWidth, termHeight = tm.Size() + renderLock.Unlock() +} + +// TermWidth returns the current terminal's width. +func TermWidth() int { + termSync() + return termWidth +} + +// TermHeight returns the current terminal's height. +func TermHeight() int { + termSync() + return termHeight +} + +// Render renders all Bufferer in the given order from left to right, +// right could overlap on left ones. +func render(bs ...Bufferer) { + defer func() { + if e := recover(); e != nil { + Close() + fmt.Fprintf(os.Stderr, "Captured a panic(value=%v) when rendering Bufferer. Exit termui and clean terminal...\nPrint stack trace:\n\n", e) + //debug.PrintStack() + gs, err := stack.ParseDump(bytes.NewReader(debug.Stack()), os.Stderr) + if err != nil { + debug.PrintStack() + os.Exit(1) + } + p := &stack.Palette{} + buckets := stack.SortBuckets(stack.Bucketize(gs, stack.AnyValue)) + srcLen, pkgLen := stack.CalcLengths(buckets, false) + for _, bucket := range buckets { + io.WriteString(os.Stdout, p.BucketHeader(&bucket, false, len(buckets) > 1)) + io.WriteString(os.Stdout, p.StackLines(&bucket.Signature, srcLen, pkgLen, false)) + } + os.Exit(1) + } + }() + for _, b := range bs { + + buf := b.Buffer() + // set cels in buf + for p, c := range buf.CellMap { + if p.In(buf.Area) { + + tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg)) + + } + } + + } + + renderLock.Lock() + // render + tm.Flush() + renderLock.Unlock() +} + +func Clear() { + tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg"))) +} + +func clearArea(r image.Rectangle, bg Attribute) { + for i := r.Min.X; i < r.Max.X; i++ { + for j := r.Min.Y; j < r.Max.Y; j++ { + tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg)) + } + } +} + +func ClearArea(r image.Rectangle, bg Attribute) { + clearArea(r, bg) + tm.Flush() +} + +var renderJobs chan []Bufferer + +func Render(bs ...Bufferer) { + //go func() { renderJobs <- bs }() + renderJobs <- bs +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/sparkline.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/sparkline.go new file mode 100644 index 0000000..d906e49 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/sparkline.go @@ -0,0 +1,167 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers. +/* + data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1} + spl := termui.NewSparkline() + spl.Data = data + spl.Title = "Sparkline 0" + spl.LineColor = termui.ColorGreen +*/ +type Sparkline struct { + Data []int + Height int + Title string + TitleColor Attribute + LineColor Attribute + displayHeight int + scale float32 + max int +} + +// Sparklines is a renderable widget which groups together the given sparklines. +/* + spls := termui.NewSparklines(spl0,spl1,spl2) //... + spls.Height = 2 + spls.Width = 20 +*/ +type Sparklines struct { + Block + Lines []Sparkline + displayLines int + displayWidth int +} + +var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} + +// Add appends a given Sparkline to s *Sparklines. +func (s *Sparklines) Add(sl Sparkline) { + s.Lines = append(s.Lines, sl) +} + +// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines. +func NewSparkline() Sparkline { + return Sparkline{ + Height: 1, + TitleColor: ThemeAttr("sparkline.title.fg"), + LineColor: ThemeAttr("sparkline.line.fg")} +} + +// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later. +func NewSparklines(ss ...Sparkline) *Sparklines { + s := &Sparklines{Block: *NewBlock(), Lines: ss} + return s +} + +func (sl *Sparklines) update() { + for i, v := range sl.Lines { + if v.Title == "" { + sl.Lines[i].displayHeight = v.Height + } else { + sl.Lines[i].displayHeight = v.Height + 1 + } + } + sl.displayWidth = sl.innerArea.Dx() + + // get how many lines gotta display + h := 0 + sl.displayLines = 0 + for _, v := range sl.Lines { + if h+v.displayHeight <= sl.innerArea.Dy() { + sl.displayLines++ + } else { + break + } + h += v.displayHeight + } + + for i := 0; i < sl.displayLines; i++ { + data := sl.Lines[i].Data + + max := 0 + for _, v := range data { + if max < v { + max = v + } + } + sl.Lines[i].max = max + if max != 0 { + sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max) + } else { // when all negative + sl.Lines[i].scale = 0 + } + } +} + +// Buffer implements Bufferer interface. +func (sl *Sparklines) Buffer() Buffer { + buf := sl.Block.Buffer() + sl.update() + + oftY := 0 + for i := 0; i < sl.displayLines; i++ { + l := sl.Lines[i] + data := l.Data + + if len(data) > sl.innerArea.Dx() { + data = data[len(data)-sl.innerArea.Dx():] + } + + if l.Title != "" { + rs := trimStr2Runes(l.Title, sl.innerArea.Dx()) + oftX := 0 + for _, v := range rs { + w := charWidth(v) + c := Cell{ + Ch: v, + Fg: l.TitleColor, + Bg: sl.Bg, + } + x := sl.innerArea.Min.X + oftX + y := sl.innerArea.Min.Y + oftY + buf.Set(x, y, c) + oftX += w + } + } + + for j, v := range data { + // display height of the data point, zero when data is negative + h := int(float32(v)*l.scale + 0.5) + if v < 0 { + h = 0 + } + + barCnt := h / 8 + barMod := h % 8 + for jj := 0; jj < barCnt; jj++ { + c := Cell{ + Ch: ' ', // => sparks[7] + Bg: l.LineColor, + } + x := sl.innerArea.Min.X + j + y := sl.innerArea.Min.Y + oftY + l.Height - jj + + //p.Bg = sl.BgColor + buf.Set(x, y, c) + } + if barMod != 0 { + c := Cell{ + Ch: sparks[barMod-1], + Fg: l.LineColor, + Bg: sl.Bg, + } + x := sl.innerArea.Min.X + j + y := sl.innerArea.Min.Y + oftY + l.Height - barCnt + buf.Set(x, y, c) + } + } + + oftY += l.displayHeight + } + + return buf +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/table.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/table.go new file mode 100644 index 0000000..e3d1bbf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/table.go @@ -0,0 +1,185 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "strings" + +/* Table is like: + +┌Awesome Table ────────────────────────────────────────────────┐ +│ Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 | +│──────────────────────────────────────────────────────────────│ +│ Some Item #1 | AAA | 123 | CCCCC | EEEEE | GGGGG | IIIII | +│──────────────────────────────────────────────────────────────│ +│ Some Item #2 | BBB | 456 | DDDDD | FFFFF | HHHHH | JJJJJ | +└──────────────────────────────────────────────────────────────┘ + +Datapoints are a two dimensional array of strings: [][]string + +Example: + data := [][]string{ + {"Col0", "Col1", "Col3", "Col4", "Col5", "Col6"}, + {"Some Item #1", "AAA", "123", "CCCCC", "EEEEE", "GGGGG", "IIIII"}, + {"Some Item #2", "BBB", "456", "DDDDD", "FFFFF", "HHHHH", "JJJJJ"}, + } + + table := termui.NewTable() + table.Rows = data // type [][]string + table.FgColor = termui.ColorWhite + table.BgColor = termui.ColorDefault + table.Height = 7 + table.Width = 62 + table.Y = 0 + table.X = 0 + table.Border = true +*/ + +// Table tracks all the attributes of a Table instance +type Table struct { + Block + Rows [][]string + CellWidth []int + FgColor Attribute + BgColor Attribute + FgColors []Attribute + BgColors []Attribute + Separator bool + TextAlign Align +} + +// NewTable returns a new Table instance +func NewTable() *Table { + table := &Table{Block: *NewBlock()} + table.FgColor = ColorWhite + table.BgColor = ColorDefault + table.Separator = true + return table +} + +// CellsWidth calculates the width of a cell array and returns an int +func cellsWidth(cells []Cell) int { + width := 0 + for _, c := range cells { + width += c.Width() + } + return width +} + +// Analysis generates and returns an array of []Cell that represent all columns in the Table +func (table *Table) Analysis() [][]Cell { + var rowCells [][]Cell + length := len(table.Rows) + if length < 1 { + return rowCells + } + + if len(table.FgColors) == 0 { + table.FgColors = make([]Attribute, len(table.Rows)) + } + if len(table.BgColors) == 0 { + table.BgColors = make([]Attribute, len(table.Rows)) + } + + cellWidths := make([]int, len(table.Rows[0])) + + for y, row := range table.Rows { + if table.FgColors[y] == 0 { + table.FgColors[y] = table.FgColor + } + if table.BgColors[y] == 0 { + table.BgColors[y] = table.BgColor + } + for x, str := range row { + cells := DefaultTxBuilder.Build(str, table.FgColors[y], table.BgColors[y]) + cw := cellsWidth(cells) + if cellWidths[x] < cw { + cellWidths[x] = cw + } + rowCells = append(rowCells, cells) + } + } + table.CellWidth = cellWidths + return rowCells +} + +// SetSize calculates the table size and sets the internal value +func (table *Table) SetSize() { + length := len(table.Rows) + if table.Separator { + table.Height = length*2 + 1 + } else { + table.Height = length + 2 + } + table.Width = 2 + if length != 0 { + for _, cellWidth := range table.CellWidth { + table.Width += cellWidth + 3 + } + } +} + +// CalculatePosition ... +func (table *Table) CalculatePosition(x int, y int, coordinateX *int, coordinateY *int, cellStart *int) { + if table.Separator { + *coordinateY = table.innerArea.Min.Y + y*2 + } else { + *coordinateY = table.innerArea.Min.Y + y + } + if x == 0 { + *cellStart = table.innerArea.Min.X + } else { + *cellStart += table.CellWidth[x-1] + 3 + } + + switch table.TextAlign { + case AlignRight: + *coordinateX = *cellStart + (table.CellWidth[x] - len(table.Rows[y][x])) + 2 + case AlignCenter: + *coordinateX = *cellStart + (table.CellWidth[x]-len(table.Rows[y][x]))/2 + 2 + default: + *coordinateX = *cellStart + 2 + } +} + +// Buffer ... +func (table *Table) Buffer() Buffer { + buffer := table.Block.Buffer() + rowCells := table.Analysis() + pointerX := table.innerArea.Min.X + 2 + pointerY := table.innerArea.Min.Y + borderPointerX := table.innerArea.Min.X + for y, row := range table.Rows { + for x := range row { + table.CalculatePosition(x, y, &pointerX, &pointerY, &borderPointerX) + background := DefaultTxBuilder.Build(strings.Repeat(" ", table.CellWidth[x]+3), table.BgColors[y], table.BgColors[y]) + cells := rowCells[y*len(row)+x] + for i, back := range background { + buffer.Set(borderPointerX+i, pointerY, back) + } + + coordinateX := pointerX + for _, printer := range cells { + buffer.Set(coordinateX, pointerY, printer) + coordinateX += printer.Width() + } + + if x != 0 { + dividors := DefaultTxBuilder.Build("|", table.FgColors[y], table.BgColors[y]) + for _, dividor := range dividors { + buffer.Set(borderPointerX, pointerY, dividor) + } + } + } + + if table.Separator { + border := DefaultTxBuilder.Build(strings.Repeat("─", table.Width-2), table.FgColor, table.BgColor) + for i, cell := range border { + buffer.Set(i+1, pointerY+1, cell) + } + } + } + + return buffer +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/textbuilder.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/textbuilder.go new file mode 100644 index 0000000..12e2055 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/textbuilder.go @@ -0,0 +1,278 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "regexp" + "strings" + + "github.com/mitchellh/go-wordwrap" +) + +// TextBuilder is a minimal interface to produce text []Cell using specific syntax (markdown). +type TextBuilder interface { + Build(s string, fg, bg Attribute) []Cell +} + +// DefaultTxBuilder is set to be MarkdownTxBuilder. +var DefaultTxBuilder = NewMarkdownTxBuilder() + +// MarkdownTxBuilder implements TextBuilder interface, using markdown syntax. +type MarkdownTxBuilder struct { + baseFg Attribute + baseBg Attribute + plainTx []rune + markers []marker +} + +type marker struct { + st int + ed int + fg Attribute + bg Attribute +} + +var colorMap = map[string]Attribute{ + "red": ColorRed, + "blue": ColorBlue, + "black": ColorBlack, + "cyan": ColorCyan, + "yellow": ColorYellow, + "white": ColorWhite, + "default": ColorDefault, + "green": ColorGreen, + "magenta": ColorMagenta, +} + +var attrMap = map[string]Attribute{ + "bold": AttrBold, + "underline": AttrUnderline, + "reverse": AttrReverse, +} + +func rmSpc(s string) string { + reg := regexp.MustCompile(`\s+`) + return reg.ReplaceAllString(s, "") +} + +// readAttr translates strings like `fg-red,fg-bold,bg-white` to fg and bg Attribute +func (mtb MarkdownTxBuilder) readAttr(s string) (Attribute, Attribute) { + fg := mtb.baseFg + bg := mtb.baseBg + + updateAttr := func(a Attribute, attrs []string) Attribute { + for _, s := range attrs { + // replace the color + if c, ok := colorMap[s]; ok { + a &= 0xFF00 // erase clr 0 ~ 8 bits + a |= c // set clr + } + // add attrs + if c, ok := attrMap[s]; ok { + a |= c + } + } + return a + } + + ss := strings.Split(s, ",") + fgs := []string{} + bgs := []string{} + for _, v := range ss { + subs := strings.Split(v, "-") + if len(subs) > 1 { + if subs[0] == "fg" { + fgs = append(fgs, subs[1]) + } + if subs[0] == "bg" { + bgs = append(bgs, subs[1]) + } + } + } + + fg = updateAttr(fg, fgs) + bg = updateAttr(bg, bgs) + return fg, bg +} + +func (mtb *MarkdownTxBuilder) reset() { + mtb.plainTx = []rune{} + mtb.markers = []marker{} +} + +// parse streams and parses text into normalized text and render sequence. +func (mtb *MarkdownTxBuilder) parse(str string) { + rs := str2runes(str) + normTx := []rune{} + square := []rune{} + brackt := []rune{} + accSquare := false + accBrackt := false + cntSquare := 0 + + reset := func() { + square = []rune{} + brackt = []rune{} + accSquare = false + accBrackt = false + cntSquare = 0 + } + // pipe stacks into normTx and clear + rollback := func() { + normTx = append(normTx, square...) + normTx = append(normTx, brackt...) + reset() + } + // chop first and last + chop := func(s []rune) []rune { + return s[1 : len(s)-1] + } + + for i, r := range rs { + switch { + // stacking brackt + case accBrackt: + brackt = append(brackt, r) + if ')' == r { + fg, bg := mtb.readAttr(string(chop(brackt))) + st := len(normTx) + ed := len(normTx) + len(square) - 2 + mtb.markers = append(mtb.markers, marker{st, ed, fg, bg}) + normTx = append(normTx, chop(square)...) + reset() + } else if i+1 == len(rs) { + rollback() + } + // stacking square + case accSquare: + switch { + // squares closed and followed by a '(' + case cntSquare == 0 && '(' == r: + accBrackt = true + brackt = append(brackt, '(') + // squares closed but not followed by a '(' + case cntSquare == 0: + rollback() + if '[' == r { + accSquare = true + cntSquare = 1 + brackt = append(brackt, '[') + } else { + normTx = append(normTx, r) + } + // hit the end + case i+1 == len(rs): + square = append(square, r) + rollback() + case '[' == r: + cntSquare++ + square = append(square, '[') + case ']' == r: + cntSquare-- + square = append(square, ']') + // normal char + default: + square = append(square, r) + } + // stacking normTx + default: + if '[' == r { + accSquare = true + cntSquare = 1 + square = append(square, '[') + } else { + normTx = append(normTx, r) + } + } + } + + mtb.plainTx = normTx +} + +func wrapTx(cs []Cell, wl int) []Cell { + tmpCell := make([]Cell, len(cs)) + copy(tmpCell, cs) + + // get the plaintext + plain := CellsToStr(cs) + + // wrap + plainWrapped := wordwrap.WrapString(plain, uint(wl)) + + // find differences and insert + finalCell := tmpCell // finalcell will get the inserts and is what is returned + + plainRune := []rune(plain) + plainWrappedRune := []rune(plainWrapped) + trigger := "go" + plainRuneNew := plainRune + + for trigger != "stop" { + plainRune = plainRuneNew + for i := range plainRune { + if plainRune[i] == plainWrappedRune[i] { + trigger = "stop" + } else if plainRune[i] != plainWrappedRune[i] && plainWrappedRune[i] == 10 { + trigger = "go" + cell := Cell{10, 0, 0} + j := i - 0 + + // insert a cell into the []Cell in correct position + tmpCell[i] = cell + + // insert the newline into plain so we avoid indexing errors + plainRuneNew = append(plainRune, 10) + copy(plainRuneNew[j+1:], plainRuneNew[j:]) + plainRuneNew[j] = plainWrappedRune[j] + + // restart the inner for loop until plain and plain wrapped are + // the same; yeah, it's inefficient, but the text amounts + // should be small + break + + } else if plainRune[i] != plainWrappedRune[i] && + plainWrappedRune[i-1] == 10 && // if the prior rune is a newline + plainRune[i] == 32 { // and this rune is a space + trigger = "go" + // need to delete plainRune[i] because it gets rid of an extra + // space + plainRuneNew = append(plainRune[:i], plainRune[i+1:]...) + break + + } else { + trigger = "stop" // stops the outer for loop + } + } + } + + finalCell = tmpCell + + return finalCell +} + +// Build implements TextBuilder interface. +func (mtb MarkdownTxBuilder) Build(s string, fg, bg Attribute) []Cell { + mtb.baseFg = fg + mtb.baseBg = bg + mtb.reset() + mtb.parse(s) + cs := make([]Cell, len(mtb.plainTx)) + for i := range cs { + cs[i] = Cell{Ch: mtb.plainTx[i], Fg: fg, Bg: bg} + } + for _, mrk := range mtb.markers { + for i := mrk.st; i < mrk.ed; i++ { + cs[i].Fg = mrk.fg + cs[i].Bg = mrk.bg + } + } + + return cs +} + +// NewMarkdownTxBuilder returns a TextBuilder employing markdown syntax. +func NewMarkdownTxBuilder() TextBuilder { + return MarkdownTxBuilder{} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/theme.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/theme.go new file mode 100644 index 0000000..21fb3bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/theme.go @@ -0,0 +1,140 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import "strings" + +/* +// A ColorScheme represents the current look-and-feel of the dashboard. +type ColorScheme struct { + BodyBg Attribute + BlockBg Attribute + HasBorder bool + BorderFg Attribute + BorderBg Attribute + BorderLabelTextFg Attribute + BorderLabelTextBg Attribute + ParTextFg Attribute + ParTextBg Attribute + SparklineLine Attribute + SparklineTitle Attribute + GaugeBar Attribute + GaugePercent Attribute + LineChartLine Attribute + LineChartAxes Attribute + ListItemFg Attribute + ListItemBg Attribute + BarChartBar Attribute + BarChartText Attribute + BarChartNum Attribute + MBarChartBar Attribute + MBarChartText Attribute + MBarChartNum Attribute + TabActiveBg Attribute +} + +// default color scheme depends on the user's terminal setting. +var themeDefault = ColorScheme{HasBorder: true} + +var themeHelloWorld = ColorScheme{ + BodyBg: ColorBlack, + BlockBg: ColorBlack, + HasBorder: true, + BorderFg: ColorWhite, + BorderBg: ColorBlack, + BorderLabelTextBg: ColorBlack, + BorderLabelTextFg: ColorGreen, + ParTextBg: ColorBlack, + ParTextFg: ColorWhite, + SparklineLine: ColorMagenta, + SparklineTitle: ColorWhite, + GaugeBar: ColorRed, + GaugePercent: ColorWhite, + LineChartLine: ColorYellow | AttrBold, + LineChartAxes: ColorWhite, + ListItemBg: ColorBlack, + ListItemFg: ColorYellow, + BarChartBar: ColorRed, + BarChartNum: ColorWhite, + BarChartText: ColorCyan, + MBarChartBar: ColorRed, + MBarChartNum: ColorWhite, + MBarChartText: ColorCyan, + TabActiveBg: ColorMagenta, +} + +var theme = themeDefault // global dep + +// Theme returns the currently used theme. +func Theme() ColorScheme { + return theme +} + +// SetTheme sets a new, custom theme. +func SetTheme(newTheme ColorScheme) { + theme = newTheme +} + +// UseTheme sets a predefined scheme. Currently available: "hello-world" and +// "black-and-white". +func UseTheme(th string) { + switch th { + case "helloworld": + theme = themeHelloWorld + default: + theme = themeDefault + } +} +*/ + +var ColorMap = map[string]Attribute{ + "fg": ColorWhite, + "bg": ColorDefault, + "border.fg": ColorWhite, + "label.fg": ColorGreen, + "par.fg": ColorYellow, + "par.label.bg": ColorWhite, +} + +func ThemeAttr(name string) Attribute { + return lookUpAttr(ColorMap, name) +} + +func lookUpAttr(clrmap map[string]Attribute, name string) Attribute { + + a, ok := clrmap[name] + if ok { + return a + } + + ns := strings.Split(name, ".") + for i := range ns { + nn := strings.Join(ns[i:len(ns)], ".") + a, ok = ColorMap[nn] + if ok { + break + } + } + + return a +} + +// 0<=r,g,b <= 5 +func ColorRGB(r, g, b int) Attribute { + within := func(n int) int { + if n < 0 { + return 0 + } + + if n > 5 { + return 5 + } + + return n + } + + r, b, g = within(r), within(b), within(g) + return Attribute(0x0f + 36*r + 6*g + b) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/widget.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/widget.go new file mode 100644 index 0000000..80276bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/gizak/termui/widget.go @@ -0,0 +1,94 @@ +// Copyright 2017 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +package termui + +import ( + "fmt" + "sync" +) + +// event mixins +type WgtMgr map[string]WgtInfo + +type WgtInfo struct { + Handlers map[string]func(Event) + WgtRef Widget + Id string +} + +type Widget interface { + Id() string +} + +func NewWgtInfo(wgt Widget) WgtInfo { + return WgtInfo{ + Handlers: make(map[string]func(Event)), + WgtRef: wgt, + Id: wgt.Id(), + } +} + +func NewWgtMgr() WgtMgr { + wm := WgtMgr(make(map[string]WgtInfo)) + return wm + +} + +func (wm WgtMgr) AddWgt(wgt Widget) { + wm[wgt.Id()] = NewWgtInfo(wgt) +} + +func (wm WgtMgr) RmWgt(wgt Widget) { + wm.RmWgtById(wgt.Id()) +} + +func (wm WgtMgr) RmWgtById(id string) { + delete(wm, id) +} + +func (wm WgtMgr) AddWgtHandler(id, path string, h func(Event)) { + if w, ok := wm[id]; ok { + w.Handlers[path] = h + } +} + +func (wm WgtMgr) RmWgtHandler(id, path string) { + if w, ok := wm[id]; ok { + delete(w.Handlers, path) + } +} + +var counter struct { + sync.RWMutex + count int +} + +func GenId() string { + counter.Lock() + defer counter.Unlock() + + counter.count += 1 + return fmt.Sprintf("%d", counter.count) +} + +func (wm WgtMgr) WgtHandlersHook() func(Event) { + return func(e Event) { + for _, v := range wm { + if k := findMatch(v.Handlers, e.Path); k != "" { + v.Handlers[k](e) + } + } + } +} + +var DefaultWgtMgr WgtMgr + +func (b *Block) Handle(path string, handler func(Event)) { + if _, ok := DefaultWgtMgr[b.Id()]; !ok { + DefaultWgtMgr.AddWgt(b) + } + + DefaultWgtMgr.AddWgtHandler(b.Id(), path, handler) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/LICENSE.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/LICENSE.md new file mode 100644 index 0000000..c8ca66c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/LICENSE.md @@ -0,0 +1,13 @@ +Copyright 2014 Chris Hines + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/README.md new file mode 100644 index 0000000..f11cccc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/README.md @@ -0,0 +1,38 @@ +[![GoDoc](https://godoc.org/github.com/go-stack/stack?status.svg)](https://godoc.org/github.com/go-stack/stack) +[![Go Report Card](https://goreportcard.com/badge/go-stack/stack)](https://goreportcard.com/report/go-stack/stack) +[![TravisCI](https://travis-ci.org/go-stack/stack.svg?branch=master)](https://travis-ci.org/go-stack/stack) +[![Coverage Status](https://coveralls.io/repos/github/go-stack/stack/badge.svg?branch=master)](https://coveralls.io/github/go-stack/stack?branch=master) + +# stack + +Package stack implements utilities to capture, manipulate, and format call +stacks. It provides a simpler API than package runtime. + +The implementation takes care of the minutia and special cases of interpreting +the program counter (pc) values returned by runtime.Callers. + +## Versioning + +Package stack publishes releases via [semver](http://semver.org/) compatible Git +tags prefixed with a single 'v'. The master branch always contains the latest +release. The develop branch contains unreleased commits. + +## Formatting + +Package stack's types implement fmt.Formatter, which provides a simple and +flexible way to declaratively configure formatting when used with logging or +error tracking packages. + +```go +func DoTheThing() { + c := stack.Caller(0) + log.Print(c) // "source.go:10" + log.Printf("%+v", c) // "pkg/path/source.go:10" + log.Printf("%n", c) // "DoTheThing" + + s := stack.Trace().TrimRuntime() + log.Print(s) // "[source.go:15 caller.go:42 main.go:14]" +} +``` + +See the docs for all of the supported formatting options. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/stack.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/stack.go new file mode 100644 index 0000000..a614eee --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/go-stack/stack/stack.go @@ -0,0 +1,349 @@ +// Package stack implements utilities to capture, manipulate, and format call +// stacks. It provides a simpler API than package runtime. +// +// The implementation takes care of the minutia and special cases of +// interpreting the program counter (pc) values returned by runtime.Callers. +// +// Package stack's types implement fmt.Formatter, which provides a simple and +// flexible way to declaratively configure formatting when used with logging +// or error tracking packages. +package stack + +import ( + "bytes" + "errors" + "fmt" + "io" + "runtime" + "strconv" + "strings" +) + +// Call records a single function invocation from a goroutine stack. +type Call struct { + fn *runtime.Func + pc uintptr +} + +// Caller returns a Call from the stack of the current goroutine. The argument +// skip is the number of stack frames to ascend, with 0 identifying the +// calling function. +func Caller(skip int) Call { + var pcs [2]uintptr + n := runtime.Callers(skip+1, pcs[:]) + + var c Call + + if n < 2 { + return c + } + + c.pc = pcs[1] + if runtime.FuncForPC(pcs[0]) != sigpanic { + c.pc-- + } + c.fn = runtime.FuncForPC(c.pc) + return c +} + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c). +func (c Call) String() string { + return fmt.Sprint(c) +} + +// MarshalText implements encoding.TextMarshaler. It formats the Call the same +// as fmt.Sprintf("%v", c). +func (c Call) MarshalText() ([]byte, error) { + if c.fn == nil { + return nil, ErrNoFunc + } + buf := bytes.Buffer{} + fmt.Fprint(&buf, c) + return buf.Bytes(), nil +} + +// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely +// cause is a Call with the zero value. +var ErrNoFunc = errors.New("no call stack information") + +// Format implements fmt.Formatter with support for the following verbs. +// +// %s source file +// %d line number +// %n function name +// %v equivalent to %s:%d +// +// It accepts the '+' and '#' flags for most of the verbs as follows. +// +// %+s path of source file relative to the compile time GOPATH +// %#s full path of source file +// %+n import path qualified function name +// %+v equivalent to %+s:%d +// %#v equivalent to %#s:%d +func (c Call) Format(s fmt.State, verb rune) { + if c.fn == nil { + fmt.Fprintf(s, "%%!%c(NOFUNC)", verb) + return + } + + switch verb { + case 's', 'v': + file, line := c.fn.FileLine(c.pc) + switch { + case s.Flag('#'): + // done + case s.Flag('+'): + file = file[pkgIndex(file, c.fn.Name()):] + default: + const sep = "/" + if i := strings.LastIndex(file, sep); i != -1 { + file = file[i+len(sep):] + } + } + io.WriteString(s, file) + if verb == 'v' { + buf := [7]byte{':'} + s.Write(strconv.AppendInt(buf[:1], int64(line), 10)) + } + + case 'd': + _, line := c.fn.FileLine(c.pc) + buf := [6]byte{} + s.Write(strconv.AppendInt(buf[:0], int64(line), 10)) + + case 'n': + name := c.fn.Name() + if !s.Flag('+') { + const pathSep = "/" + if i := strings.LastIndex(name, pathSep); i != -1 { + name = name[i+len(pathSep):] + } + const pkgSep = "." + if i := strings.Index(name, pkgSep); i != -1 { + name = name[i+len(pkgSep):] + } + } + io.WriteString(s, name) + } +} + +// PC returns the program counter for this call frame; multiple frames may +// have the same PC value. +func (c Call) PC() uintptr { + return c.pc +} + +// name returns the import path qualified name of the function containing the +// call. +func (c Call) name() string { + if c.fn == nil { + return "???" + } + return c.fn.Name() +} + +func (c Call) file() string { + if c.fn == nil { + return "???" + } + file, _ := c.fn.FileLine(c.pc) + return file +} + +func (c Call) line() int { + if c.fn == nil { + return 0 + } + _, line := c.fn.FileLine(c.pc) + return line +} + +// CallStack records a sequence of function invocations from a goroutine +// stack. +type CallStack []Call + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs). +func (cs CallStack) String() string { + return fmt.Sprint(cs) +} + +var ( + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + spaceBytes = []byte(" ") +) + +// MarshalText implements encoding.TextMarshaler. It formats the CallStack the +// same as fmt.Sprintf("%v", cs). +func (cs CallStack) MarshalText() ([]byte, error) { + buf := bytes.Buffer{} + buf.Write(openBracketBytes) + for i, pc := range cs { + if pc.fn == nil { + return nil, ErrNoFunc + } + if i > 0 { + buf.Write(spaceBytes) + } + fmt.Fprint(&buf, pc) + } + buf.Write(closeBracketBytes) + return buf.Bytes(), nil +} + +// Format implements fmt.Formatter by printing the CallStack as square brackets +// ([, ]) surrounding a space separated list of Calls each formatted with the +// supplied verb and options. +func (cs CallStack) Format(s fmt.State, verb rune) { + s.Write(openBracketBytes) + for i, pc := range cs { + if i > 0 { + s.Write(spaceBytes) + } + pc.Format(s, verb) + } + s.Write(closeBracketBytes) +} + +// findSigpanic intentionally executes faulting code to generate a stack trace +// containing an entry for runtime.sigpanic. +func findSigpanic() *runtime.Func { + var fn *runtime.Func + var p *int + func() int { + defer func() { + if p := recover(); p != nil { + var pcs [512]uintptr + n := runtime.Callers(2, pcs[:]) + for _, pc := range pcs[:n] { + f := runtime.FuncForPC(pc) + if f.Name() == "runtime.sigpanic" { + fn = f + break + } + } + } + }() + // intentional nil pointer dereference to trigger sigpanic + return *p + }() + return fn +} + +var sigpanic = findSigpanic() + +// Trace returns a CallStack for the current goroutine with element 0 +// identifying the calling function. +func Trace() CallStack { + var pcs [512]uintptr + n := runtime.Callers(2, pcs[:]) + cs := make([]Call, n) + + for i, pc := range pcs[:n] { + pcFix := pc + if i > 0 && cs[i-1].fn != sigpanic { + pcFix-- + } + cs[i] = Call{ + fn: runtime.FuncForPC(pcFix), + pc: pcFix, + } + } + + return cs +} + +// TrimBelow returns a slice of the CallStack with all entries below c +// removed. +func (cs CallStack) TrimBelow(c Call) CallStack { + for len(cs) > 0 && cs[0].pc != c.pc { + cs = cs[1:] + } + return cs +} + +// TrimAbove returns a slice of the CallStack with all entries above c +// removed. +func (cs CallStack) TrimAbove(c Call) CallStack { + for len(cs) > 0 && cs[len(cs)-1].pc != c.pc { + cs = cs[:len(cs)-1] + } + return cs +} + +// pkgIndex returns the index that results in file[index:] being the path of +// file relative to the compile time GOPATH, and file[:index] being the +// $GOPATH/src/ portion of file. funcName must be the name of a function in +// file as returned by runtime.Func.Name. +func pkgIndex(file, funcName string) int { + // As of Go 1.6.2 there is no direct way to know the compile time GOPATH + // at runtime, but we can infer the number of path segments in the GOPATH. + // We note that runtime.Func.Name() returns the function name qualified by + // the import path, which does not include the GOPATH. Thus we can trim + // segments from the beginning of the file path until the number of path + // separators remaining is one more than the number of path separators in + // the function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // file[:idx] == /home/user/src/ + // file[idx:] == pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired result for file[idx:]. We count separators from the + // end of the file path until it finds two more than in the function name + // and then move one character forward to preserve the initial path + // segment without a leading separator. + const sep = "/" + i := len(file) + for n := strings.Count(funcName, sep) + 2; n > 0; n-- { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + return i + len(sep) +} + +var runtimePath string + +func init() { + var pcs [1]uintptr + runtime.Callers(0, pcs[:]) + fn := runtime.FuncForPC(pcs[0]) + file, _ := fn.FileLine(pcs[0]) + + idx := pkgIndex(file, fn.Name()) + + runtimePath = file[:idx] + if runtime.GOOS == "windows" { + runtimePath = strings.ToLower(runtimePath) + } +} + +func inGoroot(c Call) bool { + file := c.file() + if len(file) == 0 || file[0] == '?' { + return true + } + if runtime.GOOS == "windows" { + file = strings.ToLower(file) + } + return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go") +} + +// TrimRuntime returns a slice of the CallStack with the topmost entries from +// the go runtime removed. It considers any calls originating from unknown +// files, files under GOROOT, or _testmain.go as part of the runtime. +func (cs CallStack) TrimRuntime() CallStack { + for len(cs) > 0 && inGoroot(cs[len(cs)-1]) { + cs = cs[:len(cs)-1] + } + return cs +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/AUTHORS new file mode 100644 index 0000000..bcfa195 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of Snappy-Go authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Damian Gryski +Google Inc. +Jan Mercl <0xjnml@gmail.com> +Rodolfo Carvalho +Sebastien Binet diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/CONTRIBUTORS new file mode 100644 index 0000000..931ae31 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Snappy-Go repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Damian Gryski +Jan Mercl <0xjnml@gmail.com> +Kai Backman +Marc-Antoine Ruel +Nigel Tao +Rob Pike +Rodolfo Carvalho +Russ Cox +Sebastien Binet diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/LICENSE new file mode 100644 index 0000000..6050c10 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-Go Authors. 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/README b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/README new file mode 100644 index 0000000..cea1287 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/README @@ -0,0 +1,107 @@ +The Snappy compression format in the Go programming language. + +To download and install from source: +$ go get github.com/golang/snappy + +Unless otherwise noted, the Snappy-Go source files are distributed +under the BSD-style license found in the LICENSE file. + + + +Benchmarks. + +The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten +or so files, the same set used by the C++ Snappy code (github.com/google/snappy +and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @ +3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29: + +"go test -test.bench=." + +_UFlat0-8 2.19GB/s ± 0% html +_UFlat1-8 1.41GB/s ± 0% urls +_UFlat2-8 23.5GB/s ± 2% jpg +_UFlat3-8 1.91GB/s ± 0% jpg_200 +_UFlat4-8 14.0GB/s ± 1% pdf +_UFlat5-8 1.97GB/s ± 0% html4 +_UFlat6-8 814MB/s ± 0% txt1 +_UFlat7-8 785MB/s ± 0% txt2 +_UFlat8-8 857MB/s ± 0% txt3 +_UFlat9-8 719MB/s ± 1% txt4 +_UFlat10-8 2.84GB/s ± 0% pb +_UFlat11-8 1.05GB/s ± 0% gaviota + +_ZFlat0-8 1.04GB/s ± 0% html +_ZFlat1-8 534MB/s ± 0% urls +_ZFlat2-8 15.7GB/s ± 1% jpg +_ZFlat3-8 740MB/s ± 3% jpg_200 +_ZFlat4-8 9.20GB/s ± 1% pdf +_ZFlat5-8 991MB/s ± 0% html4 +_ZFlat6-8 379MB/s ± 0% txt1 +_ZFlat7-8 352MB/s ± 0% txt2 +_ZFlat8-8 396MB/s ± 1% txt3 +_ZFlat9-8 327MB/s ± 1% txt4 +_ZFlat10-8 1.33GB/s ± 1% pb +_ZFlat11-8 605MB/s ± 1% gaviota + + + +"go test -test.bench=. -tags=noasm" + +_UFlat0-8 621MB/s ± 2% html +_UFlat1-8 494MB/s ± 1% urls +_UFlat2-8 23.2GB/s ± 1% jpg +_UFlat3-8 1.12GB/s ± 1% jpg_200 +_UFlat4-8 4.35GB/s ± 1% pdf +_UFlat5-8 609MB/s ± 0% html4 +_UFlat6-8 296MB/s ± 0% txt1 +_UFlat7-8 288MB/s ± 0% txt2 +_UFlat8-8 309MB/s ± 1% txt3 +_UFlat9-8 280MB/s ± 1% txt4 +_UFlat10-8 753MB/s ± 0% pb +_UFlat11-8 400MB/s ± 0% gaviota + +_ZFlat0-8 409MB/s ± 1% html +_ZFlat1-8 250MB/s ± 1% urls +_ZFlat2-8 12.3GB/s ± 1% jpg +_ZFlat3-8 132MB/s ± 0% jpg_200 +_ZFlat4-8 2.92GB/s ± 0% pdf +_ZFlat5-8 405MB/s ± 1% html4 +_ZFlat6-8 179MB/s ± 1% txt1 +_ZFlat7-8 170MB/s ± 1% txt2 +_ZFlat8-8 189MB/s ± 1% txt3 +_ZFlat9-8 164MB/s ± 1% txt4 +_ZFlat10-8 479MB/s ± 1% pb +_ZFlat11-8 270MB/s ± 1% gaviota + + + +For comparison (Go's encoded output is byte-for-byte identical to C++'s), here +are the numbers from C++ Snappy's + +make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log + +BM_UFlat/0 2.4GB/s html +BM_UFlat/1 1.4GB/s urls +BM_UFlat/2 21.8GB/s jpg +BM_UFlat/3 1.5GB/s jpg_200 +BM_UFlat/4 13.3GB/s pdf +BM_UFlat/5 2.1GB/s html4 +BM_UFlat/6 1.0GB/s txt1 +BM_UFlat/7 959.4MB/s txt2 +BM_UFlat/8 1.0GB/s txt3 +BM_UFlat/9 864.5MB/s txt4 +BM_UFlat/10 2.9GB/s pb +BM_UFlat/11 1.2GB/s gaviota + +BM_ZFlat/0 944.3MB/s html (22.31 %) +BM_ZFlat/1 501.6MB/s urls (47.78 %) +BM_ZFlat/2 14.3GB/s jpg (99.95 %) +BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %) +BM_ZFlat/4 8.3GB/s pdf (83.30 %) +BM_ZFlat/5 903.5MB/s html4 (22.52 %) +BM_ZFlat/6 336.0MB/s txt1 (57.88 %) +BM_ZFlat/7 312.3MB/s txt2 (61.91 %) +BM_ZFlat/8 353.1MB/s txt3 (54.99 %) +BM_ZFlat/9 289.9MB/s txt4 (66.26 %) +BM_ZFlat/10 1.2GB/s pb (19.68 %) +BM_ZFlat/11 527.4MB/s gaviota (37.72 %) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode.go new file mode 100644 index 0000000..72efb03 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode.go @@ -0,0 +1,237 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = errors.New("snappy: corrupt input") + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = errors.New("snappy: decoded block is too large") + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = errors.New("snappy: unsupported input") + + errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrTooLarge + } + return int(v), n, nil +} + +const ( + decodeErrCodeCorrupt = 1 + decodeErrCodeUnsupportedLiteralLength = 2 +) + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= len(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + switch decode(dst, src[s:]) { + case 0: + return dst, nil + case decodeErrCodeUnsupportedLiteralLength: + return nil, errUnsupportedLiteralLength + } + return nil, ErrCorrupt +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + decoded: make([]byte, maxBlockSize), + buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), + } +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +type Reader struct { + r io.Reader + err error + decoded []byte + buf []byte + // decoded[i:j] contains decoded bytes that have not yet been passed on. + i, j int + readHeader bool +} + +// Reset discards any buffered data, resets all state, and switches the Snappy +// reader to read from r. This permits reusing a Reader rather than allocating +// a new one. +func (r *Reader) Reset(reader io.Reader) { + r.r = reader + r.err = nil + r.i = 0 + r.j = 0 + r.readHeader = false +} + +func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + return true +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + for { + if r.i < r.j { + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil + } + if !r.readFull(r.buf[:4], true) { + return 0, r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return 0, r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + if chunkLen > len(r.buf) { + r.err = ErrUnsupported + return 0, r.err + } + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return 0, r.err + } + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeUncompressedData: + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - checksumSize + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.decoded[:n], false) { + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return 0, r.err + } + for i := 0; i < len(magicBody); i++ { + if r.buf[i] != magicBody[i] { + r.err = ErrCorrupt + return 0, r.err + } + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + r.err = ErrUnsupported + return 0, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.readFull(r.buf[:chunkLen], false) { + return 0, r.err + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.go new file mode 100644 index 0000000..fcd192b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +package snappy + +// decode has the same semantics as in decode_other.go. +// +//go:noescape +func decode(dst, src []byte) int diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.s new file mode 100644 index 0000000..e6179f6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_amd64.s @@ -0,0 +1,490 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// The asm code generally follows the pure Go code in decode_other.go, except +// where marked with a "!!!". + +// func decode(dst, src []byte) int +// +// All local variables fit into registers. The non-zero stack size is only to +// spill registers and push args when issuing a CALL. The register allocation: +// - AX scratch +// - BX scratch +// - CX length or x +// - DX offset +// - SI &src[s] +// - DI &dst[d] +// + R8 dst_base +// + R9 dst_len +// + R10 dst_base + dst_len +// + R11 src_base +// + R12 src_len +// + R13 src_base + src_len +// - R14 used by doCopy +// - R15 used by doCopy +// +// The registers R8-R13 (marked with a "+") are set at the start of the +// function, and after a CALL returns, and are not otherwise modified. +// +// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI. +// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI. +TEXT ·decode(SB), NOSPLIT, $48-56 + // Initialize SI, DI and R8-R13. + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, DI + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, SI + MOVQ R11, R13 + ADDQ R12, R13 + +loop: + // for s < len(src) + CMPQ SI, R13 + JEQ end + + // CX = uint32(src[s]) + // + // switch src[s] & 0x03 + MOVBLZX (SI), CX + MOVL CX, BX + ANDL $3, BX + CMPL BX, $1 + JAE tagCopy + + // ---------------------------------------- + // The code below handles literal tags. + + // case tagLiteral: + // x := uint32(src[s] >> 2) + // switch + SHRL $2, CX + CMPL CX, $60 + JAE tagLit60Plus + + // case x < 60: + // s++ + INCQ SI + +doLit: + // This is the end of the inner "switch", when we have a literal tag. + // + // We assume that CX == x and x fits in a uint32, where x is the variable + // used in the pure Go decode_other.go code. + + // length = int(x) + 1 + // + // Unlike the pure Go code, we don't need to check if length <= 0 because + // CX can hold 64 bits, so the increment cannot overflow. + INCQ CX + + // Prepare to check if copying length bytes will run past the end of dst or + // src. + // + // AX = len(dst) - d + // BX = len(src) - s + MOVQ R10, AX + SUBQ DI, AX + MOVQ R13, BX + SUBQ SI, BX + + // !!! Try a faster technique for short (16 or fewer bytes) copies. + // + // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { + // goto callMemmove // Fall back on calling runtime·memmove. + // } + // + // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s + // against 21 instead of 16, because it cannot assume that all of its input + // is contiguous in memory and so it needs to leave enough source bytes to + // read the next tag without refilling buffers, but Go's Decode assumes + // contiguousness (the src argument is a []byte). + CMPQ CX, $16 + JGT callMemmove + CMPQ AX, $16 + JLT callMemmove + CMPQ BX, $16 + JLT callMemmove + + // !!! Implement the copy from src to dst as a 16-byte load and store. + // (Decode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only length bytes, but that's + // OK. If the input is a valid Snappy encoding then subsequent iterations + // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a + // non-nil error), so the overrun will be ignored. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(SI), X0 + MOVOU X0, 0(DI) + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +callMemmove: + // if length > len(dst)-d || length > len(src)-s { etc } + CMPQ CX, AX + JGT errCorrupt + CMPQ CX, BX + JGT errCorrupt + + // copy(dst[d:], src[s:s+length]) + // + // This means calling runtime·memmove(&dst[d], &src[s], length), so we push + // DI, SI and CX as arguments. Coincidentally, we also need to spill those + // three registers to the stack, to save local variables across the CALL. + MOVQ DI, 0(SP) + MOVQ SI, 8(SP) + MOVQ CX, 16(SP) + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + MOVQ CX, 40(SP) + CALL runtime·memmove(SB) + + // Restore local variables: unspill registers from the stack and + // re-calculate R8-R13. + MOVQ 24(SP), DI + MOVQ 32(SP), SI + MOVQ 40(SP), CX + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, R13 + ADDQ R12, R13 + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +tagLit60Plus: + // !!! This fragment does the + // + // s += x - 58; if uint(s) > uint(len(src)) { etc } + // + // checks. In the asm version, we code it once instead of once per switch case. + ADDQ CX, SI + SUBQ $58, SI + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // case x == 60: + CMPL CX, $61 + JEQ tagLit61 + JA tagLit62Plus + + // x = uint32(src[s-1]) + MOVBLZX -1(SI), CX + JMP doLit + +tagLit61: + // case x == 61: + // x = uint32(src[s-2]) | uint32(src[s-1])<<8 + MOVWLZX -2(SI), CX + JMP doLit + +tagLit62Plus: + CMPL CX, $62 + JA tagLit63 + + // case x == 62: + // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + MOVWLZX -3(SI), CX + MOVBLZX -1(SI), BX + SHLL $16, BX + ORL BX, CX + JMP doLit + +tagLit63: + // case x == 63: + // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + MOVL -4(SI), CX + JMP doLit + +// The code above handles literal tags. +// ---------------------------------------- +// The code below handles copy tags. + +tagCopy4: + // case tagCopy4: + // s += 5 + ADDQ $5, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-5])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + MOVLQZX -4(SI), DX + JMP doCopy + +tagCopy2: + // case tagCopy2: + // s += 3 + ADDQ $3, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-3])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + MOVWQZX -2(SI), DX + JMP doCopy + +tagCopy: + // We have a copy tag. We assume that: + // - BX == src[s] & 0x03 + // - CX == src[s] + CMPQ BX, $2 + JEQ tagCopy2 + JA tagCopy4 + + // case tagCopy1: + // s += 2 + ADDQ $2, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + MOVQ CX, DX + ANDQ $0xe0, DX + SHLQ $3, DX + MOVBQZX -1(SI), BX + ORQ BX, DX + + // length = 4 + int(src[s-2])>>2&0x7 + SHRQ $2, CX + ANDQ $7, CX + ADDQ $4, CX + +doCopy: + // This is the end of the outer "switch", when we have a copy tag. + // + // We assume that: + // - CX == length && CX > 0 + // - DX == offset + + // if offset <= 0 { etc } + CMPQ DX, $0 + JLE errCorrupt + + // if d < offset { etc } + MOVQ DI, BX + SUBQ R8, BX + CMPQ BX, DX + JLT errCorrupt + + // if length > len(dst)-d { etc } + MOVQ R10, BX + SUBQ DI, BX + CMPQ CX, BX + JGT errCorrupt + + // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length + // + // Set: + // - R14 = len(dst)-d + // - R15 = &dst[d-offset] + MOVQ R10, R14 + SUBQ DI, R14 + MOVQ DI, R15 + SUBQ DX, R15 + + // !!! Try a faster technique for short (16 or fewer bytes) forward copies. + // + // First, try using two 8-byte load/stores, similar to the doLit technique + // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is + // still OK if offset >= 8. Note that this has to be two 8-byte load/stores + // and not one 16-byte load/store, and the first store has to be before the + // second load, due to the overlap if offset is in the range [8, 16). + // + // if length > 16 || offset < 8 || len(dst)-d < 16 { + // goto slowForwardCopy + // } + // copy 16 bytes + // d += length + CMPQ CX, $16 + JGT slowForwardCopy + CMPQ DX, $8 + JLT slowForwardCopy + CMPQ R14, $16 + JLT slowForwardCopy + MOVQ 0(R15), AX + MOVQ AX, 0(DI) + MOVQ 8(R15), BX + MOVQ BX, 8(DI) + ADDQ CX, DI + JMP loop + +slowForwardCopy: + // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we + // can still try 8-byte load stores, provided we can overrun up to 10 extra + // bytes. As above, the overrun will be fixed up by subsequent iterations + // of the outermost loop. + // + // The C++ snappy code calls this technique IncrementalCopyFastPath. Its + // commentary says: + // + // ---- + // + // The main part of this loop is a simple copy of eight bytes at a time + // until we've copied (at least) the requested amount of bytes. However, + // if d and d-offset are less than eight bytes apart (indicating a + // repeating pattern of length < 8), we first need to expand the pattern in + // order to get the correct results. For instance, if the buffer looks like + // this, with the eight-byte and patterns marked as + // intervals: + // + // abxxxxxxxxxxxx + // [------] d-offset + // [------] d + // + // a single eight-byte copy from to will repeat the pattern + // once, after which we can move two bytes without moving : + // + // ababxxxxxxxxxx + // [------] d-offset + // [------] d + // + // and repeat the exercise until the two no longer overlap. + // + // This allows us to do very well in the special case of one single byte + // repeated many times, without taking a big hit for more general cases. + // + // The worst case of extra writing past the end of the match occurs when + // offset == 1 and length == 1; the last copy will read from byte positions + // [0..7] and write to [4..11], whereas it was only supposed to write to + // position 1. Thus, ten excess bytes. + // + // ---- + // + // That "10 byte overrun" worst case is confirmed by Go's + // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy + // and finishSlowForwardCopy algorithm. + // + // if length > len(dst)-d-10 { + // goto verySlowForwardCopy + // } + SUBQ $10, R14 + CMPQ CX, R14 + JGT verySlowForwardCopy + +makeOffsetAtLeast8: + // !!! As above, expand the pattern so that offset >= 8 and we can use + // 8-byte load/stores. + // + // for offset < 8 { + // copy 8 bytes from dst[d-offset:] to dst[d:] + // length -= offset + // d += offset + // offset += offset + // // The two previous lines together means that d-offset, and therefore + // // R15, is unchanged. + // } + CMPQ DX, $8 + JGE fixUpSlowForwardCopy + MOVQ (R15), BX + MOVQ BX, (DI) + SUBQ DX, CX + ADDQ DX, DI + ADDQ DX, DX + JMP makeOffsetAtLeast8 + +fixUpSlowForwardCopy: + // !!! Add length (which might be negative now) to d (implied by DI being + // &dst[d]) so that d ends up at the right place when we jump back to the + // top of the loop. Before we do that, though, we save DI to AX so that, if + // length is positive, copying the remaining length bytes will write to the + // right place. + MOVQ DI, AX + ADDQ CX, DI + +finishSlowForwardCopy: + // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative + // length means that we overrun, but as above, that will be fixed up by + // subsequent iterations of the outermost loop. + CMPQ CX, $0 + JLE loop + MOVQ (R15), BX + MOVQ BX, (AX) + ADDQ $8, R15 + ADDQ $8, AX + SUBQ $8, CX + JMP finishSlowForwardCopy + +verySlowForwardCopy: + // verySlowForwardCopy is a simple implementation of forward copy. In C + // parlance, this is a do/while loop instead of a while loop, since we know + // that length > 0. In Go syntax: + // + // for { + // dst[d] = dst[d - offset] + // d++ + // length-- + // if length == 0 { + // break + // } + // } + MOVB (R15), BX + MOVB BX, (DI) + INCQ R15 + INCQ DI + DECQ CX + JNZ verySlowForwardCopy + JMP loop + +// The code above handles copy tags. +// ---------------------------------------- + +end: + // This is the end of the "for s < len(src)". + // + // if d != len(dst) { etc } + CMPQ DI, R10 + JNE errCorrupt + + // return 0 + MOVQ $0, ret+48(FP) + RET + +errCorrupt: + // return decodeErrCodeCorrupt + MOVQ $1, ret+48(FP) + RET diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_other.go new file mode 100644 index 0000000..8c9f204 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/decode_other.go @@ -0,0 +1,101 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine !gc noasm + +package snappy + +// decode writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func decode(dst, src []byte) int { + var d, s, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length <= 0 { + return decodeErrCodeUnsupportedLiteralLength + } + if length > len(dst)-d || length > len(src)-s { + return decodeErrCodeCorrupt + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || d < offset || length > len(dst)-d { + return decodeErrCodeCorrupt + } + // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike + // the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + for end := d + length; d != end; d++ { + dst[d] = dst[d-offset] + } + } + if d != len(dst) { + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode.go new file mode 100644 index 0000000..8d393e9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode.go @@ -0,0 +1,285 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + for len(src) > 0 { + p := src + src = nil + if len(p) > maxBlockSize { + p, src = p[:maxBlockSize], p[maxBlockSize:] + } + if len(p) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], p) + } else { + d += encodeBlock(dst[d:], p) + } + } + return dst[:d] +} + +// inputMargin is the minimum number of extra input bytes to keep, inside +// encodeBlock's inner loop. On some architectures, this margin lets us +// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) +// literals can be implemented as a single load to and store from a 16-byte +// register. That literal's actual length can be as short as 1 byte, so this +// can copy up to 15 bytes too much, but that's OK as subsequent iterations of +// the encoding loop will fix up the copy overrun, and this inputMargin ensures +// that we don't overrun the dst and src buffers. +const inputMargin = 16 - 1 + +// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that +// could be encoded with a copy tag. This is the minimum with respect to the +// algorithm used by encodeBlock, not a minimum enforced by the file format. +// +// The encoded output must start with at least a 1 byte literal, as there are +// no previous bytes to copy. A minimal (1 byte) copy after that, generated +// from an emitCopy call in encodeBlock's main loop, would require at least +// another inputMargin bytes, for the reason above: we want any emitLiteral +// calls inside encodeBlock's main loop to use the fast path if possible, which +// requires being able to overrun by inputMargin bytes. Thus, +// minNonLiteralBlockSize equals 1 + 1 + inputMargin. +// +// The C++ code doesn't use this exact threshold, but it could, as discussed at +// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion +// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an +// optimization. It should not affect the encoded form. This is tested by +// TestSameEncodingAsCppShortCopies. +const minNonLiteralBlockSize = 1 + 1 + inputMargin + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +func MaxEncodedLen(srcLen int) int { + n := uint64(srcLen) + if n > 0xffffffff { + return -1 + } + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // That is, 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + n = 32 + n + n/6 + if n > 0xffffffff { + return -1 + } + return int(n) +} + +var errClosed = errors.New("snappy: Writer is closed") + +// NewWriter returns a new Writer that compresses to w. +// +// The Writer returned does not buffer writes. There is no need to Flush or +// Close such a Writer. +// +// Deprecated: the Writer returned is not suitable for many small writes, only +// for few large writes. Use NewBufferedWriter instead, which is efficient +// regardless of the frequency and shape of the writes, and remember to Close +// that Writer when done. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + obuf: make([]byte, obufLen), + } +} + +// NewBufferedWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// The Writer returned buffers writes. Users must call Close to guarantee all +// data has been forwarded to the underlying io.Writer. They may also call +// Flush zero or more times before calling Close. +func NewBufferedWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + ibuf: make([]byte, 0, maxBlockSize), + obuf: make([]byte, obufLen), + } +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +type Writer struct { + w io.Writer + err error + + // ibuf is a buffer for the incoming (uncompressed) bytes. + // + // Its use is optional. For backwards compatibility, Writers created by the + // NewWriter function have ibuf == nil, do not buffer incoming bytes, and + // therefore do not need to be Flush'ed or Close'd. + ibuf []byte + + // obuf is a buffer for the outgoing (compressed) bytes. + obuf []byte + + // wroteStreamHeader is whether we have written the stream header. + wroteStreamHeader bool +} + +// Reset discards the writer's state and switches the Snappy writer to write to +// w. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(writer io.Writer) { + w.w = writer + w.err = nil + if w.ibuf != nil { + w.ibuf = w.ibuf[:0] + } + w.wroteStreamHeader = false +} + +// Write satisfies the io.Writer interface. +func (w *Writer) Write(p []byte) (nRet int, errRet error) { + if w.ibuf == nil { + // Do not buffer incoming bytes. This does not perform or compress well + // if the caller of Writer.Write writes many small slices. This + // behavior is therefore deprecated, but still supported for backwards + // compatibility with code that doesn't explicitly Flush or Close. + return w.write(p) + } + + // The remainder of this method is based on bufio.Writer.Write from the + // standard library. + + for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { + var n int + if len(w.ibuf) == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, _ = w.write(p) + } else { + n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + w.Flush() + } + nRet += n + p = p[n:] + } + if w.err != nil { + return nRet, w.err + } + n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + nRet += n + return nRet, nil +} + +func (w *Writer) write(p []byte) (nRet int, errRet error) { + if w.err != nil { + return 0, w.err + } + for len(p) > 0 { + obufStart := len(magicChunk) + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + copy(w.obuf, magicChunk) + obufStart = 0 + } + + var uncompressed []byte + if len(p) > maxBlockSize { + uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] + } else { + uncompressed, p = p, nil + } + checksum := crc(uncompressed) + + // Compress the buffer, discarding the result if the improvement + // isn't at least 12.5%. + compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) + chunkType := uint8(chunkTypeCompressedData) + chunkLen := 4 + len(compressed) + obufEnd := obufHeaderLen + len(compressed) + if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { + chunkType = chunkTypeUncompressedData + chunkLen = 4 + len(uncompressed) + obufEnd = obufHeaderLen + } + + // Fill in the per-chunk header that comes before the body. + w.obuf[len(magicChunk)+0] = chunkType + w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) + w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) + w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) + w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) + w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) + w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) + w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) + + if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { + w.err = err + return nRet, err + } + if chunkType == chunkTypeUncompressedData { + if _, err := w.w.Write(uncompressed); err != nil { + w.err = err + return nRet, err + } + } + nRet += len(uncompressed) + } + return nRet, nil +} + +// Flush flushes the Writer to its underlying io.Writer. +func (w *Writer) Flush() error { + if w.err != nil { + return w.err + } + if len(w.ibuf) == 0 { + return nil + } + w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + return w.err +} + +// Close calls Flush and then closes the Writer. +func (w *Writer) Close() error { + w.Flush() + ret := w.err + if w.err == nil { + w.err = errClosed + } + return ret +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.go new file mode 100644 index 0000000..150d91b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.go @@ -0,0 +1,29 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +package snappy + +// emitLiteral has the same semantics as in encode_other.go. +// +//go:noescape +func emitLiteral(dst, lit []byte) int + +// emitCopy has the same semantics as in encode_other.go. +// +//go:noescape +func emitCopy(dst []byte, offset, length int) int + +// extendMatch has the same semantics as in encode_other.go. +// +//go:noescape +func extendMatch(src []byte, i, j int) int + +// encodeBlock has the same semantics as in encode_other.go. +// +//go:noescape +func encodeBlock(dst, src []byte) (d int) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.s new file mode 100644 index 0000000..adfd979 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_amd64.s @@ -0,0 +1,730 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a +// Go toolchain regression. See https://github.com/golang/go/issues/15426 and +// https://github.com/golang/snappy/issues/29 +// +// As a workaround, the package was built with a known good assembler, and +// those instructions were disassembled by "objdump -d" to yield the +// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 +// style comments, in AT&T asm syntax. Note that rsp here is a physical +// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm). +// The instructions were then encoded as "BYTE $0x.." sequences, which assemble +// fine on Go 1.6. + +// The asm code generally follows the pure Go code in encode_other.go, except +// where marked with a "!!!". + +// ---------------------------------------------------------------------------- + +// func emitLiteral(dst, lit []byte) int +// +// All local variables fit into registers. The register allocation: +// - AX len(lit) +// - BX n +// - DX return value +// - DI &dst[i] +// - R10 &lit[0] +// +// The 24 bytes of stack space is to call runtime·memmove. +// +// The unusual register allocation of local variables, such as R10 for the +// source pointer, matches the allocation used at the call site in encodeBlock, +// which makes it easier to manually inline this function. +TEXT ·emitLiteral(SB), NOSPLIT, $24-56 + MOVQ dst_base+0(FP), DI + MOVQ lit_base+24(FP), R10 + MOVQ lit_len+32(FP), AX + MOVQ AX, DX + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT oneByte + CMPL BX, $256 + JLT twoBytes + +threeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + ADDQ $3, DX + JMP memmove + +twoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + ADDQ $2, DX + JMP memmove + +oneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + ADDQ $1, DX + +memmove: + MOVQ DX, ret+48(FP) + + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + CALL runtime·memmove(SB) + RET + +// ---------------------------------------------------------------------------- + +// func emitCopy(dst []byte, offset, length int) int +// +// All local variables fit into registers. The register allocation: +// - AX length +// - SI &dst[0] +// - DI &dst[i] +// - R11 offset +// +// The unusual register allocation of local variables, such as R11 for the +// offset, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·emitCopy(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), DI + MOVQ DI, SI + MOVQ offset+24(FP), R11 + MOVQ length+32(FP), AX + +loop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT step1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP loop0 + +step1: + // if length > 64 { etc } + CMPL AX, $64 + JLE step2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +step2: + // if length >= 12 || offset >= 2048 { goto step3 } + CMPL AX, $12 + JGE step3 + CMPL R11, $2048 + JGE step3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +step3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func extendMatch(src []byte, i, j int) int +// +// All local variables fit into registers. The register allocation: +// - DX &src[0] +// - SI &src[j] +// - R13 &src[len(src) - 8] +// - R14 &src[len(src)] +// - R15 &src[i] +// +// The unusual register allocation of local variables, such as R15 for a source +// pointer, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·extendMatch(SB), NOSPLIT, $0-48 + MOVQ src_base+0(FP), DX + MOVQ src_len+8(FP), R14 + MOVQ i+24(FP), R15 + MOVQ j+32(FP), SI + ADDQ DX, R14 + ADDQ DX, R15 + ADDQ DX, SI + MOVQ R14, R13 + SUBQ $8, R13 + +cmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA cmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE bsf + ADDQ $8, R15 + ADDQ $8, SI + JMP cmp8 + +bsf: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +cmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE extendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE extendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP cmp1 + +extendMatchEnd: + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func encodeBlock(dst, src []byte) (d int) +// +// All local variables fit into registers, other than "var table". The register +// allocation: +// - AX . . +// - BX . . +// - CX 56 shift (note that amd64 shifts by non-immediates must use CX). +// - DX 64 &src[0], tableSize +// - SI 72 &src[s] +// - DI 80 &dst[d] +// - R9 88 sLimit +// - R10 . &src[nextEmit] +// - R11 96 prevHash, currHash, nextHash, offset +// - R12 104 &src[base], skip +// - R13 . &src[nextS], &src[len(src) - 8] +// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x +// - R15 112 candidate +// +// The second column (56, 64, etc) is the stack offset to spill the registers +// when calling other functions. We could pack this slightly tighter, but it's +// simpler to have a dedicated spill map independent of the function called. +// +// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An +// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill +// local variables (registers) during calls gives 32768 + 56 + 64 = 32888. +TEXT ·encodeBlock(SB), 0, $32888-56 + MOVQ dst_base+0(FP), DI + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R14 + + // shift, tableSize := uint32(32-8), 1<<8 + MOVQ $24, CX + MOVQ $256, DX + +calcShift: + // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + // shift-- + // } + CMPQ DX, $16384 + JGE varTable + CMPQ DX, R14 + JGE varTable + SUBQ $1, CX + SHLQ $1, DX + JMP calcShift + +varTable: + // var table [maxTableSize]uint16 + // + // In the asm code, unlike the Go code, we can zero-initialize only the + // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU + // writes 16 bytes, so we can do only tableSize/8 writes instead of the + // 2048 writes that would zero-initialize all of table's 32768 bytes. + SHRQ $3, DX + LEAQ table-32768(SP), BX + PXOR X0, X0 + +memclr: + MOVOU X0, 0(BX) + ADDQ $16, BX + SUBQ $1, DX + JNZ memclr + + // !!! DX = &src[0] + MOVQ SI, DX + + // sLimit := len(src) - inputMargin + MOVQ R14, R9 + SUBQ $15, R9 + + // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't + // change for the rest of the function. + MOVQ CX, 56(SP) + MOVQ DX, 64(SP) + MOVQ R9, 88(SP) + + // nextEmit := 0 + MOVQ DX, R10 + + // s := 1 + ADDQ $1, SI + + // nextHash := hash(load32(src, s), shift) + MOVL 0(SI), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + +outer: + // for { etc } + + // skip := 32 + MOVQ $32, R12 + + // nextS := s + MOVQ SI, R13 + + // candidate := 0 + MOVQ $0, R15 + +inner0: + // for { etc } + + // s := nextS + MOVQ R13, SI + + // bytesBetweenHashLookups := skip >> 5 + MOVQ R12, R14 + SHRQ $5, R14 + + // nextS = s + bytesBetweenHashLookups + ADDQ R14, R13 + + // skip += bytesBetweenHashLookups + ADDQ R14, R12 + + // if nextS > sLimit { goto emitRemainder } + MOVQ R13, AX + SUBQ DX, AX + CMPQ AX, R9 + JA emitRemainder + + // candidate = int(table[nextHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[nextHash] = uint16(s) + MOVQ SI, AX + SUBQ DX, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // nextHash = hash(load32(src, nextS), shift) + MOVL 0(R13), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // if load32(src, s) != load32(src, candidate) { continue } break + MOVL 0(SI), AX + MOVL (DX)(R15*1), BX + CMPL AX, BX + JNE inner0 + +fourByteMatch: + // As per the encode_other.go code: + // + // A 4-byte match has been found. We'll later see etc. + + // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment + // on inputMargin in encode.go. + MOVQ SI, AX + SUBQ R10, AX + CMPQ AX, $16 + JLE emitLiteralFastPath + + // ---------------------------------------- + // Begin inline of the emitLiteral call. + // + // d += emitLiteral(dst[d:], src[nextEmit:s]) + + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT inlineEmitLiteralOneByte + CMPL BX, $256 + JLT inlineEmitLiteralTwoBytes + +inlineEmitLiteralThreeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralTwoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralOneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + +inlineEmitLiteralMemmove: + // Spill local variables (registers) onto the stack; call; unspill. + // + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)". + MOVQ SI, 72(SP) + MOVQ DI, 80(SP) + MOVQ R15, 112(SP) + CALL runtime·memmove(SB) + MOVQ 56(SP), CX + MOVQ 64(SP), DX + MOVQ 72(SP), SI + MOVQ 80(SP), DI + MOVQ 88(SP), R9 + MOVQ 112(SP), R15 + JMP inner1 + +inlineEmitLiteralEnd: + // End inline of the emitLiteral call. + // ---------------------------------------- + +emitLiteralFastPath: + // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". + MOVB AX, BX + SUBB $1, BX + SHLB $2, BX + MOVB BX, (DI) + ADDQ $1, DI + + // !!! Implement the copy from lit to dst as a 16-byte load and store. + // (Encode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only len(lit) bytes, but that's + // OK. Subsequent iterations will fix up the overrun. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(R10), X0 + MOVOU X0, 0(DI) + ADDQ AX, DI + +inner1: + // for { etc } + + // base := s + MOVQ SI, R12 + + // !!! offset := base - candidate + MOVQ R12, R11 + SUBQ R15, R11 + SUBQ DX, R11 + + // ---------------------------------------- + // Begin inline of the extendMatch call. + // + // s = extendMatch(src, candidate+4, s+4) + + // !!! R14 = &src[len(src)] + MOVQ src_len+32(FP), R14 + ADDQ DX, R14 + + // !!! R13 = &src[len(src) - 8] + MOVQ R14, R13 + SUBQ $8, R13 + + // !!! R15 = &src[candidate + 4] + ADDQ $4, R15 + ADDQ DX, R15 + + // !!! s += 4 + ADDQ $4, SI + +inlineExtendMatchCmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA inlineExtendMatchCmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE inlineExtendMatchBSF + ADDQ $8, R15 + ADDQ $8, SI + JMP inlineExtendMatchCmp8 + +inlineExtendMatchBSF: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + JMP inlineExtendMatchEnd + +inlineExtendMatchCmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE inlineExtendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE inlineExtendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP inlineExtendMatchCmp1 + +inlineExtendMatchEnd: + // End inline of the extendMatch call. + // ---------------------------------------- + + // ---------------------------------------- + // Begin inline of the emitCopy call. + // + // d += emitCopy(dst[d:], base-candidate, s-base) + + // !!! length := s - base + MOVQ SI, AX + SUBQ R12, AX + +inlineEmitCopyLoop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT inlineEmitCopyStep1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP inlineEmitCopyLoop0 + +inlineEmitCopyStep1: + // if length > 64 { etc } + CMPL AX, $64 + JLE inlineEmitCopyStep2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +inlineEmitCopyStep2: + // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } + CMPL AX, $12 + JGE inlineEmitCopyStep3 + CMPL R11, $2048 + JGE inlineEmitCopyStep3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + JMP inlineEmitCopyEnd + +inlineEmitCopyStep3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + +inlineEmitCopyEnd: + // End inline of the emitCopy call. + // ---------------------------------------- + + // nextEmit = s + MOVQ SI, R10 + + // if s >= sLimit { goto emitRemainder } + MOVQ SI, AX + SUBQ DX, AX + CMPQ AX, R9 + JAE emitRemainder + + // As per the encode_other.go code: + // + // We could immediately etc. + + // x := load64(src, s-1) + MOVQ -1(SI), R14 + + // prevHash := hash(uint32(x>>0), shift) + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // table[prevHash] = uint16(s-1) + MOVQ SI, AX + SUBQ DX, AX + SUBQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // currHash := hash(uint32(x>>8), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // candidate = int(table[currHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[currHash] = uint16(s) + ADDQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // if uint32(x>>8) == load32(src, candidate) { continue } + MOVL (DX)(R15*1), BX + CMPL R14, BX + JEQ inner1 + + // nextHash = hash(uint32(x>>16), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // s++ + ADDQ $1, SI + + // break out of the inner1 for loop, i.e. continue the outer loop. + JMP outer + +emitRemainder: + // if nextEmit < len(src) { etc } + MOVQ src_len+32(FP), AX + ADDQ DX, AX + CMPQ R10, AX + JEQ encodeBlockEnd + + // d += emitLiteral(dst[d:], src[nextEmit:]) + // + // Push args. + MOVQ DI, 0(SP) + MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ R10, 24(SP) + SUBQ R10, AX + MOVQ AX, 32(SP) + MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative. + + // Spill local variables (registers) onto the stack; call; unspill. + MOVQ DI, 80(SP) + CALL ·emitLiteral(SB) + MOVQ 80(SP), DI + + // Finish the "d +=" part of "d += emitLiteral(etc)". + ADDQ 48(SP), DI + +encodeBlockEnd: + MOVQ dst_base+0(FP), AX + SUBQ AX, DI + MOVQ DI, d+48(FP) + RET diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_other.go new file mode 100644 index 0000000..dbcae90 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/encode_other.go @@ -0,0 +1,238 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine !gc noasm + +package snappy + +func load32(b []byte, i int) uint32 { + b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func load64(b []byte, i int) uint64 { + b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= len(lit) && len(lit) <= 65536 +func emitLiteral(dst, lit []byte) int { + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[0] = 60<<2 | tagLiteral + dst[1] = uint8(n) + i = 2 + default: + dst[0] = 61<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + i = 3 + } + return i + copy(dst[i:], lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= 65535 +// 4 <= length && length <= 65535 +func emitCopy(dst []byte, offset, length int) int { + i := 0 + // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The + // threshold for this loop is a little higher (at 68 = 64 + 4), and the + // length emitted down below is is a little lower (at 60 = 64 - 4), because + // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed + // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as + // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as + // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a + // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an + // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. + for length >= 68 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[i+0] = 63<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 64 + } + if length > 64 { + // Emit a length 60 copy, encoded as 3 bytes. + dst[i+0] = 59<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 60 + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + return i + 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + dst[i+1] = uint8(offset) + return i + 2 +} + +// extendMatch returns the largest k such that k <= len(src) and that +// src[i:i+k-j] and src[j:k] have the same contents. +// +// It assumes that: +// 0 <= i && i < j && j <= len(src) +func extendMatch(src []byte, i, j int) int { + for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { + } + return j +} + +func hash(u, shift uint32) uint32 { + return (u * 0x1e35a7bd) >> shift +} + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlock(dst, src []byte) (d int) { + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + // The table element type is uint16, as s < sLimit and sLimit < len(src) + // and len(src) <= maxBlockSize and maxBlockSize == 65536. + const ( + maxTableSize = 1 << 14 + // tableMask is redundant, but helps the compiler eliminate bounds + // checks. + tableMask = maxTableSize - 1 + ) + shift := uint32(32 - 8) + for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + shift-- + } + // In Go, all array elements are zero-initialized, so there is no advantage + // to a smaller tableSize per se. However, it matches the C++ algorithm, + // and in the asm versions of this code, we can get away with zeroing only + // the first tableSize elements. + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + nextHash := hash(load32(src, s), shift) + + for { + // Copied from the C++ snappy implementation: + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. + // + // The "skip" variable keeps track of how many bytes there are since + // the last match; dividing it by 32 (ie. right-shifting by five) gives + // the number of bytes to move ahead for each iteration. + skip := 32 + + nextS := s + candidate := 0 + for { + s = nextS + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups + if nextS > sLimit { + goto emitRemainder + } + candidate = int(table[nextHash&tableMask]) + table[nextHash&tableMask] = uint16(s) + nextHash = hash(load32(src, nextS), shift) + if load32(src, s) == load32(src, candidate) { + break + } + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + + // Extend the 4-byte match as long as possible. + // + // This is an inlined version of: + // s = extendMatch(src, candidate+4, s+4) + s += 4 + for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { + } + + d += emitCopy(dst[d:], base-candidate, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load64(src, s-1) + prevHash := hash(uint32(x>>0), shift) + table[prevHash&tableMask] = uint16(s - 1) + currHash := hash(uint32(x>>8), shift) + candidate = int(table[currHash&tableMask]) + table[currHash&tableMask] = uint16(s) + if uint32(x>>8) != load32(src, candidate) { + nextHash = hash(uint32(x>>16), shift) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/snappy.go new file mode 100644 index 0000000..0cf5e37 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/golang/snappy/snappy.go @@ -0,0 +1,87 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package snappy implements the snappy block-based compression format. +// It aims for very high speeds and reasonable compression. +// +// The C++ snappy implementation is at https://github.com/google/snappy +package snappy // import "github.com/golang/snappy" + +import ( + "hash/crc32" +) + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer issued by most + encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) + +const ( + checksumSize = 4 + chunkHeaderSize = 4 + magicChunk = "\xff\x06\x00\x00" + magicBody + magicBody = "sNaPpY" + + // maxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + maxBlockSize = 65536 + + // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is + // hard coded to be a const instead of a variable, so that obufLen can also + // be a const. Their equivalence is confirmed by + // TestMaxEncodedLenOfMaxBlockSize. + maxEncodedLenOfMaxBlockSize = 76490 + + obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize + obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var crcTable = crc32.MakeTable(crc32.Castagnoli) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func crc(b []byte) uint32 { + c := crc32.Update(0, crcTable, b) + return uint32(c>>15|c<<17) + 0xa282ead8 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/2q.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/2q.go new file mode 100644 index 0000000..337d963 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/2q.go @@ -0,0 +1,212 @@ +package lru + +import ( + "fmt" + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +const ( + // Default2QRecentRatio is the ratio of the 2Q cache dedicated + // to recently added entries that have only been accessed once. + Default2QRecentRatio = 0.25 + + // Default2QGhostEntries is the default ratio of ghost + // entries kept to track entries recently evicted + Default2QGhostEntries = 0.50 +) + +// TwoQueueCache is a thread-safe fixed size 2Q cache. +// 2Q is an enhancement over the standard LRU cache +// in that it tracks both frequently and recently used +// entries separately. This avoids a burst in access to new +// entries from evicting frequently used entries. It adds some +// additional tracking overhead to the standard LRU cache, and is +// computationally about 2x the cost, and adds some metadata over +// head. The ARCCache is similar, but does not require setting any +// parameters. +type TwoQueueCache struct { + size int + recentSize int + + recent *simplelru.LRU + frequent *simplelru.LRU + recentEvict *simplelru.LRU + lock sync.RWMutex +} + +// New2Q creates a new TwoQueueCache using the default +// values for the parameters. +func New2Q(size int) (*TwoQueueCache, error) { + return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries) +} + +// New2QParams creates a new TwoQueueCache using the provided +// parameter values. +func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) { + if size <= 0 { + return nil, fmt.Errorf("invalid size") + } + if recentRatio < 0.0 || recentRatio > 1.0 { + return nil, fmt.Errorf("invalid recent ratio") + } + if ghostRatio < 0.0 || ghostRatio > 1.0 { + return nil, fmt.Errorf("invalid ghost ratio") + } + + // Determine the sub-sizes + recentSize := int(float64(size) * recentRatio) + evictSize := int(float64(size) * ghostRatio) + + // Allocate the LRUs + recent, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + frequent, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + recentEvict, err := simplelru.NewLRU(evictSize, nil) + if err != nil { + return nil, err + } + + // Initialize the cache + c := &TwoQueueCache{ + size: size, + recentSize: recentSize, + recent: recent, + frequent: frequent, + recentEvict: recentEvict, + } + return c, nil +} + +func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if this is a frequent value + if val, ok := c.frequent.Get(key); ok { + return val, ok + } + + // If the value is contained in recent, then we + // promote it to frequent + if val, ok := c.recent.Peek(key); ok { + c.recent.Remove(key) + c.frequent.Add(key, val) + return val, ok + } + + // No hit + return nil, false +} + +func (c *TwoQueueCache) Add(key, value interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if the value is frequently used already, + // and just update the value + if c.frequent.Contains(key) { + c.frequent.Add(key, value) + return + } + + // Check if the value is recently used, and promote + // the value into the frequent list + if c.recent.Contains(key) { + c.recent.Remove(key) + c.frequent.Add(key, value) + return + } + + // If the value was recently evicted, add it to the + // frequently used list + if c.recentEvict.Contains(key) { + c.ensureSpace(true) + c.recentEvict.Remove(key) + c.frequent.Add(key, value) + return + } + + // Add to the recently seen list + c.ensureSpace(false) + c.recent.Add(key, value) + return +} + +// ensureSpace is used to ensure we have space in the cache +func (c *TwoQueueCache) ensureSpace(recentEvict bool) { + // If we have space, nothing to do + recentLen := c.recent.Len() + freqLen := c.frequent.Len() + if recentLen+freqLen < c.size { + return + } + + // If the recent buffer is larger than + // the target, evict from there + if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) { + k, _, _ := c.recent.RemoveOldest() + c.recentEvict.Add(k, nil) + return + } + + // Remove from the frequent list otherwise + c.frequent.RemoveOldest() +} + +func (c *TwoQueueCache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.recent.Len() + c.frequent.Len() +} + +func (c *TwoQueueCache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + k1 := c.frequent.Keys() + k2 := c.recent.Keys() + return append(k1, k2...) +} + +func (c *TwoQueueCache) Remove(key interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + if c.frequent.Remove(key) { + return + } + if c.recent.Remove(key) { + return + } + if c.recentEvict.Remove(key) { + return + } +} + +func (c *TwoQueueCache) Purge() { + c.lock.Lock() + defer c.lock.Unlock() + c.recent.Purge() + c.frequent.Purge() + c.recentEvict.Purge() +} + +func (c *TwoQueueCache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.frequent.Contains(key) || c.recent.Contains(key) +} + +func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + if val, ok := c.frequent.Peek(key); ok { + return val, ok + } + return c.recent.Peek(key) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/LICENSE new file mode 100644 index 0000000..be2cc4d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/LICENSE @@ -0,0 +1,362 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/README.md new file mode 100644 index 0000000..33e58cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/README.md @@ -0,0 +1,25 @@ +golang-lru +========== + +This provides the `lru` package which implements a fixed-size +thread safe LRU cache. It is based on the cache in Groupcache. + +Documentation +============= + +Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru) + +Example +======= + +Using the LRU is very simple: + +```go +l, _ := New(128) +for i := 0; i < 256; i++ { + l.Add(i, nil) +} +if l.Len() != 128 { + panic(fmt.Sprintf("bad len: %v", l.Len())) +} +``` diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/arc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/arc.go new file mode 100644 index 0000000..a2a2528 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/arc.go @@ -0,0 +1,257 @@ +package lru + +import ( + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC). +// ARC is an enhancement over the standard LRU cache in that tracks both +// frequency and recency of use. This avoids a burst in access to new +// entries from evicting the frequently used older entries. It adds some +// additional tracking overhead to a standard LRU cache, computationally +// it is roughly 2x the cost, and the extra memory overhead is linear +// with the size of the cache. ARC has been patented by IBM, but is +// similar to the TwoQueueCache (2Q) which requires setting parameters. +type ARCCache struct { + size int // Size is the total capacity of the cache + p int // P is the dynamic preference towards T1 or T2 + + t1 *simplelru.LRU // T1 is the LRU for recently accessed items + b1 *simplelru.LRU // B1 is the LRU for evictions from t1 + + t2 *simplelru.LRU // T2 is the LRU for frequently accessed items + b2 *simplelru.LRU // B2 is the LRU for evictions from t2 + + lock sync.RWMutex +} + +// NewARC creates an ARC of the given size +func NewARC(size int) (*ARCCache, error) { + // Create the sub LRUs + b1, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + b2, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + t1, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + t2, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + + // Initialize the ARC + c := &ARCCache{ + size: size, + p: 0, + t1: t1, + b1: b1, + t2: t2, + b2: b2, + } + return c, nil +} + +// Get looks up a key's value from the cache. +func (c *ARCCache) Get(key interface{}) (interface{}, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + // Ff the value is contained in T1 (recent), then + // promote it to T2 (frequent) + if val, ok := c.t1.Peek(key); ok { + c.t1.Remove(key) + c.t2.Add(key, val) + return val, ok + } + + // Check if the value is contained in T2 (frequent) + if val, ok := c.t2.Get(key); ok { + return val, ok + } + + // No hit + return nil, false +} + +// Add adds a value to the cache. +func (c *ARCCache) Add(key, value interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if the value is contained in T1 (recent), and potentially + // promote it to frequent T2 + if c.t1.Contains(key) { + c.t1.Remove(key) + c.t2.Add(key, value) + return + } + + // Check if the value is already in T2 (frequent) and update it + if c.t2.Contains(key) { + c.t2.Add(key, value) + return + } + + // Check if this value was recently evicted as part of the + // recently used list + if c.b1.Contains(key) { + // T1 set is too small, increase P appropriately + delta := 1 + b1Len := c.b1.Len() + b2Len := c.b2.Len() + if b2Len > b1Len { + delta = b2Len / b1Len + } + if c.p+delta >= c.size { + c.p = c.size + } else { + c.p += delta + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(false) + } + + // Remove from B1 + c.b1.Remove(key) + + // Add the key to the frequently used list + c.t2.Add(key, value) + return + } + + // Check if this value was recently evicted as part of the + // frequently used list + if c.b2.Contains(key) { + // T2 set is too small, decrease P appropriately + delta := 1 + b1Len := c.b1.Len() + b2Len := c.b2.Len() + if b1Len > b2Len { + delta = b1Len / b2Len + } + if delta >= c.p { + c.p = 0 + } else { + c.p -= delta + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(true) + } + + // Remove from B2 + c.b2.Remove(key) + + // Add the key to the frequntly used list + c.t2.Add(key, value) + return + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(false) + } + + // Keep the size of the ghost buffers trim + if c.b1.Len() > c.size-c.p { + c.b1.RemoveOldest() + } + if c.b2.Len() > c.p { + c.b2.RemoveOldest() + } + + // Add to the recently seen list + c.t1.Add(key, value) + return +} + +// replace is used to adaptively evict from either T1 or T2 +// based on the current learned value of P +func (c *ARCCache) replace(b2ContainsKey bool) { + t1Len := c.t1.Len() + if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) { + k, _, ok := c.t1.RemoveOldest() + if ok { + c.b1.Add(k, nil) + } + } else { + k, _, ok := c.t2.RemoveOldest() + if ok { + c.b2.Add(k, nil) + } + } +} + +// Len returns the number of cached entries +func (c *ARCCache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.t1.Len() + c.t2.Len() +} + +// Keys returns all the cached keys +func (c *ARCCache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + k1 := c.t1.Keys() + k2 := c.t2.Keys() + return append(k1, k2...) +} + +// Remove is used to purge a key from the cache +func (c *ARCCache) Remove(key interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + if c.t1.Remove(key) { + return + } + if c.t2.Remove(key) { + return + } + if c.b1.Remove(key) { + return + } + if c.b2.Remove(key) { + return + } +} + +// Purge is used to clear the cache +func (c *ARCCache) Purge() { + c.lock.Lock() + defer c.lock.Unlock() + c.t1.Purge() + c.t2.Purge() + c.b1.Purge() + c.b2.Purge() +} + +// Contains is used to check if the cache contains a key +// without updating recency or frequency. +func (c *ARCCache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.t1.Contains(key) || c.t2.Contains(key) +} + +// Peek is used to inspect the cache value of a key +// without updating recency or frequency. +func (c *ARCCache) Peek(key interface{}) (interface{}, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + if val, ok := c.t1.Peek(key); ok { + return val, ok + } + return c.t2.Peek(key) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/lru.go new file mode 100644 index 0000000..a6285f9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/lru.go @@ -0,0 +1,114 @@ +// This package provides a simple LRU cache. It is based on the +// LRU implementation in groupcache: +// https://github.com/golang/groupcache/tree/master/lru +package lru + +import ( + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +// Cache is a thread-safe fixed size LRU cache. +type Cache struct { + lru *simplelru.LRU + lock sync.RWMutex +} + +// New creates an LRU of the given size +func New(size int) (*Cache, error) { + return NewWithEvict(size, nil) +} + +// NewWithEvict constructs a fixed size cache with the given eviction +// callback. +func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { + lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) + if err != nil { + return nil, err + } + c := &Cache{ + lru: lru, + } + return c, nil +} + +// Purge is used to completely clear the cache +func (c *Cache) Purge() { + c.lock.Lock() + c.lru.Purge() + c.lock.Unlock() +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *Cache) Add(key, value interface{}) bool { + c.lock.Lock() + defer c.lock.Unlock() + return c.lru.Add(key, value) +} + +// Get looks up a key's value from the cache. +func (c *Cache) Get(key interface{}) (interface{}, bool) { + c.lock.Lock() + defer c.lock.Unlock() + return c.lru.Get(key) +} + +// Check if a key is in the cache, without updating the recent-ness +// or deleting it for being stale. +func (c *Cache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Contains(key) +} + +// Returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *Cache) Peek(key interface{}) (interface{}, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Peek(key) +} + +// ContainsOrAdd checks if a key is in the cache without updating the +// recent-ness or deleting it for being stale, and if not, adds the value. +// Returns whether found and whether an eviction occurred. +func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.lru.Contains(key) { + return true, false + } else { + evict := c.lru.Add(key, value) + return false, evict + } +} + +// Remove removes the provided key from the cache. +func (c *Cache) Remove(key interface{}) { + c.lock.Lock() + c.lru.Remove(key) + c.lock.Unlock() +} + +// RemoveOldest removes the oldest item from the cache. +func (c *Cache) RemoveOldest() { + c.lock.Lock() + c.lru.RemoveOldest() + c.lock.Unlock() +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *Cache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Keys() +} + +// Len returns the number of items in the cache. +func (c *Cache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Len() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go new file mode 100644 index 0000000..cb416b3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go @@ -0,0 +1,160 @@ +package simplelru + +import ( + "container/list" + "errors" +) + +// EvictCallback is used to get a callback when a cache entry is evicted +type EvictCallback func(key interface{}, value interface{}) + +// LRU implements a non-thread safe fixed size LRU cache +type LRU struct { + size int + evictList *list.List + items map[interface{}]*list.Element + onEvict EvictCallback +} + +// entry is used to hold a value in the evictList +type entry struct { + key interface{} + value interface{} +} + +// NewLRU constructs an LRU of the given size +func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { + if size <= 0 { + return nil, errors.New("Must provide a positive size") + } + c := &LRU{ + size: size, + evictList: list.New(), + items: make(map[interface{}]*list.Element), + onEvict: onEvict, + } + return c, nil +} + +// Purge is used to completely clear the cache +func (c *LRU) Purge() { + for k, v := range c.items { + if c.onEvict != nil { + c.onEvict(k, v.Value.(*entry).value) + } + delete(c.items, k) + } + c.evictList.Init() +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *LRU) Add(key, value interface{}) bool { + // Check for existing item + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + ent.Value.(*entry).value = value + return false + } + + // Add new item + ent := &entry{key, value} + entry := c.evictList.PushFront(ent) + c.items[key] = entry + + evict := c.evictList.Len() > c.size + // Verify size not exceeded + if evict { + c.removeOldest() + } + return evict +} + +// Get looks up a key's value from the cache. +func (c *LRU) Get(key interface{}) (value interface{}, ok bool) { + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + return ent.Value.(*entry).value, true + } + return +} + +// Check if a key is in the cache, without updating the recent-ness +// or deleting it for being stale. +func (c *LRU) Contains(key interface{}) (ok bool) { + _, ok = c.items[key] + return ok +} + +// Returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) { + if ent, ok := c.items[key]; ok { + return ent.Value.(*entry).value, true + } + return nil, ok +} + +// Remove removes the provided key from the cache, returning if the +// key was contained. +func (c *LRU) Remove(key interface{}) bool { + if ent, ok := c.items[key]; ok { + c.removeElement(ent) + return true + } + return false +} + +// RemoveOldest removes the oldest item from the cache. +func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) { + ent := c.evictList.Back() + if ent != nil { + c.removeElement(ent) + kv := ent.Value.(*entry) + return kv.key, kv.value, true + } + return nil, nil, false +} + +// GetOldest returns the oldest entry +func (c *LRU) GetOldest() (interface{}, interface{}, bool) { + ent := c.evictList.Back() + if ent != nil { + kv := ent.Value.(*entry) + return kv.key, kv.value, true + } + return nil, nil, false +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *LRU) Keys() []interface{} { + keys := make([]interface{}, len(c.items)) + i := 0 + for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { + keys[i] = ent.Value.(*entry).key + i++ + } + return keys +} + +// Len returns the number of items in the cache. +func (c *LRU) Len() int { + return c.evictList.Len() +} + +// removeOldest removes the oldest item from the cache. +func (c *LRU) removeOldest() { + ent := c.evictList.Back() + if ent != nil { + c.removeElement(ent) + } +} + +// removeElement is used to remove a given list element from the cache +func (c *LRU) removeElement(e *list.Element) { + c.evictList.Remove(e) + kv := e.Value.(*entry) + delete(c.items, kv.key) + if c.onEvict != nil { + c.onEvict(kv.key, kv.value) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/LICENSE new file mode 100644 index 0000000..252e3d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013, John Beisley +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. + +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 THE COPYRIGHT HOLDER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/README.md new file mode 100644 index 0000000..433ba5c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/README.md @@ -0,0 +1,44 @@ +goupnp is a UPnP client library for Go + +Installation +------------ + +Run `go get -u github.com/huin/goupnp`. + +Documentation +------------- + +Supported DCPs (you probably want to start with one of these): + +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) av1](https://godoc.org/github.com/huin/goupnp/dcps/av1) - Client for UPnP Device Control Protocol MediaServer v1 and MediaRenderer v1. +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway1](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway1) - Client for UPnP Device Control Protocol Internet Gateway Device v1. +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway2](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway2) - Client for UPnP Device Control Protocol Internet Gateway Device v2. + +Core components: + +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) (goupnp)](https://godoc.org/github.com/huin/goupnp) core library - contains datastructures and utilities typically used by the implemented DCPs. +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) httpu](https://godoc.org/github.com/huin/goupnp/httpu) HTTPU implementation, underlies SSDP. +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) ssdp](https://godoc.org/github.com/huin/goupnp/ssdp) SSDP client implementation (simple service discovery protocol) - used to discover UPnP services on a network. +* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) soap](https://godoc.org/github.com/huin/goupnp/soap) SOAP client implementation (simple object access protocol) - used to communicate with discovered services. + + +Regenerating dcps generated source code: +---------------------------------------- + +1. Install gotasks: `go get -u github.com/jingweno/gotask` +2. Change to the gotasks directory: `cd gotasks` +3. Run specgen task: `gotask specgen` + +Supporting additional UPnP devices and services: +------------------------------------------------ + +Supporting additional services is, in the trivial case, simply a matter of +adding the service to the `dcpMetadata` whitelist in `gotasks/specgen_task.go`, +regenerating the source code (see above), and committing that source code. + +However, it would be helpful if anyone needing such a service could test the +service against the service they have, and then reporting any trouble +encountered as an [issue on this +project](https://github.com/huin/goupnp/issues/new). If it just works, then +please report at least minimal working functionality as an issue, and +optionally contribute the metadata upstream. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway1/internetgateway1.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway1/internetgateway1.go new file mode 100644 index 0000000..1e0802c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway1/internetgateway1.go @@ -0,0 +1,3717 @@ +// Client for UPnP Device Control Protocol Internet Gateway Device v1. +// +// This DCP is documented in detail at: http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf +// +// Typically, use one of the New* functions to create clients for services. +package internetgateway1 + +// Generated file - do not edit by hand. See README.md + +import ( + "net/url" + "time" + + "github.com/huin/goupnp" + "github.com/huin/goupnp/soap" +) + +// Hack to avoid Go complaining if time isn't used. +var _ time.Time + +// Device URNs: +const ( + URN_LANDevice_1 = "urn:schemas-upnp-org:device:LANDevice:1" + URN_WANConnectionDevice_1 = "urn:schemas-upnp-org:device:WANConnectionDevice:1" + URN_WANDevice_1 = "urn:schemas-upnp-org:device:WANDevice:1" +) + +// Service URNs: +const ( + URN_LANHostConfigManagement_1 = "urn:schemas-upnp-org:service:LANHostConfigManagement:1" + URN_Layer3Forwarding_1 = "urn:schemas-upnp-org:service:Layer3Forwarding:1" + URN_WANCableLinkConfig_1 = "urn:schemas-upnp-org:service:WANCableLinkConfig:1" + URN_WANCommonInterfaceConfig_1 = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" + URN_WANDSLLinkConfig_1 = "urn:schemas-upnp-org:service:WANDSLLinkConfig:1" + URN_WANEthernetLinkConfig_1 = "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" + URN_WANIPConnection_1 = "urn:schemas-upnp-org:service:WANIPConnection:1" + URN_WANPOTSLinkConfig_1 = "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1" + URN_WANPPPConnection_1 = "urn:schemas-upnp-org:service:WANPPPConnection:1" +) + +// LANHostConfigManagement1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:LANHostConfigManagement:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type LANHostConfigManagement1 struct { + goupnp.ServiceClient +} + +// NewLANHostConfigManagement1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLANHostConfigManagement1Clients() (clients []*LANHostConfigManagement1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_LANHostConfigManagement_1); err != nil { + return + } + clients = newLANHostConfigManagement1ClientsFromGenericClients(genericClients) + return +} + +// NewLANHostConfigManagement1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewLANHostConfigManagement1ClientsByURL(loc *url.URL) ([]*LANHostConfigManagement1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_LANHostConfigManagement_1) + if err != nil { + return nil, err + } + return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil +} + +// NewLANHostConfigManagement1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewLANHostConfigManagement1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*LANHostConfigManagement1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_LANHostConfigManagement_1) + if err != nil { + return nil, err + } + return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil +} + +func newLANHostConfigManagement1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*LANHostConfigManagement1 { + clients := make([]*LANHostConfigManagement1, len(genericClients)) + for i := range genericClients { + clients[i] = &LANHostConfigManagement1{genericClients[i]} + } + return clients +} + +func (client *LANHostConfigManagement1) SetDHCPServerConfigurable(NewDHCPServerConfigurable bool) (err error) { + // Request structure. + request := &struct { + NewDHCPServerConfigurable string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPServerConfigurable, err = soap.MarshalBoolean(NewDHCPServerConfigurable); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDHCPServerConfigurable() (NewDHCPServerConfigurable bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPServerConfigurable string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPServerConfigurable, err = soap.UnmarshalBoolean(response.NewDHCPServerConfigurable); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDHCPRelay(NewDHCPRelay bool) (err error) { + // Request structure. + request := &struct { + NewDHCPRelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPRelay, err = soap.MarshalBoolean(NewDHCPRelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDHCPRelay() (NewDHCPRelay bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPRelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPRelay, err = soap.UnmarshalBoolean(response.NewDHCPRelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetSubnetMask(NewSubnetMask string) (err error) { + // Request structure. + request := &struct { + NewSubnetMask string + }{} + // BEGIN Marshal arguments into request. + + if request.NewSubnetMask, err = soap.MarshalString(NewSubnetMask); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetSubnetMask() (NewSubnetMask string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewSubnetMask string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewSubnetMask, err = soap.UnmarshalString(response.NewSubnetMask); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetIPRoutersList() (NewIPRouters string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIPRouters string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetIPRoutersList", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIPRouters, err = soap.UnmarshalString(response.NewIPRouters); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDomainName(NewDomainName string) (err error) { + // Request structure. + request := &struct { + NewDomainName string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDomainName, err = soap.MarshalString(NewDomainName); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDomainName() (NewDomainName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDomainName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDomainName, err = soap.UnmarshalString(response.NewDomainName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, NewMaxAddress string) (err error) { + // Request structure. + request := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewMinAddress, err = soap.MarshalString(NewMinAddress); err != nil { + return + } + if request.NewMaxAddress, err = soap.MarshalString(NewMaxAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string, NewMaxAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMinAddress, err = soap.UnmarshalString(response.NewMinAddress); err != nil { + return + } + if NewMaxAddress, err = soap.UnmarshalString(response.NewMaxAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetReservedAddresses() (NewReservedAddresses string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedAddresses string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetReservedAddresses", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedAddresses, err = soap.UnmarshalString(response.NewReservedAddresses); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDNSServers() (NewDNSServers string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDNSServers string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDNSServers", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDNSServers, err = soap.UnmarshalString(response.NewDNSServers); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Layer3Forwarding1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:Layer3Forwarding:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type Layer3Forwarding1 struct { + goupnp.ServiceClient +} + +// NewLayer3Forwarding1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLayer3Forwarding1Clients() (clients []*Layer3Forwarding1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_Layer3Forwarding_1); err != nil { + return + } + clients = newLayer3Forwarding1ClientsFromGenericClients(genericClients) + return +} + +// NewLayer3Forwarding1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewLayer3Forwarding1ClientsByURL(loc *url.URL) ([]*Layer3Forwarding1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_Layer3Forwarding_1) + if err != nil { + return nil, err + } + return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil +} + +// NewLayer3Forwarding1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewLayer3Forwarding1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*Layer3Forwarding1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_Layer3Forwarding_1) + if err != nil { + return nil, err + } + return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil +} + +func newLayer3Forwarding1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*Layer3Forwarding1 { + clients := make([]*Layer3Forwarding1, len(genericClients)) + for i := range genericClients { + clients[i] = &Layer3Forwarding1{genericClients[i]} + } + return clients +} + +func (client *Layer3Forwarding1) SetDefaultConnectionService(NewDefaultConnectionService string) (err error) { + // Request structure. + request := &struct { + NewDefaultConnectionService string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDefaultConnectionService, err = soap.MarshalString(NewDefaultConnectionService); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "SetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *Layer3Forwarding1) GetDefaultConnectionService() (NewDefaultConnectionService string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDefaultConnectionService string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "GetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDefaultConnectionService, err = soap.UnmarshalString(response.NewDefaultConnectionService); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCableLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCableLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCableLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCableLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCableLinkConfig1Clients() (clients []*WANCableLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCableLinkConfig_1); err != nil { + return + } + clients = newWANCableLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANCableLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANCableLinkConfig1ClientsByURL(loc *url.URL) ([]*WANCableLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCableLinkConfig_1) + if err != nil { + return nil, err + } + return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANCableLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANCableLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCableLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCableLinkConfig_1) + if err != nil { + return nil, err + } + return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANCableLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCableLinkConfig1 { + clients := make([]*WANCableLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCableLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Return values: +// +// * NewCableLinkConfigState: allowed values: notReady, dsSyncComplete, usParamAcquired, rangingComplete, ipComplete, todEstablished, paramTransferComplete, registrationComplete, operational, accessDenied +// +// * NewLinkType: allowed values: Ethernet +func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigState string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewCableLinkConfigState string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetCableLinkConfigInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewCableLinkConfigState, err = soap.UnmarshalString(response.NewCableLinkConfigState); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetDownstreamFrequency() (NewDownstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamFrequency, err = soap.UnmarshalUi4(response.NewDownstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewDownstreamModulation: allowed values: 64QAM, 256QAM +func (client *WANCableLinkConfig1) GetDownstreamModulation() (NewDownstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamModulation, err = soap.UnmarshalString(response.NewDownstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamFrequency() (NewUpstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamFrequency, err = soap.UnmarshalUi4(response.NewUpstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewUpstreamModulation: allowed values: QPSK, 16QAM +func (client *WANCableLinkConfig1) GetUpstreamModulation() (NewUpstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamModulation, err = soap.UnmarshalString(response.NewUpstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamChannelID() (NewUpstreamChannelID uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamChannelID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamChannelID", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamChannelID, err = soap.UnmarshalUi4(response.NewUpstreamChannelID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamPowerLevel() (NewUpstreamPowerLevel uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamPowerLevel string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamPowerLevel", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamPowerLevel, err = soap.UnmarshalUi4(response.NewUpstreamPowerLevel); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetBPIEncryptionEnabled() (NewBPIEncryptionEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewBPIEncryptionEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetBPIEncryptionEnabled", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewBPIEncryptionEnabled, err = soap.UnmarshalBoolean(response.NewBPIEncryptionEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetConfigFile() (NewConfigFile string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConfigFile string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetConfigFile", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConfigFile, err = soap.UnmarshalString(response.NewConfigFile); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetTFTPServer() (NewTFTPServer string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTFTPServer string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetTFTPServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTFTPServer, err = soap.UnmarshalString(response.NewTFTPServer); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCommonInterfaceConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCommonInterfaceConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCommonInterfaceConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCommonInterfaceConfig1Clients() (clients []*WANCommonInterfaceConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCommonInterfaceConfig_1); err != nil { + return + } + clients = newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANCommonInterfaceConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANCommonInterfaceConfig1ClientsByURL(loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCommonInterfaceConfig_1) + if err != nil { + return nil, err + } + return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANCommonInterfaceConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANCommonInterfaceConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCommonInterfaceConfig_1) + if err != nil { + return nil, err + } + return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCommonInterfaceConfig1 { + clients := make([]*WANCommonInterfaceConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCommonInterfaceConfig1{genericClients[i]} + } + return clients +} + +func (client *WANCommonInterfaceConfig1) SetEnabledForInternet(NewEnabledForInternet bool) (err error) { + // Request structure. + request := &struct { + NewEnabledForInternet string + }{} + // BEGIN Marshal arguments into request. + + if request.NewEnabledForInternet, err = soap.MarshalBoolean(NewEnabledForInternet); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "SetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetEnabledForInternet() (NewEnabledForInternet bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEnabledForInternet string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEnabledForInternet, err = soap.UnmarshalBoolean(response.NewEnabledForInternet); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewWANAccessType: allowed values: DSL, POTS, Cable, Ethernet +// +// * NewPhysicalLinkStatus: allowed values: Up, Down +func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccessType string, NewLayer1UpstreamMaxBitRate uint32, NewLayer1DownstreamMaxBitRate uint32, NewPhysicalLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessType string + + NewLayer1UpstreamMaxBitRate string + + NewLayer1DownstreamMaxBitRate string + + NewPhysicalLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetCommonLinkProperties", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessType, err = soap.UnmarshalString(response.NewWANAccessType); err != nil { + return + } + if NewLayer1UpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1UpstreamMaxBitRate); err != nil { + return + } + if NewLayer1DownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1DownstreamMaxBitRate); err != nil { + return + } + if NewPhysicalLinkStatus, err = soap.UnmarshalString(response.NewPhysicalLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetWANAccessProvider() (NewWANAccessProvider string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessProvider string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetWANAccessProvider", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessProvider, err = soap.UnmarshalString(response.NewWANAccessProvider); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewMaximumActiveConnections: allowed value range: minimum=1, step=1 +func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaximumActiveConnections uint16, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMaximumActiveConnections string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetMaximumActiveConnections", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMaximumActiveConnections, err = soap.UnmarshalUi2(response.NewMaximumActiveConnections); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalPacketsSent() (NewTotalPacketsSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsSent, err = soap.UnmarshalUi4(response.NewTotalPacketsSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalPacketsReceived() (NewTotalPacketsReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsReceived, err = soap.UnmarshalUi4(response.NewTotalPacketsReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnectionIndex uint16) (NewActiveConnDeviceContainer string, NewActiveConnectionServiceID string, err error) { + // Request structure. + request := &struct { + NewActiveConnectionIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewActiveConnectionIndex, err = soap.MarshalUi2(NewActiveConnectionIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewActiveConnDeviceContainer string + + NewActiveConnectionServiceID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetActiveConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewActiveConnDeviceContainer, err = soap.UnmarshalString(response.NewActiveConnDeviceContainer); err != nil { + return + } + if NewActiveConnectionServiceID, err = soap.UnmarshalString(response.NewActiveConnectionServiceID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANDSLLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANDSLLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANDSLLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANDSLLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANDSLLinkConfig1Clients() (clients []*WANDSLLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANDSLLinkConfig_1); err != nil { + return + } + clients = newWANDSLLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANDSLLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANDSLLinkConfig1ClientsByURL(loc *url.URL) ([]*WANDSLLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANDSLLinkConfig_1) + if err != nil { + return nil, err + } + return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANDSLLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANDSLLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANDSLLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANDSLLinkConfig_1) + if err != nil { + return nil, err + } + return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANDSLLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANDSLLinkConfig1 { + clients := make([]*WANDSLLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANDSLLinkConfig1{genericClients[i]} + } + return clients +} + +func (client *WANDSLLinkConfig1) SetDSLLinkType(NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDSLLinkType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewLinkStatus: allowed values: Up, Down +func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewLinkType string + + NewLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDSLLinkInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + if NewLinkStatus, err = soap.UnmarshalString(response.NewLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetAutoConfig() (NewAutoConfig bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoConfig string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetAutoConfig", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoConfig, err = soap.UnmarshalBoolean(response.NewAutoConfig); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetModulationType() (NewModulationType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewModulationType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetModulationType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewModulationType, err = soap.UnmarshalString(response.NewModulationType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetDestinationAddress(NewDestinationAddress string) (err error) { + // Request structure. + request := &struct { + NewDestinationAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDestinationAddress, err = soap.MarshalString(NewDestinationAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetDestinationAddress() (NewDestinationAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDestinationAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDestinationAddress, err = soap.UnmarshalString(response.NewDestinationAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetATMEncapsulation(NewATMEncapsulation string) (err error) { + // Request structure. + request := &struct { + NewATMEncapsulation string + }{} + // BEGIN Marshal arguments into request. + + if request.NewATMEncapsulation, err = soap.MarshalString(NewATMEncapsulation); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetATMEncapsulation() (NewATMEncapsulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewATMEncapsulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewATMEncapsulation, err = soap.UnmarshalString(response.NewATMEncapsulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetFCSPreserved(NewFCSPreserved bool) (err error) { + // Request structure. + request := &struct { + NewFCSPreserved string + }{} + // BEGIN Marshal arguments into request. + + if request.NewFCSPreserved, err = soap.MarshalBoolean(NewFCSPreserved); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetFCSPreserved() (NewFCSPreserved bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFCSPreserved string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFCSPreserved, err = soap.UnmarshalBoolean(response.NewFCSPreserved); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANEthernetLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANEthernetLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANEthernetLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANEthernetLinkConfig1Clients() (clients []*WANEthernetLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANEthernetLinkConfig_1); err != nil { + return + } + clients = newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANEthernetLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANEthernetLinkConfig1ClientsByURL(loc *url.URL) ([]*WANEthernetLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANEthernetLinkConfig_1) + if err != nil { + return nil, err + } + return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANEthernetLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANEthernetLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANEthernetLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANEthernetLinkConfig_1) + if err != nil { + return nil, err + } + return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANEthernetLinkConfig1 { + clients := make([]*WANEthernetLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANEthernetLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Return values: +// +// * NewEthernetLinkStatus: allowed values: Up, Down +func (client *WANEthernetLinkConfig1) GetEthernetLinkStatus() (NewEthernetLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEthernetLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANEthernetLinkConfig_1, "GetEthernetLinkStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEthernetLinkStatus, err = soap.UnmarshalString(response.NewEthernetLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection1Clients() (clients []*WANIPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_1); err != nil { + return + } + clients = newWANIPConnection1ClientsFromGenericClients(genericClients) + return +} + +// NewWANIPConnection1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANIPConnection1ClientsByURL(loc *url.URL) ([]*WANIPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANIPConnection_1) + if err != nil { + return nil, err + } + return newWANIPConnection1ClientsFromGenericClients(genericClients), nil +} + +// NewWANIPConnection1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANIPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANIPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANIPConnection_1) + if err != nil { + return nil, err + } + return newWANIPConnection1ClientsFromGenericClients(genericClients), nil +} + +func newWANIPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANIPConnection1 { + clients := make([]*WANIPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection1{genericClients[i]} + } + return clients +} + +func (client *WANIPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, IP_Bridged +func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewProtocol: allowed values: TCP, UDP +func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPOTSLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPOTSLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANPOTSLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPOTSLinkConfig1Clients() (clients []*WANPOTSLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPOTSLinkConfig_1); err != nil { + return + } + clients = newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANPOTSLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANPOTSLinkConfig1ClientsByURL(loc *url.URL) ([]*WANPOTSLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPOTSLinkConfig_1) + if err != nil { + return nil, err + } + return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANPOTSLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANPOTSLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPOTSLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPOTSLinkConfig_1) + if err != nil { + return nil, err + } + return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPOTSLinkConfig1 { + clients := make([]*WANPOTSLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPOTSLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Arguments: +// +// * NewLinkType: allowed values: PPP_Dialup + +func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInfo string, NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewISPPhoneNumber, err = soap.MarshalString(NewISPPhoneNumber); err != nil { + return + } + if request.NewISPInfo, err = soap.MarshalString(NewISPInfo); err != nil { + return + } + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, NewDelayBetweenRetries uint32) (err error) { + // Request structure. + request := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + // BEGIN Marshal arguments into request. + + if request.NewNumberOfRetries, err = soap.MarshalUi4(NewNumberOfRetries); err != nil { + return + } + if request.NewDelayBetweenRetries, err = soap.MarshalUi4(NewDelayBetweenRetries); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewLinkType: allowed values: PPP_Dialup +func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISPInfo string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewISPPhoneNumber, err = soap.UnmarshalString(response.NewISPPhoneNumber); err != nil { + return + } + if NewISPInfo, err = soap.UnmarshalString(response.NewISPInfo); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32, NewDelayBetweenRetries uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewNumberOfRetries, err = soap.UnmarshalUi4(response.NewNumberOfRetries); err != nil { + return + } + if NewDelayBetweenRetries, err = soap.UnmarshalUi4(response.NewDelayBetweenRetries); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetFclass() (NewFclass string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFclass string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetFclass", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFclass, err = soap.UnmarshalString(response.NewFclass); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataModulationSupported() (NewDataModulationSupported string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataModulationSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataModulationSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataModulationSupported, err = soap.UnmarshalString(response.NewDataModulationSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataProtocol() (NewDataProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataProtocol, err = soap.UnmarshalString(response.NewDataProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataCompression() (NewDataCompression string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataCompression string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataCompression", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataCompression, err = soap.UnmarshalString(response.NewDataCompression); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetPlusVTRCommandSupported() (NewPlusVTRCommandSupported bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPlusVTRCommandSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetPlusVTRCommandSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPlusVTRCommandSupported, err = soap.UnmarshalBoolean(response.NewPlusVTRCommandSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPPPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPPPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPPPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANPPPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPPPConnection1Clients() (clients []*WANPPPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPPPConnection_1); err != nil { + return + } + clients = newWANPPPConnection1ClientsFromGenericClients(genericClients) + return +} + +// NewWANPPPConnection1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANPPPConnection1ClientsByURL(loc *url.URL) ([]*WANPPPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPPPConnection_1) + if err != nil { + return nil, err + } + return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil +} + +// NewWANPPPConnection1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANPPPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPPPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPPPConnection_1) + if err != nil { + return nil, err + } + return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil +} + +func newWANPPPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPPPConnection1 { + clients := make([]*WANPPPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPPPConnection1{genericClients[i]} + } + return clients +} + +func (client *WANPPPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, DHCP_Spoofed, PPPoE_Bridged, PPTP_Relay, L2TP_Relay, PPPoE_Relay +func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPassword string) (err error) { + // Request structure. + request := &struct { + NewUserName string + + NewPassword string + }{} + // BEGIN Marshal arguments into request. + + if request.NewUserName, err = soap.MarshalString(NewUserName); err != nil { + return + } + if request.NewPassword, err = soap.MarshalString(NewPassword); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ConfigureConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRate uint32, NewDownstreamMaxBitRate uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamMaxBitRate string + + NewDownstreamMaxBitRate string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetLinkLayerMaxBitRates", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewUpstreamMaxBitRate); err != nil { + return + } + if NewDownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewDownstreamMaxBitRate); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPEncryptionProtocol() (NewPPPEncryptionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPEncryptionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPEncryptionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPEncryptionProtocol, err = soap.UnmarshalString(response.NewPPPEncryptionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPCompressionProtocol() (NewPPPCompressionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPCompressionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPCompressionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPCompressionProtocol, err = soap.UnmarshalString(response.NewPPPCompressionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPAuthenticationProtocol() (NewPPPAuthenticationProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPAuthenticationProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPAuthenticationProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPAuthenticationProtocol, err = soap.UnmarshalString(response.NewPPPAuthenticationProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetUserName() (NewUserName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUserName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetUserName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUserName, err = soap.UnmarshalString(response.NewUserName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPassword() (NewPassword string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPassword string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPassword", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPassword, err = soap.UnmarshalString(response.NewPassword); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewProtocol: allowed values: TCP, UDP +func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway2/internetgateway2.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway2/internetgateway2.go new file mode 100644 index 0000000..2d67a4a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/dcps/internetgateway2/internetgateway2.go @@ -0,0 +1,5378 @@ +// Client for UPnP Device Control Protocol Internet Gateway Device v2. +// +// This DCP is documented in detail at: http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf +// +// Typically, use one of the New* functions to create clients for services. +package internetgateway2 + +// Generated file - do not edit by hand. See README.md + +import ( + "net/url" + "time" + + "github.com/huin/goupnp" + "github.com/huin/goupnp/soap" +) + +// Hack to avoid Go complaining if time isn't used. +var _ time.Time + +// Device URNs: +const ( + URN_LANDevice_1 = "urn:schemas-upnp-org:device:LANDevice:1" + URN_WANConnectionDevice_1 = "urn:schemas-upnp-org:device:WANConnectionDevice:1" + URN_WANConnectionDevice_2 = "urn:schemas-upnp-org:device:WANConnectionDevice:2" + URN_WANDevice_1 = "urn:schemas-upnp-org:device:WANDevice:1" + URN_WANDevice_2 = "urn:schemas-upnp-org:device:WANDevice:2" +) + +// Service URNs: +const ( + URN_DeviceProtection_1 = "urn:schemas-upnp-org:service:DeviceProtection:1" + URN_LANHostConfigManagement_1 = "urn:schemas-upnp-org:service:LANHostConfigManagement:1" + URN_Layer3Forwarding_1 = "urn:schemas-upnp-org:service:Layer3Forwarding:1" + URN_WANCableLinkConfig_1 = "urn:schemas-upnp-org:service:WANCableLinkConfig:1" + URN_WANCommonInterfaceConfig_1 = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" + URN_WANDSLLinkConfig_1 = "urn:schemas-upnp-org:service:WANDSLLinkConfig:1" + URN_WANEthernetLinkConfig_1 = "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" + URN_WANIPConnection_1 = "urn:schemas-upnp-org:service:WANIPConnection:1" + URN_WANIPConnection_2 = "urn:schemas-upnp-org:service:WANIPConnection:2" + URN_WANIPv6FirewallControl_1 = "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" + URN_WANPOTSLinkConfig_1 = "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1" + URN_WANPPPConnection_1 = "urn:schemas-upnp-org:service:WANPPPConnection:1" +) + +// DeviceProtection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:DeviceProtection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type DeviceProtection1 struct { + goupnp.ServiceClient +} + +// NewDeviceProtection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewDeviceProtection1Clients() (clients []*DeviceProtection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_DeviceProtection_1); err != nil { + return + } + clients = newDeviceProtection1ClientsFromGenericClients(genericClients) + return +} + +// NewDeviceProtection1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewDeviceProtection1ClientsByURL(loc *url.URL) ([]*DeviceProtection1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_DeviceProtection_1) + if err != nil { + return nil, err + } + return newDeviceProtection1ClientsFromGenericClients(genericClients), nil +} + +// NewDeviceProtection1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewDeviceProtection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*DeviceProtection1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_DeviceProtection_1) + if err != nil { + return nil, err + } + return newDeviceProtection1ClientsFromGenericClients(genericClients), nil +} + +func newDeviceProtection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*DeviceProtection1 { + clients := make([]*DeviceProtection1, len(genericClients)) + for i := range genericClients { + clients[i] = &DeviceProtection1{genericClients[i]} + } + return clients +} + +func (client *DeviceProtection1) SendSetupMessage(ProtocolType string, InMessage []byte) (OutMessage []byte, err error) { + // Request structure. + request := &struct { + ProtocolType string + + InMessage string + }{} + // BEGIN Marshal arguments into request. + + if request.ProtocolType, err = soap.MarshalString(ProtocolType); err != nil { + return + } + if request.InMessage, err = soap.MarshalBinBase64(InMessage); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + OutMessage string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "SendSetupMessage", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if OutMessage, err = soap.UnmarshalBinBase64(response.OutMessage); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) GetSupportedProtocols() (ProtocolList string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + ProtocolList string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "GetSupportedProtocols", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if ProtocolList, err = soap.UnmarshalString(response.ProtocolList); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) GetAssignedRoles() (RoleList string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + RoleList string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "GetAssignedRoles", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if RoleList, err = soap.UnmarshalString(response.RoleList); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) GetRolesForAction(DeviceUDN string, ServiceId string, ActionName string) (RoleList string, RestrictedRoleList string, err error) { + // Request structure. + request := &struct { + DeviceUDN string + + ServiceId string + + ActionName string + }{} + // BEGIN Marshal arguments into request. + + if request.DeviceUDN, err = soap.MarshalString(DeviceUDN); err != nil { + return + } + if request.ServiceId, err = soap.MarshalString(ServiceId); err != nil { + return + } + if request.ActionName, err = soap.MarshalString(ActionName); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + RoleList string + + RestrictedRoleList string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "GetRolesForAction", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if RoleList, err = soap.UnmarshalString(response.RoleList); err != nil { + return + } + if RestrictedRoleList, err = soap.UnmarshalString(response.RestrictedRoleList); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) GetUserLoginChallenge(ProtocolType string, Name string) (Salt []byte, Challenge []byte, err error) { + // Request structure. + request := &struct { + ProtocolType string + + Name string + }{} + // BEGIN Marshal arguments into request. + + if request.ProtocolType, err = soap.MarshalString(ProtocolType); err != nil { + return + } + if request.Name, err = soap.MarshalString(Name); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + Salt string + + Challenge string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "GetUserLoginChallenge", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if Salt, err = soap.UnmarshalBinBase64(response.Salt); err != nil { + return + } + if Challenge, err = soap.UnmarshalBinBase64(response.Challenge); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) UserLogin(ProtocolType string, Challenge []byte, Authenticator []byte) (err error) { + // Request structure. + request := &struct { + ProtocolType string + + Challenge string + + Authenticator string + }{} + // BEGIN Marshal arguments into request. + + if request.ProtocolType, err = soap.MarshalString(ProtocolType); err != nil { + return + } + if request.Challenge, err = soap.MarshalBinBase64(Challenge); err != nil { + return + } + if request.Authenticator, err = soap.MarshalBinBase64(Authenticator); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "UserLogin", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) UserLogout() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "UserLogout", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) GetACLData() (ACL string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + ACL string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "GetACLData", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if ACL, err = soap.UnmarshalString(response.ACL); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) AddIdentityList(IdentityList string) (IdentityListResult string, err error) { + // Request structure. + request := &struct { + IdentityList string + }{} + // BEGIN Marshal arguments into request. + + if request.IdentityList, err = soap.MarshalString(IdentityList); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + IdentityListResult string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "AddIdentityList", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if IdentityListResult, err = soap.UnmarshalString(response.IdentityListResult); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) RemoveIdentity(Identity string) (err error) { + // Request structure. + request := &struct { + Identity string + }{} + // BEGIN Marshal arguments into request. + + if request.Identity, err = soap.MarshalString(Identity); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "RemoveIdentity", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) SetUserLoginPassword(ProtocolType string, Name string, Stored []byte, Salt []byte) (err error) { + // Request structure. + request := &struct { + ProtocolType string + + Name string + + Stored string + + Salt string + }{} + // BEGIN Marshal arguments into request. + + if request.ProtocolType, err = soap.MarshalString(ProtocolType); err != nil { + return + } + if request.Name, err = soap.MarshalString(Name); err != nil { + return + } + if request.Stored, err = soap.MarshalBinBase64(Stored); err != nil { + return + } + if request.Salt, err = soap.MarshalBinBase64(Salt); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "SetUserLoginPassword", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) AddRolesForIdentity(Identity string, RoleList string) (err error) { + // Request structure. + request := &struct { + Identity string + + RoleList string + }{} + // BEGIN Marshal arguments into request. + + if request.Identity, err = soap.MarshalString(Identity); err != nil { + return + } + if request.RoleList, err = soap.MarshalString(RoleList); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "AddRolesForIdentity", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *DeviceProtection1) RemoveRolesForIdentity(Identity string, RoleList string) (err error) { + // Request structure. + request := &struct { + Identity string + + RoleList string + }{} + // BEGIN Marshal arguments into request. + + if request.Identity, err = soap.MarshalString(Identity); err != nil { + return + } + if request.RoleList, err = soap.MarshalString(RoleList); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_DeviceProtection_1, "RemoveRolesForIdentity", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// LANHostConfigManagement1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:LANHostConfigManagement:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type LANHostConfigManagement1 struct { + goupnp.ServiceClient +} + +// NewLANHostConfigManagement1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLANHostConfigManagement1Clients() (clients []*LANHostConfigManagement1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_LANHostConfigManagement_1); err != nil { + return + } + clients = newLANHostConfigManagement1ClientsFromGenericClients(genericClients) + return +} + +// NewLANHostConfigManagement1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewLANHostConfigManagement1ClientsByURL(loc *url.URL) ([]*LANHostConfigManagement1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_LANHostConfigManagement_1) + if err != nil { + return nil, err + } + return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil +} + +// NewLANHostConfigManagement1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewLANHostConfigManagement1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*LANHostConfigManagement1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_LANHostConfigManagement_1) + if err != nil { + return nil, err + } + return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil +} + +func newLANHostConfigManagement1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*LANHostConfigManagement1 { + clients := make([]*LANHostConfigManagement1, len(genericClients)) + for i := range genericClients { + clients[i] = &LANHostConfigManagement1{genericClients[i]} + } + return clients +} + +func (client *LANHostConfigManagement1) SetDHCPServerConfigurable(NewDHCPServerConfigurable bool) (err error) { + // Request structure. + request := &struct { + NewDHCPServerConfigurable string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPServerConfigurable, err = soap.MarshalBoolean(NewDHCPServerConfigurable); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDHCPServerConfigurable() (NewDHCPServerConfigurable bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPServerConfigurable string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPServerConfigurable", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPServerConfigurable, err = soap.UnmarshalBoolean(response.NewDHCPServerConfigurable); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDHCPRelay(NewDHCPRelay bool) (err error) { + // Request structure. + request := &struct { + NewDHCPRelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDHCPRelay, err = soap.MarshalBoolean(NewDHCPRelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDHCPRelay() (NewDHCPRelay bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDHCPRelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPRelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDHCPRelay, err = soap.UnmarshalBoolean(response.NewDHCPRelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetSubnetMask(NewSubnetMask string) (err error) { + // Request structure. + request := &struct { + NewSubnetMask string + }{} + // BEGIN Marshal arguments into request. + + if request.NewSubnetMask, err = soap.MarshalString(NewSubnetMask); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetSubnetMask() (NewSubnetMask string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewSubnetMask string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetSubnetMask", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewSubnetMask, err = soap.UnmarshalString(response.NewSubnetMask); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteIPRouter(NewIPRouters string) (err error) { + // Request structure. + request := &struct { + NewIPRouters string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteIPRouter", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetIPRoutersList() (NewIPRouters string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIPRouters string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetIPRoutersList", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIPRouters, err = soap.UnmarshalString(response.NewIPRouters); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDomainName(NewDomainName string) (err error) { + // Request structure. + request := &struct { + NewDomainName string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDomainName, err = soap.MarshalString(NewDomainName); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDomainName() (NewDomainName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDomainName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDomainName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDomainName, err = soap.UnmarshalString(response.NewDomainName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, NewMaxAddress string) (err error) { + // Request structure. + request := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewMinAddress, err = soap.MarshalString(NewMinAddress); err != nil { + return + } + if request.NewMaxAddress, err = soap.MarshalString(NewMaxAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string, NewMaxAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMinAddress string + + NewMaxAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetAddressRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMinAddress, err = soap.UnmarshalString(response.NewMinAddress); err != nil { + return + } + if NewMaxAddress, err = soap.UnmarshalString(response.NewMaxAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteReservedAddress(NewReservedAddresses string) (err error) { + // Request structure. + request := &struct { + NewReservedAddresses string + }{} + // BEGIN Marshal arguments into request. + + if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteReservedAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetReservedAddresses() (NewReservedAddresses string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedAddresses string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetReservedAddresses", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedAddresses, err = soap.UnmarshalString(response.NewReservedAddresses); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) SetDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) DeleteDNSServer(NewDNSServers string) (err error) { + // Request structure. + request := &struct { + NewDNSServers string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteDNSServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *LANHostConfigManagement1) GetDNSServers() (NewDNSServers string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDNSServers string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDNSServers", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDNSServers, err = soap.UnmarshalString(response.NewDNSServers); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// Layer3Forwarding1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:Layer3Forwarding:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type Layer3Forwarding1 struct { + goupnp.ServiceClient +} + +// NewLayer3Forwarding1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewLayer3Forwarding1Clients() (clients []*Layer3Forwarding1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_Layer3Forwarding_1); err != nil { + return + } + clients = newLayer3Forwarding1ClientsFromGenericClients(genericClients) + return +} + +// NewLayer3Forwarding1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewLayer3Forwarding1ClientsByURL(loc *url.URL) ([]*Layer3Forwarding1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_Layer3Forwarding_1) + if err != nil { + return nil, err + } + return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil +} + +// NewLayer3Forwarding1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewLayer3Forwarding1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*Layer3Forwarding1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_Layer3Forwarding_1) + if err != nil { + return nil, err + } + return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil +} + +func newLayer3Forwarding1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*Layer3Forwarding1 { + clients := make([]*Layer3Forwarding1, len(genericClients)) + for i := range genericClients { + clients[i] = &Layer3Forwarding1{genericClients[i]} + } + return clients +} + +func (client *Layer3Forwarding1) SetDefaultConnectionService(NewDefaultConnectionService string) (err error) { + // Request structure. + request := &struct { + NewDefaultConnectionService string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDefaultConnectionService, err = soap.MarshalString(NewDefaultConnectionService); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "SetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *Layer3Forwarding1) GetDefaultConnectionService() (NewDefaultConnectionService string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDefaultConnectionService string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "GetDefaultConnectionService", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDefaultConnectionService, err = soap.UnmarshalString(response.NewDefaultConnectionService); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCableLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCableLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCableLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCableLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCableLinkConfig1Clients() (clients []*WANCableLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCableLinkConfig_1); err != nil { + return + } + clients = newWANCableLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANCableLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANCableLinkConfig1ClientsByURL(loc *url.URL) ([]*WANCableLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCableLinkConfig_1) + if err != nil { + return nil, err + } + return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANCableLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANCableLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCableLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCableLinkConfig_1) + if err != nil { + return nil, err + } + return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANCableLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCableLinkConfig1 { + clients := make([]*WANCableLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCableLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Return values: +// +// * NewCableLinkConfigState: allowed values: notReady, dsSyncComplete, usParamAcquired, rangingComplete, ipComplete, todEstablished, paramTransferComplete, registrationComplete, operational, accessDenied +// +// * NewLinkType: allowed values: Ethernet +func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigState string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewCableLinkConfigState string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetCableLinkConfigInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewCableLinkConfigState, err = soap.UnmarshalString(response.NewCableLinkConfigState); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetDownstreamFrequency() (NewDownstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamFrequency, err = soap.UnmarshalUi4(response.NewDownstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewDownstreamModulation: allowed values: 64QAM, 256QAM +func (client *WANCableLinkConfig1) GetDownstreamModulation() (NewDownstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDownstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDownstreamModulation, err = soap.UnmarshalString(response.NewDownstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamFrequency() (NewUpstreamFrequency uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamFrequency string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamFrequency", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamFrequency, err = soap.UnmarshalUi4(response.NewUpstreamFrequency); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewUpstreamModulation: allowed values: QPSK, 16QAM +func (client *WANCableLinkConfig1) GetUpstreamModulation() (NewUpstreamModulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamModulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamModulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamModulation, err = soap.UnmarshalString(response.NewUpstreamModulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamChannelID() (NewUpstreamChannelID uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamChannelID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamChannelID", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamChannelID, err = soap.UnmarshalUi4(response.NewUpstreamChannelID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetUpstreamPowerLevel() (NewUpstreamPowerLevel uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamPowerLevel string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamPowerLevel", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamPowerLevel, err = soap.UnmarshalUi4(response.NewUpstreamPowerLevel); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetBPIEncryptionEnabled() (NewBPIEncryptionEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewBPIEncryptionEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetBPIEncryptionEnabled", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewBPIEncryptionEnabled, err = soap.UnmarshalBoolean(response.NewBPIEncryptionEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetConfigFile() (NewConfigFile string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConfigFile string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetConfigFile", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConfigFile, err = soap.UnmarshalString(response.NewConfigFile); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCableLinkConfig1) GetTFTPServer() (NewTFTPServer string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTFTPServer string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetTFTPServer", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTFTPServer, err = soap.UnmarshalString(response.NewTFTPServer); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANCommonInterfaceConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANCommonInterfaceConfig1 struct { + goupnp.ServiceClient +} + +// NewWANCommonInterfaceConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANCommonInterfaceConfig1Clients() (clients []*WANCommonInterfaceConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCommonInterfaceConfig_1); err != nil { + return + } + clients = newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANCommonInterfaceConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANCommonInterfaceConfig1ClientsByURL(loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCommonInterfaceConfig_1) + if err != nil { + return nil, err + } + return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANCommonInterfaceConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANCommonInterfaceConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCommonInterfaceConfig_1) + if err != nil { + return nil, err + } + return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCommonInterfaceConfig1 { + clients := make([]*WANCommonInterfaceConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANCommonInterfaceConfig1{genericClients[i]} + } + return clients +} + +func (client *WANCommonInterfaceConfig1) SetEnabledForInternet(NewEnabledForInternet bool) (err error) { + // Request structure. + request := &struct { + NewEnabledForInternet string + }{} + // BEGIN Marshal arguments into request. + + if request.NewEnabledForInternet, err = soap.MarshalBoolean(NewEnabledForInternet); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "SetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetEnabledForInternet() (NewEnabledForInternet bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEnabledForInternet string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetEnabledForInternet", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEnabledForInternet, err = soap.UnmarshalBoolean(response.NewEnabledForInternet); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewWANAccessType: allowed values: DSL, POTS, Cable, Ethernet +// +// * NewPhysicalLinkStatus: allowed values: Up, Down +func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccessType string, NewLayer1UpstreamMaxBitRate uint32, NewLayer1DownstreamMaxBitRate uint32, NewPhysicalLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessType string + + NewLayer1UpstreamMaxBitRate string + + NewLayer1DownstreamMaxBitRate string + + NewPhysicalLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetCommonLinkProperties", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessType, err = soap.UnmarshalString(response.NewWANAccessType); err != nil { + return + } + if NewLayer1UpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1UpstreamMaxBitRate); err != nil { + return + } + if NewLayer1DownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1DownstreamMaxBitRate); err != nil { + return + } + if NewPhysicalLinkStatus, err = soap.UnmarshalString(response.NewPhysicalLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetWANAccessProvider() (NewWANAccessProvider string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWANAccessProvider string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetWANAccessProvider", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWANAccessProvider, err = soap.UnmarshalString(response.NewWANAccessProvider); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewMaximumActiveConnections: allowed value range: minimum=1, step=1 +func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaximumActiveConnections uint16, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewMaximumActiveConnections string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetMaximumActiveConnections", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewMaximumActiveConnections, err = soap.UnmarshalUi2(response.NewMaximumActiveConnections); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalBytesReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalPacketsSent() (NewTotalPacketsSent uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsSent string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsSent", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsSent, err = soap.UnmarshalUi4(response.NewTotalPacketsSent); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetTotalPacketsReceived() (NewTotalPacketsReceived uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewTotalPacketsReceived string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsReceived", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewTotalPacketsReceived, err = soap.UnmarshalUi4(response.NewTotalPacketsReceived); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnectionIndex uint16) (NewActiveConnDeviceContainer string, NewActiveConnectionServiceID string, err error) { + // Request structure. + request := &struct { + NewActiveConnectionIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewActiveConnectionIndex, err = soap.MarshalUi2(NewActiveConnectionIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewActiveConnDeviceContainer string + + NewActiveConnectionServiceID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetActiveConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewActiveConnDeviceContainer, err = soap.UnmarshalString(response.NewActiveConnDeviceContainer); err != nil { + return + } + if NewActiveConnectionServiceID, err = soap.UnmarshalString(response.NewActiveConnectionServiceID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANDSLLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANDSLLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANDSLLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANDSLLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANDSLLinkConfig1Clients() (clients []*WANDSLLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANDSLLinkConfig_1); err != nil { + return + } + clients = newWANDSLLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANDSLLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANDSLLinkConfig1ClientsByURL(loc *url.URL) ([]*WANDSLLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANDSLLinkConfig_1) + if err != nil { + return nil, err + } + return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANDSLLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANDSLLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANDSLLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANDSLLinkConfig_1) + if err != nil { + return nil, err + } + return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANDSLLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANDSLLinkConfig1 { + clients := make([]*WANDSLLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANDSLLinkConfig1{genericClients[i]} + } + return clients +} + +func (client *WANDSLLinkConfig1) SetDSLLinkType(NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDSLLinkType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewLinkStatus: allowed values: Up, Down +func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewLinkType string + + NewLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDSLLinkInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + if NewLinkStatus, err = soap.UnmarshalString(response.NewLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetAutoConfig() (NewAutoConfig bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoConfig string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetAutoConfig", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoConfig, err = soap.UnmarshalBoolean(response.NewAutoConfig); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetModulationType() (NewModulationType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewModulationType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetModulationType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewModulationType, err = soap.UnmarshalString(response.NewModulationType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetDestinationAddress(NewDestinationAddress string) (err error) { + // Request structure. + request := &struct { + NewDestinationAddress string + }{} + // BEGIN Marshal arguments into request. + + if request.NewDestinationAddress, err = soap.MarshalString(NewDestinationAddress); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetDestinationAddress() (NewDestinationAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDestinationAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDestinationAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDestinationAddress, err = soap.UnmarshalString(response.NewDestinationAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetATMEncapsulation(NewATMEncapsulation string) (err error) { + // Request structure. + request := &struct { + NewATMEncapsulation string + }{} + // BEGIN Marshal arguments into request. + + if request.NewATMEncapsulation, err = soap.MarshalString(NewATMEncapsulation); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetATMEncapsulation() (NewATMEncapsulation string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewATMEncapsulation string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetATMEncapsulation", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewATMEncapsulation, err = soap.UnmarshalString(response.NewATMEncapsulation); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) SetFCSPreserved(NewFCSPreserved bool) (err error) { + // Request structure. + request := &struct { + NewFCSPreserved string + }{} + // BEGIN Marshal arguments into request. + + if request.NewFCSPreserved, err = soap.MarshalBoolean(NewFCSPreserved); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANDSLLinkConfig1) GetFCSPreserved() (NewFCSPreserved bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFCSPreserved string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetFCSPreserved", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFCSPreserved, err = soap.UnmarshalBoolean(response.NewFCSPreserved); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANEthernetLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANEthernetLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANEthernetLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANEthernetLinkConfig1Clients() (clients []*WANEthernetLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANEthernetLinkConfig_1); err != nil { + return + } + clients = newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANEthernetLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANEthernetLinkConfig1ClientsByURL(loc *url.URL) ([]*WANEthernetLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANEthernetLinkConfig_1) + if err != nil { + return nil, err + } + return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANEthernetLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANEthernetLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANEthernetLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANEthernetLinkConfig_1) + if err != nil { + return nil, err + } + return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANEthernetLinkConfig1 { + clients := make([]*WANEthernetLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANEthernetLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Return values: +// +// * NewEthernetLinkStatus: allowed values: Up, Down +func (client *WANEthernetLinkConfig1) GetEthernetLinkStatus() (NewEthernetLinkStatus string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewEthernetLinkStatus string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANEthernetLinkConfig_1, "GetEthernetLinkStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewEthernetLinkStatus, err = soap.UnmarshalString(response.NewEthernetLinkStatus); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection1Clients() (clients []*WANIPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_1); err != nil { + return + } + clients = newWANIPConnection1ClientsFromGenericClients(genericClients) + return +} + +// NewWANIPConnection1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANIPConnection1ClientsByURL(loc *url.URL) ([]*WANIPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANIPConnection_1) + if err != nil { + return nil, err + } + return newWANIPConnection1ClientsFromGenericClients(genericClients), nil +} + +// NewWANIPConnection1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANIPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANIPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANIPConnection_1) + if err != nil { + return nil, err + } + return newWANIPConnection1ClientsFromGenericClients(genericClients), nil +} + +func newWANIPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANIPConnection1 { + clients := make([]*WANIPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection1{genericClients[i]} + } + return clients +} + +func (client *WANIPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, IP_Bridged +func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewProtocol: allowed values: TCP, UDP +func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPConnection2 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:2". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPConnection2 struct { + goupnp.ServiceClient +} + +// NewWANIPConnection2Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPConnection2Clients() (clients []*WANIPConnection2, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPConnection_2); err != nil { + return + } + clients = newWANIPConnection2ClientsFromGenericClients(genericClients) + return +} + +// NewWANIPConnection2ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANIPConnection2ClientsByURL(loc *url.URL) ([]*WANIPConnection2, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANIPConnection_2) + if err != nil { + return nil, err + } + return newWANIPConnection2ClientsFromGenericClients(genericClients), nil +} + +// NewWANIPConnection2ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANIPConnection2ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANIPConnection2, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANIPConnection_2) + if err != nil { + return nil, err + } + return newWANIPConnection2ClientsFromGenericClients(genericClients), nil +} + +func newWANIPConnection2ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANIPConnection2 { + clients := make([]*WANIPConnection2, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPConnection2{genericClients[i]} + } + return clients +} + +func (client *WANIPConnection2) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connecting, Connected, PendingDisconnect, Disconnecting, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE, ERROR_COMMAND_ABORTED, ERROR_NOT_ENABLED_FOR_INTERNET, ERROR_USER_DISCONNECT, ERROR_ISP_DISCONNECT, ERROR_IDLE_DISCONNECT, ERROR_FORCED_DISCONNECT, ERROR_NO_CARRIER, ERROR_IP_CONFIGURATION, ERROR_UNKNOWN +func (client *WANIPConnection2) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewProtocol: allowed values: TCP, UDP +func (client *WANIPConnection2) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) DeletePortMappingRange(NewStartPort uint16, NewEndPort uint16, NewProtocol string, NewManage bool) (err error) { + // Request structure. + request := &struct { + NewStartPort string + + NewEndPort string + + NewProtocol string + + NewManage string + }{} + // BEGIN Marshal arguments into request. + + if request.NewStartPort, err = soap.MarshalUi2(NewStartPort); err != nil { + return + } + if request.NewEndPort, err = soap.MarshalUi2(NewEndPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewManage, err = soap.MarshalBoolean(NewManage); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "DeletePortMappingRange", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPConnection2) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) GetListOfPortMappings(NewStartPort uint16, NewEndPort uint16, NewProtocol string, NewManage bool, NewNumberOfPorts uint16) (NewPortListing string, err error) { + // Request structure. + request := &struct { + NewStartPort string + + NewEndPort string + + NewProtocol string + + NewManage string + + NewNumberOfPorts string + }{} + // BEGIN Marshal arguments into request. + + if request.NewStartPort, err = soap.MarshalUi2(NewStartPort); err != nil { + return + } + if request.NewEndPort, err = soap.MarshalUi2(NewEndPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewManage, err = soap.MarshalBoolean(NewManage); err != nil { + return + } + if request.NewNumberOfPorts, err = soap.MarshalUi2(NewNumberOfPorts); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPortListing string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "GetListOfPortMappings", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPortListing, err = soap.UnmarshalString(response.NewPortListing); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANIPConnection2) AddAnyPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (NewReservedPort uint16, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewReservedPort string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPConnection_2, "AddAnyPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewReservedPort, err = soap.UnmarshalUi2(response.NewReservedPort); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANIPv6FirewallControl1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANIPv6FirewallControl1 struct { + goupnp.ServiceClient +} + +// NewWANIPv6FirewallControl1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANIPv6FirewallControl1Clients() (clients []*WANIPv6FirewallControl1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANIPv6FirewallControl_1); err != nil { + return + } + clients = newWANIPv6FirewallControl1ClientsFromGenericClients(genericClients) + return +} + +// NewWANIPv6FirewallControl1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANIPv6FirewallControl1ClientsByURL(loc *url.URL) ([]*WANIPv6FirewallControl1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANIPv6FirewallControl_1) + if err != nil { + return nil, err + } + return newWANIPv6FirewallControl1ClientsFromGenericClients(genericClients), nil +} + +// NewWANIPv6FirewallControl1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANIPv6FirewallControl1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANIPv6FirewallControl1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANIPv6FirewallControl_1) + if err != nil { + return nil, err + } + return newWANIPv6FirewallControl1ClientsFromGenericClients(genericClients), nil +} + +func newWANIPv6FirewallControl1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANIPv6FirewallControl1 { + clients := make([]*WANIPv6FirewallControl1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANIPv6FirewallControl1{genericClients[i]} + } + return clients +} + +func (client *WANIPv6FirewallControl1) GetFirewallStatus() (FirewallEnabled bool, InboundPinholeAllowed bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + FirewallEnabled string + + InboundPinholeAllowed string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetFirewallStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if FirewallEnabled, err = soap.UnmarshalBoolean(response.FirewallEnabled); err != nil { + return + } + if InboundPinholeAllowed, err = soap.UnmarshalBoolean(response.InboundPinholeAllowed); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPv6FirewallControl1) GetOutboundPinholeTimeout(RemoteHost string, RemotePort uint16, InternalClient string, InternalPort uint16, Protocol uint16) (OutboundPinholeTimeout uint32, err error) { + // Request structure. + request := &struct { + RemoteHost string + + RemotePort string + + InternalClient string + + InternalPort string + + Protocol string + }{} + // BEGIN Marshal arguments into request. + + if request.RemoteHost, err = soap.MarshalString(RemoteHost); err != nil { + return + } + if request.RemotePort, err = soap.MarshalUi2(RemotePort); err != nil { + return + } + if request.InternalClient, err = soap.MarshalString(InternalClient); err != nil { + return + } + if request.InternalPort, err = soap.MarshalUi2(InternalPort); err != nil { + return + } + if request.Protocol, err = soap.MarshalUi2(Protocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + OutboundPinholeTimeout string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetOutboundPinholeTimeout", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if OutboundPinholeTimeout, err = soap.UnmarshalUi4(response.OutboundPinholeTimeout); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * LeaseTime: allowed value range: minimum=1, maximum=86400 + +func (client *WANIPv6FirewallControl1) AddPinhole(RemoteHost string, RemotePort uint16, InternalClient string, InternalPort uint16, Protocol uint16, LeaseTime uint32) (UniqueID uint16, err error) { + // Request structure. + request := &struct { + RemoteHost string + + RemotePort string + + InternalClient string + + InternalPort string + + Protocol string + + LeaseTime string + }{} + // BEGIN Marshal arguments into request. + + if request.RemoteHost, err = soap.MarshalString(RemoteHost); err != nil { + return + } + if request.RemotePort, err = soap.MarshalUi2(RemotePort); err != nil { + return + } + if request.InternalClient, err = soap.MarshalString(InternalClient); err != nil { + return + } + if request.InternalPort, err = soap.MarshalUi2(InternalPort); err != nil { + return + } + if request.Protocol, err = soap.MarshalUi2(Protocol); err != nil { + return + } + if request.LeaseTime, err = soap.MarshalUi4(LeaseTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + UniqueID string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "AddPinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if UniqueID, err = soap.UnmarshalUi2(response.UniqueID); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewLeaseTime: allowed value range: minimum=1, maximum=86400 + +func (client *WANIPv6FirewallControl1) UpdatePinhole(UniqueID uint16, NewLeaseTime uint32) (err error) { + // Request structure. + request := &struct { + UniqueID string + + NewLeaseTime string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + if request.NewLeaseTime, err = soap.MarshalUi4(NewLeaseTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "UpdatePinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPv6FirewallControl1) DeletePinhole(UniqueID uint16) (err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "DeletePinhole", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANIPv6FirewallControl1) GetPinholePackets(UniqueID uint16) (PinholePackets uint32, err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + PinholePackets string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "GetPinholePackets", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if PinholePackets, err = soap.UnmarshalUi4(response.PinholePackets); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANIPv6FirewallControl1) CheckPinholeWorking(UniqueID uint16) (IsWorking bool, err error) { + // Request structure. + request := &struct { + UniqueID string + }{} + // BEGIN Marshal arguments into request. + + if request.UniqueID, err = soap.MarshalUi2(UniqueID); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + IsWorking string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANIPv6FirewallControl_1, "CheckPinholeWorking", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if IsWorking, err = soap.UnmarshalBoolean(response.IsWorking); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPOTSLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPOTSLinkConfig1 struct { + goupnp.ServiceClient +} + +// NewWANPOTSLinkConfig1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPOTSLinkConfig1Clients() (clients []*WANPOTSLinkConfig1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPOTSLinkConfig_1); err != nil { + return + } + clients = newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients) + return +} + +// NewWANPOTSLinkConfig1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANPOTSLinkConfig1ClientsByURL(loc *url.URL) ([]*WANPOTSLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPOTSLinkConfig_1) + if err != nil { + return nil, err + } + return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +// NewWANPOTSLinkConfig1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANPOTSLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPOTSLinkConfig1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPOTSLinkConfig_1) + if err != nil { + return nil, err + } + return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil +} + +func newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPOTSLinkConfig1 { + clients := make([]*WANPOTSLinkConfig1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPOTSLinkConfig1{genericClients[i]} + } + return clients +} + +// +// Arguments: +// +// * NewLinkType: allowed values: PPP_Dialup + +func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInfo string, NewLinkType string) (err error) { + // Request structure. + request := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewISPPhoneNumber, err = soap.MarshalString(NewISPPhoneNumber); err != nil { + return + } + if request.NewISPInfo, err = soap.MarshalString(NewISPInfo); err != nil { + return + } + if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, NewDelayBetweenRetries uint32) (err error) { + // Request structure. + request := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + // BEGIN Marshal arguments into request. + + if request.NewNumberOfRetries, err = soap.MarshalUi4(NewNumberOfRetries); err != nil { + return + } + if request.NewDelayBetweenRetries, err = soap.MarshalUi4(NewDelayBetweenRetries); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewLinkType: allowed values: PPP_Dialup +func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISPInfo string, NewLinkType string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewISPPhoneNumber string + + NewISPInfo string + + NewLinkType string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetISPInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewISPPhoneNumber, err = soap.UnmarshalString(response.NewISPPhoneNumber); err != nil { + return + } + if NewISPInfo, err = soap.UnmarshalString(response.NewISPInfo); err != nil { + return + } + if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32, NewDelayBetweenRetries uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewNumberOfRetries string + + NewDelayBetweenRetries string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetCallRetryInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewNumberOfRetries, err = soap.UnmarshalUi4(response.NewNumberOfRetries); err != nil { + return + } + if NewDelayBetweenRetries, err = soap.UnmarshalUi4(response.NewDelayBetweenRetries); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetFclass() (NewFclass string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewFclass string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetFclass", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewFclass, err = soap.UnmarshalString(response.NewFclass); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataModulationSupported() (NewDataModulationSupported string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataModulationSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataModulationSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataModulationSupported, err = soap.UnmarshalString(response.NewDataModulationSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataProtocol() (NewDataProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataProtocol, err = soap.UnmarshalString(response.NewDataProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetDataCompression() (NewDataCompression string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewDataCompression string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataCompression", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewDataCompression, err = soap.UnmarshalString(response.NewDataCompression); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPOTSLinkConfig1) GetPlusVTRCommandSupported() (NewPlusVTRCommandSupported bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPlusVTRCommandSupported string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetPlusVTRCommandSupported", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPlusVTRCommandSupported, err = soap.UnmarshalBoolean(response.NewPlusVTRCommandSupported); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// WANPPPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPPPConnection:1". See +// goupnp.ServiceClient, which contains RootDevice and Service attributes which +// are provided for informational value. +type WANPPPConnection1 struct { + goupnp.ServiceClient +} + +// NewWANPPPConnection1Clients discovers instances of the service on the network, +// and returns clients to any that are found. errors will contain an error for +// any devices that replied but which could not be queried, and err will be set +// if the discovery process failed outright. +// +// This is a typical entry calling point into this package. +func NewWANPPPConnection1Clients() (clients []*WANPPPConnection1, errors []error, err error) { + var genericClients []goupnp.ServiceClient + if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPPPConnection_1); err != nil { + return + } + clients = newWANPPPConnection1ClientsFromGenericClients(genericClients) + return +} + +// NewWANPPPConnection1ClientsByURL discovers instances of the service at the given +// URL, and returns clients to any that are found. An error is returned if +// there was an error probing the service. +// +// This is a typical entry calling point into this package when reusing an +// previously discovered service URL. +func NewWANPPPConnection1ClientsByURL(loc *url.URL) ([]*WANPPPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPPPConnection_1) + if err != nil { + return nil, err + } + return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil +} + +// NewWANPPPConnection1ClientsFromRootDevice discovers instances of the service in +// a given root device, and returns clients to any that are found. An error is +// returned if there was not at least one instance of the service within the +// device. The location parameter is simply assigned to the Location attribute +// of the wrapped ServiceClient(s). +// +// This is a typical entry calling point into this package when reusing an +// previously discovered root device. +func NewWANPPPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPPPConnection1, error) { + genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPPPConnection_1) + if err != nil { + return nil, err + } + return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil +} + +func newWANPPPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPPPConnection1 { + clients := make([]*WANPPPConnection1, len(genericClients)) + for i := range genericClients { + clients[i] = &WANPPPConnection1{genericClients[i]} + } + return clients +} + +func (client *WANPPPConnection1) SetConnectionType(NewConnectionType string) (err error) { + // Request structure. + request := &struct { + NewConnectionType string + }{} + // BEGIN Marshal arguments into request. + + if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetConnectionType", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, DHCP_Spoofed, PPPoE_Bridged, PPTP_Relay, L2TP_Relay, PPPoE_Relay +func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionType string + + NewPossibleConnectionTypes string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { + return + } + if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPassword string) (err error) { + // Request structure. + request := &struct { + NewUserName string + + NewPassword string + }{} + // BEGIN Marshal arguments into request. + + if request.NewUserName, err = soap.MarshalString(NewUserName); err != nil { + return + } + if request.NewPassword, err = soap.MarshalString(NewPassword); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ConfigureConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) RequestConnection() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestConnection", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) RequestTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) ForceTermination() (err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ForceTermination", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewAutoDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { + // Request structure. + request := &struct { + NewIdleDisconnectTime string + }{} + // BEGIN Marshal arguments into request. + + if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { + // Request structure. + request := &struct { + NewWarnDisconnectDelay string + }{} + // BEGIN Marshal arguments into request. + + if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected +// +// * NewLastConnectionError: allowed values: ERROR_NONE +func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewConnectionStatus string + + NewLastConnectionError string + + NewUptime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetStatusInfo", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { + return + } + if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { + return + } + if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRate uint32, NewDownstreamMaxBitRate uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUpstreamMaxBitRate string + + NewDownstreamMaxBitRate string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetLinkLayerMaxBitRates", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewUpstreamMaxBitRate); err != nil { + return + } + if NewDownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewDownstreamMaxBitRate); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPEncryptionProtocol() (NewPPPEncryptionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPEncryptionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPEncryptionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPEncryptionProtocol, err = soap.UnmarshalString(response.NewPPPEncryptionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPCompressionProtocol() (NewPPPCompressionProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPCompressionProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPCompressionProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPCompressionProtocol, err = soap.UnmarshalString(response.NewPPPCompressionProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPPPAuthenticationProtocol() (NewPPPAuthenticationProtocol string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPPPAuthenticationProtocol string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPAuthenticationProtocol", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPPPAuthenticationProtocol, err = soap.UnmarshalString(response.NewPPPAuthenticationProtocol); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetUserName() (NewUserName string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewUserName string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetUserName", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewUserName, err = soap.UnmarshalString(response.NewUserName); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetPassword() (NewPassword string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewPassword string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPassword", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewPassword, err = soap.UnmarshalString(response.NewPassword); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewAutoDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewIdleDisconnectTime string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewWarnDisconnectDelay string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRSIPAvailable string + + NewNATEnabled string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetNATRSIPStatus", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { + return + } + if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Return values: +// +// * NewProtocol: allowed values: TCP, UDP +func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewPortMappingIndex string + }{} + // BEGIN Marshal arguments into request. + + if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { + return + } + if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { + return + } + if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { + return + } + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { + return + } + if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { + return + } + if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { + return + } + if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { + return + } + if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { + return + } + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + + NewInternalPort string + + NewInternalClient string + + NewEnabled string + + NewPortMappingDescription string + + NewLeaseDuration string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { + return + } + if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { + return + } + if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { + return + } + if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { + return + } + if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "AddPortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +// +// Arguments: +// +// * NewProtocol: allowed values: TCP, UDP + +func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { + // Request structure. + request := &struct { + NewRemoteHost string + + NewExternalPort string + + NewProtocol string + }{} + // BEGIN Marshal arguments into request. + + if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { + return + } + if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { + return + } + if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { + return + } + // END Marshal arguments into request. + + // Response structure. + response := interface{}(nil) + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "DeletePortMapping", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + // END Unmarshal arguments from response. + return +} + +func (client *WANPPPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { + // Request structure. + request := interface{}(nil) + // BEGIN Marshal arguments into request. + + // END Marshal arguments into request. + + // Response structure. + response := &struct { + NewExternalIPAddress string + }{} + + // Perform the SOAP call. + if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil { + return + } + + // BEGIN Unmarshal arguments from response. + + if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { + return + } + // END Unmarshal arguments from response. + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/device.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/device.go new file mode 100644 index 0000000..e5b658b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/device.go @@ -0,0 +1,184 @@ +// This file contains XML structures for communicating with UPnP devices. + +package goupnp + +import ( + "encoding/xml" + "errors" + "fmt" + "net/url" + + "github.com/huin/goupnp/scpd" + "github.com/huin/goupnp/soap" +) + +const ( + DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0" +) + +// RootDevice is the device description as described by section 2.3 "Device +// description" in +// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf +type RootDevice struct { + XMLName xml.Name `xml:"root"` + SpecVersion SpecVersion `xml:"specVersion"` + URLBase url.URL `xml:"-"` + URLBaseStr string `xml:"URLBase"` + Device Device `xml:"device"` +} + +// SetURLBase sets the URLBase for the RootDevice and its underlying components. +func (root *RootDevice) SetURLBase(urlBase *url.URL) { + root.URLBase = *urlBase + root.URLBaseStr = urlBase.String() + root.Device.SetURLBase(urlBase) +} + +// SpecVersion is part of a RootDevice, describes the version of the +// specification that the data adheres to. +type SpecVersion struct { + Major int32 `xml:"major"` + Minor int32 `xml:"minor"` +} + +// Device is a UPnP device. It can have child devices. +type Device struct { + DeviceType string `xml:"deviceType"` + FriendlyName string `xml:"friendlyName"` + Manufacturer string `xml:"manufacturer"` + ManufacturerURL URLField `xml:"manufacturerURL"` + ModelDescription string `xml:"modelDescription"` + ModelName string `xml:"modelName"` + ModelNumber string `xml:"modelNumber"` + ModelURL URLField `xml:"modelURL"` + SerialNumber string `xml:"serialNumber"` + UDN string `xml:"UDN"` + UPC string `xml:"UPC,omitempty"` + Icons []Icon `xml:"iconList>icon,omitempty"` + Services []Service `xml:"serviceList>service,omitempty"` + Devices []Device `xml:"deviceList>device,omitempty"` + + // Extra observed elements: + PresentationURL URLField `xml:"presentationURL"` +} + +// VisitDevices calls visitor for the device, and all its descendent devices. +func (device *Device) VisitDevices(visitor func(*Device)) { + visitor(device) + for i := range device.Devices { + device.Devices[i].VisitDevices(visitor) + } +} + +// VisitServices calls visitor for all Services under the device and all its +// descendent devices. +func (device *Device) VisitServices(visitor func(*Service)) { + device.VisitDevices(func(d *Device) { + for i := range d.Services { + visitor(&d.Services[i]) + } + }) +} + +// FindService finds all (if any) Services under the device and its descendents +// that have the given ServiceType. +func (device *Device) FindService(serviceType string) []*Service { + var services []*Service + device.VisitServices(func(s *Service) { + if s.ServiceType == serviceType { + services = append(services, s) + } + }) + return services +} + +// SetURLBase sets the URLBase for the Device and its underlying components. +func (device *Device) SetURLBase(urlBase *url.URL) { + device.ManufacturerURL.SetURLBase(urlBase) + device.ModelURL.SetURLBase(urlBase) + device.PresentationURL.SetURLBase(urlBase) + for i := range device.Icons { + device.Icons[i].SetURLBase(urlBase) + } + for i := range device.Services { + device.Services[i].SetURLBase(urlBase) + } + for i := range device.Devices { + device.Devices[i].SetURLBase(urlBase) + } +} + +func (device *Device) String() string { + return fmt.Sprintf("Device ID %s : %s (%s)", device.UDN, device.DeviceType, device.FriendlyName) +} + +// Icon is a representative image that a device might include in its +// description. +type Icon struct { + Mimetype string `xml:"mimetype"` + Width int32 `xml:"width"` + Height int32 `xml:"height"` + Depth int32 `xml:"depth"` + URL URLField `xml:"url"` +} + +// SetURLBase sets the URLBase for the Icon. +func (icon *Icon) SetURLBase(url *url.URL) { + icon.URL.SetURLBase(url) +} + +// Service is a service provided by a UPnP Device. +type Service struct { + ServiceType string `xml:"serviceType"` + ServiceId string `xml:"serviceId"` + SCPDURL URLField `xml:"SCPDURL"` + ControlURL URLField `xml:"controlURL"` + EventSubURL URLField `xml:"eventSubURL"` +} + +// SetURLBase sets the URLBase for the Service. +func (srv *Service) SetURLBase(urlBase *url.URL) { + srv.SCPDURL.SetURLBase(urlBase) + srv.ControlURL.SetURLBase(urlBase) + srv.EventSubURL.SetURLBase(urlBase) +} + +func (srv *Service) String() string { + return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType) +} + +// RequestSCDP requests the SCPD (soap actions and state variables description) +// for the service. +func (srv *Service) RequestSCDP() (*scpd.SCPD, error) { + if !srv.SCPDURL.Ok { + return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set") + } + s := new(scpd.SCPD) + if err := requestXml(srv.SCPDURL.URL.String(), scpd.SCPDXMLNamespace, s); err != nil { + return nil, err + } + return s, nil +} + +func (srv *Service) NewSOAPClient() *soap.SOAPClient { + return soap.NewSOAPClient(srv.ControlURL.URL) +} + +// URLField is a URL that is part of a device description. +type URLField struct { + URL url.URL `xml:"-"` + Ok bool `xml:"-"` + Str string `xml:",chardata"` +} + +func (uf *URLField) SetURLBase(urlBase *url.URL) { + refUrl, err := url.Parse(uf.Str) + if err != nil { + uf.URL = url.URL{} + uf.Ok = false + return + } + + uf.URL = *urlBase.ResolveReference(refUrl) + uf.Ok = true +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/goupnp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/goupnp.go new file mode 100644 index 0000000..fcb8dcd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/goupnp.go @@ -0,0 +1,131 @@ +// goupnp is an implementation of a client for various UPnP services. +// +// For most uses, it is recommended to use the code-generated packages under +// github.com/huin/goupnp/dcps. Example use is shown at +// http://godoc.org/github.com/huin/goupnp/example +// +// A commonly used client is internetgateway1.WANPPPConnection1: +// http://godoc.org/github.com/huin/goupnp/dcps/internetgateway1#WANPPPConnection1 +// +// Currently only a couple of schemas have code generated for them from the +// UPnP example XML specifications. Not all methods will work on these clients, +// because the generated stubs contain the full set of specified methods from +// the XML specifications, and the discovered services will likely support a +// subset of those methods. +package goupnp + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + "time" + + "golang.org/x/net/html/charset" + + "github.com/huin/goupnp/httpu" + "github.com/huin/goupnp/ssdp" +) + +// ContextError is an error that wraps an error with some context information. +type ContextError struct { + Context string + Err error +} + +func (err ContextError) Error() string { + return fmt.Sprintf("%s: %v", err.Context, err.Err) +} + +// MaybeRootDevice contains either a RootDevice or an error. +type MaybeRootDevice struct { + // Set iff Err == nil. + Root *RootDevice + + // The location the device was discovered at. This can be used with + // DeviceByURL, assuming the device is still present. A location represents + // the discovery of a device, regardless of if there was an error probing it. + Location *url.URL + + // Any error encountered probing a discovered device. + Err error +} + +// DiscoverDevices attempts to find targets of the given type. This is +// typically the entry-point for this package. searchTarget is typically a URN +// in the form "urn:schemas-upnp-org:device:..." or +// "urn:schemas-upnp-org:service:...". A single error is returned for errors +// while attempting to send the query. An error or RootDevice is returned for +// each discovered RootDevice. +func DiscoverDevices(searchTarget string) ([]MaybeRootDevice, error) { + httpu, err := httpu.NewHTTPUClient() + if err != nil { + return nil, err + } + defer httpu.Close() + responses, err := ssdp.SSDPRawSearch(httpu, string(searchTarget), 2, 3) + if err != nil { + return nil, err + } + + results := make([]MaybeRootDevice, len(responses)) + for i, response := range responses { + maybe := &results[i] + loc, err := response.Location() + if err != nil { + maybe.Err = ContextError{"unexpected bad location from search", err} + continue + } + maybe.Location = loc + if root, err := DeviceByURL(loc); err != nil { + maybe.Err = err + } else { + maybe.Root = root + } + } + + return results, nil +} + +func DeviceByURL(loc *url.URL) (*RootDevice, error) { + locStr := loc.String() + root := new(RootDevice) + if err := requestXml(locStr, DeviceXMLNamespace, root); err != nil { + return nil, ContextError{fmt.Sprintf("error requesting root device details from %q", locStr), err} + } + var urlBaseStr string + if root.URLBaseStr != "" { + urlBaseStr = root.URLBaseStr + } else { + urlBaseStr = locStr + } + urlBase, err := url.Parse(urlBaseStr) + if err != nil { + return nil, ContextError{fmt.Sprintf("error parsing location URL %q", locStr), err} + } + root.SetURLBase(urlBase) + return root, nil +} + +func requestXml(url string, defaultSpace string, doc interface{}) error { + timeout := time.Duration(3 * time.Second) + client := http.Client{ + Timeout: timeout, + } + resp, err := client.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("goupnp: got response status %s from %q", + resp.Status, url) + } + + decoder := xml.NewDecoder(resp.Body) + decoder.DefaultSpace = defaultSpace + decoder.CharsetReader = charset.NewReaderLabel + + return decoder.Decode(doc) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/httpu.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/httpu.go new file mode 100644 index 0000000..f52dad6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/httpu.go @@ -0,0 +1,132 @@ +package httpu + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "log" + "net" + "net/http" + "sync" + "time" +) + +// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical +// function is for HTTPMU, and particularly SSDP. +type HTTPUClient struct { + connLock sync.Mutex // Protects use of conn. + conn net.PacketConn +} + +// NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the +// purpose. +func NewHTTPUClient() (*HTTPUClient, error) { + conn, err := net.ListenPacket("udp", ":0") + if err != nil { + return nil, err + } + return &HTTPUClient{conn: conn}, nil +} + +// NewHTTPUClientAddr creates a new HTTPUClient which will broadcast packets +// from the specified address, opening up a new UDP socket for the purpose +func NewHTTPUClientAddr(addr string) (*HTTPUClient, error) { + ip := net.ParseIP(addr) + if ip == nil { + return nil, errors.New("Invalid listening address") + } + conn, err := net.ListenPacket("udp", ip.String()+":0") + if err != nil { + return nil, err + } + return &HTTPUClient{conn: conn}, nil +} + +// Close shuts down the client. The client will no longer be useful following +// this. +func (httpu *HTTPUClient) Close() error { + httpu.connLock.Lock() + defer httpu.connLock.Unlock() + return httpu.conn.Close() +} + +// Do performs a request. The timeout is how long to wait for before returning +// the responses that were received. An error is only returned for failing to +// send the request. Failures in receipt simply do not add to the resulting +// responses. +// +// Note that at present only one concurrent connection will happen per +// HTTPUClient. +func (httpu *HTTPUClient) Do(req *http.Request, timeout time.Duration, numSends int) ([]*http.Response, error) { + httpu.connLock.Lock() + defer httpu.connLock.Unlock() + + // Create the request. This is a subset of what http.Request.Write does + // deliberately to avoid creating extra fields which may confuse some + // devices. + var requestBuf bytes.Buffer + method := req.Method + if method == "" { + method = "GET" + } + if _, err := fmt.Fprintf(&requestBuf, "%s %s HTTP/1.1\r\n", method, req.URL.RequestURI()); err != nil { + return nil, err + } + if err := req.Header.Write(&requestBuf); err != nil { + return nil, err + } + if _, err := requestBuf.Write([]byte{'\r', '\n'}); err != nil { + return nil, err + } + + destAddr, err := net.ResolveUDPAddr("udp", req.Host) + if err != nil { + return nil, err + } + if err = httpu.conn.SetDeadline(time.Now().Add(timeout)); err != nil { + return nil, err + } + + // Send request. + for i := 0; i < numSends; i++ { + if n, err := httpu.conn.WriteTo(requestBuf.Bytes(), destAddr); err != nil { + return nil, err + } else if n < len(requestBuf.Bytes()) { + return nil, fmt.Errorf("httpu: wrote %d bytes rather than full %d in request", + n, len(requestBuf.Bytes())) + } + time.Sleep(5 * time.Millisecond) + } + + // Await responses until timeout. + var responses []*http.Response + responseBytes := make([]byte, 2048) + for { + // 2048 bytes should be sufficient for most networks. + n, _, err := httpu.conn.ReadFrom(responseBytes) + if err != nil { + if err, ok := err.(net.Error); ok { + if err.Timeout() { + break + } + if err.Temporary() { + // Sleep in case this is a persistent error to avoid pegging CPU until deadline. + time.Sleep(10 * time.Millisecond) + continue + } + } + return nil, err + } + + // Parse response. + response, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(responseBytes[:n])), req) + if err != nil { + log.Print("httpu: error while parsing response: %v", err) + continue + } + + responses = append(responses, response) + } + return responses, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/serve.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/serve.go new file mode 100644 index 0000000..9f67af8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/httpu/serve.go @@ -0,0 +1,108 @@ +package httpu + +import ( + "bufio" + "bytes" + "log" + "net" + "net/http" + "regexp" +) + +const ( + DefaultMaxMessageBytes = 2048 +) + +var ( + trailingWhitespaceRx = regexp.MustCompile(" +\r\n") + crlf = []byte("\r\n") +) + +// Handler is the interface by which received HTTPU messages are passed to +// handling code. +type Handler interface { + // ServeMessage is called for each HTTPU message received. peerAddr contains + // the address that the message was received from. + ServeMessage(r *http.Request) +} + +// HandlerFunc is a function-to-Handler adapter. +type HandlerFunc func(r *http.Request) + +func (f HandlerFunc) ServeMessage(r *http.Request) { + f(r) +} + +// A Server defines parameters for running an HTTPU server. +type Server struct { + Addr string // UDP address to listen on + Multicast bool // Should listen for multicast? + Interface *net.Interface // Network interface to listen on for multicast, nil for default multicast interface + Handler Handler // handler to invoke + MaxMessageBytes int // maximum number of bytes to read from a packet, DefaultMaxMessageBytes if 0 +} + +// ListenAndServe listens on the UDP network address srv.Addr. If srv.Multicast +// is true, then a multicast UDP listener will be used on srv.Interface (or +// default interface if nil). +func (srv *Server) ListenAndServe() error { + var err error + + var addr *net.UDPAddr + if addr, err = net.ResolveUDPAddr("udp", srv.Addr); err != nil { + log.Fatal(err) + } + + var conn net.PacketConn + if srv.Multicast { + if conn, err = net.ListenMulticastUDP("udp", srv.Interface, addr); err != nil { + return err + } + } else { + if conn, err = net.ListenUDP("udp", addr); err != nil { + return err + } + } + + return srv.Serve(conn) +} + +// Serve messages received on the given packet listener to the srv.Handler. +func (srv *Server) Serve(l net.PacketConn) error { + maxMessageBytes := DefaultMaxMessageBytes + if srv.MaxMessageBytes != 0 { + maxMessageBytes = srv.MaxMessageBytes + } + for { + buf := make([]byte, maxMessageBytes) + n, peerAddr, err := l.ReadFrom(buf) + if err != nil { + return err + } + buf = buf[:n] + + go func(buf []byte, peerAddr net.Addr) { + // At least one router's UPnP implementation has added a trailing space + // after "HTTP/1.1" - trim it. + buf = trailingWhitespaceRx.ReplaceAllLiteral(buf, crlf) + + req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf))) + if err != nil { + log.Printf("httpu: Failed to parse request: %v", err) + return + } + req.RemoteAddr = peerAddr.String() + srv.Handler.ServeMessage(req) + // No need to call req.Body.Close - underlying reader is bytes.Buffer. + }(buf, peerAddr) + } +} + +// Serve messages received on the given packet listener to the given handler. +func Serve(l net.PacketConn, handler Handler) error { + srv := Server{ + Handler: handler, + MaxMessageBytes: DefaultMaxMessageBytes, + } + return srv.Serve(l) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/scpd/scpd.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/scpd/scpd.go new file mode 100644 index 0000000..c9d2e69 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/scpd/scpd.go @@ -0,0 +1,167 @@ +package scpd + +import ( + "encoding/xml" + "strings" +) + +const ( + SCPDXMLNamespace = "urn:schemas-upnp-org:service-1-0" +) + +func cleanWhitespace(s *string) { + *s = strings.TrimSpace(*s) +} + +// SCPD is the service description as described by section 2.5 "Service +// description" in +// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf +type SCPD struct { + XMLName xml.Name `xml:"scpd"` + ConfigId string `xml:"configId,attr"` + SpecVersion SpecVersion `xml:"specVersion"` + Actions []Action `xml:"actionList>action"` + StateVariables []StateVariable `xml:"serviceStateTable>stateVariable"` +} + +// Clean attempts to remove stray whitespace etc. in the structure. It seems +// unfortunately common for stray whitespace to be present in SCPD documents, +// this method attempts to make it easy to clean them out. +func (scpd *SCPD) Clean() { + cleanWhitespace(&scpd.ConfigId) + for i := range scpd.Actions { + scpd.Actions[i].clean() + } + for i := range scpd.StateVariables { + scpd.StateVariables[i].clean() + } +} + +func (scpd *SCPD) GetStateVariable(variable string) *StateVariable { + for i := range scpd.StateVariables { + v := &scpd.StateVariables[i] + if v.Name == variable { + return v + } + } + return nil +} + +func (scpd *SCPD) GetAction(action string) *Action { + for i := range scpd.Actions { + a := &scpd.Actions[i] + if a.Name == action { + return a + } + } + return nil +} + +// SpecVersion is part of a SCPD document, describes the version of the +// specification that the data adheres to. +type SpecVersion struct { + Major int32 `xml:"major"` + Minor int32 `xml:"minor"` +} + +type Action struct { + Name string `xml:"name"` + Arguments []Argument `xml:"argumentList>argument"` +} + +func (action *Action) clean() { + cleanWhitespace(&action.Name) + for i := range action.Arguments { + action.Arguments[i].clean() + } +} + +func (action *Action) InputArguments() []*Argument { + var result []*Argument + for i := range action.Arguments { + arg := &action.Arguments[i] + if arg.IsInput() { + result = append(result, arg) + } + } + return result +} + +func (action *Action) OutputArguments() []*Argument { + var result []*Argument + for i := range action.Arguments { + arg := &action.Arguments[i] + if arg.IsOutput() { + result = append(result, arg) + } + } + return result +} + +type Argument struct { + Name string `xml:"name"` + Direction string `xml:"direction"` // in|out + RelatedStateVariable string `xml:"relatedStateVariable"` // ? + Retval string `xml:"retval"` // ? +} + +func (arg *Argument) clean() { + cleanWhitespace(&arg.Name) + cleanWhitespace(&arg.Direction) + cleanWhitespace(&arg.RelatedStateVariable) + cleanWhitespace(&arg.Retval) +} + +func (arg *Argument) IsInput() bool { + return arg.Direction == "in" +} + +func (arg *Argument) IsOutput() bool { + return arg.Direction == "out" +} + +type StateVariable struct { + Name string `xml:"name"` + SendEvents string `xml:"sendEvents,attr"` // yes|no + Multicast string `xml:"multicast,attr"` // yes|no + DataType DataType `xml:"dataType"` + DefaultValue string `xml:"defaultValue"` + AllowedValueRange *AllowedValueRange `xml:"allowedValueRange"` + AllowedValues []string `xml:"allowedValueList>allowedValue"` +} + +func (v *StateVariable) clean() { + cleanWhitespace(&v.Name) + cleanWhitespace(&v.SendEvents) + cleanWhitespace(&v.Multicast) + v.DataType.clean() + cleanWhitespace(&v.DefaultValue) + if v.AllowedValueRange != nil { + v.AllowedValueRange.clean() + } + for i := range v.AllowedValues { + cleanWhitespace(&v.AllowedValues[i]) + } +} + +type AllowedValueRange struct { + Minimum string `xml:"minimum"` + Maximum string `xml:"maximum"` + Step string `xml:"step"` +} + +func (r *AllowedValueRange) clean() { + cleanWhitespace(&r.Minimum) + cleanWhitespace(&r.Maximum) + cleanWhitespace(&r.Step) +} + +type DataType struct { + Name string `xml:",chardata"` + Type string `xml:"type,attr"` +} + +func (dt *DataType) clean() { + cleanWhitespace(&dt.Name) + cleanWhitespace(&dt.Type) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/service_client.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/service_client.go new file mode 100644 index 0000000..9111c93 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/service_client.go @@ -0,0 +1,88 @@ +package goupnp + +import ( + "fmt" + "net/url" + + "github.com/huin/goupnp/soap" +) + +// ServiceClient is a SOAP client, root device and the service for the SOAP +// client rolled into one value. The root device, location, and service are +// intended to be informational. Location can be used to later recreate a +// ServiceClient with NewServiceClientByURL if the service is still present; +// bypassing the discovery process. +type ServiceClient struct { + SOAPClient *soap.SOAPClient + RootDevice *RootDevice + Location *url.URL + Service *Service +} + +// NewServiceClients discovers services, and returns clients for them. err will +// report any error with the discovery process (blocking any device/service +// discovery), errors reports errors on a per-root-device basis. +func NewServiceClients(searchTarget string) (clients []ServiceClient, errors []error, err error) { + var maybeRootDevices []MaybeRootDevice + if maybeRootDevices, err = DiscoverDevices(searchTarget); err != nil { + return + } + + clients = make([]ServiceClient, 0, len(maybeRootDevices)) + + for _, maybeRootDevice := range maybeRootDevices { + if maybeRootDevice.Err != nil { + errors = append(errors, maybeRootDevice.Err) + continue + } + + deviceClients, err := NewServiceClientsFromRootDevice(maybeRootDevice.Root, maybeRootDevice.Location, searchTarget) + if err != nil { + errors = append(errors, err) + continue + } + clients = append(clients, deviceClients...) + } + + return +} + +// NewServiceClientsByURL creates client(s) for the given service URN, for a +// root device at the given URL. +func NewServiceClientsByURL(loc *url.URL, searchTarget string) ([]ServiceClient, error) { + rootDevice, err := DeviceByURL(loc) + if err != nil { + return nil, err + } + return NewServiceClientsFromRootDevice(rootDevice, loc, searchTarget) +} + +// NewServiceClientsFromDevice creates client(s) for the given service URN, in +// a given root device. The loc parameter is simply assigned to the +// Location attribute of the returned ServiceClient(s). +func NewServiceClientsFromRootDevice(rootDevice *RootDevice, loc *url.URL, searchTarget string) ([]ServiceClient, error) { + device := &rootDevice.Device + srvs := device.FindService(searchTarget) + if len(srvs) == 0 { + return nil, fmt.Errorf("goupnp: service %q not found within device %q (UDN=%q)", + searchTarget, device.FriendlyName, device.UDN) + } + + clients := make([]ServiceClient, 0, len(srvs)) + for _, srv := range srvs { + clients = append(clients, ServiceClient{ + SOAPClient: srv.NewSOAPClient(), + RootDevice: rootDevice, + Location: loc, + Service: srv, + }) + } + return clients, nil +} + +// GetServiceClient returns the ServiceClient itself. This is provided so that the +// service client attributes can be accessed via an interface method on a +// wrapping type. +func (client *ServiceClient) GetServiceClient() *ServiceClient { + return client +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/soap.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/soap.go new file mode 100644 index 0000000..8156107 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/soap.go @@ -0,0 +1,157 @@ +// Definition for the SOAP structure required for UPnP's SOAP usage. + +package soap + +import ( + "bytes" + "encoding/xml" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "reflect" +) + +const ( + soapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" + soapPrefix = xml.Header + `` + soapSuffix = `` +) + +type SOAPClient struct { + EndpointURL url.URL + HTTPClient http.Client +} + +func NewSOAPClient(endpointURL url.URL) *SOAPClient { + return &SOAPClient{ + EndpointURL: endpointURL, + } +} + +// PerformSOAPAction makes a SOAP request, with the given action. +// inAction and outAction must both be pointers to structs with string fields +// only. +func (client *SOAPClient) PerformAction(actionNamespace, actionName string, inAction interface{}, outAction interface{}) error { + requestBytes, err := encodeRequestAction(actionNamespace, actionName, inAction) + if err != nil { + return err + } + + response, err := client.HTTPClient.Do(&http.Request{ + Method: "POST", + URL: &client.EndpointURL, + Header: http.Header{ + "SOAPACTION": []string{`"` + actionNamespace + "#" + actionName + `"`}, + "CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)), + // Set ContentLength to avoid chunked encoding - some servers might not support it. + ContentLength: int64(len(requestBytes)), + }) + if err != nil { + return fmt.Errorf("goupnp: error performing SOAP HTTP request: %v", err) + } + defer response.Body.Close() + if response.StatusCode != 200 { + return fmt.Errorf("goupnp: SOAP request got HTTP %s", response.Status) + } + + responseEnv := newSOAPEnvelope() + decoder := xml.NewDecoder(response.Body) + if err := decoder.Decode(responseEnv); err != nil { + return fmt.Errorf("goupnp: error decoding response body: %v", err) + } + + if responseEnv.Body.Fault != nil { + return responseEnv.Body.Fault + } + + if outAction != nil { + if err := xml.Unmarshal(responseEnv.Body.RawAction, outAction); err != nil { + return fmt.Errorf("goupnp: error unmarshalling out action: %v, %v", err, responseEnv.Body.RawAction) + } + } + + return nil +} + +// newSOAPAction creates a soapEnvelope with the given action and arguments. +func newSOAPEnvelope() *soapEnvelope { + return &soapEnvelope{ + EncodingStyle: soapEncodingStyle, + } +} + +// encodeRequestAction is a hacky way to create an encoded SOAP envelope +// containing the given action. Experiments with one router have shown that it +// 500s for requests where the outer default xmlns is set to the SOAP +// namespace, and then reassigning the default namespace within that to the +// service namespace. Hand-coding the outer XML to work-around this. +func encodeRequestAction(actionNamespace, actionName string, inAction interface{}) ([]byte, error) { + requestBuf := new(bytes.Buffer) + requestBuf.WriteString(soapPrefix) + requestBuf.WriteString(``) + if inAction != nil { + if err := encodeRequestArgs(requestBuf, inAction); err != nil { + return nil, err + } + } + requestBuf.WriteString(``) + requestBuf.WriteString(soapSuffix) + return requestBuf.Bytes(), nil +} + +func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error { + in := reflect.Indirect(reflect.ValueOf(inAction)) + if in.Kind() != reflect.Struct { + return fmt.Errorf("goupnp: SOAP inAction is not a struct but of type %v", in.Type()) + } + enc := xml.NewEncoder(w) + nFields := in.NumField() + inType := in.Type() + for i := 0; i < nFields; i++ { + field := inType.Field(i) + argName := field.Name + if nameOverride := field.Tag.Get("soap"); nameOverride != "" { + argName = nameOverride + } + value := in.Field(i) + if value.Kind() != reflect.String { + return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type()) + } + if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil { + return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err) + } + } + enc.Flush() + return nil +} + +type soapEnvelope struct { + XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + EncodingStyle string `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"` + Body soapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` +} + +type soapBody struct { + Fault *SOAPFaultError `xml:"Fault"` + RawAction []byte `xml:",innerxml"` +} + +// SOAPFaultError implements error, and contains SOAP fault information. +type SOAPFaultError struct { + FaultCode string `xml:"faultcode"` + FaultString string `xml:"faultstring"` + Detail string `xml:"detail"` +} + +func (err *SOAPFaultError) Error() string { + return fmt.Sprintf("SOAP fault: %s", err.FaultString) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/types.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/types.go new file mode 100644 index 0000000..fdbeec8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/soap/types.go @@ -0,0 +1,519 @@ +package soap + +import ( + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "net/url" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +var ( + // localLoc acts like time.Local for this package, but is faked out by the + // unit tests to ensure that things stay constant (especially when running + // this test in a place where local time is UTC which might mask bugs). + localLoc = time.Local +) + +func MarshalUi1(v uint8) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi1(s string) (uint8, error) { + v, err := strconv.ParseUint(s, 10, 8) + return uint8(v), err +} + +func MarshalUi2(v uint16) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi2(s string) (uint16, error) { + v, err := strconv.ParseUint(s, 10, 16) + return uint16(v), err +} + +func MarshalUi4(v uint32) (string, error) { + return strconv.FormatUint(uint64(v), 10), nil +} + +func UnmarshalUi4(s string) (uint32, error) { + v, err := strconv.ParseUint(s, 10, 32) + return uint32(v), err +} + +func MarshalI1(v int8) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI1(s string) (int8, error) { + v, err := strconv.ParseInt(s, 10, 8) + return int8(v), err +} + +func MarshalI2(v int16) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI2(s string) (int16, error) { + v, err := strconv.ParseInt(s, 10, 16) + return int16(v), err +} + +func MarshalI4(v int32) (string, error) { + return strconv.FormatInt(int64(v), 10), nil +} + +func UnmarshalI4(s string) (int32, error) { + v, err := strconv.ParseInt(s, 10, 32) + return int32(v), err +} + +func MarshalInt(v int64) (string, error) { + return strconv.FormatInt(v, 10), nil +} + +func UnmarshalInt(s string) (int64, error) { + return strconv.ParseInt(s, 10, 64) +} + +func MarshalR4(v float32) (string, error) { + return strconv.FormatFloat(float64(v), 'G', -1, 32), nil +} + +func UnmarshalR4(s string) (float32, error) { + v, err := strconv.ParseFloat(s, 32) + return float32(v), err +} + +func MarshalR8(v float64) (string, error) { + return strconv.FormatFloat(v, 'G', -1, 64), nil +} + +func UnmarshalR8(s string) (float64, error) { + v, err := strconv.ParseFloat(s, 64) + return float64(v), err +} + +// MarshalFixed14_4 marshals float64 to SOAP "fixed.14.4" type. +func MarshalFixed14_4(v float64) (string, error) { + if v >= 1e14 || v <= -1e14 { + return "", fmt.Errorf("soap fixed14.4: value %v out of bounds", v) + } + return strconv.FormatFloat(v, 'f', 4, 64), nil +} + +// UnmarshalFixed14_4 unmarshals float64 from SOAP "fixed.14.4" type. +func UnmarshalFixed14_4(s string) (float64, error) { + v, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, err + } + if v >= 1e14 || v <= -1e14 { + return 0, fmt.Errorf("soap fixed14.4: value %q out of bounds", s) + } + return v, nil +} + +// MarshalChar marshals rune to SOAP "char" type. +func MarshalChar(v rune) (string, error) { + if v == 0 { + return "", errors.New("soap char: rune 0 is not allowed") + } + return string(v), nil +} + +// UnmarshalChar unmarshals rune from SOAP "char" type. +func UnmarshalChar(s string) (rune, error) { + if len(s) == 0 { + return 0, errors.New("soap char: got empty string") + } + r, n := utf8.DecodeRune([]byte(s)) + if n != len(s) { + return 0, fmt.Errorf("soap char: value %q is not a single rune", s) + } + return r, nil +} + +func MarshalString(v string) (string, error) { + return v, nil +} + +func UnmarshalString(v string) (string, error) { + return v, nil +} + +func parseInt(s string, err *error) int { + v, parseErr := strconv.ParseInt(s, 10, 64) + if parseErr != nil { + *err = parseErr + } + return int(v) +} + +var dateRegexps = []*regexp.Regexp{ + // yyyy[-mm[-dd]] + regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`), + // yyyy[mm[dd]] + regexp.MustCompile(`^(\d{4})(?:(\d{2})(?:(\d{2}))?)?$`), +} + +func parseDateParts(s string) (year, month, day int, err error) { + var parts []string + for _, re := range dateRegexps { + parts = re.FindStringSubmatch(s) + if parts != nil { + break + } + } + if parts == nil { + err = fmt.Errorf("soap date: value %q is not in a recognized ISO8601 date format", s) + return + } + + year = parseInt(parts[1], &err) + month = 1 + day = 1 + if len(parts[2]) != 0 { + month = parseInt(parts[2], &err) + if len(parts[3]) != 0 { + day = parseInt(parts[3], &err) + } + } + + if err != nil { + err = fmt.Errorf("soap date: %q: %v", s, err) + } + + return +} + +var timeRegexps = []*regexp.Regexp{ + // hh[:mm[:ss]] + regexp.MustCompile(`^(\d{2})(?::(\d{2})(?::(\d{2}))?)?$`), + // hh[mm[ss]] + regexp.MustCompile(`^(\d{2})(?:(\d{2})(?:(\d{2}))?)?$`), +} + +func parseTimeParts(s string) (hour, minute, second int, err error) { + var parts []string + for _, re := range timeRegexps { + parts = re.FindStringSubmatch(s) + if parts != nil { + break + } + } + if parts == nil { + err = fmt.Errorf("soap time: value %q is not in ISO8601 time format", s) + return + } + + hour = parseInt(parts[1], &err) + if len(parts[2]) != 0 { + minute = parseInt(parts[2], &err) + if len(parts[3]) != 0 { + second = parseInt(parts[3], &err) + } + } + + if err != nil { + err = fmt.Errorf("soap time: %q: %v", s, err) + } + + return +} + +// (+|-)hh[[:]mm] +var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2})(?::?(\d{2}))?$`) + +func parseTimezone(s string) (offset int, err error) { + if s == "Z" { + return 0, nil + } + parts := timezoneRegexp.FindStringSubmatch(s) + if parts == nil { + err = fmt.Errorf("soap timezone: value %q is not in ISO8601 timezone format", s) + return + } + + offset = parseInt(parts[2], &err) * 3600 + if len(parts[3]) != 0 { + offset += parseInt(parts[3], &err) * 60 + } + if parts[1] == "-" { + offset = -offset + } + + if err != nil { + err = fmt.Errorf("soap timezone: %q: %v", s, err) + } + + return +} + +var completeDateTimeZoneRegexp = regexp.MustCompile(`^([^T]+)(?:T([^-+Z]+)(.+)?)?$`) + +// splitCompleteDateTimeZone splits date, time and timezone apart from an +// ISO8601 string. It does not ensure that the contents of each part are +// correct, it merely splits on certain delimiters. +// e.g "2010-09-08T12:15:10+0700" => "2010-09-08", "12:15:10", "+0700". +// Timezone can only be present if time is also present. +func splitCompleteDateTimeZone(s string) (dateStr, timeStr, zoneStr string, err error) { + parts := completeDateTimeZoneRegexp.FindStringSubmatch(s) + if parts == nil { + err = fmt.Errorf("soap date/time/zone: value %q is not in ISO8601 datetime format", s) + return + } + dateStr = parts[1] + timeStr = parts[2] + zoneStr = parts[3] + return +} + +// MarshalDate marshals time.Time to SOAP "date" type. Note that this converts +// to local time, and discards the time-of-day components. +func MarshalDate(v time.Time) (string, error) { + return v.In(localLoc).Format("2006-01-02"), nil +} + +var dateFmts = []string{"2006-01-02", "20060102"} + +// UnmarshalDate unmarshals time.Time from SOAP "date" type. This outputs the +// date as midnight in the local time zone. +func UnmarshalDate(s string) (time.Time, error) { + year, month, day, err := parseDateParts(s) + if err != nil { + return time.Time{}, err + } + return time.Date(year, time.Month(month), day, 0, 0, 0, 0, localLoc), nil +} + +// TimeOfDay is used in cases where SOAP "time" or "time.tz" is used. +type TimeOfDay struct { + // Duration of time since midnight. + FromMidnight time.Duration + + // Set to true if Offset is specified. If false, then the timezone is + // unspecified (and by ISO8601 - implies some "local" time). + HasOffset bool + + // Offset is non-zero only if time.tz is used. It is otherwise ignored. If + // non-zero, then it is regarded as a UTC offset in seconds. Note that the + // sub-minutes is ignored by the marshal function. + Offset int +} + +// MarshalTimeOfDay marshals TimeOfDay to the "time" type. +func MarshalTimeOfDay(v TimeOfDay) (string, error) { + d := int64(v.FromMidnight / time.Second) + hour := d / 3600 + d = d % 3600 + minute := d / 60 + second := d % 60 + + return fmt.Sprintf("%02d:%02d:%02d", hour, minute, second), nil +} + +// UnmarshalTimeOfDay unmarshals TimeOfDay from the "time" type. +func UnmarshalTimeOfDay(s string) (TimeOfDay, error) { + t, err := UnmarshalTimeOfDayTz(s) + if err != nil { + return TimeOfDay{}, err + } else if t.HasOffset { + return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone") + } + return t, nil +} + +// MarshalTimeOfDayTz marshals TimeOfDay to the "time.tz" type. +func MarshalTimeOfDayTz(v TimeOfDay) (string, error) { + d := int64(v.FromMidnight / time.Second) + hour := d / 3600 + d = d % 3600 + minute := d / 60 + second := d % 60 + + tz := "" + if v.HasOffset { + if v.Offset == 0 { + tz = "Z" + } else { + offsetMins := v.Offset / 60 + sign := '+' + if offsetMins < 1 { + offsetMins = -offsetMins + sign = '-' + } + tz = fmt.Sprintf("%c%02d:%02d", sign, offsetMins/60, offsetMins%60) + } + } + + return fmt.Sprintf("%02d:%02d:%02d%s", hour, minute, second, tz), nil +} + +// UnmarshalTimeOfDayTz unmarshals TimeOfDay from the "time.tz" type. +func UnmarshalTimeOfDayTz(s string) (tod TimeOfDay, err error) { + zoneIndex := strings.IndexAny(s, "Z+-") + var timePart string + var hasOffset bool + var offset int + if zoneIndex == -1 { + hasOffset = false + timePart = s + } else { + hasOffset = true + timePart = s[:zoneIndex] + if offset, err = parseTimezone(s[zoneIndex:]); err != nil { + return + } + } + + hour, minute, second, err := parseTimeParts(timePart) + if err != nil { + return + } + + fromMidnight := time.Duration(hour*3600+minute*60+second) * time.Second + + // ISO8601 special case - values up to 24:00:00 are allowed, so using + // strictly greater-than for the maximum value. + if fromMidnight > 24*time.Hour || minute >= 60 || second >= 60 { + return TimeOfDay{}, fmt.Errorf("soap time.tz: value %q has value(s) out of range", s) + } + + return TimeOfDay{ + FromMidnight: time.Duration(hour*3600+minute*60+second) * time.Second, + HasOffset: hasOffset, + Offset: offset, + }, nil +} + +// MarshalDateTime marshals time.Time to SOAP "dateTime" type. Note that this +// converts to local time. +func MarshalDateTime(v time.Time) (string, error) { + return v.In(localLoc).Format("2006-01-02T15:04:05"), nil +} + +// UnmarshalDateTime unmarshals time.Time from the SOAP "dateTime" type. This +// returns a value in the local timezone. +func UnmarshalDateTime(s string) (result time.Time, err error) { + dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) + if err != nil { + return + } + + if len(zoneStr) != 0 { + err = fmt.Errorf("soap datetime: unexpected timezone in %q", s) + return + } + + year, month, day, err := parseDateParts(dateStr) + if err != nil { + return + } + + var hour, minute, second int + if len(timeStr) != 0 { + hour, minute, second, err = parseTimeParts(timeStr) + if err != nil { + return + } + } + + result = time.Date(year, time.Month(month), day, hour, minute, second, 0, localLoc) + return +} + +// MarshalDateTimeTz marshals time.Time to SOAP "dateTime.tz" type. +func MarshalDateTimeTz(v time.Time) (string, error) { + return v.Format("2006-01-02T15:04:05-07:00"), nil +} + +// UnmarshalDateTimeTz unmarshals time.Time from the SOAP "dateTime.tz" type. +// This returns a value in the local timezone when the timezone is unspecified. +func UnmarshalDateTimeTz(s string) (result time.Time, err error) { + dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) + if err != nil { + return + } + + year, month, day, err := parseDateParts(dateStr) + if err != nil { + return + } + + var hour, minute, second int + var location *time.Location = localLoc + if len(timeStr) != 0 { + hour, minute, second, err = parseTimeParts(timeStr) + if err != nil { + return + } + if len(zoneStr) != 0 { + var offset int + offset, err = parseTimezone(zoneStr) + if offset == 0 { + location = time.UTC + } else { + location = time.FixedZone("", offset) + } + } + } + + result = time.Date(year, time.Month(month), day, hour, minute, second, 0, location) + return +} + +// MarshalBoolean marshals bool to SOAP "boolean" type. +func MarshalBoolean(v bool) (string, error) { + if v { + return "1", nil + } + return "0", nil +} + +// UnmarshalBoolean unmarshals bool from the SOAP "boolean" type. +func UnmarshalBoolean(s string) (bool, error) { + switch s { + case "0", "false", "no": + return false, nil + case "1", "true", "yes": + return true, nil + } + return false, fmt.Errorf("soap boolean: %q is not a valid boolean value", s) +} + +// MarshalBinBase64 marshals []byte to SOAP "bin.base64" type. +func MarshalBinBase64(v []byte) (string, error) { + return base64.StdEncoding.EncodeToString(v), nil +} + +// UnmarshalBinBase64 unmarshals []byte from the SOAP "bin.base64" type. +func UnmarshalBinBase64(s string) ([]byte, error) { + return base64.StdEncoding.DecodeString(s) +} + +// MarshalBinHex marshals []byte to SOAP "bin.hex" type. +func MarshalBinHex(v []byte) (string, error) { + return hex.EncodeToString(v), nil +} + +// UnmarshalBinHex unmarshals []byte from the SOAP "bin.hex" type. +func UnmarshalBinHex(s string) ([]byte, error) { + return hex.DecodeString(s) +} + +// MarshalURI marshals *url.URL to SOAP "uri" type. +func MarshalURI(v *url.URL) (string, error) { + return v.String(), nil +} + +// UnmarshalURI unmarshals *url.URL from the SOAP "uri" type. +func UnmarshalURI(s string) (*url.URL, error) { + return url.Parse(s) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/registry.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/registry.go new file mode 100644 index 0000000..d3bc114 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/registry.go @@ -0,0 +1,312 @@ +package ssdp + +import ( + "fmt" + "log" + "net/http" + "net/url" + "regexp" + "strconv" + "sync" + "time" + + "github.com/huin/goupnp/httpu" +) + +const ( + maxExpiryTimeSeconds = 24 * 60 * 60 +) + +var ( + maxAgeRx = regexp.MustCompile("max-age= *([0-9]+)") +) + +const ( + EventAlive = EventType(iota) + EventUpdate + EventByeBye +) + +type EventType int8 + +func (et EventType) String() string { + switch et { + case EventAlive: + return "EventAlive" + case EventUpdate: + return "EventUpdate" + case EventByeBye: + return "EventByeBye" + default: + return fmt.Sprintf("EventUnknown(%d)", int8(et)) + } +} + +type Update struct { + // The USN of the service. + USN string + // What happened. + EventType EventType + // The entry, which is nil if the service was not known and + // EventType==EventByeBye. The contents of this must not be modified as it is + // shared with the registry and other listeners. Once created, the Registry + // does not modify the Entry value - any updates are replaced with a new + // Entry value. + Entry *Entry +} + +type Entry struct { + // The address that the entry data was actually received from. + RemoteAddr string + // Unique Service Name. Identifies a unique instance of a device or service. + USN string + // Notfication Type. The type of device or service being announced. + NT string + // Server's self-identifying string. + Server string + Host string + // Location of the UPnP root device description. + Location url.URL + + // Despite BOOTID,CONFIGID being required fields, apparently they are not + // always set by devices. Set to -1 if not present. + + BootID int32 + ConfigID int32 + + SearchPort uint16 + + // When the last update was received for this entry identified by this USN. + LastUpdate time.Time + // When the last update's cached values are advised to expire. + CacheExpiry time.Time +} + +func newEntryFromRequest(r *http.Request) (*Entry, error) { + now := time.Now() + expiryDuration, err := parseCacheControlMaxAge(r.Header.Get("CACHE-CONTROL")) + if err != nil { + return nil, fmt.Errorf("ssdp: error parsing CACHE-CONTROL max age: %v", err) + } + + loc, err := url.Parse(r.Header.Get("LOCATION")) + if err != nil { + return nil, fmt.Errorf("ssdp: error parsing entry Location URL: %v", err) + } + + bootID, err := parseUpnpIntHeader(r.Header, "BOOTID.UPNP.ORG", -1) + if err != nil { + return nil, err + } + configID, err := parseUpnpIntHeader(r.Header, "CONFIGID.UPNP.ORG", -1) + if err != nil { + return nil, err + } + searchPort, err := parseUpnpIntHeader(r.Header, "SEARCHPORT.UPNP.ORG", ssdpSearchPort) + if err != nil { + return nil, err + } + + if searchPort < 1 || searchPort > 65535 { + return nil, fmt.Errorf("ssdp: search port %d is out of range", searchPort) + } + + return &Entry{ + RemoteAddr: r.RemoteAddr, + USN: r.Header.Get("USN"), + NT: r.Header.Get("NT"), + Server: r.Header.Get("SERVER"), + Host: r.Header.Get("HOST"), + Location: *loc, + BootID: bootID, + ConfigID: configID, + SearchPort: uint16(searchPort), + LastUpdate: now, + CacheExpiry: now.Add(expiryDuration), + }, nil +} + +func parseCacheControlMaxAge(cc string) (time.Duration, error) { + matches := maxAgeRx.FindStringSubmatch(cc) + if len(matches) != 2 { + return 0, fmt.Errorf("did not find exactly one max-age in cache control header: %q", cc) + } + expirySeconds, err := strconv.ParseInt(matches[1], 10, 16) + if err != nil { + return 0, err + } + if expirySeconds < 1 || expirySeconds > maxExpiryTimeSeconds { + return 0, fmt.Errorf("rejecting bad expiry time of %d seconds", expirySeconds) + } + return time.Duration(expirySeconds) * time.Second, nil +} + +// parseUpnpIntHeader is intended to parse the +// {BOOT,CONFIGID,SEARCHPORT}.UPNP.ORG header fields. It returns the def if +// the head is empty or missing. +func parseUpnpIntHeader(headers http.Header, headerName string, def int32) (int32, error) { + s := headers.Get(headerName) + if s == "" { + return def, nil + } + v, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return 0, fmt.Errorf("ssdp: could not parse header %s: %v", headerName, err) + } + return int32(v), nil +} + +var _ httpu.Handler = new(Registry) + +// Registry maintains knowledge of discovered devices and services. +// +// NOTE: the interface for this is experimental and may change, or go away +// entirely. +type Registry struct { + lock sync.Mutex + byUSN map[string]*Entry + + listenersLock sync.RWMutex + listeners map[chan<- Update]struct{} +} + +func NewRegistry() *Registry { + return &Registry{ + byUSN: make(map[string]*Entry), + listeners: make(map[chan<- Update]struct{}), + } +} + +// NewServerAndRegistry is a convenience function to create a registry, and an +// httpu server to pass it messages. Call ListenAndServe on the server for +// messages to be processed. +func NewServerAndRegistry() (*httpu.Server, *Registry) { + reg := NewRegistry() + srv := &httpu.Server{ + Addr: ssdpUDP4Addr, + Multicast: true, + Handler: reg, + } + return srv, reg +} + +func (reg *Registry) AddListener(c chan<- Update) { + reg.listenersLock.Lock() + defer reg.listenersLock.Unlock() + reg.listeners[c] = struct{}{} +} + +func (reg *Registry) RemoveListener(c chan<- Update) { + reg.listenersLock.Lock() + defer reg.listenersLock.Unlock() + delete(reg.listeners, c) +} + +func (reg *Registry) sendUpdate(u Update) { + reg.listenersLock.RLock() + defer reg.listenersLock.RUnlock() + for c := range reg.listeners { + c <- u + } +} + +// GetService returns known service (or device) entries for the given service +// URN. +func (reg *Registry) GetService(serviceURN string) []*Entry { + // Currently assumes that the map is small, so we do a linear search rather + // than indexed to avoid maintaining two maps. + var results []*Entry + reg.lock.Lock() + defer reg.lock.Unlock() + for _, entry := range reg.byUSN { + if entry.NT == serviceURN { + results = append(results, entry) + } + } + return results +} + +// ServeMessage implements httpu.Handler, and uses SSDP NOTIFY requests to +// maintain the registry of devices and services. +func (reg *Registry) ServeMessage(r *http.Request) { + if r.Method != methodNotify { + return + } + + nts := r.Header.Get("nts") + + var err error + switch nts { + case ntsAlive: + err = reg.handleNTSAlive(r) + case ntsUpdate: + err = reg.handleNTSUpdate(r) + case ntsByebye: + err = reg.handleNTSByebye(r) + default: + err = fmt.Errorf("unknown NTS value: %q", nts) + } + if err != nil { + log.Printf("goupnp/ssdp: failed to handle %s message from %s: %v", nts, r.RemoteAddr, err) + } +} + +func (reg *Registry) handleNTSAlive(r *http.Request) error { + entry, err := newEntryFromRequest(r) + if err != nil { + return err + } + + reg.lock.Lock() + reg.byUSN[entry.USN] = entry + reg.lock.Unlock() + + reg.sendUpdate(Update{ + USN: entry.USN, + EventType: EventAlive, + Entry: entry, + }) + + return nil +} + +func (reg *Registry) handleNTSUpdate(r *http.Request) error { + entry, err := newEntryFromRequest(r) + if err != nil { + return err + } + nextBootID, err := parseUpnpIntHeader(r.Header, "NEXTBOOTID.UPNP.ORG", -1) + if err != nil { + return err + } + entry.BootID = nextBootID + + reg.lock.Lock() + reg.byUSN[entry.USN] = entry + reg.lock.Unlock() + + reg.sendUpdate(Update{ + USN: entry.USN, + EventType: EventUpdate, + Entry: entry, + }) + + return nil +} + +func (reg *Registry) handleNTSByebye(r *http.Request) error { + usn := r.Header.Get("USN") + + reg.lock.Lock() + entry := reg.byUSN[usn] + delete(reg.byUSN, usn) + reg.lock.Unlock() + + reg.sendUpdate(Update{ + USN: usn, + EventType: EventByeBye, + Entry: entry, + }) + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/ssdp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/ssdp.go new file mode 100644 index 0000000..8178f5d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/huin/goupnp/ssdp/ssdp.go @@ -0,0 +1,83 @@ +package ssdp + +import ( + "errors" + "log" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/huin/goupnp/httpu" +) + +const ( + ssdpDiscover = `"ssdp:discover"` + ntsAlive = `ssdp:alive` + ntsByebye = `ssdp:byebye` + ntsUpdate = `ssdp:update` + ssdpUDP4Addr = "239.255.255.250:1900" + ssdpSearchPort = 1900 + methodSearch = "M-SEARCH" + methodNotify = "NOTIFY" +) + +// SSDPRawSearch performs a fairly raw SSDP search request, and returns the +// unique response(s) that it receives. Each response has the requested +// searchTarget, a USN, and a valid location. maxWaitSeconds states how long to +// wait for responses in seconds, and must be a minimum of 1 (the +// implementation waits an additional 100ms for responses to arrive), 2 is a +// reasonable value for this. numSends is the number of requests to send - 3 is +// a reasonable value for this. +func SSDPRawSearch(httpu *httpu.HTTPUClient, searchTarget string, maxWaitSeconds int, numSends int) ([]*http.Response, error) { + if maxWaitSeconds < 1 { + return nil, errors.New("ssdp: maxWaitSeconds must be >= 1") + } + + seenUsns := make(map[string]bool) + var responses []*http.Response + req := http.Request{ + Method: methodSearch, + // TODO: Support both IPv4 and IPv6. + Host: ssdpUDP4Addr, + URL: &url.URL{Opaque: "*"}, + Header: http.Header{ + // Putting headers in here avoids them being title-cased. + // (The UPnP discovery protocol uses case-sensitive headers) + "HOST": []string{ssdpUDP4Addr}, + "MX": []string{strconv.FormatInt(int64(maxWaitSeconds), 10)}, + "MAN": []string{ssdpDiscover}, + "ST": []string{searchTarget}, + }, + } + allResponses, err := httpu.Do(&req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends) + if err != nil { + return nil, err + } + for _, response := range allResponses { + if response.StatusCode != 200 { + log.Printf("ssdp: got response status code %q in search response", response.Status) + continue + } + if st := response.Header.Get("ST"); st != searchTarget { + log.Printf("ssdp: got unexpected search target result %q", st) + continue + } + location, err := response.Location() + if err != nil { + log.Printf("ssdp: no usable location in search response (discarding): %v", err) + continue + } + usn := response.Header.Get("USN") + if usn == "" { + log.Printf("ssdp: empty/missing USN in search response (using location instead): %v", err) + usn = location.String() + } + if _, alreadySeen := seenUsns[usn]; !alreadySeen { + seenUsns[usn] = true + responses = append(responses, response) + } + } + + return responses, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/LICENSE new file mode 100644 index 0000000..249514b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/LICENSE @@ -0,0 +1,13 @@ + Copyright 2013 John Howard Palevich + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/README.md new file mode 100644 index 0000000..3ca687f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/README.md @@ -0,0 +1,52 @@ +go-nat-pmp +========== + +A Go language client for the NAT-PMP internet protocol for port mapping and discovering the external +IP address of a firewall. + +NAT-PMP is supported by Apple brand routers and open source routers like Tomato and DD-WRT. + +See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03 + + +[![Build Status](https://travis-ci.org/jackpal/go-nat-pmp.svg)](https://travis-ci.org/jackpal/go-nat-pmp) + +Get the package +--------------- + + go get -u github.com/jackpal/go-nat-pmp + +Usage +----- + + import ( + "github.com/jackpal/gateway" + natpmp "github.com/jackpal/go-nat-pmp" + ) + + gatewayIP, err = gateway.DiscoverGateway() + if err != nil { + return + } + + client := natpmp.NewClient(gatewayIP) + response, err := client.GetExternalAddress() + if err != nil { + return + } + print("External IP address:", response.ExternalIPAddress) + +Clients +------- + +This library is used in the Taipei Torrent BitTorrent client http://github.com/jackpal/Taipei-Torrent + +Complete documentation +---------------------- + + http://godoc.org/github.com/jackpal/go-nat-pmp + +License +------- + +This project is licensed under the Apache License 2.0. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/natpmp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/natpmp.go new file mode 100644 index 0000000..5ca7680 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/natpmp.go @@ -0,0 +1,153 @@ +package natpmp + +import ( + "fmt" + "net" + "time" +) + +// Implement the NAT-PMP protocol, typically supported by Apple routers and open source +// routers such as DD-WRT and Tomato. +// +// See http://tools.ietf.org/html/draft-cheshire-nat-pmp-03 +// +// Usage: +// +// client := natpmp.NewClient(gatewayIP) +// response, err := client.GetExternalAddress() + +// The recommended mapping lifetime for AddPortMapping +const RECOMMENDED_MAPPING_LIFETIME_SECONDS = 3600 + +// Interface used to make remote procedure calls. +type caller interface { + call(msg []byte, timeout time.Duration) (result []byte, err error) +} + +// Client is a NAT-PMP protocol client. +type Client struct { + caller caller + timeout time.Duration +} + +// Create a NAT-PMP client for the NAT-PMP server at the gateway. +// Uses default timeout which is around 128 seconds. +func NewClient(gateway net.IP) (nat *Client) { + return &Client{&network{gateway}, 0} +} + +// Create a NAT-PMP client for the NAT-PMP server at the gateway, with a timeout. +// Timeout defines the total amount of time we will keep retrying before giving up. +func NewClientWithTimeout(gateway net.IP, timeout time.Duration) (nat *Client) { + return &Client{&network{gateway}, timeout} +} + +// Results of the NAT-PMP GetExternalAddress operation. +type GetExternalAddressResult struct { + SecondsSinceStartOfEpoc uint32 + ExternalIPAddress [4]byte +} + +// Get the external address of the router. +func (n *Client) GetExternalAddress() (result *GetExternalAddressResult, err error) { + msg := make([]byte, 2) + msg[0] = 0 // Version 0 + msg[1] = 0 // OP Code 0 + response, err := n.rpc(msg, 12) + if err != nil { + return + } + result = &GetExternalAddressResult{} + result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8]) + copy(result.ExternalIPAddress[:], response[8:12]) + return +} + +// Results of the NAT-PMP AddPortMapping operation +type AddPortMappingResult struct { + SecondsSinceStartOfEpoc uint32 + InternalPort uint16 + MappedExternalPort uint16 + PortMappingLifetimeInSeconds uint32 +} + +// Add (or delete) a port mapping. To delete a mapping, set the requestedExternalPort and lifetime to 0 +func (n *Client) AddPortMapping(protocol string, internalPort, requestedExternalPort int, lifetime int) (result *AddPortMappingResult, err error) { + var opcode byte + if protocol == "udp" { + opcode = 1 + } else if protocol == "tcp" { + opcode = 2 + } else { + err = fmt.Errorf("unknown protocol %v", protocol) + return + } + msg := make([]byte, 12) + msg[0] = 0 // Version 0 + msg[1] = opcode + writeNetworkOrderUint16(msg[4:6], uint16(internalPort)) + writeNetworkOrderUint16(msg[6:8], uint16(requestedExternalPort)) + writeNetworkOrderUint32(msg[8:12], uint32(lifetime)) + response, err := n.rpc(msg, 16) + if err != nil { + return + } + result = &AddPortMappingResult{} + result.SecondsSinceStartOfEpoc = readNetworkOrderUint32(response[4:8]) + result.InternalPort = readNetworkOrderUint16(response[8:10]) + result.MappedExternalPort = readNetworkOrderUint16(response[10:12]) + result.PortMappingLifetimeInSeconds = readNetworkOrderUint32(response[12:16]) + return +} + +func (n *Client) rpc(msg []byte, resultSize int) (result []byte, err error) { + result, err = n.caller.call(msg, n.timeout) + if err != nil { + return + } + err = protocolChecks(msg, resultSize, result) + return +} + +func protocolChecks(msg []byte, resultSize int, result []byte) (err error) { + if len(result) != resultSize { + err = fmt.Errorf("unexpected result size %d, expected %d", len(result), resultSize) + return + } + if result[0] != 0 { + err = fmt.Errorf("unknown protocol version %d", result[0]) + return + } + expectedOp := msg[1] | 0x80 + if result[1] != expectedOp { + err = fmt.Errorf("Unexpected opcode %d. Expected %d", result[1], expectedOp) + return + } + resultCode := readNetworkOrderUint16(result[2:4]) + if resultCode != 0 { + err = fmt.Errorf("Non-zero result code %d", resultCode) + return + } + // If we got here the RPC is good. + return +} + +func writeNetworkOrderUint16(buf []byte, d uint16) { + buf[0] = byte(d >> 8) + buf[1] = byte(d) +} + +func writeNetworkOrderUint32(buf []byte, d uint32) { + buf[0] = byte(d >> 24) + buf[1] = byte(d >> 16) + buf[2] = byte(d >> 8) + buf[3] = byte(d) +} + +func readNetworkOrderUint16(buf []byte) uint16 { + return (uint16(buf[0]) << 8) | uint16(buf[1]) +} + +func readNetworkOrderUint32(buf []byte) uint32 { + return (uint32(buf[0]) << 24) | (uint32(buf[1]) << 16) | (uint32(buf[2]) << 8) | uint32(buf[3]) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/network.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/network.go new file mode 100644 index 0000000..c42b4fe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/network.go @@ -0,0 +1,89 @@ +package natpmp + +import ( + "fmt" + "net" + "time" +) + +const nAT_PMP_PORT = 5351 +const nAT_TRIES = 9 +const nAT_INITIAL_MS = 250 + +// A caller that implements the NAT-PMP RPC protocol. +type network struct { + gateway net.IP +} + +func (n *network) call(msg []byte, timeout time.Duration) (result []byte, err error) { + var server net.UDPAddr + server.IP = n.gateway + server.Port = nAT_PMP_PORT + conn, err := net.DialUDP("udp", nil, &server) + if err != nil { + return + } + defer conn.Close() + + // 16 bytes is the maximum result size. + result = make([]byte, 16) + + var finalTimeout time.Time + if timeout != 0 { + finalTimeout = time.Now().Add(timeout) + } + + needNewDeadline := true + + var tries uint + for tries = 0; (tries < nAT_TRIES && finalTimeout.IsZero()) || time.Now().Before(finalTimeout); { + if needNewDeadline { + nextDeadline := time.Now().Add((nAT_INITIAL_MS << tries) * time.Millisecond) + err = conn.SetDeadline(minTime(nextDeadline, finalTimeout)) + if err != nil { + return + } + needNewDeadline = false + } + _, err = conn.Write(msg) + if err != nil { + return + } + var bytesRead int + var remoteAddr *net.UDPAddr + bytesRead, remoteAddr, err = conn.ReadFromUDP(result) + if err != nil { + if err.(net.Error).Timeout() { + tries++ + needNewDeadline = true + continue + } + return + } + if !remoteAddr.IP.Equal(n.gateway) { + // Ignore this packet. + // Continue without increasing retransmission timeout or deadline. + continue + } + // Trim result to actual number of bytes received + if bytesRead < len(result) { + result = result[:bytesRead] + } + return + } + err = fmt.Errorf("Timed out trying to contact gateway") + return +} + +func minTime(a, b time.Time) time.Time { + if a.IsZero() { + return b + } + if b.IsZero() { + return a + } + if a.Before(b) { + return a + } + return b +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/recorder.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/recorder.go new file mode 100644 index 0000000..8457036 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/jackpal/go-nat-pmp/recorder.go @@ -0,0 +1,19 @@ +package natpmp + +import "time" + +type callObserver interface { + observeCall(msg []byte, result []byte, err error) +} + +// A caller that records the RPC call. +type recorder struct { + child caller + observer callObserver +} + +func (n *recorder) call(msg []byte, timeout time.Duration) (result []byte, err error) { + result, err = n.child.call(msg, timeout) + n.observer.observeCall(msg, result, err) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/LICENSE.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/LICENSE.md new file mode 100644 index 0000000..025ad35 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/LICENSE.md @@ -0,0 +1,8 @@ +The components of `hid` are licensed as such: + + * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license. + * `libusb` is released under the [GNU GPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license. + * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license. + * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license. + +Given the above, `hid` is licensed under GNU GPL 2.1 or later on Linux and 3-clause BSD on other platforms. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/README.md new file mode 100644 index 0000000..2c3cbb8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/README.md @@ -0,0 +1,41 @@ +[![GoDoc][docimg]][docurl] + +[docimg]: https://godoc.org/github.com/karalabe/hid?status.svg +[docurl]: https://godoc.org/github.com/karalabe/hid + +# Gopher Interface Devices (USB HID) + +The `hid` package is a cross platform library for accessing and communicating with USB Human Interface +Devices (HID). It is an alternative package to [`gousb`](https://github.com/karalabe/gousb) for use +cases where devices support this ligher mode of operation (e.g. input devices, hardware crypto wallets). + +The package wraps [`hidapi`](https://github.com/signal11/hidapi) for accessing OS specific USB HID APIs +directly instead of using low level USB constructs, which might have permission issues on some platforms. +On Linux the package also wraps [`libusb`](https://github.com/libusb/libusb). Both of these dependencies +are vendored directly into the repository and wrapped using CGO, making the `hid` package self-contained +and go-gettable. + +Supported platforms at the moment are Linux, macOS and Windows (exclude constraints are also specified +for Android and iOS to allow smoother vendoring into cross platform projects). + +## Acknowledgements + +Although the `hid` package is an implementation from scratch, it was heavily inspired by the existing +[`go.hid`](https://github.com/GeertJohan/go.hid) library, which seems abandoned since 2015; is incompatible +with Go 1.6+; and has various external dependencies. Given its inspirational roots, I thought it important +to give credit to the author of said package too. + +Wide character support in the `hid` package is done via the [`gowchar`](https://github.com/orofarne/gowchar) +library, unmaintained since 2013; non buildable with a modern Go release and failing `go vet` checks. As +such, `gowchar` was also vendored in inline (copyright headers and origins preserved). + +## License + +The components of `hid` are licensed as such: + + * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license. + * `libusb` is released under the [GNU GPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license. + * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license. + * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license. + +Given the above, `hid` is licensed under GNU GPL 2.1 or later on Linux and 3-clause BSD on other platforms. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid.go new file mode 100644 index 0000000..be75f94 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid.go @@ -0,0 +1,37 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU GPL 2.1 or later. + +// Package hid provides an interface for USB HID devices. +package hid + +import "errors" + +// ErrDeviceClosed is returned for operations where the device closed before or +// during the execution. +var ErrDeviceClosed = errors.New("hid: device closed") + +// ErrUnsupportedPlatform is returned for all operations where the underlying +// operating system is not supported by the library. +var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") + +// DeviceInfo is a hidapi info structure. +type DeviceInfo struct { + Path string // Platform-specific device path + VendorID uint16 // Device Vendor ID + ProductID uint16 // Device Product ID + Release uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number + Serial string // Serial Number + Manufacturer string // Manufacturer String + Product string // Product string + UsagePage uint16 // Usage Page for this Device/Interface (Windows/Mac only) + Usage uint16 // Usage for this Device/Interface (Windows/Mac only) + + // The USB interface which this logical device + // represents. Valid on both Linux implementations + // in all cases, and valid on the Windows implementation + // only if the device contains more than one interface. + Interface int +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_disabled.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_disabled.go new file mode 100644 index 0000000..0798bad --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_disabled.go @@ -0,0 +1,53 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU GPL 2.1 or later. + +// +build !linux +// +build !darwin ios +// +build !windows + +package hid + +// Supported returns whether this platform is supported by the HID library or not. +// The goal of this method is to allow programatically handling platforms that do +// not support USB HID and not having to fall back to build constraints. +func Supported() bool { + return false +} + +// Enumerate returns a list of all the HID devices attached to the system which +// match the vendor and product id. On platforms that this file implements the +// function is a noop and returns an empty list always. +func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { + return nil +} + +// Device is a live HID USB connected device handle. On platforms that this file +// implements the type lacks the actual HID device and all methods are noop. +type Device struct { + DeviceInfo // Embed the infos for easier access +} + +// Open connects to an HID device by its path name. On platforms that this file +// implements the method just returns an error. +func (info DeviceInfo) Open() (*Device, error) { + return nil, ErrUnsupportedPlatform +} + +// Close releases the HID USB device handle. On platforms that this file implements +// the method is just a noop. +func (dev *Device) Close() {} + +// Write sends an output report to a HID device. On platforms that this file +// implements the method just returns an error. +func (dev *Device) Write(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} + +// Read retrieves an input report from a HID device. On platforms that this file +// implements the method just returns an error. +func (dev *Device) Read(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_enabled.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_enabled.go new file mode 100644 index 0000000..3bc0ff3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hid_enabled.go @@ -0,0 +1,216 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU GPL 2.1 or later. + +// +build !ios +// +build linux darwin windows + +package hid + +/* +#cgo CFLAGS: -I./hidapi/hidapi + +#cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int +#cgo darwin CFLAGS: -DOS_DARWIN +#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit +#cgo windows CFLAGS: -DOS_WINDOWS +#cgo windows LDFLAGS: -lsetupapi + +#ifdef OS_LINUX + #include + #include "os/threads_posix.c" + #include "os/poll_posix.c" + + #include "os/linux_usbfs.c" + #include "os/linux_netlink.c" + + #include "core.c" + #include "descriptor.c" + #include "hotplug.c" + #include "io.c" + #include "strerror.c" + #include "sync.c" + + #include "hidapi/libusb/hid.c" +#elif OS_DARWIN + #include "hidapi/mac/hid.c" +#elif OS_WINDOWS + #include "hidapi/windows/hid.c" +#endif +*/ +import "C" +import ( + "errors" + "runtime" + "sync" + "unsafe" +) + +func init() { + // Initialize the HIDAPI library + C.hid_init() +} + +// Supported returns whether this platform is supported by the HID library or not. +// The goal of this method is to allow programatically handling platforms that do +// not support USB HID and not having to fall back to build constraints. +func Supported() bool { + return true +} + +// Enumerate returns a list of all the HID devices attached to the system which +// match the vendor and product id: +// - If the vendor id is set to 0 then any vendor matches. +// - If the product id is set to 0 then any product matches. +// - If the vendor and product id are both 0, all HID devices are returned. +func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { + // Gather all device infos and ensure they are freed before returning + head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID)) + if head == nil { + return nil + } + defer C.hid_free_enumeration(head) + + // Iterate the list and retrieve the device details + var infos []DeviceInfo + for ; head != nil; head = head.next { + info := DeviceInfo{ + Path: C.GoString(head.path), + VendorID: uint16(head.vendor_id), + ProductID: uint16(head.product_id), + Release: uint16(head.release_number), + UsagePage: uint16(head.usage_page), + Usage: uint16(head.usage), + Interface: int(head.interface_number), + } + if head.serial_number != nil { + info.Serial, _ = wcharTToString(head.serial_number) + } + if head.product_string != nil { + info.Product, _ = wcharTToString(head.product_string) + } + if head.manufacturer_string != nil { + info.Manufacturer, _ = wcharTToString(head.manufacturer_string) + } + infos = append(infos, info) + } + return infos +} + +// Open connects to an HID device by its path name. +func (info DeviceInfo) Open() (*Device, error) { + path := C.CString(info.Path) + defer C.free(unsafe.Pointer(path)) + + device := C.hid_open_path(path) + if device == nil { + return nil, errors.New("hidapi: failed to open device") + } + return &Device{ + DeviceInfo: info, + device: device, + }, nil +} + +// Device is a live HID USB connected device handle. +type Device struct { + DeviceInfo // Embed the infos for easier access + + device *C.hid_device // Low level HID device to communicate through + lock sync.Mutex +} + +// Close releases the HID USB device handle. +func (dev *Device) Close() { + dev.lock.Lock() + defer dev.lock.Unlock() + + if dev.device != nil { + C.hid_close(dev.device) + dev.device = nil + } +} + +// Write sends an output report to a HID device. +// +// Write will send the data on the first OUT endpoint, if one exists. If it does +// not, it will send the data through the Control Endpoint (Endpoint 0). +func (dev *Device) Write(b []byte) (int, error) { + // Abort if nothing to write + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Prepend a HID report ID on Windows, other OSes don't need it + var report []byte + if runtime.GOOS == "windows" { + report = append([]byte{0x00}, b...) + } else { + report = b + } + // Execute the write operation + written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report)))) + if written == -1 { + // If the write failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return written, nil +} + +// Read retrieves an input report from a HID device. +func (dev *Device) Read(b []byte) (int, error) { + // Aborth if nothing to read + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Execute the read operation + read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b)))) + if read == -1 { + // If the read failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return read, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/AUTHORS.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/AUTHORS.txt new file mode 100644 index 0000000..7acafd7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/AUTHORS.txt @@ -0,0 +1,16 @@ + +HIDAPI Authors: + +Alan Ott : + Original Author and Maintainer + Linux, Windows, and Mac implementations + +Ludovic Rousseau : + Formatting for Doxygen documentation + Bug fixes + Correctness fixes + + +For a comprehensive list of contributions, see the commit list at github: + http://github.com/signal11/hidapi/commits/master + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-bsd.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-bsd.txt new file mode 100644 index 0000000..538cdf9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-bsd.txt @@ -0,0 +1,26 @@ +Copyright (c) 2010, Alan Ott, Signal 11 Software +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 Signal 11 Software 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 THE COPYRIGHT HOLDER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-gpl3.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-gpl3.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-gpl3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-orig.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-orig.txt new file mode 100644 index 0000000..e3f3380 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE-orig.txt @@ -0,0 +1,9 @@ + HIDAPI - Multi-Platform library for + communication with HID devices. + + Copyright 2009, Alan Ott, Signal 11 Software. + All Rights Reserved. + + This software may be used by anyone for any reason so + long as the copyright notice in the source files + remains intact. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE.txt new file mode 100644 index 0000000..e1676d4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/LICENSE.txt @@ -0,0 +1,13 @@ +HIDAPI can be used under one of three licenses. + +1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt +2. A BSD-Style License, in LICENSE-bsd.txt. +3. The more liberal original HIDAPI license. LICENSE-orig.txt + +The license chosen is at the discretion of the user of HIDAPI. For example: +1. An author of GPL software would likely use HIDAPI under the terms of the +GPL. + +2. An author of commercial closed-source software would likely use HIDAPI +under the terms of the BSD-style license or the original HIDAPI license. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/README.txt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/README.txt new file mode 100644 index 0000000..f19dae4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/README.txt @@ -0,0 +1,339 @@ + HIDAPI library for Windows, Linux, FreeBSD and Mac OS X + ========================================================= + +About +====== + +HIDAPI is a multi-platform library which allows an application to interface +with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac +OS X. HIDAPI can be either built as a shared library (.so or .dll) or +can be embedded directly into a target application by adding a single source +file (per platform) and a single header. + +HIDAPI has four back-ends: + * Windows (using hid.dll) + * Linux/hidraw (using the Kernel's hidraw driver) + * Linux/libusb (using libusb-1.0) + * FreeBSD (using libusb-1.0) + * Mac (using IOHidManager) + +On Linux, either the hidraw or the libusb back-end can be used. There are +tradeoffs, and the functionality supported is slightly different. + +Linux/hidraw (linux/hid.c): +This back-end uses the hidraw interface in the Linux kernel. While this +back-end will support both USB and Bluetooth, it has some limitations on +kernels prior to 2.6.39, including the inability to send or receive feature +reports. In addition, it will only communicate with devices which have +hidraw nodes associated with them. Keyboards, mice, and some other devices +which are blacklisted from having hidraw nodes will not work. Fortunately, +for nearly all the uses of hidraw, this is not a problem. + +Linux/FreeBSD/libusb (libusb/hid.c): +This back-end uses libusb-1.0 to communicate directly to a USB device. This +back-end will of course not work with Bluetooth devices. + +HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses +Fox Toolkit (http://www.fox-toolkit.org). It will build on every platform +which HIDAPI supports. Since it relies on a 3rd party library, building it +is optional but recommended because it is so useful when debugging hardware. + +What Does the API Look Like? +============================= +The API provides the the most commonly used HID functions including sending +and receiving of input, output, and feature reports. The sample program, +which communicates with a heavily hacked up version of the Microchip USB +Generic HID sample looks like this (with error checking removed for +simplicity): + +#ifdef WIN32 +#include +#endif +#include +#include +#include "hidapi.h" + +#define MAX_STR 255 + +int main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + wchar_t wstr[MAX_STR]; + hid_device *handle; + int i; + + // Initialize the hidapi library + res = hid_init(); + + // Open the device using the VID, PID, + // and optionally the Serial number. + handle = hid_open(0x4d8, 0x3f, NULL); + + // Read the Manufacturer String + res = hid_get_manufacturer_string(handle, wstr, MAX_STR); + wprintf(L"Manufacturer String: %s\n", wstr); + + // Read the Product String + res = hid_get_product_string(handle, wstr, MAX_STR); + wprintf(L"Product String: %s\n", wstr); + + // Read the Serial Number String + res = hid_get_serial_number_string(handle, wstr, MAX_STR); + wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); + + // Read Indexed String 1 + res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); + wprintf(L"Indexed String 1: %s\n", wstr); + + // Toggle LED (cmd 0x80). The first byte is the report number (0x0). + buf[0] = 0x0; + buf[1] = 0x80; + res = hid_write(handle, buf, 65); + + // Request state (cmd 0x81). The first byte is the report number (0x0). + buf[0] = 0x0; + buf[1] = 0x81; + res = hid_write(handle, buf, 65); + + // Read requested state + res = hid_read(handle, buf, 65); + + // Print out the returned buffer. + for (i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + // Finalize the hidapi library + res = hid_exit(); + + return 0; +} + +If you have your own simple test programs which communicate with standard +hardware development boards (such as those from Microchip, TI, Atmel, +FreeScale and others), please consider sending me something like the above +for inclusion into the HIDAPI source. This will help others who have the +same hardware as you do. + +License +======== +HIDAPI may be used by one of three licenses as outlined in LICENSE.txt. + +Download +========= +HIDAPI can be downloaded from github + git clone git://github.com/signal11/hidapi.git + +Build Instructions +=================== + +This section is long. Don't be put off by this. It's not long because it's +complicated to build HIDAPI; it's quite the opposite. This section is long +because of the flexibility of HIDAPI and the large number of ways in which +it can be built and used. You will likely pick a single build method. + +HIDAPI can be built in several different ways. If you elect to build a +shared library, you will need to build it from the HIDAPI source +distribution. If you choose instead to embed HIDAPI directly into your +application, you can skip the building and look at the provided platform +Makefiles for guidance. These platform Makefiles are located in linux/ +libusb/ mac/ and windows/ and are called Makefile-manual. In addition, +Visual Studio projects are provided. Even if you're going to embed HIDAPI +into your project, it is still beneficial to build the example programs. + + +Prerequisites: +--------------- + + Linux: + ------- + On Linux, you will need to install development packages for libudev, + libusb and optionally Fox-toolkit (for the test GUI). On + Debian/Ubuntu systems these can be installed by running: + sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev + + If you downloaded the source directly from the git repository (using + git clone), you'll need Autotools: + sudo apt-get install autotools-dev autoconf automake libtool + + FreeBSD: + --------- + On FreeBSD you will need to install GNU make, libiconv, and + optionally Fox-Toolkit (for the test GUI). This is done by running + the following: + pkg_add -r gmake libiconv fox16 + + If you downloaded the source directly from the git repository (using + git clone), you'll need Autotools: + pkg_add -r autotools + + Mac: + ----- + On Mac, you will need to install Fox-Toolkit if you wish to build + the Test GUI. There are two ways to do this, and each has a slight + complication. Which method you use depends on your use case. + + If you wish to build the Test GUI just for your own testing on your + own computer, then the easiest method is to install Fox-Toolkit + using ports: + sudo port install fox + + If you wish to build the TestGUI app bundle to redistribute to + others, you will need to install Fox-toolkit from source. This is + because the version of fox that gets installed using ports uses the + ports X11 libraries which are not compatible with the Apple X11 + libraries. If you install Fox with ports and then try to distribute + your built app bundle, it will simply fail to run on other systems. + To install Fox-Toolkit manually, download the source package from + http://www.fox-toolkit.org, extract it, and run the following from + within the extracted source: + ./configure && make && make install + + Windows: + --------- + On Windows, if you want to build the test GUI, you will need to get + the hidapi-externals.zip package from the download site. This + contains pre-built binaries for Fox-toolkit. Extract + hidapi-externals.zip just outside of hidapi, so that + hidapi-externals and hidapi are on the same level, as shown: + + Parent_Folder + | + +hidapi + +hidapi-externals + + Again, this step is not required if you do not wish to build the + test GUI. + + +Building HIDAPI into a shared library on Unix Platforms: +--------------------------------------------------------- + +On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using +Mingw or Cygwin, the easiest way to build a standard system-installed shared +library is to use the GNU Autotools build system. If you checked out the +source from the git repository, run the following: + + ./bootstrap + ./configure + make + make install <----- as root, or using sudo + +If you downloaded a source package (ie: if you did not run git clone), you +can skip the ./bootstrap step. + +./configure can take several arguments which control the build. The two most +likely to be used are: + --enable-testgui + Enable build of the Test GUI. This requires Fox toolkit to + be installed. Instructions for installing Fox-Toolkit on + each platform are in the Prerequisites section above. + + --prefix=/usr + Specify where you want the output headers and libraries to + be installed. The example above will put the headers in + /usr/include and the binaries in /usr/lib. The default is to + install into /usr/local which is fine on most systems. + +Building the manual way on Unix platforms: +------------------------------------------- + +Manual Makefiles are provided mostly to give the user and idea what it takes +to build a program which embeds HIDAPI directly inside of it. These should +really be used as examples only. If you want to build a system-wide shared +library, use the Autotools method described above. + + To build HIDAPI using the manual makefiles, change to the directory + of your platform and run make. For example, on Linux run: + cd linux/ + make -f Makefile-manual + + To build the Test GUI using the manual makefiles: + cd testgui/ + make -f Makefile-manual + +Building on Windows: +--------------------- + +To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file +in the windows/ directory. + +To build the Test GUI on windows using Visual Studio, build the .sln file in +the testgui/ directory. + +To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions +in the section titled "Building HIDAPI into a shared library on Unix +Platforms" above. Note that building the Test GUI with MinGW or Cygwin will +require the Windows procedure in the Prerequisites section above (ie: +hidapi-externals.zip). + +To build HIDAPI using MinGW using the Manual Makefiles, see the section +"Building the manual way on Unix platforms" above. + +HIDAPI can also be built using the Windows DDK (now also called the Windows +Driver Kit or WDK). This method was originally required for the HIDAPI build +but not anymore. However, some users still prefer this method. It is not as +well supported anymore but should still work. Patches are welcome if it does +not. To build using the DDK: + + 1. Install the Windows Driver Kit (WDK) from Microsoft. + 2. From the Start menu, in the Windows Driver Kits folder, select Build + Environments, then your operating system, then the x86 Free Build + Environment (or one that is appropriate for your system). + 3. From the console, change directory to the windows/ddk_build/ directory, + which is part of the HIDAPI distribution. + 4. Type build. + 5. You can find the output files (DLL and LIB) in a subdirectory created + by the build system which is appropriate for your environment. On + Windows XP, this directory is objfre_wxp_x86/i386. + +Cross Compiling +================ + +This section talks about cross compiling HIDAPI for Linux using autotools. +This is useful for using HIDAPI on embedded Linux targets. These +instructions assume the most raw kind of embedded Linux build, where all +prerequisites will need to be built first. This process will of course vary +based on your embedded Linux build system if you are using one, such as +OpenEmbedded or Buildroot. + +For the purpose of this section, it will be assumed that the following +environment variables are exported. + + $ export STAGING=$HOME/out + $ export HOST=arm-linux + +STAGING and HOST can be modified to suit your setup. + +Prerequisites +-------------- + +Note that the build of libudev is the very basic configuration. + +Build Libusb. From the libusb source directory, run: + ./configure --host=$HOST --prefix=$STAGING + make + make install + +Build libudev. From the libudev source directory, run: + ./configure --disable-gudev --disable-introspection --disable-hwdb \ + --host=$HOST --prefix=$STAGING + make + make install + +Building HIDAPI +---------------- + +Build HIDAPI: + + PKG_CONFIG_DIR= \ + PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ + PKG_CONFIG_SYSROOT_DIR=$STAGING \ + ./configure --host=$HOST --prefix=$STAGING + + +Signal 11 Software - 2010-04-11 + 2010-07-28 + 2011-09-10 + 2012-05-01 + 2012-07-03 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/hidapi/hidapi.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/hidapi/hidapi.h new file mode 100644 index 0000000..e5bc2dc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/hidapi/hidapi.h @@ -0,0 +1,391 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/libusb/hid.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/libusb/hid.c new file mode 100644 index 0000000..474dff4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/libusb/hid.c @@ -0,0 +1,1512 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* GNU / LibUSB */ +#include +#ifndef __ANDROID__ +#include +#endif + +#include "hidapi.h" + +#ifdef __ANDROID__ + +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_PRINTF +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) do {} while (0) +#endif + +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif + +/* Uncomment to enable the retrieval of Usage and Usage Page in +hid_enumerate(). Warning, on platforms different from FreeBSD +this is very invasive as it requires the detach +and re-attach of the kernel driver. See comments inside hid_enumerate(). +libusb HIDAPI programs are encouraged to use the interface number +instead to differentiate between interfaces on a composite HID device. */ +/*#define INVASIVE_GET_USAGE*/ + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + + +struct hid_device_ { + /* Handle to the actual device. */ + libusb_device_handle *device_handle; + + /* Endpoint information */ + int input_endpoint; + int output_endpoint; + int input_ep_max_packet_size; + + /* The interface number of the HID */ + int interface; + + /* Indexes of Strings */ + int manufacturer_index; + int product_index; + int serial_index; + + /* Whether blocking reads are used */ + int blocking; /* boolean */ + + /* Read thread objects */ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + int shutdown_thread; + int cancelled; + struct libusb_transfer *transfer; + + /* List of received input reports. */ + struct input_report *input_reports; +}; + +static libusb_context *usb_context = NULL; + +uint16_t get_usb_code_for_current_locale(void); +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->blocking = 1; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the device itself */ + free(dev); +} + +#if 0 +/*TODO: Implement this funciton on hidapi/libusb.. */ +static void register_error(hid_device *device, const char *op) +{ + +} +#endif + +#ifdef INVASIVE_GET_USAGE +/* Get bytes from a HID Report Descriptor. + Only call with a num_bytes of 0, 1, 2, or 4. */ +static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) +{ + /* Return if there aren't enough bytes. */ + if (cur + num_bytes >= len) + return 0; + + if (num_bytes == 0) + return 0; + else if (num_bytes == 1) { + return rpt[cur+1]; + } + else if (num_bytes == 2) { + return (rpt[cur+2] * 256 + rpt[cur+1]); + } + else if (num_bytes == 4) { + return (rpt[cur+4] * 0x01000000 + + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); + } + else + return 0; +} + +/* Retrieves the device's Usage Page and Usage from the report + descriptor. The algorithm is simple, as it just returns the first + Usage and Usage Page that it finds in the descriptor. + The return value is 0 on success and -1 on failure. */ +static int get_usage(uint8_t *report_descriptor, size_t size, + unsigned short *usage_page, unsigned short *usage) +{ + unsigned int i = 0; + int size_code; + int data_len, key_size; + int usage_found = 0, usage_page_found = 0; + + while (i < size) { + int key = report_descriptor[i]; + int key_cmd = key & 0xfc; + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + if (key_cmd == 0x4) { + *usage_page = get_bytes(report_descriptor, size, data_len, i); + usage_page_found = 1; + //printf("Usage Page: %x\n", (uint32_t)*usage_page); + } + if (key_cmd == 0x8) { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + //printf("Usage: %x\n", (uint32_t)*usage); + } + + if (usage_page_found && usage_found) + return 0; /* success */ + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + return -1; /* failure */ +} +#endif /* INVASIVE_GET_USAGE */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 10 +/* The libusb version included in FreeBSD < 10 doesn't have this function. In + mainline libusb, it's inlined in libusb.h. This function will bear a striking + resemblance to that one, because there's about one way to code it. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + + +/* Get the first language the device says it reports. This comes from + USB string #0. */ +static uint16_t get_first_language(libusb_device_handle *dev) +{ + uint16_t buf[32]; + int len; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + return buf[1]; /* First two bytes are len and descriptor type. */ +} + +static int is_language_supported(libusb_device_handle *dev, uint16_t lang) +{ + uint16_t buf[32]; + int len; + int i; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + + len /= 2; /* language IDs are two-bytes each. */ + /* Start at index 1 because there are two bytes of protocol data. */ + for (i = 1; i < len; i++) { + if (buf[i] == lang) + return 1; + } + + return 0; +} + + +/* This function returns a newly allocated wide string containing the USB + device string numbered by the index. The returned string must be freed + by using free(). */ +static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) +{ + char buf[512]; + int len; + wchar_t *str = NULL; + +#ifndef __ANDROID__ /* we don't use iconv on Android */ + wchar_t wbuf[256]; + /* iconv variables */ + iconv_t ic; + size_t inbytes; + size_t outbytes; + size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else + char *inptr; +#endif + char *outptr; +#endif + + /* Determine which language to use. */ + uint16_t lang; + lang = get_usb_code_for_current_locale(); + if (!is_language_supported(dev, lang)) + lang = get_first_language(dev); + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; + +#ifdef __ANDROID__ + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for + code points that can be represented as a single UTF-16 character, + and will incorrectly convert any code points which require more + than one UTF-16 character. + + Skip over the first character (2-bytes). */ + len -= 2; + str = malloc((len / 2 + 1) * sizeof(wchar_t)); + int i; + for (i = 0; i < len / 2; i++) { + str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); + } + str[len / 2] = 0x00000000; + +#else + + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ + + /* Initialize iconv. */ + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); + return NULL; + } + + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). + Skip the first character (2-bytes). */ + inptr = buf+2; + inbytes = len-2; + outptr = (char*) wbuf; + outbytes = sizeof(wbuf); + res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); + if (res == (size_t)-1) { + LOG("iconv() failed\n"); + goto err; + } + + /* Write the terminating NULL. */ + wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; + if (outbytes >= sizeof(wbuf[0])) + *((wchar_t*)outptr) = 0x00000000; + + /* Allocate and copy the string. */ + str = wcsdup(wbuf); + +err: + iconv_close(ic); + +#endif + + return str; +} + +static char *make_path(libusb_device *dev, int interface_number) +{ + char str[64]; + snprintf(str, sizeof(str), "%04x:%04x:%02x", + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + interface_number); + str[sizeof(str)-1] = '\0'; + + return strdup(str); +} + + +int HID_API_EXPORT hid_init(void) +{ + if (!usb_context) { + const char *locale; + + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + } + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; + } + + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + ssize_t num_devs; + int i = 0; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + if(hid_init() < 0) + return NULL; + + num_devs = libusb_get_device_list(usb_context, &devs); + if (num_devs < 0) + return NULL; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int j, k; + int interface_num = 0; + + int res = libusb_get_device_descriptor(dev, &desc); + unsigned short dev_vid = desc.idVendor; + unsigned short dev_pid = desc.idProduct; + + res = libusb_get_active_config_descriptor(dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(dev, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + interface_num = intf_desc->bInterfaceNumber; + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num); + + res = libusb_open(dev, &handle); + + if (res >= 0) { + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); + + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); + +#ifdef INVASIVE_GET_USAGE +{ + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); + else + detached = 1; + } +#endif + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); + if (res >= 0) { + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; + } + else + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); +#ifdef DETACH_KERNEL_DRIVER + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } +#endif +} +#endif /* INVASIVE_GET_USAGE */ + + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; + + /* Interface Number */ + cur_dev->interface_number = interface_num; + } + } + } /* altsettings */ + } /* interfaces */ + libusb_free_config_descriptor(conf_desc); + } + } + + libusb_free_device_list(devs, 1); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (cur_dev->serial_number && + wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void read_callback(struct libusb_transfer *transfer) +{ + hid_device *dev = transfer->user_data; + int res; + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + struct input_report *rpt = malloc(sizeof(*rpt)); + rpt->data = malloc(transfer->actual_length); + memcpy(rpt->data, transfer->buffer, transfer->actual_length); + rpt->len = transfer->actual_length; + rpt->next = NULL; + + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + pthread_cond_signal(&dev->condition); + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + pthread_mutex_unlock(&dev->mutex); + } + else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + //LOG("Timeout (normal)\n"); + } + else { + LOG("Unknown transfer code: %d\n", transfer->status); + } + + /* Re-submit the transfer object. */ + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + dev->cancelled = 1; + } +} + + +static void *read_thread(void *param) +{ + hid_device *dev = param; + unsigned char *buf; + const size_t length = dev->input_ep_max_packet_size; + + /* Set up the transfer object. */ + buf = malloc(length); + dev->transfer = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(dev->transfer, + dev->device_handle, + dev->input_endpoint, + buf, + length, + read_callback, + dev, + 5000/*timeout*/); + + /* Make the first submission. Further submissions are made + from inside read_callback() */ + libusb_submit_transfer(dev->transfer); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Handle all the events. */ + while (!dev->shutdown_thread) { + int res; + res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } + } + } + + /* Cancel any transfer that may be pending. This call will fail + if no transfers are pending, but that's OK. */ + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* The dev->transfer->buffer and dev->transfer objects are cleaned up + in hid_close(). They are not cleaned up here because this thread + could end either due to a disconnect or due to a user + call to hid_close(). In both cases the objects can be safely + cleaned up after the call to pthread_join() (in hid_close()), but + since hid_close() calls libusb_cancel_transfer(), on these objects, + they can not be cleaned up here. */ + + return NULL; +} + + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + libusb_device **devs; + libusb_device *usb_dev; + int res; + int d = 0; + int good_open = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + libusb_get_device_list(usb_context, &devs); + while ((usb_dev = devs[d++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); + + if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + continue; + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); + if (!strcmp(dev_path, path)) { + /* Matched Paths. Open this device */ + + /* OPEN HERE */ + res = libusb_open(usb_dev, &dev->device_handle); + if (res < 0) { + LOG("can't open device\n"); + free(dev_path); + break; + } + good_open = 1; +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + free(dev_path); + good_open = 0; + break; + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + free(dev_path); + libusb_close(dev->device_handle); + good_open = 0; + break; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + } + free(dev_path); + } + } + } + libusb_free_config_descriptor(conf_desc); + + } + + libusb_free_device_list(devs, 1); + + /* If we have a good handle, return it. */ + if (good_open) { + return dev; + } + else { + /* Unable to open any devices. */ + free_hid_device(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + int report_number = data[0]; + int skipped_report_id = 0; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + + if (dev->output_endpoint <= 0) { + /* No interrupt out endpoint. Use the Control Endpoint */ + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID Set_Report*/, + (2/*HID output*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + length++; + + return length; + } + else { + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + length, + &actual_length, 1000); + + if (res < 0) + return -1; + + if (skipped_report_id) + actual_length++; + + return actual_length; + } +} + +/* Helper function, to simplify hid_read(). + This should be called with dev->mutex locked. */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + if (len > 0) + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static void cleanup_mutex(void *param) +{ + hid_device *dev = param; + pthread_mutex_unlock(&dev->mutex); +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + +#if 0 + int transferred; + int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); + LOG("transferred: %d\n", transferred); + return transferred; +#endif + + pthread_mutex_lock(&dev->mutex); + pthread_cleanup_push(&cleanup_mutex, dev); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been disconnected. + An error code of -1 should be returned. */ + bytes_read = -1; + goto ret; + } + + if (milliseconds == -1) { + /* Blocking */ + while (!dev->input_reports && !dev->shutdown_thread) { + pthread_cond_wait(&dev->condition, &dev->mutex); + } + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + while (!dev->input_reports && !dev->shutdown_thread) { + res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + if (res == 0) { + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + break; + } + + /* If we're here, there was a spurious wake up + or the read thread was shutdown. Run the + loop again (ie: don't break). */ + } + else if (res == ETIMEDOUT) { + /* Timed out. */ + bytes_read = 0; + break; + } + else { + /* Error. */ + bytes_read = -1; + break; + } + } + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + pthread_mutex_unlock(&dev->mutex); + pthread_cleanup_pop(0); + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + + return 0; +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + /* Account for the report ID */ + if (skipped_report_id) + length++; + + return length; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + data++; + length--; + skipped_report_id = 1; + } + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + res++; + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + libusb_cancel_transfer(dev->transfer); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Clean up the Transfer objects allocated in read_thread(). */ + free(dev->transfer->buffer); + libusb_free_transfer(dev->transfer); + + /* release the interface */ + libusb_release_interface(dev->device_handle, dev->interface); + + /* Close the handle */ + libusb_close(dev->device_handle); + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->product_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + wchar_t *str; + + str = get_usb_string(dev->device_handle, string_index); + if (str) { + wcsncpy(string, str, maxlen); + string[maxlen-1] = L'\0'; + free(str); + return 0; + } + else + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} + + +struct lang_map_entry { + const char *name; + const char *string_code; + uint16_t usb_code; +}; + +#define LANG(name,code,usb_code) { name, code, usb_code } +static struct lang_map_entry lang_map[] = { + LANG("Afrikaans", "af", 0x0436), + LANG("Albanian", "sq", 0x041C), + LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), + LANG("Arabic - Bahrain", "ar_bh", 0x3C01), + LANG("Arabic - Algeria", "ar_dz", 0x1401), + LANG("Arabic - Egypt", "ar_eg", 0x0C01), + LANG("Arabic - Iraq", "ar_iq", 0x0801), + LANG("Arabic - Jordan", "ar_jo", 0x2C01), + LANG("Arabic - Kuwait", "ar_kw", 0x3401), + LANG("Arabic - Lebanon", "ar_lb", 0x3001), + LANG("Arabic - Libya", "ar_ly", 0x1001), + LANG("Arabic - Morocco", "ar_ma", 0x1801), + LANG("Arabic - Oman", "ar_om", 0x2001), + LANG("Arabic - Qatar", "ar_qa", 0x4001), + LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), + LANG("Arabic - Syria", "ar_sy", 0x2801), + LANG("Arabic - Tunisia", "ar_tn", 0x1C01), + LANG("Arabic - Yemen", "ar_ye", 0x2401), + LANG("Armenian", "hy", 0x042B), + LANG("Azeri - Latin", "az_az", 0x042C), + LANG("Azeri - Cyrillic", "az_az", 0x082C), + LANG("Basque", "eu", 0x042D), + LANG("Belarusian", "be", 0x0423), + LANG("Bulgarian", "bg", 0x0402), + LANG("Catalan", "ca", 0x0403), + LANG("Chinese - China", "zh_cn", 0x0804), + LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), + LANG("Chinese - Macau SAR", "zh_mo", 0x1404), + LANG("Chinese - Singapore", "zh_sg", 0x1004), + LANG("Chinese - Taiwan", "zh_tw", 0x0404), + LANG("Croatian", "hr", 0x041A), + LANG("Czech", "cs", 0x0405), + LANG("Danish", "da", 0x0406), + LANG("Dutch - Netherlands", "nl_nl", 0x0413), + LANG("Dutch - Belgium", "nl_be", 0x0813), + LANG("English - Australia", "en_au", 0x0C09), + LANG("English - Belize", "en_bz", 0x2809), + LANG("English - Canada", "en_ca", 0x1009), + LANG("English - Caribbean", "en_cb", 0x2409), + LANG("English - Ireland", "en_ie", 0x1809), + LANG("English - Jamaica", "en_jm", 0x2009), + LANG("English - New Zealand", "en_nz", 0x1409), + LANG("English - Phillippines", "en_ph", 0x3409), + LANG("English - Southern Africa", "en_za", 0x1C09), + LANG("English - Trinidad", "en_tt", 0x2C09), + LANG("English - Great Britain", "en_gb", 0x0809), + LANG("English - United States", "en_us", 0x0409), + LANG("Estonian", "et", 0x0425), + LANG("Farsi", "fa", 0x0429), + LANG("Finnish", "fi", 0x040B), + LANG("Faroese", "fo", 0x0438), + LANG("French - France", "fr_fr", 0x040C), + LANG("French - Belgium", "fr_be", 0x080C), + LANG("French - Canada", "fr_ca", 0x0C0C), + LANG("French - Luxembourg", "fr_lu", 0x140C), + LANG("French - Switzerland", "fr_ch", 0x100C), + LANG("Gaelic - Ireland", "gd_ie", 0x083C), + LANG("Gaelic - Scotland", "gd", 0x043C), + LANG("German - Germany", "de_de", 0x0407), + LANG("German - Austria", "de_at", 0x0C07), + LANG("German - Liechtenstein", "de_li", 0x1407), + LANG("German - Luxembourg", "de_lu", 0x1007), + LANG("German - Switzerland", "de_ch", 0x0807), + LANG("Greek", "el", 0x0408), + LANG("Hebrew", "he", 0x040D), + LANG("Hindi", "hi", 0x0439), + LANG("Hungarian", "hu", 0x040E), + LANG("Icelandic", "is", 0x040F), + LANG("Indonesian", "id", 0x0421), + LANG("Italian - Italy", "it_it", 0x0410), + LANG("Italian - Switzerland", "it_ch", 0x0810), + LANG("Japanese", "ja", 0x0411), + LANG("Korean", "ko", 0x0412), + LANG("Latvian", "lv", 0x0426), + LANG("Lithuanian", "lt", 0x0427), + LANG("F.Y.R.O. Macedonia", "mk", 0x042F), + LANG("Malay - Malaysia", "ms_my", 0x043E), + LANG("Malay – Brunei", "ms_bn", 0x083E), + LANG("Maltese", "mt", 0x043A), + LANG("Marathi", "mr", 0x044E), + LANG("Norwegian - Bokml", "no_no", 0x0414), + LANG("Norwegian - Nynorsk", "no_no", 0x0814), + LANG("Polish", "pl", 0x0415), + LANG("Portuguese - Portugal", "pt_pt", 0x0816), + LANG("Portuguese - Brazil", "pt_br", 0x0416), + LANG("Raeto-Romance", "rm", 0x0417), + LANG("Romanian - Romania", "ro", 0x0418), + LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), + LANG("Russian", "ru", 0x0419), + LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), + LANG("Sanskrit", "sa", 0x044F), + LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), + LANG("Serbian - Latin", "sr_sp", 0x081A), + LANG("Setsuana", "tn", 0x0432), + LANG("Slovenian", "sl", 0x0424), + LANG("Slovak", "sk", 0x041B), + LANG("Sorbian", "sb", 0x042E), + LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), + LANG("Spanish - Argentina", "es_ar", 0x2C0A), + LANG("Spanish - Bolivia", "es_bo", 0x400A), + LANG("Spanish - Chile", "es_cl", 0x340A), + LANG("Spanish - Colombia", "es_co", 0x240A), + LANG("Spanish - Costa Rica", "es_cr", 0x140A), + LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), + LANG("Spanish - Ecuador", "es_ec", 0x300A), + LANG("Spanish - Guatemala", "es_gt", 0x100A), + LANG("Spanish - Honduras", "es_hn", 0x480A), + LANG("Spanish - Mexico", "es_mx", 0x080A), + LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), + LANG("Spanish - Panama", "es_pa", 0x180A), + LANG("Spanish - Peru", "es_pe", 0x280A), + LANG("Spanish - Puerto Rico", "es_pr", 0x500A), + LANG("Spanish - Paraguay", "es_py", 0x3C0A), + LANG("Spanish - El Salvador", "es_sv", 0x440A), + LANG("Spanish - Uruguay", "es_uy", 0x380A), + LANG("Spanish - Venezuela", "es_ve", 0x200A), + LANG("Southern Sotho", "st", 0x0430), + LANG("Swahili", "sw", 0x0441), + LANG("Swedish - Sweden", "sv_se", 0x041D), + LANG("Swedish - Finland", "sv_fi", 0x081D), + LANG("Tamil", "ta", 0x0449), + LANG("Tatar", "tt", 0X0444), + LANG("Thai", "th", 0x041E), + LANG("Turkish", "tr", 0x041F), + LANG("Tsonga", "ts", 0x0431), + LANG("Ukrainian", "uk", 0x0422), + LANG("Urdu", "ur", 0x0420), + LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), + LANG("Uzbek – Latin", "uz_uz", 0x0443), + LANG("Vietnamese", "vi", 0x042A), + LANG("Xhosa", "xh", 0x0434), + LANG("Yiddish", "yi", 0x043D), + LANG("Zulu", "zu", 0x0435), + LANG(NULL, NULL, 0x0), +}; + +uint16_t get_usb_code_for_current_locale(void) +{ + char *locale; + char search_string[64]; + char *ptr; + struct lang_map_entry *lang; + + /* Get the current locale. */ + locale = setlocale(0, NULL); + if (!locale) + return 0x0; + + /* Make a copy of the current locale string. */ + strncpy(search_string, locale, sizeof(search_string)); + search_string[sizeof(search_string)-1] = '\0'; + + /* Chop off the encoding part, and make it lower case. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '.') { + *ptr = '\0'; + break; + } + ptr++; + } + + /* Find the entry which matches the string code of our locale. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } + + /* There was no match. Find with just the language only. */ + /* Chop off the variant. Chop it off at the '_'. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '_') { + *ptr = '\0'; + break; + } + ptr++; + } + +#if 0 /* TODO: Do we need this? */ + /* Find the entry which matches the string code of our language. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } +#endif + + /* Found nothing. */ + return 0x0; +} + +#ifdef __cplusplus +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/mac/hid.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/mac/hid.c new file mode 100644 index 0000000..e0756a1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/mac/hid.c @@ -0,0 +1,1110 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 2010-07-03 + + Copyright 2010, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* See Apple Technical Note TN2187 for details on IOHidManager. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidapi.h" + +/* Barrier implementation because Mac OSX doesn't have pthread_barrier. + It also doesn't have clock_gettime(). So much for POSIX and SUSv2. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + +struct hid_device_ { + IOHIDDeviceRef device_handle; + int blocking; + int uses_numbered_reports; + int disconnected; + CFStringRef run_loop_mode; + CFRunLoopRef run_loop; + CFRunLoopSourceRef source; + uint8_t *input_report_buf; + CFIndex max_input_report_len; + struct input_report *input_reports; + + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ + int shutdown_thread; +}; + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = NULL; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + dev->disconnected = 0; + dev->run_loop_mode = NULL; + dev->run_loop = NULL; + dev->source = NULL; + dev->input_report_buf = NULL; + dev->input_reports = NULL; + dev->shutdown_thread = 0; + + /* Thread objects */ + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + if (!dev) + return; + + /* Delete any input reports still left over. */ + struct input_report *rpt = dev->input_reports; + while (rpt) { + struct input_report *next = rpt->next; + free(rpt->data); + free(rpt); + rpt = next; + } + + /* Free the string and the report buffer. The check for NULL + is necessary here as CFRelease() doesn't handle NULL like + free() and others do. */ + if (dev->run_loop_mode) + CFRelease(dev->run_loop_mode); + if (dev->source) + CFRelease(dev->source); + free(dev->input_report_buf); + + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->shutdown_barrier); + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the structure itself. */ + free(dev); +} + +static IOHIDManagerRef hid_mgr = 0x0; + + +#if 0 +static void register_error(hid_device *device, const char *op) +{ + +} +#endif + + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref; + int32_t value; + + ref = IOHIDDeviceGetProperty(device, key); + if (ref) { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); + return value; + } + } + return 0; +} + +static unsigned short get_vendor_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); +} + +static unsigned short get_product_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDProductIDKey)); +} + +static int32_t get_max_report_length(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); +} + +static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) +{ + CFStringRef str; + + if (!len) + return 0; + + str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0; + + if (str) { + CFIndex str_len = CFStringGetLength(str); + CFRange range; + CFIndex used_buf_len; + CFIndex chars_copied; + + len --; + + range.location = 0; + range.length = ((size_t)str_len > len)? len: (size_t)str_len; + chars_copied = CFStringGetBytes(str, + range, + kCFStringEncodingUTF32LE, + (char)'?', + FALSE, + (UInt8*)buf, + len * sizeof(wchar_t), + &used_buf_len); + + if (chars_copied == len) + buf[len] = 0; /* len is decremented above */ + else + buf[chars_copied] = 0; + + return 0; + } + else + return -1; + +} + +static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); +} + +static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); +} + +static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); +} + + +/* Implementation of wcsdup() for Mac. */ +static wchar_t *dup_wcs(const wchar_t *s) +{ + size_t len = wcslen(s); + wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); + wcscpy(ret, s); + + return ret; +} + +/* hidapi_IOHIDDeviceGetService() + * + * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: + * - on OS X 10.6 and above, calling IOHIDDeviceGetService() + * - on OS X 10.5, extract it from the IOHIDDevice struct + */ +static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) +{ + static void *iokit_framework = NULL; + static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; + + /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. + * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL + * and the fallback method will be used. + */ + if (iokit_framework == NULL) { + iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); + + if (iokit_framework != NULL) + dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); + } + + if (dynamic_IOHIDDeviceGetService != NULL) { + /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ + return dynamic_IOHIDDeviceGetService(device); + } + else + { + /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. + * + * Be naughty and pull the service out of the IOHIDDevice. + * IOHIDDevice is an opaque struct not exposed to applications, but its + * layout is stable through all available versions of OS X. + * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. + */ + struct IOHIDDevice_internal { + /* The first field of the IOHIDDevice struct is a + * CFRuntimeBase (which is a private CF struct). + * + * a, b, and c are the 3 fields that make up a CFRuntimeBase. + * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h + * + * The second field of the IOHIDDevice is the io_service_t we're looking for. + */ + uintptr_t a; + uint8_t b[4]; +#if __LP64__ + uint32_t c; +#endif + io_service_t service; + }; + struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; + + return tmp->service; + } +} + +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ +static int init_hid_manager(void) +{ + /* Initialize all the HID Manager Objects */ + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_mgr) { + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; + } + + return -1; +} + +/* Initialize the IOHIDManager if necessary. This is the public function, and + it is safe to call this function repeatedly. Return 0 for success and -1 + for failure. */ +int HID_API_EXPORT hid_init(void) +{ + if (!hid_mgr) { + return init_hid_manager(); + } + + /* Already initialized. */ + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (hid_mgr) { + /* Close the HID manager. */ + IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); + CFRelease(hid_mgr); + hid_mgr = NULL; + } + + return 0; +} + +static void process_pending_events(void) { + SInt32 res; + do { + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + CFIndex num_devices; + int i; + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* give the IOHIDManager a chance to update itself */ + process_pending_events(); + + /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + /* Convert the list into a C array so we can iterate easily. */ + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + /* Iterate over each device, making an entry for it. */ + for (i = 0; i < num_devices; i++) { + unsigned short dev_vid; + unsigned short dev_pid; + #define BUF_LEN 256 + wchar_t buf[BUF_LEN]; + + IOHIDDeviceRef dev = device_array[i]; + + if (!dev) { + continue; + } + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); + cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); + + /* Fill out the record */ + cur_dev->next = NULL; + + /* Fill in the path (IOService plane) */ + iokit_dev = hidapi_IOHIDDeviceGetService(dev); + res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); + if (res == KERN_SUCCESS) + cur_dev->path = strdup(path); + else + cur_dev->path = strdup(""); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* Interface Number (Unsupported on Mac)*/ + cur_dev->interface_number = -1; + } + } + + free(device_array); + CFRelease(device_set); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device * handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void hid_device_removal_callback(void *context, IOReturn result, + void *sender) +{ + /* Stop the Run Loop for this device. */ + hid_device *d = context; + + d->disconnected = 1; + CFRunLoopStop(d->run_loop); +} + +/* The Run Loop calls this function for each input report received. + This function puts the data into a linked list to be picked up by + hid_read(). */ +static void hid_report_callback(void *context, IOReturn result, void *sender, + IOHIDReportType report_type, uint32_t report_id, + uint8_t *report, CFIndex report_length) +{ + struct input_report *rpt; + hid_device *dev = context; + + /* Make a new Input Report object */ + rpt = calloc(1, sizeof(struct input_report)); + rpt->data = calloc(1, report_length); + memcpy(rpt->data, report, report_length); + rpt->len = report_length; + rpt->next = NULL; + + /* Lock this section */ + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + + /* Signal a waiting thread that there is data. */ + pthread_cond_signal(&dev->condition); + + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + +} + +/* This gets called when the read_thread's run loop gets signaled by + hid_close(), and serves to stop the read_thread's run loop. */ +static void perform_signal_callback(void *context) +{ + hid_device *dev = context; + CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ +} + +static void *read_thread(void *param) +{ + hid_device *dev = param; + SInt32 code; + + /* Move the device's run loop to this thread. */ + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); + + /* Create the RunLoopSource which is used to signal the + event loop to stop when hid_close() is called. */ + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = 0; + ctx.info = dev; + ctx.perform = &perform_signal_callback; + dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); + + /* Store off the Run Loop so it can be stopped from hid_close() + and on device disconnection. */ + dev->run_loop = CFRunLoopGetCurrent(); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input + reports into the hid_report_callback(). */ + while (!dev->shutdown_thread && !dev->disconnected) { + code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); + /* Return if the device has been disconnected */ + if (code == kCFRunLoopRunFinished) { + dev->disconnected = 1; + break; + } + + + /* Break if The Run Loop returns Finished or Stopped. */ + if (code != kCFRunLoopRunTimedOut && + code != kCFRunLoopRunHandledSource) { + /* There was some kind of error. Setting + shutdown seems to make sense, but + there may be something else more appropriate */ + dev->shutdown_thread = 1; + break; + } + } + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* Wait here until hid_close() is called and makes it past + the call to CFRunLoopWakeUp(). This thread still needs to + be valid when that function is called on the other thread. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + return NULL; +} + +/* hid_open_path() + * + * path must be a valid path to an IOHIDDevice in the IOService plane + * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" + */ +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + io_registry_entry_t entry = MACH_PORT_NULL; + + dev = new_hid_device(); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + return NULL; + + /* Get the IORegistry entry for the given path */ + entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + if (entry == MACH_PORT_NULL) { + /* Path wasn't valid (maybe device was removed?) */ + goto return_error; + } + + /* Create an IOHIDDevice for the entry */ + dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); + if (dev->device_handle == NULL) { + /* Error creating the HID device */ + goto return_error; + } + + /* Open the IOHIDDevice */ + IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; + + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", dev->device_handle); + dev->run_loop_mode = + CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + IOObjectRelease(entry); + return dev; + } + else { + goto return_error; + } + +return_error: + if (dev->device_handle != NULL) + CFRelease(dev->device_handle); + + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + + free_hid_device(dev); + return NULL; +} + +static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) +{ + const unsigned char *data_to_send; + size_t length_to_send; + IOReturn res; + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + return -1; + + if (data[0] == 0x0) { + /* Not using numbered Reports. + Don't send the report number. */ + data_to_send = data+1; + length_to_send = length-1; + } + else { + /* Using numbered Reports. + Send the Report Number */ + data_to_send = data; + length_to_send = length; + } + + if (!dev->disconnected) { + res = IOHIDDeviceSetReport(dev->device_handle, + type, + data[0], /* Report ID*/ + data_to_send, length_to_send); + + if (res == kIOReturnSuccess) { + return length; + } + else + return -1; + } + + return -1; +} + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeOutput, data, length); +} + +/* Helper function, so that this isn't duplicated in hid_read(). */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + while (!dev->input_reports) { + int res = pthread_cond_wait(cond, mutex); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + while (!dev->input_reports) { + int res = pthread_cond_timedwait(cond, mutex, abstime); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; + +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + + /* Lock the access to the report list. */ + pthread_mutex_lock(&dev->mutex); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + /* Return if the device has been disconnected. */ + if (dev->disconnected) { + bytes_read = -1; + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been closed (or there + has been an error. An error code of -1 should + be returned. */ + bytes_read = -1; + goto ret; + } + + /* There is no data. Go to sleep and wait for data. */ + + if (milliseconds == -1) { + /* Blocking */ + int res; + res = cond_wait(dev, &dev->condition, &dev->mutex); + if (res == 0) + bytes_read = return_data(dev, data, length); + else { + /* There was an error, or a device disconnection. */ + bytes_read = -1; + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); + if (res == 0) + bytes_read = return_data(dev, data, length); + else if (res == ETIMEDOUT) + bytes_read = 0; + else + bytes_read = -1; + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* All Nonblocking operation is handled by the library. */ + dev->blocking = !nonblock; + + return 0; +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeFeature, data, length); +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + CFIndex len = length; + IOReturn res; + + /* Return if the device has been unplugged. */ + if (dev->disconnected) + return -1; + + res = IOHIDDeviceGetReport(dev->device_handle, + kIOHIDReportTypeFeature, + data[0], /* Report ID */ + data, &len); + if (res == kIOReturnSuccess) + return len; + else + return -1; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Disconnect the report callback before close. */ + if (!dev->disconnected) { + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + } + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + + /* Wake up the run thread's event loop so that the thread can exit. */ + CFRunLoopSourceSignal(dev->source); + CFRunLoopWakeUp(dev->run_loop); + + /* Notify the read thread that it can shut down now. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + } + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + CFRelease(dev->device_handle); + + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_manufacturer_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_product_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_serial_number(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + /* TODO: */ + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + /* TODO: */ + + return NULL; +} + + + + + + + +#if 0 +static int32_t get_location_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); +} + +static int32_t get_usage(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + return res; +} + +static int32_t get_usage_page(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + return res; +} + +static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); +} + + +int main(void) +{ + IOHIDManagerRef mgr; + int i; + + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + + CFIndex num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + printf("Device: %p\n", dev); + printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); + + wchar_t serial[256], buf[256]; + char cbuf[256]; + get_serial_number(dev, serial, 256); + + + printf(" Serial: %ls\n", serial); + printf(" Loc: %ld\n", get_location_id(dev)); + get_transport(dev, buf, 256); + printf(" Trans: %ls\n", buf); + make_path(dev, cbuf, 256); + printf(" Path: %s\n", cbuf); + + } + + return 0; +} +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/windows/hid.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/windows/hid.c new file mode 100755 index 0000000..86810d7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/hidapi/windows/hid.c @@ -0,0 +1,944 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include + + +#include "hidapi.h" + +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + +#ifdef _MSC_VER + /* Thanks Microsoft, but I know how to use strncpy(). */ + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + /* Since we're not building with the DDK, and the HID header + files aren't part of the SDK, we have to define all this + stuff here. In lookup_functions(), the function pointers + defined below are set. */ + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef void* PHIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x110000 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *device, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPVOID)&msg, 0/*sz*/, + NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(device->last_error_str); + device->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path, BOOL enumerate) +{ + HANDLE handle; + DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); + DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; + + handle = CreateFileA(path, + desired_access, + share_mode, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0; ; i++) { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + goto cont; + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + goto cont; + + if (strcmp(driver_name, "HIDClass") == 0) { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + break; + } + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && + (product_id == 0x0 || attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, FALSE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) { + /* Unable to open the device. */ + register_error(dev, "CreateFile"); + goto err; + } + + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + unsigned char *buf; + memset(&ol, 0, sizeof(ol)); + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *) data; + } else { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *) malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* WriteFile() failed. Return error. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait here until the write is done. This makes + hid_write() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + /* The Write operation failed. */ + register_error(dev, "WriteFile"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + size_t copy_len = 0; + BOOL res; + + /* Copy the handle for convenience. */ + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf+1, copy_len); + } + else { + /* Copy the whole buffer, report number and all. */ + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return copy_len; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + /* The operation failed. */ + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/AUTHORS new file mode 100644 index 0000000..70d407b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/AUTHORS @@ -0,0 +1,89 @@ +Copyright © 2001 Johannes Erdfelt +Copyright © 2007-2009 Daniel Drake +Copyright © 2010-2012 Peter Stuge +Copyright © 2008-2016 Nathan Hjelm +Copyright © 2009-2013 Pete Batard +Copyright © 2009-2013 Ludovic Rousseau +Copyright © 2010-2012 Michael Plante +Copyright © 2011-2013 Hans de Goede +Copyright © 2012-2013 Martin Pieuchot +Copyright © 2012-2013 Toby Gray +Copyright © 2013-2015 Chris Dickens + +Other contributors: +Akshay Jaggi +Alan Ott +Alan Stern +Alex Vatchenko +Andrew Fernandes +Anthony Clay +Antonio Ospite +Artem Egorkine +Aurelien Jarno +Bastien Nocera +Bei Zhang +Benjamin Dobell +Carl Karsten +Colin Walters +Dave Camarillo +David Engraf +David Moore +Davidlohr Bueso +Federico Manzan +Felipe Balbi +Florian Albrechtskirchinger +Francesco Montorsi +Francisco Facioni +Gaurav Gupta +Graeme Gill +Gustavo Zacarias +Hans Ulrich Niedermann +Hector Martin +Hoi-Ho Chan +Ilya Konstantinov +James Hanko +John Sheu +Joshua Blake +Justin Bischoff +Karsten Koenig +Konrad Rzepecki +Kuangye Guo +Lars Kanis +Lars Wirzenius +Luca Longinotti +Marcus Meissner +Markus Heidelberg +Martin Ettl +Martin Koegler +Matthias Bolte +Mike Frysinger +Mikhail Gusarov +Moritz Fischer +Ларионов Даниил +Nicholas Corgan +Omri Iluz +Orin Eman +Paul Fertser +Pekka Nikander +Rob Walker +Sean McBride +Sebastian Pipping +Simon Haggett +Simon Newton +Thomas Röfer +Tim Hutt +Tim Roberts +Tobias Klauser +Toby Peterson +Tormod Volden +Trygve Laugstøl +Uri Lublin +Vasily Khoruzhick +Vegard Storheil Eriksen +Venkatesh Shukla +Vitali Lovich +Xiaofan Chen +Zoltán Kovács +Роман Донченко +parafin +xantares diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/COPYING b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/config.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/config.h new file mode 100644 index 0000000..e004f03 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/config.h @@ -0,0 +1,3 @@ +#ifndef CONFIG_H +#define CONFIG_H +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/core.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/core.c new file mode 100644 index 0000000..d45bfe1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/core.c @@ -0,0 +1,2523 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Core functions for libusb + * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYSLOG_H +#include +#endif + +#ifdef __ANDROID__ +#include +#endif + +#include "libusbi.h" +#include "hotplug.h" + +#if defined(OS_LINUX) +const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend; +#elif defined(OS_DARWIN) +const struct usbi_os_backend * const usbi_backend = &darwin_backend; +#elif defined(OS_OPENBSD) +const struct usbi_os_backend * const usbi_backend = &openbsd_backend; +#elif defined(OS_NETBSD) +const struct usbi_os_backend * const usbi_backend = &netbsd_backend; +#elif defined(OS_WINDOWS) + +#if defined(USE_USBDK) +const struct usbi_os_backend * const usbi_backend = &usbdk_backend; +#else +const struct usbi_os_backend * const usbi_backend = &windows_backend; +#endif + +#elif defined(OS_WINCE) +const struct usbi_os_backend * const usbi_backend = &wince_backend; +#elif defined(OS_HAIKU) +const struct usbi_os_backend * const usbi_backend = &haiku_usb_raw_backend; +#elif defined (OS_SUNOS) +const struct usbi_os_backend * const usbi_backend = &sunos_backend; +#else +#error "Unsupported OS" +#endif + +struct libusb_context *usbi_default_context = NULL; +static const struct libusb_version libusb_version_internal = + { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, + LIBUSB_RC, "http://libusb.info" }; +static int default_context_refcnt = 0; +static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; +static struct timespec timestamp_origin = { 0, 0 }; + +usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER; +struct list_head active_contexts_list; + +/** + * \mainpage libusb-1.0 API Reference + * + * \section intro Introduction + * + * libusb is an open source library that allows you to communicate with USB + * devices from userspace. For more info, see the + * libusb homepage. + * + * This documentation is aimed at application developers wishing to + * communicate with USB peripherals from their own software. After reviewing + * this documentation, feedback and questions can be sent to the + * libusb-devel mailing list. + * + * This documentation assumes knowledge of how to operate USB devices from + * a software standpoint (descriptors, configurations, interfaces, endpoints, + * control/bulk/interrupt/isochronous transfers, etc). Full information + * can be found in the USB 3.0 + * Specification which is available for free download. You can probably + * find less verbose introductions by searching the web. + * + * \section API Application Programming Interface (API) + * + * See the \ref libusb_api page for a complete list of the libusb functions. + * + * \section features Library features + * + * - All transfer types supported (control/bulk/interrupt/isochronous) + * - 2 transfer interfaces: + * -# Synchronous (simple) + * -# Asynchronous (more complicated, but more powerful) + * - Thread safe (although the asynchronous interface means that you + * usually won't need to thread) + * - Lightweight with lean API + * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer + * - Hotplug support (on some platforms). See \ref libusb_hotplug. + * + * \section gettingstarted Getting Started + * + * To begin reading the API documentation, start with the Modules page which + * links to the different categories of libusb's functionality. + * + * One decision you will have to make is whether to use the synchronous + * or the asynchronous data transfer interface. The \ref libusb_io documentation + * provides some insight into this topic. + * + * Some example programs can be found in the libusb source distribution under + * the "examples" subdirectory. The libusb homepage includes a list of + * real-life project examples which use libusb. + * + * \section errorhandling Error handling + * + * libusb functions typically return 0 on success or a negative error code + * on failure. These negative error codes relate to LIBUSB_ERROR constants + * which are listed on the \ref libusb_misc "miscellaneous" documentation page. + * + * \section msglog Debug message logging + * + * libusb uses stderr for all logging. By default, logging is set to NONE, + * which means that no output will be produced. However, unless the library + * has been compiled with logging disabled, then any application calls to + * libusb_set_debug(), or the setting of the environmental variable + * LIBUSB_DEBUG outside of the application, can result in logging being + * produced. Your application should therefore not close stderr, but instead + * direct it to the null device if its output is undesirable. + * + * The libusb_set_debug() function can be used to enable logging of certain + * messages. Under standard configuration, libusb doesn't really log much + * so you are advised to use this function to enable all error/warning/ + * informational messages. It will help debug problems with your software. + * + * The logged messages are unstructured. There is no one-to-one correspondence + * between messages being logged and success or failure return codes from + * libusb functions. There is no format to the messages, so you should not + * try to capture or parse them. They are not and will not be localized. + * These messages are not intended to being passed to your application user; + * instead, you should interpret the error codes returned from libusb functions + * and provide appropriate notification to the user. The messages are simply + * there to aid you as a programmer, and if you're confused because you're + * getting a strange error code from a libusb function, enabling message + * logging may give you a suitable explanation. + * + * The LIBUSB_DEBUG environment variable can be used to enable message logging + * at run-time. This environment variable should be set to a log level number, + * which is interpreted the same as the libusb_set_debug() parameter. When this + * environment variable is set, the message logging verbosity level is fixed + * and libusb_set_debug() effectively does nothing. + * + * libusb can be compiled without any logging functions, useful for embedded + * systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment + * variable have no effects. + * + * libusb can also be compiled with verbose debugging messages always. When + * the library is compiled in this way, all messages of all verbosities are + * always logged. libusb_set_debug() and the LIBUSB_DEBUG environment variable + * have no effects. + * + * \section remarks Other remarks + * + * libusb does have imperfections. The \ref libusb_caveats "caveats" page attempts + * to document these. + */ + +/** + * \page libusb_caveats Caveats + * + * \section devresets Device resets + * + * The libusb_reset_device() function allows you to reset a device. If your + * program has to call such a function, it should obviously be aware that + * the reset will cause device state to change (e.g. register values may be + * reset). + * + * The problem is that any other program could reset the device your program + * is working with, at any time. libusb does not offer a mechanism to inform + * you when this has happened, so if someone else resets your device it will + * not be clear to your own program why the device state has changed. + * + * Ultimately, this is a limitation of writing drivers in userspace. + * Separation from the USB stack in the underlying kernel makes it difficult + * for the operating system to deliver such notifications to your program. + * The Linux kernel USB stack allows such reset notifications to be delivered + * to in-kernel USB drivers, but it is not clear how such notifications could + * be delivered to second-class drivers that live in userspace. + * + * \section blockonly Blocking-only functionality + * + * The functionality listed below is only available through synchronous, + * blocking functions. There are no asynchronous/non-blocking alternatives, + * and no clear ways of implementing these. + * + * - Configuration activation (libusb_set_configuration()) + * - Interface/alternate setting activation (libusb_set_interface_alt_setting()) + * - Releasing of interfaces (libusb_release_interface()) + * - Clearing of halt/stall condition (libusb_clear_halt()) + * - Device resets (libusb_reset_device()) + * + * \section configsel Configuration selection and handling + * + * When libusb presents a device handle to an application, there is a chance + * that the corresponding device may be in unconfigured state. For devices + * with multiple configurations, there is also a chance that the configuration + * currently selected is not the one that the application wants to use. + * + * The obvious solution is to add a call to libusb_set_configuration() early + * on during your device initialization routines, but there are caveats to + * be aware of: + * -# If the device is already in the desired configuration, calling + * libusb_set_configuration() using the same configuration value will cause + * a lightweight device reset. This may not be desirable behaviour. + * -# In the case where the desired configuration is already active, libusb + * may not even be able to perform a lightweight device reset. For example, + * take my USB keyboard with fingerprint reader: I'm interested in driving + * the fingerprint reader interface through libusb, but the kernel's + * USB-HID driver will almost always have claimed the keyboard interface. + * Because the kernel has claimed an interface, it is not even possible to + * perform the lightweight device reset, so libusb_set_configuration() will + * fail. (Luckily the device in question only has a single configuration.) + * -# libusb will be unable to set a configuration if other programs or + * drivers have claimed interfaces. In particular, this means that kernel + * drivers must be detached from all the interfaces before + * libusb_set_configuration() may succeed. + * + * One solution to some of the above problems is to consider the currently + * active configuration. If the configuration we want is already active, then + * we don't have to select any configuration: +\code +cfg = -1; +libusb_get_configuration(dev, &cfg); +if (cfg != desired) + libusb_set_configuration(dev, desired); +\endcode + * + * This is probably suitable for most scenarios, but is inherently racy: + * another application or driver may change the selected configuration + * after the libusb_get_configuration() call. + * + * Even in cases where libusb_set_configuration() succeeds, consider that other + * applications or drivers may change configuration after your application + * calls libusb_set_configuration(). + * + * One possible way to lock your device into a specific configuration is as + * follows: + * -# Set the desired configuration (or use the logic above to realise that + * it is already in the desired configuration) + * -# Claim the interface that you wish to use + * -# Check that the currently active configuration is the one that you want + * to use. + * + * The above method works because once an interface is claimed, no application + * or driver is able to select another configuration. + * + * \section earlycomp Early transfer completion + * + * NOTE: This section is currently Linux-centric. I am not sure if any of these + * considerations apply to Darwin or other platforms. + * + * When a transfer completes early (i.e. when less data is received/sent in + * any one packet than the transfer buffer allows for) then libusb is designed + * to terminate the transfer immediately, not transferring or receiving any + * more data unless other transfers have been queued by the user. + * + * On legacy platforms, libusb is unable to do this in all situations. After + * the incomplete packet occurs, "surplus" data may be transferred. For recent + * versions of libusb, this information is kept (the data length of the + * transfer is updated) and, for device-to-host transfers, any surplus data was + * added to the buffer. Still, this is not a nice solution because it loses the + * information about the end of the short packet, and the user probably wanted + * that surplus data to arrive in the next logical transfer. + * + * + * \section zlp Zero length packets + * + * - libusb is able to send a packet of zero length to an endpoint simply by + * submitting a transfer of zero length. + * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET + * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux. + */ + +/** + * \page libusb_contexts Contexts + * + * It is possible that libusb may be used simultaneously from two independent + * libraries linked into the same executable. For example, if your application + * has a plugin-like system which allows the user to dynamically load a range + * of modules into your program, it is feasible that two independently + * developed modules may both use libusb. + * + * libusb is written to allow for these multiple user scenarios. The two + * "instances" of libusb will not interfere: libusb_set_debug() calls + * from one user will not affect the same settings for other users, other + * users can continue using libusb after one of them calls libusb_exit(), etc. + * + * This is made possible through libusb's context concept. When you + * call libusb_init(), you are (optionally) given a context. You can then pass + * this context pointer back into future libusb functions. + * + * In order to keep things simple for more simplistic applications, it is + * legal to pass NULL to all functions requiring a context pointer (as long as + * you're sure no other code will attempt to use libusb from the same process). + * When you pass NULL, the default context will be used. The default context + * is created the first time a process calls libusb_init() when no other + * context is alive. Contexts are destroyed during libusb_exit(). + * + * The default context is reference-counted and can be shared. That means that + * if libusb_init(NULL) is called twice within the same process, the two + * users end up sharing the same context. The deinitialization and freeing of + * the default context will only happen when the last user calls libusb_exit(). + * In other words, the default context is created and initialized when its + * reference count goes from 0 to 1, and is deinitialized and destroyed when + * its reference count goes from 1 to 0. + * + * You may be wondering why only a subset of libusb functions require a + * context pointer in their function definition. Internally, libusb stores + * context pointers in other objects (e.g. libusb_device instances) and hence + * can infer the context from those objects. + */ + + /** + * \page libusb_api Application Programming Interface + * + * This is the complete list of libusb functions, structures and + * enumerations in alphabetical order. + * + * \section Functions + * - libusb_alloc_streams() + * - libusb_alloc_transfer() + * - libusb_attach_kernel_driver() + * - libusb_bulk_transfer() + * - libusb_cancel_transfer() + * - libusb_claim_interface() + * - libusb_clear_halt() + * - libusb_close() + * - libusb_control_transfer() + * - libusb_control_transfer_get_data() + * - libusb_control_transfer_get_setup() + * - libusb_cpu_to_le16() + * - libusb_detach_kernel_driver() + * - libusb_dev_mem_alloc() + * - libusb_dev_mem_free() + * - libusb_error_name() + * - libusb_event_handler_active() + * - libusb_event_handling_ok() + * - libusb_exit() + * - libusb_fill_bulk_stream_transfer() + * - libusb_fill_bulk_transfer() + * - libusb_fill_control_setup() + * - libusb_fill_control_transfer() + * - libusb_fill_interrupt_transfer() + * - libusb_fill_iso_transfer() + * - libusb_free_bos_descriptor() + * - libusb_free_config_descriptor() + * - libusb_free_container_id_descriptor() + * - libusb_free_device_list() + * - libusb_free_pollfds() + * - libusb_free_ss_endpoint_companion_descriptor() + * - libusb_free_ss_usb_device_capability_descriptor() + * - libusb_free_streams() + * - libusb_free_transfer() + * - libusb_free_usb_2_0_extension_descriptor() + * - libusb_get_active_config_descriptor() + * - libusb_get_bos_descriptor() + * - libusb_get_bus_number() + * - libusb_get_config_descriptor() + * - libusb_get_config_descriptor_by_value() + * - libusb_get_configuration() + * - libusb_get_container_id_descriptor() + * - libusb_get_descriptor() + * - libusb_get_device() + * - libusb_get_device_address() + * - libusb_get_device_descriptor() + * - libusb_get_device_list() + * - libusb_get_device_speed() + * - libusb_get_iso_packet_buffer() + * - libusb_get_iso_packet_buffer_simple() + * - libusb_get_max_iso_packet_size() + * - libusb_get_max_packet_size() + * - libusb_get_next_timeout() + * - libusb_get_parent() + * - libusb_get_pollfds() + * - libusb_get_port_number() + * - libusb_get_port_numbers() + * - libusb_get_port_path() + * - libusb_get_ss_endpoint_companion_descriptor() + * - libusb_get_ss_usb_device_capability_descriptor() + * - libusb_get_string_descriptor() + * - libusb_get_string_descriptor_ascii() + * - libusb_get_usb_2_0_extension_descriptor() + * - libusb_get_version() + * - libusb_handle_events() + * - libusb_handle_events_completed() + * - libusb_handle_events_locked() + * - libusb_handle_events_timeout() + * - libusb_handle_events_timeout_completed() + * - libusb_has_capability() + * - libusb_hotplug_deregister_callback() + * - libusb_hotplug_register_callback() + * - libusb_init() + * - libusb_interrupt_event_handler() + * - libusb_interrupt_transfer() + * - libusb_kernel_driver_active() + * - libusb_lock_events() + * - libusb_lock_event_waiters() + * - libusb_open() + * - libusb_open_device_with_vid_pid() + * - libusb_pollfds_handle_timeouts() + * - libusb_ref_device() + * - libusb_release_interface() + * - libusb_reset_device() + * - libusb_set_auto_detach_kernel_driver() + * - libusb_set_configuration() + * - libusb_set_debug() + * - libusb_set_interface_alt_setting() + * - libusb_set_iso_packet_lengths() + * - libusb_setlocale() + * - libusb_set_pollfd_notifiers() + * - libusb_strerror() + * - libusb_submit_transfer() + * - libusb_transfer_get_stream_id() + * - libusb_transfer_set_stream_id() + * - libusb_try_lock_events() + * - libusb_unlock_events() + * - libusb_unlock_event_waiters() + * - libusb_unref_device() + * - libusb_wait_for_event() + * + * \section Structures + * - libusb_bos_descriptor + * - libusb_bos_dev_capability_descriptor + * - libusb_config_descriptor + * - libusb_container_id_descriptor + * - \ref libusb_context + * - libusb_control_setup + * - \ref libusb_device + * - libusb_device_descriptor + * - \ref libusb_device_handle + * - libusb_endpoint_descriptor + * - libusb_interface + * - libusb_interface_descriptor + * - libusb_iso_packet_descriptor + * - libusb_pollfd + * - libusb_ss_endpoint_companion_descriptor + * - libusb_ss_usb_device_capability_descriptor + * - libusb_transfer + * - libusb_usb_2_0_extension_descriptor + * - libusb_version + * + * \section Enums + * - \ref libusb_bos_type + * - \ref libusb_capability + * - \ref libusb_class_code + * - \ref libusb_descriptor_type + * - \ref libusb_endpoint_direction + * - \ref libusb_error + * - \ref libusb_iso_sync_type + * - \ref libusb_iso_usage_type + * - \ref libusb_log_level + * - \ref libusb_request_recipient + * - \ref libusb_request_type + * - \ref libusb_speed + * - \ref libusb_ss_usb_device_capability_attributes + * - \ref libusb_standard_request + * - \ref libusb_supported_speed + * - \ref libusb_transfer_flags + * - \ref libusb_transfer_status + * - \ref libusb_transfer_type + * - \ref libusb_usb_2_0_extension_attributes + */ + +/** + * @defgroup libusb_lib Library initialization/deinitialization + * This page details how to initialize and deinitialize libusb. Initialization + * must be performed before using any libusb functionality, and similarly you + * must not call any libusb functions after deinitialization. + */ + +/** + * @defgroup libusb_dev Device handling and enumeration + * The functionality documented below is designed to help with the following + * operations: + * - Enumerating the USB devices currently attached to the system + * - Choosing a device to operate from your software + * - Opening and closing the chosen device + * + * \section nutshell In a nutshell... + * + * The description below really makes things sound more complicated than they + * actually are. The following sequence of function calls will be suitable + * for almost all scenarios and does not require you to have such a deep + * understanding of the resource management issues: + * \code +// discover devices +libusb_device **list; +libusb_device *found = NULL; +ssize_t cnt = libusb_get_device_list(NULL, &list); +ssize_t i = 0; +int err = 0; +if (cnt < 0) + error(); + +for (i = 0; i < cnt; i++) { + libusb_device *device = list[i]; + if (is_interesting(device)) { + found = device; + break; + } +} + +if (found) { + libusb_device_handle *handle; + + err = libusb_open(found, &handle); + if (err) + error(); + // etc +} + +libusb_free_device_list(list, 1); +\endcode + * + * The two important points: + * - You asked libusb_free_device_list() to unreference the devices (2nd + * parameter) + * - You opened the device before freeing the list and unreferencing the + * devices + * + * If you ended up with a handle, you can now proceed to perform I/O on the + * device. + * + * \section devshandles Devices and device handles + * libusb has a concept of a USB device, represented by the + * \ref libusb_device opaque type. A device represents a USB device that + * is currently or was previously connected to the system. Using a reference + * to a device, you can determine certain information about the device (e.g. + * you can read the descriptor data). + * + * The libusb_get_device_list() function can be used to obtain a list of + * devices currently connected to the system. This is known as device + * discovery. + * + * Just because you have a reference to a device does not mean it is + * necessarily usable. The device may have been unplugged, you may not have + * permission to operate such device, or another program or driver may be + * using the device. + * + * When you've found a device that you'd like to operate, you must ask + * libusb to open the device using the libusb_open() function. Assuming + * success, libusb then returns you a device handle + * (a \ref libusb_device_handle pointer). All "real" I/O operations then + * operate on the handle rather than the original device pointer. + * + * \section devref Device discovery and reference counting + * + * Device discovery (i.e. calling libusb_get_device_list()) returns a + * freshly-allocated list of devices. The list itself must be freed when + * you are done with it. libusb also needs to know when it is OK to free + * the contents of the list - the devices themselves. + * + * To handle these issues, libusb provides you with two separate items: + * - A function to free the list itself + * - A reference counting system for the devices inside + * + * New devices presented by the libusb_get_device_list() function all have a + * reference count of 1. You can increase and decrease reference count using + * libusb_ref_device() and libusb_unref_device(). A device is destroyed when + * its reference count reaches 0. + * + * With the above information in mind, the process of opening a device can + * be viewed as follows: + * -# Discover devices using libusb_get_device_list(). + * -# Choose the device that you want to operate, and call libusb_open(). + * -# Unref all devices in the discovered device list. + * -# Free the discovered device list. + * + * The order is important - you must not unreference the device before + * attempting to open it, because unreferencing it may destroy the device. + * + * For convenience, the libusb_free_device_list() function includes a + * parameter to optionally unreference all the devices in the list before + * freeing the list itself. This combines steps 3 and 4 above. + * + * As an implementation detail, libusb_open() actually adds a reference to + * the device in question. This is because the device remains available + * through the handle via libusb_get_device(). The reference is deleted during + * libusb_close(). + */ + +/** @defgroup libusb_misc Miscellaneous */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +#define DISCOVERED_DEVICES_SIZE_STEP 8 + +static struct discovered_devs *discovered_devs_alloc(void) +{ + struct discovered_devs *ret = + malloc(sizeof(*ret) + (sizeof(void *) * DISCOVERED_DEVICES_SIZE_STEP)); + + if (ret) { + ret->len = 0; + ret->capacity = DISCOVERED_DEVICES_SIZE_STEP; + } + return ret; +} + +static void discovered_devs_free(struct discovered_devs *discdevs) +{ + size_t i; + + for (i = 0; i < discdevs->len; i++) + libusb_unref_device(discdevs->devices[i]); + + free(discdevs); +} + +/* append a device to the discovered devices collection. may realloc itself, + * returning new discdevs. returns NULL on realloc failure. */ +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev) +{ + size_t len = discdevs->len; + size_t capacity; + struct discovered_devs *new_discdevs; + + /* if there is space, just append the device */ + if (len < discdevs->capacity) { + discdevs->devices[len] = libusb_ref_device(dev); + discdevs->len++; + return discdevs; + } + + /* exceeded capacity, need to grow */ + usbi_dbg("need to increase capacity"); + capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP; + /* can't use usbi_reallocf here because in failure cases it would + * free the existing discdevs without unreferencing its devices. */ + new_discdevs = realloc(discdevs, + sizeof(*discdevs) + (sizeof(void *) * capacity)); + if (!new_discdevs) { + discovered_devs_free(discdevs); + return NULL; + } + + discdevs = new_discdevs; + discdevs->capacity = capacity; + discdevs->devices[len] = libusb_ref_device(dev); + discdevs->len++; + + return discdevs; +} + +/* Allocate a new device with a specific session ID. The returned device has + * a reference count of 1. */ +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id) +{ + size_t priv_size = usbi_backend->device_priv_size; + struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size); + int r; + + if (!dev) + return NULL; + + r = usbi_mutex_init(&dev->lock); + if (r) { + free(dev); + return NULL; + } + + dev->ctx = ctx; + dev->refcnt = 1; + dev->session_data = session_id; + dev->speed = LIBUSB_SPEED_UNKNOWN; + + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + usbi_connect_device (dev); + } + + return dev; +} + +void usbi_connect_device(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + + dev->attached = 1; + + usbi_mutex_lock(&dev->ctx->usb_devs_lock); + list_add(&dev->list, &dev->ctx->usb_devs); + usbi_mutex_unlock(&dev->ctx->usb_devs_lock); + + /* Signal that an event has occurred for this device if we support hotplug AND + * the hotplug message list is ready. This prevents an event from getting raised + * during initial enumeration. */ + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); + } +} + +void usbi_disconnect_device(struct libusb_device *dev) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + + usbi_mutex_lock(&dev->lock); + dev->attached = 0; + usbi_mutex_unlock(&dev->lock); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_del(&dev->list); + usbi_mutex_unlock(&ctx->usb_devs_lock); + + /* Signal that an event has occurred for this device if we support hotplug AND + * the hotplug message list is ready. This prevents an event from getting raised + * during initial enumeration. libusb_handle_events will take care of dereferencing + * the device. */ + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); + } +} + +/* Perform some final sanity checks on a newly discovered device. If this + * function fails (negative return code), the device should not be added + * to the discovered device list. */ +int usbi_sanitize_device(struct libusb_device *dev) +{ + int r; + uint8_t num_configurations; + + r = usbi_device_cache_descriptor(dev); + if (r < 0) + return r; + + num_configurations = dev->device_descriptor.bNumConfigurations; + if (num_configurations > USB_MAXCONFIG) { + usbi_err(DEVICE_CTX(dev), "too many configurations"); + return LIBUSB_ERROR_IO; + } else if (0 == num_configurations) + usbi_dbg("zero configurations, maybe an unauthorized device"); + + dev->num_configurations = num_configurations; + return 0; +} + +/* Examine libusb's internal list of known devices, looking for one with + * a specific session ID. Returns the matching device if it was found, and + * NULL otherwise. */ +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id) +{ + struct libusb_device *dev; + struct libusb_device *ret = NULL; + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) + if (dev->session_data == session_id) { + ret = libusb_ref_device(dev); + break; + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + return ret; +} + +/** @ingroup libusb_dev + * Returns a list of USB devices currently attached to the system. This is + * your entry point into finding a USB device to operate. + * + * You are expected to unreference all the devices when you are done with + * them, and then free the list with libusb_free_device_list(). Note that + * libusb_free_device_list() can unref all the devices for you. Be careful + * not to unreference a device you are about to open until after you have + * opened it. + * + * This return value of this function indicates the number of devices in + * the resultant list. The list is actually one element larger, as it is + * NULL-terminated. + * + * \param ctx the context to operate on, or NULL for the default context + * \param list output location for a list of devices. Must be later freed with + * libusb_free_device_list(). + * \returns the number of devices in the outputted list, or any + * \ref libusb_error according to errors encountered by the backend. + */ +ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, + libusb_device ***list) +{ + struct discovered_devs *discdevs = discovered_devs_alloc(); + struct libusb_device **ret; + int r = 0; + ssize_t i, len; + USBI_GET_CONTEXT(ctx); + usbi_dbg(""); + + if (!discdevs) + return LIBUSB_ERROR_NO_MEM; + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* backend provides hotplug support */ + struct libusb_device *dev; + + if (usbi_backend->hotplug_poll) + usbi_backend->hotplug_poll(); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { + discdevs = discovered_devs_append(discdevs, dev); + + if (!discdevs) { + r = LIBUSB_ERROR_NO_MEM; + break; + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } else { + /* backend does not provide hotplug support */ + r = usbi_backend->get_device_list(ctx, &discdevs); + } + + if (r < 0) { + len = r; + goto out; + } + + /* convert discovered_devs into a list */ + len = discdevs->len; + ret = calloc(len + 1, sizeof(struct libusb_device *)); + if (!ret) { + len = LIBUSB_ERROR_NO_MEM; + goto out; + } + + ret[len] = NULL; + for (i = 0; i < len; i++) { + struct libusb_device *dev = discdevs->devices[i]; + ret[i] = libusb_ref_device(dev); + } + *list = ret; + +out: + if (discdevs) + discovered_devs_free(discdevs); + return len; +} + +/** \ingroup libusb_dev + * Frees a list of devices previously discovered using + * libusb_get_device_list(). If the unref_devices parameter is set, the + * reference count of each device in the list is decremented by 1. + * \param list the list to free + * \param unref_devices whether to unref the devices in the list + */ +void API_EXPORTED libusb_free_device_list(libusb_device **list, + int unref_devices) +{ + if (!list) + return; + + if (unref_devices) { + int i = 0; + struct libusb_device *dev; + + while ((dev = list[i++]) != NULL) + libusb_unref_device(dev); + } + free(list); +} + +/** \ingroup libusb_dev + * Get the number of the bus that a device is connected to. + * \param dev a device + * \returns the bus number + */ +uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev) +{ + return dev->bus_number; +} + +/** \ingroup libusb_dev + * Get the number of the port that a device is connected to. + * Unless the OS does something funky, or you are hot-plugging USB extension cards, + * the port number returned by this call is usually guaranteed to be uniquely tied + * to a physical port, meaning that different devices plugged on the same physical + * port should return the same port number. + * + * But outside of this, there is no guarantee that the port number returned by this + * call will remain the same, or even match the order in which ports have been + * numbered by the HUB/HCD manufacturer. + * + * \param dev a device + * \returns the port number (0 if not available) + */ +uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) +{ + return dev->port_number; +} + +/** \ingroup libusb_dev + * Get the list of all port numbers from root for the specified device + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * \param dev a device + * \param port_numbers the array that should contain the port numbers + * \param port_numbers_len the maximum length of the array. As per the USB 3.0 + * specs, the current maximum limit for the depth is 7. + * \returns the number of elements filled + * \returns LIBUSB_ERROR_OVERFLOW if the array is too small + */ +int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, + uint8_t* port_numbers, int port_numbers_len) +{ + int i = port_numbers_len; + struct libusb_context *ctx = DEVICE_CTX(dev); + + if (port_numbers_len <= 0) + return LIBUSB_ERROR_INVALID_PARAM; + + // HCDs can be listed as devices with port #0 + while((dev) && (dev->port_number != 0)) { + if (--i < 0) { + usbi_warn(ctx, "port numbers array is too small"); + return LIBUSB_ERROR_OVERFLOW; + } + port_numbers[i] = dev->port_number; + dev = dev->parent_dev; + } + if (i < port_numbers_len) + memmove(port_numbers, &port_numbers[i], port_numbers_len - i); + return port_numbers_len - i; +} + +/** \ingroup libusb_dev + * Deprecated please use libusb_get_port_numbers instead. + */ +int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev, + uint8_t* port_numbers, uint8_t port_numbers_len) +{ + UNUSED(ctx); + + return libusb_get_port_numbers(dev, port_numbers, port_numbers_len); +} + +/** \ingroup libusb_dev + * Get the the parent from the specified device. + * \param dev a device + * \returns the device parent or NULL if not available + * You should issue a \ref libusb_get_device_list() before calling this + * function and make sure that you only access the parent before issuing + * \ref libusb_free_device_list(). The reason is that libusb currently does + * not maintain a permanent list of device instances, and therefore can + * only guarantee that parents are fully instantiated within a + * libusb_get_device_list() - libusb_free_device_list() block. + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev) +{ + return dev->parent_dev; +} + +/** \ingroup libusb_dev + * Get the address of the device on the bus it is connected to. + * \param dev a device + * \returns the device address + */ +uint8_t API_EXPORTED libusb_get_device_address(libusb_device *dev) +{ + return dev->device_address; +} + +/** \ingroup libusb_dev + * Get the negotiated connection speed for a device. + * \param dev a device + * \returns a \ref libusb_speed code, where LIBUSB_SPEED_UNKNOWN means that + * the OS doesn't know or doesn't support returning the negotiated speed. + */ +int API_EXPORTED libusb_get_device_speed(libusb_device *dev) +{ + return dev->speed; +} + +static const struct libusb_endpoint_descriptor *find_endpoint( + struct libusb_config_descriptor *config, unsigned char endpoint) +{ + int iface_idx; + for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { + const struct libusb_interface *iface = &config->interface[iface_idx]; + int altsetting_idx; + + for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; + altsetting_idx++) { + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep; + } + } + } + return NULL; +} + +/** \ingroup libusb_dev + * Convenience function to retrieve the wMaxPacketSize value for a particular + * endpoint in the active device configuration. + * + * This function was originally intended to be of assistance when setting up + * isochronous transfers, but a design mistake resulted in this function + * instead. It simply returns the wMaxPacketSize value without considering + * its contents. If you're dealing with isochronous transfers, you probably + * want libusb_get_max_iso_packet_size() instead. + * + * \param dev a device + * \param endpoint address of the endpoint in question + * \returns the wMaxPacketSize value + * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns LIBUSB_ERROR_OTHER on other failure + */ +int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_endpoint(config, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = ep->wMaxPacketSize; + +out: + libusb_free_config_descriptor(config); + return r; +} + +/** \ingroup libusb_dev + * Calculate the maximum packet size which a specific endpoint is capable is + * sending or receiving in the duration of 1 microframe + * + * Only the active configuration is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * Since v1.0.3. + * + * \param dev a device + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns LIBUSB_ERROR_OTHER on other failure + */ +int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + enum libusb_transfer_type ep_type; + uint16_t val; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_endpoint(config, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + val = ep->wMaxPacketSize; + ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + +out: + libusb_free_config_descriptor(config); + return r; +} + +/** \ingroup libusb_dev + * Increment the reference count of a device. + * \param dev the device to reference + * \returns the same device + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) +{ + usbi_mutex_lock(&dev->lock); + dev->refcnt++; + usbi_mutex_unlock(&dev->lock); + return dev; +} + +/** \ingroup libusb_dev + * Decrement the reference count of a device. If the decrement operation + * causes the reference count to reach zero, the device shall be destroyed. + * \param dev the device to unreference + */ +void API_EXPORTED libusb_unref_device(libusb_device *dev) +{ + int refcnt; + + if (!dev) + return; + + usbi_mutex_lock(&dev->lock); + refcnt = --dev->refcnt; + usbi_mutex_unlock(&dev->lock); + + if (refcnt == 0) { + usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address); + + libusb_unref_device(dev->parent_dev); + + if (usbi_backend->destroy_device) + usbi_backend->destroy_device(dev); + + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* backend does not support hotplug */ + usbi_disconnect_device(dev); + } + + usbi_mutex_destroy(&dev->lock); + free(dev); + } +} + +/* + * Signal the event pipe so that the event handling thread will be + * interrupted to process an internal event. + */ +int usbi_signal_event(struct libusb_context *ctx) +{ + unsigned char dummy = 1; + ssize_t r; + + /* write some data on event pipe to interrupt event handlers */ + r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) { + usbi_warn(ctx, "internal signalling write failed"); + return LIBUSB_ERROR_IO; + } + + return 0; +} + +/* + * Clear the event pipe so that the event handling will no longer be + * interrupted. + */ +int usbi_clear_event(struct libusb_context *ctx) +{ + unsigned char dummy; + ssize_t r; + + /* read some data on event pipe to clear it */ + r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) { + usbi_warn(ctx, "internal signalling read failed"); + return LIBUSB_ERROR_IO; + } + + return 0; +} + +/** \ingroup libusb_dev + * Open a device and obtain a device handle. A handle allows you to perform + * I/O on the device in question. + * + * Internally, this function adds a reference to the device and makes it + * available to you through libusb_get_device(). This reference is removed + * during libusb_close(). + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param dev the device to open + * \param dev_handle output location for the returned device handle pointer. Only + * populated when the return code is 0. + * \returns 0 on success + * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_open(libusb_device *dev, + libusb_device_handle **dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device_handle *_dev_handle; + size_t priv_size = usbi_backend->device_handle_priv_size; + int r; + usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); + + if (!dev->attached) { + return LIBUSB_ERROR_NO_DEVICE; + } + + _dev_handle = malloc(sizeof(*_dev_handle) + priv_size); + if (!_dev_handle) + return LIBUSB_ERROR_NO_MEM; + + r = usbi_mutex_init(&_dev_handle->lock); + if (r) { + free(_dev_handle); + return LIBUSB_ERROR_OTHER; + } + + _dev_handle->dev = libusb_ref_device(dev); + _dev_handle->auto_detach_kernel_driver = 0; + _dev_handle->claimed_interfaces = 0; + memset(&_dev_handle->os_priv, 0, priv_size); + + r = usbi_backend->open(_dev_handle); + if (r < 0) { + usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r); + libusb_unref_device(dev); + usbi_mutex_destroy(&_dev_handle->lock); + free(_dev_handle); + return r; + } + + usbi_mutex_lock(&ctx->open_devs_lock); + list_add(&_dev_handle->list, &ctx->open_devs); + usbi_mutex_unlock(&ctx->open_devs_lock); + *dev_handle = _dev_handle; + + return 0; +} + +/** \ingroup libusb_dev + * Convenience function for finding a device with a particular + * idVendor/idProduct combination. This function is intended + * for those scenarios where you are using libusb to knock up a quick test + * application - it allows you to avoid calling libusb_get_device_list() and + * worrying about traversing/freeing the list. + * + * This function has limitations and is hence not intended for use in real + * applications: if multiple devices have the same IDs it will only + * give you the first one, etc. + * + * \param ctx the context to operate on, or NULL for the default context + * \param vendor_id the idVendor value to search for + * \param product_id the idProduct value to search for + * \returns a device handle for the first found device, or NULL on error + * or if the device could not be found. */ +DEFAULT_VISIBILITY +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id) +{ + struct libusb_device **devs; + struct libusb_device *found = NULL; + struct libusb_device *dev; + struct libusb_device_handle *dev_handle = NULL; + size_t i = 0; + int r; + + if (libusb_get_device_list(ctx, &devs) < 0) + return NULL; + + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) + goto out; + if (desc.idVendor == vendor_id && desc.idProduct == product_id) { + found = dev; + break; + } + } + + if (found) { + r = libusb_open(found, &dev_handle); + if (r < 0) + dev_handle = NULL; + } + +out: + libusb_free_device_list(devs, 1); + return dev_handle; +} + +static void do_close(struct libusb_context *ctx, + struct libusb_device_handle *dev_handle) +{ + struct usbi_transfer *itransfer; + struct usbi_transfer *tmp; + + /* remove any transfers in flight that are for this device */ + usbi_mutex_lock(&ctx->flying_transfers_lock); + + /* safe iteration because transfers may be being deleted */ + list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + if (transfer->dev_handle != dev_handle) + continue; + + usbi_mutex_lock(&itransfer->lock); + if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); + + if (itransfer->state_flags & USBI_TRANSFER_CANCELLING) + usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); + else + usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); + } + usbi_mutex_unlock(&itransfer->lock); + + /* remove from the list of in-flight transfers and make sure + * we don't accidentally use the device handle in the future + * (or that such accesses will be easily caught and identified as a crash) + */ + list_del(&itransfer->list); + transfer->dev_handle = NULL; + + /* it is up to the user to free up the actual transfer struct. this is + * just making sure that we don't attempt to process the transfer after + * the device handle is invalid + */ + usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed", + transfer, dev_handle); + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + usbi_mutex_lock(&ctx->open_devs_lock); + list_del(&dev_handle->list); + usbi_mutex_unlock(&ctx->open_devs_lock); + + usbi_backend->close(dev_handle); + libusb_unref_device(dev_handle->dev); + usbi_mutex_destroy(&dev_handle->lock); + free(dev_handle); +} + +/** \ingroup libusb_dev + * Close a device handle. Should be called on all open handles before your + * application exits. + * + * Internally, this function destroys the reference that was added by + * libusb_open() on the given device. + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param dev_handle the device handle to close + */ +void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx; + int handling_events; + int pending_events; + + if (!dev_handle) + return; + usbi_dbg(""); + + ctx = HANDLE_CTX(dev_handle); + handling_events = usbi_handling_events(ctx); + + /* Similarly to libusb_open(), we want to interrupt all event handlers + * at this point. More importantly, we want to perform the actual close of + * the device while holding the event handling lock (preventing any other + * thread from doing event handling) because we will be removing a file + * descriptor from the polling loop. If this is being called by the current + * event handler, we can bypass the interruption code because we already + * hold the event handling lock. */ + + if (!handling_events) { + /* Record that we are closing a device. + * Only signal an event if there are no prior pending events. */ + usbi_mutex_lock(&ctx->event_data_lock); + pending_events = usbi_pending_events(ctx); + ctx->device_close++; + if (!pending_events) + usbi_signal_event(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + + /* take event handling lock */ + libusb_lock_events(ctx); + } + + /* Close the device */ + do_close(ctx, dev_handle); + + if (!handling_events) { + /* We're done with closing this device. + * Clear the event pipe if there are no further pending events. */ + usbi_mutex_lock(&ctx->event_data_lock); + ctx->device_close--; + pending_events = usbi_pending_events(ctx); + if (!pending_events) + usbi_clear_event(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + + /* Release event handling lock and wake up event waiters */ + libusb_unlock_events(ctx); + } +} + +/** \ingroup libusb_dev + * Get the underlying device for a device handle. This function does not modify + * the reference count of the returned device, so do not feel compelled to + * unreference it when you are done. + * \param dev_handle a device handle + * \returns the underlying device + */ +DEFAULT_VISIBILITY +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle) +{ + return dev_handle->dev; +} + +/** \ingroup libusb_dev + * Determine the bConfigurationValue of the currently active configuration. + * + * You could formulate your own control request to obtain this information, + * but this function has the advantage that it may be able to retrieve the + * information from operating system caches (no I/O involved). + * + * If the OS does not cache this information, then this function will block + * while a control transfer is submitted to retrieve the information. + * + * This function will return a value of 0 in the config output + * parameter if the device is in unconfigured state. + * + * \param dev_handle a device handle + * \param config output location for the bConfigurationValue of the active + * configuration (only valid for return code 0) + * \returns 0 on success + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, + int *config) +{ + int r = LIBUSB_ERROR_NOT_SUPPORTED; + + usbi_dbg(""); + if (usbi_backend->get_configuration) + r = usbi_backend->get_configuration(dev_handle, config); + + if (r == LIBUSB_ERROR_NOT_SUPPORTED) { + uint8_t tmp = 0; + usbi_dbg("falling back to control message"); + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000); + if (r == 0) { + usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?"); + r = LIBUSB_ERROR_IO; + } else if (r == 1) { + r = 0; + *config = tmp; + } else { + usbi_dbg("control failed, error %d", r); + } + } + + if (r == 0) + usbi_dbg("active config %d", *config); + + return r; +} + +/** \ingroup libusb_dev + * Set the active configuration for a device. + * + * The operating system may or may not have already set an active + * configuration on the device. It is up to your application to ensure the + * correct configuration is selected before you attempt to claim interfaces + * and perform other operations. + * + * If you call this function on a device already configured with the selected + * configuration, then this function will act as a lightweight device reset: + * it will issue a SET_CONFIGURATION request using the current configuration, + * causing most USB-related device state to be reset (altsetting reset to zero, + * endpoint halts cleared, toggles reset). + * + * You cannot change/reset configuration if your application has claimed + * interfaces. It is advised to set the desired configuration before claiming + * interfaces. + * + * Alternatively you can call libusb_release_interface() first. Note if you + * do things this way you must ensure that auto_detach_kernel_driver for + * dev is 0, otherwise the kernel driver will be re-attached when you + * release the interface(s). + * + * You cannot change/reset configuration if other applications or drivers have + * claimed interfaces. + * + * A configuration value of -1 will put the device in unconfigured state. + * The USB specifications state that a configuration value of 0 does this, + * however buggy devices exist which actually have a configuration 0. + * + * You should always use this function rather than formulating your own + * SET_CONFIGURATION control request. This is because the underlying operating + * system needs to know when such changes happen. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param configuration the bConfigurationValue of the configuration you + * wish to activate, or -1 if you wish to put the device in an unconfigured + * state + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist + * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration) +{ + usbi_dbg("configuration %d", configuration); + return usbi_backend->set_configuration(dev_handle, configuration); +} + +/** \ingroup libusb_dev + * Claim an interface on a given device handle. You must claim the interface + * you wish to use before you can perform I/O on any of its endpoints. + * + * It is legal to attempt to claim an already-claimed interface, in which + * case libusb just returns 0 without doing anything. + * + * If auto_detach_kernel_driver is set to 1 for dev, the kernel driver + * will be detached if necessary, on failure the detach error is returned. + * + * Claiming of interfaces is a purely logical operation; it does not cause + * any requests to be sent over the bus. Interface claiming is used to + * instruct the underlying operating system that your application wishes + * to take ownership of the interface. + * + * This is a non-blocking function. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the interface you + * wish to claim + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist + * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the + * interface + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns a LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number) +{ + int r = 0; + + usbi_dbg("interface %d", interface_number); + if (interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_mutex_lock(&dev_handle->lock); + if (dev_handle->claimed_interfaces & (1 << interface_number)) + goto out; + + r = usbi_backend->claim_interface(dev_handle, interface_number); + if (r == 0) + dev_handle->claimed_interfaces |= 1 << interface_number; + +out: + usbi_mutex_unlock(&dev_handle->lock); + return r; +} + +/** \ingroup libusb_dev + * Release an interface previously claimed with libusb_claim_interface(). You + * should release all claimed interfaces before closing a device handle. + * + * This is a blocking function. A SET_INTERFACE control request will be sent + * to the device, resetting interface state to the first alternate setting. + * + * If auto_detach_kernel_driver is set to 1 for dev, the kernel + * driver will be re-attached after releasing the interface. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the + * previously-claimed interface + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_set_auto_detach_kernel_driver() + */ +int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number) +{ + int r; + + usbi_dbg("interface %d", interface_number); + if (interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_mutex_lock(&dev_handle->lock); + if (!(dev_handle->claimed_interfaces & (1 << interface_number))) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + r = usbi_backend->release_interface(dev_handle, interface_number); + if (r == 0) + dev_handle->claimed_interfaces &= ~(1 << interface_number); + +out: + usbi_mutex_unlock(&dev_handle->lock); + return r; +} + +/** \ingroup libusb_dev + * Activate an alternate setting for an interface. The interface must have + * been previously claimed with libusb_claim_interface(). + * + * You should always use this function rather than formulating your own + * SET_INTERFACE control request. This is because the underlying operating + * system needs to know when such changes happen. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param interface_number the bInterfaceNumber of the + * previously-claimed interface + * \param alternate_setting the bAlternateSetting of the alternate + * setting to activate + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the + * requested alternate setting does not exist + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting) +{ + usbi_dbg("interface %d altsetting %d", + interface_number, alternate_setting); + if (interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_mutex_lock(&dev_handle->lock); + if (!dev_handle->dev->attached) { + usbi_mutex_unlock(&dev_handle->lock); + return LIBUSB_ERROR_NO_DEVICE; + } + + if (!(dev_handle->claimed_interfaces & (1 << interface_number))) { + usbi_mutex_unlock(&dev_handle->lock); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_mutex_unlock(&dev_handle->lock); + + return usbi_backend->set_interface_altsetting(dev_handle, interface_number, + alternate_setting); +} + +/** \ingroup libusb_dev + * Clear the halt/stall condition for an endpoint. Endpoints with halt status + * are unable to receive or transmit data until the halt condition is stalled. + * + * You should cancel all pending transfers before attempting to clear the halt + * condition. + * + * This is a blocking function. + * + * \param dev_handle a device handle + * \param endpoint the endpoint to clear halt status + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint) +{ + usbi_dbg("endpoint %x", endpoint); + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + return usbi_backend->clear_halt(dev_handle, endpoint); +} + +/** \ingroup libusb_dev + * Perform a USB port reset to reinitialize a device. The system will attempt + * to restore the previous configuration and alternate settings after the + * reset has completed. + * + * If the reset fails, the descriptors change, or the previous state cannot be + * restored, the device will appear to be disconnected and reconnected. This + * means that the device handle is no longer valid (you should close it) and + * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates + * when this is the case. + * + * This is a blocking function which usually incurs a noticeable delay. + * + * \param dev_handle a handle of the device to reset + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the + * device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) +{ + usbi_dbg(""); + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + return usbi_backend->reset_device(dev_handle); +} + +/** \ingroup libusb_asyncio + * Allocate up to num_streams usb bulk streams on the specified endpoints. This + * function takes an array of endpoints rather then a single endpoint because + * some protocols require that endpoints are setup with similar stream ids. + * All endpoints passed in must belong to the same interface. + * + * Note this function may return less streams then requested. Also note that the + * same number of streams are allocated for each endpoint in the endpoint array. + * + * Stream id 0 is reserved, and should not be used to communicate with devices. + * If libusb_alloc_streams() returns with a value of N, you may use stream ids + * 1 to N. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param dev_handle a device handle + * \param num_streams number of streams to try to allocate + * \param endpoints array of endpoints to allocate streams on + * \param num_endpoints length of the endpoints array + * \returns number of streams allocated, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints) +{ + usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints); + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend->alloc_streams) + return usbi_backend->alloc_streams(dev_handle, num_streams, endpoints, + num_endpoints); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_asyncio + * Free usb bulk streams allocated with libusb_alloc_streams(). + * + * Note streams are automatically free-ed when releasing an interface. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param dev_handle a device handle + * \param endpoints array of endpoints to free streams on + * \param num_endpoints length of the endpoints array + * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints) +{ + usbi_dbg("eps %d", num_endpoints); + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend->free_streams) + return usbi_backend->free_streams(dev_handle, endpoints, + num_endpoints); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_asyncio + * Attempts to allocate a block of persistent DMA memory suitable for transfers + * against the given device. If successful, will return a block of memory + * that is suitable for use as "buffer" in \ref libusb_transfer against this + * device. Using this memory instead of regular memory means that the host + * controller can use DMA directly into the buffer to increase performance, and + * also that transfers can no longer fail due to kernel memory fragmentation. + * + * Note that this means you should not modify this memory (or even data on + * the same cache lines) when a transfer is in progress, although it is legal + * to have several transfers going on within the same memory block. + * + * Will return NULL on failure. Many systems do not support such zerocopy + * and will always return NULL. Memory allocated with this function must be + * freed with \ref libusb_dev_mem_free. Specifically, this means that the + * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated + * with this function. + * + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 + * + * \param dev_handle a device handle + * \param length size of desired data buffer + * \returns a pointer to the newly allocated memory, or NULL on failure + */ +DEFAULT_VISIBILITY +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length) +{ + if (!dev_handle->dev->attached) + return NULL; + + if (usbi_backend->dev_mem_alloc) + return usbi_backend->dev_mem_alloc(dev_handle, length); + else + return NULL; +} + +/** \ingroup libusb_asyncio + * Free device memory allocated with libusb_dev_mem_alloc(). + * + * \param dev_handle a device handle + * \param buffer pointer to the previously allocated memory + * \param length size of previously allocated memory + * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length) +{ + if (usbi_backend->dev_mem_free) + return usbi_backend->dev_mem_free(dev_handle, buffer, length); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Determine if a kernel driver is active on an interface. If a kernel driver + * is active, you cannot claim the interface, and libusb will be unable to + * perform I/O. + * + * This functionality is not available on Windows. + * + * \param dev_handle a device handle + * \param interface_number the interface to check + * \returns 0 if no kernel driver is active + * \returns 1 if a kernel driver is active + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_detach_kernel_driver() + */ +int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number) +{ + usbi_dbg("interface %d", interface_number); + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend->kernel_driver_active) + return usbi_backend->kernel_driver_active(dev_handle, interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Detach a kernel driver from an interface. If successful, you will then be + * able to claim the interface and perform I/O. + * + * This functionality is not available on Darwin or Windows. + * + * Note that libusb itself also talks to the device through a special kernel + * driver, if this driver is already attached to the device, this call will + * not detach it and return LIBUSB_ERROR_NOT_FOUND. + * + * \param dev_handle a device handle + * \param interface_number the interface to detach the driver from + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_kernel_driver_active() + */ +int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number) +{ + usbi_dbg("interface %d", interface_number); + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend->detach_kernel_driver) + return usbi_backend->detach_kernel_driver(dev_handle, interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Re-attach an interface's kernel driver, which was previously detached + * using libusb_detach_kernel_driver(). This call is only effective on + * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms. + * + * This functionality is not available on Darwin or Windows. + * + * \param dev_handle a device handle + * \param interface_number the interface to attach the driver from + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the + * interface is claimed by a program or driver + * \returns another LIBUSB_ERROR code on other failure + * \see libusb_kernel_driver_active() + */ +int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number) +{ + usbi_dbg("interface %d", interface_number); + + if (!dev_handle->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + + if (usbi_backend->attach_kernel_driver) + return usbi_backend->attach_kernel_driver(dev_handle, interface_number); + else + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +/** \ingroup libusb_dev + * Enable/disable libusb's automatic kernel driver detachment. When this is + * enabled libusb will automatically detach the kernel driver on an interface + * when claiming the interface, and attach it when releasing the interface. + * + * Automatic kernel driver detachment is disabled on newly opened device + * handles by default. + * + * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER + * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will + * continue as if this function was never called. + * + * \param dev_handle a device handle + * \param enable whether to enable or disable auto kernel driver detachment + * + * \returns LIBUSB_SUCCESS on success + * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * \see libusb_claim_interface() + * \see libusb_release_interface() + * \see libusb_set_configuration() + */ +int API_EXPORTED libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable) +{ + if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) + return LIBUSB_ERROR_NOT_SUPPORTED; + + dev_handle->auto_detach_kernel_driver = enable; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_lib + * Set log message verbosity. + * + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stdout/stderr file descriptors. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this function does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this function does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this function + * does nothing: you'll always get messages from all levels. + * + * \param ctx the context to operate on, or NULL for the default context + * \param level debug level to set + */ +void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) +{ + USBI_GET_CONTEXT(ctx); + if (!ctx->debug_fixed) + ctx->debug = level; +} + +/** \ingroup libusb_lib + * Initialize libusb. This function must be called before calling any other + * libusb function. + * + * If you do not provide an output location for a context pointer, a default + * context will be created. If there was already a default context, it will + * be reused (and nothing will be initialized/reinitialized). + * + * \param context Optional output location for context pointer. + * Only valid on return code 0. + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \see libusb_contexts + */ +int API_EXPORTED libusb_init(libusb_context **context) +{ + struct libusb_device *dev, *next; + char *dbg = getenv("LIBUSB_DEBUG"); + struct libusb_context *ctx; + static int first_init = 1; + int r = 0; + + usbi_mutex_static_lock(&default_context_lock); + + if (!timestamp_origin.tv_sec) { + usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, ×tamp_origin); + } + + if (!context && usbi_default_context) { + usbi_dbg("reusing default context"); + default_context_refcnt++; + usbi_mutex_static_unlock(&default_context_lock); + return 0; + } + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + r = LIBUSB_ERROR_NO_MEM; + goto err_unlock; + } + +#ifdef ENABLE_DEBUG_LOGGING + ctx->debug = LIBUSB_LOG_LEVEL_DEBUG; +#endif + + if (dbg) { + ctx->debug = atoi(dbg); + if (ctx->debug) + ctx->debug_fixed = 1; + } + + /* default context should be initialized before calling usbi_dbg */ + if (!usbi_default_context) { + usbi_default_context = ctx; + default_context_refcnt++; + usbi_dbg("created default context"); + } + + usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor, + libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc); + + usbi_mutex_init(&ctx->usb_devs_lock); + usbi_mutex_init(&ctx->open_devs_lock); + usbi_mutex_init(&ctx->hotplug_cbs_lock); + list_init(&ctx->usb_devs); + list_init(&ctx->open_devs); + list_init(&ctx->hotplug_cbs); + + usbi_mutex_static_lock(&active_contexts_lock); + if (first_init) { + first_init = 0; + list_init (&active_contexts_list); + } + list_add (&ctx->list, &active_contexts_list); + usbi_mutex_static_unlock(&active_contexts_lock); + + if (usbi_backend->init) { + r = usbi_backend->init(ctx); + if (r) + goto err_free_ctx; + } + + r = usbi_io_init(ctx); + if (r < 0) + goto err_backend_exit; + + usbi_mutex_static_unlock(&default_context_lock); + + if (context) + *context = ctx; + + return 0; + +err_backend_exit: + if (usbi_backend->exit) + usbi_backend->exit(); +err_free_ctx: + if (ctx == usbi_default_context) { + usbi_default_context = NULL; + default_context_refcnt--; + } + + usbi_mutex_static_lock(&active_contexts_lock); + list_del (&ctx->list); + usbi_mutex_static_unlock(&active_contexts_lock); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) { + list_del(&dev->list); + libusb_unref_device(dev); + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + usbi_mutex_destroy(&ctx->open_devs_lock); + usbi_mutex_destroy(&ctx->usb_devs_lock); + usbi_mutex_destroy(&ctx->hotplug_cbs_lock); + + free(ctx); +err_unlock: + usbi_mutex_static_unlock(&default_context_lock); + return r; +} + +/** \ingroup libusb_lib + * Deinitialize libusb. Should be called after closing all open devices and + * before your application terminates. + * \param ctx the context to deinitialize, or NULL for the default context + */ +void API_EXPORTED libusb_exit(struct libusb_context *ctx) +{ + struct libusb_device *dev, *next; + struct timeval tv = { 0, 0 }; + + usbi_dbg(""); + USBI_GET_CONTEXT(ctx); + + /* if working with default context, only actually do the deinitialization + * if we're the last user */ + usbi_mutex_static_lock(&default_context_lock); + if (ctx == usbi_default_context) { + if (--default_context_refcnt > 0) { + usbi_dbg("not destroying default context"); + usbi_mutex_static_unlock(&default_context_lock); + return; + } + usbi_dbg("destroying default context"); + usbi_default_context = NULL; + } + usbi_mutex_static_unlock(&default_context_lock); + + usbi_mutex_static_lock(&active_contexts_lock); + list_del (&ctx->list); + usbi_mutex_static_unlock(&active_contexts_lock); + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + usbi_hotplug_deregister_all(ctx); + + /* + * Ensure any pending unplug events are read from the hotplug + * pipe. The usb_device-s hold in the events are no longer part + * of usb_devs, but the events still hold a reference! + * + * Note we don't do this if the application has left devices + * open (which implies a buggy app) to avoid packet completion + * handlers running when the app does not expect them to run. + */ + if (list_empty(&ctx->open_devs)) + libusb_handle_events_timeout(ctx, &tv); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) { + list_del(&dev->list); + libusb_unref_device(dev); + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } + + /* a few sanity checks. don't bother with locking because unless + * there is an application bug, nobody will be accessing these. */ + if (!list_empty(&ctx->usb_devs)) + usbi_warn(ctx, "some libusb_devices were leaked"); + if (!list_empty(&ctx->open_devs)) + usbi_warn(ctx, "application left some devices open"); + + usbi_io_exit(ctx); + if (usbi_backend->exit) + usbi_backend->exit(); + + usbi_mutex_destroy(&ctx->open_devs_lock); + usbi_mutex_destroy(&ctx->usb_devs_lock); + usbi_mutex_destroy(&ctx->hotplug_cbs_lock); + free(ctx); +} + +/** \ingroup libusb_misc + * Check at runtime if the loaded library has a given capability. + * This call should be performed after \ref libusb_init(), to ensure the + * backend has updated its capability set. + * + * \param capability the \ref libusb_capability to check for + * \returns nonzero if the running library has the capability, 0 otherwise + */ +int API_EXPORTED libusb_has_capability(uint32_t capability) +{ + switch (capability) { + case LIBUSB_CAP_HAS_CAPABILITY: + return 1; + case LIBUSB_CAP_HAS_HOTPLUG: + return !(usbi_backend->get_device_list); + case LIBUSB_CAP_HAS_HID_ACCESS: + return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS); + case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: + return (usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER); + } + return 0; +} + +/* this is defined in libusbi.h if needed */ +#ifdef LIBUSB_PRINTF_WIN32 +/* + * Prior to VS2015, Microsoft did not provide the snprintf() function and + * provided a vsnprintf() that did not guarantee NULL-terminated output. + * Microsoft did provide a _snprintf() function, but again it did not + * guarantee NULL-terminated output. + * + * The below implementations guarantee NULL-terminated output and are + * C99 compliant. + */ + +int usbi_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = usbi_vsnprintf(str, size, format, ap); + va_end(ap); + + return ret; +} + +int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int ret; + + ret = _vsnprintf(str, size, format, ap); + if (ret < 0 || ret == (int)size) { + /* Output is truncated, ensure buffer is NULL-terminated and + * determine how many characters would have been written. */ + str[size - 1] = '\0'; + if (ret < 0) + ret = _vsnprintf(NULL, 0, format, ap); + } + + return ret; +} +#endif + +static void usbi_log_str(struct libusb_context *ctx, + enum libusb_log_level level, const char * str) +{ +#if defined(USE_SYSTEM_LOGGING_FACILITY) +#if defined(OS_WINDOWS) + OutputDebugString(str); +#elif defined(OS_WINCE) + /* Windows CE only supports the Unicode version of OutputDebugString. */ + WCHAR wbuf[USBI_MAX_LOG_LEN]; + MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf)); + OutputDebugStringW(wbuf); +#elif defined(__ANDROID__) + int priority = ANDROID_LOG_UNKNOWN; + switch (level) { + case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break; + case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break; + case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break; + case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break; + } + __android_log_write(priority, "libusb", str); +#elif defined(HAVE_SYSLOG_FUNC) + int syslog_level = LOG_INFO; + switch (level) { + case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break; + case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break; + case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break; + case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break; + } + syslog(syslog_level, "%s", str); +#else /* All of gcc, Clang, XCode seem to use #warning */ +#warning System logging is not supported on this platform. Logging to stderr will be used instead. + fputs(str, stderr); +#endif +#else + fputs(str, stderr); +#endif /* USE_SYSTEM_LOGGING_FACILITY */ + UNUSED(ctx); + UNUSED(level); +} + +void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, va_list args) +{ + const char *prefix = ""; + char buf[USBI_MAX_LOG_LEN]; + struct timespec now; + int global_debug, header_len, text_len; + static int has_debug_header_been_displayed = 0; + +#ifdef ENABLE_DEBUG_LOGGING + global_debug = 1; + UNUSED(ctx); +#else + int ctx_level = 0; + + USBI_GET_CONTEXT(ctx); + if (ctx) { + ctx_level = ctx->debug; + } else { + char *dbg = getenv("LIBUSB_DEBUG"); + if (dbg) + ctx_level = atoi(dbg); + } + global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG); + if (!ctx_level) + return; + if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING) + return; + if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO) + return; + if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG) + return; +#endif + + usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &now); + if ((global_debug) && (!has_debug_header_been_displayed)) { + has_debug_header_been_displayed = 1; + usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] " USBI_LOG_LINE_END); + usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END); + } + if (now.tv_nsec < timestamp_origin.tv_nsec) { + now.tv_sec--; + now.tv_nsec += 1000000000L; + } + now.tv_sec -= timestamp_origin.tv_sec; + now.tv_nsec -= timestamp_origin.tv_nsec; + + switch (level) { + case LIBUSB_LOG_LEVEL_INFO: + prefix = "info"; + break; + case LIBUSB_LOG_LEVEL_WARNING: + prefix = "warning"; + break; + case LIBUSB_LOG_LEVEL_ERROR: + prefix = "error"; + break; + case LIBUSB_LOG_LEVEL_DEBUG: + prefix = "debug"; + break; + case LIBUSB_LOG_LEVEL_NONE: + return; + default: + prefix = "unknown"; + break; + } + + if (global_debug) { + header_len = snprintf(buf, sizeof(buf), + "[%2d.%06d] [%08x] libusb: %s [%s] ", + (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function); + } else { + header_len = snprintf(buf, sizeof(buf), + "libusb: %s [%s] ", prefix, function); + } + + if (header_len < 0 || header_len >= (int)sizeof(buf)) { + /* Somehow snprintf failed to write to the buffer, + * remove the header so something useful is output. */ + header_len = 0; + } + /* Make sure buffer is NUL terminated */ + buf[header_len] = '\0'; + text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len, + format, args); + if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) { + /* Truncated log output. On some platforms a -1 return value means + * that the output was truncated. */ + text_len = sizeof(buf) - header_len; + } + if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) { + /* Need to truncate the text slightly to fit on the terminator. */ + text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf); + } + strcpy(buf + header_len + text_len, USBI_LOG_LINE_END); + + usbi_log_str(ctx, level, buf); +} + +void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, ...) +{ + va_list args; + + va_start (args, format); + usbi_log_v(ctx, level, function, format, args); + va_end (args); +} + +/** \ingroup libusb_misc + * Returns a constant NULL-terminated string with the ASCII name of a libusb + * error or transfer status code. The caller must not free() the returned + * string. + * + * \param error_code The \ref libusb_error or libusb_transfer_status code to + * return the name of. + * \returns The error name, or the string **UNKNOWN** if the value of + * error_code is not a known error / status code. + */ +DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code) +{ + switch (error_code) { + case LIBUSB_ERROR_IO: + return "LIBUSB_ERROR_IO"; + case LIBUSB_ERROR_INVALID_PARAM: + return "LIBUSB_ERROR_INVALID_PARAM"; + case LIBUSB_ERROR_ACCESS: + return "LIBUSB_ERROR_ACCESS"; + case LIBUSB_ERROR_NO_DEVICE: + return "LIBUSB_ERROR_NO_DEVICE"; + case LIBUSB_ERROR_NOT_FOUND: + return "LIBUSB_ERROR_NOT_FOUND"; + case LIBUSB_ERROR_BUSY: + return "LIBUSB_ERROR_BUSY"; + case LIBUSB_ERROR_TIMEOUT: + return "LIBUSB_ERROR_TIMEOUT"; + case LIBUSB_ERROR_OVERFLOW: + return "LIBUSB_ERROR_OVERFLOW"; + case LIBUSB_ERROR_PIPE: + return "LIBUSB_ERROR_PIPE"; + case LIBUSB_ERROR_INTERRUPTED: + return "LIBUSB_ERROR_INTERRUPTED"; + case LIBUSB_ERROR_NO_MEM: + return "LIBUSB_ERROR_NO_MEM"; + case LIBUSB_ERROR_NOT_SUPPORTED: + return "LIBUSB_ERROR_NOT_SUPPORTED"; + case LIBUSB_ERROR_OTHER: + return "LIBUSB_ERROR_OTHER"; + + case LIBUSB_TRANSFER_ERROR: + return "LIBUSB_TRANSFER_ERROR"; + case LIBUSB_TRANSFER_TIMED_OUT: + return "LIBUSB_TRANSFER_TIMED_OUT"; + case LIBUSB_TRANSFER_CANCELLED: + return "LIBUSB_TRANSFER_CANCELLED"; + case LIBUSB_TRANSFER_STALL: + return "LIBUSB_TRANSFER_STALL"; + case LIBUSB_TRANSFER_NO_DEVICE: + return "LIBUSB_TRANSFER_NO_DEVICE"; + case LIBUSB_TRANSFER_OVERFLOW: + return "LIBUSB_TRANSFER_OVERFLOW"; + + case 0: + return "LIBUSB_SUCCESS / LIBUSB_TRANSFER_COMPLETED"; + default: + return "**UNKNOWN**"; + } +} + +/** \ingroup libusb_misc + * Returns a pointer to const struct libusb_version with the version + * (major, minor, micro, nano and rc) of the running library. + */ +DEFAULT_VISIBILITY +const struct libusb_version * LIBUSB_CALL libusb_get_version(void) +{ + return &libusb_version_internal; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/descriptor.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/descriptor.c new file mode 100644 index 0000000..4c9435f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/descriptor.c @@ -0,0 +1,1191 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * USB descriptor handling functions for libusb + * Copyright © 2007 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#include "libusbi.h" + +#define DESC_HEADER_LENGTH 2 +#define DEVICE_DESC_LENGTH 18 +#define CONFIG_DESC_LENGTH 9 +#define INTERFACE_DESC_LENGTH 9 +#define ENDPOINT_DESC_LENGTH 7 +#define ENDPOINT_AUDIO_DESC_LENGTH 9 + +/** @defgroup libusb_desc USB descriptors + * This page details how to examine the various standard USB descriptors + * for detected devices + */ + +/* set host_endian if the w values are already in host endian format, + * as opposed to bus endian. */ +int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, + void *dest, int host_endian) +{ + const unsigned char *sp = source; + unsigned char *dp = dest; + uint16_t w; + const char *cp; + uint32_t d; + + for (cp = descriptor; *cp; cp++) { + switch (*cp) { + case 'b': /* 8-bit byte */ + *dp++ = *sp++; + break; + case 'w': /* 16-bit word, convert from little endian to CPU */ + dp += ((uintptr_t)dp & 1); /* Align to word boundary */ + + if (host_endian) { + memcpy(dp, sp, 2); + } else { + w = (sp[1] << 8) | sp[0]; + *((uint16_t *)dp) = w; + } + sp += 2; + dp += 2; + break; + case 'd': /* 32-bit word, convert from little endian to CPU */ + dp += ((uintptr_t)dp & 1); /* Align to word boundary */ + + if (host_endian) { + memcpy(dp, sp, 4); + } else { + d = (sp[3] << 24) | (sp[2] << 16) | + (sp[1] << 8) | sp[0]; + *((uint32_t *)dp) = d; + } + sp += 4; + dp += 4; + break; + case 'u': /* 16 byte UUID */ + memcpy(dp, sp, 16); + sp += 16; + dp += 16; + break; + } + } + + return (int) (sp - source); +} + +static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint) +{ + free((void *) endpoint->extra); +} + +static int parse_endpoint(struct libusb_context *ctx, + struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer, + int size, int host_endian) +{ + struct usb_descriptor_header header; + unsigned char *extra; + unsigned char *begin; + int parsed = 0; + int len; + + if (size < DESC_HEADER_LENGTH) { + usbi_err(ctx, "short endpoint descriptor read %d/%d", + size, DESC_HEADER_LENGTH); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(buffer, "bb", &header, 0); + if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) { + usbi_err(ctx, "unexpected descriptor %x (expected %x)", + header.bDescriptorType, LIBUSB_DT_ENDPOINT); + return parsed; + } + if (header.bLength > size) { + usbi_warn(ctx, "short endpoint descriptor read %d/%d", + size, header.bLength); + return parsed; + } + if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH) + usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian); + else if (header.bLength >= ENDPOINT_DESC_LENGTH) + usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian); + else { + usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength); + return LIBUSB_ERROR_IO; + } + + buffer += header.bLength; + size -= header.bLength; + parsed += header.bLength; + + /* Skip over the rest of the Class Specific or Vendor Specific */ + /* descriptors */ + begin = buffer; + while (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + if (header.bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid extra ep desc len (%d)", + header.bLength); + return LIBUSB_ERROR_IO; + } else if (header.bLength > size) { + usbi_warn(ctx, "short extra ep desc read %d/%d", + size, header.bLength); + return parsed; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) || + (header.bDescriptorType == LIBUSB_DT_INTERFACE) || + (header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) + break; + + usbi_dbg("skipping descriptor %x", header.bDescriptorType); + buffer += header.bLength; + size -= header.bLength; + parsed += header.bLength; + } + + /* Copy any unknown descriptors into a storage area for drivers */ + /* to later parse */ + len = (int)(buffer - begin); + if (!len) { + endpoint->extra = NULL; + endpoint->extra_length = 0; + return parsed; + } + + extra = malloc(len); + endpoint->extra = extra; + if (!extra) { + endpoint->extra_length = 0; + return LIBUSB_ERROR_NO_MEM; + } + + memcpy(extra, begin, len); + endpoint->extra_length = len; + + return parsed; +} + +static void clear_interface(struct libusb_interface *usb_interface) +{ + int i; + int j; + + if (usb_interface->altsetting) { + for (i = 0; i < usb_interface->num_altsetting; i++) { + struct libusb_interface_descriptor *ifp = + (struct libusb_interface_descriptor *) + usb_interface->altsetting + i; + free((void *) ifp->extra); + if (ifp->endpoint) { + for (j = 0; j < ifp->bNumEndpoints; j++) + clear_endpoint((struct libusb_endpoint_descriptor *) + ifp->endpoint + j); + } + free((void *) ifp->endpoint); + } + } + free((void *) usb_interface->altsetting); + usb_interface->altsetting = NULL; +} + +static int parse_interface(libusb_context *ctx, + struct libusb_interface *usb_interface, unsigned char *buffer, int size, + int host_endian) +{ + int i; + int len; + int r; + int parsed = 0; + int interface_number = -1; + struct usb_descriptor_header header; + struct libusb_interface_descriptor *ifp; + unsigned char *begin; + + usb_interface->num_altsetting = 0; + + while (size >= INTERFACE_DESC_LENGTH) { + struct libusb_interface_descriptor *altsetting = + (struct libusb_interface_descriptor *) usb_interface->altsetting; + altsetting = usbi_reallocf(altsetting, + sizeof(struct libusb_interface_descriptor) * + (usb_interface->num_altsetting + 1)); + if (!altsetting) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + usb_interface->altsetting = altsetting; + + ifp = altsetting + usb_interface->num_altsetting; + usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0); + if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) { + usbi_err(ctx, "unexpected descriptor %x (expected %x)", + ifp->bDescriptorType, LIBUSB_DT_INTERFACE); + return parsed; + } + if (ifp->bLength < INTERFACE_DESC_LENGTH) { + usbi_err(ctx, "invalid interface bLength (%d)", + ifp->bLength); + r = LIBUSB_ERROR_IO; + goto err; + } + if (ifp->bLength > size) { + usbi_warn(ctx, "short intf descriptor read %d/%d", + size, ifp->bLength); + return parsed; + } + if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { + usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints); + r = LIBUSB_ERROR_IO; + goto err; + } + + usb_interface->num_altsetting++; + ifp->extra = NULL; + ifp->extra_length = 0; + ifp->endpoint = NULL; + + if (interface_number == -1) + interface_number = ifp->bInterfaceNumber; + + /* Skip over the interface */ + buffer += ifp->bLength; + parsed += ifp->bLength; + size -= ifp->bLength; + + begin = buffer; + + /* Skip over any interface, class or vendor descriptors */ + while (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + if (header.bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, + "invalid extra intf desc len (%d)", + header.bLength); + r = LIBUSB_ERROR_IO; + goto err; + } else if (header.bLength > size) { + usbi_warn(ctx, + "short extra intf desc read %d/%d", + size, header.bLength); + return parsed; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) || + (header.bDescriptorType == LIBUSB_DT_ENDPOINT) || + (header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) + break; + + buffer += header.bLength; + parsed += header.bLength; + size -= header.bLength; + } + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (len) { + ifp->extra = malloc(len); + if (!ifp->extra) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + memcpy((unsigned char *) ifp->extra, begin, len); + ifp->extra_length = len; + } + + if (ifp->bNumEndpoints > 0) { + struct libusb_endpoint_descriptor *endpoint; + endpoint = calloc(ifp->bNumEndpoints, sizeof(struct libusb_endpoint_descriptor)); + ifp->endpoint = endpoint; + if (!endpoint) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + + for (i = 0; i < ifp->bNumEndpoints; i++) { + r = parse_endpoint(ctx, endpoint + i, buffer, size, + host_endian); + if (r < 0) + goto err; + if (r == 0) { + ifp->bNumEndpoints = (uint8_t)i; + break;; + } + + buffer += r; + parsed += r; + size -= r; + } + } + + /* We check to see if it's an alternate to this one */ + ifp = (struct libusb_interface_descriptor *) buffer; + if (size < LIBUSB_DT_INTERFACE_SIZE || + ifp->bDescriptorType != LIBUSB_DT_INTERFACE || + ifp->bInterfaceNumber != interface_number) + return parsed; + } + + return parsed; +err: + clear_interface(usb_interface); + return r; +} + +static void clear_configuration(struct libusb_config_descriptor *config) +{ + int i; + if (config->interface) { + for (i = 0; i < config->bNumInterfaces; i++) + clear_interface((struct libusb_interface *) + config->interface + i); + } + free((void *) config->interface); + free((void *) config->extra); +} + +static int parse_configuration(struct libusb_context *ctx, + struct libusb_config_descriptor *config, unsigned char *buffer, + int size, int host_endian) +{ + int i; + int r; + struct usb_descriptor_header header; + struct libusb_interface *usb_interface; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian); + if (config->bDescriptorType != LIBUSB_DT_CONFIG) { + usbi_err(ctx, "unexpected descriptor %x (expected %x)", + config->bDescriptorType, LIBUSB_DT_CONFIG); + return LIBUSB_ERROR_IO; + } + if (config->bLength < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid config bLength (%d)", config->bLength); + return LIBUSB_ERROR_IO; + } + if (config->bLength > size) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, config->bLength); + return LIBUSB_ERROR_IO; + } + if (config->bNumInterfaces > USB_MAXINTERFACES) { + usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces); + return LIBUSB_ERROR_IO; + } + + usb_interface = calloc(config->bNumInterfaces, sizeof(struct libusb_interface)); + config->interface = usb_interface; + if (!usb_interface) + return LIBUSB_ERROR_NO_MEM; + + buffer += config->bLength; + size -= config->bLength; + + config->extra = NULL; + config->extra_length = 0; + + for (i = 0; i < config->bNumInterfaces; i++) { + int len; + unsigned char *begin; + + /* Skip over the rest of the Class Specific or Vendor */ + /* Specific descriptors */ + begin = buffer; + while (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + + if (header.bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, + "invalid extra config desc len (%d)", + header.bLength); + r = LIBUSB_ERROR_IO; + goto err; + } else if (header.bLength > size) { + usbi_warn(ctx, + "short extra config desc read %d/%d", + size, header.bLength); + config->bNumInterfaces = (uint8_t)i; + return size; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) || + (header.bDescriptorType == LIBUSB_DT_INTERFACE) || + (header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) + break; + + usbi_dbg("skipping descriptor 0x%x", header.bDescriptorType); + buffer += header.bLength; + size -= header.bLength; + } + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (len) { + /* FIXME: We should realloc and append here */ + if (!config->extra_length) { + config->extra = malloc(len); + if (!config->extra) { + r = LIBUSB_ERROR_NO_MEM; + goto err; + } + + memcpy((unsigned char *) config->extra, begin, len); + config->extra_length = len; + } + } + + r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian); + if (r < 0) + goto err; + if (r == 0) { + config->bNumInterfaces = (uint8_t)i; + break; + } + + buffer += r; + size -= r; + } + + return size; + +err: + clear_configuration(config); + return r; +} + +static int raw_desc_to_config(struct libusb_context *ctx, + unsigned char *buf, int size, int host_endian, + struct libusb_config_descriptor **config) +{ + struct libusb_config_descriptor *_config = malloc(sizeof(*_config)); + int r; + + if (!_config) + return LIBUSB_ERROR_NO_MEM; + + r = parse_configuration(ctx, _config, buf, size, host_endian); + if (r < 0) { + usbi_err(ctx, "parse_configuration failed with error %d", r); + free(_config); + return r; + } else if (r > 0) { + usbi_warn(ctx, "still %d bytes of descriptor data left", r); + } + + *config = _config; + return LIBUSB_SUCCESS; +} + +int usbi_device_cache_descriptor(libusb_device *dev) +{ + int r, host_endian = 0; + + r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor, + &host_endian); + if (r < 0) + return r; + + if (!host_endian) { + dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB); + dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor); + dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct); + dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice); + } + + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get the USB device descriptor for a given device. + * + * This is a non-blocking function; the device descriptor is cached in memory. + * + * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this + * function always succeeds. + * + * \param dev the device + * \param desc output location for the descriptor data + * \returns 0 on success or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc) +{ + usbi_dbg(""); + memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor, + sizeof (dev->device_descriptor)); + return 0; +} + +/** \ingroup libusb_desc + * Get the USB configuration descriptor for the currently active configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_config_descriptor + */ +int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config) +{ + struct libusb_config_descriptor _config; + unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; + unsigned char *buf = NULL; + int host_endian = 0; + int r; + + r = usbi_backend->get_active_config_descriptor(dev, tmp, + LIBUSB_DT_CONFIG_SIZE, &host_endian); + if (r < 0) + return r; + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(dev->ctx, "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); + buf = malloc(_config.wTotalLength); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = usbi_backend->get_active_config_descriptor(dev, buf, + _config.wTotalLength, &host_endian); + if (r >= 0) + r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get a USB configuration descriptor based on its index. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config_index the index of the configuration you wish to retrieve + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_config_descriptor() + * \see libusb_get_config_descriptor_by_value() + */ +int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config) +{ + struct libusb_config_descriptor _config; + unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; + unsigned char *buf = NULL; + int host_endian = 0; + int r; + + usbi_dbg("index %d", config_index); + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_NOT_FOUND; + + r = usbi_backend->get_config_descriptor(dev, config_index, tmp, + LIBUSB_DT_CONFIG_SIZE, &host_endian); + if (r < 0) + return r; + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(dev->ctx, "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); + buf = malloc(_config.wTotalLength); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = usbi_backend->get_config_descriptor(dev, config_index, buf, + _config.wTotalLength, &host_endian); + if (r >= 0) + r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + + free(buf); + return r; +} + +/* iterate through all configurations, returning the index of the configuration + * matching a specific bConfigurationValue in the idx output parameter, or -1 + * if the config was not found. + * returns 0 on success or a LIBUSB_ERROR code + */ +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx) +{ + uint8_t i; + + usbi_dbg("value %d", bConfigurationValue); + for (i = 0; i < dev->num_configurations; i++) { + unsigned char tmp[6]; + int host_endian; + int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp), + &host_endian); + if (r < 0) { + *idx = -1; + return r; + } + if (tmp[5] == bConfigurationValue) { + *idx = i; + return 0; + } + } + + *idx = -1; + return 0; +} + +/** \ingroup libusb_desc + * Get a USB configuration descriptor with a specific bConfigurationValue. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param bConfigurationValue the bConfigurationValue of the configuration you + * wish to retrieve + * \param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_config_descriptor() + * \see libusb_get_config_descriptor() + */ +int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config) +{ + int r, idx, host_endian; + unsigned char *buf = NULL; + + if (usbi_backend->get_config_descriptor_by_value) { + r = usbi_backend->get_config_descriptor_by_value(dev, + bConfigurationValue, &buf, &host_endian); + if (r < 0) + return r; + return raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + } + + r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx); + if (r < 0) + return r; + else if (idx == -1) + return LIBUSB_ERROR_NOT_FOUND; + else + return libusb_get_config_descriptor(dev, (uint8_t) idx, config); +} + +/** \ingroup libusb_desc + * Free a configuration descriptor obtained from + * libusb_get_active_config_descriptor() or libusb_get_config_descriptor(). + * It is safe to call this function with a NULL config parameter, in which + * case the function simply returns. + * + * \param config the configuration descriptor to free + */ +void API_EXPORTED libusb_free_config_descriptor( + struct libusb_config_descriptor *config) +{ + if (!config) + return; + + clear_configuration(config); + free(config); +} + +/** \ingroup libusb_desc + * Get an endpoints superspeed endpoint companion descriptor (if any) + * + * \param ctx the context to operate on, or NULL for the default context + * \param endpoint endpoint descriptor from which to get the superspeed + * endpoint companion descriptor + * \param ep_comp output location for the superspeed endpoint companion + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_endpoint_companion_descriptor() after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( + struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp) +{ + struct usb_descriptor_header header; + int size = endpoint->extra_length; + const unsigned char *buffer = endpoint->extra; + + *ep_comp = NULL; + + while (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + if (header.bLength < 2 || header.bLength > size) { + usbi_err(ctx, "invalid descriptor length %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { + buffer += header.bLength; + size -= header.bLength; + continue; + } + if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { + usbi_err(ctx, "invalid ss-ep-comp-desc length %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + *ep_comp = malloc(sizeof(**ep_comp)); + if (*ep_comp == NULL) + return LIBUSB_ERROR_NO_MEM; + usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0); + return LIBUSB_SUCCESS; + } + return LIBUSB_ERROR_NOT_FOUND; +} + +/** \ingroup libusb_desc + * Free a superspeed endpoint companion descriptor obtained from + * libusb_get_ss_endpoint_companion_descriptor(). + * It is safe to call this function with a NULL ep_comp parameter, in which + * case the function simply returns. + * + * \param ep_comp the superspeed endpoint companion descriptor to free + */ +void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp) +{ + free(ep_comp); +} + +static int parse_bos(struct libusb_context *ctx, + struct libusb_bos_descriptor **bos, + unsigned char *buffer, int size, int host_endian) +{ + struct libusb_bos_descriptor bos_header, *_bos; + struct libusb_bos_dev_capability_descriptor dev_cap; + int i; + + if (size < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "short bos descriptor read %d/%d", + size, LIBUSB_DT_BOS_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian); + if (bos_header.bDescriptorType != LIBUSB_DT_BOS) { + usbi_err(ctx, "unexpected descriptor %x (expected %x)", + bos_header.bDescriptorType, LIBUSB_DT_BOS); + return LIBUSB_ERROR_IO; + } + if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength); + return LIBUSB_ERROR_IO; + } + if (bos_header.bLength > size) { + usbi_err(ctx, "short bos descriptor read %d/%d", + size, bos_header.bLength); + return LIBUSB_ERROR_IO; + } + + _bos = calloc (1, + sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *)); + if (!_bos) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian); + buffer += bos_header.bLength; + size -= bos_header.bLength; + + /* Get the device capability descriptors */ + for (i = 0; i < bos_header.bNumDeviceCaps; i++) { + if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%d", + size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE); + break; + } + usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian); + if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { + usbi_warn(ctx, "unexpected descriptor %x (expected %x)", + dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); + break; + } + if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "invalid dev-cap bLength (%d)", + dev_cap.bLength); + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_IO; + } + if (dev_cap.bLength > size) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%d", + size, dev_cap.bLength); + break; + } + + _bos->dev_capability[i] = malloc(dev_cap.bLength); + if (!_bos->dev_capability[i]) { + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_NO_MEM; + } + memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength); + buffer += dev_cap.bLength; + size -= dev_cap.bLength; + } + _bos->bNumDeviceCaps = (uint8_t)i; + *bos = _bos; + + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get a Binary Object Store (BOS) descriptor + * This is a BLOCKING function, which will send requests to the device. + * + * \param dev_handle the handle of an open libusb device + * \param bos output location for the BOS descriptor. Only valid if 0 was returned. + * Must be freed with \ref libusb_free_bos_descriptor() after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos) +{ + struct libusb_bos_descriptor _bos; + uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0}; + unsigned char *bos_data = NULL; + const int host_endian = 0; + int r; + + /* Read the BOS. This generates 2 requests on the bus, + * one for the header, and one for the full BOS */ + r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_header, + LIBUSB_DT_BOS_SIZE); + if (r < 0) { + if (r != LIBUSB_ERROR_PIPE) + usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); + return r; + } + if (r < LIBUSB_DT_BOS_SIZE) { + usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d", + r, LIBUSB_DT_BOS_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian); + usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities", + _bos.wTotalLength, _bos.bNumDeviceCaps); + bos_data = calloc(_bos.wTotalLength, 1); + if (bos_data == NULL) + return LIBUSB_ERROR_NO_MEM; + + r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, + _bos.wTotalLength); + if (r >= 0) + r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r, host_endian); + else + usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); + + free(bos_data); + return r; +} + +/** \ingroup libusb_desc + * Free a BOS descriptor obtained from libusb_get_bos_descriptor(). + * It is safe to call this function with a NULL bos parameter, in which + * case the function simply returns. + * + * \param bos the BOS descriptor to free + */ +void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) +{ + int i; + + if (!bos) + return; + + for (i = 0; i < bos->bNumDeviceCaps; i++) + free(bos->dev_capability[i]); + free(bos); +} + +/** \ingroup libusb_desc + * Get an USB 2.0 Extension descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION + * \param usb_2_0_extension output location for the USB 2.0 Extension + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_usb_2_0_extension_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) +{ + struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_USB_2_0_EXTENSION); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE); + return LIBUSB_ERROR_IO; + } + + _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension)); + if (!_usb_2_0_extension) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd", + _usb_2_0_extension, host_endian); + + *usb_2_0_extension = _usb_2_0_extension; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a USB 2.0 Extension descriptor obtained from + * libusb_get_usb_2_0_extension_descriptor(). + * It is safe to call this function with a NULL usb_2_0_extension parameter, + * in which case the function simply returns. + * + * \param usb_2_0_extension the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension) +{ + free(usb_2_0_extension); +} + +/** \ingroup libusb_desc + * Get a SuperSpeed USB Device Capability descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * \param ss_usb_device_cap output location for the SuperSpeed USB Device + * Capability descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_usb_device_capability_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap) +{ + struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE); + return LIBUSB_ERROR_IO; + } + + _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap)); + if (!_ss_usb_device_cap) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw", + _ss_usb_device_cap, host_endian); + + *ss_usb_device_cap = _ss_usb_device_cap; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a SuperSpeed USB Device Capability descriptor obtained from + * libusb_get_ss_usb_device_capability_descriptor(). + * It is safe to call this function with a NULL ss_usb_device_cap + * parameter, in which case the function simply returns. + * + * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap) +{ + free(ss_usb_device_cap); +} + +/** \ingroup libusb_desc + * Get a Container ID descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID + * \param container_id output location for the Container ID descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_container_id_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id) +{ + struct libusb_container_id_descriptor *_container_id; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_CONTAINER_ID); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE); + return LIBUSB_ERROR_IO; + } + + _container_id = malloc(sizeof(*_container_id)); + if (!_container_id) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu", + _container_id, host_endian); + + *container_id = _container_id; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a Container ID descriptor obtained from + * libusb_get_container_id_descriptor(). + * It is safe to call this function with a NULL container_id parameter, + * in which case the function simply returns. + * + * \param container_id the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id) +{ + free(container_id); +} + +/** \ingroup libusb_desc + * Retrieve a string descriptor in C style ASCII. + * + * Wrapper around libusb_get_string_descriptor(). Uses the first language + * supported by the device. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for ASCII string descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length) +{ + unsigned char tbuf[255]; /* Some devices choke on size > 255 */ + int r, si, di; + uint16_t langid; + + /* Asking for the zero'th index is special - it returns a string + * descriptor that contains all the language IDs supported by the + * device. Typically there aren't many - often only one. Language + * IDs are 16 bit numbers, and they start at the third byte in the + * descriptor. There's also no point in trying to read descriptor 0 + * with this function. See USB 2.0 specification section 9.6.7 for + * more information. + */ + + if (desc_index == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_get_string_descriptor(dev_handle, 0, 0, tbuf, sizeof(tbuf)); + if (r < 0) + return r; + + if (r < 4) + return LIBUSB_ERROR_IO; + + langid = tbuf[2] | (tbuf[3] << 8); + + r = libusb_get_string_descriptor(dev_handle, desc_index, langid, tbuf, + sizeof(tbuf)); + if (r < 0) + return r; + + if (tbuf[1] != LIBUSB_DT_STRING) + return LIBUSB_ERROR_IO; + + if (tbuf[0] > r) + return LIBUSB_ERROR_IO; + + for (di = 0, si = 2; si < tbuf[0]; si += 2) { + if (di >= (length - 1)) + break; + + if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */ + data[di++] = '?'; + else + data[di++] = tbuf[si]; + } + + data[di] = 0; + return di; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.c new file mode 100644 index 0000000..bbfd6e7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.c @@ -0,0 +1,350 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Hotplug functions for libusb + * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2012-2013 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +#include "libusbi.h" +#include "hotplug.h" + +/** + * @defgroup libusb_hotplug Device hotplug event notification + * This page details how to use the libusb hotplug interface, where available. + * + * Be mindful that not all platforms currently implement hotplug notification and + * that you should first call on \ref libusb_has_capability() with parameter + * \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available. + * + * \page libusb_hotplug Device hotplug event notification + * + * \section hotplug_intro Introduction + * + * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support + * for hotplug events on some platforms (you should test if your platform + * supports hotplug notification by calling \ref libusb_has_capability() with + * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). + * + * This interface allows you to request notification for the arrival and departure + * of matching USB devices. + * + * To receive hotplug notification you register a callback by calling + * \ref libusb_hotplug_register_callback(). This function will optionally return + * a callback handle that can be passed to \ref libusb_hotplug_deregister_callback(). + * + * A callback function must return an int (0 or 1) indicating whether the callback is + * expecting additional events. Returning 0 will rearm the callback and 1 will cause + * the callback to be deregistered. Note that when callbacks are called from + * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE + * flag, the callback return value is ignored, iow you cannot cause a callback + * to be deregistered by returning 1 when it is called from + * libusb_hotplug_register_callback(). + * + * Callbacks for a particular context are automatically deregistered by libusb_exit(). + * + * As of 1.0.16 there are two supported hotplug events: + * - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use + * - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available + * + * A hotplug event can listen for either or both of these events. + * + * Note: If you receive notification that a device has left and you have any + * a libusb_device_handles for the device it is up to you to call libusb_close() + * on each device handle to free up any remaining resources associated with the device. + * Once a device has left any libusb_device_handle associated with the device + * are invalid and will remain so even if the device comes back. + * + * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered + * safe to call any libusb function that takes a libusb_device. It also safe to + * open a device and submit asynchronous transfers. However, most other functions + * that take a libusb_device_handle are not safe to call. Examples of such + * functions are any of the \ref libusb_syncio "synchronous API" functions or the blocking + * functions that retrieve various \ref libusb_desc "USB descriptors". These functions must + * be used outside of the context of the hotplug callback. + * + * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function + * is libusb_get_device_descriptor(). + * + * The following code provides an example of the usage of the hotplug interface: +\code +#include +#include +#include +#include + +static int count = 0; + +int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event, void *user_data) { + static libusb_device_handle *dev_handle = NULL; + struct libusb_device_descriptor desc; + int rc; + + (void)libusb_get_device_descriptor(dev, &desc); + + if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { + rc = libusb_open(dev, &dev_handle); + if (LIBUSB_SUCCESS != rc) { + printf("Could not open USB device\n"); + } + } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { + if (dev_handle) { + libusb_close(dev_handle); + dev_handle = NULL; + } + } else { + printf("Unhandled event %d\n", event); + } + count++; + + return 0; +} + +int main (void) { + libusb_hotplug_callback_handle callback_handle; + int rc; + + libusb_init(NULL); + + rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, + LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, + &callback_handle); + if (LIBUSB_SUCCESS != rc) { + printf("Error creating a hotplug callback\n"); + libusb_exit(NULL); + return EXIT_FAILURE; + } + + while (count < 2) { + libusb_handle_events_completed(NULL, NULL); + nanosleep(&(struct timespec){0, 10000000UL}, NULL); + } + + libusb_hotplug_deregister_callback(NULL, callback_handle); + libusb_exit(NULL); + + return 0; +} +\endcode + */ + +static int usbi_hotplug_match_cb (struct libusb_context *ctx, + struct libusb_device *dev, libusb_hotplug_event event, + struct libusb_hotplug_callback *hotplug_cb) +{ + /* Handle lazy deregistration of callback */ + if (hotplug_cb->needs_free) { + /* Free callback */ + return 1; + } + + if (!(hotplug_cb->events & event)) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id && + hotplug_cb->vendor_id != dev->device_descriptor.idVendor) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id && + hotplug_cb->product_id != dev->device_descriptor.idProduct) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class && + hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) { + return 0; + } + + return hotplug_cb->cb (ctx, dev, event, hotplug_cb->user_data); +} + +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event) +{ + struct libusb_hotplug_callback *hotplug_cb, *next; + int ret; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb); + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + if (ret) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + /* the backend is expected to call the callback for each active transfer */ +} + +void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event) +{ + int pending_events; + libusb_hotplug_message *message = calloc(1, sizeof(*message)); + + if (!message) { + usbi_err(ctx, "error allocating hotplug message"); + return; + } + + message->event = event; + message->device = dev; + + /* Take the event data lock and add this message to the list. + * Only signal an event if there are no prior pending events. */ + usbi_mutex_lock(&ctx->event_data_lock); + pending_events = usbi_pending_events(ctx); + list_add_tail(&message->list, &ctx->hotplug_msgs); + if (!pending_events) + usbi_signal_event(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); +} + +int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, libusb_hotplug_flag flags, + int vendor_id, int product_id, int dev_class, + libusb_hotplug_callback_fn cb_fn, void *user_data, + libusb_hotplug_callback_handle *callback_handle) +{ + libusb_hotplug_callback *new_callback; + static int handle_id = 1; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + /* check for sane values */ + if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) || + (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) || + (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || + !cb_fn) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + USBI_GET_CONTEXT(ctx); + + new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback)); + if (!new_callback) { + return LIBUSB_ERROR_NO_MEM; + } + + new_callback->ctx = ctx; + new_callback->vendor_id = vendor_id; + new_callback->product_id = product_id; + new_callback->dev_class = dev_class; + new_callback->flags = flags; + new_callback->events = events; + new_callback->cb = cb_fn; + new_callback->user_data = user_data; + new_callback->needs_free = 0; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + /* protect the handle by the context hotplug lock. it doesn't matter if the same handle + * is used for different contexts only that the handle is unique for this context */ + new_callback->handle = handle_id++; + + list_add(&new_callback->list, &ctx->hotplug_cbs); + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + + if (flags & LIBUSB_HOTPLUG_ENUMERATE) { + int i, len; + struct libusb_device **devs; + + len = (int) libusb_get_device_list(ctx, &devs); + if (len < 0) { + libusb_hotplug_deregister_callback(ctx, + new_callback->handle); + return len; + } + + for (i = 0; i < len; i++) { + usbi_hotplug_match_cb(ctx, devs[i], + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, + new_callback); + } + + libusb_free_device_list(devs, 1); + } + + + if (callback_handle) + *callback_handle = new_callback->handle; + + return LIBUSB_SUCCESS; +} + +void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle) +{ + struct libusb_hotplug_callback *hotplug_cb; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + return; + } + + USBI_GET_CONTEXT(ctx); + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, + struct libusb_hotplug_callback) { + if (callback_handle == hotplug_cb->handle) { + /* Mark this callback for deregistration */ + hotplug_cb->needs_free = 1; + } + } + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + usbi_hotplug_notification(ctx, NULL, 0); +} + +void usbi_hotplug_deregister_all(struct libusb_context *ctx) { + struct libusb_hotplug_callback *hotplug_cb, *next; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, + struct libusb_hotplug_callback) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.h new file mode 100644 index 0000000..2bec81b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/hotplug.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Hotplug support for libusb + * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2012-2013 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined(USBI_HOTPLUG_H) +#define USBI_HOTPLUG_H + +#ifndef LIBUSBI_H +#include "libusbi.h" +#endif + +/** \ingroup hotplug + * The hotplug callback structure. The user populates this structure with + * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback() + * to receive notification of hotplug events. + */ +struct libusb_hotplug_callback { + /** Context this callback is associated with */ + struct libusb_context *ctx; + + /** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int vendor_id; + + /** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int product_id; + + /** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int dev_class; + + /** Hotplug callback flags */ + libusb_hotplug_flag flags; + + /** Event(s) that will trigger this callback */ + libusb_hotplug_event events; + + /** Callback function to invoke for matching event/device */ + libusb_hotplug_callback_fn cb; + + /** Handle for this callback (used to match on deregister) */ + libusb_hotplug_callback_handle handle; + + /** User data that will be passed to the callback function */ + void *user_data; + + /** Callback is marked for deletion */ + int needs_free; + + /** List this callback is registered in (ctx->hotplug_cbs) */ + struct list_head list; +}; + +typedef struct libusb_hotplug_callback libusb_hotplug_callback; + +struct libusb_hotplug_message { + /** The hotplug event that occurred */ + libusb_hotplug_event event; + + /** The device for which this hotplug event occurred */ + struct libusb_device *device; + + /** List this message is contained in (ctx->hotplug_msgs) */ + struct list_head list; +}; + +typedef struct libusb_hotplug_message libusb_hotplug_message; + +void usbi_hotplug_deregister_all(struct libusb_context *ctx); +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event); +void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/io.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/io.c new file mode 100644 index 0000000..eb1eabf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/io.c @@ -0,0 +1,2819 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * I/O functions for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef USBI_TIMERFD_AVAILABLE +#include +#endif + +#include "libusbi.h" +#include "hotplug.h" + +/** + * \page libusb_io Synchronous and asynchronous device I/O + * + * \section io_intro Introduction + * + * If you're using libusb in your application, you're probably wanting to + * perform I/O with devices - you want to perform USB data transfers. + * + * libusb offers two separate interfaces for device I/O. This page aims to + * introduce the two in order to help you decide which one is more suitable + * for your application. You can also choose to use both interfaces in your + * application by considering each transfer on a case-by-case basis. + * + * Once you have read through the following discussion, you should consult the + * detailed API documentation pages for the details: + * - \ref libusb_syncio + * - \ref libusb_asyncio + * + * \section theory Transfers at a logical level + * + * At a logical level, USB transfers typically happen in two parts. For + * example, when reading data from a endpoint: + * -# A request for data is sent to the device + * -# Some time later, the incoming data is received by the host + * + * or when writing data to an endpoint: + * + * -# The data is sent to the device + * -# Some time later, the host receives acknowledgement from the device that + * the data has been transferred. + * + * There may be an indefinite delay between the two steps. Consider a + * fictional USB input device with a button that the user can press. In order + * to determine when the button is pressed, you would likely submit a request + * to read data on a bulk or interrupt endpoint and wait for data to arrive. + * Data will arrive when the button is pressed by the user, which is + * potentially hours later. + * + * libusb offers both a synchronous and an asynchronous interface to performing + * USB transfers. The main difference is that the synchronous interface + * combines both steps indicated above into a single function call, whereas + * the asynchronous interface separates them. + * + * \section sync The synchronous interface + * + * The synchronous I/O interface allows you to perform a USB transfer with + * a single function call. When the function call returns, the transfer has + * completed and you can parse the results. + * + * If you have used the libusb-0.1 before, this I/O style will seem familar to + * you. libusb-0.1 only offered a synchronous interface. + * + * In our input device example, to read button presses you might write code + * in the following style: +\code +unsigned char data[4]; +int actual_length; +int r = libusb_bulk_transfer(dev_handle, LIBUSB_ENDPOINT_IN, data, sizeof(data), &actual_length, 0); +if (r == 0 && actual_length == sizeof(data)) { + // results of the transaction can now be found in the data buffer + // parse them here and report button press +} else { + error(); +} +\endcode + * + * The main advantage of this model is simplicity: you did everything with + * a single simple function call. + * + * However, this interface has its limitations. Your application will sleep + * inside libusb_bulk_transfer() until the transaction has completed. If it + * takes the user 3 hours to press the button, your application will be + * sleeping for that long. Execution will be tied up inside the library - + * the entire thread will be useless for that duration. + * + * Another issue is that by tieing up the thread with that single transaction + * there is no possibility of performing I/O with multiple endpoints and/or + * multiple devices simultaneously, unless you resort to creating one thread + * per transaction. + * + * Additionally, there is no opportunity to cancel the transfer after the + * request has been submitted. + * + * For details on how to use the synchronous API, see the + * \ref libusb_syncio "synchronous I/O API documentation" pages. + * + * \section async The asynchronous interface + * + * Asynchronous I/O is the most significant new feature in libusb-1.0. + * Although it is a more complex interface, it solves all the issues detailed + * above. + * + * Instead of providing which functions that block until the I/O has complete, + * libusb's asynchronous interface presents non-blocking functions which + * begin a transfer and then return immediately. Your application passes a + * callback function pointer to this non-blocking function, which libusb will + * call with the results of the transaction when it has completed. + * + * Transfers which have been submitted through the non-blocking functions + * can be cancelled with a separate function call. + * + * The non-blocking nature of this interface allows you to be simultaneously + * performing I/O to multiple endpoints on multiple devices, without having + * to use threads. + * + * This added flexibility does come with some complications though: + * - In the interest of being a lightweight library, libusb does not create + * threads and can only operate when your application is calling into it. Your + * application must call into libusb from it's main loop when events are ready + * to be handled, or you must use some other scheme to allow libusb to + * undertake whatever work needs to be done. + * - libusb also needs to be called into at certain fixed points in time in + * order to accurately handle transfer timeouts. + * - Memory handling becomes more complex. You cannot use stack memory unless + * the function with that stack is guaranteed not to return until the transfer + * callback has finished executing. + * - You generally lose some linearity from your code flow because submitting + * the transfer request is done in a separate function from where the transfer + * results are handled. This becomes particularly obvious when you want to + * submit a second transfer based on the results of an earlier transfer. + * + * Internally, libusb's synchronous interface is expressed in terms of function + * calls to the asynchronous interface. + * + * For details on how to use the asynchronous API, see the + * \ref libusb_asyncio "asynchronous I/O API" documentation pages. + */ + + +/** + * \page libusb_packetoverflow Packets and overflows + * + * \section packets Packet abstraction + * + * The USB specifications describe how data is transmitted in packets, with + * constraints on packet size defined by endpoint descriptors. The host must + * not send data payloads larger than the endpoint's maximum packet size. + * + * libusb and the underlying OS abstract out the packet concept, allowing you + * to request transfers of any size. Internally, the request will be divided + * up into correctly-sized packets. You do not have to be concerned with + * packet sizes, but there is one exception when considering overflows. + * + * \section overflow Bulk/interrupt transfer overflows + * + * When requesting data on a bulk endpoint, libusb requires you to supply a + * buffer and the maximum number of bytes of data that libusb can put in that + * buffer. However, the size of the buffer is not communicated to the device - + * the device is just asked to send any amount of data. + * + * There is no problem if the device sends an amount of data that is less than + * or equal to the buffer size. libusb reports this condition to you through + * the \ref libusb_transfer::actual_length "libusb_transfer.actual_length" + * field. + * + * Problems may occur if the device attempts to send more data than can fit in + * the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but + * other behaviour is largely undefined: actual_length may or may not be + * accurate, the chunk of data that can fit in the buffer (before overflow) + * may or may not have been transferred. + * + * Overflows are nasty, but can be avoided. Even though you were told to + * ignore packets above, think about the lower level details: each transfer is + * split into packets (typically small, with a maximum size of 512 bytes). + * Overflows can only happen if the final packet in an incoming data transfer + * is smaller than the actual packet that the device wants to transfer. + * Therefore, you will never see an overflow if your transfer buffer size is a + * multiple of the endpoint's packet size: the final packet will either + * fill up completely or will be only partially filled. + */ + +/** + * @defgroup libusb_asyncio Asynchronous device I/O + * + * This page details libusb's asynchronous (non-blocking) API for USB device + * I/O. This interface is very powerful but is also quite complex - you will + * need to read this page carefully to understand the necessary considerations + * and issues surrounding use of this interface. Simplistic applications + * may wish to consider the \ref libusb_syncio "synchronous I/O API" instead. + * + * The asynchronous interface is built around the idea of separating transfer + * submission and handling of transfer completion (the synchronous model + * combines both of these into one). There may be a long delay between + * submission and completion, however the asynchronous submission function + * is non-blocking so will return control to your application during that + * potentially long delay. + * + * \section asyncabstraction Transfer abstraction + * + * For the asynchronous I/O, libusb implements the concept of a generic + * transfer entity for all types of I/O (control, bulk, interrupt, + * isochronous). The generic transfer object must be treated slightly + * differently depending on which type of I/O you are performing with it. + * + * This is represented by the public libusb_transfer structure type. + * + * \section asynctrf Asynchronous transfers + * + * We can view asynchronous I/O as a 5 step process: + * -# Allocation: allocate a libusb_transfer + * -# Filling: populate the libusb_transfer instance with information + * about the transfer you wish to perform + * -# Submission: ask libusb to submit the transfer + * -# Completion handling: examine transfer results in the + * libusb_transfer structure + * -# Deallocation: clean up resources + * + * + * \subsection asyncalloc Allocation + * + * This step involves allocating memory for a USB transfer. This is the + * generic transfer object mentioned above. At this stage, the transfer + * is "blank" with no details about what type of I/O it will be used for. + * + * Allocation is done with the libusb_alloc_transfer() function. You must use + * this function rather than allocating your own transfers. + * + * \subsection asyncfill Filling + * + * This step is where you take a previously allocated transfer and fill it + * with information to determine the message type and direction, data buffer, + * callback function, etc. + * + * You can either fill the required fields yourself or you can use the + * helper functions: libusb_fill_control_transfer(), libusb_fill_bulk_transfer() + * and libusb_fill_interrupt_transfer(). + * + * \subsection asyncsubmit Submission + * + * When you have allocated a transfer and filled it, you can submit it using + * libusb_submit_transfer(). This function returns immediately but can be + * regarded as firing off the I/O request in the background. + * + * \subsection asynccomplete Completion handling + * + * After a transfer has been submitted, one of four things can happen to it: + * + * - The transfer completes (i.e. some data was transferred) + * - The transfer has a timeout and the timeout expires before all data is + * transferred + * - The transfer fails due to an error + * - The transfer is cancelled + * + * Each of these will cause the user-specified transfer callback function to + * be invoked. It is up to the callback function to determine which of the + * above actually happened and to act accordingly. + * + * The user-specified callback is passed a pointer to the libusb_transfer + * structure which was used to setup and submit the transfer. At completion + * time, libusb has populated this structure with results of the transfer: + * success or failure reason, number of bytes of data transferred, etc. See + * the libusb_transfer structure documentation for more information. + * + * Important Note: The user-specified callback is called from an event + * handling context. It is therefore important that no calls are made into + * libusb that will attempt to perform any event handling. Examples of such + * functions are any listed in the \ref libusb_syncio "synchronous API" and any of + * the blocking functions that retrieve \ref libusb_desc "USB descriptors". + * + * \subsection Deallocation + * + * When a transfer has completed (i.e. the callback function has been invoked), + * you are advised to free the transfer (unless you wish to resubmit it, see + * below). Transfers are deallocated with libusb_free_transfer(). + * + * It is undefined behaviour to free a transfer which has not completed. + * + * \section asyncresubmit Resubmission + * + * You may be wondering why allocation, filling, and submission are all + * separated above where they could reasonably be combined into a single + * operation. + * + * The reason for separation is to allow you to resubmit transfers without + * having to allocate new ones every time. This is especially useful for + * common situations dealing with interrupt endpoints - you allocate one + * transfer, fill and submit it, and when it returns with results you just + * resubmit it for the next interrupt. + * + * \section asynccancel Cancellation + * + * Another advantage of using the asynchronous interface is that you have + * the ability to cancel transfers which have not yet completed. This is + * done by calling the libusb_cancel_transfer() function. + * + * libusb_cancel_transfer() is asynchronous/non-blocking in itself. When the + * cancellation actually completes, the transfer's callback function will + * be invoked, and the callback function should check the transfer status to + * determine that it was cancelled. + * + * Freeing the transfer after it has been cancelled but before cancellation + * has completed will result in undefined behaviour. + * + * When a transfer is cancelled, some of the data may have been transferred. + * libusb will communicate this to you in the transfer callback. Do not assume + * that no data was transferred. + * + * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints + * + * If your device does not have predictable transfer sizes (or it misbehaves), + * your application may submit a request for data on an IN endpoint which is + * smaller than the data that the device wishes to send. In some circumstances + * this will cause an overflow, which is a nasty condition to deal with. See + * the \ref libusb_packetoverflow page for discussion. + * + * \section asyncctrl Considerations for control transfers + * + * The libusb_transfer structure is generic and hence does not + * include specific fields for the control-specific setup packet structure. + * + * In order to perform a control transfer, you must place the 8-byte setup + * packet at the start of the data buffer. To simplify this, you could + * cast the buffer pointer to type struct libusb_control_setup, or you can + * use the helper function libusb_fill_control_setup(). + * + * The wLength field placed in the setup packet must be the length you would + * expect to be sent in the setup packet: the length of the payload that + * follows (or the expected maximum number of bytes to receive). However, + * the length field of the libusb_transfer object must be the length of + * the data buffer - i.e. it should be wLength plus the size of + * the setup packet (LIBUSB_CONTROL_SETUP_SIZE). + * + * If you use the helper functions, this is simplified for you: + * -# Allocate a buffer of size LIBUSB_CONTROL_SETUP_SIZE plus the size of the + * data you are sending/requesting. + * -# Call libusb_fill_control_setup() on the data buffer, using the transfer + * request size as the wLength value (i.e. do not include the extra space you + * allocated for the control setup). + * -# If this is a host-to-device transfer, place the data to be transferred + * in the data buffer, starting at offset LIBUSB_CONTROL_SETUP_SIZE. + * -# Call libusb_fill_control_transfer() to associate the data buffer with + * the transfer (and to set the remaining details such as callback and timeout). + * - Note that there is no parameter to set the length field of the transfer. + * The length is automatically inferred from the wLength field of the setup + * packet. + * -# Submit the transfer. + * + * The multi-byte control setup fields (wValue, wIndex and wLength) must + * be given in little-endian byte order (the endianness of the USB bus). + * Endianness conversion is transparently handled by + * libusb_fill_control_setup() which is documented to accept host-endian + * values. + * + * Further considerations are needed when handling transfer completion in + * your callback function: + * - As you might expect, the setup packet will still be sitting at the start + * of the data buffer. + * - If this was a device-to-host transfer, the received data will be sitting + * at offset LIBUSB_CONTROL_SETUP_SIZE into the buffer. + * - The actual_length field of the transfer structure is relative to the + * wLength of the setup packet, rather than the size of the data buffer. So, + * if your wLength was 4, your transfer's length was 12, then you + * should expect an actual_length of 4 to indicate that the data was + * transferred in entirity. + * + * To simplify parsing of setup packets and obtaining the data from the + * correct offset, you may wish to use the libusb_control_transfer_get_data() + * and libusb_control_transfer_get_setup() functions within your transfer + * callback. + * + * Even though control endpoints do not halt, a completed control transfer + * may have a LIBUSB_TRANSFER_STALL status code. This indicates the control + * request was not supported. + * + * \section asyncintr Considerations for interrupt transfers + * + * All interrupt transfers are performed using the polling interval presented + * by the bInterval value of the endpoint descriptor. + * + * \section asynciso Considerations for isochronous transfers + * + * Isochronous transfers are more complicated than transfers to + * non-isochronous endpoints. + * + * To perform I/O to an isochronous endpoint, allocate the transfer by calling + * libusb_alloc_transfer() with an appropriate number of isochronous packets. + * + * During filling, set \ref libusb_transfer::type "type" to + * \ref libusb_transfer_type::LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + * "LIBUSB_TRANSFER_TYPE_ISOCHRONOUS", and set + * \ref libusb_transfer::num_iso_packets "num_iso_packets" to a value less than + * or equal to the number of packets you requested during allocation. + * libusb_alloc_transfer() does not set either of these fields for you, given + * that you might not even use the transfer on an isochronous endpoint. + * + * Next, populate the length field for the first num_iso_packets entries in + * the \ref libusb_transfer::iso_packet_desc "iso_packet_desc" array. Section + * 5.6.3 of the USB2 specifications describe how the maximum isochronous + * packet length is determined by the wMaxPacketSize field in the endpoint + * descriptor. + * Two functions can help you here: + * + * - libusb_get_max_iso_packet_size() is an easy way to determine the max + * packet size for an isochronous endpoint. Note that the maximum packet + * size is actually the maximum number of bytes that can be transmitted in + * a single microframe, therefore this function multiplies the maximum number + * of bytes per transaction by the number of transaction opportunities per + * microframe. + * - libusb_set_iso_packet_lengths() assigns the same length to all packets + * within a transfer, which is usually what you want. + * + * For outgoing transfers, you'll obviously fill the buffer and populate the + * packet descriptors in hope that all the data gets transferred. For incoming + * transfers, you must ensure the buffer has sufficient capacity for + * the situation where all packets transfer the full amount of requested data. + * + * Completion handling requires some extra consideration. The + * \ref libusb_transfer::actual_length "actual_length" field of the transfer + * is meaningless and should not be examined; instead you must refer to the + * \ref libusb_iso_packet_descriptor::actual_length "actual_length" field of + * each individual packet. + * + * The \ref libusb_transfer::status "status" field of the transfer is also a + * little misleading: + * - If the packets were submitted and the isochronous data microframes + * completed normally, status will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_COMPLETED + * "LIBUSB_TRANSFER_COMPLETED". Note that bus errors and software-incurred + * delays are not counted as transfer errors; the transfer.status field may + * indicate COMPLETED even if some or all of the packets failed. Refer to + * the \ref libusb_iso_packet_descriptor::status "status" field of each + * individual packet to determine packet failures. + * - The status field will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR + * "LIBUSB_TRANSFER_ERROR" only when serious errors were encountered. + * - Other transfer status codes occur with normal behaviour. + * + * The data for each packet will be found at an offset into the buffer that + * can be calculated as if each prior packet completed in full. The + * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple() + * functions may help you here. + * + * Note: Some operating systems (e.g. Linux) may impose limits on the + * length of individual isochronous packets and/or the total length of the + * isochronous transfer. Such limits can be difficult for libusb to detect, + * so the library will simply try and submit the transfer as set up by you. + * If the transfer fails to submit because it is too large, + * libusb_submit_transfer() will return + * \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM". + * + * \section asyncmem Memory caveats + * + * In most circumstances, it is not safe to use stack memory for transfer + * buffers. This is because the function that fired off the asynchronous + * transfer may return before libusb has finished using the buffer, and when + * the function returns it's stack gets destroyed. This is true for both + * host-to-device and device-to-host transfers. + * + * The only case in which it is safe to use stack memory is where you can + * guarantee that the function owning the stack space for the buffer does not + * return until after the transfer's callback function has completed. In every + * other case, you need to use heap memory instead. + * + * \section asyncflags Fine control + * + * Through using this asynchronous interface, you may find yourself repeating + * a few simple operations many times. You can apply a bitwise OR of certain + * flags to a transfer to simplify certain things: + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_SHORT_NOT_OK + * "LIBUSB_TRANSFER_SHORT_NOT_OK" results in transfers which transferred + * less than the requested amount of data being marked with status + * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR "LIBUSB_TRANSFER_ERROR" + * (they would normally be regarded as COMPLETED) + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER + * "LIBUSB_TRANSFER_FREE_BUFFER" allows you to ask libusb to free the transfer + * buffer when freeing the transfer. + * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_TRANSFER + * "LIBUSB_TRANSFER_FREE_TRANSFER" causes libusb to automatically free the + * transfer after the transfer callback returns. + * + * \section asyncevent Event handling + * + * An asynchronous model requires that libusb perform work at various + * points in time - namely processing the results of previously-submitted + * transfers and invoking the user-supplied callback function. + * + * This gives rise to the libusb_handle_events() function which your + * application must call into when libusb has work do to. This gives libusb + * the opportunity to reap pending transfers, invoke callbacks, etc. + * + * There are 2 different approaches to dealing with libusb_handle_events: + * + * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated + * thread. + * -# Integrate libusb with your application's main event loop. libusb + * exposes a set of file descriptors which allow you to do this. + * + * The first approach has the big advantage that it will also work on Windows + * were libusb' poll API for select / poll integration is not available. So + * if you want to support Windows and use the async API, you must use this + * approach, see the \ref eventthread "Using an event handling thread" section + * below for details. + * + * If you prefer a single threaded approach with a single central event loop, + * see the \ref libusb_poll "polling and timing" section for how to integrate libusb + * into your application's main event loop. + * + * \section eventthread Using an event handling thread + * + * Lets begin with stating the obvious: If you're going to use a separate + * thread for libusb event handling, your callback functions MUST be + * threadsafe. + * + * Other then that doing event handling from a separate thread, is mostly + * simple. You can use an event thread function as follows: +\code +void *event_thread_func(void *ctx) +{ + while (event_thread_run) + libusb_handle_events(ctx); + + return NULL; +} +\endcode + * + * There is one caveat though, stopping this thread requires setting the + * event_thread_run variable to 0, and after that libusb_handle_events() needs + * to return control to event_thread_func. But unless some event happens, + * libusb_handle_events() will not return. + * + * There are 2 different ways of dealing with this, depending on if your + * application uses libusb' \ref libusb_hotplug "hotplug" support or not. + * + * Applications which do not use hotplug support, should not start the event + * thread until after their first call to libusb_open(), and should stop the + * thread when closing the last open device as follows: +\code +void my_close_handle(libusb_device_handle *dev_handle) +{ + if (open_devs == 1) + event_thread_run = 0; + + libusb_close(dev_handle); // This wakes up libusb_handle_events() + + if (open_devs == 1) + pthread_join(event_thread); + + open_devs--; +} +\endcode + * + * Applications using hotplug support should start the thread at program init, + * after having successfully called libusb_hotplug_register_callback(), and + * should stop the thread at program exit as follows: +\code +void my_libusb_exit(void) +{ + event_thread_run = 0; + libusb_hotplug_deregister_callback(ctx, hotplug_cb_handle); // This wakes up libusb_handle_events() + pthread_join(event_thread); + libusb_exit(ctx); +} +\endcode + */ + +/** + * @defgroup libusb_poll Polling and timing + * + * This page documents libusb's functions for polling events and timing. + * These functions are only necessary for users of the + * \ref libusb_asyncio "asynchronous API". If you are only using the simpler + * \ref libusb_syncio "synchronous API" then you do not need to ever call these + * functions. + * + * The justification for the functionality described here has already been + * discussed in the \ref asyncevent "event handling" section of the + * asynchronous API documentation. In summary, libusb does not create internal + * threads for event processing and hence relies on your application calling + * into libusb at certain points in time so that pending events can be handled. + * + * Your main loop is probably already calling poll() or select() or a + * variant on a set of file descriptors for other event sources (e.g. keyboard + * button presses, mouse movements, network sockets, etc). You then add + * libusb's file descriptors to your poll()/select() calls, and when activity + * is detected on such descriptors you know it is time to call + * libusb_handle_events(). + * + * There is one final event handling complication. libusb supports + * asynchronous transfers which time out after a specified time period. + * + * On some platforms a timerfd is used, so the timeout handling is just another + * fd, on other platforms this requires that libusb is called into at or after + * the timeout to handle it. So, in addition to considering libusb's file + * descriptors in your main event loop, you must also consider that libusb + * sometimes needs to be called into at fixed points in time even when there + * is no file descriptor activity, see \ref polltime details. + * + * In order to know precisely when libusb needs to be called into, libusb + * offers you a set of pollable file descriptors and information about when + * the next timeout expires. + * + * If you are using the asynchronous I/O API, you must take one of the two + * following options, otherwise your I/O will not complete. + * + * \section pollsimple The simple option + * + * If your application revolves solely around libusb and does not need to + * handle other event sources, you can have a program structure as follows: +\code +// initialize libusb +// find and open device +// maybe fire off some initial async I/O + +while (user_has_not_requested_exit) + libusb_handle_events(ctx); + +// clean up and exit +\endcode + * + * With such a simple main loop, you do not have to worry about managing + * sets of file descriptors or handling timeouts. libusb_handle_events() will + * handle those details internally. + * + * \section libusb_pollmain The more advanced option + * + * \note This functionality is currently only available on Unix-like platforms. + * On Windows, libusb_get_pollfds() simply returns NULL. Applications which + * want to support Windows are advised to use an \ref eventthread + * "event handling thread" instead. + * + * In more advanced applications, you will already have a main loop which + * is monitoring other event sources: network sockets, X11 events, mouse + * movements, etc. Through exposing a set of file descriptors, libusb is + * designed to cleanly integrate into such main loops. + * + * In addition to polling file descriptors for the other event sources, you + * take a set of file descriptors from libusb and monitor those too. When you + * detect activity on libusb's file descriptors, you call + * libusb_handle_events_timeout() in non-blocking mode. + * + * What's more, libusb may also need to handle events at specific moments in + * time. No file descriptor activity is generated at these times, so your + * own application needs to be continually aware of when the next one of these + * moments occurs (through calling libusb_get_next_timeout()), and then it + * needs to call libusb_handle_events_timeout() in non-blocking mode when + * these moments occur. This means that you need to adjust your + * poll()/select() timeout accordingly. + * + * libusb provides you with a set of file descriptors to poll and expects you + * to poll all of them, treating them as a single entity. The meaning of each + * file descriptor in the set is an internal implementation detail, + * platform-dependent and may vary from release to release. Don't try and + * interpret the meaning of the file descriptors, just do as libusb indicates, + * polling all of them at once. + * + * In pseudo-code, you want something that looks like: +\code +// initialise libusb + +libusb_get_pollfds(ctx) +while (user has not requested application exit) { + libusb_get_next_timeout(ctx); + poll(on libusb file descriptors plus any other event sources of interest, + using a timeout no larger than the value libusb just suggested) + if (poll() indicated activity on libusb file descriptors) + libusb_handle_events_timeout(ctx, &zero_tv); + if (time has elapsed to or beyond the libusb timeout) + libusb_handle_events_timeout(ctx, &zero_tv); + // handle events from other sources here +} + +// clean up and exit +\endcode + * + * \subsection polltime Notes on time-based events + * + * The above complication with having to track time and call into libusb at + * specific moments is a bit of a headache. For maximum compatibility, you do + * need to write your main loop as above, but you may decide that you can + * restrict the supported platforms of your application and get away with + * a more simplistic scheme. + * + * These time-based event complications are \b not required on the following + * platforms: + * - Darwin + * - Linux, provided that the following version requirements are satisfied: + * - Linux v2.6.27 or newer, compiled with timerfd support + * - glibc v2.9 or newer + * - libusb v1.0.5 or newer + * + * Under these configurations, libusb_get_next_timeout() will \em always return + * 0, so your main loop can be simplified to: +\code +// initialise libusb + +libusb_get_pollfds(ctx) +while (user has not requested application exit) { + poll(on libusb file descriptors plus any other event sources of interest, + using any timeout that you like) + if (poll() indicated activity on libusb file descriptors) + libusb_handle_events_timeout(ctx, &zero_tv); + // handle events from other sources here +} + +// clean up and exit +\endcode + * + * Do remember that if you simplify your main loop to the above, you will + * lose compatibility with some platforms (including legacy Linux platforms, + * and any future platforms supported by libusb which may have time-based + * event requirements). The resultant problems will likely appear as + * strange bugs in your application. + * + * You can use the libusb_pollfds_handle_timeouts() function to do a runtime + * check to see if it is safe to ignore the time-based event complications. + * If your application has taken the shortcut of ignoring libusb's next timeout + * in your main loop, then you are advised to check the return value of + * libusb_pollfds_handle_timeouts() during application startup, and to abort + * if the platform does suffer from these timing complications. + * + * \subsection fdsetchange Changes in the file descriptor set + * + * The set of file descriptors that libusb uses as event sources may change + * during the life of your application. Rather than having to repeatedly + * call libusb_get_pollfds(), you can set up notification functions for when + * the file descriptor set changes using libusb_set_pollfd_notifiers(). + * + * \subsection mtissues Multi-threaded considerations + * + * Unfortunately, the situation is complicated further when multiple threads + * come into play. If two threads are monitoring the same file descriptors, + * the fact that only one thread will be woken up when an event occurs causes + * some headaches. + * + * The events lock, event waiters lock, and libusb_handle_events_locked() + * entities are added to solve these problems. You do not need to be concerned + * with these entities otherwise. + * + * See the extra documentation: \ref libusb_mtasync + */ + +/** \page libusb_mtasync Multi-threaded applications and asynchronous I/O + * + * libusb is a thread-safe library, but extra considerations must be applied + * to applications which interact with libusb from multiple threads. + * + * The underlying issue that must be addressed is that all libusb I/O + * revolves around monitoring file descriptors through the poll()/select() + * system calls. This is directly exposed at the + * \ref libusb_asyncio "asynchronous interface" but it is important to note that the + * \ref libusb_syncio "synchronous interface" is implemented on top of the + * asynchonrous interface, therefore the same considerations apply. + * + * The issue is that if two or more threads are concurrently calling poll() + * or select() on libusb's file descriptors then only one of those threads + * will be woken up when an event arrives. The others will be completely + * oblivious that anything has happened. + * + * Consider the following pseudo-code, which submits an asynchronous transfer + * then waits for its completion. This style is one way you could implement a + * synchronous interface on top of the asynchronous interface (and libusb + * does something similar, albeit more advanced due to the complications + * explained on this page). + * +\code +void cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; +} + +void myfunc() { + struct libusb_transfer *transfer; + unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2))); + int completed = 0; + + transfer = libusb_alloc_transfer(0); + libusb_fill_control_setup(buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x04, 0x01, 0, 0); + libusb_fill_control_transfer(transfer, dev, buffer, cb, &completed, 1000); + libusb_submit_transfer(transfer); + + while (!completed) { + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_timeout(ctx, &zero_tv); + } + printf("completed!"); + // other code here +} +\endcode + * + * Here we are serializing completion of an asynchronous event + * against a condition - the condition being completion of a specific transfer. + * The poll() loop has a long timeout to minimize CPU usage during situations + * when nothing is happening (it could reasonably be unlimited). + * + * If this is the only thread that is polling libusb's file descriptors, there + * is no problem: there is no danger that another thread will swallow up the + * event that we are interested in. On the other hand, if there is another + * thread polling the same descriptors, there is a chance that it will receive + * the event that we were interested in. In this situation, myfunc() + * will only realise that the transfer has completed on the next iteration of + * the loop, up to 120 seconds later. Clearly a two-minute delay is + * undesirable, and don't even think about using short timeouts to circumvent + * this issue! + * + * The solution here is to ensure that no two threads are ever polling the + * file descriptors at the same time. A naive implementation of this would + * impact the capabilities of the library, so libusb offers the scheme + * documented below to ensure no loss of functionality. + * + * Before we go any further, it is worth mentioning that all libusb-wrapped + * event handling procedures fully adhere to the scheme documented below. + * This includes libusb_handle_events() and its variants, and all the + * synchronous I/O functions - libusb hides this headache from you. + * + * \section Using libusb_handle_events() from multiple threads + * + * Even when only using libusb_handle_events() and synchronous I/O functions, + * you can still have a race condition. You might be tempted to solve the + * above with libusb_handle_events() like so: + * +\code + libusb_submit_transfer(transfer); + + while (!completed) { + libusb_handle_events(ctx); + } + printf("completed!"); +\endcode + * + * This however has a race between the checking of completed and + * libusb_handle_events() acquiring the events lock, so another thread + * could have completed the transfer, resulting in this thread hanging + * until either a timeout or another event occurs. See also commit + * 6696512aade99bb15d6792af90ae329af270eba6 which fixes this in the + * synchronous API implementation of libusb. + * + * Fixing this race requires checking the variable completed only after + * taking the event lock, which defeats the concept of just calling + * libusb_handle_events() without worrying about locking. This is why + * libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed() + * and libusb_handle_events_completed() functions, which handles doing the + * completion check for you after they have acquired the lock: + * +\code + libusb_submit_transfer(transfer); + + while (!completed) { + libusb_handle_events_completed(ctx, &completed); + } + printf("completed!"); +\endcode + * + * This nicely fixes the race in our example. Note that if all you want to + * do is submit a single transfer and wait for its completion, then using + * one of the synchronous I/O functions is much easier. + * + * \section eventlock The events lock + * + * The problem is when we consider the fact that libusb exposes file + * descriptors to allow for you to integrate asynchronous USB I/O into + * existing main loops, effectively allowing you to do some work behind + * libusb's back. If you do take libusb's file descriptors and pass them to + * poll()/select() yourself, you need to be aware of the associated issues. + * + * The first concept to be introduced is the events lock. The events lock + * is used to serialize threads that want to handle events, such that only + * one thread is handling events at any one time. + * + * You must take the events lock before polling libusb file descriptors, + * using libusb_lock_events(). You must release the lock as soon as you have + * aborted your poll()/select() loop, using libusb_unlock_events(). + * + * \section threadwait Letting other threads do the work for you + * + * Although the events lock is a critical part of the solution, it is not + * enough on it's own. You might wonder if the following is sufficient... +\code + libusb_lock_events(ctx); + while (!completed) { + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_timeout(ctx, &zero_tv); + } + libusb_unlock_events(ctx); +\endcode + * ...and the answer is that it is not. This is because the transfer in the + * code shown above may take a long time (say 30 seconds) to complete, and + * the lock is not released until the transfer is completed. + * + * Another thread with similar code that wants to do event handling may be + * working with a transfer that completes after a few milliseconds. Despite + * having such a quick completion time, the other thread cannot check that + * status of its transfer until the code above has finished (30 seconds later) + * due to contention on the lock. + * + * To solve this, libusb offers you a mechanism to determine when another + * thread is handling events. It also offers a mechanism to block your thread + * until the event handling thread has completed an event (and this mechanism + * does not involve polling of file descriptors). + * + * After determining that another thread is currently handling events, you + * obtain the event waiters lock using libusb_lock_event_waiters(). + * You then re-check that some other thread is still handling events, and if + * so, you call libusb_wait_for_event(). + * + * libusb_wait_for_event() puts your application to sleep until an event + * occurs, or until a thread releases the events lock. When either of these + * things happen, your thread is woken up, and should re-check the condition + * it was waiting on. It should also re-check that another thread is handling + * events, and if not, it should start handling events itself. + * + * This looks like the following, as pseudo-code: +\code +retry: +if (libusb_try_lock_events(ctx) == 0) { + // we obtained the event lock: do our own event handling + while (!completed) { + if (!libusb_event_handling_ok(ctx)) { + libusb_unlock_events(ctx); + goto retry; + } + poll(libusb file descriptors, 120*1000); + if (poll indicates activity) + libusb_handle_events_locked(ctx, 0); + } + libusb_unlock_events(ctx); +} else { + // another thread is doing event handling. wait for it to signal us that + // an event has completed + libusb_lock_event_waiters(ctx); + + while (!completed) { + // now that we have the event waiters lock, double check that another + // thread is still handling events for us. (it may have ceased handling + // events in the time it took us to reach this point) + if (!libusb_event_handler_active(ctx)) { + // whoever was handling events is no longer doing so, try again + libusb_unlock_event_waiters(ctx); + goto retry; + } + + libusb_wait_for_event(ctx, NULL); + } + libusb_unlock_event_waiters(ctx); +} +printf("completed!\n"); +\endcode + * + * A naive look at the above code may suggest that this can only support + * one event waiter (hence a total of 2 competing threads, the other doing + * event handling), because the event waiter seems to have taken the event + * waiters lock while waiting for an event. However, the system does support + * multiple event waiters, because libusb_wait_for_event() actually drops + * the lock while waiting, and reaquires it before continuing. + * + * We have now implemented code which can dynamically handle situations where + * nobody is handling events (so we should do it ourselves), and it can also + * handle situations where another thread is doing event handling (so we can + * piggyback onto them). It is also equipped to handle a combination of + * the two, for example, another thread is doing event handling, but for + * whatever reason it stops doing so before our condition is met, so we take + * over the event handling. + * + * Four functions were introduced in the above pseudo-code. Their importance + * should be apparent from the code shown above. + * -# libusb_try_lock_events() is a non-blocking function which attempts + * to acquire the events lock but returns a failure code if it is contended. + * -# libusb_event_handling_ok() checks that libusb is still happy for your + * thread to be performing event handling. Sometimes, libusb needs to + * interrupt the event handler, and this is how you can check if you have + * been interrupted. If this function returns 0, the correct behaviour is + * for you to give up the event handling lock, and then to repeat the cycle. + * The following libusb_try_lock_events() will fail, so you will become an + * events waiter. For more information on this, read \ref fullstory below. + * -# libusb_handle_events_locked() is a variant of + * libusb_handle_events_timeout() that you can call while holding the + * events lock. libusb_handle_events_timeout() itself implements similar + * logic to the above, so be sure not to call it when you are + * "working behind libusb's back", as is the case here. + * -# libusb_event_handler_active() determines if someone is currently + * holding the events lock + * + * You might be wondering why there is no function to wake up all threads + * blocked on libusb_wait_for_event(). This is because libusb can do this + * internally: it will wake up all such threads when someone calls + * libusb_unlock_events() or when a transfer completes (at the point after its + * callback has returned). + * + * \subsection fullstory The full story + * + * The above explanation should be enough to get you going, but if you're + * really thinking through the issues then you may be left with some more + * questions regarding libusb's internals. If you're curious, read on, and if + * not, skip to the next section to avoid confusing yourself! + * + * The immediate question that may spring to mind is: what if one thread + * modifies the set of file descriptors that need to be polled while another + * thread is doing event handling? + * + * There are 2 situations in which this may happen. + * -# libusb_open() will add another file descriptor to the poll set, + * therefore it is desirable to interrupt the event handler so that it + * restarts, picking up the new descriptor. + * -# libusb_close() will remove a file descriptor from the poll set. There + * are all kinds of race conditions that could arise here, so it is + * important that nobody is doing event handling at this time. + * + * libusb handles these issues internally, so application developers do not + * have to stop their event handlers while opening/closing devices. Here's how + * it works, focusing on the libusb_close() situation first: + * + * -# During initialization, libusb opens an internal pipe, and it adds the read + * end of this pipe to the set of file descriptors to be polled. + * -# During libusb_close(), libusb writes some dummy data on this event pipe. + * This immediately interrupts the event handler. libusb also records + * internally that it is trying to interrupt event handlers for this + * high-priority event. + * -# At this point, some of the functions described above start behaving + * differently: + * - libusb_event_handling_ok() starts returning 1, indicating that it is NOT + * OK for event handling to continue. + * - libusb_try_lock_events() starts returning 1, indicating that another + * thread holds the event handling lock, even if the lock is uncontended. + * - libusb_event_handler_active() starts returning 1, indicating that + * another thread is doing event handling, even if that is not true. + * -# The above changes in behaviour result in the event handler stopping and + * giving up the events lock very quickly, giving the high-priority + * libusb_close() operation a "free ride" to acquire the events lock. All + * threads that are competing to do event handling become event waiters. + * -# With the events lock held inside libusb_close(), libusb can safely remove + * a file descriptor from the poll set, in the safety of knowledge that + * nobody is polling those descriptors or trying to access the poll set. + * -# After obtaining the events lock, the close operation completes very + * quickly (usually a matter of milliseconds) and then immediately releases + * the events lock. + * -# At the same time, the behaviour of libusb_event_handling_ok() and friends + * reverts to the original, documented behaviour. + * -# The release of the events lock causes the threads that are waiting for + * events to be woken up and to start competing to become event handlers + * again. One of them will succeed; it will then re-obtain the list of poll + * descriptors, and USB I/O will then continue as normal. + * + * libusb_open() is similar, and is actually a more simplistic case. Upon a + * call to libusb_open(): + * + * -# The device is opened and a file descriptor is added to the poll set. + * -# libusb sends some dummy data on the event pipe, and records that it + * is trying to modify the poll descriptor set. + * -# The event handler is interrupted, and the same behaviour change as for + * libusb_close() takes effect, causing all event handling threads to become + * event waiters. + * -# The libusb_open() implementation takes its free ride to the events lock. + * -# Happy that it has successfully paused the events handler, libusb_open() + * releases the events lock. + * -# The event waiter threads are all woken up and compete to become event + * handlers again. The one that succeeds will obtain the list of poll + * descriptors again, which will include the addition of the new device. + * + * \subsection concl Closing remarks + * + * The above may seem a little complicated, but hopefully I have made it clear + * why such complications are necessary. Also, do not forget that this only + * applies to applications that take libusb's file descriptors and integrate + * them into their own polling loops. + * + * You may decide that it is OK for your multi-threaded application to ignore + * some of the rules and locks detailed above, because you don't think that + * two threads can ever be polling the descriptors at the same time. If that + * is the case, then that's good news for you because you don't have to worry. + * But be careful here; remember that the synchronous I/O functions do event + * handling internally. If you have one thread doing event handling in a loop + * (without implementing the rules and locking semantics documented above) + * and another trying to send a synchronous USB transfer, you will end up with + * two threads monitoring the same descriptors, and the above-described + * undesirable behaviour occurring. The solution is for your polling thread to + * play by the rules; the synchronous I/O functions do so, and this will result + * in them getting along in perfect harmony. + * + * If you do have a dedicated thread doing event handling, it is perfectly + * legal for it to take the event handling lock for long periods of time. Any + * synchronous I/O functions you call from other threads will transparently + * fall back to the "event waiters" mechanism detailed above. The only + * consideration that your event handling thread must apply is the one related + * to libusb_event_handling_ok(): you must call this before every poll(), and + * give up the events lock if instructed. + */ + +int usbi_io_init(struct libusb_context *ctx) +{ + int r; + + usbi_mutex_init(&ctx->flying_transfers_lock); + usbi_mutex_init(&ctx->events_lock); + usbi_mutex_init(&ctx->event_waiters_lock); + usbi_cond_init(&ctx->event_waiters_cond); + usbi_mutex_init(&ctx->event_data_lock); + usbi_tls_key_create(&ctx->event_handling_key); + list_init(&ctx->flying_transfers); + list_init(&ctx->ipollfds); + list_init(&ctx->hotplug_msgs); + list_init(&ctx->completed_transfers); + + /* FIXME should use an eventfd on kernels that support it */ + r = usbi_pipe(ctx->event_pipe); + if (r < 0) { + r = LIBUSB_ERROR_OTHER; + goto err; + } + + r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN); + if (r < 0) + goto err_close_pipe; + +#ifdef USBI_TIMERFD_AVAILABLE + ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(), + TFD_NONBLOCK); + if (ctx->timerfd >= 0) { + usbi_dbg("using timerfd for timeouts"); + r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN); + if (r < 0) + goto err_close_timerfd; + } else { + usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno); + ctx->timerfd = -1; + } +#endif + + return 0; + +#ifdef USBI_TIMERFD_AVAILABLE +err_close_timerfd: + close(ctx->timerfd); + usbi_remove_pollfd(ctx, ctx->event_pipe[0]); +#endif +err_close_pipe: + usbi_close(ctx->event_pipe[0]); + usbi_close(ctx->event_pipe[1]); +err: + usbi_mutex_destroy(&ctx->flying_transfers_lock); + usbi_mutex_destroy(&ctx->events_lock); + usbi_mutex_destroy(&ctx->event_waiters_lock); + usbi_cond_destroy(&ctx->event_waiters_cond); + usbi_mutex_destroy(&ctx->event_data_lock); + usbi_tls_key_delete(ctx->event_handling_key); + return r; +} + +void usbi_io_exit(struct libusb_context *ctx) +{ + usbi_remove_pollfd(ctx, ctx->event_pipe[0]); + usbi_close(ctx->event_pipe[0]); + usbi_close(ctx->event_pipe[1]); +#ifdef USBI_TIMERFD_AVAILABLE + if (usbi_using_timerfd(ctx)) { + usbi_remove_pollfd(ctx, ctx->timerfd); + close(ctx->timerfd); + } +#endif + usbi_mutex_destroy(&ctx->flying_transfers_lock); + usbi_mutex_destroy(&ctx->events_lock); + usbi_mutex_destroy(&ctx->event_waiters_lock); + usbi_cond_destroy(&ctx->event_waiters_cond); + usbi_mutex_destroy(&ctx->event_data_lock); + usbi_tls_key_delete(ctx->event_handling_key); + if (ctx->pollfds) + free(ctx->pollfds); +} + +static int calculate_timeout(struct usbi_transfer *transfer) +{ + int r; + struct timespec current_time; + unsigned int timeout = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout; + + if (!timeout) + return 0; + + r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time); + if (r < 0) { + usbi_err(ITRANSFER_CTX(transfer), + "failed to read monotonic clock, errno=%d", errno); + return r; + } + + current_time.tv_sec += timeout / 1000; + current_time.tv_nsec += (timeout % 1000) * 1000000; + + while (current_time.tv_nsec >= 1000000000) { + current_time.tv_nsec -= 1000000000; + current_time.tv_sec++; + } + + TIMESPEC_TO_TIMEVAL(&transfer->timeout, ¤t_time); + return 0; +} + +/** \ingroup libusb_asyncio + * Allocate a libusb transfer with a specified number of isochronous packet + * descriptors. The returned transfer is pre-initialized for you. When the new + * transfer is no longer needed, it should be freed with + * libusb_free_transfer(). + * + * Transfers intended for non-isochronous endpoints (e.g. control, bulk, + * interrupt) should specify an iso_packets count of zero. + * + * For transfers intended for isochronous endpoints, specify an appropriate + * number of packet descriptors to be allocated as part of the transfer. + * The returned transfer is not specially initialized for isochronous I/O; + * you are still required to set the + * \ref libusb_transfer::num_iso_packets "num_iso_packets" and + * \ref libusb_transfer::type "type" fields accordingly. + * + * It is safe to allocate a transfer with some isochronous packets and then + * use it on a non-isochronous endpoint. If you do this, ensure that at time + * of submission, num_iso_packets is 0 and that type is set appropriately. + * + * \param iso_packets number of isochronous packet descriptors to allocate + * \returns a newly allocated transfer, or NULL on error + */ +DEFAULT_VISIBILITY +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( + int iso_packets) +{ + struct libusb_transfer *transfer; + size_t os_alloc_size = usbi_backend->transfer_priv_size; + size_t alloc_size = sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets) + + os_alloc_size; + struct usbi_transfer *itransfer = calloc(1, alloc_size); + if (!itransfer) + return NULL; + + itransfer->num_iso_packets = iso_packets; + usbi_mutex_init(&itransfer->lock); + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg("transfer %p", transfer); + return transfer; +} + +/** \ingroup libusb_asyncio + * Free a transfer structure. This should be called for all transfers + * allocated with libusb_alloc_transfer(). + * + * If the \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER + * "LIBUSB_TRANSFER_FREE_BUFFER" flag is set and the transfer buffer is + * non-NULL, this function will also free the transfer buffer using the + * standard system memory allocator (e.g. free()). + * + * It is legal to call this function with a NULL transfer. In this case, + * the function will simply return safely. + * + * It is not legal to free an active transfer (one which has been submitted + * and has not yet completed). + * + * \param transfer the transfer to free + */ +void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer; + if (!transfer) + return; + + usbi_dbg("transfer %p", transfer); + if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer) + free(transfer->buffer); + + itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + usbi_mutex_destroy(&itransfer->lock); + free(itransfer); +} + +#ifdef USBI_TIMERFD_AVAILABLE +static int disarm_timerfd(struct libusb_context *ctx) +{ + const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } }; + int r; + + usbi_dbg(""); + r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL); + if (r < 0) + return LIBUSB_ERROR_OTHER; + else + return 0; +} + +/* iterates through the flying transfers, and rearms the timerfd based on the + * next upcoming timeout. + * must be called with flying_list locked. + * returns 0 on success or a LIBUSB_ERROR code on failure. + */ +static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) +{ + struct usbi_transfer *transfer; + + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + struct timeval *cur_tv = &transfer->timeout; + + /* if we've reached transfers of infinite timeout, then we have no + * arming to do */ + if (!timerisset(cur_tv)) + goto disarm; + + /* act on first transfer that has not already been handled */ + if (!(transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { + int r; + const struct itimerspec it = { {0, 0}, + { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } }; + usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout); + r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL); + if (r < 0) + return LIBUSB_ERROR_OTHER; + return 0; + } + } + +disarm: + return disarm_timerfd(ctx); +} +#else +static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) +{ + UNUSED(ctx); + return 0; +} +#endif + +/* add a transfer to the (timeout-sorted) active transfers list. + * This function will return non 0 if fails to update the timer, + * in which case the transfer is *not* on the flying_transfers list. */ +static int add_to_flying_list(struct usbi_transfer *transfer) +{ + struct usbi_transfer *cur; + struct timeval *timeout = &transfer->timeout; + struct libusb_context *ctx = ITRANSFER_CTX(transfer); + int r; + int first = 1; + + r = calculate_timeout(transfer); + if (r) + return r; + + /* if we have no other flying transfers, start the list with this one */ + if (list_empty(&ctx->flying_transfers)) { + list_add(&transfer->list, &ctx->flying_transfers); + goto out; + } + + /* if we have infinite timeout, append to end of list */ + if (!timerisset(timeout)) { + list_add_tail(&transfer->list, &ctx->flying_transfers); + /* first is irrelevant in this case */ + goto out; + } + + /* otherwise, find appropriate place in list */ + list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) { + /* find first timeout that occurs after the transfer in question */ + struct timeval *cur_tv = &cur->timeout; + + if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) || + (cur_tv->tv_sec == timeout->tv_sec && + cur_tv->tv_usec > timeout->tv_usec)) { + list_add_tail(&transfer->list, &cur->list); + goto out; + } + first = 0; + } + /* first is 0 at this stage (list not empty) */ + + /* otherwise we need to be inserted at the end */ + list_add_tail(&transfer->list, &ctx->flying_transfers); +out: +#ifdef USBI_TIMERFD_AVAILABLE + if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) { + /* if this transfer has the lowest timeout of all active transfers, + * rearm the timerfd with this transfer's timeout */ + const struct itimerspec it = { {0, 0}, + { timeout->tv_sec, timeout->tv_usec * 1000 } }; + usbi_dbg("arm timerfd for timeout in %dms (first in line)", + USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout); + r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL); + if (r < 0) { + usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno); + r = LIBUSB_ERROR_OTHER; + } + } +#else + UNUSED(first); +#endif + + if (r) + list_del(&transfer->list); + + return r; +} + +/* remove a transfer from the active transfers list. + * This function will *always* remove the transfer from the + * flying_transfers list. It will return a LIBUSB_ERROR code + * if it fails to update the timer for the next timeout. */ +static int remove_from_flying_list(struct usbi_transfer *transfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(transfer); + int rearm_timerfd; + int r = 0; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + rearm_timerfd = (timerisset(&transfer->timeout) && + list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == transfer); + list_del(&transfer->list); + if (usbi_using_timerfd(ctx) && rearm_timerfd) + r = arm_timerfd_for_next_timeout(ctx); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + return r; +} + +/** \ingroup libusb_asyncio + * Submit a transfer. This function will fire off the USB transfer and then + * return immediately. + * + * \param transfer the transfer to submit + * \returns 0 on success + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted. + * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported + * by the operating system. + * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + struct libusb_context *ctx = TRANSFER_CTX(transfer); + int r; + + usbi_dbg("transfer %p", transfer); + + /* + * Important note on locking, this function takes / releases locks + * in the following order: + * take flying_transfers_lock + * take itransfer->lock + * clear transfer + * add to flying_transfers list + * release flying_transfers_lock + * submit transfer + * release itransfer->lock + * if submit failed: + * take flying_transfers_lock + * remove from flying_transfers list + * release flying_transfers_lock + * + * Note that it takes locks in the order a-b and then releases them + * in the same order a-b. This is somewhat unusual but not wrong, + * release order is not important as long as *all* locks are released + * before re-acquiring any locks. + * + * This means that the ordering of first releasing itransfer->lock + * and then re-acquiring the flying_transfers_list on error is + * important and must not be changed! + * + * This is done this way because when we take both locks we must always + * take flying_transfers_lock first to avoid ab-ba style deadlocks with + * the timeout handling and usbi_handle_disconnect paths. + * + * And we cannot release itransfer->lock before the submission is + * complete otherwise timeout handling for transfers with short + * timeouts may run before submission. + */ + usbi_mutex_lock(&ctx->flying_transfers_lock); + usbi_mutex_lock(&itransfer->lock); + if (itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&itransfer->lock); + return LIBUSB_ERROR_BUSY; + } + itransfer->transferred = 0; + itransfer->state_flags = 0; + itransfer->timeout_flags = 0; + r = add_to_flying_list(itransfer); + if (r) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&itransfer->lock); + return r; + } + /* + * We must release the flying transfers lock here, because with + * some backends the submit_transfer method is synchroneous. + */ + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + r = usbi_backend->submit_transfer(itransfer); + if (r == LIBUSB_SUCCESS) { + itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; + /* keep a reference to this device */ + libusb_ref_device(transfer->dev_handle->dev); + } + usbi_mutex_unlock(&itransfer->lock); + + if (r != LIBUSB_SUCCESS) + remove_from_flying_list(itransfer); + + return r; +} + +/** \ingroup libusb_asyncio + * Asynchronously cancel a previously submitted transfer. + * This function returns immediately, but this does not indicate cancellation + * is complete. Your callback function will be invoked at some later time + * with a transfer status of + * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED + * "LIBUSB_TRANSFER_CANCELLED." + * + * \param transfer the transfer to cancel + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, + * already complete, or already cancelled. + * \returns a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + int r; + + usbi_dbg("transfer %p", transfer ); + usbi_mutex_lock(&itransfer->lock); + if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) + || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + r = usbi_backend->cancel_transfer(itransfer); + if (r < 0) { + if (r != LIBUSB_ERROR_NOT_FOUND && + r != LIBUSB_ERROR_NO_DEVICE) + usbi_err(TRANSFER_CTX(transfer), + "cancel transfer failed error %d", r); + else + usbi_dbg("cancel transfer failed error %d", r); + + if (r == LIBUSB_ERROR_NO_DEVICE) + itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED; + } + + itransfer->state_flags |= USBI_TRANSFER_CANCELLING; + +out: + usbi_mutex_unlock(&itransfer->lock); + return r; +} + +/** \ingroup libusb_asyncio + * Set a transfers bulk stream id. Note users are advised to use + * libusb_fill_bulk_stream_transfer() instead of calling this function + * directly. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to set the stream id for + * \param stream_id the stream id to set + * \see libusb_alloc_streams() + */ +void API_EXPORTED libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + + itransfer->stream_id = stream_id; +} + +/** \ingroup libusb_asyncio + * Get a transfers bulk stream id. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to get the stream id for + * \returns the stream id for the transfer + */ +uint32_t API_EXPORTED libusb_transfer_get_stream_id( + struct libusb_transfer *transfer) +{ + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + + return itransfer->stream_id; +} + +/* Handle completion of a transfer (completion might be an error condition). + * This will invoke the user-supplied callback function, which may end up + * freeing the transfer. Therefore you cannot use the transfer structure + * after calling this function, and you should free all backend-specific + * data before calling it. + * Do not call this function with the usbi_transfer lock held. User-specified + * callback functions may attempt to directly resubmit the transfer, which + * will attempt to take the lock. */ +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_device_handle *dev_handle = transfer->dev_handle; + uint8_t flags; + int r; + + r = remove_from_flying_list(itransfer); + if (r < 0) + usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno); + + usbi_mutex_lock(&itransfer->lock); + itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT; + usbi_mutex_unlock(&itransfer->lock); + + if (status == LIBUSB_TRANSFER_COMPLETED + && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { + int rqlen = transfer->length; + if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) + rqlen -= LIBUSB_CONTROL_SETUP_SIZE; + if (rqlen != itransfer->transferred) { + usbi_dbg("interpreting short transfer as error"); + status = LIBUSB_TRANSFER_ERROR; + } + } + + flags = transfer->flags; + transfer->status = status; + transfer->actual_length = itransfer->transferred; + usbi_dbg("transfer %p has callback %p", transfer, transfer->callback); + if (transfer->callback) + transfer->callback(transfer); + /* transfer might have been freed by the above call, do not use from + * this point. */ + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(transfer); + libusb_unref_device(dev_handle->dev); + return r; +} + +/* Similar to usbi_handle_transfer_completion() but exclusively for transfers + * that were asynchronously cancelled. The same concerns w.r.t. freeing of + * transfers exist here. + * Do not call this function with the usbi_transfer lock held. User-specified + * callback functions may attempt to directly resubmit the transfer, which + * will attempt to take the lock. */ +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(transfer); + uint8_t timed_out; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT; + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + /* if the URB was cancelled due to timeout, report timeout to the user */ + if (timed_out) { + usbi_dbg("detected timeout cancellation"); + return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT); + } + + /* otherwise its a normal async cancel */ + return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED); +} + +/* Add a completed transfer to the completed_transfers list of the + * context and signal the event. The backend's handle_transfer_completion() + * function will be called the next time an event handler runs. */ +void usbi_signal_transfer_completion(struct usbi_transfer *transfer) +{ + struct libusb_context *ctx = ITRANSFER_CTX(transfer); + int pending_events; + + usbi_mutex_lock(&ctx->event_data_lock); + pending_events = usbi_pending_events(ctx); + list_add_tail(&transfer->completed_list, &ctx->completed_transfers); + if (!pending_events) + usbi_signal_event(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); +} + +/** \ingroup libusb_poll + * Attempt to acquire the event handling lock. This lock is used to ensure that + * only one thread is monitoring libusb event sources at any one time. + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * While holding this lock, you are trusted to actually be handling events. + * If you are no longer handling events, you must call libusb_unlock_events() + * as soon as possible. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 if the lock was obtained successfully + * \returns 1 if the lock was not obtained (i.e. another thread holds the lock) + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) +{ + int r; + unsigned int ru; + USBI_GET_CONTEXT(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * start event handling */ + usbi_mutex_lock(&ctx->event_data_lock); + ru = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (ru) { + usbi_dbg("someone else is closing a device"); + return 1; + } + + r = usbi_mutex_trylock(&ctx->events_lock); + if (r) + return 1; + + ctx->event_handler_active = 1; + return 0; +} + +/** \ingroup libusb_poll + * Acquire the event handling lock, blocking until successful acquisition if + * it is contended. This lock is used to ensure that only one thread is + * monitoring libusb event sources at any one time. + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * While holding this lock, you are trusted to actually be handling events. + * If you are no longer handling events, you must call libusb_unlock_events() + * as soon as possible. + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_lock_events(libusb_context *ctx) +{ + USBI_GET_CONTEXT(ctx); + usbi_mutex_lock(&ctx->events_lock); + ctx->event_handler_active = 1; +} + +/** \ingroup libusb_poll + * Release the lock previously acquired with libusb_try_lock_events() or + * libusb_lock_events(). Releasing this lock will wake up any threads blocked + * on libusb_wait_for_event(). + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_unlock_events(libusb_context *ctx) +{ + USBI_GET_CONTEXT(ctx); + ctx->event_handler_active = 0; + usbi_mutex_unlock(&ctx->events_lock); + + /* FIXME: perhaps we should be a bit more efficient by not broadcasting + * the availability of the events lock when we are modifying pollfds + * (check ctx->device_close)? */ + usbi_mutex_lock(&ctx->event_waiters_lock); + usbi_cond_broadcast(&ctx->event_waiters_cond); + usbi_mutex_unlock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Determine if it is still OK for this thread to be doing event handling. + * + * Sometimes, libusb needs to temporarily pause all event handlers, and this + * is the function you should use before polling file descriptors to see if + * this is the case. + * + * If this function instructs your thread to give up the events lock, you + * should just continue the usual logic that is documented in \ref libusb_mtasync. + * On the next iteration, your thread will fail to obtain the events lock, + * and will hence become an event waiter. + * + * This function should be called while the events lock is held: you don't + * need to worry about the results of this function if your thread is not + * the current event handler. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 1 if event handling can start or continue + * \returns 0 if this thread must give up the events lock + * \ref fullstory "Multi-threaded I/O: the full story" + */ +int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) +{ + unsigned int r; + USBI_GET_CONTEXT(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * continue event handling */ + usbi_mutex_lock(&ctx->event_data_lock); + r = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (r) { + usbi_dbg("someone else is closing a device"); + return 0; + } + + return 1; +} + + +/** \ingroup libusb_poll + * Determine if an active thread is handling events (i.e. if anyone is holding + * the event handling lock). + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 1 if a thread is handling events + * \returns 0 if there are no threads currently handling events + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) +{ + unsigned int r; + USBI_GET_CONTEXT(ctx); + + /* is someone else waiting to close a device? if so, don't let this thread + * start event handling -- indicate that event handling is happening */ + usbi_mutex_lock(&ctx->event_data_lock); + r = ctx->device_close; + usbi_mutex_unlock(&ctx->event_data_lock); + if (r) { + usbi_dbg("someone else is closing a device"); + return 1; + } + + return ctx->event_handler_active; +} + +/** \ingroup libusb_poll + * Interrupt any active thread that is handling events. This is mainly useful + * for interrupting a dedicated event handling thread when an application + * wishes to call libusb_exit(). + * + * Since version 1.0.21, \ref LIBUSB_API_VERSION >= 0x01000105 + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) +{ + int pending_events; + USBI_GET_CONTEXT(ctx); + + usbi_dbg(""); + usbi_mutex_lock(&ctx->event_data_lock); + + pending_events = usbi_pending_events(ctx); + ctx->event_flags |= USBI_EVENT_USER_INTERRUPT; + if (!pending_events) + usbi_signal_event(ctx); + + usbi_mutex_unlock(&ctx->event_data_lock); +} + +/** \ingroup libusb_poll + * Acquire the event waiters lock. This lock is designed to be obtained under + * the situation where you want to be aware when events are completed, but + * some other thread is event handling so calling libusb_handle_events() is not + * allowed. + * + * You then obtain this lock, re-check that another thread is still handling + * events, then call libusb_wait_for_event(). + * + * You only need to use this lock if you are developing an application + * which calls poll() or select() on libusb's file descriptors directly, + * and may potentially be handling events from 2 threads simultaenously. + * If you stick to libusb's event handling loop functions (e.g. + * libusb_handle_events()) then you do not need to be concerned with this + * locking. + * + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx) +{ + USBI_GET_CONTEXT(ctx); + usbi_mutex_lock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Release the event waiters lock. + * \param ctx the context to operate on, or NULL for the default context + * \ref libusb_mtasync + */ +void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) +{ + USBI_GET_CONTEXT(ctx); + usbi_mutex_unlock(&ctx->event_waiters_lock); +} + +/** \ingroup libusb_poll + * Wait for another thread to signal completion of an event. Must be called + * with the event waiters lock held, see libusb_lock_event_waiters(). + * + * This function will block until any of the following conditions are met: + * -# The timeout expires + * -# A transfer completes + * -# A thread releases the event handling lock through libusb_unlock_events() + * + * Condition 1 is obvious. Condition 2 unblocks your thread after + * the callback for the transfer has completed. Condition 3 is important + * because it means that the thread that was previously handling events is no + * longer doing so, so if any events are to complete, another thread needs to + * step up and start event handling. + * + * This function releases the event waiters lock before putting your thread + * to sleep, and reacquires the lock as it is being woken up. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv maximum timeout for this blocking function. A NULL value + * indicates unlimited timeout. + * \returns 0 after a transfer completes or another thread stops event handling + * \returns 1 if the timeout expired + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) +{ + int r; + + USBI_GET_CONTEXT(ctx); + if (tv == NULL) { + usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock); + return 0; + } + + r = usbi_cond_timedwait(&ctx->event_waiters_cond, + &ctx->event_waiters_lock, tv); + + if (r < 0) + return r; + else + return (r == ETIMEDOUT); +} + +static void handle_timeout(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int r; + + itransfer->timeout_flags |= USBI_TRANSFER_TIMEOUT_HANDLED; + r = libusb_cancel_transfer(transfer); + if (r == LIBUSB_SUCCESS) + itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + else + usbi_warn(TRANSFER_CTX(transfer), + "async cancel failed %d errno=%d", r, errno); +} + +static int handle_timeouts_locked(struct libusb_context *ctx) +{ + int r; + struct timespec systime_ts; + struct timeval systime; + struct usbi_transfer *transfer; + + if (list_empty(&ctx->flying_transfers)) + return 0; + + /* get current time */ + r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts); + if (r < 0) + return r; + + TIMESPEC_TO_TIMEVAL(&systime, &systime_ts); + + /* iterate through flying transfers list, finding all transfers that + * have expired timeouts */ + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + struct timeval *cur_tv = &transfer->timeout; + + /* if we've reached transfers of infinite timeout, we're all done */ + if (!timerisset(cur_tv)) + return 0; + + /* ignore timeouts we've already handled */ + if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if transfer has non-expired timeout, nothing more to do */ + if ((cur_tv->tv_sec > systime.tv_sec) || + (cur_tv->tv_sec == systime.tv_sec && + cur_tv->tv_usec > systime.tv_usec)) + return 0; + + /* otherwise, we've got an expired timeout to handle */ + handle_timeout(transfer); + } + return 0; +} + +static int handle_timeouts(struct libusb_context *ctx) +{ + int r; + USBI_GET_CONTEXT(ctx); + usbi_mutex_lock(&ctx->flying_transfers_lock); + r = handle_timeouts_locked(ctx); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + return r; +} + +#ifdef USBI_TIMERFD_AVAILABLE +static int handle_timerfd_trigger(struct libusb_context *ctx) +{ + int r; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + + /* process the timeout that just happened */ + r = handle_timeouts_locked(ctx); + if (r < 0) + goto out; + + /* arm for next timeout*/ + r = arm_timerfd_for_next_timeout(ctx); + +out: + usbi_mutex_unlock(&ctx->flying_transfers_lock); + return r; +} +#endif + +/* do the actual event handling. assumes that no other thread is concurrently + * doing the same thing. */ +static int handle_events(struct libusb_context *ctx, struct timeval *tv) +{ + int r; + struct usbi_pollfd *ipollfd; + POLL_NFDS_TYPE nfds = 0; + POLL_NFDS_TYPE internal_nfds; + struct pollfd *fds = NULL; + int i = -1; + int timeout_ms; + int special_event; + + /* prevent attempts to recursively handle events (e.g. calling into + * libusb_handle_events() from within a hotplug or transfer callback) */ + if (usbi_handling_events(ctx)) + return LIBUSB_ERROR_BUSY; + usbi_start_event_handling(ctx); + + /* there are certain fds that libusb uses internally, currently: + * + * 1) event pipe + * 2) timerfd + * + * the backend will never need to attempt to handle events on these fds, so + * we determine how many fds are in use internally for this context and when + * handle_events() is called in the backend, the pollfd list and count will + * be adjusted to skip over these internal fds */ + if (usbi_using_timerfd(ctx)) + internal_nfds = 2; + else + internal_nfds = 1; + + /* only reallocate the poll fds when the list of poll fds has been modified + * since the last poll, otherwise reuse them to save the additional overhead */ + usbi_mutex_lock(&ctx->event_data_lock); + if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) { + usbi_dbg("poll fds modified, reallocating"); + + if (ctx->pollfds) { + free(ctx->pollfds); + ctx->pollfds = NULL; + } + + /* sanity check - it is invalid for a context to have fewer than the + * required internal fds (memory corruption?) */ + assert(ctx->pollfds_cnt >= internal_nfds); + + ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds)); + if (!ctx->pollfds) { + usbi_mutex_unlock(&ctx->event_data_lock); + r = LIBUSB_ERROR_NO_MEM; + goto done; + } + + list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) { + struct libusb_pollfd *pollfd = &ipollfd->pollfd; + i++; + ctx->pollfds[i].fd = pollfd->fd; + ctx->pollfds[i].events = pollfd->events; + } + + /* reset the flag now that we have the updated list */ + ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED; + + /* if no further pending events, clear the event pipe so that we do + * not immediately return from poll */ + if (!usbi_pending_events(ctx)) + usbi_clear_event(ctx); + } + fds = ctx->pollfds; + nfds = ctx->pollfds_cnt; + usbi_mutex_unlock(&ctx->event_data_lock); + + timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); + + /* round up to next millisecond */ + if (tv->tv_usec % 1000) + timeout_ms++; + +redo_poll: + usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms); + r = usbi_poll(fds, nfds, timeout_ms); + usbi_dbg("poll() returned %d", r); + if (r == 0) { + r = handle_timeouts(ctx); + goto done; + } + else if (r == -1 && errno == EINTR) { + r = LIBUSB_ERROR_INTERRUPTED; + goto done; + } + else if (r < 0) { + usbi_err(ctx, "poll failed %d err=%d", r, errno); + r = LIBUSB_ERROR_IO; + goto done; + } + + special_event = 0; + + /* fds[0] is always the event pipe */ + if (fds[0].revents) { + libusb_hotplug_message *message = NULL; + struct usbi_transfer *itransfer; + int ret = 0; + + usbi_dbg("caught a fish on the event pipe"); + + /* take the the event data lock while processing events */ + usbi_mutex_lock(&ctx->event_data_lock); + + /* check if someone added a new poll fd */ + if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) + usbi_dbg("someone updated the poll fds"); + + if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { + usbi_dbg("someone purposely interrupted"); + ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT; + } + + /* check if someone is closing a device */ + if (ctx->device_close) + usbi_dbg("someone is closing a device"); + + /* check for any pending hotplug messages */ + if (!list_empty(&ctx->hotplug_msgs)) { + usbi_dbg("hotplug message received"); + special_event = 1; + message = list_first_entry(&ctx->hotplug_msgs, libusb_hotplug_message, list); + list_del(&message->list); + } + + /* complete any pending transfers */ + while (ret == 0 && !list_empty(&ctx->completed_transfers)) { + itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list); + list_del(&itransfer->completed_list); + usbi_mutex_unlock(&ctx->event_data_lock); + ret = usbi_backend->handle_transfer_completion(itransfer); + if (ret) + usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret); + usbi_mutex_lock(&ctx->event_data_lock); + } + + /* if no further pending events, clear the event pipe */ + if (!usbi_pending_events(ctx)) + usbi_clear_event(ctx); + + usbi_mutex_unlock(&ctx->event_data_lock); + + /* process the hotplug message, if any */ + if (message) { + usbi_hotplug_match(ctx, message->device, message->event); + + /* the device left, dereference the device */ + if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event) + libusb_unref_device(message->device); + + free(message); + } + + if (ret) { + /* return error code */ + r = ret; + goto done; + } + + if (0 == --r) + goto handled; + } + +#ifdef USBI_TIMERFD_AVAILABLE + /* on timerfd configurations, fds[1] is the timerfd */ + if (usbi_using_timerfd(ctx) && fds[1].revents) { + /* timerfd indicates that a timeout has expired */ + int ret; + usbi_dbg("timerfd triggered"); + special_event = 1; + + ret = handle_timerfd_trigger(ctx); + if (ret < 0) { + /* return error code */ + r = ret; + goto done; + } + + if (0 == --r) + goto handled; + } +#endif + + r = usbi_backend->handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r); + if (r) + usbi_err(ctx, "backend handle_events failed with error %d", r); + +handled: + if (r == 0 && special_event) { + timeout_ms = 0; + goto redo_poll; + } + +done: + usbi_end_event_handling(ctx); + return r; +} + +/* returns the smallest of: + * 1. timeout of next URB + * 2. user-supplied timeout + * returns 1 if there is an already-expired timeout, otherwise returns 0 + * and populates out + */ +static int get_next_timeout(libusb_context *ctx, struct timeval *tv, + struct timeval *out) +{ + struct timeval timeout; + int r = libusb_get_next_timeout(ctx, &timeout); + if (r) { + /* timeout already expired? */ + if (!timerisset(&timeout)) + return 1; + + /* choose the smallest of next URB timeout or user specified timeout */ + if (timercmp(&timeout, tv, <)) + *out = timeout; + else + *out = *tv; + } else { + *out = *tv; + } + return 0; +} + +/** \ingroup libusb_poll + * Handle any pending events. + * + * libusb determines "pending events" by checking if any timeouts have expired + * and by checking the set of file descriptors for activity. + * + * If a zero timeval is passed, this function will handle any already-pending + * events and then immediately return in non-blocking style. + * + * If a non-zero timeval is passed and no events are currently pending, this + * function will block waiting for events to handle up until the specified + * timeout. If an event arrives or a signal is raised, this function will + * return early. + * + * If the parameter completed is not NULL then after obtaining the event + * handling lock this function will return immediately if the integer + * pointed to is not 0. This allows for race free waiting for the completion + * of a specific transfer. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or an all zero + * timeval struct for non-blocking mode + * \param completed pointer to completion integer to check, or NULL + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed) +{ + int r; + struct timeval poll_timeout; + + USBI_GET_CONTEXT(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); + if (r) { + /* timeout already expired */ + return handle_timeouts(ctx); + } + +retry: + if (libusb_try_lock_events(ctx) == 0) { + if (completed == NULL || !*completed) { + /* we obtained the event lock: do our own event handling */ + usbi_dbg("doing our own event handling"); + r = handle_events(ctx, &poll_timeout); + } + libusb_unlock_events(ctx); + return r; + } + + /* another thread is doing event handling. wait for thread events that + * notify event completion. */ + libusb_lock_event_waiters(ctx); + + if (completed && *completed) + goto already_done; + + if (!libusb_event_handler_active(ctx)) { + /* we hit a race: whoever was event handling earlier finished in the + * time it took us to reach this point. try the cycle again. */ + libusb_unlock_event_waiters(ctx); + usbi_dbg("event handler was active but went away, retrying"); + goto retry; + } + + usbi_dbg("another thread is doing event handling"); + r = libusb_wait_for_event(ctx, &poll_timeout); + +already_done: + libusb_unlock_event_waiters(ctx); + + if (r < 0) + return r; + else if (r == 1) + return handle_timeouts(ctx); + else + return 0; +} + +/** \ingroup libusb_poll + * Handle any pending events + * + * Like libusb_handle_events_timeout_completed(), but without the completed + * parameter, calling this function is equivalent to calling + * libusb_handle_events_timeout_completed() with a NULL completed parameter. + * + * This function is kept primarily for backwards compatibility. + * All new code should call libusb_handle_events_completed() or + * libusb_handle_events_timeout_completed() to avoid race conditions. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or an all zero + * timeval struct for non-blocking mode + * \returns 0 on success, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv) +{ + return libusb_handle_events_timeout_completed(ctx, tv, NULL); +} + +/** \ingroup libusb_poll + * Handle any pending events in blocking mode. There is currently a timeout + * hardcoded at 60 seconds but we plan to make it unlimited in future. For + * finer control over whether this function is blocking or non-blocking, or + * for control over the timeout, use libusb_handle_events_timeout_completed() + * instead. + * + * This function is kept primarily for backwards compatibility. + * All new code should call libusb_handle_events_completed() or + * libusb_handle_events_timeout_completed() to avoid race conditions. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 on success, or a LIBUSB_ERROR code on failure + */ +int API_EXPORTED libusb_handle_events(libusb_context *ctx) +{ + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + return libusb_handle_events_timeout_completed(ctx, &tv, NULL); +} + +/** \ingroup libusb_poll + * Handle any pending events in blocking mode. + * + * Like libusb_handle_events(), with the addition of a completed parameter + * to allow for race free waiting for the completion of a specific transfer. + * + * See libusb_handle_events_timeout_completed() for details on the completed + * parameter. + * + * \param ctx the context to operate on, or NULL for the default context + * \param completed pointer to completion integer to check, or NULL + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, + int *completed) +{ + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + return libusb_handle_events_timeout_completed(ctx, &tv, completed); +} + +/** \ingroup libusb_poll + * Handle any pending events by polling file descriptors, without checking if + * any other threads are already doing so. Must be called with the event lock + * held, see libusb_lock_events(). + * + * This function is designed to be called under the situation where you have + * taken the event lock and are calling poll()/select() directly on libusb's + * file descriptors (as opposed to using libusb_handle_events() or similar). + * You detect events on libusb's descriptors, so you then call this function + * with a zero timeout value (while still holding the event lock). + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv the maximum time to block waiting for events, or zero for + * non-blocking mode + * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \ref libusb_mtasync + */ +int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv) +{ + int r; + struct timeval poll_timeout; + + USBI_GET_CONTEXT(ctx); + r = get_next_timeout(ctx, tv, &poll_timeout); + if (r) { + /* timeout already expired */ + return handle_timeouts(ctx); + } + + return handle_events(ctx, &poll_timeout); +} + +/** \ingroup libusb_poll + * Determines whether your application must apply special timing considerations + * when monitoring libusb's file descriptors. + * + * This function is only useful for applications which retrieve and poll + * libusb's file descriptors in their own main loop (\ref libusb_pollmain). + * + * Ordinarily, libusb's event handler needs to be called into at specific + * moments in time (in addition to times when there is activity on the file + * descriptor set). The usual approach is to use libusb_get_next_timeout() + * to learn about when the next timeout occurs, and to adjust your + * poll()/select() timeout accordingly so that you can make a call into the + * library at that time. + * + * Some platforms supported by libusb do not come with this baggage - any + * events relevant to timing will be represented by activity on the file + * descriptor set, and libusb_get_next_timeout() will always return 0. + * This function allows you to detect whether you are running on such a + * platform. + * + * Since v1.0.5. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns 0 if you must call into libusb at times determined by + * libusb_get_next_timeout(), or 1 if all timeout events are handled internally + * or through regular activity on the file descriptors. + * \ref libusb_pollmain "Polling libusb file descriptors for event handling" + */ +int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) +{ +#if defined(USBI_TIMERFD_AVAILABLE) + USBI_GET_CONTEXT(ctx); + return usbi_using_timerfd(ctx); +#else + UNUSED(ctx); + return 0; +#endif +} + +/** \ingroup libusb_poll + * Determine the next internal timeout that libusb needs to handle. You only + * need to use this function if you are calling poll() or select() or similar + * on libusb's file descriptors yourself - you do not need to use it if you + * are calling libusb_handle_events() or a variant directly. + * + * You should call this function in your main loop in order to determine how + * long to wait for select() or poll() to return results. libusb needs to be + * called into at this timeout, so you should use it as an upper bound on + * your select() or poll() call. + * + * When the timeout has expired, call into libusb_handle_events_timeout() + * (perhaps in non-blocking mode) so that libusb can handle the timeout. + * + * This function may return 1 (success) and an all-zero timeval. If this is + * the case, it indicates that libusb has a timeout that has already expired + * so you should call libusb_handle_events_timeout() or similar immediately. + * A return code of 0 indicates that there are no pending timeouts. + * + * On some platforms, this function will always returns 0 (no pending + * timeouts). See \ref polltime. + * + * \param ctx the context to operate on, or NULL for the default context + * \param tv output location for a relative time against the current + * clock in which libusb must be called into in order to process timeout events + * \returns 0 if there are no pending timeouts, 1 if a timeout was returned, + * or LIBUSB_ERROR_OTHER on failure + */ +int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv) +{ + struct usbi_transfer *transfer; + struct timespec cur_ts; + struct timeval cur_tv; + struct timeval next_timeout = { 0, 0 }; + int r; + + USBI_GET_CONTEXT(ctx); + if (usbi_using_timerfd(ctx)) + return 0; + + usbi_mutex_lock(&ctx->flying_transfers_lock); + if (list_empty(&ctx->flying_transfers)) { + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_dbg("no URBs, no timeout!"); + return 0; + } + + /* find next transfer which hasn't already been processed as timed out */ + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + continue; + + /* if we've reached transfers of infinte timeout, we're done looking */ + if (!timerisset(&transfer->timeout)) + break; + + next_timeout = transfer->timeout; + break; + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (!timerisset(&next_timeout)) { + usbi_dbg("no URB with timeout or all handled by OS; no timeout!"); + return 0; + } + + r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts); + if (r < 0) { + usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno); + return 0; + } + TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); + + if (!timercmp(&cur_tv, &next_timeout, <)) { + usbi_dbg("first timeout already expired"); + timerclear(tv); + } else { + timersub(&next_timeout, &cur_tv, tv); + usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec); + } + + return 1; +} + +/** \ingroup libusb_poll + * Register notification functions for file descriptor additions/removals. + * These functions will be invoked for every new or removed file descriptor + * that libusb uses as an event source. + * + * To remove notifiers, pass NULL values for the function pointers. + * + * Note that file descriptors may have been added even before you register + * these notifiers (e.g. at libusb_init() time). + * + * Additionally, note that the removal notifier may be called during + * libusb_exit() (e.g. when it is closing file descriptors that were opened + * and added to the poll set at libusb_init() time). If you don't want this, + * remove the notifiers immediately before calling libusb_exit(). + * + * \param ctx the context to operate on, or NULL for the default context + * \param added_cb pointer to function for addition notifications + * \param removed_cb pointer to function for removal notifications + * \param user_data User data to be passed back to callbacks (useful for + * passing context information) + */ +void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data) +{ + USBI_GET_CONTEXT(ctx); + ctx->fd_added_cb = added_cb; + ctx->fd_removed_cb = removed_cb; + ctx->fd_cb_user_data = user_data; +} + +/* + * Interrupt the iteration of the event handling thread, so that it picks + * up the fd change. Callers of this function must hold the event_data_lock. + */ +static void usbi_fd_notification(struct libusb_context *ctx) +{ + int pending_events; + + /* Record that there is a new poll fd. + * Only signal an event if there are no prior pending events. */ + pending_events = usbi_pending_events(ctx); + ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED; + if (!pending_events) + usbi_signal_event(ctx); +} + +/* Add a file descriptor to the list of file descriptors to be monitored. + * events should be specified as a bitmask of events passed to poll(), e.g. + * POLLIN and/or POLLOUT. */ +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events) +{ + struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd)); + if (!ipollfd) + return LIBUSB_ERROR_NO_MEM; + + usbi_dbg("add fd %d events %d", fd, events); + ipollfd->pollfd.fd = fd; + ipollfd->pollfd.events = events; + usbi_mutex_lock(&ctx->event_data_lock); + list_add_tail(&ipollfd->list, &ctx->ipollfds); + ctx->pollfds_cnt++; + usbi_fd_notification(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + + if (ctx->fd_added_cb) + ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); + return 0; +} + +/* Remove a file descriptor from the list of file descriptors to be polled. */ +void usbi_remove_pollfd(struct libusb_context *ctx, int fd) +{ + struct usbi_pollfd *ipollfd; + int found = 0; + + usbi_dbg("remove fd %d", fd); + usbi_mutex_lock(&ctx->event_data_lock); + list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) + if (ipollfd->pollfd.fd == fd) { + found = 1; + break; + } + + if (!found) { + usbi_dbg("couldn't find fd %d to remove", fd); + usbi_mutex_unlock(&ctx->event_data_lock); + return; + } + + list_del(&ipollfd->list); + ctx->pollfds_cnt--; + usbi_fd_notification(ctx); + usbi_mutex_unlock(&ctx->event_data_lock); + free(ipollfd); + if (ctx->fd_removed_cb) + ctx->fd_removed_cb(fd, ctx->fd_cb_user_data); +} + +/** \ingroup libusb_poll + * Retrieve a list of file descriptors that should be polled by your main loop + * as libusb event sources. + * + * The returned list is NULL-terminated and should be freed with libusb_free_pollfds() + * when done. The actual list contents must not be touched. + * + * As file descriptors are a Unix-specific concept, this function is not + * available on Windows and will always return NULL. + * + * \param ctx the context to operate on, or NULL for the default context + * \returns a NULL-terminated list of libusb_pollfd structures + * \returns NULL on error + * \returns NULL on platforms where the functionality is not available + */ +DEFAULT_VISIBILITY +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx) +{ +#ifndef OS_WINDOWS + struct libusb_pollfd **ret = NULL; + struct usbi_pollfd *ipollfd; + size_t i = 0; + USBI_GET_CONTEXT(ctx); + + usbi_mutex_lock(&ctx->event_data_lock); + + ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *)); + if (!ret) + goto out; + + list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) + ret[i++] = (struct libusb_pollfd *) ipollfd; + ret[ctx->pollfds_cnt] = NULL; + +out: + usbi_mutex_unlock(&ctx->event_data_lock); + return (const struct libusb_pollfd **) ret; +#else + usbi_err(ctx, "external polling of libusb's internal descriptors "\ + "is not yet supported on Windows platforms"); + return NULL; +#endif +} + +/** \ingroup libusb_poll + * Free a list of libusb_pollfd structures. This should be called for all + * pollfd lists allocated with libusb_get_pollfds(). + * + * Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104 + * + * It is legal to call this function with a NULL pollfd list. In this case, + * the function will simply return safely. + * + * \param pollfds the list of libusb_pollfd structures to free + */ +void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds) +{ + if (!pollfds) + return; + + free((void *)pollfds); +} + +/* Backends may call this from handle_events to report disconnection of a + * device. This function ensures transfers get cancelled appropriately. + * Callers of this function must hold the events_lock. + */ +void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) +{ + struct usbi_transfer *cur; + struct usbi_transfer *to_cancel; + + usbi_dbg("device %d.%d", + dev_handle->dev->bus_number, dev_handle->dev->device_address); + + /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE + * status code. + * + * when we find a transfer for this device on the list, there are two + * possible scenarios: + * 1. the transfer is currently in-flight, in which case we terminate the + * transfer here + * 2. the transfer has been added to the flying transfer list by + * libusb_submit_transfer, has failed to submit and + * libusb_submit_transfer is waiting for us to release the + * flying_transfers_lock to remove it, so we ignore it + */ + + while (1) { + to_cancel = NULL; + usbi_mutex_lock(&HANDLE_CTX(dev_handle)->flying_transfers_lock); + list_for_each_entry(cur, &HANDLE_CTX(dev_handle)->flying_transfers, list, struct usbi_transfer) + if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) { + usbi_mutex_lock(&cur->lock); + if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) + to_cancel = cur; + usbi_mutex_unlock(&cur->lock); + + if (to_cancel) + break; + } + usbi_mutex_unlock(&HANDLE_CTX(dev_handle)->flying_transfers_lock); + + if (!to_cancel) + break; + + usbi_dbg("cancelling transfer %p from disconnect", + USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel)); + + usbi_mutex_lock(&to_cancel->lock); + usbi_backend->clear_transfer_priv(to_cancel); + usbi_mutex_unlock(&to_cancel->lock); + usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); + } + +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusb.h new file mode 100644 index 0000000..c562690 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusb.h @@ -0,0 +1,2008 @@ +/* + * Public libusb header file + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2012 Pete Batard + * Copyright © 2012 Nathan Hjelm + * For more information, please visit: http://libusb.info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#ifdef _MSC_VER +/* on MS environments, the inline keyword is available in C++ only */ +#if !defined(__cplusplus) +#define inline __inline +#endif +/* ssize_t is also not available (copy/paste from MinGW) */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 + typedef __int64 ssize_t; +#else + typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +/* stdint.h is not available on older MSVC */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#if !defined(_WIN32_WCE) +#include +#endif + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) +#include +#endif + +#include +#include + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#include +#if defined(interface) +#undef interface +#endif +#if !defined(__CYGWIN__) +#include +#endif +#endif + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define LIBUSB_DEPRECATED_FOR(f) \ + __attribute__((deprecated("Use " #f " instead"))) +#else +#define LIBUSB_DEPRECATED_FOR(f) +#endif /* __GNUC__ */ + +/** \def LIBUSB_CALL + * \ingroup libusb_misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to functions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +/** \def LIBUSB_API_VERSION + * \ingroup libusb_misc + * libusb's API version. + * + * Since version 1.0.13, to help with feature detection, libusb defines + * a LIBUSB_API_VERSION macro that gets increased every time there is a + * significant change to the API, such as the introduction of a new call, + * the definition of a new macro/enum member, or any other element that + * libusb applications may want to detect at compilation time. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusb API + * #endif + * \endcode + * + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + */ +#define LIBUSB_API_VERSION 0x01000105 + +/* The following is kept for compatibility, but will be deprecated in the future */ +#define LIBUSBX_API_VERSION LIBUSB_API_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup libusb_misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup libusb_misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup libusb_desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 7, + + /** Image class */ + LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + LIBUSB_CLASS_IMAGE = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup libusb_desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** BOS descriptor */ + LIBUSB_DT_BOS = 0x0f, + + /** Device Capability descriptor */ + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ + (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ + (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ + (LIBUSB_BT_CONTAINER_ID_SIZE)) + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup libusb_desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup libusb_desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, + + /** Stream endpoint */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, +}; + +/** \ingroup libusb_misc + * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, + + /** Sets both the U1 and U2 Exit Latency */ + LIBUSB_REQUEST_SET_SEL = 0x30, + + /** Delay from the time a host transmits a packet to the time it is + * received by the device. */ + LIBUSB_SET_ISOCH_DELAY = 0x31, +}; + +/** \ingroup libusb_misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup libusb_misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup libusb_desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup libusb_desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.6 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully operation. Expressed in units + * of 2 mA when the device is operating in high-speed mode and in units + * of 8 mA when the device is operating in super-speed mode. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + + /** The maximum number of packets the endpoint can send or + * receive as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup libusb_desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + /** Device Capability type */ + uint8_t bDevCapabilityType; + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup libusb_desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + +/** \ingroup libusb_asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup libusb_lib + * Structure providing the version of the libusb runtime + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** For ABI compatibility only. */ + const char* describe; +}; + +/** \ingroup libusb_lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref libusb_contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup libusb_dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup libusb_dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup libusb_dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; + +/** \ingroup libusb_dev + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = 1, + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = 2, + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = 4, + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = 8, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 2, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 4, +}; + +/** \ingroup libusb_misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the + message strings in strerror.c when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99, +}; + +/* Total number of error codes in enum libusb_error */ +#define LIBUSB_ERROR_COUNT 14 + +/** \ingroup libusb_asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW, + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ +}; + +/** \ingroup libusb_asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, +}; + +/** \ingroup libusb_asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup libusb_asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref libusb_asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in milliseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_misc + * Capabilities supported by an instance of libusb on the current running + * platform. Test if the loaded library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0x0000, + /** Hotplug support is available on this platform. */ + LIBUSB_CAP_HAS_HOTPLUG = 0x0001, + /** The library can access HID devices without requiring user intervention. + * Note that before being able to actually access an HID device, you may + * still have to call additional libusb functions such as + * \ref libusb_detach_kernel_driver(). */ + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, + /** The library supports detaching of the default USB driver, using + * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 +}; + +/** \ingroup libusb_lib + * Log message levels. + * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default) + * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning + * and error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout, + * warnings and errors to stderr + */ +enum libusb_log_level { + LIBUSB_LOG_LEVEL_NONE = 0, + LIBUSB_LOG_LEVEL_ERROR, + LIBUSB_LOG_LEVEL_WARNING, + LIBUSB_LOG_LEVEL_INFO, + LIBUSB_LOG_LEVEL_DEBUG, +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( + struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos); +void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); +void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); +int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id); +void LIBUSB_CALL libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len); +LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable); + +/* async I/O */ + +/** \ingroup libusb_asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup libusb_asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *)(void *) transfer->buffer; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( + struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer using bulk streams. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param stream_id bulk stream id for this transfer + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_stream_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, uint32_t stream_id, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup libusb_desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev_handle a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); +} + +/** \ingroup libusb_desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup libusb_poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup libusb_poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup libusb_poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +/** \ingroup libusb_hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregisted callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * For more information, see \ref libusb_hotplug. + */ +typedef int libusb_hotplug_callback_handle; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Flags for hotplug events */ +typedef enum { + /** Default value when not using any flags. */ + LIBUSB_HOTPLUG_NO_FLAGS = 0, + + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = 1<<0, +} libusb_hotplug_flag; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug events */ +typedef enum { + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01, + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02, +} libusb_hotplug_event; + +/** \ingroup libusb_hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup libusb_hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * libusb will call this function later, when a matching event had happened on + * a matching device. See \ref libusb_hotplug for more information. + * + * It is safe to call either libusb_hotplug_register_callback() or + * libusb_hotplug_deregister_callback() from within a callback function. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param ctx context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, + libusb_hotplug_event event, + void *user_data); + +/** \ingroup libusb_hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be + * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices + * already plugged into the machine. Note that libusb modifies its internal + * device list from a separate thread, while calling hotplug callbacks from + * libusb_handle_events(), so it is possible for a device to already be present + * on, or removed from, its internal device list, while the hotplug callbacks + * still need to be dispatched. This means that when using \ref + * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival + * of the same device, once from libusb_hotplug_register_callback() and once + * from libusb_handle_events(); and/or your callback may be called for the + * removal of a device for which an arrived call was never made. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context to register this callback with + * \param[in] events bitwise or of events that will trigger this callback. See \ref + * libusb_hotplug_event + * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) + * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, + libusb_hotplug_flag flags, + int vendor_id, int product_id, + int dev_class, + libusb_hotplug_callback_fn cb_fn, + void *user_data, + libusb_hotplug_callback_handle *callback_handle); + +/** \ingroup libusb_hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to deregister + */ +void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusbi.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusbi.h new file mode 100644 index 0000000..752e398 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/libusbi.h @@ -0,0 +1,1149 @@ +/* + * Internal header for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSBI_H +#define LIBUSBI_H + +#include + +#include + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_MISSING_H +#include +#endif + +#include "libusb.h" +#include "version.h" + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEVICE_DESC_LENGTH 18 + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +/* Backend specific capabilities */ +#define USBI_CAP_HAS_HID_ACCESS 0x00010000 +#define USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER 0x00020000 + +/* Maximum number of bytes in a log line */ +#define USBI_MAX_LOG_LEN 1024 +/* Terminator for log lines */ +#define USBI_LOG_LINE_END "\n" + +/* The following is used to silence warnings for unused variables */ +#define UNUSED(var) do { (void)(var); } while(0) + +#if !defined(ARRAYSIZE) +#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member))) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = entry->prev = NULL; +} + +static inline void *usbi_reallocf(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (!ret) + free(ptr); + return ret; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *mptr = (ptr); \ + (type *)( (char *)mptr - offsetof(type,member) );}) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#define TIMEVAL_TV_SEC_TYPE long +#else +#define TIMEVAL_TV_SEC_TYPE time_t +#endif + +/* Some platforms don't have this define */ +#ifndef TIMESPEC_TO_TIMEVAL +#define TIMESPEC_TO_TIMEVAL(tv, ts) \ + do { \ + (tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ + } while (0) +#endif + +void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, ...); + +void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, + const char *function, const char *format, va_list args); + +#if !defined(_MSC_VER) || _MSC_VER >= 1400 + +#ifdef ENABLE_LOGGING +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) +#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) +#else +#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0) +#define usbi_dbg(...) do {} while(0) +#endif + +#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__) + +#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +#ifdef ENABLE_LOGGING +#define LOG_BODY(ctxt, level) \ +{ \ + va_list args; \ + va_start(args, format); \ + usbi_log_v(ctxt, level, "", format, args); \ + va_end(args); \ +} +#else +#define LOG_BODY(ctxt, level) \ +{ \ + (void)(ctxt); \ +} +#endif + +static inline void usbi_info(struct libusb_context *ctx, const char *format, ...) + LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO) +static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...) + LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING) +static inline void usbi_err(struct libusb_context *ctx, const char *format, ...) + LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR) + +static inline void usbi_dbg(const char *format, ...) + LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG) + +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +#define USBI_GET_CONTEXT(ctx) \ + do { \ + if (!(ctx)) \ + (ctx) = usbi_default_context; \ + } while(0) + +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) +#define IS_EPOUT(ep) (!IS_EPIN(ep)) +#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) +#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) + +/* Internal abstraction for thread synchronization */ +#if defined(THREADS_POSIX) +#include "os/threads_posix.h" +#elif defined(OS_WINDOWS) || defined(OS_WINCE) +#include "os/threads_windows.h" +#endif + +extern struct libusb_context *usbi_default_context; + +/* Forward declaration for use in context (fully defined inside poll abstraction) */ +struct pollfd; + +struct libusb_context { + int debug; + int debug_fixed; + + /* internal event pipe, used for signalling occurrence of an internal event. */ + int event_pipe[2]; + + struct list_head usb_devs; + usbi_mutex_t usb_devs_lock; + + /* A list of open handles. Backends are free to traverse this if required. + */ + struct list_head open_devs; + usbi_mutex_t open_devs_lock; + + /* A list of registered hotplug callbacks */ + struct list_head hotplug_cbs; + usbi_mutex_t hotplug_cbs_lock; + + /* this is a list of in-flight transfer handles, sorted by timeout + * expiration. URBs to timeout the soonest are placed at the beginning of + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + /* Note paths taking both this and usbi_transfer->lock must always + * take this lock first */ + usbi_mutex_t flying_transfers_lock; + + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* A thread-local storage key to track which thread is performing event + * handling */ + usbi_tls_key_t event_handling_key; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + + /* A lock to protect internal context event data. */ + usbi_mutex_t event_data_lock; + + /* A bitmask of flags that are set to indicate specific events that need to + * be handled. Protected by event_data_lock. */ + unsigned int event_flags; + + /* A counter that is set when we want to interrupt and prevent event handling, + * in order to safely close a device. Protected by event_data_lock. */ + unsigned int device_close; + + /* list and count of poll fds and an array of poll fd structures that is + * (re)allocated as necessary prior to polling. Protected by event_data_lock. */ + struct list_head ipollfds; + struct pollfd *pollfds; + POLL_NFDS_TYPE pollfds_cnt; + + /* A list of pending hotplug messages. Protected by event_data_lock. */ + struct list_head hotplug_msgs; + + /* A list of pending completed transfers. Protected by event_data_lock. */ + struct list_head completed_transfers; + +#ifdef USBI_TIMERFD_AVAILABLE + /* used for timeout handling, if supported by OS. + * this timerfd is maintained to trigger on the next pending timeout */ + int timerfd; +#endif + + struct list_head list; +}; + +enum usbi_event_flags { + /* The list of pollfds has been modified */ + USBI_EVENT_POLLFDS_MODIFIED = 1 << 0, + + /* The user has interrupted the event handler */ + USBI_EVENT_USER_INTERRUPT = 1 << 1, +}; + +/* Macros for managing event handling state */ +#define usbi_handling_events(ctx) \ + (usbi_tls_key_get((ctx)->event_handling_key) != NULL) + +#define usbi_start_event_handling(ctx) \ + usbi_tls_key_set((ctx)->event_handling_key, ctx) + +#define usbi_end_event_handling(ctx) \ + usbi_tls_key_set((ctx)->event_handling_key, NULL) + +/* Update the following macro if new event sources are added */ +#define usbi_pending_events(ctx) \ + ((ctx)->event_flags || (ctx)->device_close \ + || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers)) + +#ifdef USBI_TIMERFD_AVAILABLE +#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) +#else +#define usbi_using_timerfd(ctx) (0) +#endif + +struct libusb_device { + /* lock protects refcnt, everything else is finalized at initialization + * time */ + usbi_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t port_number; + struct libusb_device* parent_dev; + uint8_t device_address; + uint8_t num_configurations; + enum libusb_speed speed; + + struct list_head list; + unsigned long session_data; + + struct libusb_device_descriptor device_descriptor; + int attached; + + unsigned char os_priv +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif +#if defined(OS_SUNOS) + __attribute__ ((aligned (8))); +#else + ; +#endif +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + int auto_detach_kernel_driver; + unsigned char os_priv +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif +#if defined(OS_SUNOS) + __attribute__ ((aligned (8))); +#else + ; +#endif +}; + +enum { + USBI_CLOCK_MONOTONIC, + USBI_CLOCK_REALTIME +}; + +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ + +struct usbi_transfer { + int num_iso_packets; + struct list_head list; + struct list_head completed_list; + struct timeval timeout; + int transferred; + uint32_t stream_id; + uint8_t state_flags; /* Protected by usbi_transfer->lock */ + uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */ + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). + * Note paths taking both this and the flying_transfers_lock must + * always take the flying_transfers_lock first */ + usbi_mutex_t lock; +}; + +enum usbi_transfer_state_flags { + /* Transfer successfully submitted by backend */ + USBI_TRANSFER_IN_FLIGHT = 1 << 0, + + /* Cancellation was requested via libusb_cancel_transfer() */ + USBI_TRANSFER_CANCELLING = 1 << 1, + + /* Operation on the transfer failed because the device disappeared */ + USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2, +}; + +enum usbi_transfer_timeout_flags { + /* Set by backend submit_transfer() if the OS handles timeout */ + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0, + + /* The transfer timeout has been handled */ + USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1, + + /* The transfer timeout was successfully processed */ + USBI_TRANSFER_TIMED_OUT = 1 << 2, +}; + +#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + +/* bus structures */ + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* shared data and functions */ + +int usbi_io_init(struct libusb_context *ctx); +void usbi_io_exit(struct libusb_context *ctx); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_device_handle *dev_handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); +void usbi_signal_transfer_completion(struct usbi_transfer *transfer); + +int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, + void *dest, int host_endian); +int usbi_device_cache_descriptor(libusb_device *dev); +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx); + +void usbi_connect_device (struct libusb_device *dev); +void usbi_disconnect_device (struct libusb_device *dev); + +int usbi_signal_event(struct libusb_context *ctx); +int usbi_clear_event(struct libusb_context *ctx); + +/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */ +#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ + defined(OS_HAIKU) || defined(OS_SUNOS) +#include +#include "os/poll_posix.h" +#elif defined(OS_WINDOWS) || defined(OS_WINCE) +#include "os/poll_windows.h" +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf usbi_snprintf +#define vsnprintf usbi_vsnprintf +int usbi_snprintf(char *dst, size_t size, const char *format, ...); +int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap); +#define LIBUSB_PRINTF_WIN32 +#endif + +struct usbi_pollfd { + /* must come first */ + struct libusb_pollfd pollfd; + + struct list_head list; +}; + +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); +void usbi_remove_pollfd(struct libusb_context *ctx, int fd); + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Binary mask for backend specific capabilities */ + uint32_t caps; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * prior to use. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * that was set up by init. + * + * This function is called when the user deinitializes the library. + */ + void (*exit)(void); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a reference + * to a libusb_device structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, we can now continue. + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * If the backend has hotplug support, this function is not used! + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Apps which were written before hotplug support, may listen for + * hotplug events on their own and call libusb_get_device_list on + * device addition. In this case libusb_get_device_list will likely + * return a list without the new device in there, as the hotplug + * event thread will still be busy enumerating the device, which may + * take a while, or may not even have seen the event yet. + * + * To avoid this libusb_get_device_list will call this optional + * function for backends with hotplug support before copying + * ctx->usb_devs to the user. In this function the backend should + * ensure any pending hotplug events are fully processed before + * returning. + * + * Optional, should be implemented by backends with hotplug support. + */ + void (*hotplug_poll)(void); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_pollfd() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *dev_handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_pollfd() to inform + * libusb of any file descriptors associated with this device that should + * no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *dev_handle); + + /* Retrieve the device descriptor from a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. Alternatively, you may be able + * to retrieve it from a kernel interface (some Linux setups can do this) + * still without generating bus I/O. + * + * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into + * buffer, which is guaranteed to be big enough. + * + * This function is called when sanity-checking a device before adding + * it to the list of discovered devices, and also when the user requests + * to read the device descriptor. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_device_descriptor)(struct libusb_device *device, + unsigned char *buffer, int *host_endian); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return the length read on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); + + /* Like get_config_descriptor but then by bConfigurationValue instead + * of by index. + * + * Optional, if not present the core will call get_config_descriptor + * for all configs until it finds the desired bConfigurationValue. + * + * Returns a pointer to the raw-descriptor in *buffer, this memory + * is valid as long as device is valid. + * + * Returns the length of the returned raw-descriptor on success, + * or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor_by_value)(struct libusb_device *device, + uint8_t bConfigurationValue, unsigned char **buffer, + int *host_endian); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *dev_handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applications can use the interface because + * we now "own" it. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, + int interface_number, int altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *dev_handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. + * + * If possible, the device handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the resetted + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *dev_handle); + + /* Alloc num_streams usb3 bulk streams on the passed in endpoints */ + int (*alloc_streams)(struct libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); + + /* Free usb3 bulk streams allocated with alloc_streams */ + int (*free_streams)(struct libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + + /* Allocate persistent DMA memory for the given device, suitable for + * zerocopy. May return NULL on failure. Optional to implement. + */ + unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle, + size_t len); + + /* Free memory allocated by dev_mem_alloc. */ + int (*dev_mem_free)(struct libusb_device_handle *handle, + unsigned char *buffer, size_t len); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *dev_handle, + int interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle, + int interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle, + int interface_number); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * This function gets called with the flying_transfers_lock locked! + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events on file descriptors. Optional. + * + * Provide this function when file descriptors directly indicate device + * or transfer activity. If your backend does not have such file descriptors, + * implement the handle_transfer_completion function below. + * + * This involves monitoring any active transfers and processing their + * completion or cancellation. + * + * The function is passed an array of pollfd structures (size nfds) + * as a result of the poll() system call. The num_ready parameter + * indicates the number of file descriptors that have reported events + * (i.e. the poll() return value). This should be enough information + * for you to determine which actions need to be taken on the currently + * active transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + + /* Handle transfer completion. Optional. + * + * Provide this function when there are no file descriptors available + * that directly indicate device or transfer activity. If your backend does + * have such file descriptors, implement the handle_events function above. + * + * Your backend must tell the library when a transfer has completed by + * calling usbi_signal_transfer_completion(). You should store any private + * information about the transfer and its completion status in the transfer's + * private backend data. + * + * During event handling, this function will be called on each transfer for + * which usbi_signal_transfer_completion() was called. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_transfer_completion)(struct usbi_transfer *itransfer); + + /* Get time from specified clock. At least two clocks must be implemented + by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. + + Description of clocks: + USBI_CLOCK_REALTIME : clock returns time since system epoch. + USBI_CLOCK_MONOTONIC: clock returns time since unspecified start + time (usually boot). + */ + int (*clock_gettime)(int clkid, struct timespec *tp); + +#ifdef USBI_TIMERFD_AVAILABLE + /* clock ID of the clock that should be used for timerfd */ + clockid_t (*get_timerfd_clockid)(void); +#endif + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + */ + size_t transfer_priv_size; +}; + +extern const struct usbi_os_backend * const usbi_backend; + +extern const struct usbi_os_backend linux_usbfs_backend; +extern const struct usbi_os_backend darwin_backend; +extern const struct usbi_os_backend openbsd_backend; +extern const struct usbi_os_backend netbsd_backend; +extern const struct usbi_os_backend windows_backend; +extern const struct usbi_os_backend usbdk_backend; +extern const struct usbi_os_backend wince_backend; +extern const struct usbi_os_backend haiku_usb_raw_backend; +extern const struct usbi_os_backend sunos_backend; + +extern struct list_head active_contexts_list; +extern usbi_mutex_static_t active_contexts_lock; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.c new file mode 100644 index 0000000..b0219d1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.c @@ -0,0 +1,2094 @@ +/* -*- Mode: C; indent-tabs-mode:nil -*- */ +/* + * darwin backend for libusb 1.0 + * Copyright © 2008-2016 Nathan Hjelm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + #include +#endif + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 +/* Apple deprecated the darwin atomics in 10.12 in favor of C11 atomics */ +#include +#define libusb_darwin_atomic_fetch_add(x, y) atomic_fetch_add(x, y) + +_Atomic int32_t initCount = ATOMIC_VAR_INIT(0); +#else +/* use darwin atomics if the target is older than 10.12 */ +#include + +/* OSAtomicAdd32Barrier returns the new value */ +#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y) + +static volatile int32_t initCount = 0; +#endif + +#include "darwin_usb.h" + +/* async event thread */ +static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER; + +static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT; + +static clock_serv_t clock_realtime; +static clock_serv_t clock_monotonic; + +static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ +static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */ + +static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER; +static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices}; +static char *darwin_device_class = kIOUSBDeviceClassName; + +#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev)) + +/* async event thread */ +static pthread_t libusb_darwin_at; + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian); +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface); +static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int darwin_reset_device(struct libusb_device_handle *dev_handle); +static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0); + +static int darwin_scan_devices(struct libusb_context *ctx); +static int process_new_device (struct libusb_context *ctx, io_service_t service); + +#if defined(ENABLE_LOGGING) +static const char *darwin_error_str (int result) { + static char string_buffer[50]; + switch (result) { + case kIOReturnSuccess: + return "no error"; + case kIOReturnNotOpen: + return "device not opened for exclusive access"; + case kIOReturnNoDevice: + return "no connection to an IOService"; + case kIOUSBNoAsyncPortErr: + return "no async port has been opened for interface"; + case kIOReturnExclusiveAccess: + return "another process has device opened for exclusive access"; + case kIOUSBPipeStalled: + return "pipe is stalled"; + case kIOReturnError: + return "could not establish a connection to the Darwin kernel"; + case kIOUSBTransactionTimeout: + return "transaction timed out"; + case kIOReturnBadArgument: + return "invalid argument"; + case kIOReturnAborted: + return "transaction aborted"; + case kIOReturnNotResponding: + return "device not responding"; + case kIOReturnOverrun: + return "data overrun"; + case kIOReturnCannotWire: + return "physical memory can not be wired down"; + case kIOReturnNoResources: + return "out of resources"; + case kIOUSBHighSpeedSplitError: + return "high speed split error"; + default: + snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result); + return string_buffer; + } +} +#endif + +static int darwin_to_libusb (int result) { + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_SUCCESS; + case kIOReturnNotOpen: + case kIOReturnNoDevice: + return LIBUSB_ERROR_NO_DEVICE; + case kIOReturnExclusiveAccess: + return LIBUSB_ERROR_ACCESS; + case kIOUSBPipeStalled: + return LIBUSB_ERROR_PIPE; + case kIOReturnBadArgument: + return LIBUSB_ERROR_INVALID_PARAM; + case kIOUSBTransactionTimeout: + return LIBUSB_ERROR_TIMEOUT; + case kIOReturnNotResponding: + case kIOReturnAborted: + case kIOReturnError: + case kIOUSBNoAsyncPortErr: + default: + return LIBUSB_ERROR_OTHER; + } +} + +/* this function must be called with the darwin_cached_devices_lock held */ +static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount--; + /* free the device and remove it from the cache */ + if (0 == cached_dev->refcount) { + list_del(&cached_dev->list); + + (*(cached_dev->device))->Release(cached_dev->device); + free (cached_dev); + } +} + +static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) { + cached_dev->refcount++; +} + +static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + + /* current interface */ + struct darwin_interface *cInterface; + + int8_t i, iface; + + usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep); + + for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { + cInterface = &priv->interfaces[iface]; + + if (dev_handle->claimed_interfaces & (1 << iface)) { + for (i = 0 ; i < cInterface->num_endpoints ; i++) { + if (cInterface->endpoint_addrs[i] == ep) { + *pipep = i + 1; + + if (ifcp) + *ifcp = iface; + + if (interface_out) + *interface_out = cInterface; + + usbi_dbg ("pipe %d on interface %d matches", *pipep, iface); + return 0; + } + } + } + } + + /* No pipe found with the correct endpoint address */ + usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep); + + return LIBUSB_ERROR_NOT_FOUND; +} + +static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { + CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class); + + if (!matchingDict) + return kIOReturnError; + + if (location) { + CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (propertyMatchDict) { + /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this + internally (CFNumberType of locationID is 3) */ + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); + + CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); + /* release our reference to the CFNumber (CFDictionarySetValue retains it) */ + CFRelease (locationCF); + + CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + /* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */ + CFRelease (propertyMatchDict); + } + /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */ + } + + return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator); +} + +/* Returns 1 on success, 0 on failure. */ +static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { + CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + int ret = 0; + + if (cfNumber) { + if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { + ret = CFNumberGetValue(cfNumber, type, p); + } + + CFRelease (cfNumber); + } + + return ret; +} + +static int get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) { + CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); + int ret = 0; + + if (cfData) { + if (CFGetTypeID (cfData) == CFDataGetTypeID ()) { + CFIndex length = CFDataGetLength (cfData); + if (length < size) { + size = length; + } + + CFDataGetBytes (cfData, CFRangeMake(0, size), p); + ret = 1; + } + + CFRelease (cfData); + } + + return ret; +} + +static usb_device_t **darwin_device_from_service (io_service_t service) +{ + io_cf_plugin_ref_t *plugInInterface = NULL; + usb_device_t **device; + kern_return_t result; + SInt32 score; + + result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); + + if (kIOReturnSuccess != result || !plugInInterface) { + usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (result)); + return NULL; + } + + (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), + (LPVOID)&device); + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); + + return device; +} + +static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { + struct libusb_context *ctx; + io_service_t service; + + usbi_mutex_lock(&active_contexts_lock); + + while ((service = IOIteratorNext(add_devices))) { + /* add this device to each active context's device list */ + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + process_new_device (ctx, service);; + } + + IOObjectRelease(service); + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { + struct libusb_device *dev = NULL; + struct libusb_context *ctx; + struct darwin_cached_device *old_device; + + io_service_t device; + UInt64 session; + int ret; + + usbi_mutex_lock(&active_contexts_lock); + + while ((device = IOIteratorNext (rem_devices)) != 0) { + /* get the location from the i/o registry */ + ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); + IOObjectRelease (device); + if (!ret) + continue; + + /* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function + otherwise no cached device will ever get freed */ + usbi_mutex_lock(&darwin_cached_devices_lock); + list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) { + if (old_device->session == session) { + darwin_deref_cached_device (old_device); + break; + } + } + usbi_mutex_unlock(&darwin_cached_devices_lock); + + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + usbi_dbg ("notifying context %p of device disconnect", ctx); + + dev = usbi_get_device_by_session_id(ctx, (unsigned long) session); + if (dev) { + /* signal the core that this device has been disconnected. the core will tear down this device + when the reference count reaches 0 */ + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } + } + } + + usbi_mutex_unlock(&active_contexts_lock); +} + +static void darwin_hotplug_poll (void) +{ + /* not sure if 5 seconds will be too long/short but it should work ok */ + mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0}; + + /* since a kernel thread may nodify the IOInterators used for + * hotplug notidication we can't just clear the iterators. + * instead just wait until all IOService providers are quiet */ + (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout); +} + +static void darwin_clear_iterator (io_iterator_t iter) { + io_service_t device; + + while ((device = IOIteratorNext (iter)) != 0) + IOObjectRelease (device); +} + +static void *darwin_event_thread_main (void *arg0) { + IOReturn kresult; + struct libusb_context *ctx = (struct libusb_context *)arg0; + CFRunLoopRef runloop; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + /* Set this thread's name, so it can be seen in the debugger + and crash reports. */ + pthread_setname_np ("org.libusb.device-hotplug"); +#endif + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 + /* Tell the Objective-C garbage collector about this thread. + This is required because, unlike NSThreads, pthreads are + not automatically registered. Although we don't use + Objective-C, we use CoreFoundation, which does. + Garbage collection support was entirely removed in 10.12, + so don't bother there. */ + objc_registerThreadWithCollector(); +#endif + + /* hotplug (device arrival/removal) sources */ + CFRunLoopSourceContext libusb_shutdown_cfsourcectx; + CFRunLoopSourceRef libusb_notification_cfsource; + io_notification_port_t libusb_notification_port; + io_iterator_t libusb_rem_device_iterator; + io_iterator_t libusb_add_device_iterator; + + usbi_dbg ("creating hotplug event source"); + + runloop = CFRunLoopGetCurrent (); + CFRetain (runloop); + + /* add the shutdown cfsource to the run loop */ + memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx)); + libusb_shutdown_cfsourcectx.info = runloop; + libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop; + libusb_darwin_acfls = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx); + CFRunLoopAddSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode); + + /* add the notification port to the run loop */ + libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault); + libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port); + CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* create notifications for removed devices */ + kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_detached, + ctx, &libusb_rem_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + + pthread_exit (NULL); + } + + /* create notifications for attached devices */ + kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, + IOServiceMatching(darwin_device_class), + darwin_devices_attached, + ctx, &libusb_add_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + + pthread_exit (NULL); + } + + /* arm notifiers */ + darwin_clear_iterator (libusb_rem_device_iterator); + darwin_clear_iterator (libusb_add_device_iterator); + + usbi_dbg ("darwin event thread ready to receive events"); + + /* signal the main thread that the hotplug runloop has been created. */ + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfl = runloop; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + + /* run the runloop */ + CFRunLoopRun(); + + usbi_dbg ("darwin event thread exiting"); + + /* remove the notification cfsource */ + CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); + + /* remove the shutdown cfsource */ + CFRunLoopRemoveSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode); + + /* delete notification port */ + IONotificationPortDestroy (libusb_notification_port); + + /* delete iterators */ + IOObjectRelease (libusb_rem_device_iterator); + IOObjectRelease (libusb_add_device_iterator); + + CFRelease (libusb_darwin_acfls); + CFRelease (runloop); + + libusb_darwin_acfls = NULL; + libusb_darwin_acfl = NULL; + + pthread_exit (NULL); +} + +/* cleanup function to destroy cached devices */ +static void __attribute__((destructor)) _darwin_finalize(void) { + struct darwin_cached_device *dev, *next; + + usbi_mutex_lock(&darwin_cached_devices_lock); + list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { + darwin_deref_cached_device(dev); + } + usbi_mutex_unlock(&darwin_cached_devices_lock); +} + +static void darwin_check_version (void) { + /* adjust for changes in the USB stack in xnu 15 */ + int sysctl_args[] = {CTL_KERN, KERN_OSRELEASE}; + long version; + char version_string[256] = {'\0',}; + size_t length = 256; + + sysctl(sysctl_args, 2, version_string, &length, NULL, 0); + + errno = 0; + version = strtol (version_string, NULL, 10); + if (0 == errno && version >= 15) { + darwin_device_class = "IOUSBHostDevice"; + } +} + +static int darwin_init(struct libusb_context *ctx) { + host_name_port_t host_self; + int rc; + + rc = pthread_once (&darwin_init_once, darwin_check_version); + if (rc) { + return LIBUSB_ERROR_OTHER; + } + + rc = darwin_scan_devices (ctx); + if (LIBUSB_SUCCESS != rc) { + return rc; + } + + if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) { + /* create the clocks that will be used */ + + host_self = mach_host_self(); + host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); + host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); + mach_port_deallocate(mach_task_self(), host_self); + + pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); + + pthread_mutex_lock (&libusb_darwin_at_mutex); + while (!libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + } + + return rc; +} + +static void darwin_exit (void) { + if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) { + mach_port_deallocate(mach_task_self(), clock_realtime); + mach_port_deallocate(mach_task_self(), clock_monotonic); + + /* stop the event runloop and wait for the thread to terminate. */ + CFRunLoopSourceSignal(libusb_darwin_acfls); + CFRunLoopWakeUp (libusb_darwin_acfl); + pthread_join (libusb_darwin_at, NULL); + } +} + +static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + + /* return cached copy */ + memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH); + + *host_endian = 0; + + return 0; +} + +static int get_configuration_index (struct libusb_device *dev, int config_value) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + UInt8 i, numConfig; + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + + /* is there a simpler way to determine the index? */ + kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + for (i = 0 ; i < numConfig ; i++) { + (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc); + + if (desc->bConfigurationValue == config_value) + return i; + } + + /* configuration not found */ + return LIBUSB_ERROR_NOT_FOUND; +} + +static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + int config_index; + + if (0 == priv->active_config) + return LIBUSB_ERROR_NOT_FOUND; + + config_index = get_configuration_index (dev, priv->active_config); + if (config_index < 0) + return config_index; + + return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian); +} + +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) { + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); + IOUSBConfigurationDescriptorPtr desc; + IOReturn kresult; + int ret; + + if (!priv || !priv->device) + return LIBUSB_ERROR_OTHER; + + kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc); + if (kresult == kIOReturnSuccess) { + /* copy descriptor */ + if (libusb_le16_to_cpu(desc->wTotalLength) < len) + len = libusb_le16_to_cpu(desc->wTotalLength); + + memmove (buffer, desc, len); + + /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */ + *host_endian = 0; + } + + ret = darwin_to_libusb (kresult); + if (ret != LIBUSB_SUCCESS) + return ret; + + return (int) len; +} + +/* check whether the os has configured the device */ +static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { + usb_device_t **darwin_device = dev->device; + + IOUSBConfigurationDescriptorPtr configDesc; + IOUSBFindInterfaceRequest request; + kern_return_t kresult; + io_iterator_t interface_iterator; + io_service_t firstInterface; + + if (dev->dev_descriptor.bNumConfigurations < 1) { + usbi_err (ctx, "device has no configurations"); + return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */ + } + + /* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is + not usable anyway */ + if (0x05ac == dev->dev_descriptor.idVendor && 0x8005 == dev->dev_descriptor.idProduct) { + usbi_dbg ("ignoring configuration on root hub simulation"); + dev->active_config = 0; + return 0; + } + + /* find the first configuration */ + kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc); + dev->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1; + + /* check if the device is already configured. there is probably a better way than iterating over the + to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices + might lock up on the device request) */ + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult) + return darwin_to_libusb (kresult); + + /* iterate once */ + firstInterface = IOIteratorNext(interface_iterator); + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + if (firstInterface) { + IOObjectRelease (firstInterface); + + /* device is configured */ + if (dev->dev_descriptor.bNumConfigurations == 1) + /* to avoid problems with some devices get the configurations value from the configuration descriptor */ + dev->active_config = dev->first_config; + else + /* devices with more than one configuration should work with GetConfiguration */ + (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config); + } else + /* not configured */ + dev->active_config = 0; + + usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config); + + return 0; +} + +static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { + IOUSBDevRequestTO req; + + memset (buffer, 0, buffer_size); + + /* Set up request for descriptor/ */ + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = desc << 8; + req.wIndex = desc_index; + req.wLength = buffer_size; + req.pData = buffer; + req.noDataTimeout = 20; + req.completionTimeout = 100; + + return (*device)->DeviceRequestTO (device, &req); +} + +static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) { + usb_device_t **device = dev->device; + int retries = 1, delay = 30000; + int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; + int is_open = 0; + int ret = 0, ret2; + UInt8 bDeviceClass; + UInt16 idProduct, idVendor; + + dev->can_enumerate = 0; + + (*device)->GetDeviceClass (device, &bDeviceClass); + (*device)->GetDeviceProduct (device, &idProduct); + (*device)->GetDeviceVendor (device, &idVendor); + + /* According to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some + * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still, + * to follow the spec as closely as possible, try opening the device */ + is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess); + + do { + /**** retrieve device descriptor ****/ + ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor)); + + if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType) + /* received an overrun error but we still received a device descriptor */ + ret = kIOReturnSuccess; + + if (kIOUSBVendorIDAppleComputer == idVendor) { + /* NTH: don't bother retrying or unsuspending Apple devices */ + break; + } + + if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations || + 0 == dev->dev_descriptor.bcdUSB)) { + /* work around for incorrectly configured devices */ + if (try_reconfigure && is_open) { + usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again..."); + + /* set the first configuration */ + (*device)->SetConfiguration(device, 1); + + /* don't try to reconfigure again */ + try_reconfigure = 0; + } + + ret = kIOUSBPipeStalled; + } + + if (kIOReturnSuccess != ret && is_open && try_unsuspend) { + /* device may be suspended. unsuspend it and try again */ +#if DeviceVersion >= 320 + UInt32 info = 0; + + /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ + (void)(*device)->GetUSBDeviceInformation (device, &info); + + /* note that the device was suspended */ + if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info) + try_unsuspend = 1; +#endif + + if (try_unsuspend) { + /* try to unsuspend the device */ + ret2 = (*device)->USBDeviceSuspend (device, 0); + if (kIOReturnSuccess != ret2) { + /* prevent log spew from poorly behaving devices. this indicates the + os actually had trouble communicating with the device */ + usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); + } else + unsuspended = 1; + + try_unsuspend = 0; + } + } + + if (kIOReturnSuccess != ret) { + usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000); + /* sleep for a little while before trying again */ + nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL); + } + } while (kIOReturnSuccess != ret && retries--); + + if (unsuspended) + /* resuspend the device */ + (void)(*device)->USBDeviceSuspend (device, 1); + + if (is_open) + (void) (*device)->USBDeviceClose (device); + + if (ret != kIOReturnSuccess) { + /* a debug message was already printed out for this error */ + if (LIBUSB_CLASS_HUB == bDeviceClass) + usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + idVendor, idProduct, darwin_error_str (ret), ret); + else + usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + idVendor, idProduct, darwin_error_str (ret), ret); + return darwin_to_libusb (ret); + } + + /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ + if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) { + /* not a valid device */ + usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device", + idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); + return LIBUSB_ERROR_NO_DEVICE; + } + + usbi_dbg ("cached device descriptor:"); + usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType); + usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB); + usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass); + usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass); + usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol); + usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0); + usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor); + usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct); + usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice); + usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer); + usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct); + usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber); + usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations); + + dev->can_enumerate = 1; + + return LIBUSB_SUCCESS; +} + +static int get_device_port (io_service_t service, UInt8 *port) { + kern_return_t result; + io_service_t parent; + int ret = 0; + + if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) { + return 1; + } + + result = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent); + if (kIOReturnSuccess == result) { + ret = get_ioregistry_value_data (parent, CFSTR("port"), 1, port); + IOObjectRelease (parent); + } + + return ret; +} + +static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, + struct darwin_cached_device **cached_out) { + struct darwin_cached_device *new_device; + UInt64 sessionID = 0, parent_sessionID = 0; + int ret = LIBUSB_SUCCESS; + usb_device_t **device; + io_service_t parent; + kern_return_t result; + UInt8 port = 0; + + /* get some info from the io registry */ + (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID); + if (!get_device_port (service, &port)) { + usbi_dbg("could not get connected port number"); + } + + usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID); + + result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent); + + if (kIOReturnSuccess == result) { + (void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID); + IOObjectRelease(parent); + } + + usbi_mutex_lock(&darwin_cached_devices_lock); + do { + *cached_out = NULL; + + list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { + usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session); + if (new_device->session == sessionID) { + usbi_dbg("using cached device for device"); + *cached_out = new_device; + break; + } + } + + if (*cached_out) + break; + + usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID); + + device = darwin_device_from_service (service); + if (!device) { + ret = LIBUSB_ERROR_NO_DEVICE; + break; + } + + new_device = calloc (1, sizeof (*new_device)); + if (!new_device) { + ret = LIBUSB_ERROR_NO_MEM; + break; + } + + /* add this device to the cached device list */ + list_add(&new_device->list, &darwin_cached_devices); + + (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address); + + /* keep a reference to this device */ + darwin_ref_cached_device(new_device); + + new_device->device = device; + new_device->session = sessionID; + (*device)->GetLocationID (device, &new_device->location); + new_device->port = port; + new_device->parent_session = parent_sessionID; + + /* cache the device descriptor */ + ret = darwin_cache_device_descriptor(ctx, new_device); + if (ret) + break; + + if (new_device->can_enumerate) { + snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address, + new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct, + new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass); + } + } while (0); + + usbi_mutex_unlock(&darwin_cached_devices_lock); + + /* keep track of devices regardless of if we successfully enumerate them to + prevent them from being enumerated multiple times */ + + *cached_out = new_device; + + return ret; +} + +static int process_new_device (struct libusb_context *ctx, io_service_t service) { + struct darwin_device_priv *priv; + struct libusb_device *dev = NULL; + struct darwin_cached_device *cached_device; + UInt8 devSpeed; + int ret = 0; + + do { + ret = darwin_get_cached_device (ctx, service, &cached_device); + + if (ret < 0 || !cached_device->can_enumerate) { + return ret; + } + + /* check current active configuration (and cache the first configuration value-- + which may be used by claim_interface) */ + ret = darwin_check_configuration (ctx, cached_device); + if (ret) + break; + + usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64, + ctx, cached_device->session); + + dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session); + if (!dev) { + return LIBUSB_ERROR_NO_MEM; + } + + priv = (struct darwin_device_priv *)dev->os_priv; + + priv->dev = cached_device; + darwin_ref_cached_device (priv->dev); + + if (cached_device->parent_session > 0) { + dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session); + } else { + dev->parent_dev = NULL; + } + dev->port_number = cached_device->port; + dev->bus_number = cached_device->location >> 24; + dev->device_address = cached_device->address; + + (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); + + switch (devSpeed) { + case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; + case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break; + case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break; +#if DeviceVersion >= 500 + case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break; +#endif + default: + usbi_warn (ctx, "Got unknown device speed %d", devSpeed); + } + + ret = usbi_sanitize_device (dev); + if (ret < 0) + break; + + usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address, + dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path); + } while (0); + + if (0 == ret) { + usbi_connect_device (dev); + } else { + libusb_unref_device (dev); + } + + return ret; +} + +static int darwin_scan_devices(struct libusb_context *ctx) { + io_iterator_t deviceIterator; + io_service_t service; + kern_return_t kresult; + + kresult = usb_setup_device_iterator (&deviceIterator, 0); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + while ((service = IOIteratorNext (deviceIterator))) { + (void) process_new_device (ctx, service); + + IOObjectRelease(service); + } + + IOObjectRelease(deviceIterator); + + return 0; +} + +static int darwin_open (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + + if (0 == dpriv->open_count) { + /* try to open the device */ + kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device); + if (kresult != kIOReturnSuccess) { + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); + + if (kIOReturnExclusiveAccess != kresult) { + return darwin_to_libusb (kresult); + } + + /* it is possible to perform some actions on a device that is not open so do not return an error */ + priv->is_open = 0; + } else { + priv->is_open = 1; + } + + /* create async event source */ + kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult)); + + if (priv->is_open) { + (*(dpriv->device))->USBDeviceClose (dpriv->device); + } + + priv->is_open = 0; + + return darwin_to_libusb (kresult); + } + + CFRetain (libusb_darwin_acfl); + + /* add the cfSource to the aync run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); + } + + /* device opened successfully */ + dpriv->open_count++; + + usbi_dbg ("device open for access"); + + return 0; +} + +static void darwin_close (struct libusb_device_handle *dev_handle) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + int i; + + if (dpriv->open_count == 0) { + /* something is probably very wrong if this is the case */ + usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!"); + return; + } + + dpriv->open_count--; + + /* make sure all interfaces are released */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + libusb_release_interface (dev_handle, i); + + if (0 == dpriv->open_count) { + /* delete the device's async event source */ + if (priv->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode); + CFRelease (priv->cfSource); + priv->cfSource = NULL; + CFRelease (libusb_darwin_acfl); + } + + if (priv->is_open) { + /* close the device */ + kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device); + if (kresult) { + /* Log the fact that we had a problem closing the file, however failing a + * close isn't really an error, so return success anyway */ + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); + } + } + } +} + +static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + + *config = (int) dpriv->active_config; + + return 0; +} + +static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOReturn kresult; + int i; + + /* Setting configuration will invalidate the interface, so we need + to reclaim it. First, dispose of existing interfaces, if any. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + darwin_release_interface (dev_handle, i); + + kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* Reclaim any interfaces. */ + for (i = 0 ; i < USB_MAXINTERFACES ; i++) + if (dev_handle->claimed_interfaces & (1 << i)) + darwin_claim_interface (dev_handle, i); + + dpriv->active_config = config; + + return 0; +} + +static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { + IOUSBFindInterfaceRequest request; + kern_return_t kresult; + io_iterator_t interface_iterator; + UInt8 bInterfaceNumber; + int ret; + + *usbInterfacep = IO_OBJECT_NULL; + + /* Setup the Interface Request */ + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + if (kresult) + return kresult; + + while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { + /* find the interface number */ + ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type, + &bInterfaceNumber); + + if (ret && bInterfaceNumber == ifc) { + break; + } + + (void) IOObjectRelease (*usbInterfacep); + } + + /* done with the interface iterator */ + IOObjectRelease(interface_iterator); + + return 0; +} + +static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + kern_return_t kresult; + + u_int8_t numep, direction, number; + u_int8_t dont_care1, dont_care3; + u_int16_t dont_care2; + int rc; + + usbi_dbg ("building table of endpoints."); + + /* retrieve the total number of endpoints on this interface */ + kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* iterate through pipe references */ + for (int i = 1 ; i <= numep ; i++) { + kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, + &dont_care2, &dont_care3); + + if (kresult != kIOReturnSuccess) { + /* probably a buggy device. try to get the endpoint address from the descriptors */ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *endpoint_desc; + UInt8 alt_setting; + + kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface"); + return darwin_to_libusb (kresult); + } + + rc = libusb_get_active_config_descriptor (dev_handle->dev, &config); + if (LIBUSB_SUCCESS != rc) { + return rc; + } + + endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1; + + cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress; + } else { + cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); + } + + usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift, + cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK); + } + + cInterface->num_endpoints = numep; + + return 0; +} + +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + io_service_t usbInterface = IO_OBJECT_NULL; + IOReturn kresult; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); + if (kresult != kIOReturnSuccess) + return darwin_to_libusb (kresult); + + /* make sure we have an interface */ + if (!usbInterface && dpriv->first_config != 0) { + usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config); + + /* set the configuration */ + kresult = darwin_set_configuration (dev_handle, dpriv->first_config); + if (kresult != LIBUSB_SUCCESS) { + usbi_err (HANDLE_CTX (dev_handle), "could not set configuration"); + return kresult; + } + + kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + } + + if (!usbInterface) { + usbi_err (HANDLE_CTX (dev_handle), "interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* get an interface to the device's interface */ + kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, &score); + + /* ignore release error */ + (void)IOObjectRelease (usbInterface); + + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + if (!plugInInterface) { + usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found"); + return LIBUSB_ERROR_NOT_FOUND; + } + + /* Do the actual claim */ + kresult = (*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID)&cInterface->interface); + /* We no longer need the intermediate plug-in */ + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); + if (kresult || !cInterface->interface) { + usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* claim the interface */ + kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult)); + return darwin_to_libusb (kresult); + } + + /* update list of endpoints */ + kresult = get_endpoints (dev_handle, iface); + if (kresult) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + return kresult; + } + + cInterface->cfSource = NULL; + + /* create async event source */ + kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "could not create async event source"); + + /* can't continue without an async event source */ + (void)darwin_release_interface (dev_handle, iface); + + return darwin_to_libusb (kresult); + } + + /* add the cfSource to the async thread's run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + + usbi_dbg ("interface opened"); + + return 0; +} + +static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + IOReturn kresult; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + /* Check to see if an interface is open */ + if (!cInterface->interface) + return LIBUSB_SUCCESS; + + /* clean up endpoint data */ + cInterface->num_endpoints = 0; + + /* delete the interface's async event source */ + if (cInterface->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); + CFRelease (cInterface->cfSource); + } + + kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); + if (kresult) + usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); + + kresult = (*(cInterface->interface))->Release(cInterface->interface); + if (kresult != kIOReturnSuccess) + usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); + + cInterface->interface = (usb_interface_t **) IO_OBJECT_NULL; + + return darwin_to_libusb (kresult); +} + +static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) { + struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + IOReturn kresult; + + /* current interface */ + struct darwin_interface *cInterface = &priv->interfaces[iface]; + + if (!cInterface->interface) + return LIBUSB_ERROR_NO_DEVICE; + + kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting); + if (kresult != kIOReturnSuccess) + darwin_reset_device (dev_handle); + + /* update list of endpoints */ + kresult = get_endpoints (dev_handle, iface); + if (kresult) { + /* this should not happen */ + darwin_release_interface (dev_handle, iface); + usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); + return kresult; + } + + return darwin_to_libusb (kresult); +} + +static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) { + /* current interface */ + struct darwin_interface *cInterface; + IOReturn kresult; + uint8_t pipeRef; + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + /* newer versions of darwin support clearing additional bits on the device's endpoint */ + kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); + if (kresult) + usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_reset_device(struct libusb_device_handle *dev_handle) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + IOUSBDeviceDescriptor descriptor; + IOUSBConfigurationDescriptorPtr cached_configuration; + IOUSBConfigurationDescriptor configuration; + bool reenumerate = false; + IOReturn kresult; + int i; + + kresult = (*(dpriv->device))->ResetDevice (dpriv->device); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult)); + return darwin_to_libusb (kresult); + } + + do { + usbi_dbg ("darwin/reset_device: checking if device descriptor changed"); + + /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */ + (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor)); + + /* check if the device descriptor has changed */ + if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) { + reenumerate = true; + break; + } + + /* check if any configuration descriptor has changed */ + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i); + + (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration)); + (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + + if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) { + reenumerate = true; + break; + } + } + } while (0); + + if (reenumerate) { + usbi_dbg ("darwin/reset_device: device requires reenumeration"); + (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg ("darwin/reset_device: device reset complete"); + + return LIBUSB_SUCCESS; +} + +static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + io_service_t usbInterface; + CFTypeRef driver; + IOReturn kresult; + + kresult = darwin_get_interface (dpriv->device, interface, &usbInterface); + if (kresult) { + usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); + } + + driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0); + IOObjectRelease (usbInterface); + + if (driver) { + CFRelease (driver); + + return 1; + } + + /* no driver */ + return 0; +} + +/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */ +static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { + UNUSED(dev_handle); + UNUSED(interface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { + UNUSED(dev_handle); + UNUSED(interface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void darwin_destroy_device(struct libusb_device *dev) { + struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv; + + if (dpriv->dev) { + /* need to hold the lock in case this is the last reference to the device */ + usbi_mutex_lock(&darwin_cached_devices_lock); + darwin_deref_cached_device (dpriv->dev); + dpriv->dev = NULL; + usbi_mutex_unlock(&darwin_cached_devices_lock); + } +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + IOReturn ret; + uint8_t transferType; + /* None of the values below are used in libusbx for bulk transfers */ + uint8_t direction, number, interval, pipeRef; + uint16_t maxPacketSize; + + struct darwin_interface *cInterface; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, + &transferType, &maxPacketSize, &interval); + + if (ret) { + usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + return darwin_to_libusb (ret); + } + + if (0 != (transfer->length % maxPacketSize)) { + /* do not need a zero packet */ + transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET; + } + + /* submit the request */ + /* timeouts are unavailable on interrupt endpoints */ + if (transferType == kUSBInterrupt) { + if (IS_XFERIN(transfer)) + ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, darwin_async_io_callback, itransfer); + else + ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, darwin_async_io_callback, itransfer); + } else { + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); + else + ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); + } + + if (ret) + usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + + return darwin_to_libusb (ret); +} + +#if InterfaceVersion >= 550 +static int submit_stream_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_interface *cInterface; + uint8_t pipeRef; + IOReturn ret; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + if (IS_XFERIN(transfer)) + ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, + transfer->buffer, transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, (void *)itransfer); + else + ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, + transfer->buffer, transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, (void *)itransfer); + + if (ret) + usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(ret), ret); + + return darwin_to_libusb (ret); +} +#endif + +static int submit_iso_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + IOReturn kresult; + uint8_t direction, number, interval, pipeRef, transferType; + uint16_t maxPacketSize; + UInt64 frame; + AbsoluteTime atTime; + int i; + + struct darwin_interface *cInterface; + + /* construct an array of IOUSBIsocFrames, reuse the old one if possible */ + if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) { + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } + + if (!tpriv->isoc_framelist) { + tpriv->num_iso_packets = transfer->num_iso_packets; + tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame)); + if (!tpriv->isoc_framelist) + return LIBUSB_ERROR_NO_MEM; + } + + /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ + for (i = 0 ; i < transfer->num_iso_packets ; i++) + tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length; + + /* determine the interface/endpoint to use */ + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + /* determine the properties of this endpoint and the speed of the device */ + (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, + &transferType, &maxPacketSize, &interval); + + /* Last but not least we need the bus frame number */ + kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); + if (kresult) { + usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult); + free(tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + + return darwin_to_libusb (kresult); + } + + (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, + &transferType, &maxPacketSize, &interval); + + /* schedule for a frame a little in the future */ + frame += 4; + + if (cInterface->frames[transfer->endpoint] && frame < cInterface->frames[transfer->endpoint]) + frame = cInterface->frames[transfer->endpoint]; + + /* submit the request */ + if (IS_XFERIN(transfer)) + kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + else + kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); + + if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed) + /* Full speed */ + cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)); + else + /* High/super speed */ + cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8; + + if (kresult != kIOReturnSuccess) { + usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out", + darwin_error_str(kresult)); + free (tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } + + return darwin_to_libusb (kresult); +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + IOReturn kresult; + + memset(&tpriv->req, 0, sizeof(tpriv->req)); + + /* IOUSBDeviceInterface expects the request in cpu endianness */ + tpriv->req.bmRequestType = setup->bmRequestType; + tpriv->req.bRequest = setup->bRequest; + /* these values should be in bus order from libusb_fill_control_setup */ + tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue); + tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex); + tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength); + /* data is stored after the libusb control block */ + tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + tpriv->req.completionTimeout = transfer->timeout; + tpriv->req.noDataTimeout = transfer->timeout; + + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; + + /* all transfers in libusb-1.0 are async */ + + if (transfer->endpoint) { + struct darwin_interface *cInterface; + uint8_t pipeRef; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer); + } else + /* control request on endpoint 0 */ + kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); + + if (kresult != kIOReturnSuccess) + usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult)); + + return darwin_to_libusb (kresult); +} + +static int darwin_submit_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: +#if InterfaceVersion >= 550 + return submit_stream_transfer(itransfer); +#else + usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers"); + return LIBUSB_ERROR_NOT_SUPPORTED; +#endif + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int cancel_control_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + IOReturn kresult; + + usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe"); + + if (!dpriv->device) + return LIBUSB_ERROR_NO_DEVICE; + + kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device); + + return darwin_to_libusb (kresult); +} + +static int darwin_abort_transfers (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); + struct darwin_interface *cInterface; + uint8_t pipeRef, iface; + IOReturn kresult; + + if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) { + usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); + + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!dpriv->device) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef); + + /* abort transactions */ +#if InterfaceVersion >= 550 + if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type) + (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id); + else +#endif + (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef); + + usbi_dbg ("calling clear pipe stall to clear the data toggle bit"); + + /* newer versions of darwin support clearing additional bits on the device's endpoint */ + kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); + + return darwin_to_libusb (kresult); +} + +static int darwin_cancel_transfer(struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return cancel_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return darwin_abort_transfers (itransfer); + default: + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) { + free (tpriv->isoc_framelist); + tpriv->isoc_framelist = NULL; + } +} + +static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) { + struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon; + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + usbi_dbg ("an async io operation has completed"); + + /* if requested write a zero packet */ + if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + struct darwin_interface *cInterface; + uint8_t pipeRef; + + (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface); + + (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0); + } + + tpriv->result = result; + tpriv->size = (UInt32) (uintptr_t) arg0; + + /* signal the core that this transfer is complete */ + usbi_signal_transfer_completion(itransfer); +} + +static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) { + if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) + result = kIOUSBTransactionTimeout; + + switch (result) { + case kIOReturnUnderrun: + case kIOReturnSuccess: + return LIBUSB_TRANSFER_COMPLETED; + case kIOReturnAborted: + return LIBUSB_TRANSFER_CANCELLED; + case kIOUSBPipeStalled: + usbi_dbg ("transfer error: pipe is stalled"); + return LIBUSB_TRANSFER_STALL; + case kIOReturnOverrun: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun"); + return LIBUSB_TRANSFER_OVERFLOW; + case kIOUSBTransactionTimeout: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out"); + itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; + return LIBUSB_TRANSFER_TIMED_OUT; + default: + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); + return LIBUSB_TRANSFER_ERROR; + } +} + +static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; + int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type; + int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type; + int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type; + int i; + + if (!isIsoc && !isBulk && !isControl && !isInterrupt) { + usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg ("handling %s completion with kernel status %d", + isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result); + + if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) { + if (isIsoc && tpriv->isoc_framelist) { + /* copy isochronous results back */ + + for (i = 0; i < transfer->num_iso_packets ; i++) { + struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; + lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus); + lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; + } + } else if (!isIsoc) + itransfer->transferred += tpriv->size; + } + + /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */ + return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result)); +} + +static int darwin_clock_gettime(int clk_id, struct timespec *tp) { + mach_timespec_t sys_time; + clock_serv_t clock_ref; + + switch (clk_id) { + case USBI_CLOCK_REALTIME: + /* CLOCK_REALTIME represents time since the epoch */ + clock_ref = clock_realtime; + break; + case USBI_CLOCK_MONOTONIC: + /* use system boot time as reference for the monotonic clock */ + clock_ref = clock_monotonic; + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + clock_get_time (clock_ref, &sys_time); + + tp->tv_sec = sys_time.tv_sec; + tp->tv_nsec = sys_time.tv_nsec; + + return 0; +} + +#if InterfaceVersion >= 550 +static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, + int num_endpoints) { + struct darwin_interface *cInterface; + UInt32 supportsStreams; + uint8_t pipeRef; + int rc, i; + + /* find the mimimum number of supported streams on the endpoint list */ + for (i = 0 ; i < num_endpoints ; ++i) { + if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) { + return rc; + } + + (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams); + if (num_streams > supportsStreams) + num_streams = supportsStreams; + } + + /* it is an error if any endpoint in endpoints does not support streams */ + if (0 == num_streams) + return LIBUSB_ERROR_INVALID_PARAM; + + /* create the streams */ + for (i = 0 ; i < num_endpoints ; ++i) { + (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface); + + rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams); + if (kIOReturnSuccess != rc) + return darwin_to_libusb(rc); + } + + return num_streams; +} + +static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) { + struct darwin_interface *cInterface; + UInt32 supportsStreams; + uint8_t pipeRef; + int rc; + + for (int i = 0 ; i < num_endpoints ; ++i) { + if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) + return rc; + + (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams); + if (0 == supportsStreams) + return LIBUSB_ERROR_INVALID_PARAM; + + rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0); + if (kIOReturnSuccess != rc) + return darwin_to_libusb(rc); + } + + return LIBUSB_SUCCESS; +} +#endif + +const struct usbi_os_backend darwin_backend = { + .name = "Darwin", + .caps = 0, + .init = darwin_init, + .exit = darwin_exit, + .get_device_list = NULL, /* not needed */ + .get_device_descriptor = darwin_get_device_descriptor, + .get_active_config_descriptor = darwin_get_active_config_descriptor, + .get_config_descriptor = darwin_get_config_descriptor, + .hotplug_poll = darwin_hotplug_poll, + + .open = darwin_open, + .close = darwin_close, + .get_configuration = darwin_get_configuration, + .set_configuration = darwin_set_configuration, + .claim_interface = darwin_claim_interface, + .release_interface = darwin_release_interface, + + .set_interface_altsetting = darwin_set_interface_altsetting, + .clear_halt = darwin_clear_halt, + .reset_device = darwin_reset_device, + +#if InterfaceVersion >= 550 + .alloc_streams = darwin_alloc_streams, + .free_streams = darwin_free_streams, +#endif + + .kernel_driver_active = darwin_kernel_driver_active, + .detach_kernel_driver = darwin_detach_kernel_driver, + .attach_kernel_driver = darwin_attach_kernel_driver, + + .destroy_device = darwin_destroy_device, + + .submit_transfer = darwin_submit_transfer, + .cancel_transfer = darwin_cancel_transfer, + .clear_transfer_priv = darwin_clear_transfer_priv, + + .handle_transfer_completion = darwin_handle_transfer_completion, + + .clock_gettime = darwin_clock_gettime, + + .device_priv_size = sizeof(struct darwin_device_priv), + .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), + .transfer_priv_size = sizeof(struct darwin_transfer_priv), +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.h new file mode 100644 index 0000000..1180434 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.h @@ -0,0 +1,164 @@ +/* + * darwin backend for libusb 1.0 + * Copyright © 2008-2015 Nathan Hjelm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined(LIBUSB_DARWIN_H) +#define LIBUSB_DARWIN_H + +#include "libusbi.h" + +#include +#include +#include +#include + +/* IOUSBInterfaceInferface */ +#if defined (kIOUSBInterfaceInterfaceID700) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + +#define usb_interface_t IOUSBInterfaceInterface700 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700 +#define InterfaceVersion 700 + +#elif defined (kIOUSBInterfaceInterfaceID550) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + +#define usb_interface_t IOUSBInterfaceInterface550 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 +#define InterfaceVersion 550 + +#elif defined (kIOUSBInterfaceInterfaceID500) + +#define usb_interface_t IOUSBInterfaceInterface500 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 +#define InterfaceVersion 500 + +#elif defined (kIOUSBInterfaceInterfaceID300) + +#define usb_interface_t IOUSBInterfaceInterface300 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 +#define InterfaceVersion 300 + +#elif defined (kIOUSBInterfaceInterfaceID245) + +#define usb_interface_t IOUSBInterfaceInterface245 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 +#define InterfaceVersion 245 + +#elif defined (kIOUSBInterfaceInterfaceID220) + +#define usb_interface_t IOUSBInterfaceInterface220 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 +#define InterfaceVersion 220 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +/* IOUSBDeviceInterface */ +#if defined (kIOUSBDeviceInterfaceID500) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + +#define usb_device_t IOUSBDeviceInterface500 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID500 +#define DeviceVersion 500 + +#elif defined (kIOUSBDeviceInterfaceID320) + +#define usb_device_t IOUSBDeviceInterface320 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 +#define DeviceVersion 320 + +#elif defined (kIOUSBDeviceInterfaceID300) + +#define usb_device_t IOUSBDeviceInterface300 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 +#define DeviceVersion 300 + +#elif defined (kIOUSBDeviceInterfaceID245) + +#define usb_device_t IOUSBDeviceInterface245 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 +#define DeviceVersion 245 + +#elif defined (kIOUSBDeviceInterfaceID220) +#define usb_device_t IOUSBDeviceInterface197 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 +#define DeviceVersion 197 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +#if !defined(IO_OBJECT_NULL) +#define IO_OBJECT_NULL ((io_object_t) 0) +#endif + +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; +typedef IONotificationPortRef io_notification_port_t; + +/* private structures */ +struct darwin_cached_device { + struct list_head list; + IOUSBDeviceDescriptor dev_descriptor; + UInt32 location; + UInt64 parent_session; + UInt64 session; + UInt16 address; + char sys_path[21]; + usb_device_t **device; + int open_count; + UInt8 first_config, active_config, port; + int can_enumerate; + int refcount; +}; + +struct darwin_device_priv { + struct darwin_cached_device *dev; +}; + +struct darwin_device_handle_priv { + int is_open; + CFRunLoopSourceRef cfSource; + + struct darwin_interface { + usb_interface_t **interface; + uint8_t num_endpoints; + CFRunLoopSourceRef cfSource; + uint64_t frames[256]; + uint8_t endpoint_addrs[USB_MAXENDPOINTS]; + } interfaces[USB_MAXINTERFACES]; +}; + +struct darwin_transfer_priv { + /* Isoc */ + IOUSBIsocFrame *isoc_framelist; + int num_iso_packets; + + /* Control */ + IOUSBDevRequestTO req; + + /* Bulk */ + + /* Completion status */ + IOReturn result; + UInt32 size; +}; + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_pollfs.cpp b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_pollfs.cpp new file mode 100644 index 0000000..e0c7713 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_pollfs.cpp @@ -0,0 +1,367 @@ +/* + * Copyright 2007-2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "haiku_usb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class WatchedEntry { +public: + WatchedEntry(BMessenger *, entry_ref *); + ~WatchedEntry(); + bool EntryCreated(entry_ref *ref); + bool EntryRemoved(ino_t node); + bool InitCheck(); + +private: + BMessenger* fMessenger; + node_ref fNode; + bool fIsDirectory; + USBDevice* fDevice; + WatchedEntry* fEntries; + WatchedEntry* fLink; + bool fInitCheck; +}; + + +class RosterLooper : public BLooper { +public: + RosterLooper(USBRoster *); + void Stop(); + virtual void MessageReceived(BMessage *); + bool InitCheck(); + +private: + USBRoster* fRoster; + WatchedEntry* fRoot; + BMessenger* fMessenger; + bool fInitCheck; +}; + + +WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref) + : fMessenger(messenger), + fIsDirectory(false), + fDevice(NULL), + fEntries(NULL), + fLink(NULL), + fInitCheck(false) +{ + BEntry entry(ref); + entry.GetNodeRef(&fNode); + + BDirectory directory; + if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) { + fIsDirectory = true; + + while (directory.GetNextEntry(&entry) >= B_OK) { + if (entry.GetRef(ref) < B_OK) + continue; + + WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); + if (child == NULL) + continue; + if (child->InitCheck() == false) { + delete child; + continue; + } + + child->fLink = fEntries; + fEntries = child; + } + + watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); + } + else { + if (strncmp(ref->name, "raw", 3) == 0) + return; + + BPath path, parent_path; + entry.GetPath(&path); + fDevice = new(std::nothrow) USBDevice(path.Path()); + if (fDevice != NULL && fDevice->InitCheck() == true) { + // Add this new device to each active context's device list + struct libusb_context *ctx; + unsigned long session_id = (unsigned long)&fDevice; + + usbi_mutex_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_dbg("using previously allocated device with location %lu", session_id); + libusb_unref_device(dev); + continue; + } + usbi_dbg("allocating new device with location %lu", session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) { + usbi_dbg("device allocation failed"); + continue; + } + *((USBDevice **)dev->os_priv) = fDevice; + + // Calculate pseudo-device-address + int addr, tmp; + if (strcmp(path.Leaf(), "hub") == 0) + tmp = 100; //Random Number + else + sscanf(path.Leaf(), "%d", &tmp); + addr = tmp + 1; + path.GetParent(&parent_path); + while (strcmp(parent_path.Leaf(), "usb") != 0) { + sscanf(parent_path.Leaf(), "%d", &tmp); + addr += tmp + 1; + parent_path.GetParent(&parent_path); + } + sscanf(path.Path(), "/dev/bus/usb/%d", &dev->bus_number); + dev->device_address = addr - (dev->bus_number + 1); + + if (usbi_sanitize_device(dev) < 0) { + usbi_dbg("device sanitization failed"); + libusb_unref_device(dev); + continue; + } + usbi_connect_device(dev); + } + usbi_mutex_unlock(&active_contexts_lock); + } + else if (fDevice) { + delete fDevice; + fDevice = NULL; + return; + } + } + fInitCheck = true; +} + + +WatchedEntry::~WatchedEntry() +{ + if (fIsDirectory) { + watch_node(&fNode, B_STOP_WATCHING, *fMessenger); + + WatchedEntry *child = fEntries; + while (child) { + WatchedEntry *next = child->fLink; + delete child; + child = next; + } + } + + if (fDevice) { + // Remove this device from each active context's device list + struct libusb_context *ctx; + struct libusb_device *dev; + unsigned long session_id = (unsigned long)&fDevice; + + usbi_mutex_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev != NULL) { + usbi_disconnect_device(dev); + libusb_unref_device(dev); + } else { + usbi_dbg("device with location %lu not found", session_id); + } + } + usbi_mutex_static_unlock(&active_contexts_lock); + delete fDevice; + } +} + + +bool +WatchedEntry::EntryCreated(entry_ref *ref) +{ + if (!fIsDirectory) + return false; + + if (ref->directory != fNode.node) { + WatchedEntry *child = fEntries; + while (child) { + if (child->EntryCreated(ref)) + return true; + child = child->fLink; + } + return false; + } + + WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); + if (child == NULL) + return false; + child->fLink = fEntries; + fEntries = child; + return true; +} + + +bool +WatchedEntry::EntryRemoved(ino_t node) +{ + if (!fIsDirectory) + return false; + + WatchedEntry *child = fEntries; + WatchedEntry *lastChild = NULL; + while (child) { + if (child->fNode.node == node) { + if (lastChild) + lastChild->fLink = child->fLink; + else + fEntries = child->fLink; + delete child; + return true; + } + + if (child->EntryRemoved(node)) + return true; + + lastChild = child; + child = child->fLink; + } + return false; +} + + +bool +WatchedEntry::InitCheck() +{ + return fInitCheck; +} + + +RosterLooper::RosterLooper(USBRoster *roster) + : BLooper("LibusbRoster Looper"), + fRoster(roster), + fRoot(NULL), + fMessenger(NULL), + fInitCheck(false) +{ + BEntry entry("/dev/bus/usb"); + if (!entry.Exists()) { + usbi_err(NULL, "usb_raw not published"); + return; + } + + Run(); + fMessenger = new(std::nothrow) BMessenger(this); + if (fMessenger == NULL) { + usbi_err(NULL, "error creating BMessenger object"); + return; + } + + if (Lock()) { + entry_ref ref; + entry.GetRef(&ref); + fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref); + Unlock(); + if (fRoot == NULL) + return; + if (fRoot->InitCheck() == false) { + delete fRoot; + fRoot = NULL; + return; + } + } + fInitCheck = true; +} + + +void +RosterLooper::Stop() +{ + Lock(); + delete fRoot; + delete fMessenger; + Quit(); +} + + +void +RosterLooper::MessageReceived(BMessage *message) +{ + int32 opcode; + if (message->FindInt32("opcode", &opcode) < B_OK) + return; + + switch (opcode) { + case B_ENTRY_CREATED: + { + dev_t device; + ino_t directory; + const char *name; + if (message->FindInt32("device", &device) < B_OK || + message->FindInt64("directory", &directory) < B_OK || + message->FindString("name", &name) < B_OK) + break; + + entry_ref ref(device, directory, name); + fRoot->EntryCreated(&ref); + break; + } + case B_ENTRY_REMOVED: + { + ino_t node; + if (message->FindInt64("node", &node) < B_OK) + break; + fRoot->EntryRemoved(node); + break; + } + } +} + + +bool +RosterLooper::InitCheck() +{ + return fInitCheck; +} + + +USBRoster::USBRoster() + : fLooper(NULL) +{ +} + + +USBRoster::~USBRoster() +{ + Stop(); +} + + +int +USBRoster::Start() +{ + if (fLooper == NULL) { + fLooper = new(std::nothrow) RosterLooper(this); + if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) { + if (fLooper) + fLooper = NULL; + return LIBUSB_ERROR_OTHER; + } + } + return LIBUSB_SUCCESS; +} + + +void +USBRoster::Stop() +{ + if (fLooper) { + ((RosterLooper *)fLooper)->Stop(); + fLooper = NULL; + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb.h new file mode 100644 index 0000000..d51ae9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb.h @@ -0,0 +1,112 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "libusbi.h" +#include "haiku_usb_raw.h" + +using namespace std; + +class USBDevice; +class USBDeviceHandle; +class USBTransfer; + +class USBDevice { +public: + USBDevice(const char *); + virtual ~USBDevice(); + const char* Location() const; + uint8 CountConfigurations() const; + const usb_device_descriptor* Descriptor() const; + const usb_configuration_descriptor* ConfigurationDescriptor(uint32) const; + const usb_configuration_descriptor* ActiveConfiguration() const; + uint8 EndpointToIndex(uint8) const; + uint8 EndpointToInterface(uint8) const; + int ClaimInterface(int); + int ReleaseInterface(int); + int CheckInterfacesFree(int); + int SetActiveConfiguration(int); + int ActiveConfigurationIndex() const; + bool InitCheck(); +private: + int Initialise(); + unsigned int fClaimedInterfaces; // Max Interfaces can be 32. Using a bitmask + usb_device_descriptor fDeviceDescriptor; + unsigned char** fConfigurationDescriptors; + int fActiveConfiguration; + char* fPath; + map fConfigToIndex; + map* fEndpointToIndex; + map* fEndpointToInterface; + bool fInitCheck; +}; + +class USBDeviceHandle { +public: + USBDeviceHandle(USBDevice *dev); + virtual ~USBDeviceHandle(); + int ClaimInterface(int); + int ReleaseInterface(int); + int SetConfiguration(int); + int SetAltSetting(int, int); + status_t SubmitTransfer(struct usbi_transfer *); + status_t CancelTransfer(USBTransfer *); + bool InitCheck(); +private: + int fRawFD; + static status_t TransfersThread(void *); + void TransfersWorker(); + USBDevice* fUSBDevice; + unsigned int fClaimedInterfaces; + BList fTransfers; + BLocker fTransfersLock; + sem_id fTransfersSem; + thread_id fTransfersThread; + bool fInitCheck; +}; + +class USBTransfer { +public: + USBTransfer(struct usbi_transfer *, USBDevice *); + virtual ~USBTransfer(); + void Do(int); + struct usbi_transfer* UsbiTransfer(); + void SetCancelled(); + bool IsCancelled(); +private: + struct usbi_transfer* fUsbiTransfer; + struct libusb_transfer* fLibusbTransfer; + USBDevice* fUSBDevice; + BLocker fStatusLock; + bool fCancelled; +}; + +class USBRoster { +public: + USBRoster(); + virtual ~USBRoster(); + int Start(); + void Stop(); +private: + void* fLooper; +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_backend.cpp b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_backend.cpp new file mode 100644 index 0000000..d3de8cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_backend.cpp @@ -0,0 +1,517 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include + +#include "haiku_usb.h" + +int _errno_to_libusb(int status) +{ + return status; +} + +USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device) +{ + fUsbiTransfer = itransfer; + fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + fUSBDevice = device; + fCancelled = false; +} + +USBTransfer::~USBTransfer() +{ +} + +struct usbi_transfer * +USBTransfer::UsbiTransfer() +{ + return fUsbiTransfer; +} + +void +USBTransfer::SetCancelled() +{ + fCancelled = true; +} + +bool +USBTransfer::IsCancelled() +{ + return fCancelled; +} + +void +USBTransfer::Do(int fRawFD) +{ + switch (fLibusbTransfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + { + struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer; + usb_raw_command command; + command.control.request_type = setup->bmRequestType; + command.control.request = setup->bRequest; + command.control.value = setup->wValue; + command.control.index = setup->wIndex; + command.control.length = setup->wLength; + command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + if (fCancelled) + break; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || + command.control.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer"); + break; + } + fUsbiTransfer->transferred = command.control.length; + } + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + { + usb_raw_command command; + command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); + command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); + command.transfer.data = fLibusbTransfer->buffer; + command.transfer.length = fLibusbTransfer->length; + if (fCancelled) + break; + if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) { + if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) || + command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer"); + break; + } + } + else { + if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) || + command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer"); + break; + } + } + fUsbiTransfer->transferred = command.transfer.length; + } + break; + // IsochronousTransfers not tested + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + { + usb_raw_command command; + command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); + command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); + command.isochronous.data = fLibusbTransfer->buffer; + command.isochronous.length = fLibusbTransfer->length; + command.isochronous.packet_count = fLibusbTransfer->num_iso_packets; + int i; + usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets]; + for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { + if ((int16)(fLibusbTransfer->iso_packet_desc[i]).length != (fLibusbTransfer->iso_packet_desc[i]).length) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); + break; + } + packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length; + } + if (i < fLibusbTransfer->num_iso_packets) + break; // TODO Handle this error + command.isochronous.packet_descriptors = packetDescriptors; + if (fCancelled) + break; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) || + command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) { + fUsbiTransfer->transferred = -1; + usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); + break; + } + for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { + (fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length; + switch (packetDescriptors[i].status) { + case B_OK: + (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED; + break; + default: + (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR; + break; + } + } + delete[] packetDescriptors; + // Do we put the length of transfer here, for isochronous transfers? + fUsbiTransfer->transferred = command.transfer.length; + } + break; + default: + usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer"); + } +} + +bool +USBDeviceHandle::InitCheck() +{ + return fInitCheck; +} + +status_t +USBDeviceHandle::TransfersThread(void *self) +{ + USBDeviceHandle *handle = (USBDeviceHandle *)self; + handle->TransfersWorker(); + return B_OK; +} + +void +USBDeviceHandle::TransfersWorker() +{ + while (true) { + status_t status = acquire_sem(fTransfersSem); + if (status == B_BAD_SEM_ID) + break; + if (status == B_INTERRUPTED) + continue; + fTransfersLock.Lock(); + USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0); + fTransfersLock.Unlock(); + fPendingTransfer->Do(fRawFD); + usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer()); + } +} + +status_t +USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer) +{ + USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice); + *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = transfer; + BAutolock locker(fTransfersLock); + fTransfers.AddItem(transfer); + release_sem(fTransfersSem); + return LIBUSB_SUCCESS; +} + +status_t +USBDeviceHandle::CancelTransfer(USBTransfer *transfer) +{ + transfer->SetCancelled(); + fTransfersLock.Lock(); + bool removed = fTransfers.RemoveItem(transfer); + fTransfersLock.Unlock(); + if(removed) + usbi_signal_transfer_completion(transfer->UsbiTransfer()); + return LIBUSB_SUCCESS; +} + +USBDeviceHandle::USBDeviceHandle(USBDevice *dev) + : + fTransfersThread(-1), + fUSBDevice(dev), + fClaimedInterfaces(0), + fInitCheck(false) +{ + fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC); + if (fRawFD < 0) { + usbi_err(NULL,"failed to open device"); + return; + } + fTransfersSem = create_sem(0, "Transfers Queue Sem"); + fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this); + resume_thread(fTransfersThread); + fInitCheck = true; +} + +USBDeviceHandle::~USBDeviceHandle() +{ + if (fRawFD > 0) + close(fRawFD); + for(int i = 0; i < 32; i++) { + if (fClaimedInterfaces & (1 << i)) + ReleaseInterface(i); + } + delete_sem(fTransfersSem); + if (fTransfersThread > 0) + wait_for_thread(fTransfersThread, NULL); +} + +int +USBDeviceHandle::ClaimInterface(int inumber) +{ + int status = fUSBDevice->ClaimInterface(inumber); + if (status == LIBUSB_SUCCESS) + fClaimedInterfaces |= (1 << inumber); + return status; +} + +int +USBDeviceHandle::ReleaseInterface(int inumber) +{ + fUSBDevice->ReleaseInterface(inumber); + fClaimedInterfaces &= ~(1 << inumber); + return LIBUSB_SUCCESS; +} + +int +USBDeviceHandle::SetConfiguration(int config) +{ + int config_index = fUSBDevice->CheckInterfacesFree(config); + if(config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND) + return config_index; + usb_raw_command command; + command.config.config_index = config_index; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + return _errno_to_libusb(command.config.status); + } + fUSBDevice->SetActiveConfiguration(config_index); + return LIBUSB_SUCCESS; +} + +int +USBDeviceHandle::SetAltSetting(int inumber, int alt) +{ + usb_raw_command command; + command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex(); + command.alternate.interface_index = inumber; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) || + command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "Error retrieving active alternate interface"); + return _errno_to_libusb(command.alternate.status); + } + if (command.alternate.alternate_info == alt) { + usbi_dbg("Setting alternate interface successful"); + return LIBUSB_SUCCESS; + } + command.alternate.alternate_info = alt; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) || + command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISONNECTED PROBABLY + usbi_err(NULL, "Error setting alternate interface"); + return _errno_to_libusb(command.alternate.status); + } + usbi_dbg("Setting alternate interface successful"); + return LIBUSB_SUCCESS; +} + + +USBDevice::USBDevice(const char *path) + : + fPath(NULL), + fActiveConfiguration(0), //0? + fConfigurationDescriptors(NULL), + fClaimedInterfaces(0), + fEndpointToIndex(NULL), + fEndpointToInterface(NULL), + fInitCheck(false) +{ + fPath=strdup(path); + Initialise(); +} + +USBDevice::~USBDevice() +{ + free(fPath); + if (fConfigurationDescriptors) { + for(int i = 0; i < fDeviceDescriptor.num_configurations; i++) { + if (fConfigurationDescriptors[i]) + delete fConfigurationDescriptors[i]; + } + delete[] fConfigurationDescriptors; + } + if (fEndpointToIndex) + delete[] fEndpointToIndex; + if (fEndpointToInterface) + delete[] fEndpointToInterface; +} + +bool +USBDevice::InitCheck() +{ + return fInitCheck; +} + +const char * +USBDevice::Location() const +{ + return fPath; +} + +uint8 +USBDevice::CountConfigurations() const +{ + return fDeviceDescriptor.num_configurations; +} + +const usb_device_descriptor * +USBDevice::Descriptor() const +{ + return &fDeviceDescriptor; +} + +const usb_configuration_descriptor * +USBDevice::ConfigurationDescriptor(uint32 index) const +{ + if (index > CountConfigurations()) + return NULL; + return (usb_configuration_descriptor *) fConfigurationDescriptors[index]; +} + +const usb_configuration_descriptor * +USBDevice::ActiveConfiguration() const +{ + return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration]; +} + +int +USBDevice::ActiveConfigurationIndex() const +{ + return fActiveConfiguration; +} + +int USBDevice::ClaimInterface(int interface) +{ + if (interface > ActiveConfiguration()->number_interfaces) + return LIBUSB_ERROR_NOT_FOUND; + if (fClaimedInterfaces & (1 << interface)) + return LIBUSB_ERROR_BUSY; + fClaimedInterfaces |= (1 << interface); + return LIBUSB_SUCCESS; +} + +int USBDevice::ReleaseInterface(int interface) +{ + fClaimedInterfaces &= ~(1 << interface); + return LIBUSB_SUCCESS; +} + +int +USBDevice::CheckInterfacesFree(int config) +{ + if (fConfigToIndex.count(config) == 0) + return LIBUSB_ERROR_NOT_FOUND; + if (fClaimedInterfaces == 0) + return fConfigToIndex[(uint8)config]; + return LIBUSB_ERROR_BUSY; +} + +int +USBDevice::SetActiveConfiguration(int config_index) +{ + fActiveConfiguration = config_index; + return LIBUSB_SUCCESS; +} + +uint8 +USBDevice::EndpointToIndex(uint8 address) const +{ + return fEndpointToIndex[fActiveConfiguration][address]; +} + +uint8 +USBDevice::EndpointToInterface(uint8 address) const +{ + return fEndpointToInterface[fActiveConfiguration][address]; +} + +int +USBDevice::Initialise() //Do we need more error checking, etc? How to report? +{ + int fRawFD = open(fPath, O_RDWR | O_CLOEXEC); + if (fRawFD < 0) + return B_ERROR; + usb_raw_command command; + command.device.descriptor = &fDeviceDescriptor; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) || + command.device.status != B_USB_RAW_STATUS_SUCCESS) { + close(fRawFD); + return B_ERROR; + } + + fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations]; + fEndpointToIndex = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; + fEndpointToInterface = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; + for (int i = 0; i < fDeviceDescriptor.num_configurations; i++) { + usb_configuration_descriptor tmp_config; + command.config.descriptor = &tmp_config; + command.config.config_index = i; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving configuration descriptor"); + close(fRawFD); + return B_ERROR; + } + fConfigToIndex[tmp_config.configuration_value] = i; + fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length]; + command.control.request_type = 128; + command.control.request = 6; + command.control.value = (2 << 8) | i; + command.control.index = 0; + command.control.length = tmp_config.total_length; + command.control.data = fConfigurationDescriptors[i]; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || + command.control.status!=B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving full configuration descriptor"); + close(fRawFD); + return B_ERROR; + } + for (int j = 0; j < tmp_config.number_interfaces; j++) { + command.alternate.config_index = i; + command.alternate.interface_index = j; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving number of alternate interfaces"); + close(fRawFD); + return B_ERROR; + } + int num_alternate = command.alternate.alternate_info; + for (int k = 0; k < num_alternate; k++) { + usb_interface_descriptor tmp_interface; + command.interface_etc.config_index = i; + command.interface_etc.interface_index = j; + command.interface_etc.alternate_index = k; + command.interface_etc.descriptor = &tmp_interface; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving interface descriptor"); + close(fRawFD); + return B_ERROR; + } + for (int l = 0; l < tmp_interface.num_endpoints; l++) { + usb_endpoint_descriptor tmp_endpoint; + command.endpoint_etc.config_index = i; + command.endpoint_etc.interface_index = j; + command.endpoint_etc.alternate_index = k; + command.endpoint_etc.endpoint_index = l; + command.endpoint_etc.descriptor = &tmp_endpoint; + if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) || + command.config.status != B_USB_RAW_STATUS_SUCCESS) { + usbi_err(NULL, "failed retrieving endpoint descriptor"); + close(fRawFD); + return B_ERROR; + } + fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l; + fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j; + } + } + } + } + close(fRawFD); + fInitCheck = true; + return B_OK; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.cpp b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.cpp new file mode 100644 index 0000000..77adbd1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.cpp @@ -0,0 +1,250 @@ +/* + * Haiku Backend for libusb + * Copyright © 2014 Akshay Jaggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include + +#include "haiku_usb.h" + +USBRoster gUsbRoster; +int32 gInitCount = 0; + +static int +haiku_init(struct libusb_context *ctx) +{ + if (atomic_add(&gInitCount, 1) == 0) + return gUsbRoster.Start(); + return LIBUSB_SUCCESS; +} + +static void +haiku_exit(void) +{ + if (atomic_add(&gInitCount, -1) == 1) + gUsbRoster.Stop(); +} + +static int +haiku_open(struct libusb_device_handle *dev_handle) +{ + USBDevice *dev = *((USBDevice **)dev_handle->dev->os_priv); + USBDeviceHandle *handle = new(std::nothrow) USBDeviceHandle(dev); + if (handle == NULL) + return LIBUSB_ERROR_NO_MEM; + if (handle->InitCheck() == false) { + delete handle; + return LIBUSB_ERROR_NO_DEVICE; + } + *((USBDeviceHandle **)dev_handle->os_priv) = handle; + return LIBUSB_SUCCESS; +} + +static void +haiku_close(struct libusb_device_handle *dev_handle) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); + if (handle == NULL) + return; + delete handle; + *((USBDeviceHandle **)dev_handle->os_priv) = NULL; +} + +static int +haiku_get_device_descriptor(struct libusb_device *device, unsigned char *buffer, int *host_endian) +{ + USBDevice *dev = *((USBDevice **)device->os_priv); + memcpy(buffer, dev->Descriptor(), DEVICE_DESC_LENGTH); + *host_endian = 0; + return LIBUSB_SUCCESS; +} + +static int +haiku_get_active_config_descriptor(struct libusb_device *device, unsigned char *buffer, size_t len, int *host_endian) +{ + USBDevice *dev = *((USBDevice **)device->os_priv); + const usb_configuration_descriptor *act_config = dev->ActiveConfiguration(); + if (len > act_config->total_length) + return LIBUSB_ERROR_OVERFLOW; + memcpy(buffer, act_config, len); + *host_endian = 0; + return LIBUSB_SUCCESS; +} + +static int +haiku_get_config_descriptor(struct libusb_device *device, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + USBDevice *dev = *((USBDevice **)device->os_priv); + const usb_configuration_descriptor *config = dev->ConfigurationDescriptor(config_index); + if (config == NULL) { + usbi_err(DEVICE_CTX(device), "failed getting configuration descriptor"); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (len > config->total_length) + len = config->total_length; + memcpy(buffer, config, len); + *host_endian = 0; + return len; +} + +static int +haiku_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + USBDeviceHandle *handle= *((USBDeviceHandle **)dev_handle->os_priv); + return handle->SetConfiguration(config); +} + +static int +haiku_claim_interface(struct libusb_device_handle *dev_handle, int interface_number) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); + return handle->ClaimInterface(interface_number); +} + +static int +haiku_set_altsetting(struct libusb_device_handle *dev_handle, int interface_number, int altsetting) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); + return handle->SetAltSetting(interface_number, altsetting); +} + +static int +haiku_release_interface(struct libusb_device_handle *dev_handle, int interface_number) +{ + USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); + haiku_set_altsetting(dev_handle,interface_number, 0); + return handle->ReleaseInterface(interface_number); +} + +static int +haiku_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv); + return fDeviceHandle->SubmitTransfer(itransfer); +} + +static int +haiku_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv); + return fDeviceHandle->CancelTransfer(*((USBTransfer **)usbi_transfer_get_os_priv(itransfer))); +} + +static void +haiku_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)); + delete transfer; + *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; +} + +static int +haiku_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)); + + usbi_mutex_lock(&itransfer->lock); + if (transfer->IsCancelled()) { + delete transfer; + *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; + usbi_mutex_unlock(&itransfer->lock); + if (itransfer->transferred < 0) + itransfer->transferred = 0; + return usbi_handle_transfer_cancellation(itransfer); + } + libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; + if (itransfer->transferred < 0) { + usbi_err(ITRANSFER_CTX(itransfer), "error in transfer"); + status = LIBUSB_TRANSFER_ERROR; + itransfer->transferred = 0; + } + delete transfer; + *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); +} + +static int +haiku_clock_gettime(int clkid, struct timespec *tp) +{ + if (clkid == USBI_CLOCK_REALTIME) + return clock_gettime(CLOCK_REALTIME, tp); + if (clkid == USBI_CLOCK_MONOTONIC) + return clock_gettime(CLOCK_MONOTONIC, tp); + return LIBUSB_ERROR_INVALID_PARAM; +} + +const struct usbi_os_backend haiku_usb_raw_backend = { + /*.name =*/ "Haiku usbfs", + /*.caps =*/ 0, + /*.init =*/ haiku_init, + /*.exit =*/ haiku_exit, + /*.get_device_list =*/ NULL, + /*.hotplug_poll =*/ NULL, + /*.open =*/ haiku_open, + /*.close =*/ haiku_close, + /*.get_device_descriptor =*/ haiku_get_device_descriptor, + /*.get_active_config_descriptor =*/ haiku_get_active_config_descriptor, + /*.get_config_descriptor =*/ haiku_get_config_descriptor, + /*.get_config_descriptor_by_value =*/ NULL, + + + /*.get_configuration =*/ NULL, + /*.set_configuration =*/ haiku_set_configuration, + /*.claim_interface =*/ haiku_claim_interface, + /*.release_interface =*/ haiku_release_interface, + + /*.set_interface_altsetting =*/ haiku_set_altsetting, + /*.clear_halt =*/ NULL, + /*.reset_device =*/ NULL, + + /*.alloc_streams =*/ NULL, + /*.free_streams =*/ NULL, + + /*.dev_mem_alloc =*/ NULL, + /*.dev_mem_free =*/ NULL, + + /*.kernel_driver_active =*/ NULL, + /*.detach_kernel_driver =*/ NULL, + /*.attach_kernel_driver =*/ NULL, + + /*.destroy_device =*/ NULL, + + /*.submit_transfer =*/ haiku_submit_transfer, + /*.cancel_transfer =*/ haiku_cancel_transfer, + /*.clear_transfer_priv =*/ haiku_clear_transfer_priv, + + /*.handle_events =*/ NULL, + /*.handle_transfer_completion =*/ haiku_handle_transfer_completion, + + /*.clock_gettime =*/ haiku_clock_gettime, + +#ifdef USBI_TIMERFD_AVAILABLE + /*.get_timerfd_clockid =*/ NULL, +#endif + + /*.device_priv_size =*/ sizeof(USBDevice *), + /*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *), + /*.transfer_priv_size =*/ sizeof(USBTransfer *), +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.h new file mode 100644 index 0000000..5baf53d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.h @@ -0,0 +1,180 @@ +/* + * Copyright 2006-2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef _USB_RAW_H_ +#define _USB_RAW_H_ + +#include + +#define B_USB_RAW_PROTOCOL_VERSION 0x0015 +#define B_USB_RAW_ACTIVE_ALTERNATE 0xffffffff + +typedef enum { + B_USB_RAW_COMMAND_GET_VERSION = 0x1000, + + B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR = 0x2000, + B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_STRING_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR, + B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, + B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, + B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, + B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, + B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR_ETC, + + B_USB_RAW_COMMAND_SET_CONFIGURATION = 0x3000, + B_USB_RAW_COMMAND_SET_FEATURE, + B_USB_RAW_COMMAND_CLEAR_FEATURE, + B_USB_RAW_COMMAND_GET_STATUS, + B_USB_RAW_COMMAND_GET_DESCRIPTOR, + B_USB_RAW_COMMAND_SET_ALT_INTERFACE, + + B_USB_RAW_COMMAND_CONTROL_TRANSFER = 0x4000, + B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, + B_USB_RAW_COMMAND_BULK_TRANSFER, + B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER +} usb_raw_command_id; + + +typedef enum { + B_USB_RAW_STATUS_SUCCESS = 0, + + B_USB_RAW_STATUS_FAILED, + B_USB_RAW_STATUS_ABORTED, + B_USB_RAW_STATUS_STALLED, + B_USB_RAW_STATUS_CRC_ERROR, + B_USB_RAW_STATUS_TIMEOUT, + + B_USB_RAW_STATUS_INVALID_CONFIGURATION, + B_USB_RAW_STATUS_INVALID_INTERFACE, + B_USB_RAW_STATUS_INVALID_ENDPOINT, + B_USB_RAW_STATUS_INVALID_STRING, + + B_USB_RAW_STATUS_NO_MEMORY +} usb_raw_command_status; + + +typedef union { + struct { + status_t status; + } version; + + struct { + status_t status; + usb_device_descriptor *descriptor; + } device; + + struct { + status_t status; + usb_configuration_descriptor *descriptor; + uint32 config_index; + } config; + + struct { + status_t status; + uint32 alternate_info; + uint32 config_index; + uint32 interface_index; + } alternate; + + struct { + status_t status; + usb_interface_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + } interface; + + struct { + status_t status; + usb_interface_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + } interface_etc; + + struct { + status_t status; + usb_endpoint_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 endpoint_index; + } endpoint; + + struct { + status_t status; + usb_endpoint_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + uint32 endpoint_index; + } endpoint_etc; + + struct { + status_t status; + usb_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 generic_index; + size_t length; + } generic; + + struct { + status_t status; + usb_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 alternate_index; + uint32 generic_index; + size_t length; + } generic_etc; + + struct { + status_t status; + usb_string_descriptor *descriptor; + uint32 string_index; + size_t length; + } string; + + struct { + status_t status; + uint8 type; + uint8 index; + uint16 language_id; + void *data; + size_t length; + } descriptor; + + struct { + status_t status; + uint8 request_type; + uint8 request; + uint16 value; + uint16 index; + uint16 length; + void *data; + } control; + + struct { + status_t status; + uint32 interface; + uint32 endpoint; + void *data; + size_t length; + } transfer; + + struct { + status_t status; + uint32 interface; + uint32 endpoint; + void *data; + size_t length; + usb_iso_packet_descriptor *packet_descriptors; + uint32 packet_count; + } isochronous; +} usb_raw_command; + +#endif // _USB_RAW_H_ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_netlink.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_netlink.c new file mode 100644 index 0000000..60cf3ad --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_netlink.c @@ -0,0 +1,400 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * Copyright (c) 2013 Nathan Hjelm + * Copyright (c) 2016 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ASM_TYPES_H +#include +#endif + +#include +#include + +#include "libusbi.h" +#include "linux_usbfs.h" + +#define NL_GROUP_KERNEL 1 + +static int linux_netlink_socket = -1; +static int netlink_control_pipe[2] = { -1, -1 }; +static pthread_t libusb_linux_event_thread; + +static void *linux_netlink_event_thread_main(void *arg); + +static int set_fd_cloexec_nb(int fd) +{ + int flags; + +#if defined(FD_CLOEXEC) + flags = fcntl(fd, F_GETFD); + if (flags == -1) { + usbi_err(NULL, "failed to get netlink fd flags (%d)", errno); + return -1; + } + + if (!(flags & FD_CLOEXEC)) { + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + usbi_err(NULL, "failed to set netlink fd flags (%d)", errno); + return -1; + } + } +#endif + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno); + return -1; + } + + if (!(flags & O_NONBLOCK)) { + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + usbi_err(NULL, "failed to set netlink fd status flags (%d)", errno); + return -1; + } + } + + return 0; +} + +int linux_netlink_start_event_monitor(void) +{ + struct sockaddr_nl sa_nl = { .nl_family = AF_NETLINK, .nl_groups = NL_GROUP_KERNEL }; + int socktype = SOCK_RAW; + int opt = 1; + int ret; + +#if defined(SOCK_CLOEXEC) + socktype |= SOCK_CLOEXEC; +#endif +#if defined(SOCK_NONBLOCK) + socktype |= SOCK_NONBLOCK; +#endif + + linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT); + if (linux_netlink_socket == -1 && errno == EINVAL) { + usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype); + linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); + } + + if (linux_netlink_socket == -1) { + usbi_err(NULL, "failed to create netlink socket (%d)", errno); + goto err; + } + + ret = set_fd_cloexec_nb(linux_netlink_socket); + if (ret == -1) + goto err_close_socket; + + ret = bind(linux_netlink_socket, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); + if (ret == -1) { + usbi_err(NULL, "failed to bind netlink socket (%d)", errno); + goto err_close_socket; + } + + ret = setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); + if (ret == -1) { + usbi_err(NULL, "failed to set netlink socket SO_PASSCRED option (%d)", errno); + goto err_close_socket; + } + + ret = usbi_pipe(netlink_control_pipe); + if (ret) { + usbi_err(NULL, "failed to create netlink control pipe"); + goto err_close_socket; + } + + ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL); + if (ret != 0) { + usbi_err(NULL, "failed to create netlink event thread (%d)", ret); + goto err_close_pipe; + } + + return LIBUSB_SUCCESS; + +err_close_pipe: + close(netlink_control_pipe[0]); + close(netlink_control_pipe[1]); + netlink_control_pipe[0] = -1; + netlink_control_pipe[1] = -1; +err_close_socket: + close(linux_netlink_socket); + linux_netlink_socket = -1; +err: + return LIBUSB_ERROR_OTHER; +} + +int linux_netlink_stop_event_monitor(void) +{ + char dummy = 1; + ssize_t r; + + assert(linux_netlink_socket != -1); + + /* Write some dummy data to the control pipe and + * wait for the thread to exit */ + r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy)); + if (r <= 0) + usbi_warn(NULL, "netlink control pipe signal failed"); + + pthread_join(libusb_linux_event_thread, NULL); + + close(linux_netlink_socket); + linux_netlink_socket = -1; + + /* close and reset control pipe */ + close(netlink_control_pipe[0]); + close(netlink_control_pipe[1]); + netlink_control_pipe[0] = -1; + netlink_control_pipe[1] = -1; + + return LIBUSB_SUCCESS; +} + +static const char *netlink_message_parse(const char *buffer, size_t len, const char *key) +{ + const char *end = buffer + len; + size_t keylen = strlen(key); + + while (buffer < end && *buffer) { + if (strncmp(buffer, key, keylen) == 0 && buffer[keylen] == '=') + return buffer + keylen + 1; + buffer += strlen(buffer) + 1; + } + + return NULL; +} + +/* parse parts of netlink message common to both libudev and the kernel */ +static int linux_netlink_parse(const char *buffer, size_t len, int *detached, + const char **sys_name, uint8_t *busnum, uint8_t *devaddr) +{ + const char *tmp, *slash; + + errno = 0; + + *sys_name = NULL; + *detached = 0; + *busnum = 0; + *devaddr = 0; + + tmp = netlink_message_parse(buffer, len, "ACTION"); + if (!tmp) { + return -1; + } else if (strcmp(tmp, "remove") == 0) { + *detached = 1; + } else if (strcmp(tmp, "add") != 0) { + usbi_dbg("unknown device action %s", tmp); + return -1; + } + + /* check that this is a usb message */ + tmp = netlink_message_parse(buffer, len, "SUBSYSTEM"); + if (!tmp || strcmp(tmp, "usb") != 0) { + /* not usb. ignore */ + return -1; + } + + /* check that this is an actual usb device */ + tmp = netlink_message_parse(buffer, len, "DEVTYPE"); + if (!tmp || strcmp(tmp, "usb_device") != 0) { + /* not usb. ignore */ + return -1; + } + + tmp = netlink_message_parse(buffer, len, "BUSNUM"); + if (tmp) { + *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + tmp = netlink_message_parse(buffer, len, "DEVNUM"); + if (NULL == tmp) + return -1; + + *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + } else { + /* no bus number. try "DEVICE" */ + tmp = netlink_message_parse(buffer, len, "DEVICE"); + if (!tmp) { + /* not usb. ignore */ + return -1; + } + + /* Parse a device path such as /dev/bus/usb/003/004 */ + slash = strrchr(tmp, '/'); + if (!slash) + return -1; + + *busnum = (uint8_t)(strtoul(slash - 3, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + *devaddr = (uint8_t)(strtoul(slash + 1, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + return 0; + } + + tmp = netlink_message_parse(buffer, len, "DEVPATH"); + if (!tmp) + return -1; + + slash = strrchr(tmp, '/'); + if (slash) + *sys_name = slash + 1; + + /* found a usb device */ + return 0; +} + +static int linux_netlink_read_message(void) +{ + char cred_buffer[CMSG_SPACE(sizeof(struct ucred))]; + char msg_buffer[2048]; + const char *sys_name = NULL; + uint8_t busnum, devaddr; + int detached, r; + ssize_t len; + struct cmsghdr *cmsg; + struct ucred *cred; + struct sockaddr_nl sa_nl; + struct iovec iov = { .iov_base = msg_buffer, .iov_len = sizeof(msg_buffer) }; + struct msghdr msg = { + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer), + .msg_name = &sa_nl, .msg_namelen = sizeof(sa_nl) + }; + + /* read netlink message */ + len = recvmsg(linux_netlink_socket, &msg, 0); + if (len == -1) { + if (errno != EAGAIN && errno != EINTR) + usbi_err(NULL, "error receiving message from netlink (%d)", errno); + return -1; + } + + if (len < 32 || (msg.msg_flags & MSG_TRUNC)) { + usbi_err(NULL, "invalid netlink message length"); + return -1; + } + + if (sa_nl.nl_groups != NL_GROUP_KERNEL || sa_nl.nl_pid != 0) { + usbi_dbg("ignoring netlink message from unknown group/PID (%u/%u)", + (unsigned int)sa_nl.nl_groups, (unsigned int)sa_nl.nl_pid); + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { + usbi_dbg("ignoring netlink message with no sender credentials"); + return -1; + } + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + usbi_dbg("ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid); + return -1; + } + + r = linux_netlink_parse(msg_buffer, (size_t)len, &detached, &sys_name, &busnum, &devaddr); + if (r) + return r; + + usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s", + busnum, devaddr, sys_name, detached ? "yes" : "no"); + + /* signal device is available (or not) to all contexts */ + if (detached) + linux_device_disconnected(busnum, devaddr); + else + linux_hotplug_enumerate(busnum, devaddr, sys_name); + + return 0; +} + +static void *linux_netlink_event_thread_main(void *arg) +{ + char dummy; + ssize_t r; + struct pollfd fds[] = { + { .fd = netlink_control_pipe[0], + .events = POLLIN }, + { .fd = linux_netlink_socket, + .events = POLLIN }, + }; + + UNUSED(arg); + + usbi_dbg("netlink event thread entering"); + + while (poll(fds, 2, -1) >= 0) { + if (fds[0].revents & POLLIN) { + /* activity on control pipe, read the byte and exit */ + r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy)); + if (r <= 0) + usbi_warn(NULL, "netlink control pipe read failed"); + break; + } + if (fds[1].revents & POLLIN) { + usbi_mutex_static_lock(&linux_hotplug_lock); + linux_netlink_read_message(); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + } + + usbi_dbg("netlink event thread exiting"); + + return NULL; +} + +void linux_netlink_hotplug_poll(void) +{ + int r; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + r = linux_netlink_read_message(); + } while (r == 0); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_udev.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_udev.c new file mode 100644 index 0000000..61d953d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_udev.c @@ -0,0 +1,311 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * Copyright (c) 2012-2013 Nathan Hjelm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "linux_usbfs.h" + +/* udev context */ +static struct udev *udev_ctx = NULL; +static int udev_monitor_fd = -1; +static int udev_control_pipe[2] = {-1, -1}; +static struct udev_monitor *udev_monitor = NULL; +static pthread_t linux_event_thread; + +static void udev_hotplug_event(struct udev_device* udev_dev); +static void *linux_udev_event_thread_main(void *arg); + +int linux_udev_start_event_monitor(void) +{ + int r; + + assert(udev_ctx == NULL); + udev_ctx = udev_new(); + if (!udev_ctx) { + usbi_err(NULL, "could not create udev context"); + goto err; + } + + udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); + if (!udev_monitor) { + usbi_err(NULL, "could not initialize udev monitor"); + goto err_free_ctx; + } + + r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device"); + if (r) { + usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem"); + goto err_free_monitor; + } + + if (udev_monitor_enable_receiving(udev_monitor)) { + usbi_err(NULL, "failed to enable the udev monitor"); + goto err_free_monitor; + } + + udev_monitor_fd = udev_monitor_get_fd(udev_monitor); + + /* Some older versions of udev are not non-blocking by default, + * so make sure this is set */ + r = fcntl(udev_monitor_fd, F_GETFL); + if (r == -1) { + usbi_err(NULL, "getting udev monitor fd flags (%d)", errno); + goto err_free_monitor; + } + r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK); + if (r) { + usbi_err(NULL, "setting udev monitor fd flags (%d)", errno); + goto err_free_monitor; + } + + r = usbi_pipe(udev_control_pipe); + if (r) { + usbi_err(NULL, "could not create udev control pipe"); + goto err_free_monitor; + } + + r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL); + if (r) { + usbi_err(NULL, "creating hotplug event thread (%d)", r); + goto err_close_pipe; + } + + return LIBUSB_SUCCESS; + +err_close_pipe: + close(udev_control_pipe[0]); + close(udev_control_pipe[1]); +err_free_monitor: + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_monitor_fd = -1; +err_free_ctx: + udev_unref(udev_ctx); +err: + udev_ctx = NULL; + return LIBUSB_ERROR_OTHER; +} + +int linux_udev_stop_event_monitor(void) +{ + char dummy = 1; + int r; + + assert(udev_ctx != NULL); + assert(udev_monitor != NULL); + assert(udev_monitor_fd != -1); + + /* Write some dummy data to the control pipe and + * wait for the thread to exit */ + r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy)); + if (r <= 0) { + usbi_warn(NULL, "udev control pipe signal failed"); + } + pthread_join(linux_event_thread, NULL); + + /* Release the udev monitor */ + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_monitor_fd = -1; + + /* Clean up the udev context */ + udev_unref(udev_ctx); + udev_ctx = NULL; + + /* close and reset control pipe */ + close(udev_control_pipe[0]); + close(udev_control_pipe[1]); + udev_control_pipe[0] = -1; + udev_control_pipe[1] = -1; + + return LIBUSB_SUCCESS; +} + +static void *linux_udev_event_thread_main(void *arg) +{ + char dummy; + int r; + struct udev_device* udev_dev; + struct pollfd fds[] = { + {.fd = udev_control_pipe[0], + .events = POLLIN}, + {.fd = udev_monitor_fd, + .events = POLLIN}, + }; + + usbi_dbg("udev event thread entering."); + + while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) { + if (r < 0) { + /* temporary failure */ + continue; + } + if (fds[0].revents & POLLIN) { + /* activity on control pipe, read the byte and exit */ + r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy)); + if (r <= 0) { + usbi_warn(NULL, "udev control pipe read failed"); + } + break; + } + if (fds[1].revents & POLLIN) { + usbi_mutex_static_lock(&linux_hotplug_lock); + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) + udev_hotplug_event(udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + } + + usbi_dbg("udev event thread exiting"); + + return NULL; +} + +static int udev_device_info(struct libusb_context *ctx, int detached, + struct udev_device *udev_dev, uint8_t *busnum, + uint8_t *devaddr, const char **sys_name) { + const char *dev_node; + + dev_node = udev_device_get_devnode(udev_dev); + if (!dev_node) { + return LIBUSB_ERROR_OTHER; + } + + *sys_name = udev_device_get_sysname(udev_dev); + if (!*sys_name) { + return LIBUSB_ERROR_OTHER; + } + + return linux_get_device_address(ctx, detached, busnum, devaddr, + dev_node, *sys_name); +} + +static void udev_hotplug_event(struct udev_device* udev_dev) +{ + const char* udev_action; + const char* sys_name = NULL; + uint8_t busnum = 0, devaddr = 0; + int detached; + int r; + + do { + udev_action = udev_device_get_action(udev_dev); + if (!udev_action) { + break; + } + + detached = !strncmp(udev_action, "remove", 6); + + r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name); + if (LIBUSB_SUCCESS != r) { + break; + } + + usbi_dbg("udev hotplug event. action: %s.", udev_action); + + if (strncmp(udev_action, "add", 3) == 0) { + linux_hotplug_enumerate(busnum, devaddr, sys_name); + } else if (detached) { + linux_device_disconnected(busnum, devaddr); + } else { + usbi_err(NULL, "ignoring udev action %s", udev_action); + } + } while (0); + + udev_device_unref(udev_dev); +} + +int linux_udev_scan_devices(struct libusb_context *ctx) +{ + struct udev_enumerate *enumerator; + struct udev_list_entry *devices, *entry; + struct udev_device *udev_dev; + const char *sys_name; + int r; + + assert(udev_ctx != NULL); + + enumerator = udev_enumerate_new(udev_ctx); + if (NULL == enumerator) { + usbi_err(ctx, "error creating udev enumerator"); + return LIBUSB_ERROR_OTHER; + } + + udev_enumerate_add_match_subsystem(enumerator, "usb"); + udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); + udev_enumerate_scan_devices(enumerator); + devices = udev_enumerate_get_list_entry(enumerator); + + udev_list_entry_foreach(entry, devices) { + const char *path = udev_list_entry_get_name(entry); + uint8_t busnum = 0, devaddr = 0; + + udev_dev = udev_device_new_from_syspath(udev_ctx, path); + + r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name); + if (r) { + udev_device_unref(udev_dev); + continue; + } + + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + udev_device_unref(udev_dev); + } + + udev_enumerate_unref(enumerator); + + return LIBUSB_SUCCESS; +} + +void linux_udev_hotplug_poll(void) +{ + struct udev_device* udev_dev; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) { + usbi_dbg("Handling hotplug event from hotplug_poll"); + udev_hotplug_event(udev_dev); + } + } while (udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.c new file mode 100644 index 0000000..6b89ba2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.c @@ -0,0 +1,2738 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright © 2007-2009 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2013 Nathan Hjelm + * Copyright © 2012-2013 Hans de Goede + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "linux_usbfs.h" + +/* sysfs vs usbfs: + * opening a usbfs node causes the device to be resumed, so we attempt to + * avoid this during enumeration. + * + * sysfs allows us to read the kernel's in-memory copies of device descriptors + * and so forth, avoiding the need to open the device: + * - The binary "descriptors" file contains all config descriptors since + * 2.6.26, commit 217a9081d8e69026186067711131b77f0ce219ed + * - The binary "descriptors" file was added in 2.6.23, commit + * 69d42a78f935d19384d1f6e4f94b65bb162b36df, but it only contains the + * active config descriptors + * - The "busnum" file was added in 2.6.22, commit + * 83f7d958eab2fbc6b159ee92bf1493924e1d0f72 + * - The "devnum" file has been present since pre-2.6.18 + * - the "bConfigurationValue" file has been present since pre-2.6.18 + * + * If we have bConfigurationValue, busnum, and devnum, then we can determine + * the active configuration without having to open the usbfs node in RDWR mode. + * The busnum file is important as that is the only way we can relate sysfs + * devices to usbfs nodes. + * + * If we also have all descriptors, we can obtain the device descriptor and + * configuration without touching usbfs at all. + */ + +/* endianness for multi-byte fields: + * + * Descriptors exposed by usbfs have the multi-byte fields in the device + * descriptor as host endian. Multi-byte fields in the other descriptors are + * bus-endian. The kernel documentation says otherwise, but it is wrong. + * + * In sysfs all descriptors are bus-endian. + */ + +static const char *usbfs_path = NULL; + +/* use usbdev*.* device names in /dev instead of the usbfs bus directories */ +static int usbdev_names = 0; + +/* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically + * allows us to mark URBs as being part of a specific logical transfer when + * we submit them to the kernel. then, on any error except a cancellation, all + * URBs within that transfer will be cancelled and no more URBs will be + * accepted for the transfer, meaning that no more data can creep in. + * + * The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer + * (in either direction) except the first. + * For IN transfers, we must also set SHORT_NOT_OK on all URBs except the + * last; it means that the kernel should treat a short reply as an error. + * For OUT transfers, SHORT_NOT_OK must not be set. it isn't needed (OUT + * transfers can't be short unless there's already some sort of error), and + * setting this flag is disallowed (a kernel with USB debugging enabled will + * reject such URBs). + */ +static int supports_flag_bulk_continuation = -1; + +/* Linux 2.6.31 fixes support for the zero length packet URB flag. This + * allows us to mark URBs that should be followed by a zero length data + * packet, which can be required by device- or class-specific protocols. + */ +static int supports_flag_zero_packet = -1; + +/* clock ID for monotonic clock, as not all clock sources are available on all + * systems. appropriate choice made at initialization time. */ +static clockid_t monotonic_clkid = -1; + +/* Linux 2.6.22 (commit 83f7d958eab2fbc6b159ee92bf1493924e1d0f72) adds a busnum + * to sysfs, so we can relate devices. This also implies that we can read + * the active configuration through bConfigurationValue */ +static int sysfs_can_relate_devices = -1; + +/* Linux 2.6.26 (commit 217a9081d8e69026186067711131b77f0ce219ed) adds all + * config descriptors (rather then just the active config) to the sysfs + * descriptors file, so from then on we can use them. */ +static int sysfs_has_descriptors = -1; + +/* how many times have we initted (and not exited) ? */ +static int init_count = 0; + +/* Serialize hotplug start/stop */ +static usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER; +/* Serialize scan-devices, event-thread, and poll */ +usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER; + +static int linux_start_event_monitor(void); +static int linux_stop_event_monitor(void); +static int linux_scan_devices(struct libusb_context *ctx); +static int sysfs_scan_device(struct libusb_context *ctx, const char *devname); +static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int); + +#if !defined(USE_UDEV) +static int linux_default_scan_devices (struct libusb_context *ctx); +#endif + +struct linux_device_priv { + char *sysfs_dir; + unsigned char *descriptors; + int descriptors_len; + int active_config; /* cache val for !sysfs_can_relate_devices */ +}; + +struct linux_device_handle_priv { + int fd; + int fd_removed; + uint32_t caps; +}; + +enum reap_action { + NORMAL = 0, + /* submission failed after the first URB, so await cancellation/completion + * of all the others */ + SUBMIT_FAILED, + + /* cancelled by user or timeout */ + CANCELLED, + + /* completed multi-URB transfer in non-final URB */ + COMPLETED_EARLY, + + /* one or more urbs encountered a low-level error */ + ERROR, +}; + +struct linux_transfer_priv { + union { + struct usbfs_urb *urbs; + struct usbfs_urb **iso_urbs; + }; + + enum reap_action reap_action; + int num_urbs; + int num_retired; + enum libusb_transfer_status reap_status; + + /* next iso packet in user-supplied transfer to be populated */ + int iso_packet_offset; +}; + +static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + char path[PATH_MAX]; + int fd; + int delay = 10000; + + if (usbdev_names) + snprintf(path, PATH_MAX, "%s/usbdev%d.%d", + usbfs_path, dev->bus_number, dev->device_address); + else + snprintf(path, PATH_MAX, "%s/%03d/%03d", + usbfs_path, dev->bus_number, dev->device_address); + + fd = open(path, mode); + if (fd != -1) + return fd; /* Success */ + + if (errno == ENOENT) { + if (!silent) + usbi_err(ctx, "File doesn't exist, wait %d ms and try again", delay/1000); + + /* Wait 10ms for USB device path creation.*/ + nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL); + + fd = open(path, mode); + if (fd != -1) + return fd; /* Success */ + } + + if (!silent) { + usbi_err(ctx, "libusb couldn't open USB device %s: %s", + path, strerror(errno)); + if (errno == EACCES && mode == O_RDWR) + usbi_err(ctx, "libusb requires write access to USB " + "device nodes."); + } + + if (errno == EACCES) + return LIBUSB_ERROR_ACCESS; + if (errno == ENOENT) + return LIBUSB_ERROR_NO_DEVICE; + return LIBUSB_ERROR_IO; +} + +static struct linux_device_priv *_device_priv(struct libusb_device *dev) +{ + return (struct linux_device_priv *) dev->os_priv; +} + +static struct linux_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct linux_device_handle_priv *) handle->os_priv; +} + +/* check dirent for a /dev/usbdev%d.%d name + * optionally return bus/device on success */ +static int _is_usbdev_entry(struct dirent *entry, int *bus_p, int *dev_p) +{ + int busnum, devnum; + + if (sscanf(entry->d_name, "usbdev%d.%d", &busnum, &devnum) != 2) + return 0; + + usbi_dbg("found: %s", entry->d_name); + if (bus_p != NULL) + *bus_p = busnum; + if (dev_p != NULL) + *dev_p = devnum; + return 1; +} + +static int check_usb_vfs(const char *dirname) +{ + DIR *dir; + struct dirent *entry; + int found = 0; + + dir = opendir(dirname); + if (!dir) + return 0; + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') + continue; + + /* We assume if we find any files that it must be the right place */ + found = 1; + break; + } + + closedir(dir); + return found; +} + +static const char *find_usbfs_path(void) +{ + const char *path = "/dev/bus/usb"; + const char *ret = NULL; + + if (check_usb_vfs(path)) { + ret = path; + } else { + path = "/proc/bus/usb"; + if (check_usb_vfs(path)) + ret = path; + } + + /* look for /dev/usbdev*.* if the normal places fail */ + if (ret == NULL) { + struct dirent *entry; + DIR *dir; + + path = "/dev"; + dir = opendir(path); + if (dir != NULL) { + while ((entry = readdir(dir)) != NULL) { + if (_is_usbdev_entry(entry, NULL, NULL)) { + /* found one; that's enough */ + ret = path; + usbdev_names = 1; + break; + } + } + closedir(dir); + } + } + +/* On udev based systems without any usb-devices /dev/bus/usb will not + * exist. So if we've not found anything and we're using udev for hotplug + * simply assume /dev/bus/usb rather then making libusb_init fail. */ +#if defined(USE_UDEV) + if (ret == NULL) + ret = "/dev/bus/usb"; +#endif + + if (ret != NULL) + usbi_dbg("found usbfs at %s", ret); + + return ret; +} + +/* the monotonic clock is not usable on all systems (e.g. embedded ones often + * seem to lack it). fall back to REALTIME if we have to. */ +static clockid_t find_monotonic_clock(void) +{ +#ifdef CLOCK_MONOTONIC + struct timespec ts; + int r; + + /* Linux 2.6.28 adds CLOCK_MONOTONIC_RAW but we don't use it + * because it's not available through timerfd */ + r = clock_gettime(CLOCK_MONOTONIC, &ts); + if (r == 0) + return CLOCK_MONOTONIC; + usbi_dbg("monotonic clock doesn't work, errno %d", errno); +#endif + + return CLOCK_REALTIME; +} + +static int kernel_version_ge(int major, int minor, int sublevel) +{ + struct utsname uts; + int atoms, kmajor, kminor, ksublevel; + + if (uname(&uts) < 0) + return -1; + atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel); + if (atoms < 1) + return -1; + + if (kmajor > major) + return 1; + if (kmajor < major) + return 0; + + /* kmajor == major */ + if (atoms < 2) + return 0 == minor && 0 == sublevel; + if (kminor > minor) + return 1; + if (kminor < minor) + return 0; + + /* kminor == minor */ + if (atoms < 3) + return 0 == sublevel; + + return ksublevel >= sublevel; +} + +static int op_init(struct libusb_context *ctx) +{ + struct stat statbuf; + int r; + + usbfs_path = find_usbfs_path(); + if (!usbfs_path) { + usbi_err(ctx, "could not find usbfs"); + return LIBUSB_ERROR_OTHER; + } + + if (monotonic_clkid == -1) + monotonic_clkid = find_monotonic_clock(); + + if (supports_flag_bulk_continuation == -1) { + /* bulk continuation URB flag available from Linux 2.6.32 */ + supports_flag_bulk_continuation = kernel_version_ge(2,6,32); + if (supports_flag_bulk_continuation == -1) { + usbi_err(ctx, "error checking for bulk continuation support"); + return LIBUSB_ERROR_OTHER; + } + } + + if (supports_flag_bulk_continuation) + usbi_dbg("bulk continuation flag supported"); + + if (-1 == supports_flag_zero_packet) { + /* zero length packet URB flag fixed since Linux 2.6.31 */ + supports_flag_zero_packet = kernel_version_ge(2,6,31); + if (-1 == supports_flag_zero_packet) { + usbi_err(ctx, "error checking for zero length packet support"); + return LIBUSB_ERROR_OTHER; + } + } + + if (supports_flag_zero_packet) + usbi_dbg("zero length packet flag supported"); + + if (-1 == sysfs_has_descriptors) { + /* sysfs descriptors has all descriptors since Linux 2.6.26 */ + sysfs_has_descriptors = kernel_version_ge(2,6,26); + if (-1 == sysfs_has_descriptors) { + usbi_err(ctx, "error checking for sysfs descriptors"); + return LIBUSB_ERROR_OTHER; + } + } + + if (-1 == sysfs_can_relate_devices) { + /* sysfs has busnum since Linux 2.6.22 */ + sysfs_can_relate_devices = kernel_version_ge(2,6,22); + if (-1 == sysfs_can_relate_devices) { + usbi_err(ctx, "error checking for sysfs busnum"); + return LIBUSB_ERROR_OTHER; + } + } + + if (sysfs_can_relate_devices || sysfs_has_descriptors) { + r = stat(SYSFS_DEVICE_PATH, &statbuf); + if (r != 0 || !S_ISDIR(statbuf.st_mode)) { + usbi_warn(ctx, "sysfs not mounted"); + sysfs_can_relate_devices = 0; + sysfs_has_descriptors = 0; + } + } + + if (sysfs_can_relate_devices) + usbi_dbg("sysfs can relate devices"); + + if (sysfs_has_descriptors) + usbi_dbg("sysfs has complete descriptors"); + + usbi_mutex_static_lock(&linux_hotplug_startstop_lock); + r = LIBUSB_SUCCESS; + if (init_count == 0) { + /* start up hotplug event handler */ + r = linux_start_event_monitor(); + } + if (r == LIBUSB_SUCCESS) { + r = linux_scan_devices(ctx); + if (r == LIBUSB_SUCCESS) + init_count++; + else if (init_count == 0) + linux_stop_event_monitor(); + } else + usbi_err(ctx, "error starting hotplug event monitor"); + usbi_mutex_static_unlock(&linux_hotplug_startstop_lock); + + return r; +} + +static void op_exit(void) +{ + usbi_mutex_static_lock(&linux_hotplug_startstop_lock); + assert(init_count != 0); + if (!--init_count) { + /* tear down event handler */ + (void)linux_stop_event_monitor(); + } + usbi_mutex_static_unlock(&linux_hotplug_startstop_lock); +} + +static int linux_start_event_monitor(void) +{ +#if defined(USE_UDEV) + return linux_udev_start_event_monitor(); +#else + return linux_netlink_start_event_monitor(); +#endif +} + +static int linux_stop_event_monitor(void) +{ +#if defined(USE_UDEV) + return linux_udev_stop_event_monitor(); +#else + return linux_netlink_stop_event_monitor(); +#endif +} + +static int linux_scan_devices(struct libusb_context *ctx) +{ + int ret; + + usbi_mutex_static_lock(&linux_hotplug_lock); + +#if defined(USE_UDEV) + ret = linux_udev_scan_devices(ctx); +#else + ret = linux_default_scan_devices(ctx); +#endif + + usbi_mutex_static_unlock(&linux_hotplug_lock); + + return ret; +} + +static void op_hotplug_poll(void) +{ +#if defined(USE_UDEV) + linux_udev_hotplug_poll(); +#else + linux_netlink_hotplug_poll(); +#endif +} + +static int _open_sysfs_attr(struct libusb_device *dev, const char *attr) +{ + struct linux_device_priv *priv = _device_priv(dev); + char filename[PATH_MAX]; + int fd; + + snprintf(filename, PATH_MAX, "%s/%s/%s", + SYSFS_DEVICE_PATH, priv->sysfs_dir, attr); + fd = open(filename, O_RDONLY); + if (fd < 0) { + usbi_err(DEVICE_CTX(dev), + "open %s failed ret=%d errno=%d", filename, fd, errno); + return LIBUSB_ERROR_IO; + } + + return fd; +} + +/* Note only suitable for attributes which always read >= 0, < 0 is error */ +static int __read_sysfs_attr(struct libusb_context *ctx, + const char *devname, const char *attr) +{ + char filename[PATH_MAX]; + FILE *f; + int r, value; + + snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, + devname, attr); + f = fopen(filename, "r"); + if (f == NULL) { + if (errno == ENOENT) { + /* File doesn't exist. Assume the device has been + disconnected (see trac ticket #70). */ + return LIBUSB_ERROR_NO_DEVICE; + } + usbi_err(ctx, "open %s failed errno=%d", filename, errno); + return LIBUSB_ERROR_IO; + } + + r = fscanf(f, "%d", &value); + fclose(f); + if (r != 1) { + usbi_err(ctx, "fscanf %s returned %d, errno=%d", attr, r, errno); + return LIBUSB_ERROR_NO_DEVICE; /* For unplug race (trac #70) */ + } + if (value < 0) { + usbi_err(ctx, "%s contains a negative value", filename); + return LIBUSB_ERROR_IO; + } + + return value; +} + +static int op_get_device_descriptor(struct libusb_device *dev, + unsigned char *buffer, int *host_endian) +{ + struct linux_device_priv *priv = _device_priv(dev); + + *host_endian = sysfs_has_descriptors ? 0 : 1; + memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH); + + return 0; +} + +/* read the bConfigurationValue for a device */ +static int sysfs_get_active_config(struct libusb_device *dev, int *config) +{ + char *endptr; + char tmp[5] = {0, 0, 0, 0, 0}; + long num; + int fd; + ssize_t r; + + fd = _open_sysfs_attr(dev, "bConfigurationValue"); + if (fd < 0) + return fd; + + r = read(fd, tmp, sizeof(tmp)); + close(fd); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "read bConfigurationValue failed ret=%d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } else if (r == 0) { + usbi_dbg("device unconfigured"); + *config = -1; + return 0; + } + + if (tmp[sizeof(tmp) - 1] != 0) { + usbi_err(DEVICE_CTX(dev), "not null-terminated?"); + return LIBUSB_ERROR_IO; + } else if (tmp[0] == 0) { + usbi_err(DEVICE_CTX(dev), "no configuration value?"); + return LIBUSB_ERROR_IO; + } + + num = strtol(tmp, &endptr, 10); + if (endptr == tmp) { + usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp); + return LIBUSB_ERROR_IO; + } + + *config = (int) num; + return 0; +} + +int linux_get_device_address (struct libusb_context *ctx, int detached, + uint8_t *busnum, uint8_t *devaddr,const char *dev_node, + const char *sys_name) +{ + int sysfs_attr; + + usbi_dbg("getting address for device: %s detached: %d", sys_name, detached); + /* can't use sysfs to read the bus and device number if the + * device has been detached */ + if (!sysfs_can_relate_devices || detached || NULL == sys_name) { + if (NULL == dev_node) { + return LIBUSB_ERROR_OTHER; + } + + /* will this work with all supported kernel versions? */ + if (!strncmp(dev_node, "/dev/bus/usb", 12)) { + sscanf (dev_node, "/dev/bus/usb/%hhu/%hhu", busnum, devaddr); + } else if (!strncmp(dev_node, "/proc/bus/usb", 13)) { + sscanf (dev_node, "/proc/bus/usb/%hhu/%hhu", busnum, devaddr); + } + + return LIBUSB_SUCCESS; + } + + usbi_dbg("scan %s", sys_name); + + sysfs_attr = __read_sysfs_attr(ctx, sys_name, "busnum"); + if (0 > sysfs_attr) + return sysfs_attr; + if (sysfs_attr > 255) + return LIBUSB_ERROR_INVALID_PARAM; + *busnum = (uint8_t) sysfs_attr; + + sysfs_attr = __read_sysfs_attr(ctx, sys_name, "devnum"); + if (0 > sysfs_attr) + return sysfs_attr; + if (sysfs_attr > 255) + return LIBUSB_ERROR_INVALID_PARAM; + + *devaddr = (uint8_t) sysfs_attr; + + usbi_dbg("bus=%d dev=%d", *busnum, *devaddr); + + return LIBUSB_SUCCESS; +} + +/* Return offset of the next descriptor with the given type */ +static int seek_to_next_descriptor(struct libusb_context *ctx, + uint8_t descriptor_type, unsigned char *buffer, int size) +{ + struct usb_descriptor_header header; + int i; + + for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) { + if (size == 0) + return LIBUSB_ERROR_NOT_FOUND; + + if (size < 2) { + usbi_err(ctx, "short descriptor read %d/2", size); + return LIBUSB_ERROR_IO; + } + usbi_parse_descriptor(buffer + i, "bb", &header, 0); + + if (i && header.bDescriptorType == descriptor_type) + return i; + } + usbi_err(ctx, "bLength overflow by %d bytes", -size); + return LIBUSB_ERROR_IO; +} + +/* Return offset to next config */ +static int seek_to_next_config(struct libusb_context *ctx, + unsigned char *buffer, int size) +{ + struct libusb_config_descriptor config; + + if (size == 0) + return LIBUSB_ERROR_NOT_FOUND; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(buffer, "bbwbbbbb", &config, 0); + if (config.bDescriptorType != LIBUSB_DT_CONFIG) { + usbi_err(ctx, "descriptor is not a config desc (type 0x%02x)", + config.bDescriptorType); + return LIBUSB_ERROR_IO; + } + + /* + * In usbfs the config descriptors are config.wTotalLength bytes apart, + * with any short reads from the device appearing as holes in the file. + * + * In sysfs wTotalLength is ignored, instead the kernel returns a + * config descriptor with verified bLength fields, with descriptors + * with an invalid bLength removed. + */ + if (sysfs_has_descriptors) { + int next = seek_to_next_descriptor(ctx, LIBUSB_DT_CONFIG, + buffer, size); + if (next == LIBUSB_ERROR_NOT_FOUND) + next = size; + if (next < 0) + return next; + + if (next != config.wTotalLength) + usbi_warn(ctx, "config length mismatch wTotalLength " + "%d real %d", config.wTotalLength, next); + return next; + } else { + if (config.wTotalLength < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid wTotalLength %d", + config.wTotalLength); + return LIBUSB_ERROR_IO; + } else if (config.wTotalLength > size) { + usbi_warn(ctx, "short descriptor read %d/%d", + size, config.wTotalLength); + return size; + } else + return config.wTotalLength; + } +} + +static int op_get_config_descriptor_by_value(struct libusb_device *dev, + uint8_t value, unsigned char **buffer, int *host_endian) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct linux_device_priv *priv = _device_priv(dev); + unsigned char *descriptors = priv->descriptors; + int size = priv->descriptors_len; + struct libusb_config_descriptor *config; + + *buffer = NULL; + /* Unlike the device desc. config descs. are always in raw format */ + *host_endian = 0; + + /* Skip device header */ + descriptors += DEVICE_DESC_LENGTH; + size -= DEVICE_DESC_LENGTH; + + /* Seek till the config is found, or till "EOF" */ + while (1) { + int next = seek_to_next_config(ctx, descriptors, size); + if (next < 0) + return next; + config = (struct libusb_config_descriptor *)descriptors; + if (config->bConfigurationValue == value) { + *buffer = descriptors; + return next; + } + size -= next; + descriptors += next; + } +} + +static int op_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buffer, size_t len, int *host_endian) +{ + int r, config; + unsigned char *config_desc; + + if (sysfs_can_relate_devices) { + r = sysfs_get_active_config(dev, &config); + if (r < 0) + return r; + } else { + /* Use cached bConfigurationValue */ + struct linux_device_priv *priv = _device_priv(dev); + config = priv->active_config; + } + if (config == -1) + return LIBUSB_ERROR_NOT_FOUND; + + r = op_get_config_descriptor_by_value(dev, config, &config_desc, + host_endian); + if (r < 0) + return r; + + len = MIN(len, r); + memcpy(buffer, config_desc, len); + return len; +} + +static int op_get_config_descriptor(struct libusb_device *dev, + uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct linux_device_priv *priv = _device_priv(dev); + unsigned char *descriptors = priv->descriptors; + int i, r, size = priv->descriptors_len; + + /* Unlike the device desc. config descs. are always in raw format */ + *host_endian = 0; + + /* Skip device header */ + descriptors += DEVICE_DESC_LENGTH; + size -= DEVICE_DESC_LENGTH; + + /* Seek till the config is found, or till "EOF" */ + for (i = 0; ; i++) { + r = seek_to_next_config(DEVICE_CTX(dev), descriptors, size); + if (r < 0) + return r; + if (i == config_index) + break; + size -= r; + descriptors += r; + } + + len = MIN(len, r); + memcpy(buffer, descriptors, len); + return len; +} + +/* send a control message to retrieve active configuration */ +static int usbfs_get_active_config(struct libusb_device *dev, int fd) +{ + struct linux_device_priv *priv = _device_priv(dev); + unsigned char active_config = 0; + int r; + + struct usbfs_ctrltransfer ctrl = { + .bmRequestType = LIBUSB_ENDPOINT_IN, + .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION, + .wValue = 0, + .wIndex = 0, + .wLength = 1, + .timeout = 1000, + .data = &active_config + }; + + r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl); + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + /* we hit this error path frequently with buggy devices :( */ + usbi_warn(DEVICE_CTX(dev), + "get_configuration failed ret=%d errno=%d", r, errno); + priv->active_config = -1; + } else { + if (active_config > 0) { + priv->active_config = active_config; + } else { + /* some buggy devices have a configuration 0, but we're + * reaching into the corner of a corner case here, so let's + * not support buggy devices in these circumstances. + * stick to the specs: a configuration value of 0 means + * unconfigured. */ + usbi_warn(DEVICE_CTX(dev), + "active cfg 0? assuming unconfigured device"); + priv->active_config = -1; + } + } + + return LIBUSB_SUCCESS; +} + +static int initialize_device(struct libusb_device *dev, uint8_t busnum, + uint8_t devaddr, const char *sysfs_dir) +{ + struct linux_device_priv *priv = _device_priv(dev); + struct libusb_context *ctx = DEVICE_CTX(dev); + int descriptors_size = 512; /* Begin with a 1024 byte alloc */ + int fd, speed; + ssize_t r; + + dev->bus_number = busnum; + dev->device_address = devaddr; + + if (sysfs_dir) { + priv->sysfs_dir = strdup(sysfs_dir); + if (!priv->sysfs_dir) + return LIBUSB_ERROR_NO_MEM; + + /* Note speed can contain 1.5, in this case __read_sysfs_attr + will stop parsing at the '.' and return 1 */ + speed = __read_sysfs_attr(DEVICE_CTX(dev), sysfs_dir, "speed"); + if (speed >= 0) { + switch (speed) { + case 1: dev->speed = LIBUSB_SPEED_LOW; break; + case 12: dev->speed = LIBUSB_SPEED_FULL; break; + case 480: dev->speed = LIBUSB_SPEED_HIGH; break; + case 5000: dev->speed = LIBUSB_SPEED_SUPER; break; + default: + usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed); + } + } + } + + /* cache descriptors in memory */ + if (sysfs_has_descriptors) + fd = _open_sysfs_attr(dev, "descriptors"); + else + fd = _get_usbfs_fd(dev, O_RDONLY, 0); + if (fd < 0) + return fd; + + do { + descriptors_size *= 2; + priv->descriptors = usbi_reallocf(priv->descriptors, + descriptors_size); + if (!priv->descriptors) { + close(fd); + return LIBUSB_ERROR_NO_MEM; + } + /* usbfs has holes in the file */ + if (!sysfs_has_descriptors) { + memset(priv->descriptors + priv->descriptors_len, + 0, descriptors_size - priv->descriptors_len); + } + r = read(fd, priv->descriptors + priv->descriptors_len, + descriptors_size - priv->descriptors_len); + if (r < 0) { + usbi_err(ctx, "read descriptor failed ret=%d errno=%d", + fd, errno); + close(fd); + return LIBUSB_ERROR_IO; + } + priv->descriptors_len += r; + } while (priv->descriptors_len == descriptors_size); + + close(fd); + + if (priv->descriptors_len < DEVICE_DESC_LENGTH) { + usbi_err(ctx, "short descriptor read (%d)", + priv->descriptors_len); + return LIBUSB_ERROR_IO; + } + + if (sysfs_can_relate_devices) + return LIBUSB_SUCCESS; + + /* cache active config */ + fd = _get_usbfs_fd(dev, O_RDWR, 1); + if (fd < 0) { + /* cannot send a control message to determine the active + * config. just assume the first one is active. */ + usbi_warn(ctx, "Missing rw usbfs access; cannot determine " + "active configuration descriptor"); + if (priv->descriptors_len >= + (DEVICE_DESC_LENGTH + LIBUSB_DT_CONFIG_SIZE)) { + struct libusb_config_descriptor config; + usbi_parse_descriptor( + priv->descriptors + DEVICE_DESC_LENGTH, + "bbwbbbbb", &config, 0); + priv->active_config = config.bConfigurationValue; + } else + priv->active_config = -1; /* No config dt */ + + return LIBUSB_SUCCESS; + } + + r = usbfs_get_active_config(dev, fd); + close(fd); + + return r; +} + +static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device *it; + char *parent_sysfs_dir, *tmp; + int ret, add_parent = 1; + + /* XXX -- can we figure out the topology when using usbfs? */ + if (NULL == sysfs_dir || 0 == strncmp(sysfs_dir, "usb", 3)) { + /* either using usbfs or finding the parent of a root hub */ + return LIBUSB_SUCCESS; + } + + parent_sysfs_dir = strdup(sysfs_dir); + if (NULL == parent_sysfs_dir) { + return LIBUSB_ERROR_NO_MEM; + } + if (NULL != (tmp = strrchr(parent_sysfs_dir, '.')) || + NULL != (tmp = strrchr(parent_sysfs_dir, '-'))) { + dev->port_number = atoi(tmp + 1); + *tmp = '\0'; + } else { + usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info", + parent_sysfs_dir); + free (parent_sysfs_dir); + return LIBUSB_SUCCESS; + } + + /* is the parent a root hub? */ + if (NULL == strchr(parent_sysfs_dir, '-')) { + tmp = parent_sysfs_dir; + ret = asprintf (&parent_sysfs_dir, "usb%s", tmp); + free (tmp); + if (0 > ret) { + return LIBUSB_ERROR_NO_MEM; + } + } + +retry: + /* find the parent in the context */ + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry(it, &ctx->usb_devs, list, struct libusb_device) { + struct linux_device_priv *priv = _device_priv(it); + if (priv->sysfs_dir) { + if (0 == strcmp (priv->sysfs_dir, parent_sysfs_dir)) { + dev->parent_dev = libusb_ref_device(it); + break; + } + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + + if (!dev->parent_dev && add_parent) { + usbi_dbg("parent_dev %s not enumerated yet, enumerating now", + parent_sysfs_dir); + sysfs_scan_device(ctx, parent_sysfs_dir); + add_parent = 0; + goto retry; + } + + usbi_dbg("Dev %p (%s) has parent %p (%s) port %d", dev, sysfs_dir, + dev->parent_dev, parent_sysfs_dir, dev->port_number); + + free (parent_sysfs_dir); + + return LIBUSB_SUCCESS; +} + +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) +{ + unsigned long session_id; + struct libusb_device *dev; + int r = 0; + + /* FIXME: session ID is not guaranteed unique as addresses can wrap and + * will be reused. instead we should add a simple sysfs attribute with + * a session ID. */ + session_id = busnum << 8 | devaddr; + usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, + session_id); + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + /* device already exists in the context */ + usbi_dbg("session_id %ld already exists", session_id); + libusb_unref_device(dev); + return LIBUSB_SUCCESS; + } + + usbi_dbg("allocating new device for %d/%d (session %ld)", + busnum, devaddr, session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) + return LIBUSB_ERROR_NO_MEM; + + r = initialize_device(dev, busnum, devaddr, sysfs_dir); + if (r < 0) + goto out; + r = usbi_sanitize_device(dev); + if (r < 0) + goto out; + + r = linux_get_parent_info(dev, sysfs_dir); + if (r < 0) + goto out; +out: + if (r < 0) + libusb_unref_device(dev); + else + usbi_connect_device(dev); + + return r; +} + +void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name) +{ + struct libusb_context *ctx; + + usbi_mutex_static_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +void linux_device_disconnected(uint8_t busnum, uint8_t devaddr) +{ + struct libusb_context *ctx; + struct libusb_device *dev; + unsigned long session_id = busnum << 8 | devaddr; + + usbi_mutex_static_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + dev = usbi_get_device_by_session_id (ctx, session_id); + if (NULL != dev) { + usbi_disconnect_device (dev); + libusb_unref_device(dev); + } else { + usbi_dbg("device not found for session %x", session_id); + } + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +#if !defined(USE_UDEV) +/* open a bus directory and adds all discovered devices to the context */ +static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum) +{ + DIR *dir; + char dirpath[PATH_MAX]; + struct dirent *entry; + int r = LIBUSB_ERROR_IO; + + snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum); + usbi_dbg("%s", dirpath); + dir = opendir(dirpath); + if (!dir) { + usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno); + /* FIXME: should handle valid race conditions like hub unplugged + * during directory iteration - this is not an error */ + return r; + } + + while ((entry = readdir(dir))) { + int devaddr; + + if (entry->d_name[0] == '.') + continue; + + devaddr = atoi(entry->d_name); + if (devaddr == 0) { + usbi_dbg("unknown dir entry %s", entry->d_name); + continue; + } + + if (linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL)) { + usbi_dbg("failed to enumerate dir entry %s", entry->d_name); + continue; + } + + r = 0; + } + + closedir(dir); + return r; +} + +static int usbfs_get_device_list(struct libusb_context *ctx) +{ + struct dirent *entry; + DIR *buses = opendir(usbfs_path); + int r = 0; + + if (!buses) { + usbi_err(ctx, "opendir buses failed errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + while ((entry = readdir(buses))) { + int busnum; + + if (entry->d_name[0] == '.') + continue; + + if (usbdev_names) { + int devaddr; + if (!_is_usbdev_entry(entry, &busnum, &devaddr)) + continue; + + r = linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL); + if (r < 0) { + usbi_dbg("failed to enumerate dir entry %s", entry->d_name); + continue; + } + } else { + busnum = atoi(entry->d_name); + if (busnum == 0) { + usbi_dbg("unknown dir entry %s", entry->d_name); + continue; + } + + r = usbfs_scan_busdir(ctx, busnum); + if (r < 0) + break; + } + } + + closedir(buses); + return r; + +} +#endif + +static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) +{ + uint8_t busnum, devaddr; + int ret; + + ret = linux_get_device_address (ctx, 0, &busnum, &devaddr, NULL, devname); + if (LIBUSB_SUCCESS != ret) { + return ret; + } + + return linux_enumerate_device(ctx, busnum & 0xff, devaddr & 0xff, + devname); +} + +#if !defined(USE_UDEV) +static int sysfs_get_device_list(struct libusb_context *ctx) +{ + DIR *devices = opendir(SYSFS_DEVICE_PATH); + struct dirent *entry; + int r = LIBUSB_ERROR_IO; + + if (!devices) { + usbi_err(ctx, "opendir devices failed errno=%d", errno); + return r; + } + + while ((entry = readdir(devices))) { + if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3)) + || strchr(entry->d_name, ':')) + continue; + + if (sysfs_scan_device(ctx, entry->d_name)) { + usbi_dbg("failed to enumerate dir entry %s", entry->d_name); + continue; + } + + r = 0; + } + + closedir(devices); + return r; +} + +static int linux_default_scan_devices (struct libusb_context *ctx) +{ + /* we can retrieve device list and descriptors from sysfs or usbfs. + * sysfs is preferable, because if we use usbfs we end up resuming + * any autosuspended USB devices. however, sysfs is not available + * everywhere, so we need a usbfs fallback too. + * + * as described in the "sysfs vs usbfs" comment at the top of this + * file, sometimes we have sysfs but not enough information to + * relate sysfs devices to usbfs nodes. op_init() determines the + * adequacy of sysfs and sets sysfs_can_relate_devices. + */ + if (sysfs_can_relate_devices != 0) + return sysfs_get_device_list(ctx); + else + return usbfs_get_device_list(ctx); +} +#endif + +static int op_open(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); + int r; + + hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); + if (hpriv->fd < 0) { + if (hpriv->fd == LIBUSB_ERROR_NO_DEVICE) { + /* device will still be marked as attached if hotplug monitor thread + * hasn't processed remove event yet */ + usbi_mutex_static_lock(&linux_hotplug_lock); + if (handle->dev->attached) { + usbi_dbg("open failed with no device, but device still attached"); + linux_device_disconnected(handle->dev->bus_number, + handle->dev->device_address); + } + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + return hpriv->fd; + } + + r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps); + if (r < 0) { + if (errno == ENOTTY) + usbi_dbg("getcap not available"); + else + usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno); + hpriv->caps = 0; + if (supports_flag_zero_packet) + hpriv->caps |= USBFS_CAP_ZERO_PACKET; + if (supports_flag_bulk_continuation) + hpriv->caps |= USBFS_CAP_BULK_CONTINUATION; + } + + r = usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT); + if (r < 0) + close(hpriv->fd); + + return r; +} + +static void op_close(struct libusb_device_handle *dev_handle) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(dev_handle); + /* fd may have already been removed by POLLERR condition in op_handle_events() */ + if (!hpriv->fd_removed) + usbi_remove_pollfd(HANDLE_CTX(dev_handle), hpriv->fd); + close(hpriv->fd); +} + +static int op_get_configuration(struct libusb_device_handle *handle, + int *config) +{ + int r; + + if (sysfs_can_relate_devices) { + r = sysfs_get_active_config(handle->dev, config); + } else { + r = usbfs_get_active_config(handle->dev, + _device_handle_priv(handle)->fd); + if (r == LIBUSB_SUCCESS) + *config = _device_priv(handle->dev)->active_config; + } + if (r < 0) + return r; + + if (*config == -1) { + usbi_err(HANDLE_CTX(handle), "device unconfigured"); + *config = 0; + } + + return 0; +} + +static int op_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct linux_device_priv *priv = _device_priv(handle->dev); + int fd = _device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_SETCONFIG, &config); + if (r) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + /* update our cached active config descriptor */ + priv->active_config = config; + + return LIBUSB_SUCCESS; +} + +static int claim_interface(struct libusb_device_handle *handle, int iface) +{ + int fd = _device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface); + if (r) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "claim interface failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int release_interface(struct libusb_device_handle *handle, int iface) +{ + int fd = _device_handle_priv(handle)->fd; + int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface); + if (r) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "release interface failed, error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + return 0; +} + +static int op_set_interface(struct libusb_device_handle *handle, int iface, + int altsetting) +{ + int fd = _device_handle_priv(handle)->fd; + struct usbfs_setinterface setintf; + int r; + + setintf.interface = iface; + setintf.altsetting = altsetting; + r = ioctl(fd, IOCTL_USBFS_SETINTF, &setintf); + if (r) { + if (errno == EINVAL) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "setintf failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_clear_halt(struct libusb_device_handle *handle, + unsigned char endpoint) +{ + int fd = _device_handle_priv(handle)->fd; + unsigned int _endpoint = endpoint; + int r = ioctl(fd, IOCTL_USBFS_CLEAR_HALT, &_endpoint); + if (r) { + if (errno == ENOENT) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "clear_halt failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_reset_device(struct libusb_device_handle *handle) +{ + int fd = _device_handle_priv(handle)->fd; + int i, r, ret = 0; + + /* Doing a device reset will cause the usbfs driver to get unbound + from any interfaces it is bound to. By voluntarily unbinding + the usbfs driver ourself, we stop the kernel from rebinding + the interface after reset (which would end up with the interface + getting bound to the in kernel driver if any). */ + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (handle->claimed_interfaces & (1L << i)) { + release_interface(handle, i); + } + } + + usbi_mutex_lock(&handle->lock); + r = ioctl(fd, IOCTL_USBFS_RESET, NULL); + if (r) { + if (errno == ENODEV) { + ret = LIBUSB_ERROR_NOT_FOUND; + goto out; + } + + usbi_err(HANDLE_CTX(handle), + "reset failed error %d errno %d", r, errno); + ret = LIBUSB_ERROR_OTHER; + goto out; + } + + /* And re-claim any interfaces which were claimed before the reset */ + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (handle->claimed_interfaces & (1L << i)) { + /* + * A driver may have completed modprobing during + * IOCTL_USBFS_RESET, and bound itself as soon as + * IOCTL_USBFS_RESET released the device lock + */ + r = detach_kernel_driver_and_claim(handle, i); + if (r) { + usbi_warn(HANDLE_CTX(handle), + "failed to re-claim interface %d after reset: %s", + i, libusb_error_name(r)); + handle->claimed_interfaces &= ~(1L << i); + ret = LIBUSB_ERROR_NOT_FOUND; + } + } + } +out: + usbi_mutex_unlock(&handle->lock); + return ret; +} + +static int do_streams_ioctl(struct libusb_device_handle *handle, long req, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints) +{ + int r, fd = _device_handle_priv(handle)->fd; + struct usbfs_streams *streams; + + if (num_endpoints > 30) /* Max 15 in + 15 out eps */ + return LIBUSB_ERROR_INVALID_PARAM; + + streams = malloc(sizeof(struct usbfs_streams) + num_endpoints); + if (!streams) + return LIBUSB_ERROR_NO_MEM; + + streams->num_streams = num_streams; + streams->num_eps = num_endpoints; + memcpy(streams->eps, endpoints, num_endpoints); + + r = ioctl(fd, req, streams); + + free(streams); + + if (r < 0) { + if (errno == ENOTTY) + return LIBUSB_ERROR_NOT_SUPPORTED; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "streams-ioctl failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + return r; +} + +static int op_alloc_streams(struct libusb_device_handle *handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints) +{ + return do_streams_ioctl(handle, IOCTL_USBFS_ALLOC_STREAMS, + num_streams, endpoints, num_endpoints); +} + +static int op_free_streams(struct libusb_device_handle *handle, + unsigned char *endpoints, int num_endpoints) +{ + return do_streams_ioctl(handle, IOCTL_USBFS_FREE_STREAMS, 0, + endpoints, num_endpoints); +} + +static unsigned char *op_dev_mem_alloc(struct libusb_device_handle *handle, + size_t len) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); + unsigned char *buffer = (unsigned char *)mmap(NULL, len, + PROT_READ | PROT_WRITE, MAP_SHARED, hpriv->fd, 0); + if (buffer == MAP_FAILED) { + usbi_err(HANDLE_CTX(handle), "alloc dev mem failed errno %d", + errno); + return NULL; + } + return buffer; +} + +static int op_dev_mem_free(struct libusb_device_handle *handle, + unsigned char *buffer, size_t len) +{ + if (munmap(buffer, len) != 0) { + usbi_err(HANDLE_CTX(handle), "free dev mem failed errno %d", + errno); + return LIBUSB_ERROR_OTHER; + } else { + return LIBUSB_SUCCESS; + } +} + +static int op_kernel_driver_active(struct libusb_device_handle *handle, + int interface) +{ + int fd = _device_handle_priv(handle)->fd; + struct usbfs_getdriver getdrv; + int r; + + getdrv.interface = interface; + r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); + if (r) { + if (errno == ENODATA) + return 0; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "get driver failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return (strcmp(getdrv.driver, "usbfs") == 0) ? 0 : 1; +} + +static int op_detach_kernel_driver(struct libusb_device_handle *handle, + int interface) +{ + int fd = _device_handle_priv(handle)->fd; + struct usbfs_ioctl command; + struct usbfs_getdriver getdrv; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_DISCONNECT; + command.data = NULL; + + getdrv.interface = interface; + r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv); + if (r == 0 && strcmp(getdrv.driver, "usbfs") == 0) + return LIBUSB_ERROR_NOT_FOUND; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), + "detach failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +static int op_attach_kernel_driver(struct libusb_device_handle *handle, + int interface) +{ + int fd = _device_handle_priv(handle)->fd; + struct usbfs_ioctl command; + int r; + + command.ifno = interface; + command.ioctl_code = IOCTL_USBFS_CONNECT; + command.data = NULL; + + r = ioctl(fd, IOCTL_USBFS_IOCTL, &command); + if (r < 0) { + if (errno == ENODATA) + return LIBUSB_ERROR_NOT_FOUND; + else if (errno == EINVAL) + return LIBUSB_ERROR_INVALID_PARAM; + else if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + else if (errno == EBUSY) + return LIBUSB_ERROR_BUSY; + + usbi_err(HANDLE_CTX(handle), + "attach failed error %d errno %d", r, errno); + return LIBUSB_ERROR_OTHER; + } else if (r == 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + return 0; +} + +static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle, + int interface) +{ + struct usbfs_disconnect_claim dc; + int r, fd = _device_handle_priv(handle)->fd; + + dc.interface = interface; + strcpy(dc.driver, "usbfs"); + dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER; + r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc); + if (r == 0 || (r != 0 && errno != ENOTTY)) { + if (r == 0) + return 0; + + switch (errno) { + case EBUSY: + return LIBUSB_ERROR_BUSY; + case EINVAL: + return LIBUSB_ERROR_INVALID_PARAM; + case ENODEV: + return LIBUSB_ERROR_NO_DEVICE; + } + usbi_err(HANDLE_CTX(handle), + "disconnect-and-claim failed errno %d", errno); + return LIBUSB_ERROR_OTHER; + } + + /* Fallback code for kernels which don't support the + disconnect-and-claim ioctl */ + r = op_detach_kernel_driver(handle, interface); + if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND) + return r; + + return claim_interface(handle, interface); +} + +static int op_claim_interface(struct libusb_device_handle *handle, int iface) +{ + if (handle->auto_detach_kernel_driver) + return detach_kernel_driver_and_claim(handle, iface); + else + return claim_interface(handle, iface); +} + +static int op_release_interface(struct libusb_device_handle *handle, int iface) +{ + int r; + + r = release_interface(handle, iface); + if (r) + return r; + + if (handle->auto_detach_kernel_driver) + op_attach_kernel_driver(handle, iface); + + return 0; +} + +static void op_destroy_device(struct libusb_device *dev) +{ + struct linux_device_priv *priv = _device_priv(dev); + if (priv->descriptors) + free(priv->descriptors); + if (priv->sysfs_dir) + free(priv->sysfs_dir); +} + +/* URBs are discarded in reverse order of submission to avoid races. */ +static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plus_one) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = + usbi_transfer_get_os_priv(itransfer); + struct linux_device_handle_priv *dpriv = + _device_handle_priv(transfer->dev_handle); + int i, ret = 0; + struct usbfs_urb *urb; + + for (i = last_plus_one - 1; i >= first; i--) { + if (LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type) + urb = tpriv->iso_urbs[i]; + else + urb = &tpriv->urbs[i]; + + if (0 == ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urb)) + continue; + + if (EINVAL == errno) { + usbi_dbg("URB not found --> assuming ready to be reaped"); + if (i == (last_plus_one - 1)) + ret = LIBUSB_ERROR_NOT_FOUND; + } else if (ENODEV == errno) { + usbi_dbg("Device not found for URB --> assuming ready to be reaped"); + ret = LIBUSB_ERROR_NO_DEVICE; + } else { + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised discard errno %d", errno); + ret = LIBUSB_ERROR_OTHER; + } + } + return ret; +} + +static void free_iso_urbs(struct linux_transfer_priv *tpriv) +{ + int i; + for (i = 0; i < tpriv->num_urbs; i++) { + struct usbfs_urb *urb = tpriv->iso_urbs[i]; + if (!urb) + break; + free(urb); + } + + free(tpriv->iso_urbs); + tpriv->iso_urbs = NULL; +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct linux_device_handle_priv *dpriv = + _device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urbs; + int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int bulk_buffer_len, use_bulk_continuation; + int r; + int i; + + if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) && + !(dpriv->caps & USBFS_CAP_ZERO_PACKET)) + return LIBUSB_ERROR_NOT_SUPPORTED; + + /* + * Older versions of usbfs place a 16kb limit on bulk URBs. We work + * around this by splitting large transfers into 16k blocks, and then + * submit all urbs at once. it would be simpler to submit one urb at + * a time, but there is a big performance gain doing it this way. + * + * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), + * using arbritary large transfers can still be a bad idea though, as + * the kernel needs to allocate physical contiguous memory for this, + * which may fail for large buffers. + * + * The kernel solves this problem by splitting the transfer into + * blocks itself when the host-controller is scatter-gather capable + * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are. + * + * Last, there is the issue of short-transfers when splitting, for + * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION + * is needed, but this is not always available. + */ + if (dpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) { + /* Good! Just submit everything in one go */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else if (dpriv->caps & USBFS_CAP_BULK_CONTINUATION) { + /* Split the transfers and use bulk-continuation to + avoid issues with short-transfers */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 1; + } else if (dpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) { + /* Don't split, assume the kernel can alloc the buffer + (otherwise the submit will fail with -ENOMEM) */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else { + /* Bad, splitting without bulk-continuation, short transfers + which end before the last urb will not work reliable! */ + /* Note we don't warn here as this is "normal" on kernels < + 2.6.32 and not a problem for most applications */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 0; + } + + int num_urbs = transfer->length / bulk_buffer_len; + int last_urb_partial = 0; + + if (transfer->length == 0) { + num_urbs = 1; + } else if ((transfer->length % bulk_buffer_len) > 0) { + last_urb_partial = 1; + num_urbs++; + } + usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, + transfer->length); + urbs = calloc(num_urbs, sizeof(struct usbfs_urb)); + if (!urbs) + return LIBUSB_ERROR_NO_MEM; + tpriv->urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED; + + for (i = 0; i < num_urbs; i++) { + struct usbfs_urb *urb = &urbs[i]; + urb->usercontext = itransfer; + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + urb->type = USBFS_URB_TYPE_BULK; + urb->stream_id = 0; + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + urb->type = USBFS_URB_TYPE_BULK; + urb->stream_id = itransfer->stream_id; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + urb->type = USBFS_URB_TYPE_INTERRUPT; + break; + } + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer + (i * bulk_buffer_len); + /* don't set the short not ok flag for the last URB */ + if (use_bulk_continuation && !is_out && (i < num_urbs - 1)) + urb->flags = USBFS_URB_SHORT_NOT_OK; + if (i == num_urbs - 1 && last_urb_partial) + urb->buffer_length = transfer->length % bulk_buffer_len; + else if (transfer->length == 0) + urb->buffer_length = 0; + else + urb->buffer_length = bulk_buffer_len; + + if (i > 0 && use_bulk_continuation) + urb->flags |= USBFS_URB_BULK_CONTINUATION; + + /* we have already checked that the flag is supported */ + if (is_out && i == num_urbs - 1 && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) + urb->flags |= USBFS_URB_ZERO_PACKET; + + r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r < 0) { + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else { + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg("first URB failed, easy peasy"); + free(urbs); + tpriv->urbs = NULL; + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we may need to discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * - this URB failing may be no error; EREMOTEIO means that + * this transfer simply didn't need all the URBs we submitted + * so, we report that the transfer was submitted successfully and + * in case of error we discard all previous URBs. later when + * the final reap completes we can report error to the user, + * or success if an earlier URB was completed successfully. + */ + tpriv->reap_action = EREMOTEIO == errno ? COMPLETED_EARLY : SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired += num_urbs - i; + + /* If we completed short then don't try to discard. */ + if (COMPLETED_EARLY == tpriv->reap_action) + return 0; + + discard_urbs(itransfer, 0, i); + + usbi_dbg("reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + } + + return 0; +} + +static int submit_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct linux_device_handle_priv *dpriv = + _device_handle_priv(transfer->dev_handle); + struct usbfs_urb **urbs; + size_t alloc_size; + int num_packets = transfer->num_iso_packets; + int i; + int this_urb_len = 0; + int num_urbs = 1; + int packet_offset = 0; + unsigned int packet_len; + unsigned char *urb_buffer = transfer->buffer; + + /* usbfs places arbitrary limits on iso URBs. this limit has changed + * at least three times, and it's difficult to accurately detect which + * limit this running kernel might impose. so we attempt to submit + * whatever the user has provided. if the kernel rejects the request + * due to its size, we return an error indicating such to the user. + */ + + /* calculate how many URBs we need */ + for (i = 0; i < num_packets; i++) { + unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; + packet_len = transfer->iso_packet_desc[i].length; + + if (packet_len > space_remaining) { + num_urbs++; + this_urb_len = packet_len; + /* check that we can actually support this packet length */ + if (this_urb_len > MAX_ISO_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + } else { + this_urb_len += packet_len; + } + } + usbi_dbg("need %d %dk URBs for transfer", num_urbs, MAX_ISO_BUFFER_LENGTH / 1024); + + urbs = calloc(num_urbs, sizeof(*urbs)); + if (!urbs) + return LIBUSB_ERROR_NO_MEM; + + tpriv->iso_urbs = urbs; + tpriv->num_urbs = num_urbs; + tpriv->num_retired = 0; + tpriv->reap_action = NORMAL; + tpriv->iso_packet_offset = 0; + + /* allocate + initialize each URB with the correct number of packets */ + for (i = 0; i < num_urbs; i++) { + struct usbfs_urb *urb; + unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; + int urb_packet_offset = 0; + unsigned char *urb_buffer_orig = urb_buffer; + int j; + int k; + + /* swallow up all the packets we can fit into this URB */ + while (packet_offset < transfer->num_iso_packets) { + packet_len = transfer->iso_packet_desc[packet_offset].length; + if (packet_len <= space_remaining_in_urb) { + /* throw it in */ + urb_packet_offset++; + packet_offset++; + space_remaining_in_urb -= packet_len; + urb_buffer += packet_len; + } else { + /* it can't fit, save it for the next URB */ + break; + } + } + + alloc_size = sizeof(*urb) + + (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc)); + urb = calloc(1, alloc_size); + if (!urb) { + free_iso_urbs(tpriv); + return LIBUSB_ERROR_NO_MEM; + } + urbs[i] = urb; + + /* populate packet lengths */ + for (j = 0, k = packet_offset - urb_packet_offset; + k < packet_offset; k++, j++) { + packet_len = transfer->iso_packet_desc[k].length; + urb->iso_frame_desc[j].length = packet_len; + } + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_ISO; + /* FIXME: interface for non-ASAP data? */ + urb->flags = USBFS_URB_ISO_ASAP; + urb->endpoint = transfer->endpoint; + urb->number_of_packets = urb_packet_offset; + urb->buffer = urb_buffer_orig; + } + + /* submit URBs */ + for (i = 0; i < num_urbs; i++) { + int r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]); + if (r < 0) { + if (errno == ENODEV) { + r = LIBUSB_ERROR_NO_DEVICE; + } else if (errno == EINVAL) { + usbi_warn(TRANSFER_CTX(transfer), + "submiturb failed, transfer too large"); + r = LIBUSB_ERROR_INVALID_PARAM; + } else { + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + r = LIBUSB_ERROR_IO; + } + + /* if the first URB submission fails, we can simply free up and + * return failure immediately. */ + if (i == 0) { + usbi_dbg("first URB failed, easy peasy"); + free_iso_urbs(tpriv); + return r; + } + + /* if it's not the first URB that failed, the situation is a bit + * tricky. we must discard all previous URBs. there are + * complications: + * - discarding is asynchronous - discarded urbs will be reaped + * later. the user must not have freed the transfer when the + * discarded URBs are reaped, otherwise libusb will be using + * freed memory. + * - the earlier URBs may have completed successfully and we do + * not want to throw away any data. + * so, in this case we discard all the previous URBs BUT we report + * that the transfer was submitted successfully. then later when + * the final discard completes we can report error to the user. + */ + tpriv->reap_action = SUBMIT_FAILED; + + /* The URBs we haven't submitted yet we count as already + * retired. */ + tpriv->num_retired = num_urbs - i; + discard_urbs(itransfer, 0, i); + + usbi_dbg("reporting successful submission but waiting for %d " + "discards before reporting error", i); + return 0; + } + } + + return 0; +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_device_handle_priv *dpriv = + _device_handle_priv(transfer->dev_handle); + struct usbfs_urb *urb; + int r; + + if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + urb = calloc(1, sizeof(struct usbfs_urb)); + if (!urb) + return LIBUSB_ERROR_NO_MEM; + tpriv->urbs = urb; + tpriv->num_urbs = 1; + tpriv->reap_action = NORMAL; + + urb->usercontext = itransfer; + urb->type = USBFS_URB_TYPE_CONTROL; + urb->endpoint = transfer->endpoint; + urb->buffer = transfer->buffer; + urb->buffer_length = transfer->length; + + r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); + if (r < 0) { + free(urb); + tpriv->urbs = NULL; + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(TRANSFER_CTX(transfer), + "submiturb failed error %d errno=%d", r, errno); + return LIBUSB_ERROR_IO; + } + return 0; +} + +static int op_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), + "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int op_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int r; + + if (!tpriv->urbs) + return LIBUSB_ERROR_NOT_FOUND; + + r = discard_urbs(itransfer, 0, tpriv->num_urbs); + if (r != 0) + return r; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + if (tpriv->reap_action == ERROR) + break; + /* else, fall through */ + default: + tpriv->reap_action = CANCELLED; + } + + return 0; +} + +static void op_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + + /* urbs can be freed also in submit_transfer so lock mutex first */ + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (tpriv->urbs) { + free(tpriv->urbs); + tpriv->urbs = NULL; + } + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (tpriv->iso_urbs) { + free_iso_urbs(tpriv); + tpriv->iso_urbs = NULL; + } + break; + default: + usbi_err(TRANSFER_CTX(transfer), + "unknown endpoint type %d", transfer->type); + } +} + +static int handle_bulk_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int urb_idx = urb - tpriv->urbs; + + usbi_mutex_lock(&itransfer->lock); + usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status, + urb_idx + 1, tpriv->num_urbs); + + tpriv->num_retired++; + + if (tpriv->reap_action != NORMAL) { + /* cancelled, submit_fail, or completed early */ + usbi_dbg("abnormal reap: urb status %d", urb->status); + + /* even though we're in the process of cancelling, it's possible that + * we may receive some data in these URBs that we don't want to lose. + * examples: + * 1. while the kernel is cancelling all the packets that make up an + * URB, a few of them might complete. so we get back a successful + * cancellation *and* some data. + * 2. we receive a short URB which marks the early completion condition, + * so we start cancelling the remaining URBs. however, we're too + * slow and another URB completes (or at least completes partially). + * (this can't happen since we always use BULK_CONTINUATION.) + * + * When this happens, our objectives are not to lose any "surplus" data, + * and also to stick it at the end of the previously-received data + * (closing any holes), so that libusb reports the total amount of + * transferred data and presents it in a contiguous chunk. + */ + if (urb->actual_length > 0) { + unsigned char *target = transfer->buffer + itransfer->transferred; + usbi_dbg("received %d bytes of surplus data", urb->actual_length); + if (urb->buffer != target) { + usbi_dbg("moving surplus data from offset %d to offset %d", + (unsigned char *) urb->buffer - transfer->buffer, + target - transfer->buffer); + memmove(target, urb->buffer, urb->actual_length); + } + itransfer->transferred += urb->actual_length; + } + + if (tpriv->num_retired == tpriv->num_urbs) { + usbi_dbg("abnormal reap: last URB handled, reporting"); + if (tpriv->reap_action != COMPLETED_EARLY && + tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + goto completed; + } + goto out_unlock; + } + + itransfer->transferred += urb->actual_length; + + /* Many of these errors can occur on *any* urb of a multi-urb + * transfer. When they do, we tear down the rest of the transfer. + */ + switch (urb->status) { + case 0: + break; + case -EREMOTEIO: /* short transfer */ + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg("device removed"); + tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE; + goto cancel_remaining; + case -EPIPE: + usbi_dbg("detected endpoint stall"); + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_STALL; + goto cancel_remaining; + case -EOVERFLOW: + /* overflow can only ever occur in the last urb */ + usbi_dbg("overflow, actual_length=%d", urb->actual_length); + if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED) + tpriv->reap_status = LIBUSB_TRANSFER_OVERFLOW; + goto completed; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + usbi_dbg("low level error %d", urb->status); + tpriv->reap_action = ERROR; + goto cancel_remaining; + default: + usbi_warn(ITRANSFER_CTX(itransfer), + "unrecognised urb status %d", urb->status); + tpriv->reap_action = ERROR; + goto cancel_remaining; + } + + /* if we're the last urb or we got less data than requested then we're + * done */ + if (urb_idx == tpriv->num_urbs - 1) { + usbi_dbg("last URB in transfer --> complete!"); + goto completed; + } else if (urb->actual_length < urb->buffer_length) { + usbi_dbg("short transfer %d/%d --> complete!", + urb->actual_length, urb->buffer_length); + if (tpriv->reap_action == NORMAL) + tpriv->reap_action = COMPLETED_EARLY; + } else + goto out_unlock; + +cancel_remaining: + if (ERROR == tpriv->reap_action && LIBUSB_TRANSFER_COMPLETED == tpriv->reap_status) + tpriv->reap_status = LIBUSB_TRANSFER_ERROR; + + if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */ + goto completed; + + /* cancel remaining urbs and wait for their completion before + * reporting results */ + discard_urbs(itransfer, urb_idx + 1, tpriv->num_urbs); + +out_unlock: + usbi_mutex_unlock(&itransfer->lock); + return 0; + +completed: + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return CANCELLED == tpriv->reap_action ? + usbi_handle_transfer_cancellation(itransfer) : + usbi_handle_transfer_completion(itransfer, tpriv->reap_status); +} + +static int handle_iso_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int num_urbs = tpriv->num_urbs; + int urb_idx = 0; + int i; + enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; + + usbi_mutex_lock(&itransfer->lock); + for (i = 0; i < num_urbs; i++) { + if (urb == tpriv->iso_urbs[i]) { + urb_idx = i + 1; + break; + } + } + if (urb_idx == 0) { + usbi_err(TRANSFER_CTX(transfer), "could not locate urb!"); + usbi_mutex_unlock(&itransfer->lock); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status, + urb_idx, num_urbs); + + /* copy isochronous results back in */ + + for (i = 0; i < urb->number_of_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[tpriv->iso_packet_offset++]; + lib_desc->status = LIBUSB_TRANSFER_COMPLETED; + switch (urb_desc->status) { + case 0: + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg("device removed"); + lib_desc->status = LIBUSB_TRANSFER_NO_DEVICE; + break; + case -EPIPE: + usbi_dbg("detected endpoint stall"); + lib_desc->status = LIBUSB_TRANSFER_STALL; + break; + case -EOVERFLOW: + usbi_dbg("overflow error"); + lib_desc->status = LIBUSB_TRANSFER_OVERFLOW; + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + case -EXDEV: + usbi_dbg("low-level USB error %d", urb_desc->status); + lib_desc->status = LIBUSB_TRANSFER_ERROR; + break; + default: + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised urb status %d", urb_desc->status); + lib_desc->status = LIBUSB_TRANSFER_ERROR; + break; + } + lib_desc->actual_length = urb_desc->actual_length; + } + + tpriv->num_retired++; + + if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */ + usbi_dbg("CANCEL: urb status %d", urb->status); + + if (tpriv->num_retired == num_urbs) { + usbi_dbg("CANCEL: last URB handled, reporting"); + free_iso_urbs(tpriv); + if (tpriv->reap_action == CANCELLED) { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } else { + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, + LIBUSB_TRANSFER_ERROR); + } + } + goto out; + } + + switch (urb->status) { + case 0: + break; + case -ENOENT: /* cancelled */ + case -ECONNRESET: + break; + case -ESHUTDOWN: + usbi_dbg("device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + default: + usbi_warn(TRANSFER_CTX(transfer), + "unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + /* if we're the last urb then we're done */ + if (urb_idx == num_urbs) { + usbi_dbg("last URB in transfer --> complete!"); + free_iso_urbs(tpriv); + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); + } + +out: + usbi_mutex_unlock(&itransfer->lock); + return 0; +} + +static int handle_control_completion(struct usbi_transfer *itransfer, + struct usbfs_urb *urb) +{ + struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + int status; + + usbi_mutex_lock(&itransfer->lock); + usbi_dbg("handling completion status %d", urb->status); + + itransfer->transferred += urb->actual_length; + + if (tpriv->reap_action == CANCELLED) { + if (urb->status != 0 && urb->status != -ENOENT) + usbi_warn(ITRANSFER_CTX(itransfer), + "cancel: unrecognised urb status %d", urb->status); + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_cancellation(itransfer); + } + + switch (urb->status) { + case 0: + status = LIBUSB_TRANSFER_COMPLETED; + break; + case -ENOENT: /* cancelled */ + status = LIBUSB_TRANSFER_CANCELLED; + break; + case -ENODEV: + case -ESHUTDOWN: + usbi_dbg("device removed"); + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + case -EPIPE: + usbi_dbg("unsupported control request"); + status = LIBUSB_TRANSFER_STALL; + break; + case -EOVERFLOW: + usbi_dbg("control overflow error"); + status = LIBUSB_TRANSFER_OVERFLOW; + break; + case -ETIME: + case -EPROTO: + case -EILSEQ: + case -ECOMM: + case -ENOSR: + usbi_dbg("low-level bus error occurred"); + status = LIBUSB_TRANSFER_ERROR; + break; + default: + usbi_warn(ITRANSFER_CTX(itransfer), + "unrecognised urb status %d", urb->status); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + free(tpriv->urbs); + tpriv->urbs = NULL; + usbi_mutex_unlock(&itransfer->lock); + return usbi_handle_transfer_completion(itransfer, status); +} + +static int reap_for_handle(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); + int r; + struct usbfs_urb *urb; + struct usbi_transfer *itransfer; + struct libusb_transfer *transfer; + + r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb); + if (r == -1 && errno == EAGAIN) + return 1; + if (r < 0) { + if (errno == ENODEV) + return LIBUSB_ERROR_NO_DEVICE; + + usbi_err(HANDLE_CTX(handle), "reap failed error %d errno=%d", + r, errno); + return LIBUSB_ERROR_IO; + } + + itransfer = urb->usercontext; + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status, + urb->actual_length); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return handle_iso_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return handle_bulk_completion(itransfer, urb); + case LIBUSB_TRANSFER_TYPE_CONTROL: + return handle_control_completion(itransfer, urb); + default: + usbi_err(HANDLE_CTX(handle), "unrecognised endpoint type %x", + transfer->type); + return LIBUSB_ERROR_OTHER; + } +} + +static int op_handle_events(struct libusb_context *ctx, + struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + int r; + unsigned int i = 0; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + struct pollfd *pollfd = &fds[i]; + struct libusb_device_handle *handle; + struct linux_device_handle_priv *hpriv = NULL; + + if (!pollfd->revents) + continue; + + num_ready--; + list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { + hpriv = _device_handle_priv(handle); + if (hpriv->fd == pollfd->fd) + break; + } + + if (!hpriv || hpriv->fd != pollfd->fd) { + usbi_err(ctx, "cannot find handle for fd %d", + pollfd->fd); + continue; + } + + if (pollfd->revents & POLLERR) { + /* remove the fd from the pollfd set so that it doesn't continuously + * trigger an event, and flag that it has been removed so op_close() + * doesn't try to remove it a second time */ + usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd); + hpriv->fd_removed = 1; + + /* device will still be marked as attached if hotplug monitor thread + * hasn't processed remove event yet */ + usbi_mutex_static_lock(&linux_hotplug_lock); + if (handle->dev->attached) + linux_device_disconnected(handle->dev->bus_number, + handle->dev->device_address); + usbi_mutex_static_unlock(&linux_hotplug_lock); + + if (hpriv->caps & USBFS_CAP_REAP_AFTER_DISCONNECT) { + do { + r = reap_for_handle(handle); + } while (r == 0); + } + + usbi_handle_disconnect(handle); + continue; + } + + do { + r = reap_for_handle(handle); + } while (r == 0); + if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE) + continue; + else if (r < 0) + goto out; + } + + r = 0; +out: + usbi_mutex_unlock(&ctx->open_devs_lock); + return r; +} + +static int op_clock_gettime(int clk_id, struct timespec *tp) +{ + switch (clk_id) { + case USBI_CLOCK_MONOTONIC: + return clock_gettime(monotonic_clkid, tp); + case USBI_CLOCK_REALTIME: + return clock_gettime(CLOCK_REALTIME, tp); + default: + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +#ifdef USBI_TIMERFD_AVAILABLE +static clockid_t op_get_timerfd_clockid(void) +{ + return monotonic_clkid; + +} +#endif + +const struct usbi_os_backend linux_usbfs_backend = { + .name = "Linux usbfs", + .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, + .init = op_init, + .exit = op_exit, + .get_device_list = NULL, + .hotplug_poll = op_hotplug_poll, + .get_device_descriptor = op_get_device_descriptor, + .get_active_config_descriptor = op_get_active_config_descriptor, + .get_config_descriptor = op_get_config_descriptor, + .get_config_descriptor_by_value = op_get_config_descriptor_by_value, + + .open = op_open, + .close = op_close, + .get_configuration = op_get_configuration, + .set_configuration = op_set_configuration, + .claim_interface = op_claim_interface, + .release_interface = op_release_interface, + + .set_interface_altsetting = op_set_interface, + .clear_halt = op_clear_halt, + .reset_device = op_reset_device, + + .alloc_streams = op_alloc_streams, + .free_streams = op_free_streams, + + .dev_mem_alloc = op_dev_mem_alloc, + .dev_mem_free = op_dev_mem_free, + + .kernel_driver_active = op_kernel_driver_active, + .detach_kernel_driver = op_detach_kernel_driver, + .attach_kernel_driver = op_attach_kernel_driver, + + .destroy_device = op_destroy_device, + + .submit_transfer = op_submit_transfer, + .cancel_transfer = op_cancel_transfer, + .clear_transfer_priv = op_clear_transfer_priv, + + .handle_events = op_handle_events, + + .clock_gettime = op_clock_gettime, + +#ifdef USBI_TIMERFD_AVAILABLE + .get_timerfd_clockid = op_get_timerfd_clockid, +#endif + + .device_priv_size = sizeof(struct linux_device_priv), + .device_handle_priv_size = sizeof(struct linux_device_handle_priv), + .transfer_priv_size = sizeof(struct linux_transfer_priv), +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.h new file mode 100644 index 0000000..8bd3ebc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.h @@ -0,0 +1,193 @@ +/* + * usbfs header structures + * Copyright © 2007 Daniel Drake + * Copyright © 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_USBFS_H +#define LIBUSB_USBFS_H + +#include + +#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" + +struct usbfs_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + + uint32_t timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_bulktransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USBFS_MAXDRIVERNAME 255 + +struct usbfs_getdriver { + unsigned int interface; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 +#define USBFS_URB_ZERO_PACKET 0x40 + +enum usbfs_urb_type { + USBFS_URB_TYPE_ISO = 0, + USBFS_URB_TYPE_INTERRUPT = 1, + USBFS_URB_TYPE_CONTROL = 2, + USBFS_URB_TYPE_BULK = 3, +}; + +struct usbfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +#define MAX_ISO_BUFFER_LENGTH 49152 * 128 +#define MAX_BULK_BUFFER_LENGTH 16384 +#define MAX_CTRL_BUFFER_LENGTH 4096 + +struct usbfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + union { + int number_of_packets; /* Only used for isoc urbs */ + unsigned int stream_id; /* Only used with bulk streams */ + }; + int error_count; + unsigned int signr; + void *usercontext; + struct usbfs_iso_packet_desc iso_frame_desc[0]; +}; + +struct usbfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usbfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +struct usbfs_hub_portinfo { + unsigned char numports; + unsigned char port[127]; /* port to device num mapping */ +}; + +#define USBFS_CAP_ZERO_PACKET 0x01 +#define USBFS_CAP_BULK_CONTINUATION 0x02 +#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBFS_CAP_BULK_SCATTER_GATHER 0x08 +#define USBFS_CAP_REAP_AFTER_DISCONNECT 0x10 + +#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 +#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 + +struct usbfs_disconnect_claim { + unsigned int interface; + unsigned int flags; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +struct usbfs_streams { + unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */ + unsigned int num_eps; + unsigned char eps[0]; +}; + +#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) +#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) +#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) +#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) +#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) +#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) +#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) +#define IOCTL_USBFS_DISCARDURB _IO('U', 11) +#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) +#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) +#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) +#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) +#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) +#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) +#define IOCTL_USBFS_RESET _IO('U', 20) +#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) +#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int) +#define IOCTL_USBFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32) +#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim) +#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams) +#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams) + +extern usbi_mutex_static_t linux_hotplug_lock; + +#if defined(HAVE_LIBUDEV) +int linux_udev_start_event_monitor(void); +int linux_udev_stop_event_monitor(void); +int linux_udev_scan_devices(struct libusb_context *ctx); +void linux_udev_hotplug_poll(void); +#else +int linux_netlink_start_event_monitor(void); +int linux_netlink_stop_event_monitor(void); +void linux_netlink_hotplug_poll(void); +#endif + +void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name); +void linux_device_disconnected(uint8_t busnum, uint8_t devaddr); + +int linux_get_device_address (struct libusb_context *ctx, int detached, + uint8_t *busnum, uint8_t *devaddr, const char *dev_node, + const char *sys_name); +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir); + +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/netbsd_usb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/netbsd_usb.c new file mode 100644 index 0000000..ad1ede7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/netbsd_usb.c @@ -0,0 +1,677 @@ +/* + * Copyright © 2011 Martin Pieuchot + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "libusbi.h" + +struct device_priv { + char devnode[16]; + int fd; + + unsigned char *cdesc; /* active config descriptor */ + usb_device_descriptor_t ddesc; /* usb device descriptor */ +}; + +struct handle_priv { + int endpoints[USB_MAX_ENDPOINTS]; +}; + +/* + * Backend functions + */ +static int netbsd_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int netbsd_open(struct libusb_device_handle *); +static void netbsd_close(struct libusb_device_handle *); + +static int netbsd_get_device_descriptor(struct libusb_device *, unsigned char *, + int *); +static int netbsd_get_active_config_descriptor(struct libusb_device *, + unsigned char *, size_t, int *); +static int netbsd_get_config_descriptor(struct libusb_device *, uint8_t, + unsigned char *, size_t, int *); + +static int netbsd_get_configuration(struct libusb_device_handle *, int *); +static int netbsd_set_configuration(struct libusb_device_handle *, int); + +static int netbsd_claim_interface(struct libusb_device_handle *, int); +static int netbsd_release_interface(struct libusb_device_handle *, int); + +static int netbsd_set_interface_altsetting(struct libusb_device_handle *, int, + int); +static int netbsd_clear_halt(struct libusb_device_handle *, unsigned char); +static int netbsd_reset_device(struct libusb_device_handle *); +static void netbsd_destroy_device(struct libusb_device *); + +static int netbsd_submit_transfer(struct usbi_transfer *); +static int netbsd_cancel_transfer(struct usbi_transfer *); +static void netbsd_clear_transfer_priv(struct usbi_transfer *); +static int netbsd_handle_transfer_completion(struct usbi_transfer *); +static int netbsd_clock_gettime(int, struct timespec *); + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int _cache_active_config_descriptor(struct libusb_device *, int); +static int _sync_control_transfer(struct usbi_transfer *); +static int _sync_gen_transfer(struct usbi_transfer *); +static int _access_endpoint(struct libusb_transfer *); + +const struct usbi_os_backend netbsd_backend = { + "Synchronous NetBSD backend", + 0, + NULL, /* init() */ + NULL, /* exit() */ + netbsd_get_device_list, + NULL, /* hotplug_poll */ + netbsd_open, + netbsd_close, + + netbsd_get_device_descriptor, + netbsd_get_active_config_descriptor, + netbsd_get_config_descriptor, + NULL, /* get_config_descriptor_by_value() */ + + netbsd_get_configuration, + netbsd_set_configuration, + + netbsd_claim_interface, + netbsd_release_interface, + + netbsd_set_interface_altsetting, + netbsd_clear_halt, + netbsd_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + NULL, /* dev_mem_alloc() */ + NULL, /* dev_mem_free() */ + + NULL, /* kernel_driver_active() */ + NULL, /* detach_kernel_driver() */ + NULL, /* attach_kernel_driver() */ + + netbsd_destroy_device, + + netbsd_submit_transfer, + netbsd_cancel_transfer, + netbsd_clear_transfer_priv, + + NULL, /* handle_events() */ + netbsd_handle_transfer_completion, + + netbsd_clock_gettime, + sizeof(struct device_priv), + sizeof(struct handle_priv), + 0, /* transfer_priv_size */ +}; + +int +netbsd_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + struct libusb_device *dev; + struct device_priv *dpriv; + struct usb_device_info di; + unsigned long session_id; + char devnode[16]; + int fd, err, i; + + usbi_dbg(""); + + /* Only ugen(4) is supported */ + for (i = 0; i < USB_MAX_DEVICES; i++) { + /* Control endpoint is always .00 */ + snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i); + + if ((fd = open(devnode, O_RDONLY)) < 0) { + if (errno != ENOENT && errno != ENXIO) + usbi_err(ctx, "could not open %s", devnode); + continue; + } + + if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0) + continue; + + session_id = (di.udi_bus << 8 | di.udi_addr); + dev = usbi_get_device_by_session_id(ctx, session_id); + + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) + return (LIBUSB_ERROR_NO_MEM); + + dev->bus_number = di.udi_bus; + dev->device_address = di.udi_addr; + dev->speed = di.udi_speed; + + dpriv = (struct device_priv *)dev->os_priv; + strlcpy(dpriv->devnode, devnode, sizeof(devnode)); + dpriv->fd = -1; + + if (ioctl(fd, USB_GET_DEVICE_DESC, &dpriv->ddesc) < 0) { + err = errno; + goto error; + } + + dpriv->cdesc = NULL; + if (_cache_active_config_descriptor(dev, fd)) { + err = errno; + goto error; + } + + if ((err = usbi_sanitize_device(dev))) + goto error; + } + close(fd); + + if (discovered_devs_append(*discdevs, dev) == NULL) + return (LIBUSB_ERROR_NO_MEM); + + libusb_unref_device(dev); + } + + return (LIBUSB_SUCCESS); + +error: + close(fd); + libusb_unref_device(dev); + return _errno_to_libusb(err); +} + +int +netbsd_open(struct libusb_device_handle *handle) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + dpriv->fd = open(dpriv->devnode, O_RDWR); + if (dpriv->fd < 0) { + dpriv->fd = open(dpriv->devnode, O_RDONLY); + if (dpriv->fd < 0) + return _errno_to_libusb(errno); + } + + usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd); + + return (LIBUSB_SUCCESS); +} + +void +netbsd_close(struct libusb_device_handle *handle) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + usbi_dbg("close: fd %d", dpriv->fd); + + close(dpriv->fd); + dpriv->fd = -1; +} + +int +netbsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf, + int *host_endian) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + + usbi_dbg(""); + + memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH); + + *host_endian = 0; + + return (LIBUSB_SUCCESS); +} + +int +netbsd_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buf, size_t len, int *host_endian) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + usb_config_descriptor_t *ucd; + + ucd = (usb_config_descriptor_t *) dpriv->cdesc; + len = MIN(len, UGETW(ucd->wTotalLength)); + + usbi_dbg("len %d", len); + + memcpy(buf, dpriv->cdesc, len); + + *host_endian = 0; + + return len; +} + +int +netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + unsigned char *buf, size_t len, int *host_endian) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + struct usb_full_desc ufd; + int fd, err; + + usbi_dbg("index %d, len %d", idx, len); + + /* A config descriptor may be requested before opening the device */ + if (dpriv->fd >= 0) { + fd = dpriv->fd; + } else { + fd = open(dpriv->devnode, O_RDONLY); + if (fd < 0) + return _errno_to_libusb(errno); + } + + ufd.ufd_config_index = idx; + ufd.ufd_size = len; + ufd.ufd_data = buf; + + if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { + err = errno; + if (dpriv->fd < 0) + close(fd); + return _errno_to_libusb(err); + } + + if (dpriv->fd < 0) + close(fd); + + *host_endian = 0; + + return len; +} + +int +netbsd_get_configuration(struct libusb_device_handle *handle, int *config) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + usbi_dbg(""); + + if (ioctl(dpriv->fd, USB_GET_CONFIG, config) < 0) + return _errno_to_libusb(errno); + + usbi_dbg("configuration %d", *config); + + return (LIBUSB_SUCCESS); +} + +int +netbsd_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + usbi_dbg("configuration %d", config); + + if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) + return _errno_to_libusb(errno); + + return _cache_active_config_descriptor(handle->dev, dpriv->fd); +} + +int +netbsd_claim_interface(struct libusb_device_handle *handle, int iface) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + int i; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + hpriv->endpoints[i] = -1; + + return (LIBUSB_SUCCESS); +} + +int +netbsd_release_interface(struct libusb_device_handle *handle, int iface) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + int i; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + if (hpriv->endpoints[i] >= 0) + close(hpriv->endpoints[i]); + + return (LIBUSB_SUCCESS); +} + +int +netbsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, + int altsetting) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + struct usb_alt_interface intf; + + usbi_dbg("iface %d, setting %d", iface, altsetting); + + memset(&intf, 0, sizeof(intf)); + + intf.uai_interface_index = iface; + intf.uai_alt_no = altsetting; + + if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) + return _errno_to_libusb(errno); + + return (LIBUSB_SUCCESS); +} + +int +netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + struct usb_ctl_request req; + + usbi_dbg(""); + + req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; + req.ucr_request.bRequest = UR_CLEAR_FEATURE; + USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); + USETW(req.ucr_request.wIndex, endpoint); + USETW(req.ucr_request.wLength, 0); + + if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0) + return _errno_to_libusb(errno); + + return (LIBUSB_SUCCESS); +} + +int +netbsd_reset_device(struct libusb_device_handle *handle) +{ + usbi_dbg(""); + + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +void +netbsd_destroy_device(struct libusb_device *dev) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + + usbi_dbg(""); + + free(dpriv->cdesc); +} + +int +netbsd_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct handle_priv *hpriv; + int err = 0; + + usbi_dbg(""); + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + err = _sync_control_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (IS_XFEROUT(transfer)) { + /* Isochronous write is not supported */ + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + if (err) + return (err); + + usbi_signal_transfer_completion(itransfer); + + return (LIBUSB_SUCCESS); +} + +int +netbsd_cancel_transfer(struct usbi_transfer *itransfer) +{ + usbi_dbg(""); + + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +void +netbsd_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + usbi_dbg(""); + + /* Nothing to do */ +} + +int +netbsd_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +netbsd_clock_gettime(int clkid, struct timespec *tp) +{ + usbi_dbg("clock %d", clkid); + + if (clkid == USBI_CLOCK_REALTIME) + return clock_gettime(CLOCK_REALTIME, tp); + + if (clkid == USBI_CLOCK_MONOTONIC) + return clock_gettime(CLOCK_MONOTONIC, tp); + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +int +_errno_to_libusb(int err) +{ + switch (err) { + case EIO: + return (LIBUSB_ERROR_IO); + case EACCES: + return (LIBUSB_ERROR_ACCESS); + case ENOENT: + return (LIBUSB_ERROR_NO_DEVICE); + case ENOMEM: + return (LIBUSB_ERROR_NO_MEM); + } + + usbi_dbg("error: %s", strerror(err)); + + return (LIBUSB_ERROR_OTHER); +} + +int +_cache_active_config_descriptor(struct libusb_device *dev, int fd) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + struct usb_config_desc ucd; + struct usb_full_desc ufd; + unsigned char* buf; + int len; + + usbi_dbg("fd %d", fd); + + ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX; + + if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0) + return _errno_to_libusb(errno); + + usbi_dbg("active bLength %d", ucd.ucd_desc.bLength); + + len = UGETW(ucd.ucd_desc.wTotalLength); + buf = malloc(len); + if (buf == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ufd.ufd_config_index = ucd.ucd_config_index; + ufd.ufd_size = len; + ufd.ufd_data = buf; + + usbi_dbg("index %d, len %d", ufd.ufd_config_index, len); + + if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { + free(buf); + return _errno_to_libusb(errno); + } + + if (dpriv->cdesc) + free(dpriv->cdesc); + dpriv->cdesc = buf; + + return (0); +} + +int +_sync_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_control_setup *setup; + struct device_priv *dpriv; + struct usb_ctl_request req; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; + setup = (struct libusb_control_setup *)transfer->buffer; + + usbi_dbg("type %d request %d value %d index %d length %d timeout %d", + setup->bmRequestType, setup->bRequest, + libusb_le16_to_cpu(setup->wValue), + libusb_le16_to_cpu(setup->wIndex), + libusb_le16_to_cpu(setup->wLength), transfer->timeout); + + req.ucr_request.bmRequestType = setup->bmRequestType; + req.ucr_request.bRequest = setup->bRequest; + /* Don't use USETW, libusb already deals with the endianness */ + (*(uint16_t *)req.ucr_request.wValue) = setup->wValue; + (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; + (*(uint16_t *)req.ucr_request.wLength) = setup->wLength; + req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + req.ucr_flags = USBD_SHORT_XFER_OK; + + if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) + return _errno_to_libusb(errno); + + if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = req.ucr_actlen; + + usbi_dbg("transferred %d", itransfer->transferred); + + return (0); +} + +int +_access_endpoint(struct libusb_transfer *transfer) +{ + struct handle_priv *hpriv; + struct device_priv *dpriv; + char *s, devnode[16]; + int fd, endpt; + mode_t mode; + + hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; + dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; + + endpt = UE_GET_ADDR(transfer->endpoint); + mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; + + usbi_dbg("endpoint %d mode %d", endpt, mode); + + if (hpriv->endpoints[endpt] < 0) { + /* Pick the right node given the control one */ + strlcpy(devnode, dpriv->devnode, sizeof(devnode)); + s = strchr(devnode, '.'); + snprintf(s, 4, ".%02d", endpt); + + /* We may need to read/write to the same endpoint later. */ + if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) + if ((fd = open(devnode, mode)) < 0) + return (-1); + + hpriv->endpoints[endpt] = fd; + } + + return (hpriv->endpoints[endpt]); +} + +int +_sync_gen_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + int fd, nr = 1; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + /* + * Bulk, Interrupt or Isochronous transfer depends on the + * endpoint and thus the node to open. + */ + if ((fd = _access_endpoint(transfer)) < 0) + return _errno_to_libusb(errno); + + if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) + return _errno_to_libusb(errno); + + if (IS_XFERIN(transfer)) { + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0) + return _errno_to_libusb(errno); + + nr = read(fd, transfer->buffer, transfer->length); + } else { + nr = write(fd, transfer->buffer, transfer->length); + } + + if (nr < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = nr; + + return (0); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/openbsd_usb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/openbsd_usb.c new file mode 100644 index 0000000..c660257 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/openbsd_usb.c @@ -0,0 +1,771 @@ +/* + * Copyright © 2011-2013 Martin Pieuchot + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "libusbi.h" + +struct device_priv { + char *devname; /* name of the ugen(4) node */ + int fd; /* device file descriptor */ + + unsigned char *cdesc; /* active config descriptor */ + usb_device_descriptor_t ddesc; /* usb device descriptor */ +}; + +struct handle_priv { + int endpoints[USB_MAX_ENDPOINTS]; +}; + +/* + * Backend functions + */ +static int obsd_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int obsd_open(struct libusb_device_handle *); +static void obsd_close(struct libusb_device_handle *); + +static int obsd_get_device_descriptor(struct libusb_device *, unsigned char *, + int *); +static int obsd_get_active_config_descriptor(struct libusb_device *, + unsigned char *, size_t, int *); +static int obsd_get_config_descriptor(struct libusb_device *, uint8_t, + unsigned char *, size_t, int *); + +static int obsd_get_configuration(struct libusb_device_handle *, int *); +static int obsd_set_configuration(struct libusb_device_handle *, int); + +static int obsd_claim_interface(struct libusb_device_handle *, int); +static int obsd_release_interface(struct libusb_device_handle *, int); + +static int obsd_set_interface_altsetting(struct libusb_device_handle *, int, + int); +static int obsd_clear_halt(struct libusb_device_handle *, unsigned char); +static int obsd_reset_device(struct libusb_device_handle *); +static void obsd_destroy_device(struct libusb_device *); + +static int obsd_submit_transfer(struct usbi_transfer *); +static int obsd_cancel_transfer(struct usbi_transfer *); +static void obsd_clear_transfer_priv(struct usbi_transfer *); +static int obsd_handle_transfer_completion(struct usbi_transfer *); +static int obsd_clock_gettime(int, struct timespec *); + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int _cache_active_config_descriptor(struct libusb_device *); +static int _sync_control_transfer(struct usbi_transfer *); +static int _sync_gen_transfer(struct usbi_transfer *); +static int _access_endpoint(struct libusb_transfer *); + +static int _bus_open(int); + + +const struct usbi_os_backend openbsd_backend = { + "Synchronous OpenBSD backend", + 0, + NULL, /* init() */ + NULL, /* exit() */ + obsd_get_device_list, + NULL, /* hotplug_poll */ + obsd_open, + obsd_close, + + obsd_get_device_descriptor, + obsd_get_active_config_descriptor, + obsd_get_config_descriptor, + NULL, /* get_config_descriptor_by_value() */ + + obsd_get_configuration, + obsd_set_configuration, + + obsd_claim_interface, + obsd_release_interface, + + obsd_set_interface_altsetting, + obsd_clear_halt, + obsd_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + NULL, /* dev_mem_alloc() */ + NULL, /* dev_mem_free() */ + + NULL, /* kernel_driver_active() */ + NULL, /* detach_kernel_driver() */ + NULL, /* attach_kernel_driver() */ + + obsd_destroy_device, + + obsd_submit_transfer, + obsd_cancel_transfer, + obsd_clear_transfer_priv, + + NULL, /* handle_events() */ + obsd_handle_transfer_completion, + + obsd_clock_gettime, + sizeof(struct device_priv), + sizeof(struct handle_priv), + 0, /* transfer_priv_size */ +}; + +#define DEVPATH "/dev/" +#define USBDEV DEVPATH "usb" + +int +obsd_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + struct discovered_devs *ddd; + struct libusb_device *dev; + struct device_priv *dpriv; + struct usb_device_info di; + struct usb_device_ddesc dd; + unsigned long session_id; + char devices[USB_MAX_DEVICES]; + char busnode[16]; + char *udevname; + int fd, addr, i, j; + + usbi_dbg(""); + + for (i = 0; i < 8; i++) { + snprintf(busnode, sizeof(busnode), USBDEV "%d", i); + + if ((fd = open(busnode, O_RDWR)) < 0) { + if (errno != ENOENT && errno != ENXIO) + usbi_err(ctx, "could not open %s", busnode); + continue; + } + + bzero(devices, sizeof(devices)); + for (addr = 1; addr < USB_MAX_DEVICES; addr++) { + if (devices[addr]) + continue; + + di.udi_addr = addr; + if (ioctl(fd, USB_DEVICEINFO, &di) < 0) + continue; + + /* + * XXX If ugen(4) is attached to the USB device + * it will be used. + */ + udevname = NULL; + for (j = 0; j < USB_MAX_DEVNAMES; j++) + if (!strncmp("ugen", di.udi_devnames[j], 4)) { + udevname = strdup(di.udi_devnames[j]); + break; + } + + session_id = (di.udi_bus << 8 | di.udi_addr); + dev = usbi_get_device_by_session_id(ctx, session_id); + + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + close(fd); + return (LIBUSB_ERROR_NO_MEM); + } + + dev->bus_number = di.udi_bus; + dev->device_address = di.udi_addr; + dev->speed = di.udi_speed; + + dpriv = (struct device_priv *)dev->os_priv; + dpriv->fd = -1; + dpriv->cdesc = NULL; + dpriv->devname = udevname; + + dd.udd_bus = di.udi_bus; + dd.udd_addr = di.udi_addr; + if (ioctl(fd, USB_DEVICE_GET_DDESC, &dd) < 0) { + libusb_unref_device(dev); + continue; + } + dpriv->ddesc = dd.udd_desc; + + if (_cache_active_config_descriptor(dev)) { + libusb_unref_device(dev); + continue; + } + + if (usbi_sanitize_device(dev)) { + libusb_unref_device(dev); + continue; + } + } + + ddd = discovered_devs_append(*discdevs, dev); + if (ddd == NULL) { + close(fd); + return (LIBUSB_ERROR_NO_MEM); + } + libusb_unref_device(dev); + + *discdevs = ddd; + devices[addr] = 1; + } + + close(fd); + } + + return (LIBUSB_SUCCESS); +} + +int +obsd_open(struct libusb_device_handle *handle) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + char devnode[16]; + + if (dpriv->devname) { + /* + * Only open ugen(4) attached devices read-write, all + * read-only operations are done through the bus node. + */ + snprintf(devnode, sizeof(devnode), DEVPATH "%s.00", + dpriv->devname); + dpriv->fd = open(devnode, O_RDWR); + if (dpriv->fd < 0) + return _errno_to_libusb(errno); + + usbi_dbg("open %s: fd %d", devnode, dpriv->fd); + } + + return (LIBUSB_SUCCESS); +} + +void +obsd_close(struct libusb_device_handle *handle) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + if (dpriv->devname) { + usbi_dbg("close: fd %d", dpriv->fd); + + close(dpriv->fd); + dpriv->fd = -1; + } +} + +int +obsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf, + int *host_endian) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + + usbi_dbg(""); + + memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH); + + *host_endian = 0; + + return (LIBUSB_SUCCESS); +} + +int +obsd_get_active_config_descriptor(struct libusb_device *dev, + unsigned char *buf, size_t len, int *host_endian) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc; + + len = MIN(len, UGETW(ucd->wTotalLength)); + + usbi_dbg("len %d", len); + + memcpy(buf, dpriv->cdesc, len); + + *host_endian = 0; + + return (len); +} + +int +obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + unsigned char *buf, size_t len, int *host_endian) +{ + struct usb_device_fdesc udf; + int fd, err; + + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + udf.udf_bus = dev->bus_number; + udf.udf_addr = dev->device_address; + udf.udf_config_index = idx; + udf.udf_size = len; + udf.udf_data = buf; + + usbi_dbg("index %d, len %d", udf.udf_config_index, len); + + if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + + *host_endian = 0; + + return (len); +} + +int +obsd_get_configuration(struct libusb_device_handle *handle, int *config) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc; + + *config = ucd->bConfigurationValue; + + usbi_dbg("bConfigurationValue %d", *config); + + return (LIBUSB_SUCCESS); +} + +int +obsd_set_configuration(struct libusb_device_handle *handle, int config) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + + if (dpriv->devname == NULL) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + usbi_dbg("bConfigurationValue %d", config); + + if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) + return _errno_to_libusb(errno); + + return _cache_active_config_descriptor(handle->dev); +} + +int +obsd_claim_interface(struct libusb_device_handle *handle, int iface) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + int i; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + hpriv->endpoints[i] = -1; + + return (LIBUSB_SUCCESS); +} + +int +obsd_release_interface(struct libusb_device_handle *handle, int iface) +{ + struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; + int i; + + for (i = 0; i < USB_MAX_ENDPOINTS; i++) + if (hpriv->endpoints[i] >= 0) + close(hpriv->endpoints[i]); + + return (LIBUSB_SUCCESS); +} + +int +obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, + int altsetting) +{ + struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + struct usb_alt_interface intf; + + if (dpriv->devname == NULL) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + usbi_dbg("iface %d, setting %d", iface, altsetting); + + memset(&intf, 0, sizeof(intf)); + + intf.uai_interface_index = iface; + intf.uai_alt_no = altsetting; + + if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) + return _errno_to_libusb(errno); + + return (LIBUSB_SUCCESS); +} + +int +obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) +{ + struct usb_ctl_request req; + int fd, err; + + if ((fd = _bus_open(handle->dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + usbi_dbg(""); + + req.ucr_addr = handle->dev->device_address; + req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; + req.ucr_request.bRequest = UR_CLEAR_FEATURE; + USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); + USETW(req.ucr_request.wIndex, endpoint); + USETW(req.ucr_request.wLength, 0); + + if (ioctl(fd, USB_REQUEST, &req) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + + return (LIBUSB_SUCCESS); +} + +int +obsd_reset_device(struct libusb_device_handle *handle) +{ + usbi_dbg(""); + + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +void +obsd_destroy_device(struct libusb_device *dev) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + + usbi_dbg(""); + + free(dpriv->cdesc); + free(dpriv->devname); +} + +int +obsd_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct handle_priv *hpriv; + int err = 0; + + usbi_dbg(""); + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + err = _sync_control_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + if (IS_XFEROUT(transfer)) { + /* Isochronous write is not supported */ + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + err = _sync_gen_transfer(itransfer); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + if (err) + return (err); + + usbi_signal_transfer_completion(itransfer); + + return (LIBUSB_SUCCESS); +} + +int +obsd_cancel_transfer(struct usbi_transfer *itransfer) +{ + usbi_dbg(""); + + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +void +obsd_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + usbi_dbg(""); + + /* Nothing to do */ +} + +int +obsd_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +obsd_clock_gettime(int clkid, struct timespec *tp) +{ + usbi_dbg("clock %d", clkid); + + if (clkid == USBI_CLOCK_REALTIME) + return clock_gettime(CLOCK_REALTIME, tp); + + if (clkid == USBI_CLOCK_MONOTONIC) + return clock_gettime(CLOCK_MONOTONIC, tp); + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +int +_errno_to_libusb(int err) +{ + usbi_dbg("error: %s (%d)", strerror(err), err); + + switch (err) { + case EIO: + return (LIBUSB_ERROR_IO); + case EACCES: + return (LIBUSB_ERROR_ACCESS); + case ENOENT: + return (LIBUSB_ERROR_NO_DEVICE); + case ENOMEM: + return (LIBUSB_ERROR_NO_MEM); + case ETIMEDOUT: + return (LIBUSB_ERROR_TIMEOUT); + } + + return (LIBUSB_ERROR_OTHER); +} + +int +_cache_active_config_descriptor(struct libusb_device *dev) +{ + struct device_priv *dpriv = (struct device_priv *)dev->os_priv; + struct usb_device_cdesc udc; + struct usb_device_fdesc udf; + unsigned char* buf; + int fd, len, err; + + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + usbi_dbg("fd %d, addr %d", fd, dev->device_address); + + udc.udc_bus = dev->bus_number; + udc.udc_addr = dev->device_address; + udc.udc_config_index = USB_CURRENT_CONFIG_INDEX; + if (ioctl(fd, USB_DEVICE_GET_CDESC, &udc) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(errno); + } + + usbi_dbg("active bLength %d", udc.udc_desc.bLength); + + len = UGETW(udc.udc_desc.wTotalLength); + buf = malloc(len); + if (buf == NULL) + return (LIBUSB_ERROR_NO_MEM); + + udf.udf_bus = dev->bus_number; + udf.udf_addr = dev->device_address; + udf.udf_config_index = udc.udc_config_index; + udf.udf_size = len; + udf.udf_data = buf; + + usbi_dbg("index %d, len %d", udf.udf_config_index, len); + + if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { + err = errno; + close(fd); + free(buf); + return _errno_to_libusb(err); + } + close(fd); + + if (dpriv->cdesc) + free(dpriv->cdesc); + dpriv->cdesc = buf; + + return (LIBUSB_SUCCESS); +} + +int +_sync_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_control_setup *setup; + struct device_priv *dpriv; + struct usb_ctl_request req; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; + setup = (struct libusb_control_setup *)transfer->buffer; + + usbi_dbg("type %x request %x value %x index %d length %d timeout %d", + setup->bmRequestType, setup->bRequest, + libusb_le16_to_cpu(setup->wValue), + libusb_le16_to_cpu(setup->wIndex), + libusb_le16_to_cpu(setup->wLength), transfer->timeout); + + req.ucr_addr = transfer->dev_handle->dev->device_address; + req.ucr_request.bmRequestType = setup->bmRequestType; + req.ucr_request.bRequest = setup->bRequest; + /* Don't use USETW, libusb already deals with the endianness */ + (*(uint16_t *)req.ucr_request.wValue) = setup->wValue; + (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; + (*(uint16_t *)req.ucr_request.wLength) = setup->wLength; + req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + req.ucr_flags = USBD_SHORT_XFER_OK; + + if (dpriv->devname == NULL) { + /* + * XXX If the device is not attached to ugen(4) it is + * XXX still possible to submit a control transfer but + * XXX with the default timeout only. + */ + int fd, err; + + if ((fd = _bus_open(transfer->dev_handle->dev->bus_number)) < 0) + return _errno_to_libusb(errno); + + if ((ioctl(fd, USB_REQUEST, &req)) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); + } else { + if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) + return _errno_to_libusb(errno); + + if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0) + return _errno_to_libusb(errno); + } + + itransfer->transferred = req.ucr_actlen; + + usbi_dbg("transferred %d", itransfer->transferred); + + return (0); +} + +int +_access_endpoint(struct libusb_transfer *transfer) +{ + struct handle_priv *hpriv; + struct device_priv *dpriv; + char devnode[16]; + int fd, endpt; + mode_t mode; + + hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; + dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; + + endpt = UE_GET_ADDR(transfer->endpoint); + mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; + + usbi_dbg("endpoint %d mode %d", endpt, mode); + + if (hpriv->endpoints[endpt] < 0) { + /* Pick the right endpoint node */ + snprintf(devnode, sizeof(devnode), DEVPATH "%s.%02d", + dpriv->devname, endpt); + + /* We may need to read/write to the same endpoint later. */ + if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) + if ((fd = open(devnode, mode)) < 0) + return (-1); + + hpriv->endpoints[endpt] = fd; + } + + return (hpriv->endpoints[endpt]); +} + +int +_sync_gen_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct device_priv *dpriv; + int fd, nr = 1; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; + + if (dpriv->devname == NULL) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + /* + * Bulk, Interrupt or Isochronous transfer depends on the + * endpoint and thus the node to open. + */ + if ((fd = _access_endpoint(transfer)) < 0) + return _errno_to_libusb(errno); + + if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) + return _errno_to_libusb(errno); + + if (IS_XFERIN(transfer)) { + if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) + if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0) + return _errno_to_libusb(errno); + + nr = read(fd, transfer->buffer, transfer->length); + } else { + nr = write(fd, transfer->buffer, transfer->length); + } + + if (nr < 0) + return _errno_to_libusb(errno); + + itransfer->transferred = nr; + + return (0); +} + +int +_bus_open(int number) +{ + char busnode[16]; + + snprintf(busnode, sizeof(busnode), USBDEV "%d", number); + + return open(busnode, O_RDWR); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.c new file mode 100644 index 0000000..e2f55a5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.c @@ -0,0 +1,53 @@ +/* + * poll_posix: poll compatibility wrapper for POSIX systems + * Copyright © 2013 RealVNC Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include + +#include "libusbi.h" + +int usbi_pipe(int pipefd[2]) +{ + int ret = pipe(pipefd); + if (ret != 0) { + return ret; + } + ret = fcntl(pipefd[1], F_GETFL); + if (ret == -1) { + usbi_dbg("Failed to get pipe fd flags: %d", errno); + goto err_close_pipe; + } + ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK); + if (ret != 0) { + usbi_dbg("Failed to set non-blocking on new pipe: %d", errno); + goto err_close_pipe; + } + + return 0; + +err_close_pipe: + usbi_close(pipefd[0]); + usbi_close(pipefd[1]); + return ret; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.h new file mode 100644 index 0000000..5b4b2c9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.h @@ -0,0 +1,11 @@ +#ifndef LIBUSB_POLL_POSIX_H +#define LIBUSB_POLL_POSIX_H + +#define usbi_write write +#define usbi_read read +#define usbi_close close +#define usbi_poll poll + +int usbi_pipe(int pipefd[2]); + +#endif /* LIBUSB_POLL_POSIX_H */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c new file mode 100644 index 0000000..9825607 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c @@ -0,0 +1,728 @@ +/* + * poll_windows: poll compatibility wrapper for Windows + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * poll() and pipe() Windows compatibility layer for libusb 1.0 + * + * The way this layer works is by using OVERLAPPED with async I/O transfers, as + * OVERLAPPED have an associated event which is flagged for I/O completion. + * + * For USB pollable async I/O, you would typically: + * - obtain a Windows HANDLE to a file or device that has been opened in + * OVERLAPPED mode + * - call usbi_create_fd with this handle to obtain a custom fd. + * Note that if you need simultaneous R/W access, you need to call create_fd + * twice, once in RW_READ and once in RW_WRITE mode to obtain 2 separate + * pollable fds + * - leave the core functions call the poll routine and flag POLLIN/POLLOUT + * + * The pipe pollable synchronous I/O works using the overlapped event associated + * with a fake pipe. The read/write functions are only meant to be used in that + * context. + */ +#include + +#include +#include +#include + +#include "libusbi.h" + +// Uncomment to debug the polling layer +//#define DEBUG_POLL_WINDOWS +#if defined(DEBUG_POLL_WINDOWS) +#define poll_dbg usbi_dbg +#else +// MSVC++ < 2005 cannot use a variadic argument and non MSVC +// compilers produce warnings if parenthesis are omitted. +#if defined(_MSC_VER) && (_MSC_VER < 1400) +#define poll_dbg +#else +#define poll_dbg(...) +#endif +#endif + +#if defined(_PREFAST_) +#pragma warning(disable:28719) +#endif + +#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) + +// public fd data +const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE}; +struct winfd poll_fd[MAX_FDS]; +// internal fd data +struct { + CRITICAL_SECTION mutex; // lock for fds + // Additional variables for XP CancelIoEx partial emulation + HANDLE original_handle; + DWORD thread_id; +} _poll_fd[MAX_FDS]; + +// globals +BOOLEAN is_polling_set = FALSE; +LONG pipe_number = 0; +static volatile LONG compat_spinlock = 0; + +#if !defined(_WIN32_WCE) +// CancelIoEx, available on Vista and later only, provides the ability to cancel +// a single transfer (OVERLAPPED) when used. As it may not be part of any of the +// platform headers, we hook into the Kernel32 system DLL directly to seek it. +static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL; +#define Use_Duplicate_Handles (pCancelIoEx == NULL) + +static inline void setup_cancel_io(void) +{ + HMODULE hKernel32 = GetModuleHandleA("KERNEL32"); + if (hKernel32 != NULL) { + pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) + GetProcAddress(hKernel32, "CancelIoEx"); + } + usbi_dbg("Will use CancelIo%s for I/O cancellation", + Use_Duplicate_Handles?"":"Ex"); +} + +static inline BOOL cancel_io(int _index) +{ + if ((_index < 0) || (_index >= MAX_FDS)) { + return FALSE; + } + + if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) + || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { + return TRUE; + } + if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { + // Cancel outstanding transfer via the specific callback + (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); + return TRUE; + } + if (pCancelIoEx != NULL) { + return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped); + } + if (_poll_fd[_index].thread_id == GetCurrentThreadId()) { + return CancelIo(poll_fd[_index].handle); + } + usbi_warn(NULL, "Unable to cancel I/O that was started from another thread"); + return FALSE; +} +#else +#define Use_Duplicate_Handles FALSE + +static __inline void setup_cancel_io() +{ + // No setup needed on WinCE +} + +static __inline BOOL cancel_io(int _index) +{ + if ((_index < 0) || (_index >= MAX_FDS)) { + return FALSE; + } + if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) + || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { + return TRUE; + } + if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { + // Cancel outstanding transfer via the specific callback + (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); + } + return TRUE; +} +#endif + +// Init +void init_polling(void) +{ + int i; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (!is_polling_set) { + setup_cancel_io(); + for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(overlapped->hEvent == NULL) { + free (overlapped); + return NULL; + } + return overlapped; +} + +static void free_overlapped(OVERLAPPED *overlapped) +{ + if (overlapped == NULL) + return; + + if ( (overlapped->hEvent != 0) + && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { + CloseHandle(overlapped->hEvent); + } + free(overlapped); +} + +void exit_polling(void) +{ + int i; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (is_polling_set) { + is_polling_set = FALSE; + + for (i=0; iInternal = STATUS_PENDING; + overlapped->InternalHigh = 0; + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + + // Use index as the unique fd number + poll_fd[i].fd = i; + // Read end of the "pipe" + filedes[0] = poll_fd[i].fd; + // We can use the same handle for both ends + filedes[1] = filedes[0]; + + poll_fd[i].handle = DUMMY_HANDLE; + poll_fd[i].overlapped = overlapped; + // There's no polling on the write end, so we just use READ for our needs + poll_fd[i].rw = RW_READ; + _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; + LeaveCriticalSection(&_poll_fd[i].mutex); + return 0; + } + } + free_overlapped(overlapped); + return -1; +} + +/* + * Create both an fd and an OVERLAPPED from an open Windows handle, so that + * it can be used with our polling function + * The handle MUST support overlapped transfers (usually requires CreateFile + * with FILE_FLAG_OVERLAPPED) + * Return a pollable file descriptor struct, or INVALID_WINFD on error + * + * Note that the fd returned by this function is a per-transfer fd, rather + * than a per-session fd and cannot be used for anything else but our + * custom functions (the fd itself points to the NUL: device) + * if you plan to do R/W on the same handle, you MUST create 2 fds: one for + * read and one for write. Using a single R/W fd is unsupported and will + * produce unexpected results + */ +struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn) +{ + int i; + struct winfd wfd = INVALID_WINFD; + OVERLAPPED* overlapped = NULL; + + CHECK_INIT_POLLING; + + if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { + return INVALID_WINFD; + } + + wfd.itransfer = itransfer; + wfd.cancel_fn = cancel_fn; + + if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) { + usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported. " + "If you want to poll for R/W simultaneously, create multiple fds from the same handle."); + return INVALID_WINFD; + } + if (access_mode == RW_READ) { + wfd.rw = RW_READ; + } else { + wfd.rw = RW_WRITE; + } + + overlapped = create_overlapped(); + if(overlapped == NULL) { + return INVALID_WINFD; + } + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + // Use index as the unique fd number + wfd.fd = i; + // Attempt to emulate some of the CancelIoEx behaviour on platforms + // that don't have it + if (Use_Duplicate_Handles) { + _poll_fd[i].thread_id = GetCurrentThreadId(); + if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), + &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + usbi_dbg("could not duplicate handle for CancelIo - using original one"); + wfd.handle = handle; + // Make sure we won't close the original handle on fd deletion then + _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; + } else { + _poll_fd[i].original_handle = handle; + } + } else { + wfd.handle = handle; + } + wfd.overlapped = overlapped; + memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); + return wfd; + } + } + free_overlapped(overlapped); + return INVALID_WINFD; +} + +static void _free_index(int _index) +{ + // Cancel any async IO (Don't care about the validity of our handles for this) + cancel_io(_index); + // close the duplicate handle (if we have an actual duplicate) + if (Use_Duplicate_Handles) { + if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) { + CloseHandle(poll_fd[_index].handle); + } + _poll_fd[_index].original_handle = INVALID_HANDLE_VALUE; + _poll_fd[_index].thread_id = 0; + } + free_overlapped(poll_fd[_index].overlapped); + poll_fd[_index] = INVALID_WINFD; +} + +/* + * Release a pollable file descriptor. + * + * Note that the associated Windows handle is not closed by this call + */ +void usbi_free_fd(struct winfd *wfd) +{ + int _index; + + CHECK_INIT_POLLING; + + _index = _fd_to_index_and_lock(wfd->fd); + if (_index < 0) { + return; + } + _free_index(_index); + *wfd = INVALID_WINFD; + LeaveCriticalSection(&_poll_fd[_index].mutex); +} + +/* + * The functions below perform various conversions between fd, handle and OVERLAPPED + */ +struct winfd fd_to_winfd(int fd) +{ + int i; + struct winfd wfd; + + CHECK_INIT_POLLING; + + if (fd < 0) + return INVALID_WINFD; + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + usbi_warn(NULL, "invalid fd"); + triggered = -1; + goto poll_exit; + } + + // IN or OUT must match our fd direction + if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) { + fds[i].revents |= POLLNVAL | POLLERR; + errno = EBADF; + usbi_warn(NULL, "attempted POLLIN on fd without READ access"); + LeaveCriticalSection(&_poll_fd[_index].mutex); + triggered = -1; + goto poll_exit; + } + + if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) { + fds[i].revents |= POLLNVAL | POLLERR; + errno = EBADF; + usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access"); + LeaveCriticalSection(&_poll_fd[_index].mutex); + triggered = -1; + goto poll_exit; + } + + // The following macro only works if overlapped I/O was reported pending + if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped)) + || (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) { + poll_dbg(" completed"); + // checks above should ensure this works: + fds[i].revents = fds[i].events; + triggered++; + } else { + handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent; + handle_to_index[nb_handles_to_wait_on] = i; + nb_handles_to_wait_on++; + } + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + + // If nothing was triggered, wait on all fds that require it + if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) { + if (timeout < 0) { + poll_dbg("starting infinite wait for %u handles...", (unsigned int)nb_handles_to_wait_on); + } else { + poll_dbg("starting %d ms wait for %u handles...", timeout, (unsigned int)nb_handles_to_wait_on); + } + ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on, + FALSE, (timeout<0)?INFINITE:(DWORD)timeout); + object_index = ret-WAIT_OBJECT_0; + if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { + poll_dbg(" completed after wait"); + i = handle_to_index[object_index]; + _index = _fd_to_index_and_lock(fds[i].fd); + fds[i].revents = fds[i].events; + triggered++; + if (_index >= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + } else if (ret == WAIT_TIMEOUT) { + poll_dbg(" timed out"); + triggered = 0; // 0 = timeout + } else { + errno = EIO; + triggered = -1; // error + } + } + +poll_exit: + if (handles_to_wait_on != NULL) { + free(handles_to_wait_on); + } + if (handle_to_index != NULL) { + free(handle_to_index); + } + return triggered; +} + +/* + * close a fake pipe fd + */ +int usbi_close(int fd) +{ + int _index; + int r = -1; + + CHECK_INIT_POLLING; + + _index = _fd_to_index_and_lock(fd); + + if (_index < 0) { + errno = EBADF; + } else { + free_overlapped(poll_fd[_index].overlapped); + poll_fd[_index] = INVALID_WINFD; + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + return r; +} + +/* + * synchronous write for fake "pipe" signaling + */ +ssize_t usbi_write(int fd, const void *buf, size_t count) +{ + int _index; + UNUSED(buf); + + CHECK_INIT_POLLING; + + if (count != sizeof(unsigned char)) { + usbi_err(NULL, "this function should only used for signaling"); + return -1; + } + + _index = _fd_to_index_and_lock(fd); + + if ( (_index < 0) || (poll_fd[_index].overlapped == NULL) ) { + errno = EBADF; + if (_index >= 0) { + LeaveCriticalSection(&_poll_fd[_index].mutex); + } + return -1; + } + + poll_dbg("set pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId()); + SetEvent(poll_fd[_index].overlapped->hEvent); + poll_fd[_index].overlapped->Internal = STATUS_WAIT_0; + // If two threads write on the pipe at the same time, we need to + // process two separate reads => use the overlapped as a counter + poll_fd[_index].overlapped->InternalHigh++; + + LeaveCriticalSection(&_poll_fd[_index].mutex); + return sizeof(unsigned char); +} + +/* + * synchronous read for fake "pipe" signaling + */ +ssize_t usbi_read(int fd, void *buf, size_t count) +{ + int _index; + ssize_t r = -1; + UNUSED(buf); + + CHECK_INIT_POLLING; + + if (count != sizeof(unsigned char)) { + usbi_err(NULL, "this function should only used for signaling"); + return -1; + } + + _index = _fd_to_index_and_lock(fd); + + if (_index < 0) { + errno = EBADF; + return -1; + } + + if (WaitForSingleObject(poll_fd[_index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) { + usbi_warn(NULL, "waiting for event failed: %u", (unsigned int)GetLastError()); + errno = EIO; + goto out; + } + + poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId()); + poll_fd[_index].overlapped->InternalHigh--; + // Don't reset unless we don't have any more events to process + if (poll_fd[_index].overlapped->InternalHigh <= 0) { + ResetEvent(poll_fd[_index].overlapped->hEvent); + poll_fd[_index].overlapped->Internal = STATUS_PENDING; + } + + r = sizeof(unsigned char); + +out: + LeaveCriticalSection(&_poll_fd[_index].mutex); + return r; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.h new file mode 100644 index 0000000..aa4c985 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.h @@ -0,0 +1,131 @@ +/* + * Windows compat: POSIX compatibility wrapper + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#endif + +// Handle synchronous completion through the overlapped structure +#if !defined(STATUS_REPARSE) // reuse the REPARSE status code +#define STATUS_REPARSE ((LONG)0x00000104L) +#endif +#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE +#if defined(_WIN32_WCE) +// WinCE doesn't have a HasOverlappedIoCompleted() macro, so attempt to emulate it +#define HasOverlappedIoCompleted(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) != STATUS_PENDING) +#endif +#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) + +#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2) + +/* Windows versions */ +enum windows_version { + WINDOWS_CE = -2, + WINDOWS_UNDEFINED = -1, + WINDOWS_UNSUPPORTED = 0, + WINDOWS_XP = 0x51, + WINDOWS_2003 = 0x52, // Also XP x64 + WINDOWS_VISTA = 0x60, + WINDOWS_7 = 0x61, + WINDOWS_8 = 0x62, + WINDOWS_8_1_OR_LATER = 0x63, + WINDOWS_MAX +}; +extern int windows_version; + +#define MAX_FDS 256 + +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +// access modes +enum rw_type { + RW_NONE, + RW_READ, + RW_WRITE, +}; + +// fd struct that can be used for polling on Windows +typedef int cancel_transfer(struct usbi_transfer *itransfer); + +struct winfd { + int fd; // what's exposed to libusb core + HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it + OVERLAPPED* overlapped; // what will report our I/O status + struct usbi_transfer *itransfer; // Associated transfer, or NULL if completed + cancel_transfer *cancel_fn; // Function pointer to cancel transfer API + enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) +}; +extern const struct winfd INVALID_WINFD; + +int usbi_pipe(int pipefd[2]); +int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); +ssize_t usbi_write(int fd, const void *buf, size_t count); +ssize_t usbi_read(int fd, void *buf, size_t count); +int usbi_close(int fd); + +void init_polling(void); +void exit_polling(void); +struct winfd usbi_create_fd(HANDLE handle, int access_mode, + struct usbi_transfer *transfer, cancel_transfer *cancel_fn); +void usbi_free_fd(struct winfd* winfd); +struct winfd fd_to_winfd(int fd); +struct winfd handle_to_winfd(HANDLE handle); +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); + +/* + * Timeval operations + */ +#if defined(DDKBUILD) +#include // defines timeval functions on DDK +#endif + +#if !defined(TIMESPEC_TO_TIMEVAL) +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (long)(ts)->tv_sec; \ + (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ +} +#endif +#if !defined(timersub) +#define timersub(a, b, result) \ +do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ +} while (0) +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.c new file mode 100644 index 0000000..cb60897 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.c @@ -0,0 +1,1292 @@ +/* + * + * Copyright (c) 2016, Oracle and/or its affiliates. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "sunos_usb.h" + +/* + * Backend functions + */ +static int sunos_init(struct libusb_context *); +static void sunos_exit(void); +static int sunos_get_device_list(struct libusb_context *, + struct discovered_devs **); +static int sunos_open(struct libusb_device_handle *); +static void sunos_close(struct libusb_device_handle *); +static int sunos_get_device_descriptor(struct libusb_device *, + uint8_t*, int *); +static int sunos_get_active_config_descriptor(struct libusb_device *, + uint8_t*, size_t, int *); +static int sunos_get_config_descriptor(struct libusb_device *, uint8_t, + uint8_t*, size_t, int *); +static int sunos_get_configuration(struct libusb_device_handle *, int *); +static int sunos_set_configuration(struct libusb_device_handle *, int); +static int sunos_claim_interface(struct libusb_device_handle *, int); +static int sunos_release_interface(struct libusb_device_handle *, int); +static int sunos_set_interface_altsetting(struct libusb_device_handle *, + int, int); +static int sunos_clear_halt(struct libusb_device_handle *, uint8_t); +static int sunos_reset_device(struct libusb_device_handle *); +static void sunos_destroy_device(struct libusb_device *); +static int sunos_submit_transfer(struct usbi_transfer *); +static int sunos_cancel_transfer(struct usbi_transfer *); +static void sunos_clear_transfer_priv(struct usbi_transfer *); +static int sunos_handle_transfer_completion(struct usbi_transfer *); +static int sunos_clock_gettime(int, struct timespec *); + +/* + * Private functions + */ +static int _errno_to_libusb(int); +static int sunos_usb_get_status(int fd); + +static int sunos_init(struct libusb_context *ctx) +{ + return (LIBUSB_SUCCESS); +} + +static void sunos_exit(void) +{ + usbi_dbg(""); +} + +static int +sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev) +{ + int proplen; + int n, *addr, *port_prop; + char *phypath; + uint8_t *rdata; + struct libusb_device_descriptor *descr; + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; + + /* Device descriptors */ + proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "usb-dev-descriptor", &rdata); + if (proplen <= 0) { + + return (LIBUSB_ERROR_IO); + } + + descr = (struct libusb_device_descriptor *)rdata; + bcopy(descr, &dpriv->dev_descr, LIBUSB_DT_DEVICE_SIZE); + dpriv->dev_descr.bcdUSB = libusb_cpu_to_le16(descr->bcdUSB); + dpriv->dev_descr.idVendor = libusb_cpu_to_le16(descr->idVendor); + dpriv->dev_descr.idProduct = libusb_cpu_to_le16(descr->idProduct); + dpriv->dev_descr.bcdDevice = libusb_cpu_to_le16(descr->bcdDevice); + + /* Raw configuration descriptors */ + proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "usb-raw-cfg-descriptors", &rdata); + if (proplen <= 0) { + usbi_dbg("can't find raw config descriptors"); + + return (LIBUSB_ERROR_IO); + } + dpriv->raw_cfgdescr = calloc(1, proplen); + if (dpriv->raw_cfgdescr == NULL) { + return (LIBUSB_ERROR_NO_MEM); + } else { + bcopy(rdata, dpriv->raw_cfgdescr, proplen); + dpriv->cfgvalue = ((struct libusb_config_descriptor *) + rdata)->bConfigurationValue; + } + + n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &port_prop); + + if ((n != 1) || (*port_prop <= 0)) { + return (LIBUSB_ERROR_IO); + } + dev->port_number = *port_prop; + + /* device physical path */ + phypath = di_devfs_path(node); + if (phypath) { + dpriv->phypath = strdup(phypath); + di_devfs_path_free(phypath); + } else { + free(dpriv->raw_cfgdescr); + + return (LIBUSB_ERROR_IO); + } + + /* address */ + n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "assigned-address", &addr); + if (n != 1 || *addr == 0) { + usbi_dbg("can't get address"); + } else { + dev->device_address = *addr; + } + + /* speed */ + if (di_prop_exists(DDI_DEV_T_ANY, node, "low-speed") == 1) { + dev->speed = LIBUSB_SPEED_LOW; + } else if (di_prop_exists(DDI_DEV_T_ANY, node, "high-speed") == 1) { + dev->speed = LIBUSB_SPEED_HIGH; + } else if (di_prop_exists(DDI_DEV_T_ANY, node, "full-speed") == 1) { + dev->speed = LIBUSB_SPEED_FULL; + } else if (di_prop_exists(DDI_DEV_T_ANY, node, "super-speed") == 1) { + dev->speed = LIBUSB_SPEED_SUPER; + } + + usbi_dbg("vid=%x pid=%x, path=%s, bus_nmber=0x%x, port_number=%d, " + "speed=%d", dpriv->dev_descr.idVendor, dpriv->dev_descr.idProduct, + dpriv->phypath, dev->bus_number, dev->port_number, dev->speed); + + return (LIBUSB_SUCCESS); +} + + +static int +sunos_add_devices(di_devlink_t link, void *arg) +{ + struct devlink_cbarg *largs = (struct devlink_cbarg *)arg; + struct node_args *nargs; + di_node_t myself, pnode; + uint64_t session_id = 0; + uint16_t bdf = 0; + struct libusb_device *dev; + sunos_dev_priv_t *devpriv; + const char *path, *newpath; + int n, i; + int *addr_prop; + uint8_t bus_number = 0; + + nargs = (struct node_args *)largs->nargs; + myself = largs->myself; + if (nargs->last_ugenpath) { + /* the same node's links */ + return (DI_WALK_CONTINUE); + } + + /* + * Construct session ID. + * session ID = ...parent hub addr|hub addr|dev addr. + */ + pnode = myself; + i = 0; + while (pnode != DI_NODE_NIL) { + if (di_prop_exists(DDI_DEV_T_ANY, pnode, "root-hub") == 1) { + /* walk to root */ + uint32_t *regbuf = NULL; + uint32_t reg; + + n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode, "reg", + (int **)®buf); + reg = regbuf[0]; + bdf = (PCI_REG_BUS_G(reg) << 8) | + (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); + session_id |= (bdf << i * 8); + + /* same as 'unit-address' property */ + bus_number = + (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); + + usbi_dbg("device bus address=%s:%x", + di_bus_addr(pnode), bus_number); + + break; + } + + /* usb_addr */ + n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode, + "assigned-address", &addr_prop); + if ((n != 1) || (addr_prop[0] == 0)) { + usbi_dbg("cannot get valid usb_addr"); + + return (DI_WALK_CONTINUE); + } + + session_id |= ((addr_prop[0] & 0xff) << i * 8); + if (++i > 7) + break; + + pnode = di_parent_node(pnode); + } + + path = di_devlink_path(link); + dev = usbi_get_device_by_session_id(nargs->ctx, session_id); + if (dev == NULL) { + dev = usbi_alloc_device(nargs->ctx, session_id); + if (dev == NULL) { + usbi_dbg("can't alloc device"); + + return (DI_WALK_TERMINATE); + } + devpriv = (sunos_dev_priv_t *)dev->os_priv; + if ((newpath = strrchr(path, '/')) == NULL) { + libusb_unref_device(dev); + + return (DI_WALK_TERMINATE); + } + devpriv->ugenpath = strndup(path, strlen(path) - + strlen(newpath)); + dev->bus_number = bus_number; + + if (sunos_fill_in_dev_info(myself, dev) != LIBUSB_SUCCESS) { + libusb_unref_device(dev); + + return (DI_WALK_TERMINATE); + } + if (usbi_sanitize_device(dev) < 0) { + libusb_unref_device(dev); + usbi_dbg("sanatize failed: "); + return (DI_WALK_TERMINATE); + } + } else { + usbi_dbg("Dev %s exists", path); + } + + devpriv = (sunos_dev_priv_t *)dev->os_priv; + if (nargs->last_ugenpath == NULL) { + /* first device */ + nargs->last_ugenpath = devpriv->ugenpath; + + if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) { + usbi_dbg("cannot append device"); + } + + /* + * we alloc and hence ref this dev. We don't need to ref it + * hereafter. Front end or app should take care of their ref. + */ + libusb_unref_device(dev); + } + + usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x", + devpriv->ugenpath, path, (uint64_t)session_id, + (*nargs->discdevs)->len, bdf); + + return (DI_WALK_CONTINUE); +} + +static int +sunos_walk_minor_node_link(di_node_t node, void *args) +{ + di_minor_t minor = DI_MINOR_NIL; + char *minor_path; + struct devlink_cbarg arg; + struct node_args *nargs = (struct node_args *)args; + di_devlink_handle_t devlink_hdl = nargs->dlink_hdl; + + /* walk each minor to find ugen devices */ + while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { + minor_path = di_devfs_minor_path(minor); + arg.nargs = args; + arg.myself = node; + arg.minor = minor; + (void) di_devlink_walk(devlink_hdl, + "^usb/[0-9a-f]+[.][0-9a-f]+", minor_path, + DI_PRIMARY_LINK, (void *)&arg, sunos_add_devices); + di_devfs_path_free(minor_path); + } + + /* switch to a different node */ + nargs->last_ugenpath = NULL; + + return (DI_WALK_CONTINUE); +} + +int +sunos_get_device_list(struct libusb_context * ctx, + struct discovered_devs **discdevs) +{ + di_node_t root_node; + struct node_args args; + di_devlink_handle_t devlink_hdl; + + args.ctx = ctx; + args.discdevs = discdevs; + args.last_ugenpath = NULL; + if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { + usbi_dbg("di_int() failed: %s", strerror(errno)); + return (LIBUSB_ERROR_IO); + } + + if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { + di_fini(root_node); + usbi_dbg("di_devlink_init() failed: %s", strerror(errno)); + + return (LIBUSB_ERROR_IO); + } + args.dlink_hdl = devlink_hdl; + + /* walk each node to find USB devices */ + if (di_walk_node(root_node, DI_WALK_SIBFIRST, &args, + sunos_walk_minor_node_link) == -1) { + usbi_dbg("di_walk_node() failed: %s", strerror(errno)); + di_fini(root_node); + + return (LIBUSB_ERROR_IO); + } + + di_fini(root_node); + di_devlink_fini(&devlink_hdl); + + usbi_dbg("%d devices", (*discdevs)->len); + + return ((*discdevs)->len); +} + +static int +sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv) +{ + char filename[PATH_MAX + 1]; + + if (hpriv->eps[0].datafd > 0) { + + return (LIBUSB_SUCCESS); + } + snprintf(filename, PATH_MAX, "%s/cntrl0", dpriv->ugenpath); + + usbi_dbg("opening %s", filename); + hpriv->eps[0].datafd = open(filename, O_RDWR); + if (hpriv->eps[0].datafd < 0) { + return(_errno_to_libusb(errno)); + } + + snprintf(filename, PATH_MAX, "%s/cntrl0stat", dpriv->ugenpath); + hpriv->eps[0].statfd = open(filename, O_RDONLY); + if (hpriv->eps[0].statfd < 0) { + close(hpriv->eps[0].datafd); + hpriv->eps[0].datafd = -1; + + return(_errno_to_libusb(errno)); + } + + return (LIBUSB_SUCCESS); +} + +static void +sunos_usb_close_all_eps(sunos_dev_handle_priv_t *hdev) +{ + int i; + + /* not close ep0 */ + for (i = 1; i < USB_MAXENDPOINTS; i++) { + if (hdev->eps[i].datafd != -1) { + (void) close(hdev->eps[i].datafd); + hdev->eps[i].datafd = -1; + } + if (hdev->eps[i].statfd != -1) { + (void) close(hdev->eps[i].statfd); + hdev->eps[i].statfd = -1; + } + } +} + +static void +sunos_usb_close_ep0(sunos_dev_handle_priv_t *hdev, sunos_dev_priv_t *dpriv) +{ + if (hdev->eps[0].datafd >= 0) { + close(hdev->eps[0].datafd); + close(hdev->eps[0].statfd); + hdev->eps[0].datafd = -1; + hdev->eps[0].statfd = -1; + } +} + +static uchar_t +sunos_usb_ep_index(uint8_t ep_addr) +{ + return ((ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK) + + ((ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? 16 : 0)); +} + +static int +sunos_find_interface(struct libusb_device_handle *hdev, + uint8_t endpoint, uint8_t *interface) +{ + struct libusb_config_descriptor *config; + int r; + int iface_idx; + + r = libusb_get_active_config_descriptor(hdev->dev, &config); + if (r < 0) { + return (LIBUSB_ERROR_INVALID_PARAM); + } + + for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { + const struct libusb_interface *iface = + &config->interface[iface_idx]; + int altsetting_idx; + + for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; + altsetting_idx++) { + const struct libusb_interface_descriptor *altsetting = + &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; + ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) { + *interface = iface_idx; + libusb_free_config_descriptor(config); + + return (LIBUSB_SUCCESS); + } + } + } + } + libusb_free_config_descriptor(config); + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +static int +sunos_check_device_and_status_open(struct libusb_device_handle *hdl, + uint8_t ep_addr, int ep_type) +{ + char filename[PATH_MAX + 1], statfilename[PATH_MAX + 1]; + char cfg_num[16], alt_num[16]; + int fd, fdstat, mode; + uint8_t ifc = 0; + uint8_t ep_index; + sunos_dev_handle_priv_t *hpriv; + + usbi_dbg("open ep 0x%02x", ep_addr); + hpriv = (sunos_dev_handle_priv_t *)hdl->os_priv; + ep_index = sunos_usb_ep_index(ep_addr); + /* ep already opened */ + if ((hpriv->eps[ep_index].datafd > 0) && + (hpriv->eps[ep_index].statfd > 0)) { + usbi_dbg("ep 0x%02x already opened, return success", + ep_addr); + + return (0); + } + + if (sunos_find_interface(hdl, ep_addr, &ifc) < 0) { + usbi_dbg("can't find interface for endpoint 0x%02x", + ep_addr); + + return (LIBUSB_ERROR_ACCESS); + } + + /* create filename */ + if (hpriv->config_index > 0) { + (void) snprintf(cfg_num, sizeof (cfg_num), "cfg%d", + hpriv->config_index + 1); + } else { + bzero(cfg_num, sizeof (cfg_num)); + } + + if (hpriv->altsetting[ifc] > 0) { + (void) snprintf(alt_num, sizeof (alt_num), ".%d", + hpriv->altsetting[ifc]); + } else { + bzero(alt_num, sizeof (alt_num)); + } + + (void) snprintf(filename, PATH_MAX, "%s/%sif%d%s%s%d", + hpriv->dpriv->ugenpath, cfg_num, ifc, alt_num, + (ep_addr & LIBUSB_ENDPOINT_DIR_MASK) ? "in" : + "out", (ep_addr & LIBUSB_ENDPOINT_ADDRESS_MASK)); + (void) snprintf(statfilename, PATH_MAX, "%sstat", filename); + + /* + * for interrupt IN endpoints, we need to enable one xfer + * mode before opening the endpoint + */ + if ((ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) && + (ep_addr & LIBUSB_ENDPOINT_IN)) { + char control = USB_EP_INTR_ONE_XFER; + int count; + + /* open the status device node for the ep first RDWR */ + if ((fdstat = open(statfilename, O_RDWR)) == -1) { + usbi_dbg("can't open %s RDWR: %d", + statfilename, errno); + } else { + count = write(fdstat, &control, sizeof (control)); + if (count != 1) { + /* this should have worked */ + usbi_dbg("can't write to %s: %d", + statfilename, errno); + (void) close(fdstat); + + return (errno); + } + /* close status node and open xfer node first */ + close (fdstat); + } + } + + /* open the xfer node first in case alt needs to be changed */ + if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + mode = O_RDWR; + } else if (ep_addr & LIBUSB_ENDPOINT_IN) { + mode = O_RDONLY; + } else { + mode = O_WRONLY; + } + + /* + * IMPORTANT: must open data xfer node first and then open stat node + * Otherwise, it will fail on multi-config or multi-altsetting devices + * with "Device Busy" error. See ugen_epxs_switch_cfg_alt() and + * ugen_epxs_check_alt_switch() in ugen driver source code. + */ + if ((fd = open(filename, mode)) == -1) { + usbi_dbg("can't open %s: %d(%s)", filename, errno, + strerror(errno)); + + return (errno); + } + /* open the status node */ + if ((fdstat = open(statfilename, O_RDONLY)) == -1) { + usbi_dbg("can't open %s: %d", statfilename, errno); + + (void) close(fd); + + return (errno); + } + + hpriv->eps[ep_index].datafd = fd; + hpriv->eps[ep_index].statfd = fdstat; + usbi_dbg("ep=0x%02x datafd=%d, statfd=%d", ep_addr, fd, fdstat); + + return (0); +} + +int +sunos_open(struct libusb_device_handle *handle) +{ + sunos_dev_handle_priv_t *hpriv; + sunos_dev_priv_t *dpriv; + int i; + int ret; + + hpriv = (sunos_dev_handle_priv_t *)handle->os_priv; + dpriv = (sunos_dev_priv_t *)handle->dev->os_priv; + hpriv->dpriv = dpriv; + + /* set all file descriptors to "closed" */ + for (i = 0; i < USB_MAXENDPOINTS; i++) { + hpriv->eps[i].datafd = -1; + hpriv->eps[i].statfd = -1; + } + + if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) { + usbi_dbg("fail: %d", ret); + return (ret); + } + + return (LIBUSB_SUCCESS); +} + +void +sunos_close(struct libusb_device_handle *handle) +{ + sunos_dev_handle_priv_t *hpriv; + sunos_dev_priv_t *dpriv; + + usbi_dbg(""); + if (!handle) { + return; + } + + hpriv = (sunos_dev_handle_priv_t *)handle->os_priv; + if (!hpriv) { + return; + } + dpriv = (sunos_dev_priv_t *)handle->dev->os_priv; + if (!dpriv) { + return; + } + + sunos_usb_close_all_eps(hpriv); + sunos_usb_close_ep0(hpriv, dpriv); +} + +int +sunos_get_device_descriptor(struct libusb_device *dev, uint8_t *buf, + int *host_endian) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; + + memcpy(buf, &dpriv->dev_descr, LIBUSB_DT_DEVICE_SIZE); + *host_endian = 0; + + return (LIBUSB_SUCCESS); +} + +int +sunos_get_active_config_descriptor(struct libusb_device *dev, + uint8_t *buf, size_t len, int *host_endian) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; + struct libusb_config_descriptor *cfg; + int proplen; + di_node_t node; + uint8_t *rdata; + + /* + * Keep raw configuration descriptors updated, in case config + * has ever been changed through setCfg. + */ + if ((node = di_init(dpriv->phypath, DINFOCPYALL)) == DI_NODE_NIL) { + usbi_dbg("di_int() failed: %s", strerror(errno)); + return (LIBUSB_ERROR_IO); + } + proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, + "usb-raw-cfg-descriptors", &rdata); + if (proplen <= 0) { + usbi_dbg("can't find raw config descriptors"); + + return (LIBUSB_ERROR_IO); + } + dpriv->raw_cfgdescr = realloc(dpriv->raw_cfgdescr, proplen); + if (dpriv->raw_cfgdescr == NULL) { + return (LIBUSB_ERROR_NO_MEM); + } else { + bcopy(rdata, dpriv->raw_cfgdescr, proplen); + dpriv->cfgvalue = ((struct libusb_config_descriptor *) + rdata)->bConfigurationValue; + } + di_fini(node); + + cfg = (struct libusb_config_descriptor *)dpriv->raw_cfgdescr; + len = MIN(len, libusb_le16_to_cpu(cfg->wTotalLength)); + memcpy(buf, dpriv->raw_cfgdescr, len); + *host_endian = 0; + usbi_dbg("path:%s len %d", dpriv->phypath, len); + + return (len); +} + +int +sunos_get_config_descriptor(struct libusb_device *dev, uint8_t idx, + uint8_t *buf, size_t len, int *host_endian) +{ + /* XXX */ + return(sunos_get_active_config_descriptor(dev, buf, len, host_endian)); +} + +int +sunos_get_configuration(struct libusb_device_handle *handle, int *config) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv; + + *config = dpriv->cfgvalue; + + usbi_dbg("bConfigurationValue %d", *config); + + return (LIBUSB_SUCCESS); +} + +int +sunos_set_configuration(struct libusb_device_handle *handle, int config) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv; + sunos_dev_handle_priv_t *hpriv; + + usbi_dbg("bConfigurationValue %d", config); + hpriv = (sunos_dev_handle_priv_t *)handle->os_priv; + + if (dpriv->ugenpath == NULL) + return (LIBUSB_ERROR_NOT_SUPPORTED); + + if (config < 1 || config > dpriv->dev_descr.bNumConfigurations) + return (LIBUSB_ERROR_INVALID_PARAM); + + dpriv->cfgvalue = config; + hpriv->config_index = config - 1; + + return (LIBUSB_SUCCESS); +} + +int +sunos_claim_interface(struct libusb_device_handle *handle, int iface) +{ + usbi_dbg("iface %d", iface); + if (iface < 0) { + return (LIBUSB_ERROR_INVALID_PARAM); + } + + return (LIBUSB_SUCCESS); +} + +int +sunos_release_interface(struct libusb_device_handle *handle, int iface) +{ + sunos_dev_handle_priv_t *hpriv = + (sunos_dev_handle_priv_t *)handle->os_priv; + + usbi_dbg("iface %d", iface); + if (iface < 0) { + return (LIBUSB_ERROR_INVALID_PARAM); + } + + /* XXX: can we release it? */ + hpriv->altsetting[iface] = 0; + + return (LIBUSB_SUCCESS); +} + +int +sunos_set_interface_altsetting(struct libusb_device_handle *handle, int iface, + int altsetting) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)handle->dev->os_priv; + sunos_dev_handle_priv_t *hpriv = + (sunos_dev_handle_priv_t *)handle->os_priv; + + usbi_dbg("iface %d, setting %d", iface, altsetting); + + if (iface < 0 || altsetting < 0) { + return (LIBUSB_ERROR_INVALID_PARAM); + } + if (dpriv->ugenpath == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + /* XXX: can we switch altsetting? */ + hpriv->altsetting[iface] = altsetting; + + return (LIBUSB_SUCCESS); +} + +static void +usb_dump_data(unsigned char *data, size_t size) +{ + int i; + + if (getenv("LIBUSB_DEBUG") == NULL) { + return; + } + + (void) fprintf(stderr, "data dump:"); + for (i = 0; i < size; i++) { + if (i % 16 == 0) { + (void) fprintf(stderr, "\n%08x ", i); + } + (void) fprintf(stderr, "%02x ", (uchar_t)data[i]); + } + (void) fprintf(stderr, "\n"); +} + +static void +sunos_async_callback(union sigval arg) +{ + struct sunos_transfer_priv *tpriv = + (struct sunos_transfer_priv *)arg.sival_ptr; + struct libusb_transfer *xfer = tpriv->transfer; + struct aiocb *aiocb = &tpriv->aiocb; + int ret; + sunos_dev_handle_priv_t *hpriv; + uint8_t ep; + + hpriv = (sunos_dev_handle_priv_t *)xfer->dev_handle->os_priv; + ep = sunos_usb_ep_index(xfer->endpoint); + + ret = aio_error(aiocb); + if (ret != 0) { + xfer->status = sunos_usb_get_status(hpriv->eps[ep].statfd); + } else { + xfer->actual_length = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)->transferred = + aio_return(aiocb); + } + + usb_dump_data(xfer->buffer, xfer->actual_length); + + usbi_dbg("ret=%d, len=%d, actual_len=%d", ret, xfer->length, + xfer->actual_length); + + /* async notification */ + usbi_signal_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)); +} + +static int +sunos_do_async_io(struct libusb_transfer *transfer) +{ + int ret = -1; + struct aiocb *aiocb; + sunos_dev_handle_priv_t *hpriv; + uint8_t ep; + struct sunos_transfer_priv *tpriv; + + usbi_dbg(""); + + tpriv = usbi_transfer_get_os_priv(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)); + hpriv = (sunos_dev_handle_priv_t *)transfer->dev_handle->os_priv; + ep = sunos_usb_ep_index(transfer->endpoint); + + tpriv->transfer = transfer; + aiocb = &tpriv->aiocb; + bzero(aiocb, sizeof (*aiocb)); + aiocb->aio_fildes = hpriv->eps[ep].datafd; + aiocb->aio_buf = transfer->buffer; + aiocb->aio_nbytes = transfer->length; + aiocb->aio_lio_opcode = + ((transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) == + LIBUSB_ENDPOINT_IN) ? LIO_READ:LIO_WRITE; + aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD; + aiocb->aio_sigevent.sigev_value.sival_ptr = tpriv; + aiocb->aio_sigevent.sigev_notify_function = sunos_async_callback; + + if (aiocb->aio_lio_opcode == LIO_READ) { + ret = aio_read(aiocb); + } else { + ret = aio_write(aiocb); + } + + return (ret); +} + +/* return the number of bytes read/written */ +static int +usb_do_io(int fd, int stat_fd, char *data, size_t size, int flag, int *status) +{ + int error; + int ret = -1; + + usbi_dbg("usb_do_io(): datafd=%d statfd=%d size=0x%x flag=%s", + fd, stat_fd, size, flag? "WRITE":"READ"); + + switch (flag) { + case READ: + errno = 0; + ret = read(fd, data, size); + usb_dump_data(data, size); + break; + case WRITE: + usb_dump_data(data, size); + errno = 0; + ret = write(fd, data, size); + break; + } + + usbi_dbg("usb_do_io(): amount=%d", ret); + + if (ret < 0) { + int save_errno = errno; + + usbi_dbg("TID=%x io %s errno=%d(%s) ret=%d", pthread_self(), + flag?"WRITE":"READ", errno, strerror(errno), ret); + + /* sunos_usb_get_status will do a read and overwrite errno */ + error = sunos_usb_get_status(stat_fd); + usbi_dbg("io status=%d errno=%d(%s)", error, + save_errno, strerror(save_errno)); + + if (status) { + *status = save_errno; + } + + return (save_errno); + + } else if (status) { + *status = 0; + } + + return (ret); +} + +static int +solaris_submit_ctrl_on_default(struct libusb_transfer *transfer) +{ + int ret = -1, setup_ret; + int status; + sunos_dev_handle_priv_t *hpriv; + struct libusb_device_handle *hdl = transfer->dev_handle; + uint16_t wLength; + uint8_t *data = transfer->buffer; + + hpriv = (sunos_dev_handle_priv_t *)hdl->os_priv; + wLength = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (hpriv->eps[0].datafd == -1) { + usbi_dbg("ep0 not opened"); + + return (LIBUSB_ERROR_NOT_FOUND); + } + + if ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) { + usbi_dbg("IN request"); + ret = usb_do_io(hpriv->eps[0].datafd, + hpriv->eps[0].statfd, (char *)data, LIBUSB_CONTROL_SETUP_SIZE, + WRITE, (int *)&status); + } else { + usbi_dbg("OUT request"); + ret = usb_do_io(hpriv->eps[0].datafd, hpriv->eps[0].statfd, + transfer->buffer, transfer->length, WRITE, + (int *)&transfer->status); + } + + setup_ret = ret; + if (ret < LIBUSB_CONTROL_SETUP_SIZE) { + usbi_dbg("error sending control msg: %d", ret); + + return (LIBUSB_ERROR_IO); + } + + ret = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + /* Read the remaining bytes for IN request */ + if ((wLength) && ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) == + LIBUSB_ENDPOINT_IN)) { + usbi_dbg("DATA: %d", transfer->length - setup_ret); + ret = usb_do_io(hpriv->eps[0].datafd, + hpriv->eps[0].statfd, + (char *)transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, + wLength, READ, (int *)&transfer->status); + } + + if (ret >= 0) { + transfer->actual_length = ret; + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->transferred = ret; + } + usbi_dbg("Done: ctrl data bytes %d", ret); + + /* sync transfer handling */ + ret = usbi_handle_transfer_completion(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer), + transfer->status); + + return (ret); +} + +int +sunos_clear_halt(struct libusb_device_handle *handle, uint8_t endpoint) +{ + int ret; + + usbi_dbg("endpoint=0x%02x", endpoint); + + ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_RECIPIENT_ENDPOINT | LIBUSB_REQUEST_TYPE_STANDARD, + LIBUSB_REQUEST_CLEAR_FEATURE, 0, endpoint, NULL, 0, 1000); + + usbi_dbg("ret=%d", ret); + + return (ret); +} + +int +sunos_reset_device(struct libusb_device_handle *handle) +{ + usbi_dbg(""); + + return (LIBUSB_ERROR_NOT_SUPPORTED); +} + +void +sunos_destroy_device(struct libusb_device *dev) +{ + sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; + + usbi_dbg(""); + + free(dpriv->raw_cfgdescr); + free(dpriv->ugenpath); + free(dpriv->phypath); +} + +int +sunos_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer; + struct libusb_device_handle *hdl; + int err = 0; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hdl = transfer->dev_handle; + + err = sunos_check_device_and_status_open(hdl, + transfer->endpoint, transfer->type); + if (err < 0) { + + return (_errno_to_libusb(err)); + } + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + /* sync transfer */ + usbi_dbg("CTRL transfer: %d", transfer->length); + err = solaris_submit_ctrl_on_default(transfer); + break; + + case LIBUSB_TRANSFER_TYPE_BULK: + /* fallthru */ + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (transfer->type == LIBUSB_TRANSFER_TYPE_BULK) + usbi_dbg("BULK transfer: %d", transfer->length); + else + usbi_dbg("INTR transfer: %d", transfer->length); + err = sunos_do_async_io(transfer); + break; + + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + /* Isochronous/Stream is not supported */ + + /* fallthru */ + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + usbi_dbg("ISOC transfer: %d", transfer->length); + else + usbi_dbg("BULK STREAM transfer: %d", transfer->length); + err = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + return (err); +} + +int +sunos_cancel_transfer(struct usbi_transfer *itransfer) +{ + sunos_xfer_priv_t *tpriv; + sunos_dev_handle_priv_t *hpriv; + struct libusb_transfer *transfer; + struct aiocb *aiocb; + uint8_t ep; + int ret; + + tpriv = usbi_transfer_get_os_priv(itransfer); + aiocb = &tpriv->aiocb; + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + hpriv = (sunos_dev_handle_priv_t *)transfer->dev_handle->os_priv; + ep = sunos_usb_ep_index(transfer->endpoint); + + ret = aio_cancel(hpriv->eps[ep].datafd, aiocb); + + usbi_dbg("aio->fd=%d fd=%d ret = %d, %s", aiocb->aio_fildes, + hpriv->eps[ep].datafd, ret, (ret == AIO_CANCELED)? + strerror(0):strerror(errno)); + + if (ret != AIO_CANCELED) { + ret = _errno_to_libusb(errno); + } else { + /* + * we don't need to call usbi_handle_transfer_cancellation(), + * because we'll handle everything in sunos_async_callback. + */ + ret = LIBUSB_SUCCESS; + } + + return (ret); +} + +void +sunos_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + usbi_dbg(""); + + /* Nothing to do */ +} + +int +sunos_handle_transfer_completion(struct usbi_transfer *itransfer) +{ + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); +} + +int +sunos_clock_gettime(int clkid, struct timespec *tp) +{ + usbi_dbg("clock %d", clkid); + + if (clkid == USBI_CLOCK_REALTIME) + return clock_gettime(CLOCK_REALTIME, tp); + + if (clkid == USBI_CLOCK_MONOTONIC) + return clock_gettime(CLOCK_MONOTONIC, tp); + + return (LIBUSB_ERROR_INVALID_PARAM); +} + +int +_errno_to_libusb(int err) +{ + usbi_dbg("error: %s (%d)", strerror(err), err); + + switch (err) { + case EIO: + return (LIBUSB_ERROR_IO); + case EACCES: + return (LIBUSB_ERROR_ACCESS); + case ENOENT: + return (LIBUSB_ERROR_NO_DEVICE); + case ENOMEM: + return (LIBUSB_ERROR_NO_MEM); + case ETIMEDOUT: + return (LIBUSB_ERROR_TIMEOUT); + } + + return (LIBUSB_ERROR_OTHER); +} + +/* + * sunos_usb_get_status: + * gets status of endpoint + * + * Returns: ugen's last cmd status + */ +static int +sunos_usb_get_status(int fd) +{ + int status, ret; + + usbi_dbg("sunos_usb_get_status(): fd=%d", fd); + + ret = read(fd, &status, sizeof (status)); + if (ret == sizeof (status)) { + switch (status) { + case USB_LC_STAT_NOERROR: + usbi_dbg("No Error"); + break; + case USB_LC_STAT_CRC: + usbi_dbg("CRC Timeout Detected\n"); + break; + case USB_LC_STAT_BITSTUFFING: + usbi_dbg("Bit Stuffing Violation\n"); + break; + case USB_LC_STAT_DATA_TOGGLE_MM: + usbi_dbg("Data Toggle Mismatch\n"); + break; + case USB_LC_STAT_STALL: + usbi_dbg("End Point Stalled\n"); + break; + case USB_LC_STAT_DEV_NOT_RESP: + usbi_dbg("Device is Not Responding\n"); + break; + case USB_LC_STAT_PID_CHECKFAILURE: + usbi_dbg("PID Check Failure\n"); + break; + case USB_LC_STAT_UNEXP_PID: + usbi_dbg("Unexpected PID\n"); + break; + case USB_LC_STAT_DATA_OVERRUN: + usbi_dbg("Data Exceeded Size\n"); + break; + case USB_LC_STAT_DATA_UNDERRUN: + usbi_dbg("Less data received\n"); + break; + case USB_LC_STAT_BUFFER_OVERRUN: + usbi_dbg("Buffer Size Exceeded\n"); + break; + case USB_LC_STAT_BUFFER_UNDERRUN: + usbi_dbg("Buffer Underrun\n"); + break; + case USB_LC_STAT_TIMEOUT: + usbi_dbg("Command Timed Out\n"); + break; + case USB_LC_STAT_NOT_ACCESSED: + usbi_dbg("Not Accessed by h/w\n"); + break; + case USB_LC_STAT_UNSPECIFIED_ERR: + usbi_dbg("Unspecified Error\n"); + break; + case USB_LC_STAT_NO_BANDWIDTH: + usbi_dbg("No Bandwidth\n"); + break; + case USB_LC_STAT_HW_ERR: + usbi_dbg("Host Controller h/w Error\n"); + break; + case USB_LC_STAT_SUSPENDED: + usbi_dbg("Device was Suspended\n"); + break; + case USB_LC_STAT_DISCONNECTED: + usbi_dbg("Device was Disconnected\n"); + break; + case USB_LC_STAT_INTR_BUF_FULL: + usbi_dbg("Interrupt buffer was full\n"); + break; + case USB_LC_STAT_INVALID_REQ: + usbi_dbg("Request was Invalid\n"); + break; + case USB_LC_STAT_INTERRUPTED: + usbi_dbg("Request was Interrupted\n"); + break; + case USB_LC_STAT_NO_RESOURCES: + usbi_dbg("No resources available for " + "request\n"); + break; + case USB_LC_STAT_INTR_POLLING_FAILED: + usbi_dbg("Failed to Restart Poll"); + break; + default: + usbi_dbg("Error Not Determined %d\n", + status); + break; + } + } else { + usbi_dbg("read stat error: %s",strerror(errno)); + status = -1; + } + + return (status); +} + +const struct usbi_os_backend sunos_backend = { + .name = "Solaris", + .caps = 0, + .init = sunos_init, + .exit = sunos_exit, + .get_device_list = sunos_get_device_list, + .get_device_descriptor = sunos_get_device_descriptor, + .get_active_config_descriptor = sunos_get_active_config_descriptor, + .get_config_descriptor = sunos_get_config_descriptor, + .hotplug_poll = NULL, + .open = sunos_open, + .close = sunos_close, + .get_configuration = sunos_get_configuration, + .set_configuration = sunos_set_configuration, + + .claim_interface = sunos_claim_interface, + .release_interface = sunos_release_interface, + .set_interface_altsetting = sunos_set_interface_altsetting, + .clear_halt = sunos_clear_halt, + .reset_device = sunos_reset_device, /* TODO */ + .alloc_streams = NULL, + .free_streams = NULL, + .kernel_driver_active = NULL, + .detach_kernel_driver = NULL, + .attach_kernel_driver = NULL, + .destroy_device = sunos_destroy_device, + .submit_transfer = sunos_submit_transfer, + .cancel_transfer = sunos_cancel_transfer, + .handle_events = NULL, + .clear_transfer_priv = sunos_clear_transfer_priv, + .handle_transfer_completion = sunos_handle_transfer_completion, + .clock_gettime = sunos_clock_gettime, + .device_priv_size = sizeof(sunos_dev_priv_t), + .device_handle_priv_size = sizeof(sunos_dev_handle_priv_t), + .transfer_priv_size = sizeof(sunos_xfer_priv_t), +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.h new file mode 100644 index 0000000..5741660 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.h @@ -0,0 +1,74 @@ +/* + * + * Copyright (c) 2016, Oracle and/or its affiliates. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_SUNOS_H +#define LIBUSB_SUNOS_H + +#include +#include +#include "libusbi.h" + +#define READ 0 +#define WRITE 1 + +typedef struct sunos_device_priv { + uint8_t cfgvalue; /* active config value */ + uint8_t *raw_cfgdescr; /* active config descriptor */ + struct libusb_device_descriptor dev_descr; /* usb device descriptor */ + char *ugenpath; /* name of the ugen(4) node */ + char *phypath; /* physical path */ +} sunos_dev_priv_t; + +typedef struct endpoint { + int datafd; /* data file */ + int statfd; /* state file */ +} sunos_ep_priv_t; + +typedef struct sunos_device_handle_priv { + uint8_t altsetting[USB_MAXINTERFACES]; /* a interface's alt */ + uint8_t config_index; + sunos_ep_priv_t eps[USB_MAXENDPOINTS]; + sunos_dev_priv_t *dpriv; /* device private */ +} sunos_dev_handle_priv_t; + +typedef struct sunos_transfer_priv { + struct aiocb aiocb; + struct libusb_transfer *transfer; +} sunos_xfer_priv_t; + +struct node_args { + struct libusb_context *ctx; + struct discovered_devs **discdevs; + const char *last_ugenpath; + di_devlink_handle_t dlink_hdl; +}; + +struct devlink_cbarg { + struct node_args *nargs; /* di node walk arguments */ + di_node_t myself; /* the di node */ + di_minor_t minor; +}; + +/* AIO callback args */ +struct aio_callback_args{ + struct libusb_transfer *transfer; + struct aiocb aiocb; +}; + +#endif /* LIBUSB_SUNOS_H */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.c new file mode 100644 index 0000000..a4f270b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.c @@ -0,0 +1,79 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright © 2011 Vitali Lovich + * Copyright © 2011 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#if defined(__linux__) || defined(__OpenBSD__) +# if defined(__OpenBSD__) +# define _BSD_SOURCE +# endif +# include +# include +#elif defined(__APPLE__) +# include +#elif defined(__CYGWIN__) +# include +#endif + +#include "threads_posix.h" +#include "libusbi.h" + +int usbi_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, const struct timeval *tv) +{ + struct timespec timeout; + int r; + + r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout); + if (r < 0) + return r; + + timeout.tv_sec += tv->tv_sec; + timeout.tv_nsec += tv->tv_usec * 1000; + while (timeout.tv_nsec >= 1000000000L) { + timeout.tv_nsec -= 1000000000L; + timeout.tv_sec++; + } + + return pthread_cond_timedwait(cond, mutex, &timeout); +} + +int usbi_get_tid(void) +{ + int ret = -1; +#if defined(__ANDROID__) + ret = gettid(); +#elif defined(__linux__) + ret = syscall(SYS_gettid); +#elif defined(__OpenBSD__) + /* The following only works with OpenBSD > 5.1 as it requires + real thread support. For 5.1 and earlier, -1 is returned. */ + ret = syscall(SYS_getthrid); +#elif defined(__APPLE__) + ret = mach_thread_self(); + mach_port_deallocate(mach_task_self(), ret); +#elif defined(__CYGWIN__) + ret = GetCurrentThreadId(); +#endif +/* TODO: NetBSD thread ID support */ + return ret; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h new file mode 100644 index 0000000..4c1514e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h @@ -0,0 +1,55 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright © 2010 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_POSIX_H +#define LIBUSB_THREADS_POSIX_H + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#define usbi_mutex_static_t pthread_mutex_t +#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define usbi_mutex_static_lock pthread_mutex_lock +#define usbi_mutex_static_unlock pthread_mutex_unlock + +#define usbi_mutex_t pthread_mutex_t +#define usbi_mutex_init(mutex) pthread_mutex_init((mutex), NULL) +#define usbi_mutex_lock pthread_mutex_lock +#define usbi_mutex_unlock pthread_mutex_unlock +#define usbi_mutex_trylock pthread_mutex_trylock +#define usbi_mutex_destroy pthread_mutex_destroy + +#define usbi_cond_t pthread_cond_t +#define usbi_cond_init(cond) pthread_cond_init((cond), NULL) +#define usbi_cond_wait pthread_cond_wait +#define usbi_cond_broadcast pthread_cond_broadcast +#define usbi_cond_destroy pthread_cond_destroy + +#define usbi_tls_key_t pthread_key_t +#define usbi_tls_key_create(key) pthread_key_create((key), NULL) +#define usbi_tls_key_get pthread_getspecific +#define usbi_tls_key_set pthread_setspecific +#define usbi_tls_key_delete pthread_key_delete + +int usbi_get_tid(void); + +#endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c new file mode 100644 index 0000000..7c2e52d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c @@ -0,0 +1,259 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright © 2010 Michael Plante + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include "libusbi.h" + +struct usbi_cond_perthread { + struct list_head list; + DWORD tid; + HANDLE event; +}; + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) +{ + if (!mutex) + return EINVAL; + while (InterlockedExchange(mutex, 1) == 1) + SleepEx(0, TRUE); + return 0; +} + +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) +{ + if (!mutex) + return EINVAL; + InterlockedExchange(mutex, 0); + return 0; +} + +int usbi_mutex_init(usbi_mutex_t *mutex) +{ + if (!mutex) + return EINVAL; + *mutex = CreateMutex(NULL, FALSE, NULL); + if (!*mutex) + return ENOMEM; + return 0; +} + +int usbi_mutex_lock(usbi_mutex_t *mutex) +{ + DWORD result; + + if (!mutex) + return EINVAL; + result = WaitForSingleObject(*mutex, INFINITE); + if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) + return 0; // acquired (ToDo: check that abandoned is ok) + else + return EINVAL; // don't know how this would happen + // so don't know proper errno +} + +int usbi_mutex_unlock(usbi_mutex_t *mutex) +{ + if (!mutex) + return EINVAL; + if (ReleaseMutex(*mutex)) + return 0; + else + return EPERM; +} + +int usbi_mutex_trylock(usbi_mutex_t *mutex) +{ + DWORD result; + + if (!mutex) + return EINVAL; + result = WaitForSingleObject(*mutex, 0); + if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) + return 0; // acquired (ToDo: check that abandoned is ok) + else if (result == WAIT_TIMEOUT) + return EBUSY; + else + return EINVAL; // don't know how this would happen + // so don't know proper error +} + +int usbi_mutex_destroy(usbi_mutex_t *mutex) +{ + // It is not clear if CloseHandle failure is due to failure to unlock. + // If so, this should be errno=EBUSY. + if (!mutex || !CloseHandle(*mutex)) + return EINVAL; + *mutex = NULL; + return 0; +} + +int usbi_cond_init(usbi_cond_t *cond) +{ + if (!cond) + return EINVAL; + list_init(&cond->waiters); + list_init(&cond->not_waiting); + return 0; +} + +int usbi_cond_destroy(usbi_cond_t *cond) +{ + // This assumes no one is using this anymore. The check MAY NOT BE safe. + struct usbi_cond_perthread *pos, *next_pos; + + if(!cond) + return EINVAL; + if (!list_empty(&cond->waiters)) + return EBUSY; // (!see above!) + list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) { + CloseHandle(pos->event); + list_del(&pos->list); + free(pos); + } + return 0; +} + +int usbi_cond_broadcast(usbi_cond_t *cond) +{ + // Assumes mutex is locked; this is not in keeping with POSIX spec, but + // libusb does this anyway, so we simplify by not adding more sync + // primitives to the CV definition! + int fail = 0; + struct usbi_cond_perthread *pos; + + if (!cond) + return EINVAL; + list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) { + if (!SetEvent(pos->event)) + fail = 1; + } + // The wait function will remove its respective item from the list. + return fail ? EINVAL : 0; +} + +__inline static int usbi_cond_intwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, DWORD timeout_ms) +{ + struct usbi_cond_perthread *pos; + int r, found = 0; + DWORD r2, tid = GetCurrentThreadId(); + + if (!cond || !mutex) + return EINVAL; + list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) { + if(tid == pos->tid) { + found = 1; + break; + } + } + + if (!found) { + pos = calloc(1, sizeof(struct usbi_cond_perthread)); + if (!pos) + return ENOMEM; // This errno is not POSIX-allowed. + pos->tid = tid; + pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset. + if (!pos->event) { + free(pos); + return ENOMEM; + } + list_add(&pos->list, &cond->not_waiting); + } + + list_del(&pos->list); // remove from not_waiting list. + list_add(&pos->list, &cond->waiters); + + r = usbi_mutex_unlock(mutex); + if (r) + return r; + + r2 = WaitForSingleObject(pos->event, timeout_ms); + r = usbi_mutex_lock(mutex); + if (r) + return r; + + list_del(&pos->list); + list_add(&pos->list, &cond->not_waiting); + + if (r2 == WAIT_OBJECT_0) + return 0; + else if (r2 == WAIT_TIMEOUT) + return ETIMEDOUT; + else + return EINVAL; +} +// N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot! +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) +{ + return usbi_cond_intwait(cond, mutex, INFINITE); +} + +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv) +{ + DWORD millis; + + millis = (DWORD)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); + /* round up to next millisecond */ + if (tv->tv_usec % 1000) + millis++; + return usbi_cond_intwait(cond, mutex, millis); +} + +int usbi_tls_key_create(usbi_tls_key_t *key) +{ + if (!key) + return EINVAL; + *key = TlsAlloc(); + if (*key == TLS_OUT_OF_INDEXES) + return ENOMEM; + else + return 0; +} + +void *usbi_tls_key_get(usbi_tls_key_t key) +{ + return TlsGetValue(key); +} + +int usbi_tls_key_set(usbi_tls_key_t key, void *value) +{ + if (TlsSetValue(key, value)) + return 0; + else + return EINVAL; +} + +int usbi_tls_key_delete(usbi_tls_key_t key) +{ + if (TlsFree(key)) + return 0; + else + return EINVAL; +} + +int usbi_get_tid(void) +{ + return (int)GetCurrentThreadId(); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.h new file mode 100644 index 0000000..e97ee78 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.h @@ -0,0 +1,76 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright © 2010 Michael Plante + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_WINDOWS_H +#define LIBUSB_THREADS_WINDOWS_H + +#define usbi_mutex_static_t volatile LONG +#define USBI_MUTEX_INITIALIZER 0 + +#define usbi_mutex_t HANDLE + +typedef struct usbi_cond { + // Every time a thread touches the CV, it winds up in one of these lists. + // It stays there until the CV is destroyed, even if the thread terminates. + struct list_head waiters; + struct list_head not_waiting; +} usbi_cond_t; + +// We *were* getting timespec from pthread.h: +#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) +#define HAVE_STRUCT_TIMESPEC 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ + +// We *were* getting ETIMEDOUT from pthread.h: +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#define usbi_tls_key_t DWORD + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); + +int usbi_mutex_init(usbi_mutex_t *mutex); +int usbi_mutex_lock(usbi_mutex_t *mutex); +int usbi_mutex_unlock(usbi_mutex_t *mutex); +int usbi_mutex_trylock(usbi_mutex_t *mutex); +int usbi_mutex_destroy(usbi_mutex_t *mutex); + +int usbi_cond_init(usbi_cond_t *cond); +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, const struct timeval *tv); +int usbi_cond_broadcast(usbi_cond_t *cond); +int usbi_cond_destroy(usbi_cond_t *cond); + +int usbi_tls_key_create(usbi_tls_key_t *key); +void *usbi_tls_key_get(usbi_tls_key_t key); +int usbi_tls_key_set(usbi_tls_key_t key, void *value); +int usbi_tls_key_delete(usbi_tls_key_t key); + +int usbi_get_tid(void); + +#endif /* LIBUSB_THREADS_WINDOWS_H */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.c new file mode 100644 index 0000000..0d466b8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.c @@ -0,0 +1,899 @@ +/* + * Windows CE backend for libusb 1.0 + * Copyright © 2011-2013 RealVNC Ltd. + * Large portions taken from Windows backend, which is + * Copyright © 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include + +#include "libusbi.h" +#include "wince_usb.h" + +// Global variables +int windows_version = WINDOWS_CE; +static uint64_t hires_frequency, hires_ticks_to_ps; +static HANDLE driver_handle = INVALID_HANDLE_VALUE; +static int concurrent_usage = -1; + +/* + * Converts a windows error to human readable string + * uses retval as errorcode, or, if 0, use GetLastError() + */ +#if defined(ENABLE_LOGGING) +static const char *windows_error_str(DWORD error_code) +{ + static TCHAR wErr_string[ERR_BUFFER_SIZE]; + static char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + int len; + + if (error_code == 0) + error_code = GetLastError(); + + len = sprintf(err_string, "[%u] ", (unsigned int)error_code); + + size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + wErr_string, ERR_BUFFER_SIZE, NULL); + if (size == 0) { + DWORD format_error = GetLastError(); + if (format_error) + snprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", + (unsigned int)error_code, (unsigned int)format_error); + else + snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code); + } else { + // Remove CR/LF terminators, if present + size_t pos = size - 2; + if (wErr_string[pos] == 0x0D) + wErr_string[pos] = 0; + + if (!WideCharToMultiByte(CP_ACP, 0, wErr_string, -1, &err_string[len], ERR_BUFFER_SIZE - len, NULL, NULL)) + strcpy(err_string, "Unable to convert error string"); + } + + return err_string; +} +#endif + +static struct wince_device_priv *_device_priv(struct libusb_device *dev) +{ + return (struct wince_device_priv *)dev->os_priv; +} + +// ceusbkwrapper to libusb error code mapping +static int translate_driver_error(DWORD error) +{ + switch (error) { + case ERROR_INVALID_PARAMETER: + return LIBUSB_ERROR_INVALID_PARAM; + case ERROR_CALL_NOT_IMPLEMENTED: + case ERROR_NOT_SUPPORTED: + return LIBUSB_ERROR_NOT_SUPPORTED; + case ERROR_NOT_ENOUGH_MEMORY: + return LIBUSB_ERROR_NO_MEM; + case ERROR_INVALID_HANDLE: + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_BUSY: + return LIBUSB_ERROR_BUSY; + + // Error codes that are either unexpected, or have + // no suitable LIBUSB_ERROR equivalent. + case ERROR_CANCELLED: + case ERROR_INTERNAL_ERROR: + default: + return LIBUSB_ERROR_OTHER; + } +} + +static int init_dllimports(void) +{ + DLL_GET_HANDLE(ceusbkwrapper); + DLL_LOAD_FUNC(ceusbkwrapper, UkwOpenDriver, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceList, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseDeviceList, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceAddress, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceDescriptor, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfigDescriptor, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwCloseDriver, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwCancelTransfer, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueControlTransfer, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwClaimInterface, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseInterface, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwSetInterfaceAlternateSetting, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltHost, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltDevice, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfig, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwSetConfig, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwResetDevice, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwKernelDriverActive, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwAttachKernelDriver, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwDetachKernelDriver, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueBulkTransfer, TRUE); + DLL_LOAD_FUNC(ceusbkwrapper, UkwIsPipeHalted, TRUE); + + return LIBUSB_SUCCESS; +} + +static void exit_dllimports(void) +{ + DLL_FREE_HANDLE(ceusbkwrapper); +} + +static int init_device( + struct libusb_device *dev, UKW_DEVICE drv_dev, + unsigned char bus_addr, unsigned char dev_addr) +{ + struct wince_device_priv *priv = _device_priv(dev); + int r = LIBUSB_SUCCESS; + + dev->bus_number = bus_addr; + dev->device_address = dev_addr; + priv->dev = drv_dev; + + if (!UkwGetDeviceDescriptor(priv->dev, &(priv->desc))) + r = translate_driver_error(GetLastError()); + + return r; +} + +// Internal API functions +static int wince_init(struct libusb_context *ctx) +{ + int r = LIBUSB_ERROR_OTHER; + HANDLE semaphore; + LARGE_INTEGER li_frequency; + TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' + + _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); + semaphore = CreateSemaphore(NULL, 1, 1, sem_name); + if (semaphore == NULL) { + usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_MEM; + } + + // A successful wait brings our semaphore count to 0 (unsignaled) + // => any concurent wait stalls until the semaphore's release + if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { + usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0)); + CloseHandle(semaphore); + return LIBUSB_ERROR_NO_MEM; + } + + // NB: concurrent usage supposes that init calls are equally balanced with + // exit calls. If init is called more than exit, we will not exit properly + if ( ++concurrent_usage == 0 ) { // First init? + // Initialize pollable file descriptors + init_polling(); + + // Load DLL imports + if (init_dllimports() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + // try to open a handle to the driver + driver_handle = UkwOpenDriver(); + if (driver_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not connect to driver"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + // find out if we have access to a monotonic (hires) timer + if (QueryPerformanceFrequency(&li_frequency)) { + hires_frequency = li_frequency.QuadPart; + // The hires frequency can go as high as 4 GHz, so we'll use a conversion + // to picoseconds to compute the tv_nsecs part in clock_gettime + hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; + usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); + } else { + usbi_dbg("no hires timer available on this platform"); + hires_frequency = 0; + hires_ticks_to_ps = UINT64_C(0); + } + } + // At this stage, either we went through full init successfully, or didn't need to + r = LIBUSB_SUCCESS; + +init_exit: // Holds semaphore here. + if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + exit_dllimports(); + exit_polling(); + + if (driver_handle != INVALID_HANDLE_VALUE) { + UkwCloseDriver(driver_handle); + driver_handle = INVALID_HANDLE_VALUE; + } + } + + if (r != LIBUSB_SUCCESS) + --concurrent_usage; // Not expected to call libusb_exit if we failed. + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); + return r; +} + +static void wince_exit(void) +{ + HANDLE semaphore; + TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' + + _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); + semaphore = CreateSemaphore(NULL, 1, 1, sem_name); + if (semaphore == NULL) + return; + + // A successful wait brings our semaphore count to 0 (unsignaled) + // => any concurent wait stalls until the semaphore release + if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { + CloseHandle(semaphore); + return; + } + + // Only works if exits and inits are balanced exactly + if (--concurrent_usage < 0) { // Last exit + exit_dllimports(); + exit_polling(); + + if (driver_handle != INVALID_HANDLE_VALUE) { + UkwCloseDriver(driver_handle); + driver_handle = INVALID_HANDLE_VALUE; + } + } + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); +} + +static int wince_get_device_list( + struct libusb_context *ctx, + struct discovered_devs **discdevs) +{ + UKW_DEVICE devices[MAX_DEVICE_COUNT]; + struct discovered_devs *new_devices = *discdevs; + DWORD count = 0, i; + struct libusb_device *dev = NULL; + unsigned char bus_addr, dev_addr; + unsigned long session_id; + BOOL success; + DWORD release_list_offset = 0; + int r = LIBUSB_SUCCESS; + + success = UkwGetDeviceList(driver_handle, devices, MAX_DEVICE_COUNT, &count); + if (!success) { + int libusbErr = translate_driver_error(GetLastError()); + usbi_err(ctx, "could not get devices: %s", windows_error_str(0)); + return libusbErr; + } + + for (i = 0; i < count; ++i) { + release_list_offset = i; + success = UkwGetDeviceAddress(devices[i], &bus_addr, &dev_addr, &session_id); + if (!success) { + r = translate_driver_error(GetLastError()); + usbi_err(ctx, "could not get device address for %u: %s", (unsigned int)i, windows_error_str(0)); + goto err_out; + } + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev) { + usbi_dbg("using existing device for %u/%u (session %lu)", + bus_addr, dev_addr, session_id); + // Release just this element in the device list (as we already hold a + // reference to it). + UkwReleaseDeviceList(driver_handle, &devices[i], 1); + release_list_offset++; + } else { + usbi_dbg("allocating new device for %u/%u (session %lu)", + bus_addr, dev_addr, session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) { + r = LIBUSB_ERROR_NO_MEM; + goto err_out; + } + + r = init_device(dev, devices[i], bus_addr, dev_addr); + if (r < 0) + goto err_out; + + r = usbi_sanitize_device(dev); + if (r < 0) + goto err_out; + } + + new_devices = discovered_devs_append(new_devices, dev); + if (!discdevs) { + r = LIBUSB_ERROR_NO_MEM; + goto err_out; + } + + libusb_unref_device(dev); + } + + *discdevs = new_devices; + return r; +err_out: + *discdevs = new_devices; + libusb_unref_device(dev); + // Release the remainder of the unprocessed device list. + // The devices added to new_devices already will still be passed up to libusb, + // which can dispose of them at its leisure. + UkwReleaseDeviceList(driver_handle, &devices[release_list_offset], count - release_list_offset); + return r; +} + +static int wince_open(struct libusb_device_handle *handle) +{ + // Nothing to do to open devices as a handle to it has + // been retrieved by wince_get_device_list + return LIBUSB_SUCCESS; +} + +static void wince_close(struct libusb_device_handle *handle) +{ + // Nothing to do as wince_open does nothing. +} + +static int wince_get_device_descriptor( + struct libusb_device *device, + unsigned char *buffer, int *host_endian) +{ + struct wince_device_priv *priv = _device_priv(device); + + *host_endian = 1; + memcpy(buffer, &priv->desc, DEVICE_DESC_LENGTH); + return LIBUSB_SUCCESS; +} + +static int wince_get_active_config_descriptor( + struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian) +{ + struct wince_device_priv *priv = _device_priv(device); + DWORD actualSize = len; + + *host_endian = 0; + if (!UkwGetConfigDescriptor(priv->dev, UKW_ACTIVE_CONFIGURATION, buffer, len, &actualSize)) + return translate_driver_error(GetLastError()); + + return actualSize; +} + +static int wince_get_config_descriptor( + struct libusb_device *device, + uint8_t config_index, + unsigned char *buffer, size_t len, int *host_endian) +{ + struct wince_device_priv *priv = _device_priv(device); + DWORD actualSize = len; + + *host_endian = 0; + if (!UkwGetConfigDescriptor(priv->dev, config_index, buffer, len, &actualSize)) + return translate_driver_error(GetLastError()); + + return actualSize; +} + +static int wince_get_configuration( + struct libusb_device_handle *handle, + int *config) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + UCHAR cv = 0; + + if (!UkwGetConfig(priv->dev, &cv)) + return translate_driver_error(GetLastError()); + + (*config) = cv; + return LIBUSB_SUCCESS; +} + +static int wince_set_configuration( + struct libusb_device_handle *handle, + int config) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + // Setting configuration 0 places the device in Address state. + // This should correspond to the "unconfigured state" required by + // libusb when the specified configuration is -1. + UCHAR cv = (config < 0) ? 0 : config; + if (!UkwSetConfig(priv->dev, cv)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_claim_interface( + struct libusb_device_handle *handle, + int interface_number) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwClaimInterface(priv->dev, interface_number)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_release_interface( + struct libusb_device_handle *handle, + int interface_number) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, 0)) + return translate_driver_error(GetLastError()); + + if (!UkwReleaseInterface(priv->dev, interface_number)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_set_interface_altsetting( + struct libusb_device_handle *handle, + int interface_number, int altsetting) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, altsetting)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_clear_halt( + struct libusb_device_handle *handle, + unsigned char endpoint) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwClearHaltHost(priv->dev, endpoint)) + return translate_driver_error(GetLastError()); + + if (!UkwClearHaltDevice(priv->dev, endpoint)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_reset_device( + struct libusb_device_handle *handle) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwResetDevice(priv->dev)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_kernel_driver_active( + struct libusb_device_handle *handle, + int interface_number) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + BOOL result = FALSE; + + if (!UkwKernelDriverActive(priv->dev, interface_number, &result)) + return translate_driver_error(GetLastError()); + + return result ? 1 : 0; +} + +static int wince_detach_kernel_driver( + struct libusb_device_handle *handle, + int interface_number) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwDetachKernelDriver(priv->dev, interface_number)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_attach_kernel_driver( + struct libusb_device_handle *handle, + int interface_number) +{ + struct wince_device_priv *priv = _device_priv(handle->dev); + + if (!UkwAttachKernelDriver(priv->dev, interface_number)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static void wince_destroy_device(struct libusb_device *dev) +{ + struct wince_device_priv *priv = _device_priv(dev); + + UkwReleaseDeviceList(driver_handle, &priv->dev, 1); +} + +static void wince_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd); + + // No need to cancel transfer as it is either complete or abandoned + wfd.itransfer = NULL; + CloseHandle(wfd.handle); + usbi_free_fd(&transfer_priv->pollable_fd); +} + +static int wince_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + + if (!UkwCancelTransfer(priv->dev, transfer_priv->pollable_fd.overlapped, UKW_TF_NO_WAIT)) + return translate_driver_error(GetLastError()); + + return LIBUSB_SUCCESS; +} + +static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); + BOOL direction_in, ret; + struct winfd wfd; + DWORD flags; + HANDLE eventHandle; + PUKW_CONTROL_HEADER setup = NULL; + const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL; + + transfer_priv->pollable_fd = INVALID_WINFD; + if (control_transfer) { + setup = (PUKW_CONTROL_HEADER) transfer->buffer; + direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN; + } else { + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + } + flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER; + flags |= UKW_TF_SHORT_TRANSFER_OK; + + eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (eventHandle == NULL) { + usbi_err(ctx, "Failed to create event for async transfer"); + return LIBUSB_ERROR_NO_MEM; + } + + wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer); + if (wfd.fd < 0) { + CloseHandle(eventHandle); + return LIBUSB_ERROR_NO_MEM; + } + + transfer_priv->pollable_fd = wfd; + if (control_transfer) { + // Split out control setup header and data buffer + DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER); + PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)]; + + ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped); + } else { + ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer, + transfer->length, &transfer->actual_length, wfd.overlapped); + } + + if (!ret) { + int libusbErr = translate_driver_error(GetLastError()); + usbi_err(ctx, "UkwIssue%sTransfer failed: error %u", + control_transfer ? "Control" : "Bulk", (unsigned int)GetLastError()); + wince_clear_transfer_priv(itransfer); + return libusbErr; + } + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT); + + return LIBUSB_SUCCESS; +} + +static int wince_submit_iso_transfer(struct usbi_transfer *itransfer) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int wince_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return wince_submit_control_or_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return wince_submit_iso_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void wince_transfer_callback( + struct usbi_transfer *itransfer, + uint32_t io_result, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int status; + + usbi_dbg("handling I/O completion with errcode %u", io_result); + + if (io_result == ERROR_NOT_SUPPORTED && + transfer->type != LIBUSB_TRANSFER_TYPE_CONTROL) { + /* For functional stalls, the WinCE USB layer (and therefore the USB Kernel Wrapper + * Driver) will report USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the + * endpoint isn't actually stalled. + * + * One example of this is that some devices will occasionally fail to reply to an IN + * token. The WinCE USB layer carries on with the transaction until it is completed + * (or cancelled) but then completes it with USB_ERROR_STALL. + * + * This code therefore needs to confirm that there really is a stall error, by both + * checking the pipe status and requesting the endpoint status from the device. + */ + BOOL halted = FALSE; + usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall"); + if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) { + /* Pipe status retrieved, so now request endpoint status by sending a GET_STATUS + * control request to the device. This is done synchronously, which is a bit + * naughty, but this is a special corner case. + */ + WORD wStatus = 0; + DWORD written = 0; + UKW_CONTROL_HEADER ctrlHeader; + ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD | + LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT; + ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS; + ctrlHeader.wValue = 0; + ctrlHeader.wIndex = transfer->endpoint; + ctrlHeader.wLength = sizeof(wStatus); + if (UkwIssueControlTransfer(priv->dev, + UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT, + &ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) { + if (written == sizeof(wStatus) && + (wStatus & STATUS_HALT_FLAG) == 0) { + if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) { + usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success"); + io_result = ERROR_SUCCESS; + } else { + usbi_dbg("Endpoint doesn't appear to be stalled, but the host is halted, changing error"); + io_result = ERROR_IO_DEVICE; + } + } + } + } + } + + switch(io_result) { + case ERROR_SUCCESS: + itransfer->transferred += io_size; + status = LIBUSB_TRANSFER_COMPLETED; + break; + case ERROR_CANCELLED: + usbi_dbg("detected transfer cancel"); + status = LIBUSB_TRANSFER_CANCELLED; + break; + case ERROR_NOT_SUPPORTED: + case ERROR_GEN_FAILURE: + usbi_dbg("detected endpoint stall"); + status = LIBUSB_TRANSFER_STALL; + break; + case ERROR_SEM_TIMEOUT: + usbi_dbg("detected semaphore timeout"); + status = LIBUSB_TRANSFER_TIMED_OUT; + break; + case ERROR_OPERATION_ABORTED: + usbi_dbg("detected operation aborted"); + status = LIBUSB_TRANSFER_CANCELLED; + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + + wince_clear_transfer_priv(itransfer); + if (status == LIBUSB_TRANSFER_CANCELLED) + usbi_handle_transfer_cancellation(itransfer); + else + usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); +} + +static void wince_handle_callback( + struct usbi_transfer *itransfer, + uint32_t io_result, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + wince_transfer_callback (itransfer, io_result, io_size); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + } +} + +static int wince_handle_events( + struct libusb_context *ctx, + struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + struct wince_transfer_priv* transfer_priv = NULL; + POLL_NFDS_TYPE i = 0; + BOOL found = FALSE; + struct usbi_transfer *transfer; + DWORD io_size, io_result; + int r = LIBUSB_SUCCESS; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + + usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); + + if (!fds[i].revents) + continue; + + num_ready--; + + // Because a Windows OVERLAPPED is used for poll emulation, + // a pollable fd is created and stored with each transfer + usbi_mutex_lock(&ctx->flying_transfers_lock); + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + transfer_priv = usbi_transfer_get_os_priv(transfer); + if (transfer_priv->pollable_fd.fd == fds[i].fd) { + found = TRUE; + break; + } + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (found && HasOverlappedIoCompleted(transfer_priv->pollable_fd.overlapped)) { + io_result = (DWORD)transfer_priv->pollable_fd.overlapped->Internal; + io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; + usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd); + // let handle_callback free the event using the transfer wfd + // If you don't use the transfer wfd, you run a risk of trying to free a + // newly allocated wfd that took the place of the one from the transfer. + wince_handle_callback(transfer, io_result, io_size); + } else if (found) { + usbi_err(ctx, "matching transfer for fd %d has not completed", fds[i]); + r = LIBUSB_ERROR_OTHER; + break; + } else { + usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); + r = LIBUSB_ERROR_NOT_FOUND; + break; + } + } + usbi_mutex_unlock(&ctx->open_devs_lock); + + return r; +} + +/* + * Monotonic and real time functions + */ +static int wince_clock_gettime(int clk_id, struct timespec *tp) +{ + LARGE_INTEGER hires_counter; + ULARGE_INTEGER rtime; + FILETIME filetime; + SYSTEMTIME st; + + switch(clk_id) { + case USBI_CLOCK_MONOTONIC: + if (hires_frequency != 0 && QueryPerformanceCounter(&hires_counter)) { + tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); + tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps); + return LIBUSB_SUCCESS; + } + // Fall through and return real-time if monotonic read failed or was not detected @ init + case USBI_CLOCK_REALTIME: + // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx + // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00 + // Note however that our resolution is bounded by the Windows system time + // functions and is at best of the order of 1 ms (or, usually, worse) + GetSystemTime(&st); + SystemTimeToFileTime(&st, &filetime); + rtime.LowPart = filetime.dwLowDateTime; + rtime.HighPart = filetime.dwHighDateTime; + rtime.QuadPart -= EPOCH_TIME; + tp->tv_sec = (long)(rtime.QuadPart / 10000000); + tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100); + return LIBUSB_SUCCESS; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +const struct usbi_os_backend wince_backend = { + "Windows CE", + 0, + wince_init, + wince_exit, + + wince_get_device_list, + NULL, /* hotplug_poll */ + wince_open, + wince_close, + + wince_get_device_descriptor, + wince_get_active_config_descriptor, + wince_get_config_descriptor, + NULL, /* get_config_descriptor_by_value() */ + + wince_get_configuration, + wince_set_configuration, + wince_claim_interface, + wince_release_interface, + + wince_set_interface_altsetting, + wince_clear_halt, + wince_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + NULL, /* dev_mem_alloc() */ + NULL, /* dev_mem_free() */ + + wince_kernel_driver_active, + wince_detach_kernel_driver, + wince_attach_kernel_driver, + + wince_destroy_device, + + wince_submit_transfer, + wince_cancel_transfer, + wince_clear_transfer_priv, + + wince_handle_events, + NULL, /* handle_transfer_completion() */ + + wince_clock_gettime, + sizeof(struct wince_device_priv), + 0, + sizeof(struct wince_transfer_priv), +}; diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.h new file mode 100644 index 0000000..edcb9fc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.h @@ -0,0 +1,126 @@ +/* + * Windows CE backend for libusb 1.0 + * Copyright © 2011-2013 RealVNC Ltd. + * Portions taken from Windows backend, which is + * Copyright © 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#pragma once + +#include "windows_common.h" + +#include +#include "poll_windows.h" + +#define MAX_DEVICE_COUNT 256 + +// This is a modified dump of the types in the ceusbkwrapper.h library header +// with functions transformed into extern pointers. +// +// This backend dynamically loads ceusbkwrapper.dll and doesn't include +// ceusbkwrapper.h directly to simplify the build process. The kernel +// side wrapper driver is built using the platform image build tools, +// which makes it difficult to reference directly from the libusb build +// system. +struct UKW_DEVICE_PRIV; +typedef struct UKW_DEVICE_PRIV *UKW_DEVICE; +typedef UKW_DEVICE *PUKW_DEVICE, *LPUKW_DEVICE; + +typedef struct { + UINT8 bLength; + UINT8 bDescriptorType; + UINT16 bcdUSB; + UINT8 bDeviceClass; + UINT8 bDeviceSubClass; + UINT8 bDeviceProtocol; + UINT8 bMaxPacketSize0; + UINT16 idVendor; + UINT16 idProduct; + UINT16 bcdDevice; + UINT8 iManufacturer; + UINT8 iProduct; + UINT8 iSerialNumber; + UINT8 bNumConfigurations; +} UKW_DEVICE_DESCRIPTOR, *PUKW_DEVICE_DESCRIPTOR, *LPUKW_DEVICE_DESCRIPTOR; + +typedef struct { + UINT8 bmRequestType; + UINT8 bRequest; + UINT16 wValue; + UINT16 wIndex; + UINT16 wLength; +} UKW_CONTROL_HEADER, *PUKW_CONTROL_HEADER, *LPUKW_CONTROL_HEADER; + +// Collection of flags which can be used when issuing transfer requests +/* Indicates that the transfer direction is 'in' */ +#define UKW_TF_IN_TRANSFER 0x00000001 +/* Indicates that the transfer direction is 'out' */ +#define UKW_TF_OUT_TRANSFER 0x00000000 +/* Specifies that the transfer should complete as soon as possible, + * even if no OVERLAPPED structure has been provided. */ +#define UKW_TF_NO_WAIT 0x00000100 +/* Indicates that transfers shorter than the buffer are ok */ +#define UKW_TF_SHORT_TRANSFER_OK 0x00000200 +#define UKW_TF_SEND_TO_DEVICE 0x00010000 +#define UKW_TF_SEND_TO_INTERFACE 0x00020000 +#define UKW_TF_SEND_TO_ENDPOINT 0x00040000 +/* Don't block when waiting for memory allocations */ +#define UKW_TF_DONT_BLOCK_FOR_MEM 0x00080000 + +/* Value to use when dealing with configuration values, such as UkwGetConfigDescriptor, + * to specify the currently active configuration for the device. */ +#define UKW_ACTIVE_CONFIGURATION -1 + +DLL_DECLARE_HANDLE(ceusbkwrapper); +DLL_DECLARE_FUNC(WINAPI, HANDLE, UkwOpenDriver, ()); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceList, (HANDLE, LPUKW_DEVICE, DWORD, LPDWORD)); +DLL_DECLARE_FUNC(WINAPI, void, UkwReleaseDeviceList, (HANDLE, LPUKW_DEVICE, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceAddress, (UKW_DEVICE, unsigned char*, unsigned char*, unsigned long*)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceDescriptor, (UKW_DEVICE, LPUKW_DEVICE_DESCRIPTOR)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfigDescriptor, (UKW_DEVICE, DWORD, LPVOID, DWORD, LPDWORD)); +DLL_DECLARE_FUNC(WINAPI, void, UkwCloseDriver, (HANDLE)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwCancelTransfer, (UKW_DEVICE, LPOVERLAPPED, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueControlTransfer, (UKW_DEVICE, DWORD, LPUKW_CONTROL_HEADER, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClaimInterface, (UKW_DEVICE, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwReleaseInterface, (UKW_DEVICE, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetInterfaceAlternateSetting, (UKW_DEVICE, DWORD, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltHost, (UKW_DEVICE, UCHAR)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltDevice, (UKW_DEVICE, UCHAR)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfig, (UKW_DEVICE, PUCHAR)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetConfig, (UKW_DEVICE, UCHAR)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwResetDevice, (UKW_DEVICE)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwKernelDriverActive, (UKW_DEVICE, DWORD, PBOOL)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwAttachKernelDriver, (UKW_DEVICE, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwDetachKernelDriver, (UKW_DEVICE, DWORD)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueBulkTransfer, (UKW_DEVICE, DWORD, UCHAR, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)); +DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIsPipeHalted, (UKW_DEVICE, UCHAR, LPBOOL)); + +// Used to determine if an endpoint status really is halted on a failed transfer. +#define STATUS_HALT_FLAG 0x1 + +struct wince_device_priv { + UKW_DEVICE dev; + UKW_DEVICE_DESCRIPTOR desc; +}; + +struct wince_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; +}; + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_common.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_common.h new file mode 100644 index 0000000..55344ca --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_common.h @@ -0,0 +1,124 @@ +/* + * Windows backend common header for libusb 1.0 + * + * This file brings together header code common between + * the desktop Windows and Windows CE backends. + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +// Windows API default is uppercase - ugh! +#if !defined(bool) +#define bool BOOL +#endif +#if !defined(true) +#define true TRUE +#endif +#if !defined(false) +#define false FALSE +#endif + +#define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime + +#if defined(__CYGWIN__ ) +#define _stricmp strcasecmp +#define _strdup strdup +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, (LPDWORD)f) +#endif + +#define safe_free(p) do {if (p != NULL) {free((void *)p); p = NULL;}} while (0) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#define ERR_BUFFER_SIZE 256 + +/* + * API macros - leveraged from libusb-win32 1.x + */ +#ifndef _WIN32_WCE +#define DLL_STRINGIFY(s) #s +#define DLL_LOAD_LIBRARY(name) LoadLibraryA(DLL_STRINGIFY(name)) +#else +#define DLL_STRINGIFY(s) L#s +#define DLL_LOAD_LIBRARY(name) LoadLibrary(DLL_STRINGIFY(name)) +#endif + +/* + * Macros for handling DLL themselves + */ +#define DLL_DECLARE_HANDLE(name) \ + static HMODULE __dll_##name##_handle = NULL + +#define DLL_GET_HANDLE(name) \ + do { \ + __dll_##name##_handle = DLL_LOAD_LIBRARY(name); \ + if (!__dll_##name##_handle) \ + return LIBUSB_ERROR_OTHER; \ + } while (0) + +#define DLL_FREE_HANDLE(name) \ + do { \ + if (__dll_##name##_handle) { \ + FreeLibrary(__dll_##name##_handle); \ + __dll_##name##_handle = NULL; \ + } \ + } while(0) + + +/* + * Macros for handling functions within a DLL + */ +#define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args) \ + typedef ret (api * __dll_##name##_func_t)args; \ + static __dll_##name##_func_t prefixname = NULL + +#define DLL_DECLARE_FUNC(api, ret, name, args) \ + DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args) +#define DLL_DECLARE_FUNC_PREFIXED(api, ret, prefix, name, args) \ + DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefix##name, name, args) + +#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ + do { \ + HMODULE h = __dll_##dll##_handle; \ + prefixname = (__dll_##name##_func_t)GetProcAddress(h, \ + DLL_STRINGIFY(name)); \ + if (prefixname) \ + break; \ + prefixname = (__dll_##name##_func_t)GetProcAddress(h, \ + DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \ + if (prefixname) \ + break; \ + prefixname = (__dll_##name##_func_t)GetProcAddress(h, \ + DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \ + if (prefixname) \ + break; \ + if (ret_on_failure) \ + return LIBUSB_ERROR_NOT_FOUND; \ + } while(0) + +#define DLL_LOAD_FUNC(dll, name, ret_on_failure) \ + DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure) +#define DLL_LOAD_FUNC_PREFIXED(dll, prefix, name, ret_on_failure) \ + DLL_LOAD_FUNC_PREFIXNAME(dll, prefix##name, name, ret_on_failure) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.c new file mode 100644 index 0000000..d935394 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.c @@ -0,0 +1,591 @@ +/* + * windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * Hash table functions adapted from glibc, by Ulrich Drepper et al. + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include + +#include "libusbi.h" +#include "windows_common.h" +#include "windows_nt_common.h" + +// Global variables for clock_gettime mechanism +static uint64_t hires_ticks_to_ps; +static uint64_t hires_frequency; + +#define TIMER_REQUEST_RETRY_MS 100 +#define WM_TIMER_REQUEST (WM_USER + 1) +#define WM_TIMER_EXIT (WM_USER + 2) + +// used for monotonic clock_gettime() +struct timer_request { + struct timespec *tp; + HANDLE event; +}; + +// Timer thread +static HANDLE timer_thread = NULL; +static DWORD timer_thread_id = 0; + +/* User32 dependencies */ +DLL_DECLARE_HANDLE(User32); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM)); + +static unsigned __stdcall windows_clock_gettime_threaded(void *param); + +/* +* Converts a windows error to human readable string +* uses retval as errorcode, or, if 0, use GetLastError() +*/ +#if defined(ENABLE_LOGGING) +const char *windows_error_str(DWORD error_code) +{ + static char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + int len; + + if (error_code == 0) + error_code = GetLastError(); + + len = sprintf(err_string, "[%u] ", (unsigned int)error_code); + + // Translate codes returned by SetupAPI. The ones we are dealing with are either + // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx + switch (error_code & 0xE0000000) { + case 0: + error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified + break; + case 0xE0000000: + error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); + break; + default: + break; + } + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + &err_string[len], ERR_BUFFER_SIZE - len, NULL); + if (size == 0) { + DWORD format_error = GetLastError(); + if (format_error) + snprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", + (unsigned int)error_code, (unsigned int)format_error); + else + snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code); + } else { + // Remove CRLF from end of message, if present + size_t pos = len + size - 2; + if (err_string[pos] == '\r') + err_string[pos] = '\0'; + } + + return err_string; +} +#endif + +/* Hash table functions - modified From glibc 2.3.2: + [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + [Knuth] The Art of Computer Programming, part 3 (6.4) */ + +#define HTAB_SIZE 1021UL // *MUST* be a prime number!! + +typedef struct htab_entry { + unsigned long used; + char *str; +} htab_entry; + +static htab_entry *htab_table = NULL; +static usbi_mutex_t htab_mutex = NULL; +static unsigned long htab_filled; + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +static bool htab_create(struct libusb_context *ctx) +{ + if (htab_table != NULL) { + usbi_err(ctx, "hash table already allocated"); + return true; + } + + // Create a mutex + usbi_mutex_init(&htab_mutex); + + usbi_dbg("using %lu entries hash table", HTAB_SIZE); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = calloc(HTAB_SIZE + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return false; + } + + return true; +} + +/* After using the hash table it has to be destroyed. */ +static void htab_destroy(void) +{ + unsigned long i; + + if (htab_table == NULL) + return; + + for (i = 0; i < HTAB_SIZE; i++) + free(htab_table[i].str); + + safe_free(htab_table); + + usbi_mutex_destroy(&htab_mutex); +} + +/* This is the search function. It uses double hashing with open addressing. + We use a trick to speed up the lookup. The table is created with one + more element available. This enables us to use the index zero special. + This index will never be used because we store the first hash index in + the field used where zero means not used. Every other value means used. + The used field can be used as a first fast comparison for equality of + the stored and the parameter value. This helps to prevent unnecessary + expensive calls of strcmp. */ +unsigned long htab_hash(const char *str) +{ + unsigned long hval, hval2; + unsigned long idx; + unsigned long r = 5381; + int c; + const char *sz = str; + + if (str == NULL) + return 0; + + // Compute main hash value (algorithm suggested by Nokia) + while ((c = *sz++) != 0) + r = ((r << 5) + r) + c; + if (r == 0) + ++r; + + // compute table hash: simply take the modulus + hval = r % HTAB_SIZE; + if (hval == 0) + ++hval; + + // Try the first index + idx = hval; + + // Mutually exclusive access (R/W lock would be better) + usbi_mutex_lock(&htab_mutex); + + if (htab_table[idx].used) { + if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) + goto out_unlock; // existing hash + + usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str); + + // Second hash function, as suggested in [Knuth] + hval2 = 1 + hval % (HTAB_SIZE - 2); + + do { + // Because size is prime this guarantees to step through all available indexes + if (idx <= hval2) + idx = HTAB_SIZE + idx - hval2; + else + idx -= hval2; + + // If we visited all entries leave the loop unsuccessfully + if (idx == hval) + break; + + // If entry is found use it. + if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) + goto out_unlock; + } while (htab_table[idx].used); + } + + // Not found => New entry + + // If the table is full return an error + if (htab_filled >= HTAB_SIZE) { + usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE); + idx = 0; + goto out_unlock; + } + + htab_table[idx].str = _strdup(str); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + idx = 0; + goto out_unlock; + } + + htab_table[idx].used = hval; + ++htab_filled; + +out_unlock: + usbi_mutex_unlock(&htab_mutex); + + return idx; +} + +static int windows_init_dlls(void) +{ + DLL_GET_HANDLE(User32); + DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE); + DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE); + DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE); + + return LIBUSB_SUCCESS; +} + +static void windows_exit_dlls(void) +{ + DLL_FREE_HANDLE(User32); +} + +static bool windows_init_clock(struct libusb_context *ctx) +{ + DWORD_PTR affinity, dummy; + HANDLE event = NULL; + LARGE_INTEGER li_frequency; + int i; + + if (QueryPerformanceFrequency(&li_frequency)) { + // Load DLL imports + if (windows_init_dlls() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + return false; + } + + // The hires frequency can go as high as 4 GHz, so we'll use a conversion + // to picoseconds to compute the tv_nsecs part in clock_gettime + hires_frequency = li_frequency.QuadPart; + hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; + usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); + + // Because QueryPerformanceCounter might report different values when + // running on different cores, we create a separate thread for the timer + // calls, which we glue to the first available core always to prevent timing discrepancies. + if (!GetProcessAffinityMask(GetCurrentProcess(), &affinity, &dummy) || (affinity == 0)) { + usbi_err(ctx, "could not get process affinity: %s", windows_error_str(0)); + return false; + } + + // The process affinity mask is a bitmask where each set bit represents a core on + // which this process is allowed to run, so we find the first set bit + for (i = 0; !(affinity & (DWORD_PTR)(1 << i)); i++); + affinity = (DWORD_PTR)(1 << i); + + usbi_dbg("timer thread will run on core #%d", i); + + event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (event == NULL) { + usbi_err(ctx, "could not create event: %s", windows_error_str(0)); + return false; + } + + timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, (void *)event, + 0, (unsigned int *)&timer_thread_id); + if (timer_thread == NULL) { + usbi_err(ctx, "unable to create timer thread - aborting"); + CloseHandle(event); + return false; + } + + if (!SetThreadAffinityMask(timer_thread, affinity)) + usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise"); + + // Wait for timer thread to init before continuing. + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) { + usbi_err(ctx, "failed to wait for timer thread to become ready - aborting"); + CloseHandle(event); + return false; + } + + CloseHandle(event); + } else { + usbi_dbg("no hires timer available on this platform"); + hires_frequency = 0; + hires_ticks_to_ps = UINT64_C(0); + } + + return true; +} + +void windows_destroy_clock(void) +{ + if (timer_thread) { + // actually the signal to quit the thread. + if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_EXIT, 0, 0) + || (WaitForSingleObject(timer_thread, INFINITE) != WAIT_OBJECT_0)) { + usbi_dbg("could not wait for timer thread to quit"); + TerminateThread(timer_thread, 1); + // shouldn't happen, but we're destroying + // all objects it might have held anyway. + } + CloseHandle(timer_thread); + timer_thread = NULL; + timer_thread_id = 0; + } +} + +/* +* Monotonic and real time functions +*/ +static unsigned __stdcall windows_clock_gettime_threaded(void *param) +{ + struct timer_request *request; + LARGE_INTEGER hires_counter; + MSG msg; + + // The following call will create this thread's message queue + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946.aspx + pPeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + // Signal windows_init_clock() that we're ready to service requests + if (!SetEvent((HANDLE)param)) + usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0)); + param = NULL; + + // Main loop - wait for requests + while (1) { + if (pGetMessageA(&msg, NULL, WM_TIMER_REQUEST, WM_TIMER_EXIT) == -1) { + usbi_err(NULL, "GetMessage failed for timer thread: %s", windows_error_str(0)); + return 1; + } + + switch (msg.message) { + case WM_TIMER_REQUEST: + // Requests to this thread are for hires always + // Microsoft says that this function always succeeds on XP and later + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx + request = (struct timer_request *)msg.lParam; + QueryPerformanceCounter(&hires_counter); + request->tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); + request->tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps); + if (!SetEvent(request->event)) + usbi_err(NULL, "SetEvent failed for timer request: %s", windows_error_str(0)); + break; + case WM_TIMER_EXIT: + usbi_dbg("timer thread quitting"); + return 0; + } + } +} + +int windows_clock_gettime(int clk_id, struct timespec *tp) +{ + struct timer_request request; +#if !defined(_MSC_VER) || (_MSC_VER < 1900) + FILETIME filetime; + ULARGE_INTEGER rtime; +#endif + DWORD r; + + switch (clk_id) { + case USBI_CLOCK_MONOTONIC: + if (timer_thread) { + request.tp = tp; + request.event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (request.event == NULL) + return LIBUSB_ERROR_NO_MEM; + + if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) { + usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0)); + CloseHandle(request.event); + return LIBUSB_ERROR_OTHER; + } + + do { + r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS); + if (r == WAIT_TIMEOUT) + usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?"); + else if (r == WAIT_FAILED) + usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0)); + } while (r == WAIT_TIMEOUT); + CloseHandle(request.event); + + if (r == WAIT_OBJECT_0) + return LIBUSB_SUCCESS; + else + return LIBUSB_ERROR_OTHER; + } + // Fall through and return real-time if monotonic was not detected @ timer init + case USBI_CLOCK_REALTIME: +#if defined(_MSC_VER) && (_MSC_VER >= 1900) + timespec_get(tp, TIME_UTC); +#else + // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx + // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00 + // Note however that our resolution is bounded by the Windows system time + // functions and is at best of the order of 1 ms (or, usually, worse) + GetSystemTimeAsFileTime(&filetime); + rtime.LowPart = filetime.dwLowDateTime; + rtime.HighPart = filetime.dwHighDateTime; + rtime.QuadPart -= EPOCH_TIME; + tp->tv_sec = (long)(rtime.QuadPart / 10000000); + tp->tv_nsec = (long)((rtime.QuadPart % 10000000) * 100); +#endif + return LIBUSB_SUCCESS; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +{ + int status, istatus; + + usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size); + + switch (io_result) { + case NO_ERROR: + status = windows_copy_transfer_data(itransfer, io_size); + break; + case ERROR_GEN_FAILURE: + usbi_dbg("detected endpoint stall"); + status = LIBUSB_TRANSFER_STALL; + break; + case ERROR_SEM_TIMEOUT: + usbi_dbg("detected semaphore timeout"); + status = LIBUSB_TRANSFER_TIMED_OUT; + break; + case ERROR_OPERATION_ABORTED: + istatus = windows_copy_transfer_data(itransfer, io_size); + if (istatus != LIBUSB_TRANSFER_COMPLETED) + usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); + + usbi_dbg("detected operation aborted"); + status = LIBUSB_TRANSFER_CANCELLED; + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + windows_clear_transfer_priv(itransfer); // Cancel polling + if (status == LIBUSB_TRANSFER_CANCELLED) + usbi_handle_transfer_cancellation(itransfer); + else + usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); +} + +void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + windows_transfer_callback(itransfer, io_result, io_size); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform"); + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + } +} + +int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + POLL_NFDS_TYPE i; + bool found = false; + struct usbi_transfer *transfer; + struct winfd *pollable_fd = NULL; + DWORD io_size, io_result; + int r = LIBUSB_SUCCESS; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + + usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); + + if (!fds[i].revents) + continue; + + num_ready--; + + // Because a Windows OVERLAPPED is used for poll emulation, + // a pollable fd is created and stored with each transfer + usbi_mutex_lock(&ctx->flying_transfers_lock); + found = false; + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + pollable_fd = windows_get_fd(transfer); + if (pollable_fd->fd == fds[i].fd) { + found = true; + break; + } + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (found) { + windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size); + + usbi_remove_pollfd(ctx, pollable_fd->fd); + // let handle_callback free the event using the transfer wfd + // If you don't use the transfer wfd, you run a risk of trying to free a + // newly allocated wfd that took the place of the one from the transfer. + windows_handle_callback(transfer, io_result, io_size); + } else { + usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); + r = LIBUSB_ERROR_NOT_FOUND; + break; + } + } + usbi_mutex_unlock(&ctx->open_devs_lock); + + return r; +} + +int windows_common_init(struct libusb_context *ctx) +{ + if (!windows_init_clock(ctx)) + goto error_roll_back; + + if (!htab_create(ctx)) + goto error_roll_back; + + return LIBUSB_SUCCESS; + +error_roll_back: + windows_common_exit(); + return LIBUSB_ERROR_NO_MEM; +} + +void windows_common_exit(void) +{ + htab_destroy(); + windows_destroy_clock(); + windows_exit_dlls(); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h new file mode 100644 index 0000000..52ea870 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h @@ -0,0 +1,63 @@ +/* + * Windows backend common header for libusb 1.0 + * + * This file brings together header code common between + * the desktop Windows backends. + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +// Missing from MinGW +#if !defined(FACILITY_SETUPAPI) +#define FACILITY_SETUPAPI 15 +#endif + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; + +int windows_common_init(struct libusb_context *ctx); +void windows_common_exit(void); + +unsigned long htab_hash(const char *str); +int windows_clock_gettime(int clk_id, struct timespec *tp); + +void windows_clear_transfer_priv(struct usbi_transfer *itransfer); +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +struct winfd *windows_get_fd(struct usbi_transfer *transfer); +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size); + +void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size); +int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + +#if defined(ENABLE_LOGGING) +const char *windows_error_str(DWORD error_code); +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.c new file mode 100644 index 0000000..7cc5793 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.c @@ -0,0 +1,905 @@ +/* + * windows UsbDk backend for libusb 1.0 + * Copyright © 2014 Red Hat, Inc. + + * Authors: + * Dmitry Fleytman + * Pavel Gurvich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#if defined(USE_USBDK) + +#include +#include +#include + +#include "libusbi.h" +#include "windows_common.h" +#include "windows_nt_common.h" + +#define ULONG64 uint64_t +#define PVOID64 uint64_t + +typedef CONST WCHAR *PCWCHAR; +#define wcsncpy_s wcsncpy + +#include "windows_usbdk.h" + +#if !defined(STATUS_SUCCESS) +typedef LONG NTSTATUS; +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +#if !defined(STATUS_CANCELLED) +#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) +#endif + +#if !defined(STATUS_REQUEST_CANCELED) +#define STATUS_REQUEST_CANCELED ((NTSTATUS)0xC0000703L) +#endif + +#if !defined(USBD_SUCCESS) +typedef int32_t USBD_STATUS; +#define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0) +#define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1) +#define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0) +#define USBD_STATUS_STALL_PID ((USBD_STATUS) 0xc0000004) +#define USBD_STATUS_ENDPOINT_HALTED ((USBD_STATUS) 0xc0000030) +#define USBD_STATUS_BAD_START_FRAME ((USBD_STATUS) 0xc0000a00) +#define USBD_STATUS_TIMEOUT ((USBD_STATUS) 0xc0006000) +#define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000) +#endif + +static int concurrent_usage = -1; + +struct usbdk_device_priv { + USB_DK_DEVICE_INFO info; + PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors; + HANDLE redirector_handle; + uint8_t active_configuration; +}; + +struct usbdk_transfer_priv { + USB_DK_TRANSFER_REQUEST request; + struct winfd pollable_fd; + PULONG64 IsochronousPacketsArray; + PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray; +}; + +static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev) +{ + return (struct usbdk_device_priv *)dev->os_priv; +} + +static inline struct usbdk_transfer_priv *_usbdk_transfer_priv(struct usbi_transfer *itransfer) +{ + return (struct usbdk_transfer_priv *)usbi_transfer_get_os_priv(itransfer); +} + +static struct { + HMODULE module; + + USBDK_GET_DEVICES_LIST GetDevicesList; + USBDK_RELEASE_DEVICES_LIST ReleaseDevicesList; + USBDK_START_REDIRECT StartRedirect; + USBDK_STOP_REDIRECT StopRedirect; + USBDK_GET_CONFIGURATION_DESCRIPTOR GetConfigurationDescriptor; + USBDK_RELEASE_CONFIGURATION_DESCRIPTOR ReleaseConfigurationDescriptor; + USBDK_READ_PIPE ReadPipe; + USBDK_WRITE_PIPE WritePipe; + USBDK_ABORT_PIPE AbortPipe; + USBDK_RESET_PIPE ResetPipe; + USBDK_SET_ALTSETTING SetAltsetting; + USBDK_RESET_DEVICE ResetDevice; + USBDK_GET_REDIRECTOR_SYSTEM_HANDLE GetRedirectorSystemHandle; +} usbdk_helper; + +static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name) +{ + FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name); + + if (api_ptr == NULL) + usbi_err(ctx, "UsbDkHelper API %s not found, error %d", api_name, GetLastError()); + + return api_ptr; +} + +static void unload_usbdk_helper_dll(void) +{ + if (usbdk_helper.module != NULL) { + FreeLibrary(usbdk_helper.module); + usbdk_helper.module = NULL; + } +} + +static int load_usbdk_helper_dll(struct libusb_context *ctx) +{ + usbdk_helper.module = LoadLibraryA("UsbDkHelper"); + if (usbdk_helper.module == NULL) { + usbi_err(ctx, "Failed to load UsbDkHelper.dll, error %d", GetLastError()); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbdk_helper.GetDevicesList = (USBDK_GET_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_GetDevicesList"); + if (usbdk_helper.GetDevicesList == NULL) + goto error_unload; + + usbdk_helper.ReleaseDevicesList = (USBDK_RELEASE_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseDevicesList"); + if (usbdk_helper.ReleaseDevicesList == NULL) + goto error_unload; + + usbdk_helper.StartRedirect = (USBDK_START_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StartRedirect"); + if (usbdk_helper.StartRedirect == NULL) + goto error_unload; + + usbdk_helper.StopRedirect = (USBDK_STOP_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StopRedirect"); + if (usbdk_helper.StopRedirect == NULL) + goto error_unload; + + usbdk_helper.GetConfigurationDescriptor = (USBDK_GET_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_GetConfigurationDescriptor"); + if (usbdk_helper.GetConfigurationDescriptor == NULL) + goto error_unload; + + usbdk_helper.ReleaseConfigurationDescriptor = (USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseConfigurationDescriptor"); + if (usbdk_helper.ReleaseConfigurationDescriptor == NULL) + goto error_unload; + + usbdk_helper.ReadPipe = (USBDK_READ_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ReadPipe"); + if (usbdk_helper.ReadPipe == NULL) + goto error_unload; + + usbdk_helper.WritePipe = (USBDK_WRITE_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_WritePipe"); + if (usbdk_helper.WritePipe == NULL) + goto error_unload; + + usbdk_helper.AbortPipe = (USBDK_ABORT_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_AbortPipe"); + if (usbdk_helper.AbortPipe == NULL) + goto error_unload; + + usbdk_helper.ResetPipe = (USBDK_RESET_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ResetPipe"); + if (usbdk_helper.ResetPipe == NULL) + goto error_unload; + + usbdk_helper.SetAltsetting = (USBDK_SET_ALTSETTING)get_usbdk_proc_addr(ctx, "UsbDk_SetAltsetting"); + if (usbdk_helper.SetAltsetting == NULL) + goto error_unload; + + usbdk_helper.ResetDevice = (USBDK_RESET_DEVICE)get_usbdk_proc_addr(ctx, "UsbDk_ResetDevice"); + if (usbdk_helper.ResetDevice == NULL) + goto error_unload; + + usbdk_helper.GetRedirectorSystemHandle = (USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)get_usbdk_proc_addr(ctx, "UsbDk_GetRedirectorSystemHandle"); + if (usbdk_helper.GetRedirectorSystemHandle == NULL) + goto error_unload; + + return LIBUSB_SUCCESS; + +error_unload: + FreeLibrary(usbdk_helper.module); + usbdk_helper.module = NULL; + return LIBUSB_ERROR_NOT_FOUND; +} + +static int usbdk_init(struct libusb_context *ctx) +{ + int r; + + if (++concurrent_usage == 0) { // First init? + r = load_usbdk_helper_dll(ctx); + if (r) + goto init_exit; + + init_polling(); + + r = windows_common_init(ctx); + if (r) + goto init_exit; + } + // At this stage, either we went through full init successfully, or didn't need to + r = LIBUSB_SUCCESS; + +init_exit: + if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + exit_polling(); + windows_common_exit(); + unload_usbdk_helper_dll(); + } + + if (r != LIBUSB_SUCCESS) + --concurrent_usage; // Not expected to call libusb_exit if we failed. + + return r; +} + +static int usbdk_get_session_id_for_device(struct libusb_context *ctx, + PUSB_DK_DEVICE_ID id, unsigned long *session_id) +{ + char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID)]; + + if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) { + usbi_warn(ctx, "cannot form device identity", id->DeviceID); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + *session_id = htab_hash(dev_identity); + + return LIBUSB_SUCCESS; +} + +static void usbdk_release_config_descriptors(struct usbdk_device_priv *p, uint8_t count) +{ + uint8_t i; + + for (i = 0; i < count; i++) + usbdk_helper.ReleaseConfigurationDescriptor(p->config_descriptors[i]); + + free(p->config_descriptors); + p->config_descriptors = NULL; +} + +static int usbdk_cache_config_descriptors(struct libusb_context *ctx, + struct usbdk_device_priv *p, PUSB_DK_DEVICE_INFO info) +{ + uint8_t i; + USB_DK_CONFIG_DESCRIPTOR_REQUEST Request; + Request.ID = info->ID; + + p->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + if (p->config_descriptors == NULL) { + usbi_err(ctx, "failed to allocate configuration descriptors holder"); + return LIBUSB_ERROR_NO_MEM; + } + + for (i = 0; i < info->DeviceDescriptor.bNumConfigurations; i++) { + ULONG Length; + + Request.Index = i; + if (!usbdk_helper.GetConfigurationDescriptor(&Request, &p->config_descriptors[i], &Length)) { + usbi_err(ctx, "failed to retrieve configuration descriptors"); + usbdk_release_config_descriptors(p, i); + return LIBUSB_ERROR_OTHER; + } + } + + return LIBUSB_SUCCESS; +} + +static inline int usbdk_device_priv_init(struct libusb_context *ctx, struct libusb_device *dev, PUSB_DK_DEVICE_INFO info) +{ + struct usbdk_device_priv *p = _usbdk_device_priv(dev); + + p->info = *info; + p->active_configuration = 0; + + return usbdk_cache_config_descriptors(ctx, p, info); +} + +static void usbdk_device_init(libusb_device *dev, PUSB_DK_DEVICE_INFO info) +{ + dev->bus_number = (uint8_t)info->FilterID; + dev->port_number = (uint8_t)info->Port; + dev->parent_dev = NULL; + + //Addresses in libusb are 1-based + dev->device_address = (uint8_t)(info->Port + 1); + + dev->num_configurations = info->DeviceDescriptor.bNumConfigurations; + dev->device_descriptor = info->DeviceDescriptor; + + switch (info->Speed) { + case LowSpeed: + dev->speed = LIBUSB_SPEED_LOW; + break; + case FullSpeed: + dev->speed = LIBUSB_SPEED_FULL; + break; + case HighSpeed: + dev->speed = LIBUSB_SPEED_HIGH; + break; + case SuperSpeed: + dev->speed = LIBUSB_SPEED_SUPER; + break; + case NoSpeed: + default: + dev->speed = LIBUSB_SPEED_UNKNOWN; + break; + } +} + +static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + int r = LIBUSB_SUCCESS; + ULONG i; + struct discovered_devs *discdevs = NULL; + ULONG dev_number; + PUSB_DK_DEVICE_INFO devices; + + if(!usbdk_helper.GetDevicesList(&devices, &dev_number)) + return LIBUSB_ERROR_OTHER; + + for (i = 0; i < dev_number; i++) { + unsigned long session_id; + struct libusb_device *dev = NULL; + + if (usbdk_get_session_id_for_device(ctx, &devices[i].ID, &session_id)) + continue; + + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) { + usbi_err(ctx, "failed to allocate a new device structure"); + continue; + } + + usbdk_device_init(dev, &devices[i]); + if (usbdk_device_priv_init(ctx, dev, &devices[i]) != LIBUSB_SUCCESS) { + libusb_unref_device(dev); + continue; + } + } + + discdevs = discovered_devs_append(*_discdevs, dev); + libusb_unref_device(dev); + if (!discdevs) { + usbi_err(ctx, "cannot append new device to list"); + r = LIBUSB_ERROR_NO_MEM; + goto func_exit; + } + + *_discdevs = discdevs; + } + +func_exit: + usbdk_helper.ReleaseDevicesList(devices); + return r; +} + +static void usbdk_exit(void) +{ + if (--concurrent_usage < 0) { + windows_common_exit(); + exit_polling(); + unload_usbdk_helper_dll(); + } +} + +static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev); + + memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptors[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, config_header, size); + *host_endian = 0; + + return (int)size; +} + +static inline int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration, + buffer, len, host_endian); +} + +static int usbdk_open(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + priv->redirector_handle = usbdk_helper.StartRedirect(&priv->info.ID); + if (priv->redirector_handle == INVALID_HANDLE_VALUE) { + usbi_err(DEVICE_CTX(dev_handle->dev), "Redirector startup failed"); + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +static void usbdk_close(struct libusb_device_handle *dev_handle) +{ + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.StopRedirect(priv->redirector_handle)) { + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + usbi_err(ctx, "Redirector shutdown failed"); + } +} + +static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + *config = _usbdk_device_priv(dev_handle->dev)->active_configuration; + + return LIBUSB_SUCCESS; +} + +static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + UNUSED(dev_handle); + UNUSED(config); + return LIBUSB_SUCCESS; +} + +static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) { + usbi_err(ctx, "SetAltsetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_SUCCESS; +} + +static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_reset_device(struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); + + if (!usbdk_helper.ResetDevice(priv->redirector_handle)) { + usbi_err(ctx, "ResetDevice failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int usbdk_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int usbdk_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + UNUSED(dev_handle); + UNUSED(iface); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void usbdk_destroy_device(struct libusb_device *dev) +{ + struct usbdk_device_priv* p = _usbdk_device_priv(dev); + + if (p->config_descriptors != NULL) + usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations); +} + +void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + usbi_free_fd(&transfer_priv->pollable_fd); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + safe_free(transfer_priv->IsochronousPacketsArray); + safe_free(transfer_priv->IsochronousResultsArray); + } +} + +static int usbdk_do_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct winfd wfd; + ULONG Length; + TransferResult transResult; + HANDLE sysHandle; + + sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + wfd = usbi_create_fd(sysHandle, RW_READ, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) + return LIBUSB_ERROR_NO_MEM; + + transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.TransferType = ControlTransferType; + transfer_priv->pollable_fd = INVALID_WINFD; + Length = (ULONG)transfer->length; + + if (IS_XFERIN(transfer)) + transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + else + transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + + switch (transResult) { + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)Length; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + + return LIBUSB_SUCCESS; +} + +static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct winfd wfd; + TransferResult transferRes; + HANDLE sysHandle; + + transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_BULK: + transfer_priv->request.TransferType = BulkTransferType; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + transfer_priv->request.TransferType = IntertuptTransferType; + break; + default: + usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0)); + return LIBUSB_ERROR_INVALID_PARAM; + } + + transfer_priv->pollable_fd = INVALID_WINFD; + + sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) + return LIBUSB_ERROR_NO_MEM; + + if (IS_XFERIN(transfer)) + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + else + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + + switch (transferRes) { + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT); + + return LIBUSB_SUCCESS; +} + +static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct winfd wfd; + TransferResult transferRes; + int i; + HANDLE sysHandle; + + transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; + transfer_priv->request.BufferLength = transfer->length; + transfer_priv->request.EndpointAddress = transfer->endpoint; + transfer_priv->request.TransferType = IsochronousTransferType; + transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets; + transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64)); + transfer_priv->request.IsochronousPacketsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousPacketsArray; + if (!transfer_priv->IsochronousPacketsArray) { + usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT)); + transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousResultsArray; + if (!transfer_priv->IsochronousResultsArray) { + usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0)); + free(transfer_priv->IsochronousPacketsArray); + return LIBUSB_ERROR_IO; + } + + for (i = 0; i < transfer->num_iso_packets; i++) + transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length; + + transfer_priv->pollable_fd = INVALID_WINFD; + + sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); + + wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + free(transfer_priv->IsochronousPacketsArray); + free(transfer_priv->IsochronousResultsArray); + return LIBUSB_ERROR_NO_MEM; + } + + if (IS_XFERIN(transfer)) + transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + else + transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); + + switch (transferRes) { + case TransferSuccess: + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + break; + case TransferSuccessAsync: + break; + case TransferFailure: + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + free(transfer_priv->IsochronousPacketsArray); + free(transfer_priv->IsochronousResultsArray); + return LIBUSB_ERROR_IO; + } + + transfer_priv->pollable_fd = wfd; + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT); + + return LIBUSB_SUCCESS; +} + +static int usbdk_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return usbdk_do_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)) + return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk + else + return usbdk_do_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return usbdk_do_iso_transfer(itransfer); + default: + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int usbdk_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); + + if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +static int usbdk_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + // Control transfers cancelled by IoCancelXXX() API + // No special treatment needed + return LIBUSB_SUCCESS; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return usbdk_abort_transfers(itransfer); + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +struct winfd *windows_get_fd(struct usbi_transfer *transfer) +{ + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); + return &transfer_priv->pollable_fd; +} + +static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus) +{ + if (USBD_SUCCESS(UsbdStatus)) + return NO_ERROR; + + switch (UsbdStatus) { + case USBD_STATUS_STALL_PID: + case USBD_STATUS_ENDPOINT_HALTED: + case USBD_STATUS_BAD_START_FRAME: + return ERROR_GEN_FAILURE; + case USBD_STATUS_TIMEOUT: + return ERROR_SEM_TIMEOUT; + case USBD_STATUS_CANCELED: + return ERROR_OPERATION_ABORTED; + default: + return ERROR_FUNCTION_FAILED; + } +} + +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size) +{ + if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first + || GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped + struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer); + struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); + + if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + int i; + for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) { + struct libusb_iso_packet_descriptor *lib_desc = <ransfer->iso_packet_desc[i]; + + switch (transfer_priv->IsochronousResultsArray[i].TransferResult) { + case STATUS_SUCCESS: + case STATUS_CANCELLED: + case STATUS_REQUEST_CANCELED: + lib_desc->status = LIBUSB_TRANSFER_COMPLETED; // == ERROR_SUCCESS + break; + default: + lib_desc->status = LIBUSB_TRANSFER_ERROR; // ERROR_UNKNOWN_EXCEPTION; + break; + } + + lib_desc->actual_length = (unsigned int)transfer_priv->IsochronousResultsArray[i].ActualLength; + } + } + + *io_size = (DWORD) transfer_priv->request.Result.GenResult.BytesTransferred; + *io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus); + } + else { + *io_result = GetLastError(); + } +} + +static int usbdk_clock_gettime(int clk_id, struct timespec *tp) +{ + return windows_clock_gettime(clk_id, tp); +} + +const struct usbi_os_backend usbdk_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + usbdk_init, + usbdk_exit, + + usbdk_get_device_list, + NULL, + usbdk_open, + usbdk_close, + + usbdk_get_device_descriptor, + usbdk_get_active_config_descriptor, + usbdk_get_config_descriptor, + NULL, + + usbdk_get_configuration, + usbdk_set_configuration, + usbdk_claim_interface, + usbdk_release_interface, + + usbdk_set_interface_altsetting, + usbdk_clear_halt, + usbdk_reset_device, + + NULL, + NULL, + + NULL, // dev_mem_alloc() + NULL, // dev_mem_free() + + usbdk_kernel_driver_active, + usbdk_detach_kernel_driver, + usbdk_attach_kernel_driver, + + usbdk_destroy_device, + + usbdk_submit_transfer, + usbdk_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + NULL, + + usbdk_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct usbdk_device_priv), + 0, + sizeof(struct usbdk_transfer_priv), +}; + +#endif /* USE_USBDK */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.h new file mode 100644 index 0000000..04a9787 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.h @@ -0,0 +1,146 @@ +/* +* windows UsbDk backend for libusb 1.0 +* Copyright © 2014 Red Hat, Inc. + +* Authors: +* Dmitry Fleytman +* Pavel Gurvich +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +typedef struct tag_USB_DK_DEVICE_ID { + WCHAR DeviceID[MAX_DEVICE_ID_LEN]; + WCHAR InstanceID[MAX_DEVICE_ID_LEN]; +} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID; + +static inline void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID) +{ + wcsncpy_s(ID->DeviceID, DeviceID, MAX_DEVICE_ID_LEN); + wcsncpy_s(ID->InstanceID, InstanceID, MAX_DEVICE_ID_LEN); +} + +typedef struct tag_USB_DK_DEVICE_INFO { + USB_DK_DEVICE_ID ID; + ULONG64 FilterID; + ULONG64 Port; + ULONG64 Speed; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; +} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO; + +typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST { + USB_DK_DEVICE_ID ID; + ULONG64 Index; +} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST; + +typedef struct tag_USB_DK_ISO_TRANSFER_RESULT { + ULONG64 ActualLength; + ULONG64 TransferResult; +} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT; + +typedef struct tag_USB_DK_GEN_TRANSFER_RESULT { + ULONG64 BytesTransferred; + ULONG64 UsbdStatus; // USBD_STATUS code +} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT; + +typedef struct tag_USB_DK_TRANSFER_RESULT { + USB_DK_GEN_TRANSFER_RESULT GenResult; + PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT +} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT; + +typedef struct tag_USB_DK_TRANSFER_REQUEST { + ULONG64 EndpointAddress; + PVOID64 Buffer; + ULONG64 BufferLength; + ULONG64 TransferType; + ULONG64 IsochronousPacketsArraySize; + PVOID64 IsochronousPacketsArray; + + USB_DK_TRANSFER_RESULT Result; +} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST; + +typedef enum { + TransferFailure = 0, + TransferSuccess, + TransferSuccessAsync +} TransferResult; + +typedef enum { + NoSpeed = 0, + LowSpeed, + FullSpeed, + HighSpeed, + SuperSpeed +} USB_DK_DEVICE_SPEED; + +typedef enum { + ControlTransferType, + BulkTransferType, + IntertuptTransferType, + IsochronousTransferType +} USB_DK_TRANSFER_TYPE; + +typedef BOOL (__cdecl *USBDK_GET_DEVICES_LIST)( + PUSB_DK_DEVICE_INFO *DeviceInfo, + PULONG DeviceNumber +); +typedef void (__cdecl *USBDK_RELEASE_DEVICES_LIST)( + PUSB_DK_DEVICE_INFO DeviceInfo +); +typedef HANDLE (__cdecl *USBDK_START_REDIRECT)( + PUSB_DK_DEVICE_ID DeviceId +); +typedef BOOL (__cdecl *USBDK_STOP_REDIRECT)( + HANDLE DeviceHandle +); +typedef BOOL (__cdecl *USBDK_GET_CONFIGURATION_DESCRIPTOR)( + PUSB_DK_CONFIG_DESCRIPTOR_REQUEST Request, + PUSB_CONFIGURATION_DESCRIPTOR *Descriptor, + PULONG Length +); +typedef void (__cdecl *USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)( + PUSB_CONFIGURATION_DESCRIPTOR Descriptor +); +typedef TransferResult (__cdecl *USBDK_WRITE_PIPE)( + HANDLE DeviceHandle, + PUSB_DK_TRANSFER_REQUEST Request, + LPOVERLAPPED lpOverlapped +); +typedef TransferResult (__cdecl *USBDK_READ_PIPE)( + HANDLE DeviceHandle, + PUSB_DK_TRANSFER_REQUEST Request, + LPOVERLAPPED lpOverlapped +); +typedef BOOL (__cdecl *USBDK_ABORT_PIPE)( + HANDLE DeviceHandle, + ULONG64 PipeAddress +); +typedef BOOL (__cdecl *USBDK_RESET_PIPE)( + HANDLE DeviceHandle, + ULONG64 PipeAddress +); +typedef BOOL (__cdecl *USBDK_SET_ALTSETTING)( + HANDLE DeviceHandle, + ULONG64 InterfaceIdx, + ULONG64 AltSettingIdx +); +typedef BOOL (__cdecl *USBDK_RESET_DEVICE)( + HANDLE DeviceHandle +); +typedef HANDLE (__cdecl *USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)( + HANDLE DeviceHandle +); diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c new file mode 100644 index 0000000..0dce0ea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c @@ -0,0 +1,4290 @@ +/* + * windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * Hash table functions adapted from glibc, by Ulrich Drepper et al. + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#if !defined(USE_USBDK) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusbi.h" +#include "poll_windows.h" +#include "windows_winusb.h" + +#define HANDLE_VALID(h) (((h) != 0) && ((h) != INVALID_HANDLE_VALUE)) + +// The 2 macros below are used in conjunction with safe loops. +#define LOOP_CHECK(fcall) \ + { \ + r = fcall; \ + if (r != LIBUSB_SUCCESS) \ + continue; \ + } +#define LOOP_BREAK(err) \ + { \ + r = err; \ + continue; \ + } + +// WinUSB-like API prototypes +static int winusbx_init(int sub_api, struct libusb_context *ctx); +static int winusbx_exit(int sub_api); +static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle); +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// HID API prototypes +static int hid_init(int sub_api, struct libusb_context *ctx); +static int hid_exit(int sub_api); +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle); +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// Composite API prototypes +static int composite_init(int sub_api, struct libusb_context *ctx); +static int composite_exit(int sub_api); +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle); +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); + + +// Global variables +int windows_version = WINDOWS_UNDEFINED; +static char windows_version_str[128] = "Undefined"; +// Concurrency +static int concurrent_usage = -1; +static usbi_mutex_t autoclaim_lock; +// API globals +#define CHECK_WINUSBX_AVAILABLE(sub_api) \ + do { \ + if (sub_api == SUB_API_NOTSET) \ + sub_api = priv->sub_api; \ + if (!WinUSBX[sub_api].initialized) \ + return LIBUSB_ERROR_ACCESS; \ + } while(0) + +static HMODULE WinUSBX_handle = NULL; +static struct winusb_interface WinUSBX[SUB_API_MAX]; +static const char *sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES; + +static bool api_hid_available = false; +#define CHECK_HID_AVAILABLE \ + do { \ + if (!api_hid_available) \ + return LIBUSB_ERROR_ACCESS; \ + } while (0) + +static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) +{ + if ((guid1 != NULL) && (guid2 != NULL)) + return (memcmp(guid1, guid2, sizeof(GUID)) == 0); + + return false; +} + +#if defined(ENABLE_LOGGING) +static char *guid_to_string(const GUID *guid) +{ + static char guid_string[MAX_GUID_STRING_LENGTH]; + + if (guid == NULL) + return NULL; + + sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned int)guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + + return guid_string; +} +#endif + +/* + * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes. + * Return an allocated sanitized string or NULL on error. + */ +static char *sanitize_path(const char *path) +{ + const char root_prefix[] = { '\\', '\\', '.', '\\' }; + size_t j, size; + char *ret_path; + size_t add_root = 0; + + if (path == NULL) + return NULL; + + size = strlen(path) + 1; + + // Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes. + if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) + || ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) { + add_root = sizeof(root_prefix); + size += add_root; + } + + ret_path = malloc(size); + if (ret_path == NULL) + return NULL; + + strcpy(&ret_path[add_root], path); + + // Ensure consistency with root prefix + memcpy(ret_path, root_prefix, sizeof(root_prefix)); + + // Same goes for '\' and '#' after the root prefix. Ensure '#' is used + for (j = sizeof(root_prefix); j < size; j++) { + ret_path[j] = (char)toupper((int)ret_path[j]); // Fix case too + if (ret_path[j] == '\\') + ret_path[j] = '#'; + } + + return ret_path; +} + +/* + * Cfgmgr32, OLE32 and SetupAPI DLL functions + */ +static int init_dlls(void) +{ + DLL_GET_HANDLE(Cfgmgr32); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, TRUE); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, TRUE); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Sibling, TRUE); + DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Device_IDA, TRUE); + + // Prefixed to avoid conflict with header files + DLL_GET_HANDLE(AdvAPI32); + DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, TRUE); + DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, TRUE); + + DLL_GET_HANDLE(Kernel32); + DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE); + + DLL_GET_HANDLE(OLE32); + DLL_LOAD_FUNC_PREFIXED(OLE32, p, CLSIDFromString, TRUE); + + DLL_GET_HANDLE(SetupAPI); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, TRUE); + DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, TRUE); + + return LIBUSB_SUCCESS; +} + +static void exit_dlls(void) +{ + DLL_FREE_HANDLE(Cfgmgr32); + DLL_FREE_HANDLE(AdvAPI32); + DLL_FREE_HANDLE(Kernel32); + DLL_FREE_HANDLE(OLE32); + DLL_FREE_HANDLE(SetupAPI); +} + +/* + * enumerate interfaces for the whole USB class + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * usb_class: the generic USB class for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static bool get_devinfo_data(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *usb_class, unsigned _index) +{ + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); + if (*dev_info == INVALID_HANDLE_VALUE) + return false; + } + + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return false; + } + return true; +} + +/* + * enumerate interfaces for a specific GUID + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * guid: the GUID for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index) +{ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details; + DWORD size; + + if (_index <= 0) + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + + dev_interface_details = calloc(1, size); + if (dev_interface_details == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + } + + return dev_interface_details; + +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; +} + +/* For libusb0 filter */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index, char *filter_path) +{ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details; + DWORD size; + + if (_index <= 0) + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + + dev_interface_details = calloc(1, size); + if (dev_interface_details == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, dev_interface_details, size, &size, NULL)) + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + + // [trobinso] lookup the libusb0 symbolic index. + if (dev_interface_details) { + HKEY hkey_device_interface = pSetupDiOpenDeviceInterfaceRegKey(*dev_info, &dev_interface_data, 0, KEY_READ); + if (hkey_device_interface != INVALID_HANDLE_VALUE) { + DWORD libusb0_symboliclink_index = 0; + DWORD value_length = sizeof(DWORD); + DWORD value_type = 0; + LONG status; + + status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type, + (LPBYTE)&libusb0_symboliclink_index, &value_length); + if (status == ERROR_SUCCESS) { + if (libusb0_symboliclink_index < 256) { + // libusb0.sys is connected to this device instance. + // If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter. + sprintf(filter_path, "\\\\.\\libusb0-%04u", (unsigned int)libusb0_symboliclink_index); + usbi_dbg("assigned libusb0 symbolic link %s", filter_path); + } else { + // libusb0.sys was connected to this device instance at one time; but not anymore. + } + } + pRegCloseKey(hkey_device_interface); + } + } + + return dev_interface_details; + +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; +} + +/* + * Returns the session ID of a device's nth level ancestor + * If there's no device at the nth level, return 0 + */ +static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level) +{ + DWORD parent_devinst; + unsigned long session_id; + char *sanitized_path; + char path[MAX_PATH_LENGTH]; + unsigned i; + + if (level < 1) + return 0; + + for (i = 0; i < level; i++) { + if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS) + return 0; + devinst = parent_devinst; + } + + if (CM_Get_Device_IDA(devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) + return 0; + + // TODO: (post hotplug): try without sanitizing + sanitized_path = sanitize_path(path); + if (sanitized_path == NULL) + return 0; + + session_id = htab_hash(sanitized_path); + free(sanitized_path); + return session_id; +} + +/* + * Determine which interface the given endpoint address belongs to + */ +static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, uint8_t ep) +{ + const struct libusb_interface *intf; + const struct libusb_interface_descriptor *intf_desc; + int i, j, k; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + intf = &conf_desc->interface[i]; + for (j = 0; j < intf->num_altsetting; j++) { + intf_desc = &intf->altsetting[j]; + for (k = 0; k < intf_desc->bNumEndpoints; k++) { + if (intf_desc->endpoint[k].bEndpointAddress == ep) { + usbi_dbg("found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i); + return intf_desc->bInterfaceNumber; + } + } + } + } + + usbi_dbg("endpoint %02X not found on any interface", ep); + return LIBUSB_ERROR_NOT_FOUND; +} + +/* + * Populate the endpoints addresses of the device_priv interface helper structs + */ +static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + int i, r; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct libusb_config_descriptor *conf_desc; + const struct libusb_interface_descriptor *if_desc; + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + r = libusb_get_active_config_descriptor(dev_handle->dev, &conf_desc); + if (r != LIBUSB_SUCCESS) { + usbi_warn(ctx, "could not read config descriptor: error %d", r); + return r; + } + + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); + + if (if_desc->bNumEndpoints == 0) { + usbi_dbg("no endpoints found for interface %d", iface); + libusb_free_config_descriptor(conf_desc); + return LIBUSB_SUCCESS; + } + + priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints); + if (priv->usb_interface[iface].endpoint == NULL) { + libusb_free_config_descriptor(conf_desc); + return LIBUSB_ERROR_NO_MEM; + } + + priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints; + for (i = 0; i < if_desc->bNumEndpoints; i++) { + priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress; + usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface); + } + libusb_free_config_descriptor(conf_desc); + + // Extra init may be required to configure endpoints + return priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface); +} + +// Lookup for a match in the list of API driver names +// return -1 if not found, driver match number otherwise +static int get_sub_api(char *driver, int api) +{ + int i; + const char sep_str[2] = {LIST_SEPARATOR, 0}; + char *tok, *tmp_str; + size_t len = strlen(driver); + + if (len == 0) + return SUB_API_NOTSET; + + tmp_str = _strdup(driver); + if (tmp_str == NULL) + return SUB_API_NOTSET; + + tok = strtok(tmp_str, sep_str); + while (tok != NULL) { + for (i = 0; i < usb_api_backend[api].nb_driver_names; i++) { + if (_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) { + free(tmp_str); + return i; + } + } + tok = strtok(NULL, sep_str); + } + + free(tmp_str); + return SUB_API_NOTSET; +} + +/* + * auto-claiming and auto-release helper functions + */ +static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type) +{ + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv( + transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface = *interface_number; + int r = LIBUSB_SUCCESS; + + switch(api_type) { + case USB_API_WINUSBX: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_mutex_lock(&autoclaim_lock); + if (current_interface < 0) { // No serviceable interface was found + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + // Must claim an interface of the same API type + if ((priv->usb_interface[current_interface].apib->id == api_type) + && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS)) { + usbi_dbg("auto-claimed interface %d for control request", current_interface); + if (handle_priv->autoclaim_count[current_interface] != 0) + usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero"); + handle_priv->autoclaim_count[current_interface]++; + break; + } + } + if (current_interface == USB_MAXINTERFACES) { + usbi_err(ctx, "could not auto-claim any interface"); + r = LIBUSB_ERROR_NOT_FOUND; + } + } else { + // If we have a valid interface that was autoclaimed, we must increment + // its autoclaim count so that we can prevent an early release. + if (handle_priv->autoclaim_count[current_interface] != 0) + handle_priv->autoclaim_count[current_interface]++; + } + usbi_mutex_unlock(&autoclaim_lock); + + *interface_number = current_interface; + return r; +} + +static void auto_release(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + libusb_device_handle *dev_handle = transfer->dev_handle; + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + int r; + + usbi_mutex_lock(&autoclaim_lock); + if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) { + handle_priv->autoclaim_count[transfer_priv->interface_number]--; + if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) { + r = libusb_release_interface(dev_handle, transfer_priv->interface_number); + if (r == LIBUSB_SUCCESS) + usbi_dbg("auto-released interface %d", transfer_priv->interface_number); + else + usbi_dbg("failed to auto-release interface %d (%s)", + transfer_priv->interface_number, libusb_error_name((enum libusb_error)r)); + } + } + usbi_mutex_unlock(&autoclaim_lock); +} + +/* Windows version dtection */ +static BOOL is_x64(void) +{ + BOOL ret = FALSE; + + // Detect if we're running a 32 or 64 bit system + if (sizeof(uintptr_t) < 8) { + if (pIsWow64Process != NULL) + pIsWow64Process(GetCurrentProcess(), &ret); + } else { + ret = TRUE; + } + + return ret; +} + +static void get_windows_version(void) +{ + OSVERSIONINFOEXA vi, vi2; + const char *arch, *w = NULL; + unsigned major, minor; + ULONGLONG major_equal, minor_equal; + BOOL ws; + + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) + return; + } + + if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) { + // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + + major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (major = vi.dwMajorVersion; major <= 9; major++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMajorVersion = major; + if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal)) + continue; + + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; + vi.dwMinorVersion = 0; + } + + minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal)) + continue; + + vi.dwMinorVersion = minor; + break; + } + + break; + } + } + + if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { + ws = (vi.wProductType <= VER_NT_WORKSTATION); + windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion; + switch (windows_version) { + case 0x50: w = "2000"; break; + case 0x51: w = "XP"; break; + case 0x52: w = "2003"; break; + case 0x60: w = (ws ? "Vista" : "2008"); break; + case 0x61: w = (ws ? "7" : "2008_R2"); break; + case 0x62: w = (ws ? "8" : "2012"); break; + case 0x63: w = (ws ? "8.1" : "2012_R2"); break; + case 0x64: w = (ws ? "10" : "2015"); break; + default: + if (windows_version < 0x50) + windows_version = WINDOWS_UNSUPPORTED; + else + w = "11 or later"; + break; + } + } + } + + arch = is_x64() ? "64-bit" : "32-bit"; + + if (w == NULL) + snprintf(windows_version_str, sizeof(windows_version_str), "%s %u.%u %s", + (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"), + (unsigned int)vi.dwMajorVersion, (unsigned int)vi.dwMinorVersion, arch); + else if (vi.wServicePackMinor) + snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u.%u %s", + w, vi.wServicePackMajor, vi.wServicePackMinor, arch); + else if (vi.wServicePackMajor) + snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u %s", + w, vi.wServicePackMajor, arch); + else + snprintf(windows_version_str, sizeof(windows_version_str), "%s %s", + w, arch); +} + +/* + * init: libusb backend init function + * + * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list + * In our implementation, we equate Windows' "HCD" to libusb's "bus". Note that bus is zero indexed. + * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?) + */ +static int windows_init(struct libusb_context *ctx) +{ + int i, r = LIBUSB_ERROR_OTHER; + HANDLE semaphore; + char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' + + sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); + semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name); + if (semaphore == NULL) { + usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_MEM; + } + + // A successful wait brings our semaphore count to 0 (unsignaled) + // => any concurent wait stalls until the semaphore's release + if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { + usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0)); + CloseHandle(semaphore); + return LIBUSB_ERROR_NO_MEM; + } + + // NB: concurrent usage supposes that init calls are equally balanced with + // exit calls. If init is called more than exit, we will not exit properly + if (++concurrent_usage == 0) { // First init? + get_windows_version(); + usbi_dbg("Windows %s", windows_version_str); + + if (windows_version == WINDOWS_UNSUPPORTED) { + usbi_err(ctx, "This version of Windows is NOT supported"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + // We need a lock for proper auto-release + usbi_mutex_init(&autoclaim_lock); + + // Initialize pollable file descriptors + init_polling(); + + // Load DLL imports + if (init_dlls() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + goto init_exit; + } + + // Initialize the low level APIs (we don't care about errors at this stage) + for (i = 0; i < USB_API_MAX; i++) + usb_api_backend[i].init(SUB_API_NOTSET, ctx); + + r = windows_common_init(ctx); + if (r) + goto init_exit; + } + // At this stage, either we went through full init successfully, or didn't need to + r = LIBUSB_SUCCESS; + +init_exit: // Holds semaphore here. + if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + for (i = 0; i < USB_API_MAX; i++) + usb_api_backend[i].exit(SUB_API_NOTSET); + exit_dlls(); + exit_polling(); + windows_common_exit(); + usbi_mutex_destroy(&autoclaim_lock); + } + + if (r != LIBUSB_SUCCESS) + --concurrent_usage; // Not expected to call libusb_exit if we failed. + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); + return r; +} + +/* + * HCD (root) hubs need to have their device descriptor manually populated + * + * Note that, like Microsoft does in the device manager, we populate the + * Vendor and Device ID for HCD hubs with the ones from the PCI HCD device. + */ +static int force_hcd_device_descriptor(struct libusb_device *dev) +{ + struct windows_device_priv *parent_priv, *priv = _device_priv(dev); + struct libusb_context *ctx = DEVICE_CTX(dev); + int vid, pid; + + dev->num_configurations = 1; + priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); + priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; + priv->dev_descriptor.bNumConfigurations = 1; + priv->active_config = 1; + + if (dev->parent_dev == NULL) { + usbi_err(ctx, "program assertion failed - HCD hub has no parent"); + return LIBUSB_ERROR_NO_DEVICE; + } + + parent_priv = _device_priv(dev->parent_dev); + if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) { + priv->dev_descriptor.idVendor = (uint16_t)vid; + priv->dev_descriptor.idProduct = (uint16_t)pid; + } else { + usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path); + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + priv->dev_descriptor.idProduct = 1; + } + + return LIBUSB_SUCCESS; +} + +/* + * fetch and cache all the config descriptors through I/O + */ +static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle, char *device_id) +{ + DWORD size, ret_size; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct windows_device_priv *priv = _device_priv(dev); + int r; + uint8_t i; + + USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request + PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request + PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL; + + if (dev->num_configurations == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + priv->config_descriptor = calloc(dev->num_configurations, sizeof(unsigned char *)); + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NO_MEM; + + for (i = 0; i < dev->num_configurations; i++) + priv->config_descriptor[i] = NULL; + + for (i = 0, r = LIBUSB_SUCCESS; ; i++) { + // safe loop: release all dynamic resources + safe_free(cd_buf_actual); + + // safe loop: end of loop condition + if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS)) + break; + + size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT); + memset(&cd_buf_short, 0, size); + + cd_buf_short.req.ConnectionIndex = (ULONG)priv->port; + cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_short.req.SetupPacket.wIndex = 0; + cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + // Dummy call to get the required data size. Initial failures are reported as info rather + // than error as they can occur for non-penalizing situations, such as with some hubs. + // coverity[tainted_data_argument] + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, + &cd_buf_short, size, &ret_size, NULL)) { + usbi_info(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { + usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength; + cd_buf_actual = calloc(1, size); + if (cd_buf_actual == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + + // Actual call + cd_buf_actual->ConnectionIndex = (ULONG)priv->port; + cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_actual->SetupPacket.wIndex = 0; + cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, + cd_buf_actual, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor (actual) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR *)cd_buf_actual + sizeof(USB_DESCRIPTOR_REQUEST)); + + if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) { + usbi_err(ctx, "unexpected configuration descriptor size (actual) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { + usbi_err(ctx, "not a configuration descriptor for '%s'", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + usbi_dbg("cached config descriptor %d (bConfigurationValue=%u, %u bytes)", + i, cd_data->bConfigurationValue, cd_data->wTotalLength); + + // Cache the descriptor + priv->config_descriptor[i] = malloc(cd_data->wTotalLength); + if (priv->config_descriptor[i] == NULL) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength); + } + return LIBUSB_SUCCESS; +} + +/* + * Populate a libusb device structure + */ +static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev, + uint8_t port_number, char *device_id, DWORD devinst) +{ + HANDLE handle; + DWORD size; + USB_NODE_CONNECTION_INFORMATION_EX conn_info; + USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; + struct windows_device_priv *priv, *parent_priv; + struct libusb_context *ctx; + struct libusb_device *tmp_dev; + unsigned long tmp_id; + unsigned i; + + if ((dev == NULL) || (parent_dev == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + ctx = DEVICE_CTX(dev); + priv = _device_priv(dev); + parent_priv = _device_priv(parent_dev); + if (parent_priv->apib->id != USB_API_HUB) { + usbi_warn(ctx, "parent for device '%s' is not a hub", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + // It is possible for the parent hub not to have been initialized yet + // If that's the case, lookup the ancestors to set the bus number + if (parent_dev->bus_number == 0) { + for (i = 2; ; i++) { + tmp_id = get_ancestor_session_id(devinst, i); + if (tmp_id == 0) + break; + + tmp_dev = usbi_get_device_by_session_id(ctx, tmp_id); + if (tmp_dev == NULL) + continue; + + if (tmp_dev->bus_number != 0) { + usbi_dbg("got bus number from ancestor #%u", i); + parent_dev->bus_number = tmp_dev->bus_number; + libusb_unref_device(tmp_dev); + break; + } + + libusb_unref_device(tmp_dev); + } + } + + if (parent_dev->bus_number == 0) { + usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + dev->bus_number = parent_dev->bus_number; + priv->port = port_number; + dev->port_number = port_number; + priv->depth = parent_priv->depth + 1; + dev->parent_dev = parent_dev; + + // If the device address is already set, we can stop here + if (dev->device_address != 0) + return LIBUSB_SUCCESS; + + memset(&conn_info, 0, sizeof(conn_info)); + if (priv->depth != 0) { // Not a HCD hub + handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + + size = sizeof(conn_info); + conn_info.ConnectionIndex = (ULONG)port_number; + // coverity[tainted_data_argument] + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size, + &conn_info, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + device_id, windows_error_str(0)); + CloseHandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", device_id); + CloseHandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + + memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); + dev->num_configurations = priv->dev_descriptor.bNumConfigurations; + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg("found %u configurations (active conf: %u)", dev->num_configurations, priv->active_config); + + // If we can't read the config descriptors, just set the number of confs to zero + if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) { + dev->num_configurations = 0; + priv->dev_descriptor.bNumConfigurations = 0; + } + + // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8 + if (windows_version >= WINDOWS_8) { + memset(&conn_info_v2, 0, sizeof(conn_info_v2)); + size = sizeof(conn_info_v2); + conn_info_v2.ConnectionIndex = (ULONG)port_number; + conn_info_v2.Length = size; + conn_info_v2.SupportedUsbProtocols.Usb300 = 1; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, + &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s", + device_id, windows_error_str(0)); + } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) { + conn_info.Speed = 3; + } + } + + CloseHandle(handle); + + if (conn_info.DeviceAddress > UINT8_MAX) + usbi_err(ctx, "program assertion failed: device address overflow"); + + dev->device_address = (uint8_t)conn_info.DeviceAddress + 1; + if (dev->device_address == 1) + usbi_err(ctx, "program assertion failed: device address collision with root hub"); + + switch (conn_info.Speed) { + case 0: dev->speed = LIBUSB_SPEED_LOW; break; + case 1: dev->speed = LIBUSB_SPEED_FULL; break; + case 2: dev->speed = LIBUSB_SPEED_HIGH; break; + case 3: dev->speed = LIBUSB_SPEED_SUPER; break; + default: + usbi_warn(ctx, "Got unknown device speed %u", conn_info.Speed); + break; + } + } else { + dev->device_address = 1; // root hubs are set to use device number 1 + force_hcd_device_descriptor(dev); + } + + usbi_sanitize_device(dev); + + usbi_dbg("(bus: %u, addr: %u, depth: %u, port: %u): '%s'", + dev->bus_number, dev->device_address, priv->depth, priv->port, device_id); + + return LIBUSB_SUCCESS; +} + +// Returns the api type, or 0 if not found/unsupported +static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info, + SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + DWORD size, reg_type; + unsigned k, l; + int i, j; + + *api = USB_API_UNSUPPORTED; + *sub_api = SUB_API_NOTSET; + + // Check the service & filter names to know the API we should use + for (k = 0; k < 3; k++) { + if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop, + ®_type, (BYTE *)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) + // our buffers are MAX_KEY_LENGTH + 1 so we can overflow if needed + lookup[k].list[strlen(lookup[k].list) + 1] = 0; + + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsuported one does, it's not an issue + for (l = 0; (lookup[k].list[l] != 0) || (lookup[k].list[l + 1] != 0); l++) { + if (lookup[k].list[l] == 0) + lookup[k].list[l] = LIST_SEPARATOR; + } + usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); + } else { + if (GetLastError() != ERROR_INVALID_DATA) + usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); + lookup[k].list[0] = 0; + } + } + + for (i = 1; i < USB_API_MAX; i++) { + for (k = 0; k < 3; k++) { + j = get_sub_api(lookup[k].list, i); + if (j >= 0) { + usbi_dbg("matched %s name against %s", lookup[k].designation, + (i != USB_API_WINUSBX) ? usb_api_backend[i].designation : sub_api_name[j]); + *api = i; + *sub_api = j; + return; + } + } + } +} + +static int set_composite_interface(struct libusb_context *ctx, struct libusb_device *dev, + char *dev_interface_path, char *device_id, int api, int sub_api) +{ + unsigned i; + struct windows_device_priv *priv = _device_priv(dev); + int interface_number; + + if (priv->apib->id != USB_API_COMPOSITE) { + usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id); + return LIBUSB_ERROR_NO_DEVICE; + } + + // Because MI_## are not necessarily in sequential order (some composite + // devices will have only MI_00 & MI_03 for instance), we retrieve the actual + // interface number from the path's MI value + interface_number = 0; + for (i = 0; device_id[i] != 0; ) { + if ((device_id[i++] == 'M') && (device_id[i++] == 'I') + && (device_id[i++] == '_')) { + interface_number = (device_id[i++] - '0') * 10; + interface_number += device_id[i] - '0'; + break; + } + } + + if (device_id[i] == 0) + usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", + device_id, interface_number); + + if (priv->usb_interface[interface_number].path != NULL) { + if (api == USB_API_HID) { + // HID devices can have multiple collections (COL##) for each MI_## interface + usbi_dbg("interface[%d] already set - ignoring HID collection: %s", + interface_number, device_id); + return LIBUSB_ERROR_ACCESS; + } + // In other cases, just use the latest data + safe_free(priv->usb_interface[interface_number].path); + } + + usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path); + priv->usb_interface[interface_number].path = dev_interface_path; + priv->usb_interface[interface_number].apib = &usb_api_backend[api]; + priv->usb_interface[interface_number].sub_api = sub_api; + if ((api == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) + return LIBUSB_ERROR_NO_MEM; + } + + return LIBUSB_SUCCESS; +} + +static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *dev, + char *dev_interface_path) +{ + int i; + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed: parent is not HID"); + return LIBUSB_ERROR_NO_DEVICE; + } else if (priv->hid->nb_interfaces == USB_MAXINTERFACES) { + usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device"); + return LIBUSB_ERROR_NO_DEVICE; + } + + for (i = 0; i < priv->hid->nb_interfaces; i++) { + if ((priv->usb_interface[i].path != NULL) && strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) { + usbi_dbg("interface[%d] already set to %s", i, dev_interface_path); + return LIBUSB_ERROR_ACCESS; + } + } + + priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path; + priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID]; + usbi_dbg("interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path); + priv->hid->nb_interfaces++; + return LIBUSB_SUCCESS; +} + +/* + * get_device_list: libusb backend device enumeration function + */ +static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + struct discovered_devs *discdevs; + HDEVINFO dev_info = { 0 }; + const char *usb_class[] = {"USB", "NUSB3", "IUSB3", "IARUSB3"}; + SP_DEVINFO_DATA dev_info_data = { 0 }; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + GUID hid_guid; +#define MAX_ENUM_GUIDS 64 + const GUID *guid[MAX_ENUM_GUIDS]; +#define HCD_PASS 0 +#define HUB_PASS 1 +#define GEN_PASS 2 +#define DEV_PASS 3 +#define HID_PASS 4 + int r = LIBUSB_SUCCESS; + int api, sub_api; + size_t class_index = 0; + unsigned int nb_guids, pass, i, j, ancestor; + char path[MAX_PATH_LENGTH]; + char strbuf[MAX_PATH_LENGTH]; + struct libusb_device *dev, *parent_dev; + struct windows_device_priv *priv, *parent_priv; + char *dev_interface_path = NULL; + char *dev_id_path = NULL; + unsigned long session_id; + DWORD size, reg_type, port_nr, install_state; + HKEY key; + WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; + GUID *if_guid; + LONG s; + // Keep a list of newly allocated devs to unref + libusb_device **unref_list, **new_unref_list; + unsigned int unref_size = 64; + unsigned int unref_cur = 0; + + // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug) + // PASS 2 : (re)enumerate HUBS + // PASS 3 : (re)enumerate generic USB devices (including driverless) + // and list additional USB device interface GUIDs to explore + // PASS 4 : (re)enumerate master USB devices that have a device interface + // PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and + // set the device interfaces. + + // Init the GUID table + guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; + guid[GEN_PASS] = NULL; + guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; + HidD_GetHidGuid(&hid_guid); + guid[HID_PASS] = &hid_guid; + nb_guids = HID_PASS + 1; + + unref_list = calloc(unref_size, sizeof(libusb_device *)); + if (unref_list == NULL) + return LIBUSB_ERROR_NO_MEM; + + for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { +//#define ENUM_DEBUG +#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG) + const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" }; + usbi_dbg("#### PROCESSING %ss %s", passname[(pass <= HID_PASS) ? pass : (HID_PASS + 1)], + (pass != GEN_PASS) ? guid_to_string(guid[pass]) : ""); +#endif + for (i = 0; ; i++) { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_details); + safe_free(dev_interface_path); + safe_free(dev_id_path); + priv = parent_priv = NULL; + dev = parent_dev = NULL; + + // Safe loop: end of loop conditions + if (r != LIBUSB_SUCCESS) + break; + + if ((pass == HCD_PASS) && (i == UINT8_MAX)) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX); + break; + } + + if (pass != GEN_PASS) { + // Except for GEN, all passes deal with device interfaces + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i); + if (dev_interface_details == NULL) + break; + + dev_interface_path = sanitize_path(dev_interface_details->DevicePath); + if (dev_interface_path == NULL) { + usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath); + continue; + } + } else { + // Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are + // being listed under the "NUSB3" PnP Symbolic Name rather than "USB". + // The Intel USB 3.0 driver behaves similar, but uses "IUSB3" + // The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3" + for (; class_index < ARRAYSIZE(usb_class); class_index++) { + if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i)) + break; + i = 0; + } + if (class_index >= ARRAYSIZE(usb_class)) + break; + } + + // Read the Device ID path. This is what we'll use as UID + // Note that if the device is plugged in a different port or hub, the Device ID changes + if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not read the device id path for devinst %X, skipping", + (unsigned int)dev_info_data.DevInst); + continue; + } + + dev_id_path = sanitize_path(path); + if (dev_id_path == NULL) { + usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping", + (unsigned int)dev_info_data.DevInst); + continue; + } +#ifdef ENUM_DEBUG + usbi_dbg("PRO: %s", dev_id_path); +#endif + + // The SPDRP_ADDRESS for USB devices is the device port number on the hub + port_nr = 0; + if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) { + if ((!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS, + ®_type, (BYTE *)&port_nr, 4, &size)) || (size != 4)) { + usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s", + dev_id_path, windows_error_str(0)); + continue; + } + } + + // Set API to use or get additional data from generic pass + api = USB_API_UNSUPPORTED; + sub_api = SUB_API_NOTSET; + switch (pass) { + case HCD_PASS: + break; + case GEN_PASS: + // We use the GEN pass to detect driverless devices... + size = sizeof(strbuf); + if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER, + ®_type, (BYTE *)strbuf, size, &size)) { + usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path); + usbi_info(ctx, "libusb will not be able to access it."); + } + // ...and to add the additional device interface GUIDs + key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key != INVALID_HANDLE_VALUE) { + size = sizeof(guid_string_w); + s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, ®_type, + (BYTE *)guid_string_w, &size); + pRegCloseKey(key); + if (s == ERROR_SUCCESS) { + if (nb_guids >= MAX_ENUM_GUIDS) { + // If this assert is ever reported, grow a GUID table dynamically + usbi_err(ctx, "program assertion failed: too many GUIDs"); + LOOP_BREAK(LIBUSB_ERROR_OVERFLOW); + } + if_guid = calloc(1, sizeof(GUID)); + if (if_guid == NULL) { + usbi_err(ctx, "could not calloc for if_guid: not enough memory"); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + pCLSIDFromString(guid_string_w, if_guid); + guid[nb_guids++] = if_guid; + usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + } + } + break; + case HID_PASS: + api = USB_API_HID; + break; + default: + // Get the API type (after checking that the driver installation is OK) + if ((!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + ®_type, (BYTE *)&install_state, 4, &size)) || (size != 4)) { + usbi_warn(ctx, "could not detect installation state of driver for '%s': %s", + dev_id_path, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %u) - skipping", + dev_id_path, (unsigned int)install_state); + continue; + } + get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api); + break; + } + + // Find parent device (for the passes that need it) + switch (pass) { + case HCD_PASS: + case DEV_PASS: + case HUB_PASS: + break; + default: + // Go through the ancestors until we see a face we recognize + parent_dev = NULL; + for (ancestor = 1; parent_dev == NULL; ancestor++) { + session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor); + if (session_id == 0) + break; + + parent_dev = usbi_get_device_by_session_id(ctx, session_id); + } + + if (parent_dev == NULL) { + usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path); + continue; + } + + parent_priv = _device_priv(parent_dev); + // virtual USB devices are also listed during GEN - don't process these yet + if ((pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB)) { + libusb_unref_device(parent_dev); + continue; + } + + break; + } + + // Create new or match existing device, using the (hashed) device_id as session id + if (pass <= DEV_PASS) { // For subsequent passes, we'll lookup the parent + // These are the passes that create "new" devices + session_id = htab_hash(dev_id_path); + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + if (pass == DEV_PASS) { + // This can occur if the OS only reports a newly plugged device after we started enum + usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)" + " - ignoring", dev_id_path); + continue; + } + + usbi_dbg("allocating new device for session [%lX]", session_id); + dev = usbi_alloc_device(ctx, session_id); + if (dev == NULL) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + + priv = windows_device_priv_init(dev); + } else { + usbi_dbg("found existing device for session [%lX] (%u.%u)", + session_id, dev->bus_number, dev->device_address); + + priv = _device_priv(dev); + if ((parent_dev != NULL) && (dev->parent_dev != NULL)) { + if (dev->parent_dev != parent_dev) { + // It is possible for the actual parent device to not have existed at the + // time of enumeration, so the currently assigned parent may in fact be a + // grandparent. If the devices differ, we assume the "new" parent device + // is in fact closer to the device. + usbi_dbg("updating parent device [session %lX -> %lX]", + dev->parent_dev->session_data, parent_dev->session_data); + libusb_unref_device(dev->parent_dev); + dev->parent_dev = parent_dev; + } else { + // We hold a reference to parent_dev instance, but this device already + // has a parent_dev reference (only one per child) + libusb_unref_device(parent_dev); + } + } + } + + // Keep track of devices that need unref + unref_list[unref_cur++] = dev; + if (unref_cur >= unref_size) { + unref_size += 64; + new_unref_list = usbi_reallocf(unref_list, unref_size * sizeof(libusb_device *)); + if (new_unref_list == NULL) { + usbi_err(ctx, "could not realloc list for unref - aborting."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } else { + unref_list = new_unref_list; + } + } + } + + // Setup device + switch (pass) { + case HCD_PASS: + // If the hcd has already been setup, don't do it again + if (priv->path != NULL) + break; + dev->bus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected + dev->device_address = 0; + dev->num_configurations = 0; + priv->apib = &usb_api_backend[USB_API_HUB]; + priv->sub_api = SUB_API_NOTSET; + priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs + priv->path = dev_interface_path; + dev_interface_path = NULL; + break; + case HUB_PASS: + case DEV_PASS: + // If the device has already been setup, don't do it again + if (priv->path != NULL) + break; + // Take care of API initialization + priv->path = dev_interface_path; + dev_interface_path = NULL; + priv->apib = &usb_api_backend[api]; + priv->sub_api = sub_api; + switch(api) { + case USB_API_COMPOSITE: + case USB_API_HUB: + break; + case USB_API_HID: + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + + priv->hid->nb_interfaces = 0; + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = _strdup(priv->path); + if (priv->usb_interface[0].path == NULL) + usbi_warn(ctx, "could not duplicate interface path '%s'", priv->path); + // The following is needed if we want API calls to work for both simple + // and composite devices. + for (j = 0; j < USB_MAXINTERFACES; j++) + priv->usb_interface[j].apib = &usb_api_backend[api]; + + break; + } + break; + case GEN_PASS: + r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst); + if (r == LIBUSB_SUCCESS) { + // Append device to the list of discovered devices + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + + *_discdevs = discdevs; + } else if (r == LIBUSB_ERROR_NO_DEVICE) { + // This can occur if the device was disconnected but Windows hasn't + // refreshed its enumeration yet - in that case, we ignore the device + r = LIBUSB_SUCCESS; + } + break; + default: // HID_PASS and later + if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) { + if (parent_priv->apib->id == USB_API_HID) { + usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data); + r = set_hid_interface(ctx, parent_dev, dev_interface_path); + } else { + usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data); + r = set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api); + } + switch (r) { + case LIBUSB_SUCCESS: + dev_interface_path = NULL; + break; + case LIBUSB_ERROR_ACCESS: + // interface has already been set => make sure dev_interface_path is freed then + r = LIBUSB_SUCCESS; + break; + default: + LOOP_BREAK(r); + break; + } + } + libusb_unref_device(parent_dev); + break; + } + } + } + + // Free any additional GUIDs + for (pass = HID_PASS + 1; pass < nb_guids; pass++) + free((void *)guid[pass]); + + // Unref newly allocated devs + for (i = 0; i < unref_cur; i++) + libusb_unref_device(unref_list[i]); + free(unref_list); + + return r; +} + +/* + * exit: libusb backend deinitialization function + */ +static void windows_exit(void) +{ + int i; + HANDLE semaphore; + char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' + + sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); + semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name); + if (semaphore == NULL) + return; + + // A successful wait brings our semaphore count to 0 (unsignaled) + // => any concurent wait stalls until the semaphore release + if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { + CloseHandle(semaphore); + return; + } + + // Only works if exits and inits are balanced exactly + if (--concurrent_usage < 0) { // Last exit + for (i = 0; i < USB_API_MAX; i++) + usb_api_backend[i].exit(SUB_API_NOTSET); + exit_dlls(); + exit_polling(); + windows_common_exit(); + usbi_mutex_destroy(&autoclaim_lock); + } + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); +} + +static int windows_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + + memcpy(buffer, &priv->dev_descriptor, DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + // config index is zero based + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index]; + + size = MIN(config_header->wTotalLength, len); + memcpy(buffer, priv->config_descriptor[config_index], size); + *host_endian = 0; + + return (int)size; +} + +static int windows_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, + unsigned char **buffer, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + uint8_t index; + + *buffer = NULL; + *host_endian = 0; + + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NOT_FOUND; + + for (index = 0; index < dev->num_configurations; index++) { + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[index]; + if (config_header->bConfigurationValue == bConfigurationValue) { + *buffer = priv->config_descriptor[index]; + return (int)config_header->wTotalLength; + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +/* + * return the cached copy of the active config descriptor + */ +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + unsigned char *config_desc; + int r; + + if (priv->active_config == 0) + return LIBUSB_ERROR_NOT_FOUND; + + r = windows_get_config_descriptor_by_value(dev, priv->active_config, &config_desc, host_endian); + if (r < 0) + return r; + + len = MIN((size_t)r, len); + memcpy(buffer, config_desc, len); + return (int)len; +} + +static int windows_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + if (priv->apib == NULL) { + usbi_err(ctx, "program assertion failed - device is not initialized"); + return LIBUSB_ERROR_NO_DEVICE; + } + + return priv->apib->open(SUB_API_NOTSET, dev_handle); +} + +static void windows_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + priv->apib->close(SUB_API_NOTSET, dev_handle); +} + +static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + if (priv->active_config == 0) { + *config = 0; + return LIBUSB_ERROR_NOT_FOUND; + } + + *config = priv->active_config; + return LIBUSB_SUCCESS; +} + +/* + * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver + * does not currently expose a service that allows higher-level drivers to set + * the configuration." + */ +static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_SUCCESS; + + if (config >= USB_MAXCONFIG) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config, + 0, NULL, 0, 1000); + + if (r == LIBUSB_SUCCESS) + priv->active_config = (uint8_t)config; + + return r; +} + +static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints = 0; + + r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface); + + if (r == LIBUSB_SUCCESS) + r = windows_assign_endpoints(dev_handle, iface, 0); + + return r; +} + +static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints = 0; + + r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting); + + if (r == LIBUSB_SUCCESS) + r = windows_assign_endpoints(dev_handle, iface, altsetting); + + return r; +} + +static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface); +} + +static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint); +} + +static int windows_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->reset_device(SUB_API_NOTSET, dev_handle); +} + +// The 3 functions below are unlikely to ever get supported on Windows +static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void windows_destroy_device(struct libusb_device *dev) +{ + windows_device_priv_release(dev); +} + +void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + + usbi_free_fd(&transfer_priv->pollable_fd); + safe_free(transfer_priv->hid_buffer); + // When auto claim is in use, attempt to release the auto-claimed interface + auto_release(itransfer); +} + +static int submit_bulk_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) + return r; + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + return LIBUSB_SUCCESS; +} + +static int submit_iso_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) + return r; + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + return LIBUSB_SUCCESS; +} + +static int submit_control_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) + return r; + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + + return LIBUSB_SUCCESS; +} + +static int windows_submit_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return submit_control_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)) + return LIBUSB_ERROR_NOT_SUPPORTED; + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return submit_iso_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static int windows_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_control(SUB_API_NOTSET, itransfer); +} + +static int windows_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer); +} + +static int windows_cancel_transfer(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + + switch (transfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return windows_abort_control(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return windows_abort_transfers(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); +} + +struct winfd *windows_get_fd(struct usbi_transfer *transfer) +{ + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(transfer); + return &transfer_priv->pollable_fd; +} + +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size) +{ + if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) { + *io_result = NO_ERROR; + *io_size = (DWORD)pollable_fd->overlapped->InternalHigh; + } else if (GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { + // Regular async overlapped + *io_result = NO_ERROR; + } else { + *io_result = GetLastError(); + } +} + +// NB: MSVC6 does not support named initializers. +const struct usbi_os_backend windows_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + windows_init, + windows_exit, + + windows_get_device_list, + NULL, /* hotplug_poll */ + windows_open, + windows_close, + + windows_get_device_descriptor, + windows_get_active_config_descriptor, + windows_get_config_descriptor, + windows_get_config_descriptor_by_value, + + windows_get_configuration, + windows_set_configuration, + windows_claim_interface, + windows_release_interface, + + windows_set_interface_altsetting, + windows_clear_halt, + windows_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + NULL, /* dev_mem_alloc */ + NULL, /* dev_mem_free */ + + windows_kernel_driver_active, + windows_detach_kernel_driver, + windows_attach_kernel_driver, + + windows_destroy_device, + + windows_submit_transfer, + windows_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + NULL, + + windows_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct windows_device_priv), + sizeof(struct windows_device_handle_priv), + sizeof(struct windows_transfer_priv), +}; + + +/* + * USB API backends + */ +static int unsupported_init(int sub_api, struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int unsupported_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + PRINT_UNSUPPORTED_API(open); +} + +static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + usbi_dbg("unsupported API call for 'close'"); +} + +static int unsupported_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + PRINT_UNSUPPORTED_API(configure_endpoints); +} + +static int unsupported_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + PRINT_UNSUPPORTED_API(claim_interface); +} + +static int unsupported_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + PRINT_UNSUPPORTED_API(set_interface_altsetting); +} + +static int unsupported_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + PRINT_UNSUPPORTED_API(release_interface); +} + +static int unsupported_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + PRINT_UNSUPPORTED_API(clear_halt); +} + +static int unsupported_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + PRINT_UNSUPPORTED_API(reset_device); +} + +static int unsupported_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + PRINT_UNSUPPORTED_API(submit_bulk_transfer); +} + +static int unsupported_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + PRINT_UNSUPPORTED_API(submit_iso_transfer); +} + +static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + PRINT_UNSUPPORTED_API(submit_control_transfer); +} + +static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + PRINT_UNSUPPORTED_API(abort_control); +} + +static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + PRINT_UNSUPPORTED_API(abort_transfers); +} + +static int unsupported_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + PRINT_UNSUPPORTED_API(copy_transfer_data); +} + +static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_SUCCESS; +} + +// These names must be uppercase +static const char *hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30", "VHHUB", "AUSB3HUB"}; +static const char *composite_driver_names[] = {"USBCCGP"}; +static const char *winusbx_driver_names[] = WINUSBX_DRV_NAMES; +static const char *hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + USB_API_UNSUPPORTED, + "Unsupported API", + NULL, + 0, + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, + { + USB_API_HUB, + "HUB API", + hub_driver_names, + ARRAYSIZE(hub_driver_names), + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, + { + USB_API_COMPOSITE, + "Composite API", + composite_driver_names, + ARRAYSIZE(composite_driver_names), + composite_init, + composite_exit, + composite_open, + composite_close, + common_configure_endpoints, + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_abort_control, + composite_abort_transfers, + composite_copy_transfer_data, + }, + { + USB_API_WINUSBX, + "WinUSB-like APIs", + winusbx_driver_names, + ARRAYSIZE(winusbx_driver_names), + winusbx_init, + winusbx_exit, + winusbx_open, + winusbx_close, + winusbx_configure_endpoints, + winusbx_claim_interface, + winusbx_set_interface_altsetting, + winusbx_release_interface, + winusbx_clear_halt, + winusbx_reset_device, + winusbx_submit_bulk_transfer, + unsupported_submit_iso_transfer, + winusbx_submit_control_transfer, + winusbx_abort_control, + winusbx_abort_transfers, + winusbx_copy_transfer_data, + }, + { + USB_API_HID, + "HID API", + hid_driver_names, + ARRAYSIZE(hid_driver_names), + hid_init, + hid_exit, + hid_open, + hid_close, + common_configure_endpoints, + hid_claim_interface, + hid_set_interface_altsetting, + hid_release_interface, + hid_clear_halt, + hid_reset_device, + hid_submit_bulk_transfer, + unsupported_submit_iso_transfer, + hid_submit_control_transfer, + hid_abort_transfers, + hid_abort_transfers, + hid_copy_transfer_data, + }, +}; + + +/* + * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions + */ +#define WinUSBX_Set(fn) \ + do { \ + if (native_winusb) \ + WinUSBX[i].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn); \ + else \ + pLibK_GetProcAddress((PVOID *)&WinUSBX[i].fn, i, KUSB_FNID_##fn); \ + } while (0) + +static int winusbx_init(int sub_api, struct libusb_context *ctx) +{ + HMODULE h; + bool native_winusb; + int i; + KLIB_VERSION LibK_Version; + LibK_GetProcAddress_t pLibK_GetProcAddress = NULL; + LibK_GetVersion_t pLibK_GetVersion; + + h = LoadLibraryA("libusbK"); + + if (h == NULL) { + usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB"); + h = LoadLibraryA("WinUSB"); + + if (h == NULL) { + usbi_warn(ctx, "WinUSB DLL is not available either, " + "you will not be able to access devices outside of enumeration"); + return LIBUSB_ERROR_NOT_FOUND; + } + } else { + usbi_dbg("using libusbK DLL for universal access"); + pLibK_GetVersion = (LibK_GetVersion_t)GetProcAddress(h, "LibK_GetVersion"); + if (pLibK_GetVersion != NULL) { + pLibK_GetVersion(&LibK_Version); + usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor, + LibK_Version.Micro, LibK_Version.Nano); + } + pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(h, "LibK_GetProcAddress"); + if (pLibK_GetProcAddress == NULL) { + usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL"); + FreeLibrary(h); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + native_winusb = (pLibK_GetProcAddress == NULL); + for (i = SUB_API_LIBUSBK; i < SUB_API_MAX; i++) { + WinUSBX_Set(AbortPipe); + WinUSBX_Set(ControlTransfer); + WinUSBX_Set(FlushPipe); + WinUSBX_Set(Free); + WinUSBX_Set(GetAssociatedInterface); + WinUSBX_Set(GetCurrentAlternateSetting); + WinUSBX_Set(GetDescriptor); + WinUSBX_Set(GetOverlappedResult); + WinUSBX_Set(GetPipePolicy); + WinUSBX_Set(GetPowerPolicy); + WinUSBX_Set(Initialize); + WinUSBX_Set(QueryDeviceInformation); + WinUSBX_Set(QueryInterfaceSettings); + WinUSBX_Set(QueryPipe); + WinUSBX_Set(ReadPipe); + WinUSBX_Set(ResetPipe); + WinUSBX_Set(SetCurrentAlternateSetting); + WinUSBX_Set(SetPipePolicy); + WinUSBX_Set(SetPowerPolicy); + WinUSBX_Set(WritePipe); + if (!native_winusb) + WinUSBX_Set(ResetDevice); + + if (WinUSBX[i].Initialize != NULL) { + WinUSBX[i].initialized = true; + usbi_dbg("initalized sub API %s", sub_api_name[i]); + } else { + usbi_warn(ctx, "Failed to initalize sub API %s", sub_api_name[i]); + WinUSBX[i].initialized = false; + } + } + + WinUSBX_handle = h; + return LIBUSB_SUCCESS; +} + +static int winusbx_exit(int sub_api) +{ + if (WinUSBX_handle != NULL) { + FreeLibrary(WinUSBX_handle); + WinUSBX_handle = NULL; + + /* Reset the WinUSBX API structures */ + memset(&WinUSBX, 0, sizeof(WinUSBX)); + } + + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HANDLE file_handle; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // WinUSB requires a separate handle for each interface + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSBX)) { + file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].dev_handle = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE handle; + int i; + + if (sub_api == SUB_API_NOTSET) + sub_api = priv->sub_api; + + if (!WinUSBX[sub_api].initialized) + return; + + if (priv->apib->id == USB_API_COMPOSITE) { + // If this is a composite device, just free and close all WinUSB-like + // interfaces directly (each is independent and not associated with another) + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) { + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[i].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } + } + } else { + // If this is a WinUSB device, free all interfaces above interface 0, + // then free and close interface 0 last + for (i = 1; i < USB_MAXINTERFACES; i++) { + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + } + handle = handle_priv->interface_handle[0].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[0].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } +} + +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle; + UCHAR policy; + ULONG timeout = 0; + uint8_t endpoint_address; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // With handle and enpoints set (in parent), we can setup the default pipe properties + // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx + for (i = -1; i < priv->usb_interface[iface].nb_endpoints; i++) { + endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i]; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) + usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address); + + if ((i == -1) || (sub_api == SUB_API_LIBUSB0)) + continue; // Other policies don't apply to control endpoint or libusb0 + + policy = false; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) + usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address); + + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) + usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address); + + policy = true; + /* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See: + https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */ + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) + usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); + + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) + usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address); + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE); + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + HDEVINFO dev_info = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA dev_info_data; + char *dev_path_no_guid = NULL; + char filter_path[] = "\\\\.\\libusb0-0000"; + bool found_filter = false; + HANDLE file_handle, winusb_handle; + DWORD err; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // If the device is composite, but using the default Windows composite parent driver (usbccgp) + // or if it's the first WinUSB-like interface, we get a handle through Initialize(). + if ((is_using_usbccgp) || (iface == 0)) { + // composite device (independent interfaces) or interface 0 + file_handle = handle_priv->interface_handle[iface].dev_handle; + if (!HANDLE_VALID(file_handle)) + return LIBUSB_ERROR_NOT_FOUND; + + if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + err = GetLastError(); + switch(err) { + case ERROR_BAD_COMMAND: + // The device was disconnected + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + default: + // it may be that we're using the libusb0 filter driver. + // TODO: can we move this whole business into the K/0 DLL? + for (i = 0; ; i++) { + safe_free(dev_interface_details); + safe_free(dev_path_no_guid); + + dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path); + if ((found_filter) || (dev_interface_details == NULL)) + break; + + // ignore GUID part + dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{")); + if (dev_path_no_guid == NULL) + continue; + + if (strncmp(dev_path_no_guid, priv->usb_interface[iface].path, strlen(dev_path_no_guid)) == 0) { + file_handle = CreateFileA(filter_path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle != INVALID_HANDLE_VALUE) { + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + // Replace the existing file handle with the working one + CloseHandle(handle_priv->interface_handle[iface].dev_handle); + handle_priv->interface_handle[iface].dev_handle = file_handle; + found_filter = true; + } else { + usbi_err(ctx, "could not initialize filter driver for %s", filter_path); + CloseHandle(file_handle); + } + } else { + usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0)); + } + } + } + free(dev_interface_details); + if (!found_filter) { + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err)); + return LIBUSB_ERROR_ACCESS; + } + } + } + handle_priv->interface_handle[iface].api_handle = winusb_handle; + } else { + // For all other interfaces, use GetAssociatedInterface() + winusb_handle = handle_priv->interface_handle[0].api_handle; + // It is a requirement for multiple interface devices on Windows that, to you + // must first claim the first interface before you claim the others + if (!HANDLE_VALID(winusb_handle)) { + file_handle = handle_priv->interface_handle[0].dev_handle; + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[0].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface); + } else { + usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB): %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1), + &handle_priv->interface_handle[iface].api_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + switch(GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + } + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if (!HANDLE_VALID(winusb_handle)) + return LIBUSB_ERROR_NOT_FOUND; + + WinUSBX[sub_api].Free(winusb_handle); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +/* + * Return the first valid interface (of the same API type), for control transfers + */ +static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int i; + + if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) { + usbi_dbg("unsupported API ID"); + return -1; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (HANDLE_VALID(handle_priv->interface_handle[i].dev_handle) + && HANDLE_VALID(handle_priv->interface_handle[i].api_handle) + && (priv->usb_interface[i].apib->id == api_id)) + return i; + } + + return -1; +} + +/* + * Lookup interface by endpoint address. -1 if not found + */ +static int interface_by_endpoint(struct windows_device_priv *priv, + struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address) +{ + int i, j; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (!HANDLE_VALID(handle_priv->interface_handle[i].api_handle)) + continue; + if (priv->usb_interface[i].endpoint == NULL) + continue; + for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) { + if (priv->usb_interface[i].endpoint[j] == endpoint_address) + return i; + } + } + + return -1; +} + +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer; + ULONG size; + HANDLE winusb_handle; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + // Windows places upper limits on the control transfer size + // See: https://msdn.microsoft.com/en-us/library/windows/hardware/ff538112.aspx + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS) + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("will use interface %d", current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) + return LIBUSB_ERROR_NO_MEM; + + // Sending of set configuration control requests from WinUSB creates issues + if (((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD) + && (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION)) { + if (setup->value != priv->active_config) { + usbi_warn(ctx, "cannot set configuration other than the default one"); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_INVALID_PARAM; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = 0; + } else { + if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)size; + } + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + if (altsetting > 255) + return LIBUSB_ERROR_INVALID_PARAM; + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if (!HANDLE_VALID(winusb_handle)) { + usbi_err(ctx, "interface must be claimed first"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) { + usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + bool ret; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) + return LIBUSB_ERROR_NO_MEM; + + if (IS_XFERIN(transfer)) { + usbi_dbg("reading %d bytes", transfer->length); + ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } else { + usbi_dbg("writing %d bytes", transfer->length); + ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } + + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)transfer->length; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed + * through testing as well): + * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel + * the control transfer using CancelIo" + */ +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + // Cancelling of the I/O is done in the parent + return LIBUSB_SUCCESS; +} + +static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = transfer_priv->interface_number; + if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { + usbi_err(ctx, "program assertion failed: invalid interface_number"); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_dbg("will use interface %d", current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper + * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx): + * "WinUSB does not support host-initiated reset port and cycle port operations" and + * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the + * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is + * cycle the pipes (and even then, the control pipe can not be reset using WinUSB) + */ +// TODO: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?) +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct winfd wfd; + HANDLE winusb_handle; + int i, j; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // Reset any available pipe (except control) + for (i = 0; i < USB_MAXINTERFACES; i++) { + winusb_handle = handle_priv->interface_handle[i].api_handle; + for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0; ) { + // Cancel any pollable I/O + usbi_remove_pollfd(ctx, wfd.fd); + usbi_free_fd(&wfd); + wfd = handle_to_winfd(winusb_handle); + } + + if (HANDLE_VALID(winusb_handle)) { + for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) { + usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]); + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) + usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + + // FlushPipe seems to fail on OUT pipes + if (IS_EPIN(priv->usb_interface[i].endpoint[j]) + && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))) + usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) + usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + } + } + + // libusbK & libusb0 have the ability to issue an actual device reset + if (WinUSBX[sub_api].ResetDevice != NULL) { + winusb_handle = handle_priv->interface_handle[0].api_handle; + if (HANDLE_VALID(winusb_handle)) + WinUSBX[sub_api].ResetDevice(winusb_handle); + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv *dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv *dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + + while (str[i] && (str[i] != 0x409)) + i++; + + return i; +} + +static int _hid_get_device_descriptor(struct hid_device_priv *dev, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = 0x0200; /* 2.00 */ + d.bDeviceClass = 0; + d.bDeviceSubClass = 0; + d.bDeviceProtocol = 0; + d.bMaxPacketSize0 = 64; /* fix this! */ + d.idVendor = (uint16_t)dev->vid; + d.idProduct = (uint16_t)dev->pid; + d.bcdDevice = 0x0100; + d.iManufacturer = dev->string_index[0]; + d.iProduct = dev->string_index[1]; + d.iSerialNumber = dev->string_index[2]; + d.bNumConfigurations = 1; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv *dev, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (dev->input_report_size) + num_endpoints++; + if (dev->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t)config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(dev, hd, &tmp_size); + + if (dev->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->input_report_size - 1; + ed->bInterval = 10; + ed = (struct libusb_endpoint_descriptor *)((char *)ed + LIBUSB_DT_ENDPOINT_SIZE); + } + + if (dev->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv *dev, int _index, + void *data, size_t *size) +{ + void *tmp = NULL; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = {0x09, 0x04}; + + if ((*size < 2) || (*size > 255)) + return LIBUSB_ERROR_OVERFLOW; + + if (_index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid) + 2; + } else { + for (i = 0; i < 3; i++) { + if (_index == (dev->string_index[i])) { + tmp = dev->string[i]; + tmp_size = (_hid_wcslen(dev->string[i]) + 1) * sizeof(WCHAR); + break; + } + } + + if (i == 3) // not found + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (!tmp_size) + return LIBUSB_ERROR_INVALID_PARAM; + + if (tmp_size < *size) + *size = tmp_size; + + // 2 byte header + ((uint8_t *)data)[0] = (uint8_t)*size; + ((uint8_t *)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t *)data + 2, tmp, *size - 2); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv *dev, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(dev, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv *dev, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page (0xFFA0 == vendor defined) */ + d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF; + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (dev->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (dev->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (dev->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct hid_device_priv *dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) +{ + switch(type) { + case LIBUSB_DT_DEVICE: + usbi_dbg("LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, data, size); + case LIBUSB_DT_CONFIG: + usbi_dbg("LIBUSB_DT_CONFIG"); + if (!_index) + return _hid_get_config_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_STRING: + usbi_dbg("LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(dev, _index, data, size); + case LIBUSB_DT_HID: + usbi_dbg("LIBUSB_DT_HID"); + if (!_index) + return _hid_get_hid_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_REPORT: + usbi_dbg("LIBUSB_DT_REPORT"); + if (!_index) + return _hid_get_report_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_PHYSICAL: + usbi_dbg("LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + + usbi_dbg("unsupported"); + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int _hid_get_report(struct hid_device_priv *dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED *overlapped, int report_type) +{ + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; + + if (tp->hid_buffer != NULL) + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%u)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // Add a trailing byte to detect overflows + buf = calloc(1, expected_size + 1); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + buf[0] = (uint8_t)id; // Must be set always + usbi_dbg("report ID: 0x%02X", buf[0]); + + tp->hid_expected_size = expected_size; + read_size = expected_size; + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size + 1, + buf, expected_size + 1, &read_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); + free(buf); + return LIBUSB_ERROR_IO; + } + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously => copy and discard extra buffer + if (read_size == 0) { + usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read"); + *size = 0; + } else { + if (buf[0] != id) + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + + if ((size_t)read_size > expected_size) { + r = LIBUSB_ERROR_OVERFLOW; + usbi_dbg("OVERFLOW!"); + } else { + r = LIBUSB_COMPLETED; + } + + *size = MIN((size_t)read_size, *size); + if (id == 0) + memcpy(data, buf + 1, *size); // Discard report ID + else + memcpy(data, buf, *size); + } + + free(buf); + return r; +} + +static int _hid_set_report(struct hid_device_priv *dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED *overlapped, int report_type) +{ + uint8_t *buf = NULL; + DWORD ioctl_code, write_size = (DWORD)*size; + + if (tp->hid_buffer != NULL) + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%u)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg("report ID: 0x%02X", id); + // When report IDs are not used (i.e. when id == 0), we must add + // a null report ID. Otherwise, we just use original data buffer + if (id == 0) + write_size++; + + buf = malloc(write_size); + if (buf == NULL) + return LIBUSB_ERROR_NO_MEM; + + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, *size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, *size); + if (buf[0] != id) + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, &write_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); + free(buf); + return LIBUSB_ERROR_IO; + } + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously + *size = write_size; + if (write_size == 0) + usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); + + free(buf); + return LIBUSB_COMPLETED; +} + +static int _hid_class_request(struct hid_device_priv *dev, HANDLE hid_handle, int request_type, + int request, int value, int _index, void *data, struct windows_transfer_priv *tp, + size_t *size, OVERLAPPED *overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + if ((LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE)) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + + +/* + * HID API functions + */ +static int hid_init(int sub_api, struct libusb_context *ctx) +{ + DLL_GET_HANDLE(hid); + DLL_LOAD_FUNC(hid, HidD_GetAttributes, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetHidGuid, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetPreparsedData, TRUE); + DLL_LOAD_FUNC(hid, HidD_FreePreparsedData, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetManufacturerString, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetProductString, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetSerialNumberString, TRUE); + DLL_LOAD_FUNC(hid, HidP_GetCaps, TRUE); + DLL_LOAD_FUNC(hid, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD_FUNC(hid, HidD_SetFeature, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetFeature, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD_FUNC(hid, HidD_GetInputReport, FALSE); + DLL_LOAD_FUNC(hid, HidD_SetOutputReport, FALSE); + DLL_LOAD_FUNC(hid, HidD_FlushQueue, TRUE); + DLL_LOAD_FUNC(hid, HidP_GetValueCaps, TRUE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(int sub_api) +{ + DLL_FREE_HANDLE(hid); + + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HIDD_ATTRIBUTES hid_attributes; + PHIDP_PREPARSED_DATA preparsed_data = NULL; + HIDP_CAPS capabilities; + HIDP_VALUE_CAPS *value_caps; + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + int nb_ids[2]; // zero and nonzero report IDs +#if defined(ENABLE_LOGGING) + const char *type[3] = {"input", "output", "feature"}; +#endif + + CHECK_HID_AVAILABLE; + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - private HID structure is unitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID)) { + hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i = 32; HidD_SetNumInputBuffers(hid_handle, i); i *= 2); + usbi_dbg("set maximum input buffer size to %d", i / 2); + + // Get the maximum input and output report size + if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) { + usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j = HidP_Input; j <= HidP_Feature; j++) { + usbi_dbg("%u HID %s report value(s) found", (unsigned int)size[j], type[j]); + priv->hid->uses_report_ids[j] = false; + if (size[j] > 0) { + value_caps = calloc(size[j], sizeof(HIDP_VALUE_CAPS)); + if ((value_caps != NULL) + && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1)) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i = 0; i < (int)size[j]; i++) { + usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) + nb_ids[1]++; + else + nb_ids[0]++; + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) + usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s", + type[j]); + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(ctx, " could not process %s report IDs", type[j]); + } + free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Fetch string descriptors + priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], sizeof(priv->hid->string[0])); + else + priv->hid->string[0][0] = 0; + + priv->hid->string_index[1] = priv->dev_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) + HidD_GetProductString(hid_handle, priv->hid->string[1], sizeof(priv->hid->string[1])); + else + priv->hid->string[1][0] = 0; + + priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], sizeof(priv->hid->string[2])); + else + priv->hid->string[2][0] = 0; + } while(0); + + if (preparsed_data) + HidD_FreePreparsedData(preparsed_data); + + return LIBUSB_SUCCESS; +} + +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(file_handle)) + CloseHandle(file_handle); + } + } +} + +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) + return LIBUSB_ERROR_BUSY; // already claimed + + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (altsetting > 255) + return LIBUSB_ERROR_INVALID_PARAM; + + if (altsetting != 0) { + usbi_err(ctx, "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer; + HANDLE hid_handle; + struct winfd wfd; + int current_interface, config; + size_t size; + int r = LIBUSB_ERROR_INVALID_PARAM; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("will use interface %d", current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + // Always use the handle returned from usbi_create_fd (wfd.handle) + wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL); + if (wfd.fd < 0) + return LIBUSB_ERROR_NOT_FOUND; + + switch(LIBUSB_REQ_TYPE(setup->request_type)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch(setup->request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type), + (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = windows_get_configuration(transfer->dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(ctx, "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t *)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value); + if (r == LIBUSB_SUCCESS) + r = LIBUSB_COMPLETED; + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r = _hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value, + setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + &size, wfd.overlapped); + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + break; + } + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx + // set InternalHigh to the number of bytes transferred + wfd.overlapped->InternalHigh = (DWORD)size; + r = LIBUSB_SUCCESS; + } + + if (r == LIBUSB_SUCCESS) { + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + } else { + usbi_free_fd(&wfd); + } + + return r; +} + +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct winfd wfd; + HANDLE hid_handle; + bool direction_in, ret; + int current_interface, length; + DWORD size; + int r = LIBUSB_SUCCESS; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + transfer_priv->hid_dest = NULL; + safe_free(transfer_priv->hid_buffer); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) + return LIBUSB_ERROR_NO_MEM; + + // If report IDs are not in use, an extra prefix byte must be added + if (((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1]))) + length = transfer->length + 1; + else + length = transfer->length; + + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = calloc(1, length + 1); + if (transfer_priv->hid_buffer == NULL) + return LIBUSB_ERROR_NO_MEM; + + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg("reading %d bytes (report ID: 0x00)", length); + ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length + 1, &size, wfd.overlapped); + } else { + if (!priv->hid->uses_report_ids[1]) + memcpy(transfer_priv->hid_buffer + 1, transfer->buffer, transfer->length); + else + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + + usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped); + } + + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + } else { + // Only write operations that completed synchronously need to free up + // hid_buffer. For reads, copy_transfer_data() handles that process. + if (!direction_in) + safe_free(transfer_priv->hid_buffer); + + if (size == 0) { + usbi_err(ctx, "program assertion failed - no data was transferred"); + size = 1; + } + if (size > (size_t)length) { + usbi_err(ctx, "OVERFLOW!"); + r = LIBUSB_ERROR_OVERFLOW; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = size; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return r; +} + +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = transfer_priv->interface_number; + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + CancelIo(hid_handle); + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if (HANDLE_VALID(hid_handle)) + HidD_FlushQueue(hid_handle); + } + + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + + // No endpoint selection with Microsoft's implementation, so we try to flush the + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + int r = LIBUSB_TRANSFER_COMPLETED; + uint32_t corrected_size = io_size; + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + if (corrected_size > 0) { + // First, check for overflow + if (corrected_size > transfer_priv->hid_expected_size) { + usbi_err(ctx, "OVERFLOW!"); + corrected_size = (uint32_t)transfer_priv->hid_expected_size; + r = LIBUSB_TRANSFER_OVERFLOW; + } + + if (transfer_priv->hid_buffer[0] == 0) { + // Discard the 1 byte report ID prefix + corrected_size--; + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer + 1, corrected_size); + } else { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size); + } + } + transfer_priv->hid_dest = NULL; + } + // For write, we just need to free the hid buffer + safe_free(transfer_priv->hid_buffer); + } + + itransfer->transferred += corrected_size; + return r; +} + + +/* + * Composite API functions + */ +static int composite_init(int sub_api, struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int composite_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_ERROR_NOT_FOUND; + uint8_t i; + // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX + 1] = { 0 }; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + switch (priv->usb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle); + if (r != LIBUSB_SUCCESS) + return r; + } + } + + if (available[SUB_API_MAX]) // HID driver + r = hid_open(SUB_API_NOTSET, dev_handle); + + return r; +} + +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + uint8_t i; + // SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX + 1] = { 0 }; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + switch (priv->usb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers + if (available[i]) + usb_api_backend[USB_API_WINUSBX].close(i, dev_handle); + } + + if (available[SUB_API_MAX]) // HID driver + hid_close(SUB_API_NOTSET, dev_handle); +} + +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->usb_interface[iface].apib-> + claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->usb_interface[iface].apib-> + set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting); +} + +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->usb_interface[iface].apib-> + release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct libusb_config_descriptor *conf_desc; + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer; + int iface, pass, r; + + // Interface shouldn't matter for control, but it does in practice, with Windows' + // restrictions with regards to accessing HID keyboards and mice. Try to target + // a specific interface first, if possible. + switch (LIBUSB_REQ_RECIPIENT(setup->request_type)) { + case LIBUSB_RECIPIENT_INTERFACE: + iface = setup->index & 0xFF; + break; + case LIBUSB_RECIPIENT_ENDPOINT: + r = libusb_get_active_config_descriptor(transfer->dev_handle->dev, &conf_desc); + if (r == LIBUSB_SUCCESS) { + iface = get_interface_by_endpoint(conf_desc, (setup->index & 0xFF)); + libusb_free_config_descriptor(conf_desc); + break; + } + // Fall through if not able to determine interface + default: + iface = -1; + break; + } + + // Try and target a specific interface if the control setup indicates such + if ((iface >= 0) && (iface < USB_MAXINTERFACES)) { + usbi_dbg("attempting control transfer targeted to interface %d", iface); + if (priv->usb_interface[iface].path != NULL) { + r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer); + if (r == LIBUSB_SUCCESS) + return r; + } + } + + // Either not targeted to a specific interface or no luck in doing so. + // Try a 2 pass approach with all interfaces. + for (pass = 0; pass < 2; pass++) { + for (iface = 0; iface < USB_MAXINTERFACES; iface++) { + if (priv->usb_interface[iface].path != NULL) { + if ((pass == 0) && (priv->usb_interface[iface].restricted_functionality)) { + usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", iface); + continue; + } + usbi_dbg("using interface %d", iface); + r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer); + // If not supported on this API, it may be supported on another, so don't give up yet!! + if (r == LIBUSB_ERROR_NOT_SUPPORTED) + continue; + return r; + } + } + } + + usbi_err(ctx, "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer); +} + +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer); +} + +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint); +} + +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer); +} + +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer); +} + +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r; + uint8_t i; + bool available[SUB_API_MAX]; + + for (i = 0; i < SUB_API_MAX; i++) + available[i] = false; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ((priv->usb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET)) + available[priv->usb_interface[i].sub_api] = true; + } + + for (i = 0; i < SUB_API_MAX; i++) { + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle); + if (r != LIBUSB_SUCCESS) + return r; + } + } + + return LIBUSB_SUCCESS; +} + +static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + copy_transfer_data(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size); +} + +#endif /* !USE_USBDK */ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.h new file mode 100644 index 0000000..b7b9cd9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.h @@ -0,0 +1,937 @@ +/* + * Windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "windows_common.h" +#include "windows_nt_common.h" + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4996) // deprecated API calls +#pragma warning(disable:28159) // more deprecated API calls +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +#define MAX_CTRL_BUFFER_LENGTH 4096 +#define MAX_USB_DEVICES 256 +#define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 +#define MAX_GUID_STRING_LENGTH 40 +#define MAX_PATH_LENGTH 128 +#define MAX_KEY_LENGTH 256 +#define LIST_SEPARATOR ';' + +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + +// http://msdn.microsoft.com/en-us/library/ff545978.aspx +// http://msdn.microsoft.com/en-us/library/ff545972.aspx +// http://msdn.microsoft.com/en-us/library/ff545982.aspx +#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) +const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_DEVICE) +const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_HUB) +const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; +#endif +#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) +const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; +#endif + + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_HUB 1 +#define USB_API_COMPOSITE 2 +#define USB_API_WINUSBX 3 +#define USB_API_HID 4 +#define USB_API_MAX 5 +// The following is used to indicate if the HID or composite extra props have already been set. +#define USB_API_SET (1 << USB_API_MAX) + +// Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL) +// Must have the same values as the KUSB_DRVID enum from libusbk.h +#define SUB_API_NOTSET -1 +#define SUB_API_LIBUSBK 0 +#define SUB_API_LIBUSB0 1 +#define SUB_API_WINUSB 2 +#define SUB_API_MAX 3 + +#define WINUSBX_DRV_NAMES {"libusbK", "libusb0", "WinUSB"} + +struct windows_usb_api_backend { + const uint8_t id; + const char *designation; + const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp" + const uint8_t nb_driver_names; + int (*init)(int sub_api, struct libusb_context *ctx); + int (*exit)(int sub_api); + int (*open)(int sub_api, struct libusb_device_handle *dev_handle); + void (*close)(int sub_api, struct libusb_device_handle *dev_handle); + int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*claim_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*set_interface_altsetting)(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); + int (*release_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*clear_halt)(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); + int (*reset_device)(int sub_api, struct libusb_device_handle *dev_handle); + int (*submit_bulk_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_iso_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_control_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*abort_control)(int sub_api, struct usbi_transfer *itransfer); + int (*abort_transfers)(int sub_api, struct usbi_transfer *itransfer); + int (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +}; + +extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; + +#define PRINT_UNSUPPORTED_API(fname) \ + usbi_dbg("unsupported API call for '" \ + #fname "' (unrecognized device driver)"); \ + return LIBUSB_ERROR_NOT_SUPPORTED; + +/* + * private structures definition + * with inline pseudo constructors/destructors + */ + +// TODO (v2+): move hid desc to libusb.h? +struct libusb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bClassDescriptorType; + uint16_t wClassDescriptorLength; +}; + +#define LIBUSB_DT_HID_SIZE 9 +#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \ + + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE) +#define HID_MAX_REPORT_SIZE 1024 +#define HID_IN_EP 0x81 +#define HID_OUT_EP 0x02 +#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F) +#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5)) +#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN) +#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) + +// The following are used for HID reports IOCTLs +#define HID_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS) +#define HID_BUFFER_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) +#define HID_IN_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define HID_OUT_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + +#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) +#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) +#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100) +#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101) + +enum libusb_hid_request_type { + HID_REQ_GET_REPORT = 0x01, + HID_REQ_GET_IDLE = 0x02, + HID_REQ_GET_PROTOCOL = 0x03, + HID_REQ_SET_REPORT = 0x09, + HID_REQ_SET_IDLE = 0x0A, + HID_REQ_SET_PROTOCOL = 0x0B +}; + +enum libusb_hid_report_type { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03 +}; + +struct hid_device_priv { + uint16_t vid; + uint16_t pid; + uint8_t config; + uint8_t nb_interfaces; + bool uses_report_ids[3]; // input, ouptput, feature + uint16_t input_report_size; + uint16_t output_report_size; + uint16_t feature_report_size; + WCHAR string[3][MAX_USB_STRING_LENGTH]; + uint8_t string_index[3]; // man, prod, ser +}; + +struct windows_device_priv { + uint8_t depth; // distance to HCD + uint8_t port; // port number on the hub + uint8_t active_config; + struct windows_usb_api_backend const *apib; + char *path; // device interface path + int sub_api; // for WinUSB-like APIs + struct { + char *path; // each interface needs a device interface path, + struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support), + int sub_api; + int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) + uint8_t *endpoint; + bool restricted_functionality; // indicates if the interface functionality is restricted + // by Windows (eg. HID keyboards or mice cannot do R/W) + } usb_interface[USB_MAXINTERFACES]; + struct hid_device_priv *hid; + USB_DEVICE_DESCRIPTOR dev_descriptor; + unsigned char **config_descriptor; // list of pointers to the cached config descriptors +}; + +static inline struct windows_device_priv *_device_priv(struct libusb_device *dev) +{ + return (struct windows_device_priv *)dev->os_priv; +} + +static inline struct windows_device_priv *windows_device_priv_init(struct libusb_device *dev) +{ + struct windows_device_priv *p = _device_priv(dev); + int i; + + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->sub_api = SUB_API_NOTSET; + for (i = 0; i < USB_MAXINTERFACES; i++) { + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].sub_api = SUB_API_NOTSET; + } + + return p; +} + +static inline void windows_device_priv_release(struct libusb_device *dev) +{ + struct windows_device_priv *p = _device_priv(dev); + int i; + + free(p->path); + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i = 0; i < dev->num_configurations; i++) + free(p->config_descriptor[i]); + } + free(p->config_descriptor); + free(p->hid); + for (i = 0; i < USB_MAXINTERFACES; i++) { + free(p->usb_interface[i].path); + free(p->usb_interface[i].endpoint); + } +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +}; + +static inline struct windows_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *)handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; + uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID + uint8_t *hid_dest; // transfer buffer destination, required for HID + size_t hid_expected_size; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retrieve list + const char* designation; // internal designation (for debug output) +}; + +/* OLE32 dependency */ +DLL_DECLARE_HANDLE(OLE32); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); + +/* Kernel32 dependencies */ +DLL_DECLARE_HANDLE(Kernel32); +/* This call is only available from XP SP2 */ +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL)); + +/* SetupAPI dependencies */ +DLL_DECLARE_HANDLE(SetupAPI); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD)); + +/* AdvAPI32 dependencies */ +DLL_DECLARE_HANDLE(AdvAPI32); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#endif +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access) \ + (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE_HANDLE(Cfgmgr32); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + +typedef struct USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef union _USB_PROTOCOLS { + ULONG ul; + struct { + ULONG Usb110:1; + ULONG Usb200:1; + ULONG Usb300:1; + ULONG ReservedMBZ:29; + }; +} USB_PROTOCOLS, *PUSB_PROTOCOLS; + +typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS { + ULONG ul; + struct { + ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1; + ULONG DeviceIsSuperSpeedCapableOrHigher:1; + ULONG ReservedMBZ:30; + }; +} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS; + +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { + ULONG ConnectionIndex; + ULONG Length; + USB_PROTOCOLS SupportedUsbProtocols; + USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; +} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; + +typedef struct USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable:1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +typedef BOOL (WINAPI *WinUsb_AbortPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + WINUSB_SETUP_PACKET SetupPacket, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_FlushPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_Free_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AssociatedInterfaceIndex, + PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + PUCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR DescriptorType, + UCHAR Index, + USHORT LanguageID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred +); +typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, + BOOL bWait +); +typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_Initialize_t)( + HANDLE DeviceHandle, + PWINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG InformationType, + PULONG BufferLength, + PVOID Buffer +); +typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSettingNumber, + PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor +); +typedef BOOL (WINAPI *WinUsb_QueryPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceNumber, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION PipeInformation +); +typedef BOOL (WINAPI *WinUsb_ReadPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_WritePipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetDevice_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); + +/* /!\ These must match the ones from the official libusbk.h */ +typedef enum _KUSB_FNID { + KUSB_FNID_Init, + KUSB_FNID_Free, + KUSB_FNID_ClaimInterface, + KUSB_FNID_ReleaseInterface, + KUSB_FNID_SetAltInterface, + KUSB_FNID_GetAltInterface, + KUSB_FNID_GetDescriptor, + KUSB_FNID_ControlTransfer, + KUSB_FNID_SetPowerPolicy, + KUSB_FNID_GetPowerPolicy, + KUSB_FNID_SetConfiguration, + KUSB_FNID_GetConfiguration, + KUSB_FNID_ResetDevice, + KUSB_FNID_Initialize, + KUSB_FNID_SelectInterface, + KUSB_FNID_GetAssociatedInterface, + KUSB_FNID_Clone, + KUSB_FNID_QueryInterfaceSettings, + KUSB_FNID_QueryDeviceInformation, + KUSB_FNID_SetCurrentAlternateSetting, + KUSB_FNID_GetCurrentAlternateSetting, + KUSB_FNID_QueryPipe, + KUSB_FNID_SetPipePolicy, + KUSB_FNID_GetPipePolicy, + KUSB_FNID_ReadPipe, + KUSB_FNID_WritePipe, + KUSB_FNID_ResetPipe, + KUSB_FNID_AbortPipe, + KUSB_FNID_FlushPipe, + KUSB_FNID_IsoReadPipe, + KUSB_FNID_IsoWritePipe, + KUSB_FNID_GetCurrentFrameNumber, + KUSB_FNID_GetOverlappedResult, + KUSB_FNID_GetProperty, + KUSB_FNID_COUNT, +} KUSB_FNID; + +typedef struct _KLIB_VERSION { + INT Major; + INT Minor; + INT Micro; + INT Nano; +} KLIB_VERSION; +typedef KLIB_VERSION* PKLIB_VERSION; + +typedef BOOL (WINAPI *LibK_GetProcAddress_t)( + PVOID *ProcAddress, + ULONG DriverID, + ULONG FunctionID +); + +typedef VOID (WINAPI *LibK_GetVersion_t)( + PKLIB_VERSION Version +); + +struct winusb_interface { + bool initialized; + WinUsb_AbortPipe_t AbortPipe; + WinUsb_ControlTransfer_t ControlTransfer; + WinUsb_FlushPipe_t FlushPipe; + WinUsb_Free_t Free; + WinUsb_GetAssociatedInterface_t GetAssociatedInterface; + WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting; + WinUsb_GetDescriptor_t GetDescriptor; + WinUsb_GetOverlappedResult_t GetOverlappedResult; + WinUsb_GetPipePolicy_t GetPipePolicy; + WinUsb_GetPowerPolicy_t GetPowerPolicy; + WinUsb_Initialize_t Initialize; + WinUsb_QueryDeviceInformation_t QueryDeviceInformation; + WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings; + WinUsb_QueryPipe_t QueryPipe; + WinUsb_ReadPipe_t ReadPipe; + WinUsb_ResetPipe_t ResetPipe; + WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; + WinUsb_SetPipePolicy_t SetPipePolicy; + WinUsb_SetPowerPolicy_t SetPowerPolicy; + WinUsb_WritePipe_t WritePipe; + WinUsb_ResetDevice_t ResetDevice; +}; + +/* hid.dll interface */ + +#define HIDP_STATUS_SUCCESS 0x110000 +typedef void * PHIDP_PREPARSED_DATA; + +#pragma pack(1) +typedef struct { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; +#pragma pack() + +typedef USHORT USAGE; +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + } u; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +DLL_DECLARE_HANDLE(hid); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE_FUNC(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); +DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); \ No newline at end of file diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/strerror.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/strerror.c new file mode 100644 index 0000000..d2be0e2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/strerror.c @@ -0,0 +1,202 @@ +/* + * libusb strerror code + * Copyright © 2013 Hans de Goede + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#if defined(HAVE_STRINGS_H) +#include +#endif + +#include "libusbi.h" + +#if defined(_MSC_VER) +#define strncasecmp _strnicmp +#endif + +static size_t usbi_locale = 0; + +/** \ingroup libusb_misc + * How to add a new \ref libusb_strerror() translation: + *
    + *
  1. Download the latest \c strerror.c from:
    + * https://raw.github.com/libusb/libusb/master/libusb/sterror.c
  2. + *
  3. Open the file in an UTF-8 capable editor
  4. + *
  5. Add the 2 letter ISO 639-1 + * code for your locale at the end of \c usbi_locale_supported[]
    + * Eg. for Chinese, you would add "zh" so that: + * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode + * becomes: + * \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode
  6. + *
  7. Copy the { / * English (en) * / ... } section and add it at the end of \c usbi_localized_errors
    + * Eg. for Chinese, the last section of \c usbi_localized_errors could look like: + * \code + * }, { / * Chinese (zh) * / + * "Success", + * ... + * "Other error", + * } + * };\endcode
  8. + *
  9. Translate each of the English messages from the section you copied into your language
  10. + *
  11. Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net
  12. + *
+ */ + +static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" }; +static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { + { /* English (en) */ + "Success", + "Input/Output Error", + "Invalid parameter", + "Access denied (insufficient permissions)", + "No such device (it may have been disconnected)", + "Entity not found", + "Resource busy", + "Operation timed out", + "Overflow", + "Pipe error", + "System call interrupted (perhaps due to signal)", + "Insufficient memory", + "Operation not supported or unimplemented on this platform", + "Other error", + }, { /* Dutch (nl) */ + "Gelukt", + "Invoer-/uitvoerfout", + "Ongeldig argument", + "Toegang geweigerd (onvoldoende toegangsrechten)", + "Apparaat bestaat niet (verbinding met apparaat verbroken?)", + "Niet gevonden", + "Apparaat of hulpbron is bezig", + "Bewerking verlopen", + "Waarde is te groot", + "Gebroken pijp", + "Onderbroken systeemaanroep", + "Onvoldoende geheugen beschikbaar", + "Bewerking wordt niet ondersteund", + "Andere fout", + }, { /* French (fr) */ + "Succès", + "Erreur d'entrée/sortie", + "Paramètre invalide", + "Accès refusé (permissions insuffisantes)", + "Périphérique introuvable (peut-être déconnecté)", + "Elément introuvable", + "Resource déjà occupée", + "Operation expirée", + "Débordement", + "Erreur de pipe", + "Appel système abandonné (peut-être à cause d’un signal)", + "Mémoire insuffisante", + "Opération non supportée or non implémentée sur cette plateforme", + "Autre erreur", + }, { /* Russian (ru) */ + "Успех", + "Ошибка ввода/вывода", + "Неверный параметр", + "Доступ запрещён (не хватает прав)", + "Устройство отсутствует (возможно, оно было отсоединено)", + "Элемент не найден", + "Ресурс занят", + "Истекло время ожидания операции", + "Переполнение", + "Ошибка канала", + "Системный вызов прерван (возможно, сигналом)", + "Память исчерпана", + "Операция не поддерживается данной платформой", + "Неизвестная ошибка" + } +}; + +/** \ingroup libusb_misc + * Set the language, and only the language, not the encoding! used for + * translatable libusb messages. + * + * This takes a locale string in the default setlocale format: lang[-region] + * or lang[_country_region][.codeset]. Only the lang part of the string is + * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". + * The optional region, country_region or codeset parts are ignored. This + * means that functions which return translatable strings will NOT honor the + * specified encoding. + * All strings returned are encoded as UTF-8 strings. + * + * If libusb_setlocale() is not called, all messages will be in English. + * + * The following functions return translatable strings: libusb_strerror(). + * Note that the libusb log messages controlled through libusb_set_debug() + * are not translated, they are always in English. + * + * For POSIX UTF-8 environments if you want libusb to follow the standard + * locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)), + * after your app has done its locale setup. + * + * \param locale locale-string in the form of lang[_country_region][.codeset] + * or lang[-region], where lang is a 2 letter ISO 639-1 code + * \returns LIBUSB_SUCCESS on success + * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns a LIBUSB_ERROR code on other errors + */ + +int API_EXPORTED libusb_setlocale(const char *locale) +{ + size_t i; + + if ( (locale == NULL) || (strlen(locale) < 2) + || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) ) + return LIBUSB_ERROR_INVALID_PARAM; + + for (i=0; i= ARRAYSIZE(usbi_locale_supported)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_locale = i; + + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_misc + * Returns a constant string with a short description of the given error code, + * this description is intended for displaying to the end user and will be in + * the language set by libusb_setlocale(). + * + * The returned string is encoded in UTF-8. + * + * The messages always start with a capital letter and end without any dot. + * The caller must not free() the returned string. + * + * \param errcode the error code whose description is desired + * \returns a short description of the error code in UTF-8 encoding + */ +DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode) +{ + int errcode_index = -errcode; + + if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) { + /* "Other Error", which should always be our last message, is returned */ + errcode_index = LIBUSB_ERROR_COUNT - 1; + } + + return usbi_localized_errors[usbi_locale][errcode_index]; +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/sync.c b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/sync.c new file mode 100644 index 0000000..a609f65 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/sync.c @@ -0,0 +1,327 @@ +/* + * Synchronous I/O functions for libusb + * Copyright © 2007-2008 Daniel Drake + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#include "libusbi.h" + +/** + * @defgroup libusb_syncio Synchronous device I/O + * + * This page documents libusb's synchronous (blocking) API for USB device I/O. + * This interface is easy to use but has some limitations. More advanced users + * may wish to consider using the \ref libusb_asyncio "asynchronous I/O API" instead. + */ + +static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; + usbi_dbg("actual_length=%d", transfer->actual_length); + /* caller interprets result and frees transfer */ +} + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle); + + while (!*completed) { + r = libusb_handle_events_completed(ctx, completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying", + libusb_error_name(r)); + libusb_cancel_transfer(transfer); + continue; + } + } +} + +/** \ingroup libusb_syncio + * Perform a USB control transfer. + * + * The direction of the transfer is inferred from the bmRequestType field of + * the setup packet. + * + * The wValue, wIndex and wLength fields values should be given in host-endian + * byte order. + * + * \param dev_handle a handle for the device to communicate with + * \param bmRequestType the request type field for the setup packet + * \param bRequest the request field for the setup packet + * \param wValue the value field for the setup packet + * \param wIndex the index field for the setup packet + * \param data a suitably-sized data buffer for either input or output + * (depending on direction bits within bmRequestType) + * \param wLength the length field for the setup packet. The data buffer should + * be at least this size. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * \returns on success, the number of bytes actually transferred + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * device + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_BUSY if called from event handling context + * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) +{ + struct libusb_transfer *transfer; + unsigned char *buffer; + int completed = 0; + int r; + + if (usbi_handling_events(HANDLE_CTX(dev_handle))) + return LIBUSB_ERROR_BUSY; + + transfer = libusb_alloc_transfer(0); + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + if (!buffer) { + libusb_free_transfer(transfer); + return LIBUSB_ERROR_NO_MEM; + } + + libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, + wLength); + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, + sync_transfer_cb, &completed, timeout); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, libusb_control_transfer_get_data(transfer), + transfer->actual_length); + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = transfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + int *transferred, unsigned int timeout, unsigned char type) +{ + struct libusb_transfer *transfer; + int completed = 0; + int r; + + if (usbi_handling_events(HANDLE_CTX(dev_handle))) + return LIBUSB_ERROR_BUSY; + + transfer = libusb_alloc_transfer(0); + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, + sync_transfer_cb, &completed, timeout); + transfer->type = type; + + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if (transferred) + *transferred = transfer->actual_length; + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +/** \ingroup libusb_syncio + * Perform a USB bulk transfer. The direction of the transfer is inferred from + * the direction bits of the endpoint address. + * + * For bulk reads, the length field indicates the maximum length of + * data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for bulk writes. + * Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), + * it is legal to pass a NULL pointer if you do not wish to receive this + * information. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * transferred) + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref libusb_packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_BUSY if called from event handling context + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, int *transferred, + unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); +} + +/** \ingroup libusb_syncio + * Perform a USB interrupt transfer. The direction of the transfer is inferred + * from the direction bits of the endpoint address. + * + * For interrupt reads, the length field indicates the maximum length + * of data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for interrupt + * writes. Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * The default endpoint bInterval value is used as the polling interval. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), + * it is legal to pass a NULL pointer if you do not wish to receive this + * information. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref libusb_packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns LIBUSB_ERROR_BUSY if called from event handling context + * \returns another LIBUSB_ERROR code on other error + */ +int API_EXPORTED libusb_interrupt_transfer( + struct libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *data, int length, int *transferred, unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version.h new file mode 100644 index 0000000..6ce48a7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#include "version_nano.h" +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 21 +#endif +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h new file mode 100644 index 0000000..0a5d1c9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h @@ -0,0 +1 @@ +#define LIBUSB_NANO 11182 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/wchar.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/wchar.go new file mode 100644 index 0000000..d103bef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/karalabe/hid/wchar.go @@ -0,0 +1,227 @@ +// This file is https://github.com/orofarne/gowchar/blob/master/gowchar.go +// +// It was vendored inline to work around CGO limitations that don't allow C types +// to directly cross package API boundaries. +// +// The vendored file is licensed under the 3-clause BSD license, according to: +// https://github.com/orofarne/gowchar/blob/master/LICENSE + +// +build !ios +// +build linux darwin windows + +package hid + +/* +#include + +const size_t SIZEOF_WCHAR_T = sizeof(wchar_t); + +void gowchar_set (wchar_t *arr, int pos, wchar_t val) +{ + arr[pos] = val; +} + +wchar_t gowchar_get (wchar_t *arr, int pos) +{ + return arr[pos]; +} +*/ +import "C" + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +var sizeofWcharT C.size_t = C.size_t(C.SIZEOF_WCHAR_T) + +func stringToWcharT(s string) (*C.wchar_t, C.size_t) { + switch sizeofWcharT { + case 2: + return stringToWchar2(s) // Windows + case 4: + return stringToWchar4(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTToString(s *C.wchar_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2ToString(s) // Windows + case 4: + return wchar4ToString(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTNToString(s *C.wchar_t, size C.size_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2NToString(s, size) // Windows + case 4: + return wchar4NToString(s, size) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +// Windows +func stringToWchar2(s string) (*C.wchar_t, C.size_t) { + var slen int + s1 := s + for len(s1) > 0 { + r, size := utf8.DecodeRuneInString(s1) + if er, _ := utf16.EncodeRune(r); er == '\uFFFD' { + slen += 1 + } else { + slen += 2 + } + s1 = s1[size:] + } + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + if r1, r2 := utf16.EncodeRune(r); r1 != '\uFFFD' { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r1)) + i++ + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r2)) + i++ + } else { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + i++ + } + s = s[size:] + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Unix +func stringToWchar4(s string) (*C.wchar_t, C.size_t) { + slen := utf8.RuneCountInString(s) + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + s = s[size:] + i++ + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Windows +func wchar2ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + } else { + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +} + +// Windows +func wchar2NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + + res += string(r) + } else { + if i >= N { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/.travis.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/.travis.yml new file mode 100644 index 0000000..c6da8f0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/.travis.yml @@ -0,0 +1,15 @@ +# Copyright 2014 Marc-Antoine Ruel. All rights reserved. +# Use of this source code is governed under the Apache License, Version 2.0 +# that can be found in the LICENSE file. + +sudo: false +language: go + +go: +- tip + +before_install: + - go get github.com/maruel/pre-commit-go/cmd/pcg + +script: + - pcg diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/LICENSE new file mode 100644 index 0000000..b76840c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Marc-Antoine Ruel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/README.md new file mode 100644 index 0000000..9fc039c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/README.md @@ -0,0 +1,123 @@ +panicparse +========== + +Parses panic stack traces, densifies and deduplicates goroutines with similar +stack traces. Helps debugging crashes and deadlocks in heavily parallelized +process. + +[![Build Status](https://travis-ci.org/maruel/panicparse.svg?branch=master)](https://travis-ci.org/maruel/panicparse) + +panicparse helps make sense of Go crash dumps: + +![Screencast](https://raw.githubusercontent.com/wiki/maruel/panicparse/parse.gif "Screencast") + + +Features +-------- + + * >50% more compact output than original stack dump yet more readable. + * Exported symbols are bold, private symbols are darker. + * Stdlib is green, main is yellow, rest is red. + * Deduplicates redundant goroutine stacks. Useful for large server crashes. + * Arguments as pointer IDs instead of raw pointer values. + * Pushes stdlib-only stacks at the bottom to help focus on important code. + * Usable as a library! + [![GoDoc](https://godoc.org/github.com/maruel/panicparse/stack?status.svg)](https://godoc.org/github.com/maruel/panicparse/stack) + * Warning: please pin the version (e.g. vendor it). Breaking changes are + not planned but may happen. + * Parses the source files if available to augment the output. + * Works on Windows. + + +Installation +------------ + + go get github.com/maruel/panicparse/cmd/pp + + +Usage +----- + +### Piping a stack trace from another process + +#### TL;DR + + * Ubuntu (bash v4 or zsh): `|&` + * OSX, [install bash 4+](README.md#updating-bash-on-osx), then: `|&` + * Windows _or_ OSX with stock bash v3: `2>&1 |` + * [Fish](http://fishshell.com/) shell: `^|` + + +#### Longer version + +`pp` streams its stdin to stdout as long as it doesn't detect any panic. +`panic()` and Go's native deadlock detector [print to +stderr](https://golang.org/src/runtime/panic1.go) via the native [`print()` +function](https://golang.org/pkg/builtin/#print). + + +**Bash v4** or **zsh**: `|&` tells the shell to redirect stderr to stdout, +it's an alias for `2>&1 |` ([bash +v4](https://www.gnu.org/software/bash/manual/bash.html#Pipelines), +[zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Grammar.html#Simple-Commands-_0026-Pipelines)): + + go test -v |&pp + + +**Windows or OSX native bash** [(which is +3.2.57)](http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/): They don't +have this shortcut, so use the long form: + + go test -v 2>&1 | pp + + +**Fish**: It uses [^ for stderr +redirection](http://fishshell.com/docs/current/tutorial.html#tut_pipes_and_redirections) +so the shortcut is `^|`: + + go test -v ^|pp + + +**PowerShell**: [It has broken `2>&1` redirection](https://connect.microsoft.com/PowerShell/feedback/details/765551/in-powershell-v3-you-cant-redirect-stderr-to-stdout-without-generating-error-records). The workaround is to shell out to cmd.exe. :( + + +### Investigate deadlock + +On POSIX, use `Ctrl-\` to send SIGQUIT to your process, `pp` will ignore +the signal and will parse the stack trace. + + +### Parsing from a file + +To dump to a file then parse, pass the file path of a stack trace + + go test 2> stack.txt + pp stack.txt + + +Tips +---- + +### GOTRACEBACK + +Starting with Go 1.6, [`GOTRACEBACK`](https://golang.org/pkg/runtime/) defaults +to `single` instead of `all` / `1` that was used in 1.5 and before. To get all +goroutines trace and not just the crashing one, set the environment variable: + + export GOTRACEBACK=all + +or `set GOTRACEBACK=all` on Windows. Probably worth to put it in your `.bashrc`. + + +### Updating bash on OSX + +Install bash v4+ on OSX via [homebrew](http://brew.sh) or +[macports](https://www.macports.org/). Your future self will appreciate having +done that. + + +### If you have `/usr/bin/pp` installed + +You may have the Perl PAR Packager installed. Use long name `panicparse` then; + + go get github.com/maruel/panicparse diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/source.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/source.go new file mode 100644 index 0000000..f09e673 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/source.go @@ -0,0 +1,291 @@ +// Copyright 2015 Marc-Antoine Ruel. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains the code to process sources, to be able to deduct the +// original types. + +package stack + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/ioutil" + "log" + "math" + "strings" +) + +// cache is a cache of sources on the file system. +type cache struct { + files map[string][]byte + parsed map[string]*parsedFile +} + +// Augment processes source files to improve calls to be more descriptive. +// +// It modifies goroutines in place. +func Augment(goroutines []Goroutine) { + c := &cache{} + for i := range goroutines { + c.augmentGoroutine(&goroutines[i]) + } +} + +// augmentGoroutine processes source files to improve call to be more +// descriptive. +// +// It modifies the routine. +func (c *cache) augmentGoroutine(goroutine *Goroutine) { + if c.files == nil { + c.files = map[string][]byte{} + } + if c.parsed == nil { + c.parsed = map[string]*parsedFile{} + } + // For each call site, look at the next call and populate it. Then we can + // walk back and reformat things. + for i := range goroutine.Stack.Calls { + c.load(goroutine.Stack.Calls[i].SourcePath) + } + + // Once all loaded, we can look at the next call when available. + for i := 1; i < len(goroutine.Stack.Calls); i++ { + // Get the AST from the previous call and process the call line with it. + if f := c.getFuncAST(&goroutine.Stack.Calls[i]); f != nil { + processCall(&goroutine.Stack.Calls[i], f) + } + } +} + +// Private stuff. + +// load loads a source file and parses the AST tree. Failures are ignored. +func (c *cache) load(fileName string) { + if _, ok := c.parsed[fileName]; ok { + return + } + c.parsed[fileName] = nil + if !strings.HasSuffix(fileName, ".go") { + // Ignore C and assembly. + c.files[fileName] = nil + return + } + log.Printf("load(%s)", fileName) + if _, ok := c.files[fileName]; !ok { + var err error + if c.files[fileName], err = ioutil.ReadFile(fileName); err != nil { + log.Printf("Failed to read %s: %s", fileName, err) + c.files[fileName] = nil + return + } + } + fset := token.NewFileSet() + src := c.files[fileName] + parsed, err := parser.ParseFile(fset, fileName, src, 0) + if err != nil { + log.Printf("Failed to parse %s: %s", fileName, err) + return + } + // Convert the line number into raw file offset. + offsets := []int{0, 0} + start := 0 + for l := 1; start < len(src); l++ { + start += bytes.IndexByte(src[start:], '\n') + 1 + offsets = append(offsets, start) + } + c.parsed[fileName] = &parsedFile{offsets, parsed} +} + +func (c *cache) getFuncAST(call *Call) *ast.FuncDecl { + if p := c.parsed[call.SourcePath]; p != nil { + return p.getFuncAST(call.Func.Name(), call.Line) + } + return nil +} + +type parsedFile struct { + lineToByteOffset []int + parsed *ast.File +} + +// getFuncAST gets the callee site function AST representation for the code +// inside the function f at line l. +func (p *parsedFile) getFuncAST(f string, l int) (d *ast.FuncDecl) { + // Walk the AST to find the lineToByteOffset that fits the line number. + var lastFunc *ast.FuncDecl + var found ast.Node + // Inspect() goes depth first. This means for example that a function like: + // func a() { + // b := func() {} + // c() + // } + // + // Were we are looking at the c() call can return confused values. It is + // important to look at the actual ast.Node hierarchy. + ast.Inspect(p.parsed, func(n ast.Node) bool { + if d != nil { + return false + } + if n == nil { + return true + } + if found != nil { + // We are walking up. + } + if int(n.Pos()) >= p.lineToByteOffset[l] { + // We are expecting a ast.CallExpr node. It can be harder to figure out + // when there are multiple calls on a single line, as the stack trace + // doesn't have file byte offset information, only line based. + // gofmt will always format to one function call per line but there can + // be edge cases, like: + // a = A{Foo(), Bar()} + d = lastFunc + //p.processNode(call, n) + return false + } else if f, ok := n.(*ast.FuncDecl); ok { + lastFunc = f + } + return true + }) + return +} + +func name(n ast.Node) string { + if _, ok := n.(*ast.InterfaceType); ok { + return "interface{}" + } + if i, ok := n.(*ast.Ident); ok { + return i.Name + } + if _, ok := n.(*ast.FuncType); ok { + return "func" + } + if s, ok := n.(*ast.SelectorExpr); ok { + return s.Sel.Name + } + // TODO(maruel): Implement anything missing. + return "" +} + +// fieldToType returns the type name and whether if it's an ellipsis. +func fieldToType(f *ast.Field) (string, bool) { + switch arg := f.Type.(type) { + case *ast.ArrayType: + return "[]" + name(arg.Elt), false + case *ast.Ellipsis: + return name(arg.Elt), true + case *ast.FuncType: + // Do not print the function signature to not overload the trace. + return "func", false + case *ast.Ident: + return arg.Name, false + case *ast.InterfaceType: + return "interface{}", false + case *ast.SelectorExpr: + return arg.Sel.Name, false + case *ast.StarExpr: + return "*" + name(arg.X), false + default: + // TODO(maruel): Implement anything missing. + return "", false + } +} + +// extractArgumentsType returns the name of the type of each input argument. +func extractArgumentsType(f *ast.FuncDecl) ([]string, bool) { + var fields []*ast.Field + if f.Recv != nil { + if len(f.Recv.List) != 1 { + panic("Expect only one receiver; please fix panicparse's code") + } + // If it is an object receiver (vs a pointer receiver), its address is not + // printed in the stack trace so it needs to be ignored. + if _, ok := f.Recv.List[0].Type.(*ast.StarExpr); ok { + fields = append(fields, f.Recv.List[0]) + } + } + var types []string + extra := false + for _, arg := range append(fields, f.Type.Params.List...) { + // Assert that extra is only set on the last item of fields? + var t string + t, extra = fieldToType(arg) + mult := len(arg.Names) + if mult == 0 { + mult = 1 + } + for i := 0; i < mult; i++ { + types = append(types, t) + } + } + return types, extra +} + +// processCall walks the function and populate call accordingly. +func processCall(call *Call, f *ast.FuncDecl) { + values := make([]uint64, len(call.Args.Values)) + for i := range call.Args.Values { + values[i] = call.Args.Values[i].Value + } + index := 0 + pop := func() uint64 { + if len(values) != 0 { + x := values[0] + values = values[1:] + index++ + return x + } + return 0 + } + popName := func() string { + n := call.Args.Values[index].Name + v := pop() + if len(n) == 0 { + return fmt.Sprintf("0x%x", v) + } + return n + } + + types, extra := extractArgumentsType(f) + for i := 0; len(values) != 0; i++ { + var t string + if i >= len(types) { + if !extra { + // These are unexpected value! Print them as hex. + call.Args.Processed = append(call.Args.Processed, popName()) + continue + } + t = types[len(types)-1] + } else { + t = types[i] + } + switch t { + case "float32": + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float32frombits(uint32(pop())))) + case "float64": + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float64frombits(pop()))) + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%d", pop())) + case "string": + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s, len=%d)", t, popName(), pop())) + default: + if strings.HasPrefix(t, "*") { + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName())) + } else if strings.HasPrefix(t, "[]") { + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s len=%d cap=%d)", t, popName(), pop(), pop())) + } else { + // Assumes it's an interface. For now, discard the object value, which + // is probably not a good idea. + call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName())) + pop() + } + } + if len(values) == 0 && call.Args.Elided { + return + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/stack.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/stack.go new file mode 100644 index 0000000..cfb502e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/stack.go @@ -0,0 +1,832 @@ +// Copyright 2015 Marc-Antoine Ruel. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package stack analyzes stack dump of Go processes and simplifies it. +// +// It is mostly useful on servers will large number of identical goroutines, +// making the crash dump harder to read than strictly necesary. +package stack + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "math" + "net/url" + "os" + "path/filepath" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +const lockedToThread = "locked to thread" + +var ( + // TODO(maruel): Handle corrupted stack cases: + // - missed stack barrier + // - found next stack barrier at 0x123; expected + // - runtime: unexpected return pc for FUNC_NAME called from 0x123 + + reRoutineHeader = regexp.MustCompile("^goroutine (\\d+) \\[([^\\]]+)\\]\\:\n$") + reMinutes = regexp.MustCompile("^(\\d+) minutes$") + reUnavail = regexp.MustCompile("^(?:\t| +)goroutine running on other thread; stack unavailable") + // See gentraceback() in src/runtime/traceback.go for more information. + // - Sometimes the source file comes up as "". It is the + // compiler than generated these, not the runtime. + // - The tab may be replaced with spaces when a user copy-paste it, handle + // this transparently. + // - "runtime.gopanic" is explicitly replaced with "panic" by gentraceback(). + // - The +0x123 byte offset is printed when frame.pc > _func.entry. _func is + // generated by the linker. + // - The +0x123 byte offset is not included with generated code, e.g. unnamed + // functions "func·006()" which is generally go func() { ... }() + // statements. Since the _func is generated at runtime, it's probably why + // _func.entry is not set. + // - C calls may have fp=0x123 sp=0x123 appended. I think it normally happens + // when a signal is not correctly handled. It is printed with m.throwing>0. + // These are discarded. + // - For cgo, the source file may be "??". + reFile = regexp.MustCompile("^(?:\t| +)(\\?\\?|\\|.+\\.(?:c|go|s))\\:(\\d+)(?:| \\+0x[0-9a-f]+)(?:| fp=0x[0-9a-f]+ sp=0x[0-9a-f]+)\n$") + // Sadly, it doesn't note the goroutine number so we could cascade them per + // parenthood. + reCreated = regexp.MustCompile("^created by (.+)\n$") + reFunc = regexp.MustCompile("^(.+)\\((.*)\\)\n$") + reElided = regexp.MustCompile("^\\.\\.\\.additional frames elided\\.\\.\\.\n$") + // Include frequent GOROOT value on Windows, distro provided and user + // installed path. This simplifies the user's life when processing a trace + // generated on another VM. + // TODO(maruel): Guess the path automatically via traces containing the + // 'runtime' package, which is very frequent. This would be "less bad" than + // throwing up random values at the parser. + goroots = []string{runtime.GOROOT(), "c:/go", "/usr/lib/go", "/usr/local/go"} +) + +// Similarity is the level at which two call lines arguments must match to be +// considered similar enough to coalesce them. +type Similarity int + +const ( + // ExactFlags requires same bits (e.g. Locked). + ExactFlags Similarity = iota + // ExactLines requests the exact same arguments on the call line. + ExactLines + // AnyPointer considers different pointers a similar call line. + AnyPointer + // AnyValue accepts any value as similar call line. + AnyValue +) + +// Function is a function call. +// +// Go stack traces print a mangled function call, this wrapper unmangle the +// string before printing and adds other filtering methods. +type Function struct { + Raw string +} + +// String is the fully qualified function name. +// +// Sadly Go is a bit confused when the package name doesn't match the directory +// containing the source file and will use the directory name instead of the +// real package name. +func (f Function) String() string { + s, _ := url.QueryUnescape(f.Raw) + return s +} + +// Name is the naked function name. +func (f Function) Name() string { + parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) + if len(parts) == 1 { + return parts[0] + } + return parts[1] +} + +// PkgName is the package name for this function reference. +func (f Function) PkgName() string { + parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) + if len(parts) == 1 { + return "" + } + s, _ := url.QueryUnescape(parts[0]) + return s +} + +// PkgDotName returns "." format. +func (f Function) PkgDotName() string { + parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) + s, _ := url.QueryUnescape(parts[0]) + if len(parts) == 1 { + return parts[0] + } + if s != "" || parts[1] != "" { + return s + "." + parts[1] + } + return "" +} + +// IsExported returns true if the function is exported. +func (f Function) IsExported() bool { + name := f.Name() + parts := strings.Split(name, ".") + r, _ := utf8.DecodeRuneInString(parts[len(parts)-1]) + if unicode.ToUpper(r) == r { + return true + } + return f.PkgName() == "main" && name == "main" +} + +// Arg is an argument on a Call. +type Arg struct { + Value uint64 // Value is the raw value as found in the stack trace + Name string // Name is a pseudo name given to the argument +} + +// IsPtr returns true if we guess it's a pointer. It's only a guess, it can be +// easily be confused by a bitmask. +func (a *Arg) IsPtr() bool { + // Assumes all pointers are above 16Mb and positive. + return a.Value > 16*1024*1024 && a.Value < math.MaxInt64 +} + +func (a Arg) String() string { + if a.Name != "" { + return a.Name + } + if a.Value == 0 { + return "0" + } + return fmt.Sprintf("0x%x", a.Value) +} + +// Args is a series of function call arguments. +type Args struct { + Values []Arg // Values is the arguments as shown on the stack trace. They are mangled via simplification. + Processed []string // Processed is the arguments generated from processing the source files. It can have a length lower than Values. + Elided bool // If set, it means there was a trailing ", ..." +} + +func (a Args) String() string { + var v []string + if len(a.Processed) != 0 { + v = make([]string, 0, len(a.Processed)) + for _, item := range a.Processed { + v = append(v, item) + } + } else { + v = make([]string, 0, len(a.Values)) + for _, item := range a.Values { + v = append(v, item.String()) + } + } + if a.Elided { + v = append(v, "...") + } + return strings.Join(v, ", ") +} + +// Equal returns true only if both arguments are exactly equal. +func (a *Args) Equal(r *Args) bool { + if a.Elided != r.Elided || len(a.Values) != len(r.Values) { + return false + } + for i, l := range a.Values { + if l != r.Values[i] { + return false + } + } + return true +} + +// Similar returns true if the two Args are equal or almost but not quite +// equal. +func (a *Args) Similar(r *Args, similar Similarity) bool { + if a.Elided != r.Elided || len(a.Values) != len(r.Values) { + return false + } + if similar == AnyValue { + return true + } + for i, l := range a.Values { + switch similar { + case ExactFlags, ExactLines: + if l != r.Values[i] { + return false + } + default: + if l.IsPtr() != r.Values[i].IsPtr() || (!l.IsPtr() && l != r.Values[i]) { + return false + } + } + } + return true +} + +// Merge merges two similar Args, zapping out differences. +func (a *Args) Merge(r *Args) Args { + out := Args{ + Values: make([]Arg, len(a.Values)), + Elided: a.Elided, + } + for i, l := range a.Values { + if l != r.Values[i] { + out.Values[i].Name = "*" + out.Values[i].Value = l.Value + } else { + out.Values[i] = l + } + } + return out +} + +// Call is an item in the stack trace. +type Call struct { + SourcePath string // Full path name of the source file + Line int // Line number + Func Function // Fully qualified function name (encoded). + Args Args // Call arguments +} + +// Equal returns true only if both calls are exactly equal. +func (c *Call) Equal(r *Call) bool { + return c.SourcePath == r.SourcePath && c.Line == r.Line && c.Func == r.Func && c.Args.Equal(&r.Args) +} + +// Similar returns true if the two Call are equal or almost but not quite +// equal. +func (c *Call) Similar(r *Call, similar Similarity) bool { + return c.SourcePath == r.SourcePath && c.Line == r.Line && c.Func == r.Func && c.Args.Similar(&r.Args, similar) +} + +// Merge merges two similar Call, zapping out differences. +func (c *Call) Merge(r *Call) Call { + return Call{ + SourcePath: c.SourcePath, + Line: c.Line, + Func: c.Func, + Args: c.Args.Merge(&r.Args), + } +} + +// SourceName returns the base file name of the source file. +func (c *Call) SourceName() string { + return filepath.Base(c.SourcePath) +} + +// SourceLine returns "source.go:line", including only the base file name. +func (c *Call) SourceLine() string { + return fmt.Sprintf("%s:%d", c.SourceName(), c.Line) +} + +// FullSourceLine returns "/path/to/source.go:line". +func (c *Call) FullSourceLine() string { + return fmt.Sprintf("%s:%d", c.SourcePath, c.Line) +} + +// PkgSource is one directory plus the file name of the source file. +func (c *Call) PkgSource() string { + return filepath.Join(filepath.Base(filepath.Dir(c.SourcePath)), c.SourceName()) +} + +const testMainSource = "_test" + string(os.PathSeparator) + "_testmain.go" + +// IsStdlib returns true if it is a Go standard library function. This includes +// the 'go test' generated main executable. +func (c *Call) IsStdlib() bool { + for _, goroot := range goroots { + if strings.HasPrefix(c.SourcePath, goroot) { + return true + } + } + // Consider _test/_testmain.go as stdlib since it's injected by "go test". + return c.PkgSource() == testMainSource +} + +// IsPkgMain returns true if it is in the main package. +func (c *Call) IsPkgMain() bool { + return c.Func.PkgName() == "main" +} + +// Stack is a call stack. +type Stack struct { + Calls []Call // Call stack. First is original function, last is leaf function. + Elided bool // Happens when there's >100 items in Stack, currently hardcoded in package runtime. +} + +// Equal returns true on if both call stacks are exactly equal. +func (s *Stack) Equal(r *Stack) bool { + if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided { + return false + } + for i := range s.Calls { + if !s.Calls[i].Equal(&r.Calls[i]) { + return false + } + } + return true +} + +// Similar returns true if the two Stack are equal or almost but not quite +// equal. +func (s *Stack) Similar(r *Stack, similar Similarity) bool { + if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided { + return false + } + for i := range s.Calls { + if !s.Calls[i].Similar(&r.Calls[i], similar) { + return false + } + } + return true +} + +// Merge merges two similar Stack, zapping out differences. +func (s *Stack) Merge(r *Stack) *Stack { + // Assumes similar stacks have the same length. + out := &Stack{ + Calls: make([]Call, len(s.Calls)), + Elided: s.Elided, + } + for i := range s.Calls { + out.Calls[i] = s.Calls[i].Merge(&r.Calls[i]) + } + return out +} + +// Less compares two Stack, where the ones that are less are more +// important, so they come up front. A Stack with more private functions is +// 'less' so it is at the top. Inversely, a Stack with only public +// functions is 'more' so it is at the bottom. +func (s *Stack) Less(r *Stack) bool { + lStdlib := 0 + lPrivate := 0 + for _, c := range s.Calls { + if c.IsStdlib() { + lStdlib++ + } else { + lPrivate++ + } + } + rStdlib := 0 + rPrivate := 0 + for _, s := range r.Calls { + if s.IsStdlib() { + rStdlib++ + } else { + rPrivate++ + } + } + if lPrivate > rPrivate { + return true + } + if lPrivate < rPrivate { + return false + } + if lStdlib > rStdlib { + return false + } + if lStdlib < rStdlib { + return true + } + + // Stack lengths are the same. + for x := range s.Calls { + if s.Calls[x].Func.Raw < r.Calls[x].Func.Raw { + return true + } + if s.Calls[x].Func.Raw > r.Calls[x].Func.Raw { + return true + } + if s.Calls[x].PkgSource() < r.Calls[x].PkgSource() { + return true + } + if s.Calls[x].PkgSource() > r.Calls[x].PkgSource() { + return true + } + if s.Calls[x].Line < r.Calls[x].Line { + return true + } + if s.Calls[x].Line > r.Calls[x].Line { + return true + } + } + return false +} + +// Signature represents the signature of one or multiple goroutines. +// +// It is effectively the stack trace plus the goroutine internal bits, like +// it's state, if it is thread locked, which call site created this goroutine, +// etc. +type Signature struct { + // Use git grep 'gopark(|unlock)\(' to find them all plus everything listed + // in runtime/traceback.go. Valid values includes: + // - chan send, chan receive, select + // - finalizer wait, mark wait (idle), + // - Concurrent GC wait, GC sweep wait, force gc (idle) + // - IO wait, panicwait + // - semacquire, semarelease + // - sleep, timer goroutine (idle) + // - trace reader (blocked) + // Stuck cases: + // - chan send (nil chan), chan receive (nil chan), select (no cases) + // Runnable states: + // - idle, runnable, running, syscall, waiting, dead, enqueue, copystack, + // Scan states: + // - scan, scanrunnable, scanrunning, scansyscall, scanwaiting, scandead, + // scanenqueue + State string + CreatedBy Call // Which other goroutine which created this one. + SleepMin int // Wait time in minutes, if applicable. + SleepMax int // Wait time in minutes, if applicable. + Stack Stack + Locked bool // Locked to an OS thread. +} + +// Equal returns true only if both signatures are exactly equal. +func (s *Signature) Equal(r *Signature) bool { + if s.State != r.State || !s.CreatedBy.Equal(&r.CreatedBy) || s.Locked != r.Locked || s.SleepMin != r.SleepMin || s.SleepMax != r.SleepMax { + return false + } + return s.Stack.Equal(&r.Stack) +} + +// Similar returns true if the two Signature are equal or almost but not quite +// equal. +func (s *Signature) Similar(r *Signature, similar Similarity) bool { + if s.State != r.State || !s.CreatedBy.Similar(&r.CreatedBy, similar) { + return false + } + if similar == ExactFlags && s.Locked != r.Locked { + return false + } + return s.Stack.Similar(&r.Stack, similar) +} + +// Merge merges two similar Signature, zapping out differences. +func (s *Signature) Merge(r *Signature) *Signature { + min := s.SleepMin + if r.SleepMin < min { + min = r.SleepMin + } + max := s.SleepMax + if r.SleepMax > max { + max = r.SleepMax + } + return &Signature{ + State: s.State, // Drop right side. + CreatedBy: s.CreatedBy, // Drop right side. + SleepMin: min, + SleepMax: max, + Stack: *s.Stack.Merge(&r.Stack), + Locked: s.Locked || r.Locked, // TODO(maruel): This is weirdo. + } +} + +// Less compares two Signature, where the ones that are less are more +// important, so they come up front. A Signature with more private functions is +// 'less' so it is at the top. Inversely, a Signature with only public +// functions is 'more' so it is at the bottom. +func (s *Signature) Less(r *Signature) bool { + if s.Stack.Less(&r.Stack) { + return true + } + if r.Stack.Less(&s.Stack) { + return false + } + if s.Locked && !r.Locked { + return true + } + if r.Locked && !s.Locked { + return false + } + if s.State < r.State { + return true + } + if s.State > r.State { + return false + } + return false +} + +// Goroutine represents the state of one goroutine, including the stack trace. +type Goroutine struct { + Signature // It's stack trace, internal bits, state, which call site created it, etc. + ID int // Goroutine ID. + First bool // First is the goroutine first printed, normally the one that crashed. +} + +// Bucketize returns the number of similar goroutines. +func Bucketize(goroutines []Goroutine, similar Similarity) map[*Signature][]Goroutine { + out := map[*Signature][]Goroutine{} + // O(n²). Fix eventually. + for _, routine := range goroutines { + found := false + for key := range out { + // When a match is found, this effectively drops the other goroutine ID. + if key.Similar(&routine.Signature, similar) { + found = true + if !key.Equal(&routine.Signature) { + // Almost but not quite equal. There's different pointers passed + // around but the same values. Zap out the different values. + newKey := key.Merge(&routine.Signature) + out[newKey] = append(out[key], routine) + delete(out, key) + } else { + out[key] = append(out[key], routine) + } + break + } + } + if !found { + key := &Signature{} + *key = routine.Signature + out[key] = []Goroutine{routine} + } + } + return out +} + +// Bucket is a stack trace signature and the list of goroutines that fits this +// signature. +type Bucket struct { + Signature + Routines []Goroutine +} + +// First returns true if it contains the first goroutine, e.g. the ones that +// likely generated the panic() call, if any. +func (b *Bucket) First() bool { + for _, r := range b.Routines { + if r.First { + return true + } + } + return false +} + +// Less does reverse sort. +func (b *Bucket) Less(r *Bucket) bool { + if b.First() { + return true + } + if r.First() { + return false + } + return b.Signature.Less(&r.Signature) +} + +// Buckets is a list of Bucket sorted by repeation count. +type Buckets []Bucket + +func (b Buckets) Len() int { + return len(b) +} + +func (b Buckets) Less(i, j int) bool { + return b[i].Less(&b[j]) +} + +func (b Buckets) Swap(i, j int) { + b[j], b[i] = b[i], b[j] +} + +// SortBuckets creates a list of Bucket from each goroutine stack trace count. +func SortBuckets(buckets map[*Signature][]Goroutine) Buckets { + out := make(Buckets, 0, len(buckets)) + for signature, count := range buckets { + out = append(out, Bucket{*signature, count}) + } + sort.Sort(out) + return out +} + +// scanLines is similar to bufio.ScanLines except that it: +// - doesn't drop '\n' +// - doesn't strip '\r' +// - returns when the data is bufio.MaxScanTokenSize bytes +func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + return i + 1, data[0 : i+1], nil + } + if atEOF { + return len(data), data, nil + } + if len(data) >= bufio.MaxScanTokenSize { + // Returns the line even if it is not at EOF nor has a '\n', otherwise the + // scanner will return bufio.ErrTooLong which is definitely not what we + // want. + return len(data), data, nil + } + return 0, nil, nil +} + +// ParseDump processes the output from runtime.Stack(). +// +// It supports piping from another command and assumes there is junk before the +// actual stack trace. The junk is streamed to out. +func ParseDump(r io.Reader, out io.Writer) ([]Goroutine, error) { + goroutines := make([]Goroutine, 0, 16) + var goroutine *Goroutine + scanner := bufio.NewScanner(r) + scanner.Split(scanLines) + // TODO(maruel): Use a formal state machine. Patterns follows: + // - reRoutineHeader + // Either: + // - reUnavail + // - reFunc + reFile in a loop + // - reElided + // Optionally ends with: + // - reCreated + reFile + // Between each goroutine stack dump: an empty line + created := false + // firstLine is the first line after the reRoutineHeader header line. + firstLine := false + for scanner.Scan() { + line := scanner.Text() + if line == "\n" { + if goroutine != nil { + goroutine = nil + continue + } + } else if line[len(line)-1] == '\n' { + if goroutine == nil { + if match := reRoutineHeader.FindStringSubmatch(line); match != nil { + if id, err := strconv.Atoi(match[1]); err == nil { + // See runtime/traceback.go. + // ", \d+ minutes, locked to thread" + items := strings.Split(match[2], ", ") + sleep := 0 + locked := false + for i := 1; i < len(items); i++ { + if items[i] == lockedToThread { + locked = true + continue + } + // Look for duration, if any. + if match2 := reMinutes.FindStringSubmatch(items[i]); match2 != nil { + sleep, _ = strconv.Atoi(match2[1]) + } + } + goroutines = append(goroutines, Goroutine{ + Signature: Signature{ + State: items[0], + SleepMin: sleep, + SleepMax: sleep, + Locked: locked, + }, + ID: id, + First: len(goroutines) == 0, + }) + goroutine = &goroutines[len(goroutines)-1] + firstLine = true + continue + } + } + } else { + if firstLine { + firstLine = false + if match := reUnavail.FindStringSubmatch(line); match != nil { + // Generate a fake stack entry. + goroutine.Stack.Calls = []Call{{SourcePath: ""}} + continue + } + } + + if match := reFile.FindStringSubmatch(line); match != nil { + // Triggers after a reFunc or a reCreated. + num, err := strconv.Atoi(match[2]) + if err != nil { + return goroutines, fmt.Errorf("failed to parse int on line: \"%s\"", line) + } + if created { + created = false + goroutine.CreatedBy.SourcePath = match[1] + goroutine.CreatedBy.Line = num + } else { + i := len(goroutine.Stack.Calls) - 1 + if i < 0 { + return goroutines, errors.New("unexpected order") + } + goroutine.Stack.Calls[i].SourcePath = match[1] + goroutine.Stack.Calls[i].Line = num + } + continue + } + + if match := reCreated.FindStringSubmatch(line); match != nil { + created = true + goroutine.CreatedBy.Func.Raw = match[1] + continue + } + + if match := reFunc.FindStringSubmatch(line); match != nil { + args := Args{} + for _, a := range strings.Split(match[2], ", ") { + if a == "..." { + args.Elided = true + continue + } + if a == "" { + // Remaining values were dropped. + break + } + v, err := strconv.ParseUint(a, 0, 64) + if err != nil { + return goroutines, fmt.Errorf("failed to parse int on line: \"%s\"", line) + } + args.Values = append(args.Values, Arg{Value: v}) + } + goroutine.Stack.Calls = append(goroutine.Stack.Calls, Call{Func: Function{match[1]}, Args: args}) + continue + } + + if match := reElided.FindStringSubmatch(line); match != nil { + goroutine.Stack.Elided = true + continue + } + } + } + _, _ = io.WriteString(out, line) + goroutine = nil + } + nameArguments(goroutines) + return goroutines, scanner.Err() +} + +// Private stuff. + +func nameArguments(goroutines []Goroutine) { + // Set a name for any pointer occuring more than once. + type object struct { + args []*Arg + inPrimary bool + id int + } + objects := map[uint64]object{} + // Enumerate all the arguments. + for i := range goroutines { + for j := range goroutines[i].Stack.Calls { + for k := range goroutines[i].Stack.Calls[j].Args.Values { + arg := goroutines[i].Stack.Calls[j].Args.Values[k] + if arg.IsPtr() { + objects[arg.Value] = object{ + args: append(objects[arg.Value].args, &goroutines[i].Stack.Calls[j].Args.Values[k]), + inPrimary: objects[arg.Value].inPrimary || i == 0, + } + } + } + } + // CreatedBy.Args is never set. + } + order := uint64Slice{} + for k, obj := range objects { + if len(obj.args) > 1 && obj.inPrimary { + order = append(order, k) + } + } + sort.Sort(order) + nextID := 1 + for _, k := range order { + for _, arg := range objects[k].args { + arg.Name = fmt.Sprintf("#%d", nextID) + } + nextID++ + } + + // Now do the rest. This is done so the output is deterministic. + order = uint64Slice{} + for k := range objects { + order = append(order, k) + } + sort.Sort(order) + for _, k := range order { + // Process the remaining pointers, they were not referenced by primary + // thread so will have higher IDs. + if objects[k].inPrimary { + continue + } + for _, arg := range objects[k].args { + arg.Name = fmt.Sprintf("#%d", nextID) + } + nextID++ + } +} + +type uint64Slice []uint64 + +func (a uint64Slice) Len() int { return len(a) } +func (a uint64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a uint64Slice) Less(i, j int) bool { return a[i] < a[j] } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/ui.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/ui.go new file mode 100644 index 0000000..b125fc9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/stack/ui.go @@ -0,0 +1,139 @@ +// Copyright 2016 Marc-Antoine Ruel. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package stack + +import ( + "fmt" + "strings" +) + +// Palette defines the color used. +// +// An empty object Palette{} can be used to disable coloring. +type Palette struct { + EOLReset string + + // Routine header. + RoutineFirst string // The first routine printed. + Routine string // Following routines. + CreatedBy string + + // Call line. + Package string + SourceFile string + FunctionStdLib string + FunctionStdLibExported string + FunctionMain string + FunctionOther string + FunctionOtherExported string + Arguments string +} + +// CalcLengths returns the maximum length of the source lines and package names. +func CalcLengths(buckets Buckets, fullPath bool) (int, int) { + srcLen := 0 + pkgLen := 0 + for _, bucket := range buckets { + for _, line := range bucket.Signature.Stack.Calls { + l := 0 + if fullPath { + l = len(line.FullSourceLine()) + } else { + l = len(line.SourceLine()) + } + if l > srcLen { + srcLen = l + } + l = len(line.Func.PkgName()) + if l > pkgLen { + pkgLen = l + } + } + } + return srcLen, pkgLen +} + +// functionColor returns the color to be used for the function name based on +// the type of package the function is in. +func (p *Palette) functionColor(line *Call) string { + if line.IsStdlib() { + if line.Func.IsExported() { + return p.FunctionStdLibExported + } + return p.FunctionStdLib + } else if line.IsPkgMain() { + return p.FunctionMain + } else if line.Func.IsExported() { + return p.FunctionOtherExported + } + return p.FunctionOther +} + +// routineColor returns the color for the header of the goroutines bucket. +func (p *Palette) routineColor(bucket *Bucket, multipleBuckets bool) string { + if bucket.First() && multipleBuckets { + return p.RoutineFirst + } + return p.Routine +} + +// BucketHeader prints the header of a goroutine signature. +func (p *Palette) BucketHeader(bucket *Bucket, fullPath, multipleBuckets bool) string { + extra := "" + if bucket.SleepMax != 0 { + if bucket.SleepMin != bucket.SleepMax { + extra += fmt.Sprintf(" [%d~%d minutes]", bucket.SleepMin, bucket.SleepMax) + } else { + extra += fmt.Sprintf(" [%d minutes]", bucket.SleepMax) + } + } + if bucket.Locked { + extra += " [locked]" + } + created := bucket.CreatedBy.Func.PkgDotName() + if created != "" { + created += " @ " + if fullPath { + created += bucket.CreatedBy.FullSourceLine() + } else { + created += bucket.CreatedBy.SourceLine() + } + extra += p.CreatedBy + " [Created by " + created + "]" + } + return fmt.Sprintf( + "%s%d: %s%s%s\n", + p.routineColor(bucket, multipleBuckets), len(bucket.Routines), + bucket.State, extra, + p.EOLReset) +} + +// callLine prints one stack line. +func (p *Palette) callLine(line *Call, srcLen, pkgLen int, fullPath bool) string { + src := "" + if fullPath { + src = line.FullSourceLine() + } else { + src = line.SourceLine() + } + return fmt.Sprintf( + " %s%-*s %s%-*s %s%s%s(%s)%s", + p.Package, pkgLen, line.Func.PkgName(), + p.SourceFile, srcLen, src, + p.functionColor(line), line.Func.Name(), + p.Arguments, line.Args, + p.EOLReset) +} + +// StackLines prints one complete stack trace, without the header. +func (p *Palette) StackLines(signature *Signature, srcLen, pkgLen int, fullPath bool) string { + out := make([]string, len(signature.Stack.Calls)) + for i := range signature.Stack.Calls { + out[i] = p.callLine(&signature.Stack.Calls[i], srcLen, pkgLen, fullPath) + } + if signature.Stack.Elided { + out = append(out, " (...)") + } + return strings.Join(out, "\n") + "\n" +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/vendor.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/vendor.yml new file mode 100644 index 0000000..ff3d43f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/maruel/panicparse/vendor.yml @@ -0,0 +1,17 @@ +vendors: +- path: github.com/kr/pretty + rev: 737b74a46c4bf788349f72cb256fed10aea4d0ac +- path: github.com/kr/text + rev: 7cafcd837844e784b526369c9bce262804aebc60 +- path: github.com/maruel/ut + rev: a9c9f15ccfa6f8b90182a53df32f4745586fbae3 +- path: github.com/mattn/go-colorable + rev: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59 +- path: github.com/mattn/go-isatty + rev: 56b76bdf51f7708750eac80fa38b952bb9f32639 +- path: github.com/mgutz/ansi + rev: c286dcecd19ff979eeb73ea444e479b903f2cfcb +- path: github.com/pmezard/go-difflib + rev: 792786c7400a136282c1664665ae0a8db921c6c2 +- path: golang.org/x/sys + rev: a646d33e2ee3172a661fc09bca23bb4889a41bc8 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/LICENSE new file mode 100644 index 0000000..91b5cef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/README.md new file mode 100644 index 0000000..e84226a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/README.md @@ -0,0 +1,43 @@ +# go-colorable + +Colorable writer for windows. + +For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) +This package is possible to handle escape sequence for ansi color on windows. + +## Too Bad! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) + + +## So Good! + +![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) + +## Usage + +```go +logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) +logrus.SetOutput(colorable.NewColorableStdout()) + +logrus.Info("succeeded") +logrus.Warn("not correct") +logrus.Error("something error") +logrus.Fatal("panic") +``` + +You can compile above code on non-windows OSs. + +## Installation + +``` +$ go get github.com/mattn/go-colorable +``` + +# License + +MIT + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_others.go new file mode 100644 index 0000000..a7fe19a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_others.go @@ -0,0 +1,27 @@ +// +build !windows + +package colorable + +import ( + "io" + "os" +) + +// NewColorable return new instance of Writer which handle escape sequence. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + return file +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return os.Stdout +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return os.Stderr +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_windows.go new file mode 100644 index 0000000..628ad90 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -0,0 +1,820 @@ +package colorable + +import ( + "bytes" + "io" + "math" + "os" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + foregroundBlue = 0x1 + foregroundGreen = 0x2 + foregroundRed = 0x4 + foregroundIntensity = 0x8 + foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) + backgroundBlue = 0x10 + backgroundGreen = 0x20 + backgroundRed = 0x40 + backgroundIntensity = 0x80 + backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") +) + +type Writer struct { + out io.Writer + handle syscall.Handle + lastbuf bytes.Buffer + oldattr word + oldpos coord +} + +// NewColorable return new instance of Writer which handle escape sequence from File. +func NewColorable(file *os.File) io.Writer { + if file == nil { + panic("nil passed instead of *os.File to NewColorable()") + } + + if isatty.IsTerminal(file.Fd()) { + var csbi consoleScreenBufferInfo + handle := syscall.Handle(file.Fd()) + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + } else { + return file + } +} + +// NewColorableStdout return new instance of Writer which handle escape sequence for stdout. +func NewColorableStdout() io.Writer { + return NewColorable(os.Stdout) +} + +// NewColorableStderr return new instance of Writer which handle escape sequence for stderr. +func NewColorableStderr() io.Writer { + return NewColorable(os.Stderr) +} + +var color256 = map[int]int{ + 0: 0x000000, + 1: 0x800000, + 2: 0x008000, + 3: 0x808000, + 4: 0x000080, + 5: 0x800080, + 6: 0x008080, + 7: 0xc0c0c0, + 8: 0x808080, + 9: 0xff0000, + 10: 0x00ff00, + 11: 0xffff00, + 12: 0x0000ff, + 13: 0xff00ff, + 14: 0x00ffff, + 15: 0xffffff, + 16: 0x000000, + 17: 0x00005f, + 18: 0x000087, + 19: 0x0000af, + 20: 0x0000d7, + 21: 0x0000ff, + 22: 0x005f00, + 23: 0x005f5f, + 24: 0x005f87, + 25: 0x005faf, + 26: 0x005fd7, + 27: 0x005fff, + 28: 0x008700, + 29: 0x00875f, + 30: 0x008787, + 31: 0x0087af, + 32: 0x0087d7, + 33: 0x0087ff, + 34: 0x00af00, + 35: 0x00af5f, + 36: 0x00af87, + 37: 0x00afaf, + 38: 0x00afd7, + 39: 0x00afff, + 40: 0x00d700, + 41: 0x00d75f, + 42: 0x00d787, + 43: 0x00d7af, + 44: 0x00d7d7, + 45: 0x00d7ff, + 46: 0x00ff00, + 47: 0x00ff5f, + 48: 0x00ff87, + 49: 0x00ffaf, + 50: 0x00ffd7, + 51: 0x00ffff, + 52: 0x5f0000, + 53: 0x5f005f, + 54: 0x5f0087, + 55: 0x5f00af, + 56: 0x5f00d7, + 57: 0x5f00ff, + 58: 0x5f5f00, + 59: 0x5f5f5f, + 60: 0x5f5f87, + 61: 0x5f5faf, + 62: 0x5f5fd7, + 63: 0x5f5fff, + 64: 0x5f8700, + 65: 0x5f875f, + 66: 0x5f8787, + 67: 0x5f87af, + 68: 0x5f87d7, + 69: 0x5f87ff, + 70: 0x5faf00, + 71: 0x5faf5f, + 72: 0x5faf87, + 73: 0x5fafaf, + 74: 0x5fafd7, + 75: 0x5fafff, + 76: 0x5fd700, + 77: 0x5fd75f, + 78: 0x5fd787, + 79: 0x5fd7af, + 80: 0x5fd7d7, + 81: 0x5fd7ff, + 82: 0x5fff00, + 83: 0x5fff5f, + 84: 0x5fff87, + 85: 0x5fffaf, + 86: 0x5fffd7, + 87: 0x5fffff, + 88: 0x870000, + 89: 0x87005f, + 90: 0x870087, + 91: 0x8700af, + 92: 0x8700d7, + 93: 0x8700ff, + 94: 0x875f00, + 95: 0x875f5f, + 96: 0x875f87, + 97: 0x875faf, + 98: 0x875fd7, + 99: 0x875fff, + 100: 0x878700, + 101: 0x87875f, + 102: 0x878787, + 103: 0x8787af, + 104: 0x8787d7, + 105: 0x8787ff, + 106: 0x87af00, + 107: 0x87af5f, + 108: 0x87af87, + 109: 0x87afaf, + 110: 0x87afd7, + 111: 0x87afff, + 112: 0x87d700, + 113: 0x87d75f, + 114: 0x87d787, + 115: 0x87d7af, + 116: 0x87d7d7, + 117: 0x87d7ff, + 118: 0x87ff00, + 119: 0x87ff5f, + 120: 0x87ff87, + 121: 0x87ffaf, + 122: 0x87ffd7, + 123: 0x87ffff, + 124: 0xaf0000, + 125: 0xaf005f, + 126: 0xaf0087, + 127: 0xaf00af, + 128: 0xaf00d7, + 129: 0xaf00ff, + 130: 0xaf5f00, + 131: 0xaf5f5f, + 132: 0xaf5f87, + 133: 0xaf5faf, + 134: 0xaf5fd7, + 135: 0xaf5fff, + 136: 0xaf8700, + 137: 0xaf875f, + 138: 0xaf8787, + 139: 0xaf87af, + 140: 0xaf87d7, + 141: 0xaf87ff, + 142: 0xafaf00, + 143: 0xafaf5f, + 144: 0xafaf87, + 145: 0xafafaf, + 146: 0xafafd7, + 147: 0xafafff, + 148: 0xafd700, + 149: 0xafd75f, + 150: 0xafd787, + 151: 0xafd7af, + 152: 0xafd7d7, + 153: 0xafd7ff, + 154: 0xafff00, + 155: 0xafff5f, + 156: 0xafff87, + 157: 0xafffaf, + 158: 0xafffd7, + 159: 0xafffff, + 160: 0xd70000, + 161: 0xd7005f, + 162: 0xd70087, + 163: 0xd700af, + 164: 0xd700d7, + 165: 0xd700ff, + 166: 0xd75f00, + 167: 0xd75f5f, + 168: 0xd75f87, + 169: 0xd75faf, + 170: 0xd75fd7, + 171: 0xd75fff, + 172: 0xd78700, + 173: 0xd7875f, + 174: 0xd78787, + 175: 0xd787af, + 176: 0xd787d7, + 177: 0xd787ff, + 178: 0xd7af00, + 179: 0xd7af5f, + 180: 0xd7af87, + 181: 0xd7afaf, + 182: 0xd7afd7, + 183: 0xd7afff, + 184: 0xd7d700, + 185: 0xd7d75f, + 186: 0xd7d787, + 187: 0xd7d7af, + 188: 0xd7d7d7, + 189: 0xd7d7ff, + 190: 0xd7ff00, + 191: 0xd7ff5f, + 192: 0xd7ff87, + 193: 0xd7ffaf, + 194: 0xd7ffd7, + 195: 0xd7ffff, + 196: 0xff0000, + 197: 0xff005f, + 198: 0xff0087, + 199: 0xff00af, + 200: 0xff00d7, + 201: 0xff00ff, + 202: 0xff5f00, + 203: 0xff5f5f, + 204: 0xff5f87, + 205: 0xff5faf, + 206: 0xff5fd7, + 207: 0xff5fff, + 208: 0xff8700, + 209: 0xff875f, + 210: 0xff8787, + 211: 0xff87af, + 212: 0xff87d7, + 213: 0xff87ff, + 214: 0xffaf00, + 215: 0xffaf5f, + 216: 0xffaf87, + 217: 0xffafaf, + 218: 0xffafd7, + 219: 0xffafff, + 220: 0xffd700, + 221: 0xffd75f, + 222: 0xffd787, + 223: 0xffd7af, + 224: 0xffd7d7, + 225: 0xffd7ff, + 226: 0xffff00, + 227: 0xffff5f, + 228: 0xffff87, + 229: 0xffffaf, + 230: 0xffffd7, + 231: 0xffffff, + 232: 0x080808, + 233: 0x121212, + 234: 0x1c1c1c, + 235: 0x262626, + 236: 0x303030, + 237: 0x3a3a3a, + 238: 0x444444, + 239: 0x4e4e4e, + 240: 0x585858, + 241: 0x626262, + 242: 0x6c6c6c, + 243: 0x767676, + 244: 0x808080, + 245: 0x8a8a8a, + 246: 0x949494, + 247: 0x9e9e9e, + 248: 0xa8a8a8, + 249: 0xb2b2b2, + 250: 0xbcbcbc, + 251: 0xc6c6c6, + 252: 0xd0d0d0, + 253: 0xdadada, + 254: 0xe4e4e4, + 255: 0xeeeeee, +} + +// Write write data on console +func (w *Writer) Write(data []byte) (n int, err error) { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + break loop + } + + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + var m byte + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + m = c + break + } + buf.Write([]byte(string(c))) + } + + var csbi consoleScreenBufferInfo + switch m { + case 'A': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'B': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'C': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'D': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + if n, err = strconv.Atoi(buf.String()); err == nil { + var csbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + } + case 'E': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y += short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'F': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = 0 + csbi.cursorPosition.y -= short(n) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'G': + n, err = strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + csbi.cursorPosition.x = short(n - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'H': + token := strings.Split(buf.String(), ";") + if len(token) != 2 { + continue + } + n1, err := strconv.Atoi(token[0]) + if err != nil { + continue + } + n2, err := strconv.Atoi(token[1]) + if err != nil { + continue + } + csbi.cursorPosition.x = short(n2 - 1) + csbi.cursorPosition.y = short(n1 - 1) + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) + case 'J': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'K': + n, err := strconv.Atoi(buf.String()) + if err != nil { + continue + } + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + var cursor coord + switch n { + case 0: + cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} + case 1: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + case 2: + cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} + } + var count, written dword + count = dword(csbi.size.x - csbi.cursorPosition.x) + procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) + case 'm': + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + attr := csbi.attributes + cs := buf.String() + if cs == "" { + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) + continue + } + token := strings.Split(cs, ";") + for i := 0; i < len(token); i++ { + ns := token[i] + if n, err = strconv.Atoi(ns); err == nil { + switch { + case n == 0 || n == 100: + attr = w.oldattr + case 1 <= n && n <= 5: + attr |= foregroundIntensity + case n == 7: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 22 == n || n == 25 || n == 25: + attr |= foregroundIntensity + case n == 27: + attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) + case 30 <= n && n <= 37: + attr &= backgroundMask + if (n-30)&1 != 0 { + attr |= foregroundRed + } + if (n-30)&2 != 0 { + attr |= foregroundGreen + } + if (n-30)&4 != 0 { + attr |= foregroundBlue + } + case n == 38: // set foreground color. + if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256foreAttr == nil { + n256setup() + } + attr &= backgroundMask + attr |= n256foreAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & backgroundMask) + } + case n == 39: // reset foreground color. + attr &= backgroundMask + attr |= w.oldattr & foregroundMask + case 40 <= n && n <= 47: + attr &= foregroundMask + if (n-40)&1 != 0 { + attr |= backgroundRed + } + if (n-40)&2 != 0 { + attr |= backgroundGreen + } + if (n-40)&4 != 0 { + attr |= backgroundBlue + } + case n == 48: // set background color. + if i < len(token)-2 && token[i+1] == "5" { + if n256, err := strconv.Atoi(token[i+2]); err == nil { + if n256backAttr == nil { + n256setup() + } + attr &= foregroundMask + attr |= n256backAttr[n256] + i += 2 + } + } else { + attr = attr & (w.oldattr & foregroundMask) + } + case n == 49: // reset foreground color. + attr &= foregroundMask + attr |= w.oldattr & backgroundMask + case 90 <= n && n <= 97: + attr = (attr & backgroundMask) + attr |= foregroundIntensity + if (n-90)&1 != 0 { + attr |= foregroundRed + } + if (n-90)&2 != 0 { + attr |= foregroundGreen + } + if (n-90)&4 != 0 { + attr |= foregroundBlue + } + case 100 <= n && n <= 107: + attr = (attr & foregroundMask) + attr |= backgroundIntensity + if (n-100)&1 != 0 { + attr |= backgroundRed + } + if (n-100)&2 != 0 { + attr |= backgroundGreen + } + if (n-100)&4 != 0 { + attr |= backgroundBlue + } + } + procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) + } + } + case 'h': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 1 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + case 'l': + cs := buf.String() + if cs == "?25" { + var ci consoleCursorInfo + procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + ci.visible = 0 + procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci))) + } + case 's': + procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + w.oldpos = csbi.cursorPosition + case 'u': + procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) + } + } + return len(data) - w.lastbuf.Len(), nil +} + +type consoleColor struct { + rgb int + red bool + green bool + blue bool + intensity bool +} + +func (c consoleColor) foregroundAttr() (attr word) { + if c.red { + attr |= foregroundRed + } + if c.green { + attr |= foregroundGreen + } + if c.blue { + attr |= foregroundBlue + } + if c.intensity { + attr |= foregroundIntensity + } + return +} + +func (c consoleColor) backgroundAttr() (attr word) { + if c.red { + attr |= backgroundRed + } + if c.green { + attr |= backgroundGreen + } + if c.blue { + attr |= backgroundBlue + } + if c.intensity { + attr |= backgroundIntensity + } + return +} + +var color16 = []consoleColor{ + consoleColor{0x000000, false, false, false, false}, + consoleColor{0x000080, false, false, true, false}, + consoleColor{0x008000, false, true, false, false}, + consoleColor{0x008080, false, true, true, false}, + consoleColor{0x800000, true, false, false, false}, + consoleColor{0x800080, true, false, true, false}, + consoleColor{0x808000, true, true, false, false}, + consoleColor{0xc0c0c0, true, true, true, false}, + consoleColor{0x808080, false, false, false, true}, + consoleColor{0x0000ff, false, false, true, true}, + consoleColor{0x00ff00, false, true, false, true}, + consoleColor{0x00ffff, false, true, true, true}, + consoleColor{0xff0000, true, false, false, true}, + consoleColor{0xff00ff, true, false, true, true}, + consoleColor{0xffff00, true, true, false, true}, + consoleColor{0xffffff, true, true, true, true}, +} + +type hsv struct { + h, s, v float32 +} + +func (a hsv) dist(b hsv) float32 { + dh := a.h - b.h + switch { + case dh > 0.5: + dh = 1 - dh + case dh < -0.5: + dh = -1 - dh + } + ds := a.s - b.s + dv := a.v - b.v + return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) +} + +func toHSV(rgb int) hsv { + r, g, b := float32((rgb&0xFF0000)>>16)/256.0, + float32((rgb&0x00FF00)>>8)/256.0, + float32(rgb&0x0000FF)/256.0 + min, max := minmax3f(r, g, b) + h := max - min + if h > 0 { + if max == r { + h = (g - b) / h + if h < 0 { + h += 6 + } + } else if max == g { + h = 2 + (b-r)/h + } else { + h = 4 + (r-g)/h + } + } + h /= 6.0 + s := max - min + if max != 0 { + s /= max + } + v := max + return hsv{h: h, s: s, v: v} +} + +type hsvTable []hsv + +func toHSVTable(rgbTable []consoleColor) hsvTable { + t := make(hsvTable, len(rgbTable)) + for i, c := range rgbTable { + t[i] = toHSV(c.rgb) + } + return t +} + +func (t hsvTable) find(rgb int) consoleColor { + hsv := toHSV(rgb) + n := 7 + l := float32(5.0) + for i, p := range t { + d := hsv.dist(p) + if d < l { + l, n = d, i + } + } + return color16[n] +} + +func minmax3f(a, b, c float32) (min, max float32) { + if a < b { + if b < c { + return a, c + } else if a < c { + return a, b + } else { + return c, b + } + } else { + if a < c { + return b, c + } else if b < c { + return b, a + } else { + return c, a + } + } +} + +var n256foreAttr []word +var n256backAttr []word + +func n256setup() { + n256foreAttr = make([]word, 256) + n256backAttr = make([]word, 256) + t := toHSVTable(color16) + for i, rgb := range color256 { + c := t.find(rgb) + n256foreAttr[i] = c.foregroundAttr() + n256backAttr[i] = c.backgroundAttr() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/noncolorable.go new file mode 100644 index 0000000..ca588c7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-colorable/noncolorable.go @@ -0,0 +1,61 @@ +package colorable + +import ( + "bytes" + "io" +) + +// NonColorable hold writer but remove escape sequence. +type NonColorable struct { + out io.Writer + lastbuf bytes.Buffer +} + +// NewNonColorable return new instance of Writer which remove escape sequence from Writer. +func NewNonColorable(w io.Writer) io.Writer { + return &NonColorable{out: w} +} + +// Write write data on console +func (w *NonColorable) Write(data []byte) (n int, err error) { + er := bytes.NewReader(data) + var bw [1]byte +loop: + for { + c1, err := er.ReadByte() + if err != nil { + break loop + } + if c1 != 0x1b { + bw[0] = c1 + w.out.Write(bw[:]) + continue + } + c2, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + break loop + } + if c2 != 0x5b { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + continue + } + + var buf bytes.Buffer + for { + c, err := er.ReadByte() + if err != nil { + w.lastbuf.WriteByte(c1) + w.lastbuf.WriteByte(c2) + w.lastbuf.Write(buf.Bytes()) + break loop + } + if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { + break + } + buf.Write([]byte(string(c))) + } + } + return len(data) - w.lastbuf.Len(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 0000000..65dc692 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO + +MIT License (Expat) + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 0000000..8e4365f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,47 @@ +# go-isatty + +[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty) [![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 0000000..17d4f90 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_appengine.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_appengine.go new file mode 100644 index 0000000..83c5887 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_appengine.go @@ -0,0 +1,9 @@ +// +build appengine + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on on appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 0000000..42f2514 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,18 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TIOCGETA + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_linux.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_linux.go new file mode 100644 index 0000000..9d24bac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_linux.go @@ -0,0 +1,18 @@ +// +build linux +// +build !appengine + +package isatty + +import ( + "syscall" + "unsafe" +) + +const ioctlReadTermios = syscall.TCGETS + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_not_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_not_windows.go new file mode 100644 index 0000000..616832d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_not_windows.go @@ -0,0 +1,9 @@ +// +build !windows appengine + +package isatty + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 0000000..1f0c6bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,16 @@ +// +build solaris +// +build !appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func IsTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 0000000..af51cbc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,94 @@ +// +build windows +// +build !appengine + +package isatty + +import ( + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + fileNameInfo uintptr = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && token[0] != `\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + return false + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 0000000..91b5cef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/README.mkd b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/README.mkd new file mode 100644 index 0000000..66663a9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/README.mkd @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 0000000..2164497 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,1223 @@ +package runewidth + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth = IsEastAsian() + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{EastAsianWidth} +) + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + // func (t table) IncludesRune(r rune) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) / 2 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return true + } + } + + return false +} + +var private = table{ + {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, +} + +var nonprint = table{ + {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, + {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, + {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, + {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, + {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711}, + {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, + {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, + {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1}, + {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F}, + {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983}, + {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, + {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63}, + {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03}, + {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83}, + {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, + {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3}, + {0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63}, + {0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, + {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, + {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, + {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F}, + {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E}, + {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, + {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, + {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F}, + {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, + {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, + {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, + {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B}, + {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F}, + {0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, + {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, + {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2}, + {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4}, + {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, + {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, + {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, + {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881}, + {0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D}, + {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, + {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, + {0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, + {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, + {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, + {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E}, + {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, + {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03}, + {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, + {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002}, + {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, + {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173}, + {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC}, + {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA}, + {0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, + {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5}, + {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, + {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36}, + {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, + {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E}, + {0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169}, + {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, + {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36}, + {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, + {0xE0100, 0xE01EF}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, + {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, + {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, + {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, + {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, + {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, + {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC}, + {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004}, + {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, + {0x1F250, 0x1F251}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, + {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, + {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, + {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, + {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, + {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, + {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, + {0x1F6D0, 0x1F6D2}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6}, + {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930}, + {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E}, + {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD}, + {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} + +var emoji = table{ + {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F321}, {0x1F324, 0x1F32C}, + {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F396, 0x1F397}, + {0x1F399, 0x1F39B}, {0x1F39E, 0x1F39F}, {0x1F3CB, 0x1F3CE}, + {0x1F3D4, 0x1F3DF}, {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3F7}, + {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FD}, + {0x1F549, 0x1F54A}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F579}, + {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, + {0x1F5A5, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, + {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, + {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, + {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, + {0x1F5FA, 0x1F5FA}, {0x1F6CB, 0x1F6CF}, {0x1F6E0, 0x1F6E5}, + {0x1F6E9, 0x1F6E9}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F3}, +} + +var notassigned = table{ + {0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B}, + {0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530}, + {0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588}, + {0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF}, + {0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D}, + {0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF}, + {0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F}, + {0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5}, + {0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E}, + {0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1}, + {0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6}, + {0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB}, + {0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00}, + {0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12}, + {0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34}, + {0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D}, + {0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50}, + {0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65}, + {0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E}, + {0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1}, + {0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6}, + {0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF}, + {0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00}, + {0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12}, + {0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34}, + {0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A}, + {0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E}, + {0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84}, + {0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98}, + {0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2}, + {0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD}, + {0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF}, + {0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF}, + {0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11}, + {0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45}, + {0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57}, + {0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77}, + {0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91}, + {0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB}, + {0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4}, + {0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5}, + {0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04}, + {0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C}, + {0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53}, + {0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84}, + {0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC}, + {0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE}, + {0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5}, + {0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E}, + {0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86}, + {0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93}, + {0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4}, + {0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC}, + {0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5}, + {0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB}, + {0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70}, + {0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD}, + {0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC}, + {0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F}, + {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F}, + {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1}, + {0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1}, + {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311}, + {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F}, + {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF}, + {0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D}, + {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F}, + {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F}, + {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF}, + {0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F}, + {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F}, + {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943}, + {0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF}, + {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D}, + {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F}, + {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF}, + {0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB}, + {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF}, + {0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF}, + {0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F}, + {0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58}, + {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E}, + {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5}, + {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1}, + {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065}, + {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F}, + {0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F}, + {0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F}, + {0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC}, + {0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF}, + {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8}, + {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F}, + {0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F}, + {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7}, + {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF}, + {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F}, + {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF}, + {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098}, + {0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F}, + {0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F}, + {0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF}, + {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F}, + {0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6}, + {0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F}, + {0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF}, + {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE}, + {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F}, + {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA}, + {0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10}, + {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F}, + {0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF}, + {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF}, + {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12}, + {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D}, + {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45}, + {0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91}, + {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F}, + {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F}, + {0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00}, + {0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1}, + {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7}, + {0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C}, + {0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E}, + {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF}, + {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F}, + {0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F}, + {0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF}, + {0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F}, + {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF}, + {0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7}, + {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E}, + {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F}, + {0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809}, + {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E}, + {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF}, + {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E}, + {0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB}, + {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, + {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37}, + {0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F}, + {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF}, + {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77}, + {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF}, + {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9}, + {0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051}, + {0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF}, + {0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F}, + {0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0}, + {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F}, + {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E}, + {0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF}, + {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E}, + {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331}, + {0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346}, + {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356}, + {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F}, + {0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C}, + {0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F}, + {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F}, + {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF}, + {0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F}, + {0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF}, + {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37}, + {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91}, + {0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF}, + {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF}, + {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F}, + {0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF}, + {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F}, + {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C}, + {0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E}, + {0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF}, + {0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F}, + {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B}, + {0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128}, + {0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F}, + {0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D}, + {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8}, + {0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC}, + {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C}, + {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A}, + {0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549}, + {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD}, + {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF}, + {0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022}, + {0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6}, + {0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D}, + {0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20}, + {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28}, + {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A}, + {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48}, + {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50}, + {0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58}, + {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E}, + {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66}, + {0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78}, + {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A}, + {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA}, + {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F}, + {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, + {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F}, + {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5}, + {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F}, + {0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF}, + {0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, + {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, + {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F}, + {0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F}, + {0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF}, + {0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F}, + {0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000}, + {0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF}, + {0xFFFFE, 0xFFFFF}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x007F}, {0x0080, 0x009F}, + {0x00A0, 0x00A0}, {0x00A9, 0x00A9}, {0x00AB, 0x00AB}, + {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, {0x00C0, 0x00C5}, + {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, {0x00D9, 0x00DD}, + {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, {0x00EB, 0x00EB}, + {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, {0x00F4, 0x00F6}, + {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, {0x00FF, 0x00FF}, + {0x0100, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, + {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, + {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, + {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, + {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, + {0x016C, 0x017F}, {0x0180, 0x01BA}, {0x01BB, 0x01BB}, + {0x01BC, 0x01BF}, {0x01C0, 0x01C3}, {0x01C4, 0x01CD}, + {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, + {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, + {0x01DB, 0x01DB}, {0x01DD, 0x024F}, {0x0250, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x0293}, {0x0294, 0x0294}, + {0x0295, 0x02AF}, {0x02B0, 0x02C1}, {0x02C2, 0x02C3}, + {0x02C5, 0x02C5}, {0x02C6, 0x02C6}, {0x02C8, 0x02C8}, + {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D1}, + {0x02D2, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02E4}, {0x02E5, 0x02EB}, {0x02EC, 0x02EC}, + {0x02ED, 0x02ED}, {0x02EE, 0x02EE}, {0x02EF, 0x02FF}, + {0x0370, 0x0373}, {0x0374, 0x0374}, {0x0375, 0x0375}, + {0x0376, 0x0377}, {0x037A, 0x037A}, {0x037B, 0x037D}, + {0x037E, 0x037E}, {0x037F, 0x037F}, {0x0384, 0x0385}, + {0x0386, 0x0386}, {0x0387, 0x0387}, {0x0388, 0x038A}, + {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, + {0x03C2, 0x03C2}, {0x03CA, 0x03F5}, {0x03F6, 0x03F6}, + {0x03F7, 0x03FF}, {0x0400, 0x0400}, {0x0402, 0x040F}, + {0x0450, 0x0450}, {0x0452, 0x0481}, {0x0482, 0x0482}, + {0x0483, 0x0487}, {0x0488, 0x0489}, {0x048A, 0x04FF}, + {0x0500, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x0559}, + {0x055A, 0x055F}, {0x0561, 0x0587}, {0x0589, 0x0589}, + {0x058A, 0x058A}, {0x058D, 0x058E}, {0x058F, 0x058F}, + {0x0591, 0x05BD}, {0x05BE, 0x05BE}, {0x05BF, 0x05BF}, + {0x05C0, 0x05C0}, {0x05C1, 0x05C2}, {0x05C3, 0x05C3}, + {0x05C4, 0x05C5}, {0x05C6, 0x05C6}, {0x05C7, 0x05C7}, + {0x05D0, 0x05EA}, {0x05F0, 0x05F2}, {0x05F3, 0x05F4}, + {0x0600, 0x0605}, {0x0606, 0x0608}, {0x0609, 0x060A}, + {0x060B, 0x060B}, {0x060C, 0x060D}, {0x060E, 0x060F}, + {0x0610, 0x061A}, {0x061B, 0x061B}, {0x061C, 0x061C}, + {0x061E, 0x061F}, {0x0620, 0x063F}, {0x0640, 0x0640}, + {0x0641, 0x064A}, {0x064B, 0x065F}, {0x0660, 0x0669}, + {0x066A, 0x066D}, {0x066E, 0x066F}, {0x0670, 0x0670}, + {0x0671, 0x06D3}, {0x06D4, 0x06D4}, {0x06D5, 0x06D5}, + {0x06D6, 0x06DC}, {0x06DD, 0x06DD}, {0x06DE, 0x06DE}, + {0x06DF, 0x06E4}, {0x06E5, 0x06E6}, {0x06E7, 0x06E8}, + {0x06E9, 0x06E9}, {0x06EA, 0x06ED}, {0x06EE, 0x06EF}, + {0x06F0, 0x06F9}, {0x06FA, 0x06FC}, {0x06FD, 0x06FE}, + {0x06FF, 0x06FF}, {0x0700, 0x070D}, {0x070F, 0x070F}, + {0x0710, 0x0710}, {0x0711, 0x0711}, {0x0712, 0x072F}, + {0x0730, 0x074A}, {0x074D, 0x074F}, {0x0750, 0x077F}, + {0x0780, 0x07A5}, {0x07A6, 0x07B0}, {0x07B1, 0x07B1}, + {0x07C0, 0x07C9}, {0x07CA, 0x07EA}, {0x07EB, 0x07F3}, + {0x07F4, 0x07F5}, {0x07F6, 0x07F6}, {0x07F7, 0x07F9}, + {0x07FA, 0x07FA}, {0x0800, 0x0815}, {0x0816, 0x0819}, + {0x081A, 0x081A}, {0x081B, 0x0823}, {0x0824, 0x0824}, + {0x0825, 0x0827}, {0x0828, 0x0828}, {0x0829, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x0858}, {0x0859, 0x085B}, + {0x085E, 0x085E}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, + {0x08D4, 0x08E1}, {0x08E2, 0x08E2}, {0x08E3, 0x08FF}, + {0x0900, 0x0902}, {0x0903, 0x0903}, {0x0904, 0x0939}, + {0x093A, 0x093A}, {0x093B, 0x093B}, {0x093C, 0x093C}, + {0x093D, 0x093D}, {0x093E, 0x0940}, {0x0941, 0x0948}, + {0x0949, 0x094C}, {0x094D, 0x094D}, {0x094E, 0x094F}, + {0x0950, 0x0950}, {0x0951, 0x0957}, {0x0958, 0x0961}, + {0x0962, 0x0963}, {0x0964, 0x0965}, {0x0966, 0x096F}, + {0x0970, 0x0970}, {0x0971, 0x0971}, {0x0972, 0x097F}, + {0x0980, 0x0980}, {0x0981, 0x0981}, {0x0982, 0x0983}, + {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, + {0x09BC, 0x09BC}, {0x09BD, 0x09BD}, {0x09BE, 0x09C0}, + {0x09C1, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CC}, + {0x09CD, 0x09CD}, {0x09CE, 0x09CE}, {0x09D7, 0x09D7}, + {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09E2, 0x09E3}, + {0x09E6, 0x09EF}, {0x09F0, 0x09F1}, {0x09F2, 0x09F3}, + {0x09F4, 0x09F9}, {0x09FA, 0x09FA}, {0x09FB, 0x09FB}, + {0x0A01, 0x0A02}, {0x0A03, 0x0A03}, {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, + {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A40}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A6F}, + {0x0A70, 0x0A71}, {0x0A72, 0x0A74}, {0x0A75, 0x0A75}, + {0x0A81, 0x0A82}, {0x0A83, 0x0A83}, {0x0A85, 0x0A8D}, + {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, + {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0ABC}, + {0x0ABD, 0x0ABD}, {0x0ABE, 0x0AC0}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0AC9, 0x0AC9}, {0x0ACB, 0x0ACC}, + {0x0ACD, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE1}, + {0x0AE2, 0x0AE3}, {0x0AE6, 0x0AEF}, {0x0AF0, 0x0AF0}, + {0x0AF1, 0x0AF1}, {0x0AF9, 0x0AF9}, {0x0B01, 0x0B01}, + {0x0B02, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B35, 0x0B39}, {0x0B3C, 0x0B3C}, {0x0B3D, 0x0B3D}, + {0x0B3E, 0x0B3E}, {0x0B3F, 0x0B3F}, {0x0B40, 0x0B40}, + {0x0B41, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4C}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B57, 0x0B57}, + {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B62, 0x0B63}, + {0x0B66, 0x0B6F}, {0x0B70, 0x0B70}, {0x0B71, 0x0B71}, + {0x0B72, 0x0B77}, {0x0B82, 0x0B82}, {0x0B83, 0x0B83}, + {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, + {0x0BBE, 0x0BBF}, {0x0BC0, 0x0BC0}, {0x0BC1, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCC}, {0x0BCD, 0x0BCD}, + {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BEF}, + {0x0BF0, 0x0BF2}, {0x0BF3, 0x0BF8}, {0x0BF9, 0x0BF9}, + {0x0BFA, 0x0BFA}, {0x0C00, 0x0C00}, {0x0C01, 0x0C03}, + {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, + {0x0C2A, 0x0C39}, {0x0C3D, 0x0C3D}, {0x0C3E, 0x0C40}, + {0x0C41, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C61}, + {0x0C62, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C78, 0x0C7E}, + {0x0C7F, 0x0C7F}, {0x0C80, 0x0C80}, {0x0C81, 0x0C81}, + {0x0C82, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CBC}, {0x0CBD, 0x0CBD}, {0x0CBE, 0x0CBE}, + {0x0CBF, 0x0CBF}, {0x0CC0, 0x0CC4}, {0x0CC6, 0x0CC6}, + {0x0CC7, 0x0CC8}, {0x0CCA, 0x0CCB}, {0x0CCC, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, + {0x0CE2, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, + {0x0D01, 0x0D01}, {0x0D02, 0x0D03}, {0x0D05, 0x0D0C}, + {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A}, {0x0D3D, 0x0D3D}, + {0x0D3E, 0x0D40}, {0x0D41, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4C}, {0x0D4D, 0x0D4D}, {0x0D4E, 0x0D4E}, + {0x0D4F, 0x0D4F}, {0x0D54, 0x0D56}, {0x0D57, 0x0D57}, + {0x0D58, 0x0D5E}, {0x0D5F, 0x0D61}, {0x0D62, 0x0D63}, + {0x0D66, 0x0D6F}, {0x0D70, 0x0D78}, {0x0D79, 0x0D79}, + {0x0D7A, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, + {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, + {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD1}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, + {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF3}, {0x0DF4, 0x0DF4}, + {0x0E01, 0x0E30}, {0x0E31, 0x0E31}, {0x0E32, 0x0E33}, + {0x0E34, 0x0E3A}, {0x0E3F, 0x0E3F}, {0x0E40, 0x0E45}, + {0x0E46, 0x0E46}, {0x0E47, 0x0E4E}, {0x0E4F, 0x0E4F}, + {0x0E50, 0x0E59}, {0x0E5A, 0x0E5B}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, + {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, + {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EB0}, {0x0EB1, 0x0EB1}, + {0x0EB2, 0x0EB3}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, + {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, + {0x0F00, 0x0F00}, {0x0F01, 0x0F03}, {0x0F04, 0x0F12}, + {0x0F13, 0x0F13}, {0x0F14, 0x0F14}, {0x0F15, 0x0F17}, + {0x0F18, 0x0F19}, {0x0F1A, 0x0F1F}, {0x0F20, 0x0F29}, + {0x0F2A, 0x0F33}, {0x0F34, 0x0F34}, {0x0F35, 0x0F35}, + {0x0F36, 0x0F36}, {0x0F37, 0x0F37}, {0x0F38, 0x0F38}, + {0x0F39, 0x0F39}, {0x0F3A, 0x0F3A}, {0x0F3B, 0x0F3B}, + {0x0F3C, 0x0F3C}, {0x0F3D, 0x0F3D}, {0x0F3E, 0x0F3F}, + {0x0F40, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F7E}, + {0x0F7F, 0x0F7F}, {0x0F80, 0x0F84}, {0x0F85, 0x0F85}, + {0x0F86, 0x0F87}, {0x0F88, 0x0F8C}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FBE, 0x0FC5}, {0x0FC6, 0x0FC6}, + {0x0FC7, 0x0FCC}, {0x0FCE, 0x0FCF}, {0x0FD0, 0x0FD4}, + {0x0FD5, 0x0FD8}, {0x0FD9, 0x0FDA}, {0x1000, 0x102A}, + {0x102B, 0x102C}, {0x102D, 0x1030}, {0x1031, 0x1031}, + {0x1032, 0x1037}, {0x1038, 0x1038}, {0x1039, 0x103A}, + {0x103B, 0x103C}, {0x103D, 0x103E}, {0x103F, 0x103F}, + {0x1040, 0x1049}, {0x104A, 0x104F}, {0x1050, 0x1055}, + {0x1056, 0x1057}, {0x1058, 0x1059}, {0x105A, 0x105D}, + {0x105E, 0x1060}, {0x1061, 0x1061}, {0x1062, 0x1064}, + {0x1065, 0x1066}, {0x1067, 0x106D}, {0x106E, 0x1070}, + {0x1071, 0x1074}, {0x1075, 0x1081}, {0x1082, 0x1082}, + {0x1083, 0x1084}, {0x1085, 0x1086}, {0x1087, 0x108C}, + {0x108D, 0x108D}, {0x108E, 0x108E}, {0x108F, 0x108F}, + {0x1090, 0x1099}, {0x109A, 0x109C}, {0x109D, 0x109D}, + {0x109E, 0x109F}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7}, + {0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FB, 0x10FB}, + {0x10FC, 0x10FC}, {0x10FD, 0x10FF}, {0x1160, 0x11FF}, + {0x1200, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, + {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, + {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, + {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, + {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, + {0x1318, 0x135A}, {0x135D, 0x135F}, {0x1360, 0x1368}, + {0x1369, 0x137C}, {0x1380, 0x138F}, {0x1390, 0x1399}, + {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x1400}, + {0x1401, 0x166C}, {0x166D, 0x166E}, {0x166F, 0x167F}, + {0x1680, 0x1680}, {0x1681, 0x169A}, {0x169B, 0x169B}, + {0x169C, 0x169C}, {0x16A0, 0x16EA}, {0x16EB, 0x16ED}, + {0x16EE, 0x16F0}, {0x16F1, 0x16F8}, {0x1700, 0x170C}, + {0x170E, 0x1711}, {0x1712, 0x1714}, {0x1720, 0x1731}, + {0x1732, 0x1734}, {0x1735, 0x1736}, {0x1740, 0x1751}, + {0x1752, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, + {0x1772, 0x1773}, {0x1780, 0x17B3}, {0x17B4, 0x17B5}, + {0x17B6, 0x17B6}, {0x17B7, 0x17BD}, {0x17BE, 0x17C5}, + {0x17C6, 0x17C6}, {0x17C7, 0x17C8}, {0x17C9, 0x17D3}, + {0x17D4, 0x17D6}, {0x17D7, 0x17D7}, {0x17D8, 0x17DA}, + {0x17DB, 0x17DB}, {0x17DC, 0x17DC}, {0x17DD, 0x17DD}, + {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1805}, + {0x1806, 0x1806}, {0x1807, 0x180A}, {0x180B, 0x180D}, + {0x180E, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1842}, + {0x1843, 0x1843}, {0x1844, 0x1877}, {0x1880, 0x1884}, + {0x1885, 0x1886}, {0x1887, 0x18A8}, {0x18A9, 0x18A9}, + {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, + {0x1920, 0x1922}, {0x1923, 0x1926}, {0x1927, 0x1928}, + {0x1929, 0x192B}, {0x1930, 0x1931}, {0x1932, 0x1932}, + {0x1933, 0x1938}, {0x1939, 0x193B}, {0x1940, 0x1940}, + {0x1944, 0x1945}, {0x1946, 0x194F}, {0x1950, 0x196D}, + {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, + {0x19D0, 0x19D9}, {0x19DA, 0x19DA}, {0x19DE, 0x19DF}, + {0x19E0, 0x19FF}, {0x1A00, 0x1A16}, {0x1A17, 0x1A18}, + {0x1A19, 0x1A1A}, {0x1A1B, 0x1A1B}, {0x1A1E, 0x1A1F}, + {0x1A20, 0x1A54}, {0x1A55, 0x1A55}, {0x1A56, 0x1A56}, + {0x1A57, 0x1A57}, {0x1A58, 0x1A5E}, {0x1A60, 0x1A60}, + {0x1A61, 0x1A61}, {0x1A62, 0x1A62}, {0x1A63, 0x1A64}, + {0x1A65, 0x1A6C}, {0x1A6D, 0x1A72}, {0x1A73, 0x1A7C}, + {0x1A7F, 0x1A7F}, {0x1A80, 0x1A89}, {0x1A90, 0x1A99}, + {0x1AA0, 0x1AA6}, {0x1AA7, 0x1AA7}, {0x1AA8, 0x1AAD}, + {0x1AB0, 0x1ABD}, {0x1ABE, 0x1ABE}, {0x1B00, 0x1B03}, + {0x1B04, 0x1B04}, {0x1B05, 0x1B33}, {0x1B34, 0x1B34}, + {0x1B35, 0x1B35}, {0x1B36, 0x1B3A}, {0x1B3B, 0x1B3B}, + {0x1B3C, 0x1B3C}, {0x1B3D, 0x1B41}, {0x1B42, 0x1B42}, + {0x1B43, 0x1B44}, {0x1B45, 0x1B4B}, {0x1B50, 0x1B59}, + {0x1B5A, 0x1B60}, {0x1B61, 0x1B6A}, {0x1B6B, 0x1B73}, + {0x1B74, 0x1B7C}, {0x1B80, 0x1B81}, {0x1B82, 0x1B82}, + {0x1B83, 0x1BA0}, {0x1BA1, 0x1BA1}, {0x1BA2, 0x1BA5}, + {0x1BA6, 0x1BA7}, {0x1BA8, 0x1BA9}, {0x1BAA, 0x1BAA}, + {0x1BAB, 0x1BAD}, {0x1BAE, 0x1BAF}, {0x1BB0, 0x1BB9}, + {0x1BBA, 0x1BBF}, {0x1BC0, 0x1BE5}, {0x1BE6, 0x1BE6}, + {0x1BE7, 0x1BE7}, {0x1BE8, 0x1BE9}, {0x1BEA, 0x1BEC}, + {0x1BED, 0x1BED}, {0x1BEE, 0x1BEE}, {0x1BEF, 0x1BF1}, + {0x1BF2, 0x1BF3}, {0x1BFC, 0x1BFF}, {0x1C00, 0x1C23}, + {0x1C24, 0x1C2B}, {0x1C2C, 0x1C33}, {0x1C34, 0x1C35}, + {0x1C36, 0x1C37}, {0x1C3B, 0x1C3F}, {0x1C40, 0x1C49}, + {0x1C4D, 0x1C4F}, {0x1C50, 0x1C59}, {0x1C5A, 0x1C77}, + {0x1C78, 0x1C7D}, {0x1C7E, 0x1C7F}, {0x1C80, 0x1C88}, + {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CD2}, {0x1CD3, 0x1CD3}, + {0x1CD4, 0x1CE0}, {0x1CE1, 0x1CE1}, {0x1CE2, 0x1CE8}, + {0x1CE9, 0x1CEC}, {0x1CED, 0x1CED}, {0x1CEE, 0x1CF1}, + {0x1CF2, 0x1CF3}, {0x1CF4, 0x1CF4}, {0x1CF5, 0x1CF6}, + {0x1CF8, 0x1CF9}, {0x1D00, 0x1D2B}, {0x1D2C, 0x1D6A}, + {0x1D6B, 0x1D77}, {0x1D78, 0x1D78}, {0x1D79, 0x1D7F}, + {0x1D80, 0x1D9A}, {0x1D9B, 0x1DBF}, {0x1DC0, 0x1DF5}, + {0x1DFB, 0x1DFF}, {0x1E00, 0x1EFF}, {0x1F00, 0x1F15}, + {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBD, 0x1FBD}, {0x1FBE, 0x1FBE}, + {0x1FBF, 0x1FC1}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, + {0x1FCD, 0x1FCF}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, + {0x1FDD, 0x1FDF}, {0x1FE0, 0x1FEC}, {0x1FED, 0x1FEF}, + {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x1FFD, 0x1FFE}, + {0x2000, 0x200A}, {0x200B, 0x200F}, {0x2011, 0x2012}, + {0x2017, 0x2017}, {0x201A, 0x201A}, {0x201B, 0x201B}, + {0x201E, 0x201E}, {0x201F, 0x201F}, {0x2023, 0x2023}, + {0x2028, 0x2028}, {0x2029, 0x2029}, {0x202A, 0x202E}, + {0x202F, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, + {0x2036, 0x2038}, {0x2039, 0x2039}, {0x203A, 0x203A}, + {0x203C, 0x203D}, {0x203F, 0x2040}, {0x2041, 0x2043}, + {0x2044, 0x2044}, {0x2045, 0x2045}, {0x2046, 0x2046}, + {0x2047, 0x2051}, {0x2052, 0x2052}, {0x2053, 0x2053}, + {0x2054, 0x2054}, {0x2055, 0x205E}, {0x205F, 0x205F}, + {0x2060, 0x2064}, {0x2066, 0x206F}, {0x2070, 0x2070}, + {0x2071, 0x2071}, {0x2075, 0x2079}, {0x207A, 0x207C}, + {0x207D, 0x207D}, {0x207E, 0x207E}, {0x2080, 0x2080}, + {0x2085, 0x2089}, {0x208A, 0x208C}, {0x208D, 0x208D}, + {0x208E, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, + {0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20DC}, + {0x20DD, 0x20E0}, {0x20E1, 0x20E1}, {0x20E2, 0x20E4}, + {0x20E5, 0x20F0}, {0x2100, 0x2101}, {0x2102, 0x2102}, + {0x2104, 0x2104}, {0x2106, 0x2106}, {0x2107, 0x2107}, + {0x2108, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2114}, + {0x2115, 0x2115}, {0x2117, 0x2117}, {0x2118, 0x2118}, + {0x2119, 0x211D}, {0x211E, 0x2120}, {0x2123, 0x2123}, + {0x2124, 0x2124}, {0x2125, 0x2125}, {0x2127, 0x2127}, + {0x2128, 0x2128}, {0x2129, 0x2129}, {0x212A, 0x212A}, + {0x212C, 0x212D}, {0x212E, 0x212E}, {0x212F, 0x2134}, + {0x2135, 0x2138}, {0x2139, 0x2139}, {0x213A, 0x213B}, + {0x213C, 0x213F}, {0x2140, 0x2144}, {0x2145, 0x2149}, + {0x214A, 0x214A}, {0x214B, 0x214B}, {0x214C, 0x214D}, + {0x214E, 0x214E}, {0x214F, 0x214F}, {0x2150, 0x2152}, + {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, + {0x217A, 0x2182}, {0x2183, 0x2184}, {0x2185, 0x2188}, + {0x218A, 0x218B}, {0x219A, 0x219B}, {0x219C, 0x219F}, + {0x21A0, 0x21A0}, {0x21A1, 0x21A2}, {0x21A3, 0x21A3}, + {0x21A4, 0x21A5}, {0x21A6, 0x21A6}, {0x21A7, 0x21AD}, + {0x21AE, 0x21AE}, {0x21AF, 0x21B7}, {0x21BA, 0x21CD}, + {0x21CE, 0x21CF}, {0x21D0, 0x21D1}, {0x21D3, 0x21D3}, + {0x21D5, 0x21E6}, {0x21E8, 0x21F3}, {0x21F4, 0x21FF}, + {0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A}, + {0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214}, + {0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222}, + {0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D}, + {0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247}, + {0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F}, + {0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D}, + {0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294}, + {0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE}, + {0x22C0, 0x22FF}, {0x2300, 0x2307}, {0x2308, 0x2308}, + {0x2309, 0x2309}, {0x230A, 0x230A}, {0x230B, 0x230B}, + {0x230C, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x231F}, + {0x2320, 0x2321}, {0x2322, 0x2328}, {0x232B, 0x237B}, + {0x237C, 0x237C}, {0x237D, 0x239A}, {0x239B, 0x23B3}, + {0x23B4, 0x23DB}, {0x23DC, 0x23E1}, {0x23E2, 0x23E8}, + {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x23FE}, + {0x2400, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, + {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, + {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, + {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, + {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, + {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25F7}, + {0x25F8, 0x25FC}, {0x25FF, 0x25FF}, {0x2600, 0x2604}, + {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, + {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, + {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, + {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, + {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, + {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, + {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, + {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, + {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, + {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, + {0x2758, 0x2767}, {0x2768, 0x2768}, {0x2769, 0x2769}, + {0x276A, 0x276A}, {0x276B, 0x276B}, {0x276C, 0x276C}, + {0x276D, 0x276D}, {0x276E, 0x276E}, {0x276F, 0x276F}, + {0x2770, 0x2770}, {0x2771, 0x2771}, {0x2772, 0x2772}, + {0x2773, 0x2773}, {0x2774, 0x2774}, {0x2775, 0x2775}, + {0x2780, 0x2793}, {0x2794, 0x2794}, {0x2798, 0x27AF}, + {0x27B1, 0x27BE}, {0x27C0, 0x27C4}, {0x27C5, 0x27C5}, + {0x27C6, 0x27C6}, {0x27C7, 0x27E5}, {0x27EE, 0x27EE}, + {0x27EF, 0x27EF}, {0x27F0, 0x27FF}, {0x2800, 0x28FF}, + {0x2900, 0x297F}, {0x2980, 0x2982}, {0x2983, 0x2983}, + {0x2984, 0x2984}, {0x2987, 0x2987}, {0x2988, 0x2988}, + {0x2989, 0x2989}, {0x298A, 0x298A}, {0x298B, 0x298B}, + {0x298C, 0x298C}, {0x298D, 0x298D}, {0x298E, 0x298E}, + {0x298F, 0x298F}, {0x2990, 0x2990}, {0x2991, 0x2991}, + {0x2992, 0x2992}, {0x2993, 0x2993}, {0x2994, 0x2994}, + {0x2995, 0x2995}, {0x2996, 0x2996}, {0x2997, 0x2997}, + {0x2998, 0x2998}, {0x2999, 0x29D7}, {0x29D8, 0x29D8}, + {0x29D9, 0x29D9}, {0x29DA, 0x29DA}, {0x29DB, 0x29DB}, + {0x29DC, 0x29FB}, {0x29FC, 0x29FC}, {0x29FD, 0x29FD}, + {0x29FE, 0x29FF}, {0x2A00, 0x2AFF}, {0x2B00, 0x2B1A}, + {0x2B1D, 0x2B2F}, {0x2B30, 0x2B44}, {0x2B45, 0x2B46}, + {0x2B47, 0x2B4C}, {0x2B4D, 0x2B4F}, {0x2B51, 0x2B54}, + {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9}, + {0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF}, + {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2C7B}, + {0x2C7C, 0x2C7D}, {0x2C7E, 0x2C7F}, {0x2C80, 0x2CE4}, + {0x2CE5, 0x2CEA}, {0x2CEB, 0x2CEE}, {0x2CEF, 0x2CF1}, + {0x2CF2, 0x2CF3}, {0x2CF9, 0x2CFC}, {0x2CFD, 0x2CFD}, + {0x2CFE, 0x2CFF}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F}, + {0x2D70, 0x2D70}, {0x2D7F, 0x2D7F}, {0x2D80, 0x2D96}, + {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, + {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, + {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2DFF}, + {0x2E00, 0x2E01}, {0x2E02, 0x2E02}, {0x2E03, 0x2E03}, + {0x2E04, 0x2E04}, {0x2E05, 0x2E05}, {0x2E06, 0x2E08}, + {0x2E09, 0x2E09}, {0x2E0A, 0x2E0A}, {0x2E0B, 0x2E0B}, + {0x2E0C, 0x2E0C}, {0x2E0D, 0x2E0D}, {0x2E0E, 0x2E16}, + {0x2E17, 0x2E17}, {0x2E18, 0x2E19}, {0x2E1A, 0x2E1A}, + {0x2E1B, 0x2E1B}, {0x2E1C, 0x2E1C}, {0x2E1D, 0x2E1D}, + {0x2E1E, 0x2E1F}, {0x2E20, 0x2E20}, {0x2E21, 0x2E21}, + {0x2E22, 0x2E22}, {0x2E23, 0x2E23}, {0x2E24, 0x2E24}, + {0x2E25, 0x2E25}, {0x2E26, 0x2E26}, {0x2E27, 0x2E27}, + {0x2E28, 0x2E28}, {0x2E29, 0x2E29}, {0x2E2A, 0x2E2E}, + {0x2E2F, 0x2E2F}, {0x2E30, 0x2E39}, {0x2E3A, 0x2E3B}, + {0x2E3C, 0x2E3F}, {0x2E40, 0x2E40}, {0x2E41, 0x2E41}, + {0x2E42, 0x2E42}, {0x2E43, 0x2E44}, {0x303F, 0x303F}, + {0x4DC0, 0x4DFF}, {0xA4D0, 0xA4F7}, {0xA4F8, 0xA4FD}, + {0xA4FE, 0xA4FF}, {0xA500, 0xA60B}, {0xA60C, 0xA60C}, + {0xA60D, 0xA60F}, {0xA610, 0xA61F}, {0xA620, 0xA629}, + {0xA62A, 0xA62B}, {0xA640, 0xA66D}, {0xA66E, 0xA66E}, + {0xA66F, 0xA66F}, {0xA670, 0xA672}, {0xA673, 0xA673}, + {0xA674, 0xA67D}, {0xA67E, 0xA67E}, {0xA67F, 0xA67F}, + {0xA680, 0xA69B}, {0xA69C, 0xA69D}, {0xA69E, 0xA69F}, + {0xA6A0, 0xA6E5}, {0xA6E6, 0xA6EF}, {0xA6F0, 0xA6F1}, + {0xA6F2, 0xA6F7}, {0xA700, 0xA716}, {0xA717, 0xA71F}, + {0xA720, 0xA721}, {0xA722, 0xA76F}, {0xA770, 0xA770}, + {0xA771, 0xA787}, {0xA788, 0xA788}, {0xA789, 0xA78A}, + {0xA78B, 0xA78E}, {0xA78F, 0xA78F}, {0xA790, 0xA7AE}, + {0xA7B0, 0xA7B7}, {0xA7F7, 0xA7F7}, {0xA7F8, 0xA7F9}, + {0xA7FA, 0xA7FA}, {0xA7FB, 0xA7FF}, {0xA800, 0xA801}, + {0xA802, 0xA802}, {0xA803, 0xA805}, {0xA806, 0xA806}, + {0xA807, 0xA80A}, {0xA80B, 0xA80B}, {0xA80C, 0xA822}, + {0xA823, 0xA824}, {0xA825, 0xA826}, {0xA827, 0xA827}, + {0xA828, 0xA82B}, {0xA830, 0xA835}, {0xA836, 0xA837}, + {0xA838, 0xA838}, {0xA839, 0xA839}, {0xA840, 0xA873}, + {0xA874, 0xA877}, {0xA880, 0xA881}, {0xA882, 0xA8B3}, + {0xA8B4, 0xA8C3}, {0xA8C4, 0xA8C5}, {0xA8CE, 0xA8CF}, + {0xA8D0, 0xA8D9}, {0xA8E0, 0xA8F1}, {0xA8F2, 0xA8F7}, + {0xA8F8, 0xA8FA}, {0xA8FB, 0xA8FB}, {0xA8FC, 0xA8FC}, + {0xA8FD, 0xA8FD}, {0xA900, 0xA909}, {0xA90A, 0xA925}, + {0xA926, 0xA92D}, {0xA92E, 0xA92F}, {0xA930, 0xA946}, + {0xA947, 0xA951}, {0xA952, 0xA953}, {0xA95F, 0xA95F}, + {0xA980, 0xA982}, {0xA983, 0xA983}, {0xA984, 0xA9B2}, + {0xA9B3, 0xA9B3}, {0xA9B4, 0xA9B5}, {0xA9B6, 0xA9B9}, + {0xA9BA, 0xA9BB}, {0xA9BC, 0xA9BC}, {0xA9BD, 0xA9C0}, + {0xA9C1, 0xA9CD}, {0xA9CF, 0xA9CF}, {0xA9D0, 0xA9D9}, + {0xA9DE, 0xA9DF}, {0xA9E0, 0xA9E4}, {0xA9E5, 0xA9E5}, + {0xA9E6, 0xA9E6}, {0xA9E7, 0xA9EF}, {0xA9F0, 0xA9F9}, + {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA29, 0xAA2E}, + {0xAA2F, 0xAA30}, {0xAA31, 0xAA32}, {0xAA33, 0xAA34}, + {0xAA35, 0xAA36}, {0xAA40, 0xAA42}, {0xAA43, 0xAA43}, + {0xAA44, 0xAA4B}, {0xAA4C, 0xAA4C}, {0xAA4D, 0xAA4D}, + {0xAA50, 0xAA59}, {0xAA5C, 0xAA5F}, {0xAA60, 0xAA6F}, + {0xAA70, 0xAA70}, {0xAA71, 0xAA76}, {0xAA77, 0xAA79}, + {0xAA7A, 0xAA7A}, {0xAA7B, 0xAA7B}, {0xAA7C, 0xAA7C}, + {0xAA7D, 0xAA7D}, {0xAA7E, 0xAA7F}, {0xAA80, 0xAAAF}, + {0xAAB0, 0xAAB0}, {0xAAB1, 0xAAB1}, {0xAAB2, 0xAAB4}, + {0xAAB5, 0xAAB6}, {0xAAB7, 0xAAB8}, {0xAAB9, 0xAABD}, + {0xAABE, 0xAABF}, {0xAAC0, 0xAAC0}, {0xAAC1, 0xAAC1}, + {0xAAC2, 0xAAC2}, {0xAADB, 0xAADC}, {0xAADD, 0xAADD}, + {0xAADE, 0xAADF}, {0xAAE0, 0xAAEA}, {0xAAEB, 0xAAEB}, + {0xAAEC, 0xAAED}, {0xAAEE, 0xAAEF}, {0xAAF0, 0xAAF1}, + {0xAAF2, 0xAAF2}, {0xAAF3, 0xAAF4}, {0xAAF5, 0xAAF5}, + {0xAAF6, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, + {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, + {0xAB30, 0xAB5A}, {0xAB5B, 0xAB5B}, {0xAB5C, 0xAB5F}, + {0xAB60, 0xAB65}, {0xAB70, 0xABBF}, {0xABC0, 0xABE2}, + {0xABE3, 0xABE4}, {0xABE5, 0xABE5}, {0xABE6, 0xABE7}, + {0xABE8, 0xABE8}, {0xABE9, 0xABEA}, {0xABEB, 0xABEB}, + {0xABEC, 0xABEC}, {0xABED, 0xABED}, {0xABF0, 0xABF9}, + {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDB7F}, + {0xDB80, 0xDBFF}, {0xDC00, 0xDFFF}, {0xFB00, 0xFB06}, + {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1E, 0xFB1E}, + {0xFB1F, 0xFB28}, {0xFB29, 0xFB29}, {0xFB2A, 0xFB36}, + {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, {0xFB46, 0xFB4F}, {0xFB50, 0xFBB1}, + {0xFBB2, 0xFBC1}, {0xFBD3, 0xFD3D}, {0xFD3E, 0xFD3E}, + {0xFD3F, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, + {0xFDF0, 0xFDFB}, {0xFDFC, 0xFDFC}, {0xFDFD, 0xFDFD}, + {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFC, 0xFFFC}, + {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, + {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, + {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, + {0x10137, 0x1013F}, {0x10140, 0x10174}, {0x10175, 0x10178}, + {0x10179, 0x10189}, {0x1018A, 0x1018B}, {0x1018C, 0x1018E}, + {0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FC}, + {0x101FD, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, + {0x102E0, 0x102E0}, {0x102E1, 0x102FB}, {0x10300, 0x1031F}, + {0x10320, 0x10323}, {0x10330, 0x10340}, {0x10341, 0x10341}, + {0x10342, 0x10349}, {0x1034A, 0x1034A}, {0x10350, 0x10375}, + {0x10376, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x1039F}, + {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x103D0, 0x103D0}, + {0x103D1, 0x103D5}, {0x10400, 0x1044F}, {0x10450, 0x1047F}, + {0x10480, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, + {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, + {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, + {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, + {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, + {0x1083F, 0x1083F}, {0x10840, 0x10855}, {0x10857, 0x10857}, + {0x10858, 0x1085F}, {0x10860, 0x10876}, {0x10877, 0x10878}, + {0x10879, 0x1087F}, {0x10880, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x108FF}, + {0x10900, 0x10915}, {0x10916, 0x1091B}, {0x1091F, 0x1091F}, + {0x10920, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x1099F}, + {0x109A0, 0x109B7}, {0x109BC, 0x109BD}, {0x109BE, 0x109BF}, + {0x109C0, 0x109CF}, {0x109D2, 0x109FF}, {0x10A00, 0x10A00}, + {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, + {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x10A40, 0x10A47}, + {0x10A50, 0x10A58}, {0x10A60, 0x10A7C}, {0x10A7D, 0x10A7E}, + {0x10A7F, 0x10A7F}, {0x10A80, 0x10A9C}, {0x10A9D, 0x10A9F}, + {0x10AC0, 0x10AC7}, {0x10AC8, 0x10AC8}, {0x10AC9, 0x10AE4}, + {0x10AE5, 0x10AE6}, {0x10AEB, 0x10AEF}, {0x10AF0, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B3F}, {0x10B40, 0x10B55}, + {0x10B58, 0x10B5F}, {0x10B60, 0x10B72}, {0x10B78, 0x10B7F}, + {0x10B80, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x11000}, + {0x11001, 0x11001}, {0x11002, 0x11002}, {0x11003, 0x11037}, + {0x11038, 0x11046}, {0x11047, 0x1104D}, {0x11052, 0x11065}, + {0x11066, 0x1106F}, {0x1107F, 0x1107F}, {0x11080, 0x11081}, + {0x11082, 0x11082}, {0x11083, 0x110AF}, {0x110B0, 0x110B2}, + {0x110B3, 0x110B6}, {0x110B7, 0x110B8}, {0x110B9, 0x110BA}, + {0x110BB, 0x110BC}, {0x110BD, 0x110BD}, {0x110BE, 0x110C1}, + {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11102}, + {0x11103, 0x11126}, {0x11127, 0x1112B}, {0x1112C, 0x1112C}, + {0x1112D, 0x11134}, {0x11136, 0x1113F}, {0x11140, 0x11143}, + {0x11150, 0x11172}, {0x11173, 0x11173}, {0x11174, 0x11175}, + {0x11176, 0x11176}, {0x11180, 0x11181}, {0x11182, 0x11182}, + {0x11183, 0x111B2}, {0x111B3, 0x111B5}, {0x111B6, 0x111BE}, + {0x111BF, 0x111C0}, {0x111C1, 0x111C4}, {0x111C5, 0x111C9}, + {0x111CA, 0x111CC}, {0x111CD, 0x111CD}, {0x111D0, 0x111D9}, + {0x111DA, 0x111DA}, {0x111DB, 0x111DB}, {0x111DC, 0x111DC}, + {0x111DD, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, + {0x11213, 0x1122B}, {0x1122C, 0x1122E}, {0x1122F, 0x11231}, + {0x11232, 0x11233}, {0x11234, 0x11234}, {0x11235, 0x11235}, + {0x11236, 0x11237}, {0x11238, 0x1123D}, {0x1123E, 0x1123E}, + {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, + {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112A9, 0x112A9}, + {0x112B0, 0x112DE}, {0x112DF, 0x112DF}, {0x112E0, 0x112E2}, + {0x112E3, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11301}, + {0x11302, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133C, 0x1133C}, {0x1133D, 0x1133D}, + {0x1133E, 0x1133F}, {0x11340, 0x11340}, {0x11341, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, + {0x11357, 0x11357}, {0x1135D, 0x11361}, {0x11362, 0x11363}, + {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x11434}, + {0x11435, 0x11437}, {0x11438, 0x1143F}, {0x11440, 0x11441}, + {0x11442, 0x11444}, {0x11445, 0x11445}, {0x11446, 0x11446}, + {0x11447, 0x1144A}, {0x1144B, 0x1144F}, {0x11450, 0x11459}, + {0x1145B, 0x1145B}, {0x1145D, 0x1145D}, {0x11480, 0x114AF}, + {0x114B0, 0x114B2}, {0x114B3, 0x114B8}, {0x114B9, 0x114B9}, + {0x114BA, 0x114BA}, {0x114BB, 0x114BE}, {0x114BF, 0x114C0}, + {0x114C1, 0x114C1}, {0x114C2, 0x114C3}, {0x114C4, 0x114C5}, + {0x114C6, 0x114C6}, {0x114C7, 0x114C7}, {0x114D0, 0x114D9}, + {0x11580, 0x115AE}, {0x115AF, 0x115B1}, {0x115B2, 0x115B5}, + {0x115B8, 0x115BB}, {0x115BC, 0x115BD}, {0x115BE, 0x115BE}, + {0x115BF, 0x115C0}, {0x115C1, 0x115D7}, {0x115D8, 0x115DB}, + {0x115DC, 0x115DD}, {0x11600, 0x1162F}, {0x11630, 0x11632}, + {0x11633, 0x1163A}, {0x1163B, 0x1163C}, {0x1163D, 0x1163D}, + {0x1163E, 0x1163E}, {0x1163F, 0x11640}, {0x11641, 0x11643}, + {0x11644, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, + {0x11680, 0x116AA}, {0x116AB, 0x116AB}, {0x116AC, 0x116AC}, + {0x116AD, 0x116AD}, {0x116AE, 0x116AF}, {0x116B0, 0x116B5}, + {0x116B6, 0x116B6}, {0x116B7, 0x116B7}, {0x116C0, 0x116C9}, + {0x11700, 0x11719}, {0x1171D, 0x1171F}, {0x11720, 0x11721}, + {0x11722, 0x11725}, {0x11726, 0x11726}, {0x11727, 0x1172B}, + {0x11730, 0x11739}, {0x1173A, 0x1173B}, {0x1173C, 0x1173E}, + {0x1173F, 0x1173F}, {0x118A0, 0x118DF}, {0x118E0, 0x118E9}, + {0x118EA, 0x118F2}, {0x118FF, 0x118FF}, {0x11AC0, 0x11AF8}, + {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C2F, 0x11C2F}, + {0x11C30, 0x11C36}, {0x11C38, 0x11C3D}, {0x11C3E, 0x11C3E}, + {0x11C3F, 0x11C3F}, {0x11C40, 0x11C40}, {0x11C41, 0x11C45}, + {0x11C50, 0x11C59}, {0x11C5A, 0x11C6C}, {0x11C70, 0x11C71}, + {0x11C72, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CA9}, + {0x11CAA, 0x11CB0}, {0x11CB1, 0x11CB1}, {0x11CB2, 0x11CB3}, + {0x11CB4, 0x11CB4}, {0x11CB5, 0x11CB6}, {0x12000, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, + {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F}, + {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF4}, {0x16AF5, 0x16AF5}, + {0x16B00, 0x16B2F}, {0x16B30, 0x16B36}, {0x16B37, 0x16B3B}, + {0x16B3C, 0x16B3F}, {0x16B40, 0x16B43}, {0x16B44, 0x16B44}, + {0x16B45, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, + {0x16F50, 0x16F50}, {0x16F51, 0x16F7E}, {0x16F8F, 0x16F92}, + {0x16F93, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, + {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BC9C}, + {0x1BC9D, 0x1BC9E}, {0x1BC9F, 0x1BC9F}, {0x1BCA0, 0x1BCA3}, + {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D164}, + {0x1D165, 0x1D166}, {0x1D167, 0x1D169}, {0x1D16A, 0x1D16C}, + {0x1D16D, 0x1D172}, {0x1D173, 0x1D17A}, {0x1D17B, 0x1D182}, + {0x1D183, 0x1D184}, {0x1D185, 0x1D18B}, {0x1D18C, 0x1D1A9}, + {0x1D1AA, 0x1D1AD}, {0x1D1AE, 0x1D1E8}, {0x1D200, 0x1D241}, + {0x1D242, 0x1D244}, {0x1D245, 0x1D245}, {0x1D300, 0x1D356}, + {0x1D360, 0x1D371}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, + {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, + {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, + {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, + {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, + {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, + {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, + {0x1D6C1, 0x1D6C1}, {0x1D6C2, 0x1D6DA}, {0x1D6DB, 0x1D6DB}, + {0x1D6DC, 0x1D6FA}, {0x1D6FB, 0x1D6FB}, {0x1D6FC, 0x1D714}, + {0x1D715, 0x1D715}, {0x1D716, 0x1D734}, {0x1D735, 0x1D735}, + {0x1D736, 0x1D74E}, {0x1D74F, 0x1D74F}, {0x1D750, 0x1D76E}, + {0x1D76F, 0x1D76F}, {0x1D770, 0x1D788}, {0x1D789, 0x1D789}, + {0x1D78A, 0x1D7A8}, {0x1D7A9, 0x1D7A9}, {0x1D7AA, 0x1D7C2}, + {0x1D7C3, 0x1D7C3}, {0x1D7C4, 0x1D7CB}, {0x1D7CE, 0x1D7FF}, + {0x1D800, 0x1D9FF}, {0x1DA00, 0x1DA36}, {0x1DA37, 0x1DA3A}, + {0x1DA3B, 0x1DA6C}, {0x1DA6D, 0x1DA74}, {0x1DA75, 0x1DA75}, + {0x1DA76, 0x1DA83}, {0x1DA84, 0x1DA84}, {0x1DA85, 0x1DA86}, + {0x1DA87, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, + {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, + {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, + {0x1E8C7, 0x1E8CF}, {0x1E8D0, 0x1E8D6}, {0x1E900, 0x1E943}, + {0x1E944, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, + {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, + {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, + {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, + {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, + {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, + {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, + {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, + {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, + {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, + {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, + {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, + {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, + {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, + {0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF}, + {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, + {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, + {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, + {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, + {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, + {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, + {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA}, + {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4}, + {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, + {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{EastAsianWidth} +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + switch { + case r < 0 || r > 0x10FFFF || + inTables(r, nonprint, combining, notassigned): + return 0 + case (c.EastAsianWidth && IsAmbiguousWidth(r)) || + inTables(r, doublewidth, emoji): + return 2 + default: + return 1 + } +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + for _, r := range []rune(s) { + width += c.RuneWidth(r) + } + return width +} + +// Truncate return string truncated with w cells +func (c *Condition) Truncate(s string, w int, tail string) string { + if c.StringWidth(s) <= w { + return s + } + r := []rune(s) + tw := c.StringWidth(tail) + w -= tw + width := 0 + i := 0 + for ; i < len(r); i++ { + cw := c.RuneWidth(r[i]) + if width+cw > w { + break + } + width += cw + } + return string(r[0:i]) + tail +} + +// Wrap return string wrapped with w cells +func (c *Condition) Wrap(s string, w int) string { + width := 0 + out := "" + for _, r := range []rune(s) { + cw := RuneWidth(r) + if r == '\n' { + out += string(r) + width = 0 + continue + } else if width+cw > w { + out += "\n" + width = 0 + out += string(r) + width += cw + continue + } + out += string(r) + width += cw + } + return out +} + +// FillLeft return string filled in left by spaces in w cells +func (c *Condition) FillLeft(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return string(b) + s + } + return s +} + +// FillRight return string filled in left by spaces in w cells +func (c *Condition) FillRight(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return s + string(b) + } + return s +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func RuneWidth(r rune) int { + return DefaultCondition.RuneWidth(r) +} + +// IsAmbiguousWidth returns whether is ambiguous width or not. +func IsAmbiguousWidth(r rune) bool { + return inTables(r, private, ambiguous) +} + +// IsNeutralWidth returns whether is neutral width or not. +func IsNeutralWidth(r rune) bool { + return inTable(r, neutral) +} + +// StringWidth return width as you can see +func StringWidth(s string) (width int) { + return DefaultCondition.StringWidth(s) +} + +// Truncate return string truncated with w cells +func Truncate(s string, w int, tail string) string { + return DefaultCondition.Truncate(s, w, tail) +} + +// Wrap return string wrapped with w cells +func Wrap(s string, w int) string { + return DefaultCondition.Wrap(s, w) +} + +// FillLeft return string filled in left by spaces in w cells +func FillLeft(s string, w int) string { + return DefaultCondition.FillLeft(s, w) +} + +// FillRight return string filled in left by spaces in w cells +func FillRight(s string, w int) string { + return DefaultCondition.FillRight(s, w) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 0000000..0ce32c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,8 @@ +// +build js + +package runewidth + +func IsEastAsian() bool { + // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 0000000..c579e9a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,77 @@ +// +build !windows,!js + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_CTYPE") + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 0000000..0258876 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,25 @@ +package runewidth + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") +) + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + r1, _, _ := procGetConsoleOutputCP.Call() + if r1 == 0 { + return false + } + + switch int(r1) { + case 932, 51932, 936, 949, 950: + return true + } + + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md new file mode 100644 index 0000000..2298515 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/README.md new file mode 100644 index 0000000..60ae311 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/README.md @@ -0,0 +1,39 @@ +# go-wordwrap + +`go-wordwrap` (Golang package: `wordwrap`) is a package for Go that +automatically wraps words into multiple lines. The primary use case for this +is in formatting CLI output, but of course word wrapping is a generally useful +thing to do. + +## Installation and Usage + +Install using `go get github.com/mitchellh/go-wordwrap`. + +Full documentation is available at +http://godoc.org/github.com/mitchellh/go-wordwrap + +Below is an example of its usage ignoring errors: + +```go +wrapped := wordwrap.WrapString("foo bar baz", 3) +fmt.Println(wrapped) +``` + +Would output: + +``` +foo +bar +baz +``` + +## Word Wrap Algorithm + +This library doesn't use any clever algorithm for word wrapping. The wrapping +is actually very naive: whenever there is whitespace or an explicit linebreak. +The goal of this library is for word wrapping CLI output, so the input is +typically pretty well controlled human language. Because of this, the naive +approach typically works just fine. + +In the future, we'd like to make the algorithm more advanced. We would do +so without breaking the API. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go new file mode 100644 index 0000000..ac67205 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go @@ -0,0 +1,73 @@ +package wordwrap + +import ( + "bytes" + "unicode" +) + +// WrapString wraps the given string within lim width in characters. +// +// Wrapping is currently naive and only happens at white-space. A future +// version of the library will implement smarter wrapping. This means that +// pathological cases can dramatically reach past the limit, such as a very +// long word. +func WrapString(s string, lim uint) string { + // Initialize a buffer with a slightly larger size to account for breaks + init := make([]byte, 0, len(s)) + buf := bytes.NewBuffer(init) + + var current uint + var wordBuf, spaceBuf bytes.Buffer + + for _, char := range s { + if char == '\n' { + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) > lim { + current = 0 + } else { + current += uint(spaceBuf.Len()) + spaceBuf.WriteTo(buf) + } + spaceBuf.Reset() + } else { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + buf.WriteRune(char) + current = 0 + } else if unicode.IsSpace(char) { + if spaceBuf.Len() == 0 || wordBuf.Len() > 0 { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + + spaceBuf.WriteRune(char) + } else { + + wordBuf.WriteRune(char) + + if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim { + buf.WriteRune('\n') + current = 0 + spaceBuf.Reset() + } + } + } + + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) <= lim { + spaceBuf.WriteTo(buf) + } + } else { + spaceBuf.WriteTo(buf) + wordBuf.WriteTo(buf) + } + + return buf.String() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/AUTHORS new file mode 100644 index 0000000..fe26fb0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/AUTHORS @@ -0,0 +1,4 @@ +# Please keep this file sorted. + +Georg Reinke +nsf diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/LICENSE new file mode 100644 index 0000000..d9bc068 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 termbox-go authors + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/README.md new file mode 100644 index 0000000..4b2e73c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/README.md @@ -0,0 +1,31 @@ +## Termbox +Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area. + +### Installation +Install and update this go package with `go get -u github.com/nsf/termbox-go` + +### Examples +For examples of what can be done take a look at demos in the _demos directory. You can try them with go run: `go run _demos/keyboard.go` + +There are also some interesting projects using termbox-go: + - [godit](https://github.com/nsf/godit) is an emacsish lightweight text editor written using termbox. + - [gomatrix](https://github.com/GeertJohan/gomatrix) connects to The Matrix and displays its data streams in your terminal. + - [gotetris](https://github.com/jjinux/gotetris) is an implementation of Tetris. + - [sokoban-go](https://github.com/rn2dy/sokoban-go) is an implementation of sokoban game. + - [hecate](https://github.com/evanmiller/hecate) is a hex editor designed by Satan. + - [httopd](https://github.com/verdverm/httopd) is top for httpd logs. + - [mop](https://github.com/michaeldv/mop) is stock market tracker for hackers. + - [termui](https://github.com/gizak/termui) is a terminal dashboard. + - [termloop](https://github.com/JoelOtter/termloop) is a terminal game engine. + - [xterm-color-chart](https://github.com/kutuluk/xterm-color-chart) is a XTerm 256 color chart. + - [gocui](https://github.com/jroimartin/gocui) is a minimalist Go library aimed at creating console user interfaces. + - [dry](https://github.com/moncho/dry) is an interactive cli to manage Docker containers. + - [pxl](https://github.com/ichinaski/pxl) displays images in the terminal. + - [snake-game](https://github.com/DyegoCosta/snake-game) is an implementation of the Snake game. + - [gone](https://github.com/guillaumebreton/gone) is a CLI pomodoro® timer. + - [Spoof.go](https://github.com/sabey/spoofgo) controllable movement spoofing from the cli + - [lf](https://github.com/gokcehan/lf) is a terminal file manager + - [rat](https://github.com/ericfreese/rat) lets you compose shell commands to build terminal applications. + +### API reference +[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api.go new file mode 100644 index 0000000..b242ddc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api.go @@ -0,0 +1,457 @@ +// +build !windows + +package termbox + +import "github.com/mattn/go-runewidth" +import "fmt" +import "os" +import "os/signal" +import "syscall" +import "runtime" + +// public API + +// Initializes termbox library. This function should be called before any other functions. +// After successful initialization, the library must be finalized using 'Close' function. +// +// Example usage: +// err := termbox.Init() +// if err != nil { +// panic(err) +// } +// defer termbox.Close() +func Init() error { + var err error + + out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0) + if err != nil { + return err + } + in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0) + if err != nil { + return err + } + + err = setup_term() + if err != nil { + return fmt.Errorf("termbox: error while reading terminfo data: %v", err) + } + + signal.Notify(sigwinch, syscall.SIGWINCH) + signal.Notify(sigio, syscall.SIGIO) + + _, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK) + if err != nil { + return err + } + _, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid()) + if runtime.GOOS != "darwin" && err != nil { + return err + } + err = tcgetattr(out.Fd(), &orig_tios) + if err != nil { + return err + } + + tios := orig_tios + tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK | + syscall_ISTRIP | syscall_INLCR | syscall_IGNCR | + syscall_ICRNL | syscall_IXON + tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON | + syscall_ISIG | syscall_IEXTEN + tios.Cflag &^= syscall_CSIZE | syscall_PARENB + tios.Cflag |= syscall_CS8 + tios.Cc[syscall_VMIN] = 1 + tios.Cc[syscall_VTIME] = 0 + + err = tcsetattr(out.Fd(), &tios) + if err != nil { + return err + } + + out.WriteString(funcs[t_enter_ca]) + out.WriteString(funcs[t_enter_keypad]) + out.WriteString(funcs[t_hide_cursor]) + out.WriteString(funcs[t_clear_screen]) + + termw, termh = get_term_size(out.Fd()) + back_buffer.init(termw, termh) + front_buffer.init(termw, termh) + back_buffer.clear() + front_buffer.clear() + + go func() { + buf := make([]byte, 128) + for { + select { + case <-sigio: + for { + n, err := syscall.Read(in, buf) + if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK { + break + } + select { + case input_comm <- input_event{buf[:n], err}: + ie := <-input_comm + buf = ie.data[:128] + case <-quit: + return + } + } + case <-quit: + return + } + } + }() + + IsInit = true + return nil +} + +// Interrupt an in-progress call to PollEvent by causing it to return +// EventInterrupt. Note that this function will block until the PollEvent +// function has successfully been interrupted. +func Interrupt() { + interrupt_comm <- struct{}{} +} + +// Finalizes termbox library, should be called after successful initialization +// when termbox's functionality isn't required anymore. +func Close() { + quit <- 1 + out.WriteString(funcs[t_show_cursor]) + out.WriteString(funcs[t_sgr0]) + out.WriteString(funcs[t_clear_screen]) + out.WriteString(funcs[t_exit_ca]) + out.WriteString(funcs[t_exit_keypad]) + out.WriteString(funcs[t_exit_mouse]) + tcsetattr(out.Fd(), &orig_tios) + + out.Close() + syscall.Close(in) + + // reset the state, so that on next Init() it will work again + termw = 0 + termh = 0 + input_mode = InputEsc + out = nil + in = 0 + lastfg = attr_invalid + lastbg = attr_invalid + lastx = coord_invalid + lasty = coord_invalid + cursor_x = cursor_hidden + cursor_y = cursor_hidden + foreground = ColorDefault + background = ColorDefault + IsInit = false +} + +// Synchronizes the internal back buffer with the terminal. +func Flush() error { + // invalidate cursor position + lastx = coord_invalid + lasty = coord_invalid + + update_size_maybe() + + for y := 0; y < front_buffer.height; y++ { + line_offset := y * front_buffer.width + for x := 0; x < front_buffer.width; { + cell_offset := line_offset + x + back := &back_buffer.cells[cell_offset] + front := &front_buffer.cells[cell_offset] + if back.Ch < ' ' { + back.Ch = ' ' + } + w := runewidth.RuneWidth(back.Ch) + if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) { + w = 1 + } + if *back == *front { + x += w + continue + } + *front = *back + send_attr(back.Fg, back.Bg) + + if w == 2 && x == front_buffer.width-1 { + // there's not enough space for 2-cells rune, + // let's just put a space in there + send_char(x, y, ' ') + } else { + send_char(x, y, back.Ch) + if w == 2 { + next := cell_offset + 1 + front_buffer.cells[next] = Cell{ + Ch: 0, + Fg: back.Fg, + Bg: back.Bg, + } + } + } + x += w + } + } + if !is_cursor_hidden(cursor_x, cursor_y) { + write_cursor(cursor_x, cursor_y) + } + return flush() +} + +// Sets the position of the cursor. See also HideCursor(). +func SetCursor(x, y int) { + if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { + outbuf.WriteString(funcs[t_show_cursor]) + } + + if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { + outbuf.WriteString(funcs[t_hide_cursor]) + } + + cursor_x, cursor_y = x, y + if !is_cursor_hidden(cursor_x, cursor_y) { + write_cursor(cursor_x, cursor_y) + } +} + +// The shortcut for SetCursor(-1, -1). +func HideCursor() { + SetCursor(cursor_hidden, cursor_hidden) +} + +// Changes cell's parameters in the internal back buffer at the specified +// position. +func SetCell(x, y int, ch rune, fg, bg Attribute) { + if x < 0 || x >= back_buffer.width { + return + } + if y < 0 || y >= back_buffer.height { + return + } + + back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} +} + +// Returns a slice into the termbox's back buffer. You can get its dimensions +// using 'Size' function. The slice remains valid as long as no 'Clear' or +// 'Flush' function calls were made after call to this function. +func CellBuffer() []Cell { + return back_buffer.cells +} + +// After getting a raw event from PollRawEvent function call, you can parse it +// again into an ordinary one using termbox logic. That is parse an event as +// termbox would do it. Returned event in addition to usual Event struct fields +// sets N field to the amount of bytes used within 'data' slice. If the length +// of 'data' slice is zero or event cannot be parsed for some other reason, the +// function will return a special event type: EventNone. +// +// IMPORTANT: EventNone may contain a non-zero N, which means you should skip +// these bytes, because termbox cannot recognize them. +// +// NOTE: This API is experimental and may change in future. +func ParseEvent(data []byte) Event { + event := Event{Type: EventKey} + ok := extract_event(data, &event) + if !ok { + return Event{Type: EventNone, N: event.N} + } + return event +} + +// Wait for an event and return it. This is a blocking function call. Instead +// of EventKey and EventMouse it returns EventRaw events. Raw event is written +// into `data` slice and Event's N field is set to the amount of bytes written. +// The minimum required length of the 'data' slice is 1. This requirement may +// vary on different platforms. +// +// NOTE: This API is experimental and may change in future. +func PollRawEvent(data []byte) Event { + if len(data) == 0 { + panic("len(data) >= 1 is a requirement") + } + + var event Event + if extract_raw_event(data, &event) { + return event + } + + for { + select { + case ev := <-input_comm: + if ev.err != nil { + return Event{Type: EventError, Err: ev.err} + } + + inbuf = append(inbuf, ev.data...) + input_comm <- ev + if extract_raw_event(data, &event) { + return event + } + case <-interrupt_comm: + event.Type = EventInterrupt + return event + + case <-sigwinch: + event.Type = EventResize + event.Width, event.Height = get_term_size(out.Fd()) + return event + } + } +} + +// Wait for an event and return it. This is a blocking function call. +func PollEvent() Event { + var event Event + + // try to extract event from input buffer, return on success + event.Type = EventKey + ok := extract_event(inbuf, &event) + if event.N != 0 { + copy(inbuf, inbuf[event.N:]) + inbuf = inbuf[:len(inbuf)-event.N] + } + if ok { + return event + } + + for { + select { + case ev := <-input_comm: + if ev.err != nil { + return Event{Type: EventError, Err: ev.err} + } + + inbuf = append(inbuf, ev.data...) + input_comm <- ev + ok := extract_event(inbuf, &event) + if event.N != 0 { + copy(inbuf, inbuf[event.N:]) + inbuf = inbuf[:len(inbuf)-event.N] + } + if ok { + return event + } + case <-interrupt_comm: + event.Type = EventInterrupt + return event + + case <-sigwinch: + event.Type = EventResize + event.Width, event.Height = get_term_size(out.Fd()) + return event + } + } +} + +// Returns the size of the internal back buffer (which is mostly the same as +// terminal's window size in characters). But it doesn't always match the size +// of the terminal window, after the terminal size has changed, the internal +// back buffer will get in sync only after Clear or Flush function calls. +func Size() (width int, height int) { + return termw, termh +} + +// Clears the internal back buffer. +func Clear(fg, bg Attribute) error { + foreground, background = fg, bg + err := update_size_maybe() + back_buffer.clear() + return err +} + +// Sets termbox input mode. Termbox has two input modes: +// +// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC means KeyEsc. This is the default input mode. +// +// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC enables ModAlt modifier for the next keyboard event. +// +// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will +// enable mouse button press/release and drag events. +// +// If 'mode' is InputCurrent, returns the current input mode. See also Input* +// constants. +func SetInputMode(mode InputMode) InputMode { + if mode == InputCurrent { + return input_mode + } + if mode&(InputEsc|InputAlt) == 0 { + mode |= InputEsc + } + if mode&(InputEsc|InputAlt) == InputEsc|InputAlt { + mode &^= InputAlt + } + if mode&InputMouse != 0 { + out.WriteString(funcs[t_enter_mouse]) + } else { + out.WriteString(funcs[t_exit_mouse]) + } + + input_mode = mode + return input_mode +} + +// Sets the termbox output mode. Termbox has four output options: +// +// 1. OutputNormal => [1..8] +// This mode provides 8 different colors: +// black, red, green, yellow, blue, magenta, cyan, white +// Shortcut: ColorBlack, ColorRed, ... +// Attributes: AttrBold, AttrUnderline, AttrReverse +// +// Example usage: +// SetCell(x, y, '@', ColorBlack | AttrBold, ColorRed); +// +// 2. Output256 => [1..256] +// In this mode you can leverage the 256 terminal mode: +// 0x01 - 0x08: the 8 colors as in OutputNormal +// 0x09 - 0x10: Color* | AttrBold +// 0x11 - 0xe8: 216 different colors +// 0xe9 - 0x1ff: 24 different shades of grey +// +// Example usage: +// SetCell(x, y, '@', 184, 240); +// SetCell(x, y, '@', 0xb8, 0xf0); +// +// 3. Output216 => [1..216] +// This mode supports the 3rd range of the 256 mode only. +// But you dont need to provide an offset. +// +// 4. OutputGrayscale => [1..26] +// This mode supports the 4th range of the 256 mode +// and black and white colors from 3th range of the 256 mode +// But you dont need to provide an offset. +// +// In all modes, 0x00 represents the default color. +// +// `go run _demos/output.go` to see its impact on your terminal. +// +// If 'mode' is OutputCurrent, it returns the current output mode. +// +// Note that this may return a different OutputMode than the one requested, +// as the requested mode may not be available on the target platform. +func SetOutputMode(mode OutputMode) OutputMode { + if mode == OutputCurrent { + return output_mode + } + + output_mode = mode + return output_mode +} + +// Sync comes handy when something causes desync between termbox's understanding +// of a terminal buffer and the reality. Such as a third party process. Sync +// forces a complete resync between the termbox and a terminal, it may not be +// visually pretty though. +func Sync() error { + front_buffer.clear() + err := send_clear() + if err != nil { + return err + } + + return Flush() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_common.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_common.go new file mode 100644 index 0000000..9f23661 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_common.go @@ -0,0 +1,187 @@ +// termbox is a library for creating cross-platform text-based interfaces +package termbox + +// public API, common OS agnostic part + +type ( + InputMode int + OutputMode int + EventType uint8 + Modifier uint8 + Key uint16 + Attribute uint16 +) + +// This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are +// valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if +// 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError. +type Event struct { + Type EventType // one of Event* constants + Mod Modifier // one of Mod* constants or 0 + Key Key // one of Key* constants, invalid if 'Ch' is not 0 + Ch rune // a unicode character + Width int // width of the screen + Height int // height of the screen + Err error // error in case if input failed + MouseX int // x coord of mouse + MouseY int // y coord of mouse + N int // number of bytes written when getting a raw event +} + +// A cell, single conceptual entity on the screen. The screen is basically a 2d +// array of cells. 'Ch' is a unicode character, 'Fg' and 'Bg' are foreground +// and background attributes respectively. +type Cell struct { + Ch rune + Fg Attribute + Bg Attribute +} + +// To know if termbox has been initialized or not +var ( + IsInit bool = false +) + +// Key constants, see Event.Key field. +const ( + KeyF1 Key = 0xFFFF - iota + KeyF2 + KeyF3 + KeyF4 + KeyF5 + KeyF6 + KeyF7 + KeyF8 + KeyF9 + KeyF10 + KeyF11 + KeyF12 + KeyInsert + KeyDelete + KeyHome + KeyEnd + KeyPgup + KeyPgdn + KeyArrowUp + KeyArrowDown + KeyArrowLeft + KeyArrowRight + key_min // see terminfo + MouseLeft + MouseMiddle + MouseRight + MouseRelease + MouseWheelUp + MouseWheelDown +) + +const ( + KeyCtrlTilde Key = 0x00 + KeyCtrl2 Key = 0x00 + KeyCtrlSpace Key = 0x00 + KeyCtrlA Key = 0x01 + KeyCtrlB Key = 0x02 + KeyCtrlC Key = 0x03 + KeyCtrlD Key = 0x04 + KeyCtrlE Key = 0x05 + KeyCtrlF Key = 0x06 + KeyCtrlG Key = 0x07 + KeyBackspace Key = 0x08 + KeyCtrlH Key = 0x08 + KeyTab Key = 0x09 + KeyCtrlI Key = 0x09 + KeyCtrlJ Key = 0x0A + KeyCtrlK Key = 0x0B + KeyCtrlL Key = 0x0C + KeyEnter Key = 0x0D + KeyCtrlM Key = 0x0D + KeyCtrlN Key = 0x0E + KeyCtrlO Key = 0x0F + KeyCtrlP Key = 0x10 + KeyCtrlQ Key = 0x11 + KeyCtrlR Key = 0x12 + KeyCtrlS Key = 0x13 + KeyCtrlT Key = 0x14 + KeyCtrlU Key = 0x15 + KeyCtrlV Key = 0x16 + KeyCtrlW Key = 0x17 + KeyCtrlX Key = 0x18 + KeyCtrlY Key = 0x19 + KeyCtrlZ Key = 0x1A + KeyEsc Key = 0x1B + KeyCtrlLsqBracket Key = 0x1B + KeyCtrl3 Key = 0x1B + KeyCtrl4 Key = 0x1C + KeyCtrlBackslash Key = 0x1C + KeyCtrl5 Key = 0x1D + KeyCtrlRsqBracket Key = 0x1D + KeyCtrl6 Key = 0x1E + KeyCtrl7 Key = 0x1F + KeyCtrlSlash Key = 0x1F + KeyCtrlUnderscore Key = 0x1F + KeySpace Key = 0x20 + KeyBackspace2 Key = 0x7F + KeyCtrl8 Key = 0x7F +) + +// Alt modifier constant, see Event.Mod field and SetInputMode function. +const ( + ModAlt Modifier = 1 << iota + ModMotion +) + +// Cell colors, you can combine a color with multiple attributes using bitwise +// OR ('|'). +const ( + ColorDefault Attribute = iota + ColorBlack + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +// Cell attributes, it is possible to use multiple attributes by combining them +// using bitwise OR ('|'). Although, colors cannot be combined. But you can +// combine attributes and a single color. +// +// It's worth mentioning that some platforms don't support certain attibutes. +// For example windows console doesn't support AttrUnderline. And on some +// terminals applying AttrBold to background may result in blinking text. Use +// them with caution and test your code on various terminals. +const ( + AttrBold Attribute = 1 << (iota + 9) + AttrUnderline + AttrReverse +) + +// Input mode. See SetInputMode function. +const ( + InputEsc InputMode = 1 << iota + InputAlt + InputMouse + InputCurrent InputMode = 0 +) + +// Output mode. See SetOutputMode function. +const ( + OutputCurrent OutputMode = iota + OutputNormal + Output256 + Output216 + OutputGrayscale +) + +// Event type. See Event.Type field. +const ( + EventKey EventType = iota + EventResize + EventMouse + EventError + EventInterrupt + EventRaw + EventNone +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_windows.go new file mode 100644 index 0000000..7def30a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/api_windows.go @@ -0,0 +1,239 @@ +package termbox + +import ( + "syscall" +) + +// public API + +// Initializes termbox library. This function should be called before any other functions. +// After successful initialization, the library must be finalized using 'Close' function. +// +// Example usage: +// err := termbox.Init() +// if err != nil { +// panic(err) +// } +// defer termbox.Close() +func Init() error { + var err error + + interrupt, err = create_event() + if err != nil { + return err + } + + in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0) + if err != nil { + return err + } + out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + return err + } + + err = get_console_mode(in, &orig_mode) + if err != nil { + return err + } + + err = set_console_mode(in, enable_window_input) + if err != nil { + return err + } + + orig_size = get_term_size(out) + win_size := get_win_size(out) + + err = set_console_screen_buffer_size(out, win_size) + if err != nil { + return err + } + + err = get_console_cursor_info(out, &orig_cursor_info) + if err != nil { + return err + } + + show_cursor(false) + term_size = get_term_size(out) + back_buffer.init(int(term_size.x), int(term_size.y)) + front_buffer.init(int(term_size.x), int(term_size.y)) + back_buffer.clear() + front_buffer.clear() + clear() + + diffbuf = make([]diff_msg, 0, 32) + + go input_event_producer() + IsInit = true + return nil +} + +// Finalizes termbox library, should be called after successful initialization +// when termbox's functionality isn't required anymore. +func Close() { + // we ignore errors here, because we can't really do anything about them + Clear(0, 0) + Flush() + + // stop event producer + cancel_comm <- true + set_event(interrupt) + select { + case <-input_comm: + default: + } + <-cancel_done_comm + + set_console_cursor_info(out, &orig_cursor_info) + set_console_cursor_position(out, coord{}) + set_console_screen_buffer_size(out, orig_size) + set_console_mode(in, orig_mode) + syscall.Close(in) + syscall.Close(out) + syscall.Close(interrupt) + IsInit = false +} + +// Interrupt an in-progress call to PollEvent by causing it to return +// EventInterrupt. Note that this function will block until the PollEvent +// function has successfully been interrupted. +func Interrupt() { + interrupt_comm <- struct{}{} +} + +// Synchronizes the internal back buffer with the terminal. +func Flush() error { + update_size_maybe() + prepare_diff_messages() + for _, diff := range diffbuf { + r := small_rect{ + left: 0, + top: diff.pos, + right: term_size.x - 1, + bottom: diff.pos + diff.lines - 1, + } + write_console_output(out, diff.chars, r) + } + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } + return nil +} + +// Sets the position of the cursor. See also HideCursor(). +func SetCursor(x, y int) { + if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { + show_cursor(true) + } + + if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { + show_cursor(false) + } + + cursor_x, cursor_y = x, y + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } +} + +// The shortcut for SetCursor(-1, -1). +func HideCursor() { + SetCursor(cursor_hidden, cursor_hidden) +} + +// Changes cell's parameters in the internal back buffer at the specified +// position. +func SetCell(x, y int, ch rune, fg, bg Attribute) { + if x < 0 || x >= back_buffer.width { + return + } + if y < 0 || y >= back_buffer.height { + return + } + + back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} +} + +// Returns a slice into the termbox's back buffer. You can get its dimensions +// using 'Size' function. The slice remains valid as long as no 'Clear' or +// 'Flush' function calls were made after call to this function. +func CellBuffer() []Cell { + return back_buffer.cells +} + +// Wait for an event and return it. This is a blocking function call. +func PollEvent() Event { + select { + case ev := <-input_comm: + return ev + case <-interrupt_comm: + return Event{Type: EventInterrupt} + } +} + +// Returns the size of the internal back buffer (which is mostly the same as +// console's window size in characters). But it doesn't always match the size +// of the console window, after the console size has changed, the internal back +// buffer will get in sync only after Clear or Flush function calls. +func Size() (int, int) { + return int(term_size.x), int(term_size.y) +} + +// Clears the internal back buffer. +func Clear(fg, bg Attribute) error { + foreground, background = fg, bg + update_size_maybe() + back_buffer.clear() + return nil +} + +// Sets termbox input mode. Termbox has two input modes: +// +// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC means KeyEsc. This is the default input mode. +// +// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match +// any known sequence. ESC enables ModAlt modifier for the next keyboard event. +// +// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will +// enable mouse button press/release and drag events. +// +// If 'mode' is InputCurrent, returns the current input mode. See also Input* +// constants. +func SetInputMode(mode InputMode) InputMode { + if mode == InputCurrent { + return input_mode + } + if mode&InputMouse != 0 { + err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags) + if err != nil { + panic(err) + } + } else { + err := set_console_mode(in, enable_window_input) + if err != nil { + panic(err) + } + } + + input_mode = mode + return input_mode +} + +// Sets the termbox output mode. +// +// Windows console does not support extra colour modes, +// so this will always set and return OutputNormal. +func SetOutputMode(mode OutputMode) OutputMode { + return OutputNormal +} + +// Sync comes handy when something causes desync between termbox's understanding +// of a terminal buffer and the reality. Such as a third party process. Sync +// forces a complete resync between the termbox and a terminal, it may not be +// visually pretty though. At the moment on Windows it does nothing. +func Sync() error { + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/collect_terminfo.py b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/collect_terminfo.py new file mode 100755 index 0000000..5e50975 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/collect_terminfo.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +import sys, os, subprocess + +def escaped(s): + return repr(s)[1:-1] + +def tput(term, name): + try: + return subprocess.check_output(['tput', '-T%s' % term, name]).decode() + except subprocess.CalledProcessError as e: + return e.output.decode() + + +def w(s): + if s == None: + return + sys.stdout.write(s) + +terminals = { + 'xterm' : 'xterm', + 'rxvt-256color' : 'rxvt_256color', + 'rxvt-unicode' : 'rxvt_unicode', + 'linux' : 'linux', + 'Eterm' : 'eterm', + 'screen' : 'screen' +} + +keys = [ + "F1", "kf1", + "F2", "kf2", + "F3", "kf3", + "F4", "kf4", + "F5", "kf5", + "F6", "kf6", + "F7", "kf7", + "F8", "kf8", + "F9", "kf9", + "F10", "kf10", + "F11", "kf11", + "F12", "kf12", + "INSERT", "kich1", + "DELETE", "kdch1", + "HOME", "khome", + "END", "kend", + "PGUP", "kpp", + "PGDN", "knp", + "KEY_UP", "kcuu1", + "KEY_DOWN", "kcud1", + "KEY_LEFT", "kcub1", + "KEY_RIGHT", "kcuf1" +] + +funcs = [ + "T_ENTER_CA", "smcup", + "T_EXIT_CA", "rmcup", + "T_SHOW_CURSOR", "cnorm", + "T_HIDE_CURSOR", "civis", + "T_CLEAR_SCREEN", "clear", + "T_SGR0", "sgr0", + "T_UNDERLINE", "smul", + "T_BOLD", "bold", + "T_BLINK", "blink", + "T_REVERSE", "rev", + "T_ENTER_KEYPAD", "smkx", + "T_EXIT_KEYPAD", "rmkx" +] + +def iter_pairs(iterable): + iterable = iter(iterable) + while True: + yield (next(iterable), next(iterable)) + +def do_term(term, nick): + w("// %s\n" % term) + w("var %s_keys = []string{\n\t" % nick) + for k, v in iter_pairs(keys): + w('"') + w(escaped(tput(term, v))) + w('",') + w("\n}\n") + w("var %s_funcs = []string{\n\t" % nick) + for k,v in iter_pairs(funcs): + w('"') + if v == "sgr": + w("\\033[3%d;4%dm") + elif v == "cup": + w("\\033[%d;%dH") + else: + w(escaped(tput(term, v))) + w('", ') + w("\n}\n\n") + +def do_terms(d): + w("var terms = []struct {\n") + w("\tname string\n") + w("\tkeys []string\n") + w("\tfuncs []string\n") + w("}{\n") + for k, v in d.items(): + w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v)) + w("}\n\n") + +w("// +build !windows\n\npackage termbox\n\n") + +for k,v in terminals.items(): + do_term(k, v) + +do_terms(terminals) + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls.go new file mode 100644 index 0000000..4f52bb9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls.go @@ -0,0 +1,39 @@ +// +build ignore + +package termbox + +/* +#include +#include +*/ +import "C" + +type syscall_Termios C.struct_termios + +const ( + syscall_IGNBRK = C.IGNBRK + syscall_BRKINT = C.BRKINT + syscall_PARMRK = C.PARMRK + syscall_ISTRIP = C.ISTRIP + syscall_INLCR = C.INLCR + syscall_IGNCR = C.IGNCR + syscall_ICRNL = C.ICRNL + syscall_IXON = C.IXON + syscall_OPOST = C.OPOST + syscall_ECHO = C.ECHO + syscall_ECHONL = C.ECHONL + syscall_ICANON = C.ICANON + syscall_ISIG = C.ISIG + syscall_IEXTEN = C.IEXTEN + syscall_CSIZE = C.CSIZE + syscall_PARENB = C.PARENB + syscall_CS8 = C.CS8 + syscall_VMIN = C.VMIN + syscall_VTIME = C.VTIME + + // on darwin change these to (on *bsd too?): + // C.TIOCGETA + // C.TIOCSETA + syscall_TCGETS = C.TCGETS + syscall_TCSETS = C.TCSETS +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin.go new file mode 100644 index 0000000..25b78f7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin.go @@ -0,0 +1,41 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +// +build !amd64 + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go new file mode 100644 index 0000000..11f25be --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go @@ -0,0 +1,40 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint64 + Oflag uint64 + Cflag uint64 + Lflag uint64 + Cc [20]uint8 + Pad_cgo_0 [4]byte + Ispeed uint64 + Ospeed uint64 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x40487413 + syscall_TCSETS = 0x80487414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go new file mode 100644 index 0000000..e03624e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go new file mode 100644 index 0000000..e03624e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_linux.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_linux.go new file mode 100644 index 0000000..b88960d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_linux.go @@ -0,0 +1,33 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +import "syscall" + +type syscall_Termios syscall.Termios + +const ( + syscall_IGNBRK = syscall.IGNBRK + syscall_BRKINT = syscall.BRKINT + syscall_PARMRK = syscall.PARMRK + syscall_ISTRIP = syscall.ISTRIP + syscall_INLCR = syscall.INLCR + syscall_IGNCR = syscall.IGNCR + syscall_ICRNL = syscall.ICRNL + syscall_IXON = syscall.IXON + syscall_OPOST = syscall.OPOST + syscall_ECHO = syscall.ECHO + syscall_ECHONL = syscall.ECHONL + syscall_ICANON = syscall.ICANON + syscall_ISIG = syscall.ISIG + syscall_IEXTEN = syscall.IEXTEN + syscall_CSIZE = syscall.CSIZE + syscall_PARENB = syscall.PARENB + syscall_CS8 = syscall.CS8 + syscall_VMIN = syscall.VMIN + syscall_VTIME = syscall.VTIME + + syscall_TCGETS = syscall.TCGETS + syscall_TCSETS = syscall.TCSETS +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go new file mode 100644 index 0000000..49a3355 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed int32 + Ospeed int32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go new file mode 100644 index 0000000..49a3355 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go @@ -0,0 +1,39 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs syscalls.go + +package termbox + +type syscall_Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed int32 + Ospeed int32 +} + +const ( + syscall_IGNBRK = 0x1 + syscall_BRKINT = 0x2 + syscall_PARMRK = 0x8 + syscall_ISTRIP = 0x20 + syscall_INLCR = 0x40 + syscall_IGNCR = 0x80 + syscall_ICRNL = 0x100 + syscall_IXON = 0x200 + syscall_OPOST = 0x1 + syscall_ECHO = 0x8 + syscall_ECHONL = 0x10 + syscall_ICANON = 0x100 + syscall_ISIG = 0x80 + syscall_IEXTEN = 0x400 + syscall_CSIZE = 0x300 + syscall_PARENB = 0x1000 + syscall_CS8 = 0x300 + syscall_VMIN = 0x10 + syscall_VTIME = 0x11 + + syscall_TCGETS = 0x402c7413 + syscall_TCSETS = 0x802c7414 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_windows.go new file mode 100644 index 0000000..472d002 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/syscalls_windows.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs -- -DUNICODE syscalls.go + +package termbox + +const ( + foreground_blue = 0x1 + foreground_green = 0x2 + foreground_red = 0x4 + foreground_intensity = 0x8 + background_blue = 0x10 + background_green = 0x20 + background_red = 0x40 + background_intensity = 0x80 + std_input_handle = -0xa + std_output_handle = -0xb + key_event = 0x1 + mouse_event = 0x2 + window_buffer_size_event = 0x4 + enable_window_input = 0x8 + enable_mouse_input = 0x10 + enable_extended_flags = 0x80 + + vk_f1 = 0x70 + vk_f2 = 0x71 + vk_f3 = 0x72 + vk_f4 = 0x73 + vk_f5 = 0x74 + vk_f6 = 0x75 + vk_f7 = 0x76 + vk_f8 = 0x77 + vk_f9 = 0x78 + vk_f10 = 0x79 + vk_f11 = 0x7a + vk_f12 = 0x7b + vk_insert = 0x2d + vk_delete = 0x2e + vk_home = 0x24 + vk_end = 0x23 + vk_pgup = 0x21 + vk_pgdn = 0x22 + vk_arrow_up = 0x26 + vk_arrow_down = 0x28 + vk_arrow_left = 0x25 + vk_arrow_right = 0x27 + vk_backspace = 0x8 + vk_tab = 0x9 + vk_enter = 0xd + vk_esc = 0x1b + vk_space = 0x20 + + left_alt_pressed = 0x2 + left_ctrl_pressed = 0x8 + right_alt_pressed = 0x1 + right_ctrl_pressed = 0x4 + shift_pressed = 0x10 + + generic_read = 0x80000000 + generic_write = 0x40000000 + console_textmode_buffer = 0x1 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox.go new file mode 100644 index 0000000..c2d86c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox.go @@ -0,0 +1,511 @@ +// +build !windows + +package termbox + +import "unicode/utf8" +import "bytes" +import "syscall" +import "unsafe" +import "strings" +import "strconv" +import "os" +import "io" + +// private API + +const ( + t_enter_ca = iota + t_exit_ca + t_show_cursor + t_hide_cursor + t_clear_screen + t_sgr0 + t_underline + t_bold + t_blink + t_reverse + t_enter_keypad + t_exit_keypad + t_enter_mouse + t_exit_mouse + t_max_funcs +) + +const ( + coord_invalid = -2 + attr_invalid = Attribute(0xFFFF) +) + +type input_event struct { + data []byte + err error +} + +var ( + // term specific sequences + keys []string + funcs []string + + // termbox inner state + orig_tios syscall_Termios + back_buffer cellbuf + front_buffer cellbuf + termw int + termh int + input_mode = InputEsc + output_mode = OutputNormal + out *os.File + in int + lastfg = attr_invalid + lastbg = attr_invalid + lastx = coord_invalid + lasty = coord_invalid + cursor_x = cursor_hidden + cursor_y = cursor_hidden + foreground = ColorDefault + background = ColorDefault + inbuf = make([]byte, 0, 64) + outbuf bytes.Buffer + sigwinch = make(chan os.Signal, 1) + sigio = make(chan os.Signal, 1) + quit = make(chan int) + input_comm = make(chan input_event) + interrupt_comm = make(chan struct{}) + intbuf = make([]byte, 0, 16) + + // grayscale indexes + grayscale = []Attribute{ + 0, 17, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 232, + } +) + +func write_cursor(x, y int) { + outbuf.WriteString("\033[") + outbuf.Write(strconv.AppendUint(intbuf, uint64(y+1), 10)) + outbuf.WriteString(";") + outbuf.Write(strconv.AppendUint(intbuf, uint64(x+1), 10)) + outbuf.WriteString("H") +} + +func write_sgr_fg(a Attribute) { + switch output_mode { + case Output256, Output216, OutputGrayscale: + outbuf.WriteString("\033[38;5;") + outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10)) + outbuf.WriteString("m") + default: + outbuf.WriteString("\033[3") + outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10)) + outbuf.WriteString("m") + } +} + +func write_sgr_bg(a Attribute) { + switch output_mode { + case Output256, Output216, OutputGrayscale: + outbuf.WriteString("\033[48;5;") + outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10)) + outbuf.WriteString("m") + default: + outbuf.WriteString("\033[4") + outbuf.Write(strconv.AppendUint(intbuf, uint64(a-1), 10)) + outbuf.WriteString("m") + } +} + +func write_sgr(fg, bg Attribute) { + switch output_mode { + case Output256, Output216, OutputGrayscale: + outbuf.WriteString("\033[38;5;") + outbuf.Write(strconv.AppendUint(intbuf, uint64(fg-1), 10)) + outbuf.WriteString("m") + outbuf.WriteString("\033[48;5;") + outbuf.Write(strconv.AppendUint(intbuf, uint64(bg-1), 10)) + outbuf.WriteString("m") + default: + outbuf.WriteString("\033[3") + outbuf.Write(strconv.AppendUint(intbuf, uint64(fg-1), 10)) + outbuf.WriteString(";4") + outbuf.Write(strconv.AppendUint(intbuf, uint64(bg-1), 10)) + outbuf.WriteString("m") + } +} + +type winsize struct { + rows uint16 + cols uint16 + xpixels uint16 + ypixels uint16 +} + +func get_term_size(fd uintptr) (int, int) { + var sz winsize + _, _, _ = syscall.Syscall(syscall.SYS_IOCTL, + fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz))) + return int(sz.cols), int(sz.rows) +} + +func send_attr(fg, bg Attribute) { + if fg == lastfg && bg == lastbg { + return + } + + outbuf.WriteString(funcs[t_sgr0]) + + var fgcol, bgcol Attribute + + switch output_mode { + case Output256: + fgcol = fg & 0x1FF + bgcol = bg & 0x1FF + case Output216: + fgcol = fg & 0xFF + bgcol = bg & 0xFF + if fgcol > 216 { + fgcol = ColorDefault + } + if bgcol > 216 { + bgcol = ColorDefault + } + if fgcol != ColorDefault { + fgcol += 0x10 + } + if bgcol != ColorDefault { + bgcol += 0x10 + } + case OutputGrayscale: + fgcol = fg & 0x1F + bgcol = bg & 0x1F + if fgcol > 26 { + fgcol = ColorDefault + } + if bgcol > 26 { + bgcol = ColorDefault + } + if fgcol != ColorDefault { + fgcol = grayscale[fgcol] + } + if bgcol != ColorDefault { + bgcol = grayscale[bgcol] + } + default: + fgcol = fg & 0x0F + bgcol = bg & 0x0F + } + + if fgcol != ColorDefault { + if bgcol != ColorDefault { + write_sgr(fgcol, bgcol) + } else { + write_sgr_fg(fgcol) + } + } else if bgcol != ColorDefault { + write_sgr_bg(bgcol) + } + + if fg&AttrBold != 0 { + outbuf.WriteString(funcs[t_bold]) + } + if bg&AttrBold != 0 { + outbuf.WriteString(funcs[t_blink]) + } + if fg&AttrUnderline != 0 { + outbuf.WriteString(funcs[t_underline]) + } + if fg&AttrReverse|bg&AttrReverse != 0 { + outbuf.WriteString(funcs[t_reverse]) + } + + lastfg, lastbg = fg, bg +} + +func send_char(x, y int, ch rune) { + var buf [8]byte + n := utf8.EncodeRune(buf[:], ch) + if x-1 != lastx || y != lasty { + write_cursor(x, y) + } + lastx, lasty = x, y + outbuf.Write(buf[:n]) +} + +func flush() error { + _, err := io.Copy(out, &outbuf) + outbuf.Reset() + return err +} + +func send_clear() error { + send_attr(foreground, background) + outbuf.WriteString(funcs[t_clear_screen]) + if !is_cursor_hidden(cursor_x, cursor_y) { + write_cursor(cursor_x, cursor_y) + } + + // we need to invalidate cursor position too and these two vars are + // used only for simple cursor positioning optimization, cursor + // actually may be in the correct place, but we simply discard + // optimization once and it gives us simple solution for the case when + // cursor moved + lastx = coord_invalid + lasty = coord_invalid + + return flush() +} + +func update_size_maybe() error { + w, h := get_term_size(out.Fd()) + if w != termw || h != termh { + termw, termh = w, h + back_buffer.resize(termw, termh) + front_buffer.resize(termw, termh) + front_buffer.clear() + return send_clear() + } + return nil +} + +func tcsetattr(fd uintptr, termios *syscall_Termios) error { + r, _, e := syscall.Syscall(syscall.SYS_IOCTL, + fd, uintptr(syscall_TCSETS), uintptr(unsafe.Pointer(termios))) + if r != 0 { + return os.NewSyscallError("SYS_IOCTL", e) + } + return nil +} + +func tcgetattr(fd uintptr, termios *syscall_Termios) error { + r, _, e := syscall.Syscall(syscall.SYS_IOCTL, + fd, uintptr(syscall_TCGETS), uintptr(unsafe.Pointer(termios))) + if r != 0 { + return os.NewSyscallError("SYS_IOCTL", e) + } + return nil +} + +func parse_mouse_event(event *Event, buf string) (int, bool) { + if strings.HasPrefix(buf, "\033[M") && len(buf) >= 6 { + // X10 mouse encoding, the simplest one + // \033 [ M Cb Cx Cy + b := buf[3] - 32 + switch b & 3 { + case 0: + if b&64 != 0 { + event.Key = MouseWheelUp + } else { + event.Key = MouseLeft + } + case 1: + if b&64 != 0 { + event.Key = MouseWheelDown + } else { + event.Key = MouseMiddle + } + case 2: + event.Key = MouseRight + case 3: + event.Key = MouseRelease + default: + return 6, false + } + event.Type = EventMouse // KeyEvent by default + if b&32 != 0 { + event.Mod |= ModMotion + } + + // the coord is 1,1 for upper left + event.MouseX = int(buf[4]) - 1 - 32 + event.MouseY = int(buf[5]) - 1 - 32 + return 6, true + } else if strings.HasPrefix(buf, "\033[<") || strings.HasPrefix(buf, "\033[") { + // xterm 1006 extended mode or urxvt 1015 extended mode + // xterm: \033 [ < Cb ; Cx ; Cy (M or m) + // urxvt: \033 [ Cb ; Cx ; Cy M + + // find the first M or m, that's where we stop + mi := strings.IndexAny(buf, "Mm") + if mi == -1 { + return 0, false + } + + // whether it's a capital M or not + isM := buf[mi] == 'M' + + // whether it's urxvt or not + isU := false + + // buf[2] is safe here, because having M or m found means we have at + // least 3 bytes in a string + if buf[2] == '<' { + buf = buf[3:mi] + } else { + isU = true + buf = buf[2:mi] + } + + s1 := strings.Index(buf, ";") + s2 := strings.LastIndex(buf, ";") + // not found or only one ';' + if s1 == -1 || s2 == -1 || s1 == s2 { + return 0, false + } + + n1, err := strconv.ParseInt(buf[0:s1], 10, 64) + if err != nil { + return 0, false + } + n2, err := strconv.ParseInt(buf[s1+1:s2], 10, 64) + if err != nil { + return 0, false + } + n3, err := strconv.ParseInt(buf[s2+1:], 10, 64) + if err != nil { + return 0, false + } + + // on urxvt, first number is encoded exactly as in X10, but we need to + // make it zero-based, on xterm it is zero-based already + if isU { + n1 -= 32 + } + switch n1 & 3 { + case 0: + if n1&64 != 0 { + event.Key = MouseWheelUp + } else { + event.Key = MouseLeft + } + case 1: + if n1&64 != 0 { + event.Key = MouseWheelDown + } else { + event.Key = MouseMiddle + } + case 2: + event.Key = MouseRight + case 3: + event.Key = MouseRelease + default: + return mi + 1, false + } + if !isM { + // on xterm mouse release is signaled by lowercase m + event.Key = MouseRelease + } + + event.Type = EventMouse // KeyEvent by default + if n1&32 != 0 { + event.Mod |= ModMotion + } + + event.MouseX = int(n2) - 1 + event.MouseY = int(n3) - 1 + return mi + 1, true + } + + return 0, false +} + +func parse_escape_sequence(event *Event, buf []byte) (int, bool) { + bufstr := string(buf) + for i, key := range keys { + if strings.HasPrefix(bufstr, key) { + event.Ch = 0 + event.Key = Key(0xFFFF - i) + return len(key), true + } + } + + // if none of the keys match, let's try mouse seqences + return parse_mouse_event(event, bufstr) +} + +func extract_raw_event(data []byte, event *Event) bool { + if len(inbuf) == 0 { + return false + } + + n := len(data) + if n == 0 { + return false + } + + n = copy(data, inbuf) + copy(inbuf, inbuf[n:]) + inbuf = inbuf[:len(inbuf)-n] + + event.N = n + event.Type = EventRaw + return true +} + +func extract_event(inbuf []byte, event *Event) bool { + if len(inbuf) == 0 { + event.N = 0 + return false + } + + if inbuf[0] == '\033' { + // possible escape sequence + if n, ok := parse_escape_sequence(event, inbuf); n != 0 { + event.N = n + return ok + } + + // it's not escape sequence, then it's Alt or Esc, check input_mode + switch { + case input_mode&InputEsc != 0: + // if we're in escape mode, fill Esc event, pop buffer, return success + event.Ch = 0 + event.Key = KeyEsc + event.Mod = 0 + event.N = 1 + return true + case input_mode&InputAlt != 0: + // if we're in alt mode, set Alt modifier to event and redo parsing + event.Mod = ModAlt + ok := extract_event(inbuf[1:], event) + if ok { + event.N++ + } else { + event.N = 0 + } + return ok + default: + panic("unreachable") + } + } + + // if we're here, this is not an escape sequence and not an alt sequence + // so, it's a FUNCTIONAL KEY or a UNICODE character + + // first of all check if it's a functional key + if Key(inbuf[0]) <= KeySpace || Key(inbuf[0]) == KeyBackspace2 { + // fill event, pop buffer, return success + event.Ch = 0 + event.Key = Key(inbuf[0]) + event.N = 1 + return true + } + + // the only possible option is utf8 rune + if r, n := utf8.DecodeRune(inbuf); r != utf8.RuneError { + event.Ch = r + event.Key = 0 + event.N = n + return true + } + + return false +} + +func fcntl(fd int, cmd int, arg int) (val int, err error) { + r, _, e := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), + uintptr(arg)) + val = int(r) + if e != 0 { + err = e + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_common.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_common.go new file mode 100644 index 0000000..c3355cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_common.go @@ -0,0 +1,59 @@ +package termbox + +// private API, common OS agnostic part + +type cellbuf struct { + width int + height int + cells []Cell +} + +func (this *cellbuf) init(width, height int) { + this.width = width + this.height = height + this.cells = make([]Cell, width*height) +} + +func (this *cellbuf) resize(width, height int) { + if this.width == width && this.height == height { + return + } + + oldw := this.width + oldh := this.height + oldcells := this.cells + + this.init(width, height) + this.clear() + + minw, minh := oldw, oldh + + if width < minw { + minw = width + } + if height < minh { + minh = height + } + + for i := 0; i < minh; i++ { + srco, dsto := i*oldw, i*width + src := oldcells[srco : srco+minw] + dst := this.cells[dsto : dsto+minw] + copy(dst, src) + } +} + +func (this *cellbuf) clear() { + for i := range this.cells { + c := &this.cells[i] + c.Ch = ' ' + c.Fg = foreground + c.Bg = background + } +} + +const cursor_hidden = -1 + +func is_cursor_hidden(x, y int) bool { + return x == cursor_hidden || y == cursor_hidden +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_windows.go new file mode 100644 index 0000000..f7dad7b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/termbox_windows.go @@ -0,0 +1,856 @@ +package termbox + +import "syscall" +import "unsafe" +import "unicode/utf16" +import "github.com/mattn/go-runewidth" + +type ( + wchar uint16 + short int16 + dword uint32 + word uint16 + char_info struct { + char wchar + attr word + } + coord struct { + x short + y short + } + small_rect struct { + left short + top short + right short + bottom short + } + console_screen_buffer_info struct { + size coord + cursor_position coord + attributes word + window small_rect + maximum_window_size coord + } + console_cursor_info struct { + size dword + visible int32 + } + input_record struct { + event_type word + _ [2]byte + event [16]byte + } + key_event_record struct { + key_down int32 + repeat_count word + virtual_key_code word + virtual_scan_code word + unicode_char wchar + control_key_state dword + } + window_buffer_size_record struct { + size coord + } + mouse_event_record struct { + mouse_pos coord + button_state dword + control_key_state dword + event_flags dword + } +) + +const ( + mouse_lmb = 0x1 + mouse_rmb = 0x2 + mouse_mmb = 0x4 | 0x8 | 0x10 +) + +func (this coord) uintptr() uintptr { + return uintptr(*(*int32)(unsafe.Pointer(&this))) +} + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") +var is_cjk = runewidth.IsEastAsian() + +var ( + proc_set_console_active_screen_buffer = kernel32.NewProc("SetConsoleActiveScreenBuffer") + proc_set_console_screen_buffer_size = kernel32.NewProc("SetConsoleScreenBufferSize") + proc_create_console_screen_buffer = kernel32.NewProc("CreateConsoleScreenBuffer") + proc_get_console_screen_buffer_info = kernel32.NewProc("GetConsoleScreenBufferInfo") + proc_write_console_output = kernel32.NewProc("WriteConsoleOutputW") + proc_write_console_output_character = kernel32.NewProc("WriteConsoleOutputCharacterW") + proc_write_console_output_attribute = kernel32.NewProc("WriteConsoleOutputAttribute") + proc_set_console_cursor_info = kernel32.NewProc("SetConsoleCursorInfo") + proc_set_console_cursor_position = kernel32.NewProc("SetConsoleCursorPosition") + proc_get_console_cursor_info = kernel32.NewProc("GetConsoleCursorInfo") + proc_read_console_input = kernel32.NewProc("ReadConsoleInputW") + proc_get_console_mode = kernel32.NewProc("GetConsoleMode") + proc_set_console_mode = kernel32.NewProc("SetConsoleMode") + proc_fill_console_output_character = kernel32.NewProc("FillConsoleOutputCharacterW") + proc_fill_console_output_attribute = kernel32.NewProc("FillConsoleOutputAttribute") + proc_create_event = kernel32.NewProc("CreateEventW") + proc_wait_for_multiple_objects = kernel32.NewProc("WaitForMultipleObjects") + proc_set_event = kernel32.NewProc("SetEvent") +) + +func set_console_active_screen_buffer(h syscall.Handle) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_console_active_screen_buffer.Addr(), + 1, uintptr(h), 0, 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func set_console_screen_buffer_size(h syscall.Handle, size coord) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_console_screen_buffer_size.Addr(), + 2, uintptr(h), size.uintptr(), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func create_console_screen_buffer() (h syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(proc_create_console_screen_buffer.Addr(), + 5, uintptr(generic_read|generic_write), 0, 0, console_textmode_buffer, 0, 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return syscall.Handle(r0), err +} + +func get_console_screen_buffer_info(h syscall.Handle, info *console_screen_buffer_info) (err error) { + r0, _, e1 := syscall.Syscall(proc_get_console_screen_buffer_info.Addr(), + 2, uintptr(h), uintptr(unsafe.Pointer(info)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func write_console_output(h syscall.Handle, chars []char_info, dst small_rect) (err error) { + tmp_coord = coord{dst.right - dst.left + 1, dst.bottom - dst.top + 1} + tmp_rect = dst + r0, _, e1 := syscall.Syscall6(proc_write_console_output.Addr(), + 5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), tmp_coord.uintptr(), + tmp_coord0.uintptr(), uintptr(unsafe.Pointer(&tmp_rect)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func write_console_output_character(h syscall.Handle, chars []wchar, pos coord) (err error) { + r0, _, e1 := syscall.Syscall6(proc_write_console_output_character.Addr(), + 5, uintptr(h), uintptr(unsafe.Pointer(&chars[0])), uintptr(len(chars)), + pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func write_console_output_attribute(h syscall.Handle, attrs []word, pos coord) (err error) { + r0, _, e1 := syscall.Syscall6(proc_write_console_output_attribute.Addr(), + 5, uintptr(h), uintptr(unsafe.Pointer(&attrs[0])), uintptr(len(attrs)), + pos.uintptr(), uintptr(unsafe.Pointer(&tmp_arg)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func set_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_console_cursor_info.Addr(), + 2, uintptr(h), uintptr(unsafe.Pointer(info)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func get_console_cursor_info(h syscall.Handle, info *console_cursor_info) (err error) { + r0, _, e1 := syscall.Syscall(proc_get_console_cursor_info.Addr(), + 2, uintptr(h), uintptr(unsafe.Pointer(info)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func set_console_cursor_position(h syscall.Handle, pos coord) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_console_cursor_position.Addr(), + 2, uintptr(h), pos.uintptr(), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func read_console_input(h syscall.Handle, record *input_record) (err error) { + r0, _, e1 := syscall.Syscall6(proc_read_console_input.Addr(), + 4, uintptr(h), uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&tmp_arg)), 0, 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func get_console_mode(h syscall.Handle, mode *dword) (err error) { + r0, _, e1 := syscall.Syscall(proc_get_console_mode.Addr(), + 2, uintptr(h), uintptr(unsafe.Pointer(mode)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func set_console_mode(h syscall.Handle, mode dword) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_console_mode.Addr(), + 2, uintptr(h), uintptr(mode), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fill_console_output_character(h syscall.Handle, char wchar, n int) (err error) { + r0, _, e1 := syscall.Syscall6(proc_fill_console_output_character.Addr(), + 5, uintptr(h), uintptr(char), uintptr(n), tmp_coord.uintptr(), + uintptr(unsafe.Pointer(&tmp_arg)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func fill_console_output_attribute(h syscall.Handle, attr word, n int) (err error) { + r0, _, e1 := syscall.Syscall6(proc_fill_console_output_attribute.Addr(), + 5, uintptr(h), uintptr(attr), uintptr(n), tmp_coord.uintptr(), + uintptr(unsafe.Pointer(&tmp_arg)), 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func create_event() (out syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(proc_create_event.Addr(), + 4, 0, 0, 0, 0, 0, 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return syscall.Handle(r0), err +} + +func wait_for_multiple_objects(objects []syscall.Handle) (err error) { + r0, _, e1 := syscall.Syscall6(proc_wait_for_multiple_objects.Addr(), + 4, uintptr(len(objects)), uintptr(unsafe.Pointer(&objects[0])), + 0, 0xFFFFFFFF, 0, 0) + if uint32(r0) == 0xFFFFFFFF { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func set_event(ev syscall.Handle) (err error) { + r0, _, e1 := syscall.Syscall(proc_set_event.Addr(), + 1, uintptr(ev), 0, 0) + if int(r0) == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +type diff_msg struct { + pos short + lines short + chars []char_info +} + +type input_event struct { + event Event + err error +} + +var ( + orig_cursor_info console_cursor_info + orig_size coord + orig_mode dword + orig_screen syscall.Handle + back_buffer cellbuf + front_buffer cellbuf + term_size coord + input_mode = InputEsc + cursor_x = cursor_hidden + cursor_y = cursor_hidden + foreground = ColorDefault + background = ColorDefault + in syscall.Handle + out syscall.Handle + interrupt syscall.Handle + charbuf []char_info + diffbuf []diff_msg + beg_x = -1 + beg_y = -1 + beg_i = -1 + input_comm = make(chan Event) + interrupt_comm = make(chan struct{}) + cancel_comm = make(chan bool, 1) + cancel_done_comm = make(chan bool) + alt_mode_esc = false + + // these ones just to prevent heap allocs at all costs + tmp_info console_screen_buffer_info + tmp_arg dword + tmp_coord0 = coord{0, 0} + tmp_coord = coord{0, 0} + tmp_rect = small_rect{0, 0, 0, 0} +) + +func get_cursor_position(out syscall.Handle) coord { + err := get_console_screen_buffer_info(out, &tmp_info) + if err != nil { + panic(err) + } + return tmp_info.cursor_position +} + +func get_term_size(out syscall.Handle) coord { + err := get_console_screen_buffer_info(out, &tmp_info) + if err != nil { + panic(err) + } + return tmp_info.size +} + +func get_win_size(out syscall.Handle) coord { + err := get_console_screen_buffer_info(out, &tmp_info) + if err != nil { + panic(err) + } + return coord{ + x: tmp_info.window.right - tmp_info.window.left + 1, + y: tmp_info.window.bottom - tmp_info.window.top + 1, + } +} + +func update_size_maybe() { + size := get_term_size(out) + if size.x != term_size.x || size.y != term_size.y { + term_size = size + back_buffer.resize(int(size.x), int(size.y)) + front_buffer.resize(int(size.x), int(size.y)) + front_buffer.clear() + clear() + + area := int(size.x) * int(size.y) + if cap(charbuf) < area { + charbuf = make([]char_info, 0, area) + } + } +} + +var color_table_bg = []word{ + 0, // default (black) + 0, // black + background_red, + background_green, + background_red | background_green, // yellow + background_blue, + background_red | background_blue, // magenta + background_green | background_blue, // cyan + background_red | background_blue | background_green, // white +} + +var color_table_fg = []word{ + foreground_red | foreground_blue | foreground_green, // default (white) + 0, + foreground_red, + foreground_green, + foreground_red | foreground_green, // yellow + foreground_blue, + foreground_red | foreground_blue, // magenta + foreground_green | foreground_blue, // cyan + foreground_red | foreground_blue | foreground_green, // white +} + +const ( + replacement_char = '\uFFFD' + max_rune = '\U0010FFFF' + surr1 = 0xd800 + surr2 = 0xdc00 + surr3 = 0xe000 + surr_self = 0x10000 +) + +func append_diff_line(y int) int { + n := 0 + for x := 0; x < front_buffer.width; { + cell_offset := y*front_buffer.width + x + back := &back_buffer.cells[cell_offset] + front := &front_buffer.cells[cell_offset] + attr, char := cell_to_char_info(*back) + charbuf = append(charbuf, char_info{attr: attr, char: char[0]}) + *front = *back + n++ + w := runewidth.RuneWidth(back.Ch) + if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) { + w = 1 + } + x += w + // If not CJK, fill trailing space with whitespace + if !is_cjk && w == 2 { + charbuf = append(charbuf, char_info{attr: attr, char: ' '}) + } + } + return n +} + +// compares 'back_buffer' with 'front_buffer' and prepares all changes in the form of +// 'diff_msg's in the 'diff_buf' +func prepare_diff_messages() { + // clear buffers + diffbuf = diffbuf[:0] + charbuf = charbuf[:0] + + var diff diff_msg + gbeg := 0 + for y := 0; y < front_buffer.height; y++ { + same := true + line_offset := y * front_buffer.width + for x := 0; x < front_buffer.width; x++ { + cell_offset := line_offset + x + back := &back_buffer.cells[cell_offset] + front := &front_buffer.cells[cell_offset] + if *back != *front { + same = false + break + } + } + if same && diff.lines > 0 { + diffbuf = append(diffbuf, diff) + diff = diff_msg{} + } + if !same { + beg := len(charbuf) + end := beg + append_diff_line(y) + if diff.lines == 0 { + diff.pos = short(y) + gbeg = beg + } + diff.lines++ + diff.chars = charbuf[gbeg:end] + } + } + if diff.lines > 0 { + diffbuf = append(diffbuf, diff) + diff = diff_msg{} + } +} + +func get_ct(table []word, idx int) word { + idx = idx & 0x0F + if idx >= len(table) { + idx = len(table) - 1 + } + return table[idx] +} + +func cell_to_char_info(c Cell) (attr word, wc [2]wchar) { + attr = get_ct(color_table_fg, int(c.Fg)) | get_ct(color_table_bg, int(c.Bg)) + if c.Fg&AttrReverse|c.Bg&AttrReverse != 0 { + attr = (attr&0xF0)>>4 | (attr&0x0F)<<4 + } + if c.Fg&AttrBold != 0 { + attr |= foreground_intensity + } + if c.Bg&AttrBold != 0 { + attr |= background_intensity + } + + r0, r1 := utf16.EncodeRune(c.Ch) + if r0 == 0xFFFD { + wc[0] = wchar(c.Ch) + wc[1] = ' ' + } else { + wc[0] = wchar(r0) + wc[1] = wchar(r1) + } + return +} + +func move_cursor(x, y int) { + err := set_console_cursor_position(out, coord{short(x), short(y)}) + if err != nil { + panic(err) + } +} + +func show_cursor(visible bool) { + var v int32 + if visible { + v = 1 + } + + var info console_cursor_info + info.size = 100 + info.visible = v + err := set_console_cursor_info(out, &info) + if err != nil { + panic(err) + } +} + +func clear() { + var err error + attr, char := cell_to_char_info(Cell{ + ' ', + foreground, + background, + }) + + area := int(term_size.x) * int(term_size.y) + err = fill_console_output_attribute(out, attr, area) + if err != nil { + panic(err) + } + err = fill_console_output_character(out, char[0], area) + if err != nil { + panic(err) + } + if !is_cursor_hidden(cursor_x, cursor_y) { + move_cursor(cursor_x, cursor_y) + } +} + +func key_event_record_to_event(r *key_event_record) (Event, bool) { + if r.key_down == 0 { + return Event{}, false + } + + e := Event{Type: EventKey} + if input_mode&InputAlt != 0 { + if alt_mode_esc { + e.Mod = ModAlt + alt_mode_esc = false + } + if r.control_key_state&(left_alt_pressed|right_alt_pressed) != 0 { + e.Mod = ModAlt + } + } + + ctrlpressed := r.control_key_state&(left_ctrl_pressed|right_ctrl_pressed) != 0 + + if r.virtual_key_code >= vk_f1 && r.virtual_key_code <= vk_f12 { + switch r.virtual_key_code { + case vk_f1: + e.Key = KeyF1 + case vk_f2: + e.Key = KeyF2 + case vk_f3: + e.Key = KeyF3 + case vk_f4: + e.Key = KeyF4 + case vk_f5: + e.Key = KeyF5 + case vk_f6: + e.Key = KeyF6 + case vk_f7: + e.Key = KeyF7 + case vk_f8: + e.Key = KeyF8 + case vk_f9: + e.Key = KeyF9 + case vk_f10: + e.Key = KeyF10 + case vk_f11: + e.Key = KeyF11 + case vk_f12: + e.Key = KeyF12 + default: + panic("unreachable") + } + + return e, true + } + + if r.virtual_key_code <= vk_delete { + switch r.virtual_key_code { + case vk_insert: + e.Key = KeyInsert + case vk_delete: + e.Key = KeyDelete + case vk_home: + e.Key = KeyHome + case vk_end: + e.Key = KeyEnd + case vk_pgup: + e.Key = KeyPgup + case vk_pgdn: + e.Key = KeyPgdn + case vk_arrow_up: + e.Key = KeyArrowUp + case vk_arrow_down: + e.Key = KeyArrowDown + case vk_arrow_left: + e.Key = KeyArrowLeft + case vk_arrow_right: + e.Key = KeyArrowRight + case vk_backspace: + if ctrlpressed { + e.Key = KeyBackspace2 + } else { + e.Key = KeyBackspace + } + case vk_tab: + e.Key = KeyTab + case vk_enter: + e.Key = KeyEnter + case vk_esc: + switch { + case input_mode&InputEsc != 0: + e.Key = KeyEsc + case input_mode&InputAlt != 0: + alt_mode_esc = true + return Event{}, false + } + case vk_space: + if ctrlpressed { + // manual return here, because KeyCtrlSpace is zero + e.Key = KeyCtrlSpace + return e, true + } else { + e.Key = KeySpace + } + } + + if e.Key != 0 { + return e, true + } + } + + if ctrlpressed { + if Key(r.unicode_char) >= KeyCtrlA && Key(r.unicode_char) <= KeyCtrlRsqBracket { + e.Key = Key(r.unicode_char) + if input_mode&InputAlt != 0 && e.Key == KeyEsc { + alt_mode_esc = true + return Event{}, false + } + return e, true + } + switch r.virtual_key_code { + case 192, 50: + // manual return here, because KeyCtrl2 is zero + e.Key = KeyCtrl2 + return e, true + case 51: + if input_mode&InputAlt != 0 { + alt_mode_esc = true + return Event{}, false + } + e.Key = KeyCtrl3 + case 52: + e.Key = KeyCtrl4 + case 53: + e.Key = KeyCtrl5 + case 54: + e.Key = KeyCtrl6 + case 189, 191, 55: + e.Key = KeyCtrl7 + case 8, 56: + e.Key = KeyCtrl8 + } + + if e.Key != 0 { + return e, true + } + } + + if r.unicode_char != 0 { + e.Ch = rune(r.unicode_char) + return e, true + } + + return Event{}, false +} + +func input_event_producer() { + var r input_record + var err error + var last_button Key + var last_button_pressed Key + var last_state = dword(0) + var last_x, last_y = -1, -1 + handles := []syscall.Handle{in, interrupt} + for { + err = wait_for_multiple_objects(handles) + if err != nil { + input_comm <- Event{Type: EventError, Err: err} + } + + select { + case <-cancel_comm: + cancel_done_comm <- true + return + default: + } + + err = read_console_input(in, &r) + if err != nil { + input_comm <- Event{Type: EventError, Err: err} + } + + switch r.event_type { + case key_event: + kr := (*key_event_record)(unsafe.Pointer(&r.event)) + ev, ok := key_event_record_to_event(kr) + if ok { + for i := 0; i < int(kr.repeat_count); i++ { + input_comm <- ev + } + } + case window_buffer_size_event: + sr := *(*window_buffer_size_record)(unsafe.Pointer(&r.event)) + input_comm <- Event{ + Type: EventResize, + Width: int(sr.size.x), + Height: int(sr.size.y), + } + case mouse_event: + mr := *(*mouse_event_record)(unsafe.Pointer(&r.event)) + ev := Event{Type: EventMouse} + switch mr.event_flags { + case 0, 2: + // single or double click + cur_state := mr.button_state + switch { + case last_state&mouse_lmb == 0 && cur_state&mouse_lmb != 0: + last_button = MouseLeft + last_button_pressed = last_button + case last_state&mouse_rmb == 0 && cur_state&mouse_rmb != 0: + last_button = MouseRight + last_button_pressed = last_button + case last_state&mouse_mmb == 0 && cur_state&mouse_mmb != 0: + last_button = MouseMiddle + last_button_pressed = last_button + case last_state&mouse_lmb != 0 && cur_state&mouse_lmb == 0: + last_button = MouseRelease + case last_state&mouse_rmb != 0 && cur_state&mouse_rmb == 0: + last_button = MouseRelease + case last_state&mouse_mmb != 0 && cur_state&mouse_mmb == 0: + last_button = MouseRelease + default: + last_state = cur_state + continue + } + last_state = cur_state + ev.Key = last_button + last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y) + ev.MouseX = last_x + ev.MouseY = last_y + case 1: + // mouse motion + x, y := int(mr.mouse_pos.x), int(mr.mouse_pos.y) + if last_state != 0 && (last_x != x || last_y != y) { + ev.Key = last_button_pressed + ev.Mod = ModMotion + ev.MouseX = x + ev.MouseY = y + last_x, last_y = x, y + } else { + ev.Type = EventNone + } + case 4: + // mouse wheel + n := int16(mr.button_state >> 16) + if n > 0 { + ev.Key = MouseWheelUp + } else { + ev.Key = MouseWheelDown + } + last_x, last_y = int(mr.mouse_pos.x), int(mr.mouse_pos.y) + ev.MouseX = last_x + ev.MouseY = last_y + default: + ev.Type = EventNone + } + if ev.Type != EventNone { + input_comm <- ev + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo.go new file mode 100644 index 0000000..35dbd70 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo.go @@ -0,0 +1,221 @@ +// +build !windows +// This file contains a simple and incomplete implementation of the terminfo +// database. Information was taken from the ncurses manpages term(5) and +// terminfo(5). Currently, only the string capabilities for special keys and for +// functions without parameters are actually used. Colors are still done with +// ANSI escape sequences. Other special features that are not (yet?) supported +// are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database +// format and extended capabilities. + +package termbox + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io/ioutil" + "os" + "strings" +) + +const ( + ti_magic = 0432 + ti_header_length = 12 + ti_mouse_enter = "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" + ti_mouse_leave = "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" +) + +func load_terminfo() ([]byte, error) { + var data []byte + var err error + + term := os.Getenv("TERM") + if term == "" { + return nil, fmt.Errorf("termbox: TERM not set") + } + + // The following behaviour follows the one described in terminfo(5) as + // distributed by ncurses. + + terminfo := os.Getenv("TERMINFO") + if terminfo != "" { + // if TERMINFO is set, no other directory should be searched + return ti_try_path(terminfo) + } + + // next, consider ~/.terminfo + home := os.Getenv("HOME") + if home != "" { + data, err = ti_try_path(home + "/.terminfo") + if err == nil { + return data, nil + } + } + + // next, TERMINFO_DIRS + dirs := os.Getenv("TERMINFO_DIRS") + if dirs != "" { + for _, dir := range strings.Split(dirs, ":") { + if dir == "" { + // "" -> "/usr/share/terminfo" + dir = "/usr/share/terminfo" + } + data, err = ti_try_path(dir) + if err == nil { + return data, nil + } + } + } + + // fall back to /usr/share/terminfo + return ti_try_path("/usr/share/terminfo") +} + +func ti_try_path(path string) (data []byte, err error) { + // load_terminfo already made sure it is set + term := os.Getenv("TERM") + + // first try, the typical *nix path + terminfo := path + "/" + term[0:1] + "/" + term + data, err = ioutil.ReadFile(terminfo) + if err == nil { + return + } + + // fallback to darwin specific dirs structure + terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term + data, err = ioutil.ReadFile(terminfo) + return +} + +func setup_term_builtin() error { + name := os.Getenv("TERM") + if name == "" { + return errors.New("termbox: TERM environment variable not set") + } + + for _, t := range terms { + if t.name == name { + keys = t.keys + funcs = t.funcs + return nil + } + } + + compat_table := []struct { + partial string + keys []string + funcs []string + }{ + {"xterm", xterm_keys, xterm_funcs}, + {"rxvt", rxvt_unicode_keys, rxvt_unicode_funcs}, + {"linux", linux_keys, linux_funcs}, + {"Eterm", eterm_keys, eterm_funcs}, + {"screen", screen_keys, screen_funcs}, + // let's assume that 'cygwin' is xterm compatible + {"cygwin", xterm_keys, xterm_funcs}, + {"st", xterm_keys, xterm_funcs}, + } + + // try compatibility variants + for _, it := range compat_table { + if strings.Contains(name, it.partial) { + keys = it.keys + funcs = it.funcs + return nil + } + } + + return errors.New("termbox: unsupported terminal") +} + +func setup_term() (err error) { + var data []byte + var header [6]int16 + var str_offset, table_offset int16 + + data, err = load_terminfo() + if err != nil { + return setup_term_builtin() + } + + rd := bytes.NewReader(data) + // 0: magic number, 1: size of names section, 2: size of boolean section, 3: + // size of numbers section (in integers), 4: size of the strings section (in + // integers), 5: size of the string table + + err = binary.Read(rd, binary.LittleEndian, header[:]) + if err != nil { + return + } + + if (header[1]+header[2])%2 != 0 { + // old quirk to align everything on word boundaries + header[2] += 1 + } + str_offset = ti_header_length + header[1] + header[2] + 2*header[3] + table_offset = str_offset + 2*header[4] + + keys = make([]string, 0xFFFF-key_min) + for i, _ := range keys { + keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset) + if err != nil { + return + } + } + funcs = make([]string, t_max_funcs) + // the last two entries are reserved for mouse. because the table offset is + // not there, the two entries have to fill in manually + for i, _ := range funcs[:len(funcs)-2] { + funcs[i], err = ti_read_string(rd, str_offset+2*ti_funcs[i], table_offset) + if err != nil { + return + } + } + funcs[t_max_funcs-2] = ti_mouse_enter + funcs[t_max_funcs-1] = ti_mouse_leave + return nil +} + +func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) { + var off int16 + + _, err := rd.Seek(int64(str_off), 0) + if err != nil { + return "", err + } + err = binary.Read(rd, binary.LittleEndian, &off) + if err != nil { + return "", err + } + _, err = rd.Seek(int64(table+off), 0) + if err != nil { + return "", err + } + var bs []byte + for { + b, err := rd.ReadByte() + if err != nil { + return "", err + } + if b == byte(0x00) { + break + } + bs = append(bs, b) + } + return string(bs), nil +} + +// "Maps" the function constants from termbox.go to the number of the respective +// string capability in the terminfo file. Taken from (ncurses) term.h. +var ti_funcs = []int16{ + 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, +} + +// Same as above for the special keys. +var ti_keys = []int16{ + 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70, + 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo_builtin.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo_builtin.go new file mode 100644 index 0000000..a948660 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/nsf/termbox-go/terminfo_builtin.go @@ -0,0 +1,64 @@ +// +build !windows + +package termbox + +// Eterm +var eterm_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var eterm_funcs = []string{ + "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", +} + +// screen +var screen_keys = []string{ + "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", +} +var screen_funcs = []string{ + "\x1b[?1049h", "\x1b[?1049l", "\x1b[34h\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// xterm +var xterm_keys = []string{ + "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1bOH", "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", +} +var xterm_funcs = []string{ + "\x1b[?1049h", "\x1b[?1049l", "\x1b[?12l\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b(B\x1b[m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// rxvt-unicode +var rxvt_unicode_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var rxvt_unicode_funcs = []string{ + "\x1b[?1049h", "\x1b[r\x1b[?1049l", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x1b(B", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +// linux +var linux_keys = []string{ + "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var linux_funcs = []string{ + "", "", "\x1b[?25h\x1b[?0c", "\x1b[?25l\x1b[?1c", "\x1b[H\x1b[J", "\x1b[0;10m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", +} + +// rxvt-256color +var rxvt_256color_keys = []string{ + "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", +} +var rxvt_256color_funcs = []string{ + "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, +} + +var terms = []struct { + name string + keys []string + funcs []string +}{ + {"Eterm", eterm_keys, eterm_funcs}, + {"screen", screen_keys, screen_funcs}, + {"xterm", xterm_keys, xterm_funcs}, + {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, + {"linux", linux_keys, linux_funcs}, + {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTING.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTING.md new file mode 100644 index 0000000..04fdf09 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# How to contribute + +We definitely welcome patches and contribution to this project! + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +You may have already signed it for other Google projects. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTORS new file mode 100644 index 0000000..b382a04 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/CONTRIBUTORS @@ -0,0 +1 @@ +Paul Borman diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/LICENSE new file mode 100644 index 0000000..5dc6826 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/README.md new file mode 100644 index 0000000..b0396b2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/README.md @@ -0,0 +1,13 @@ +This project was automatically exported from code.google.com/p/go-uuid + +# uuid ![build status](https://travis-ci.org/pborman/uuid.svg?branch=master) +The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. + +###### Install +`go get github.com/pborman/uuid` + +###### Documentation +[![GoDoc](https://godoc.org/github.com/pborman/uuid?status.svg)](http://godoc.org/github.com/pborman/uuid) + +Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here: +http://godoc.org/github.com/pborman/uuid diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/dce.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/dce.go new file mode 100644 index 0000000..50a0f2d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/dce.go @@ -0,0 +1,84 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) UUID { + uuid := NewUUID() + if uuid != nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCEPerson(Person, uint32(os.Getuid())) +func NewDCEPerson() UUID { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCEGroup(Group, uint32(os.Getgid())) +func NewDCEGroup() UUID { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID or false. +func (uuid UUID) Domain() (Domain, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return Domain(uuid[9]), true +} + +// Id returns the id for a Version 2 UUID or false. +func (uuid UUID) Id() (uint32, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return binary.BigEndian.Uint32(uuid[0:4]), true +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/doc.go new file mode 100644 index 0000000..d8bd013 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/doc.go @@ -0,0 +1,8 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uuid package generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. +package uuid diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/hash.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/hash.go new file mode 100644 index 0000000..a0420c1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known Name Space IDs and UUIDs +var ( + NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NIL = Parse("00000000-0000-0000-0000-000000000000") +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space) + h.Write([]byte(data)) + s := h.Sum(nil) + uuid := make([]byte, 16) + copy(uuid, s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/marshal.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/marshal.go new file mode 100644 index 0000000..6621dd5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/marshal.go @@ -0,0 +1,83 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "errors" + "fmt" +) + +// MarshalText implements encoding.TextMarshaler. +func (u UUID) MarshalText() ([]byte, error) { + if len(u) != 16 { + return nil, nil + } + var js [36]byte + encodeHex(js[:], u) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (u *UUID) UnmarshalText(data []byte) error { + if len(data) == 0 { + return nil + } + id := Parse(string(data)) + if id == nil { + return errors.New("invalid UUID") + } + *u = id + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (u UUID) MarshalBinary() ([]byte, error) { + return u[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (u *UUID) UnmarshalBinary(data []byte) error { + if len(data) == 0 { + return nil + } + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + var id [16]byte + copy(id[:], data) + *u = id[:] + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (u Array) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], u[:]) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (u *Array) UnmarshalText(data []byte) error { + id := Parse(string(data)) + if id == nil { + return errors.New("invalid UUID") + } + *u = id.Array() + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (u Array) MarshalBinary() ([]byte, error) { + return u[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (u *Array) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(u[:], data) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/node.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/node.go new file mode 100644 index 0000000..42d60da --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/node.go @@ -0,0 +1,117 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "net" + "sync" +) + +var ( + nodeMu sync.Mutex + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID []byte // hardware for version 1 UUIDs +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + if setNodeID(ifs.HardwareAddr) { + ifname = ifs.Name + return true + } + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + if nodeID == nil { + nodeID = make([]byte, 6) + } + randomBits(nodeID) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == nil { + setNodeInterface("") + } + nid := make([]byte, 6) + copy(nid, nodeID) + return nid +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + if setNodeID(id) { + ifname = "user" + return true + } + return false +} + +func setNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + if nodeID == nil { + nodeID = make([]byte, 6) + } + copy(nodeID, id) + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + node := make([]byte, 6) + copy(node, uuid[10:]) + return node +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/sql.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/sql.go new file mode 100644 index 0000000..d015bfd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/sql.go @@ -0,0 +1,66 @@ +// Copyright 2015 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "errors" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src.(type) { + case string: + // if an empty UUID comes from a table, we return a null UUID + if src.(string) == "" { + return nil + } + + // see uuid.Parse for required string format + parsed := Parse(src.(string)) + + if parsed == nil { + return errors.New("Scan: invalid UUID format") + } + + *uuid = parsed + case []byte: + b := src.([]byte) + + // if an empty UUID comes from a table, we return a null UUID + if len(b) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(b) == 16 { + *uuid = UUID(b) + } else { + u := Parse(string(b)) + + if u == nil { + return errors.New("Scan: invalid UUID format") + } + + *uuid = u + } + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/time.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/time.go new file mode 100644 index 0000000..eedf242 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/time.go @@ -0,0 +1,132 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clock_seq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clock_seq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clock_seq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence a new random +// clock sequence is generated the first time a clock sequence is requested by +// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated +// for +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clock_seq == 0 { + setClockSequence(-1) + } + return int(clock_seq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clock_seq + clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clock_seq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. It returns false if uuid is not valid. The time is only well defined +// for version 1 and 2 UUIDs. +func (uuid UUID) Time() (Time, bool) { + if len(uuid) != 16 { + return 0, false + } + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time), true +} + +// ClockSequence returns the clock sequence encoded in uuid. It returns false +// if uuid is not valid. The clock sequence is only well defined for version 1 +// and 2 UUIDs. +func (uuid UUID) ClockSequence() (int, bool) { + if len(uuid) != 16 { + return 0, false + } + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/util.go new file mode 100644 index 0000000..fc8e052 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts the the first two hex bytes of x into a byte. +func xtob(x string) (byte, bool) { + b1 := xvalues[x[0]] + b2 := xvalues[x[1]] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/uuid.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/uuid.go new file mode 100644 index 0000000..7c643cf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/uuid.go @@ -0,0 +1,201 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "strings" +) + +// Array is a pass-by-value UUID that can be used as an effecient key in a map. +type Array [16]byte + +// UUID converts uuid into a slice. +func (uuid Array) UUID() UUID { + return uuid[:] +} + +// String returns the string representation of uuid, +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (uuid Array) String() string { + return uuid.UUID().String() +} + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID []byte + +// A Version represents a UUIDs version. +type Version byte + +// A Variant represents a UUIDs variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// New returns a new random (version 4) UUID as a string. It is a convenience +// function for NewRandom().String(). +func New() string { + return NewRandom().String() +} + +// Parse decodes s into a UUID or returns nil. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) UUID { + if len(s) == 36+9 { + if strings.ToLower(s[:9]) != "urn:uuid:" { + return nil + } + s = s[9:] + } else if len(s) != 36 { + return nil + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return nil + } + var uuid [16]byte + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x:]); !ok { + return nil + } else { + uuid[i] = v + } + } + return uuid[:] +} + +// Equal returns true if uuid1 and uuid2 are equal. +func Equal(uuid1, uuid2 UUID) bool { + return bytes.Equal(uuid1, uuid2) +} + +// Array returns an array representation of uuid that can be used as a map key. +// Array panics if uuid is not valid. +func (uuid UUID) Array() Array { + if len(uuid) != 16 { + panic("invalid uuid") + } + var a Array + copy(a[:], uuid) + return a +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + if len(uuid) != 16 { + return "" + } + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + if len(uuid) != 16 { + return "" + } + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst[:], uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. It returns Invalid if +// uuid is invalid. +func (uuid UUID) Variant() Variant { + if len(uuid) != 16 { + return Invalid + } + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. It returns false if uuid is not +// valid. +func (uuid UUID) Version() (Version, bool) { + if len(uuid) != 16 { + return 0, false + } + return Version(uuid[6] >> 4), true +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version1.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version1.go new file mode 100644 index 0000000..0127eac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version1.go @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil. +func NewUUID() UUID { + if nodeID == nil { + SetNodeInterface("") + } + + now, seq, err := GetTime() + if err != nil { + return nil + } + + uuid := make([]byte, 16) + + time_low := uint32(now & 0xffffffff) + time_mid := uint16((now >> 32) & 0xffff) + time_hi := uint16((now >> 48) & 0x0fff) + time_hi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], time_low) + binary.BigEndian.PutUint16(uuid[4:], time_mid) + binary.BigEndian.PutUint16(uuid[6:], time_hi) + binary.BigEndian.PutUint16(uuid[8:], seq) + copy(uuid[10:], nodeID) + + return uuid +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version4.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version4.go new file mode 100644 index 0000000..b3d4a36 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/pborman/uuid/version4.go @@ -0,0 +1,25 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +// Random returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() UUID { + uuid := make([]byte, 16) + randomBits([]byte(uuid)) + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/COPYING b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/COPYING new file mode 100644 index 0000000..9e8c9f2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/COPYING @@ -0,0 +1,21 @@ +Copyright © 2012 Peter Harris + +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 (including the next +paragraph) 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. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/README.md new file mode 100644 index 0000000..9148b24 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/README.md @@ -0,0 +1,100 @@ +Liner +===== + +Liner is a command line editor with history. It was inspired by linenoise; +everything Unix-like is a VT100 (or is trying very hard to be). If your +terminal is not pretending to be a VT100, change it. Liner also support +Windows. + +Liner is released under the X11 license (which is similar to the new BSD +license). + +Line Editing +------------ + +The following line editing commands are supported on platforms and terminals +that Liner supports: + +Keystroke | Action +--------- | ------ +Ctrl-A, Home | Move cursor to beginning of line +Ctrl-E, End | Move cursor to end of line +Ctrl-B, Left | Move cursor one character left +Ctrl-F, Right| Move cursor one character right +Ctrl-Left, Alt-B | Move cursor to previous word +Ctrl-Right, Alt-F | Move cursor to next word +Ctrl-D, Del | (if line is *not* empty) Delete character under cursor +Ctrl-D | (if line *is* empty) End of File - usually quits application +Ctrl-C | Reset input (create new empty prompt) +Ctrl-L | Clear screen (line is unmodified) +Ctrl-T | Transpose previous character with current character +Ctrl-H, BackSpace | Delete character before cursor +Ctrl-W | Delete word leading up to cursor +Ctrl-K | Delete from cursor to end of line +Ctrl-U | Delete from start of line to cursor +Ctrl-P, Up | Previous match from history +Ctrl-N, Down | Next match from history +Ctrl-R | Reverse Search history (Ctrl-S forward, Ctrl-G cancel) +Ctrl-Y | Paste from Yank buffer (Alt-Y to paste next yank instead) +Tab | Next completion +Shift-Tab | (after Tab) Previous completion + +Getting started +----------------- + +```go +package main + +import ( + "log" + "os" + "path/filepath" + "strings" + + "github.com/peterh/liner" +) + +var ( + history_fn = filepath.Join(os.TempDir(), ".liner_example_history") + names = []string{"john", "james", "mary", "nancy"} +) + +func main() { + line := liner.NewLiner() + defer line.Close() + + line.SetCtrlCAborts(true) + + line.SetCompleter(func(line string) (c []string) { + for _, n := range names { + if strings.HasPrefix(n, strings.ToLower(line)) { + c = append(c, n) + } + } + return + }) + + if f, err := os.Open(history_fn); err == nil { + line.ReadHistory(f) + f.Close() + } + + if name, err := line.Prompt("What is your name? "); err == nil { + log.Print("Got: ", name) + line.AppendHistory(name) + } else if err == liner.ErrPromptAborted { + log.Print("Aborted") + } else { + log.Print("Error reading line: ", err) + } + + if f, err := os.Create(history_fn); err != nil { + log.Print("Error writing history file: ", err) + } else { + line.WriteHistory(f) + f.Close() + } +} +``` + +For documentation, see http://godoc.org/github.com/peterh/liner diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/bsdinput.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/bsdinput.go new file mode 100644 index 0000000..3593398 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/bsdinput.go @@ -0,0 +1,41 @@ +// +build openbsd freebsd netbsd + +package liner + +import "syscall" + +const ( + getTermios = syscall.TIOCGETA + setTermios = syscall.TIOCSETA +) + +const ( + // Input flags + inpck = 0x010 + istrip = 0x020 + icrnl = 0x100 + ixon = 0x200 + + // Output flags + opost = 0x1 + + // Control flags + cs8 = 0x300 + + // Local flags + isig = 0x080 + icanon = 0x100 + iexten = 0x400 +) + +type termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]byte + Ispeed int32 + Ospeed int32 +} + +const cursorColumn = false diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/common.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/common.go new file mode 100644 index 0000000..e5b8fc2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/common.go @@ -0,0 +1,250 @@ +/* +Package liner implements a simple command line editor, inspired by linenoise +(https://github.com/antirez/linenoise/). This package supports WIN32 in +addition to the xterm codes supported by everything else. +*/ +package liner + +import ( + "bufio" + "container/ring" + "errors" + "fmt" + "io" + "strings" + "sync" + "unicode/utf8" +) + +type commonState struct { + terminalSupported bool + outputRedirected bool + inputRedirected bool + history []string + historyMutex sync.RWMutex + completer WordCompleter + columns int + killRing *ring.Ring + ctrlCAborts bool + r *bufio.Reader + tabStyle TabStyle + multiLineMode bool + cursorRows int + maxRows int + shouldRestart ShouldRestart + needRefresh bool +} + +// TabStyle is used to select how tab completions are displayed. +type TabStyle int + +// Two tab styles are currently available: +// +// TabCircular cycles through each completion item and displays it directly on +// the prompt +// +// TabPrints prints the list of completion items to the screen after a second +// tab key is pressed. This behaves similar to GNU readline and BASH (which +// uses readline) +const ( + TabCircular TabStyle = iota + TabPrints +) + +// ErrPromptAborted is returned from Prompt or PasswordPrompt when the user presses Ctrl-C +// if SetCtrlCAborts(true) has been called on the State +var ErrPromptAborted = errors.New("prompt aborted") + +// ErrNotTerminalOutput is returned from Prompt or PasswordPrompt if the +// platform is normally supported, but stdout has been redirected +var ErrNotTerminalOutput = errors.New("standard output is not a terminal") + +// ErrInvalidPrompt is returned from Prompt or PasswordPrompt if the +// prompt contains any unprintable runes (including substrings that could +// be colour codes on some platforms). +var ErrInvalidPrompt = errors.New("invalid prompt") + +// KillRingMax is the max number of elements to save on the killring. +const KillRingMax = 60 + +// HistoryLimit is the maximum number of entries saved in the scrollback history. +const HistoryLimit = 1000 + +// ReadHistory reads scrollback history from r. Returns the number of lines +// read, and any read error (except io.EOF). +func (s *State) ReadHistory(r io.Reader) (num int, err error) { + s.historyMutex.Lock() + defer s.historyMutex.Unlock() + + in := bufio.NewReader(r) + num = 0 + for { + line, part, err := in.ReadLine() + if err == io.EOF { + break + } + if err != nil { + return num, err + } + if part { + return num, fmt.Errorf("line %d is too long", num+1) + } + if !utf8.Valid(line) { + return num, fmt.Errorf("invalid string at line %d", num+1) + } + num++ + s.history = append(s.history, string(line)) + if len(s.history) > HistoryLimit { + s.history = s.history[1:] + } + } + return num, nil +} + +// WriteHistory writes scrollback history to w. Returns the number of lines +// successfully written, and any write error. +// +// Unlike the rest of liner's API, WriteHistory is safe to call +// from another goroutine while Prompt is in progress. +// This exception is to facilitate the saving of the history buffer +// during an unexpected exit (for example, due to Ctrl-C being invoked) +func (s *State) WriteHistory(w io.Writer) (num int, err error) { + s.historyMutex.RLock() + defer s.historyMutex.RUnlock() + + for _, item := range s.history { + _, err := fmt.Fprintln(w, item) + if err != nil { + return num, err + } + num++ + } + return num, nil +} + +// AppendHistory appends an entry to the scrollback history. AppendHistory +// should be called iff Prompt returns a valid command. +func (s *State) AppendHistory(item string) { + s.historyMutex.Lock() + defer s.historyMutex.Unlock() + + if len(s.history) > 0 { + if item == s.history[len(s.history)-1] { + return + } + } + s.history = append(s.history, item) + if len(s.history) > HistoryLimit { + s.history = s.history[1:] + } +} + +// ClearHistory clears the scroollback history. +func (s *State) ClearHistory() { + s.historyMutex.Lock() + defer s.historyMutex.Unlock() + s.history = nil +} + +// Returns the history lines starting with prefix +func (s *State) getHistoryByPrefix(prefix string) (ph []string) { + for _, h := range s.history { + if strings.HasPrefix(h, prefix) { + ph = append(ph, h) + } + } + return +} + +// Returns the history lines matching the inteligent search +func (s *State) getHistoryByPattern(pattern string) (ph []string, pos []int) { + if pattern == "" { + return + } + for _, h := range s.history { + if i := strings.Index(h, pattern); i >= 0 { + ph = append(ph, h) + pos = append(pos, i) + } + } + return +} + +// Completer takes the currently edited line content at the left of the cursor +// and returns a list of completion candidates. +// If the line is "Hello, wo!!!" and the cursor is before the first '!', "Hello, wo" is passed +// to the completer which may return {"Hello, world", "Hello, Word"} to have "Hello, world!!!". +type Completer func(line string) []string + +// WordCompleter takes the currently edited line with the cursor position and +// returns the completion candidates for the partial word to be completed. +// If the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, wo!!!", 9) is passed +// to the completer which may returns ("Hello, ", {"world", "Word"}, "!!!") to have "Hello, world!!!". +type WordCompleter func(line string, pos int) (head string, completions []string, tail string) + +// SetCompleter sets the completion function that Liner will call to +// fetch completion candidates when the user presses tab. +func (s *State) SetCompleter(f Completer) { + if f == nil { + s.completer = nil + return + } + s.completer = func(line string, pos int) (string, []string, string) { + return "", f(string([]rune(line)[:pos])), string([]rune(line)[pos:]) + } +} + +// SetWordCompleter sets the completion function that Liner will call to +// fetch completion candidates when the user presses tab. +func (s *State) SetWordCompleter(f WordCompleter) { + s.completer = f +} + +// SetTabCompletionStyle sets the behvavior when the Tab key is pressed +// for auto-completion. TabCircular is the default behavior and cycles +// through the list of candidates at the prompt. TabPrints will print +// the available completion candidates to the screen similar to BASH +// and GNU Readline +func (s *State) SetTabCompletionStyle(tabStyle TabStyle) { + s.tabStyle = tabStyle +} + +// ModeApplier is the interface that wraps a representation of the terminal +// mode. ApplyMode sets the terminal to this mode. +type ModeApplier interface { + ApplyMode() error +} + +// SetCtrlCAborts sets whether Prompt on a supported terminal will return an +// ErrPromptAborted when Ctrl-C is pressed. The default is false (will not +// return when Ctrl-C is pressed). Unsupported terminals typically raise SIGINT +// (and Prompt does not return) regardless of the value passed to SetCtrlCAborts. +func (s *State) SetCtrlCAborts(aborts bool) { + s.ctrlCAborts = aborts +} + +// SetMultiLineMode sets whether line is auto-wrapped. The default is false (single line). +func (s *State) SetMultiLineMode(mlmode bool) { + s.multiLineMode = mlmode +} + +// ShouldRestart is passed the error generated by readNext and returns true if +// the the read should be restarted or false if the error should be returned. +type ShouldRestart func(err error) bool + +// SetShouldRestart sets the restart function that Liner will call to determine +// whether to retry the call to, or return the error returned by, readNext. +func (s *State) SetShouldRestart(f ShouldRestart) { + s.shouldRestart = f +} + +func (s *State) promptUnsupported(p string) (string, error) { + if !s.inputRedirected || !s.terminalSupported { + fmt.Print(p) + } + linebuf, _, err := s.r.ReadLine() + if err != nil { + return "", err + } + return string(linebuf), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/fallbackinput.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/fallbackinput.go new file mode 100644 index 0000000..d9eb79d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/fallbackinput.go @@ -0,0 +1,57 @@ +// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd + +package liner + +import ( + "bufio" + "errors" + "os" +) + +// State represents an open terminal +type State struct { + commonState +} + +// Prompt displays p, and then waits for user input. Prompt does not support +// line editing on this operating system. +func (s *State) Prompt(p string) (string, error) { + return s.promptUnsupported(p) +} + +// PasswordPrompt is not supported in this OS. +func (s *State) PasswordPrompt(p string) (string, error) { + return "", errors.New("liner: function not supported in this terminal") +} + +// NewLiner initializes a new *State +// +// Note that this operating system uses a fallback mode without line +// editing. Patches welcome. +func NewLiner() *State { + var s State + s.r = bufio.NewReader(os.Stdin) + return &s +} + +// Close returns the terminal to its previous mode +func (s *State) Close() error { + return nil +} + +// TerminalSupported returns false because line editing is not +// supported on this platform. +func TerminalSupported() bool { + return false +} + +type noopMode struct{} + +func (n noopMode) ApplyMode() error { + return nil +} + +// TerminalMode returns a noop InputModeSetter on this platform. +func TerminalMode() (ModeApplier, error) { + return noopMode{}, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input.go new file mode 100644 index 0000000..904fbf6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input.go @@ -0,0 +1,364 @@ +// +build linux darwin openbsd freebsd netbsd + +package liner + +import ( + "bufio" + "errors" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" +) + +type nexter struct { + r rune + err error +} + +// State represents an open terminal +type State struct { + commonState + origMode termios + defaultMode termios + next <-chan nexter + winch chan os.Signal + pending []rune + useCHA bool +} + +// NewLiner initializes a new *State, and sets the terminal into raw mode. To +// restore the terminal to its previous state, call State.Close(). +func NewLiner() *State { + var s State + s.r = bufio.NewReader(os.Stdin) + + s.terminalSupported = TerminalSupported() + if m, err := TerminalMode(); err == nil { + s.origMode = *m.(*termios) + } else { + s.inputRedirected = true + } + if _, err := getMode(syscall.Stdout); err != 0 { + s.outputRedirected = true + } + if s.inputRedirected && s.outputRedirected { + s.terminalSupported = false + } + if s.terminalSupported && !s.inputRedirected && !s.outputRedirected { + mode := s.origMode + mode.Iflag &^= icrnl | inpck | istrip | ixon + mode.Cflag |= cs8 + mode.Lflag &^= syscall.ECHO | icanon | iexten + mode.ApplyMode() + + winch := make(chan os.Signal, 1) + signal.Notify(winch, syscall.SIGWINCH) + s.winch = winch + + s.checkOutput() + } + + if !s.outputRedirected { + s.outputRedirected = !s.getColumns() + } + + return &s +} + +var errTimedOut = errors.New("timeout") + +func (s *State) startPrompt() { + if s.terminalSupported { + if m, err := TerminalMode(); err == nil { + s.defaultMode = *m.(*termios) + mode := s.defaultMode + mode.Lflag &^= isig + mode.ApplyMode() + } + } + s.restartPrompt() +} + +func (s *State) inputWaiting() bool { + return len(s.next) > 0 +} + +func (s *State) restartPrompt() { + next := make(chan nexter, 200) + go func() { + for { + var n nexter + n.r, _, n.err = s.r.ReadRune() + next <- n + // Shut down nexter loop when an end condition has been reached + if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD { + close(next) + return + } + } + }() + s.next = next +} + +func (s *State) stopPrompt() { + if s.terminalSupported { + s.defaultMode.ApplyMode() + } +} + +func (s *State) nextPending(timeout <-chan time.Time) (rune, error) { + select { + case thing, ok := <-s.next: + if !ok { + return 0, errors.New("liner: internal error") + } + if thing.err != nil { + return 0, thing.err + } + s.pending = append(s.pending, thing.r) + return thing.r, nil + case <-timeout: + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, errTimedOut + } +} + +func (s *State) readNext() (interface{}, error) { + if len(s.pending) > 0 { + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + var r rune + select { + case thing, ok := <-s.next: + if !ok { + return 0, errors.New("liner: internal error") + } + if thing.err != nil { + return nil, thing.err + } + r = thing.r + case <-s.winch: + s.getColumns() + return winch, nil + } + if r != esc { + return r, nil + } + s.pending = append(s.pending, r) + + // Wait at most 50 ms for the rest of the escape sequence + // If nothing else arrives, it was an actual press of the esc key + timeout := time.After(50 * time.Millisecond) + flag, err := s.nextPending(timeout) + if err != nil { + if err == errTimedOut { + return flag, nil + } + return unknown, err + } + + switch flag { + case '[': + code, err := s.nextPending(timeout) + if err != nil { + if err == errTimedOut { + return code, nil + } + return unknown, err + } + switch code { + case 'A': + s.pending = s.pending[:0] // escape code complete + return up, nil + case 'B': + s.pending = s.pending[:0] // escape code complete + return down, nil + case 'C': + s.pending = s.pending[:0] // escape code complete + return right, nil + case 'D': + s.pending = s.pending[:0] // escape code complete + return left, nil + case 'F': + s.pending = s.pending[:0] // escape code complete + return end, nil + case 'H': + s.pending = s.pending[:0] // escape code complete + return home, nil + case 'Z': + s.pending = s.pending[:0] // escape code complete + return shiftTab, nil + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + num := []rune{code} + for { + code, err := s.nextPending(timeout) + if err != nil { + if err == errTimedOut { + return code, nil + } + return nil, err + } + switch code { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + num = append(num, code) + case ';': + // Modifier code to follow + // This only supports Ctrl-left and Ctrl-right for now + x, _ := strconv.ParseInt(string(num), 10, 32) + if x != 1 { + // Can't be left or right + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + num = num[:0] + for { + code, err = s.nextPending(timeout) + if err != nil { + if err == errTimedOut { + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + return nil, err + } + switch code { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + num = append(num, code) + case 'C', 'D': + // right, left + mod, _ := strconv.ParseInt(string(num), 10, 32) + if mod != 5 { + // Not bare Ctrl + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + s.pending = s.pending[:0] // escape code complete + if code == 'C' { + return wordRight, nil + } + return wordLeft, nil + default: + // Not left or right + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + } + case '~': + s.pending = s.pending[:0] // escape code complete + x, _ := strconv.ParseInt(string(num), 10, 32) + switch x { + case 2: + return insert, nil + case 3: + return del, nil + case 5: + return pageUp, nil + case 6: + return pageDown, nil + case 7: + return home, nil + case 8: + return end, nil + case 15: + return f5, nil + case 17: + return f6, nil + case 18: + return f7, nil + case 19: + return f8, nil + case 20: + return f9, nil + case 21: + return f10, nil + case 23: + return f11, nil + case 24: + return f12, nil + default: + return unknown, nil + } + default: + // unrecognized escape code + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + } + } + + case 'O': + code, err := s.nextPending(timeout) + if err != nil { + if err == errTimedOut { + return code, nil + } + return nil, err + } + s.pending = s.pending[:0] // escape code complete + switch code { + case 'c': + return wordRight, nil + case 'd': + return wordLeft, nil + case 'H': + return home, nil + case 'F': + return end, nil + case 'P': + return f1, nil + case 'Q': + return f2, nil + case 'R': + return f3, nil + case 'S': + return f4, nil + default: + return unknown, nil + } + case 'b': + s.pending = s.pending[:0] // escape code complete + return altB, nil + case 'f': + s.pending = s.pending[:0] // escape code complete + return altF, nil + case 'y': + s.pending = s.pending[:0] // escape code complete + return altY, nil + default: + rv := s.pending[0] + s.pending = s.pending[1:] + return rv, nil + } + + // not reached + return r, nil +} + +// Close returns the terminal to its previous mode +func (s *State) Close() error { + signal.Stop(s.winch) + if !s.inputRedirected { + s.origMode.ApplyMode() + } + return nil +} + +// TerminalSupported returns true if the current terminal supports +// line editing features, and false if liner will use the 'dumb' +// fallback for input. +// Note that TerminalSupported does not check all factors that may +// cause liner to not fully support the terminal (such as stdin redirection) +func TerminalSupported() bool { + bad := map[string]bool{"": true, "dumb": true, "cons25": true} + return !bad[strings.ToLower(os.Getenv("TERM"))] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_darwin.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_darwin.go new file mode 100644 index 0000000..e98ab4a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_darwin.go @@ -0,0 +1,43 @@ +// +build darwin + +package liner + +import "syscall" + +const ( + getTermios = syscall.TIOCGETA + setTermios = syscall.TIOCSETA +) + +const ( + // Input flags + inpck = 0x010 + istrip = 0x020 + icrnl = 0x100 + ixon = 0x200 + + // Output flags + opost = 0x1 + + // Control flags + cs8 = 0x300 + + // Local flags + isig = 0x080 + icanon = 0x100 + iexten = 0x400 +) + +type termios struct { + Iflag uintptr + Oflag uintptr + Cflag uintptr + Lflag uintptr + Cc [20]byte + Ispeed uintptr + Ospeed uintptr +} + +// Terminal.app needs a column for the cursor when the input line is at the +// bottom of the window. +const cursorColumn = true diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_linux.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_linux.go new file mode 100644 index 0000000..56ed185 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_linux.go @@ -0,0 +1,28 @@ +// +build linux + +package liner + +import "syscall" + +const ( + getTermios = syscall.TCGETS + setTermios = syscall.TCSETS +) + +const ( + icrnl = syscall.ICRNL + inpck = syscall.INPCK + istrip = syscall.ISTRIP + ixon = syscall.IXON + opost = syscall.OPOST + cs8 = syscall.CS8 + isig = syscall.ISIG + icanon = syscall.ICANON + iexten = syscall.IEXTEN +) + +type termios struct { + syscall.Termios +} + +const cursorColumn = false diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_windows.go new file mode 100644 index 0000000..a48eb0f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/input_windows.go @@ -0,0 +1,339 @@ +package liner + +import ( + "bufio" + "os" + "syscall" + "unsafe" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") + procGetNumberOfConsoleInputEvents = kernel32.NewProc("GetNumberOfConsoleInputEvents") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") +) + +// These names are from the Win32 api, so they use underscores (contrary to +// what golint suggests) +const ( + std_input_handle = uint32(-10 & 0xFFFFFFFF) + std_output_handle = uint32(-11 & 0xFFFFFFFF) + std_error_handle = uint32(-12 & 0xFFFFFFFF) + invalid_handle_value = ^uintptr(0) +) + +type inputMode uint32 + +// State represents an open terminal +type State struct { + commonState + handle syscall.Handle + hOut syscall.Handle + origMode inputMode + defaultMode inputMode + key interface{} + repeat uint16 +} + +const ( + enableEchoInput = 0x4 + enableInsertMode = 0x20 + enableLineInput = 0x2 + enableMouseInput = 0x10 + enableProcessedInput = 0x1 + enableQuickEditMode = 0x40 + enableWindowInput = 0x8 +) + +// NewLiner initializes a new *State, and sets the terminal into raw mode. To +// restore the terminal to its previous state, call State.Close(). +func NewLiner() *State { + var s State + hIn, _, _ := procGetStdHandle.Call(uintptr(std_input_handle)) + s.handle = syscall.Handle(hIn) + hOut, _, _ := procGetStdHandle.Call(uintptr(std_output_handle)) + s.hOut = syscall.Handle(hOut) + + s.terminalSupported = true + if m, err := TerminalMode(); err == nil { + s.origMode = m.(inputMode) + mode := s.origMode + mode &^= enableEchoInput + mode &^= enableInsertMode + mode &^= enableLineInput + mode &^= enableMouseInput + mode |= enableWindowInput + mode.ApplyMode() + } else { + s.inputRedirected = true + s.r = bufio.NewReader(os.Stdin) + } + + s.getColumns() + s.outputRedirected = s.columns <= 0 + + return &s +} + +// These names are from the Win32 api, so they use underscores (contrary to +// what golint suggests) +const ( + focus_event = 0x0010 + key_event = 0x0001 + menu_event = 0x0008 + mouse_event = 0x0002 + window_buffer_size_event = 0x0004 +) + +type input_record struct { + eventType uint16 + pad uint16 + blob [16]byte +} + +type key_event_record struct { + KeyDown int32 + RepeatCount uint16 + VirtualKeyCode uint16 + VirtualScanCode uint16 + Char int16 + ControlKeyState uint32 +} + +// These names are from the Win32 api, so they use underscores (contrary to +// what golint suggests) +const ( + vk_tab = 0x09 + vk_prior = 0x21 + vk_next = 0x22 + vk_end = 0x23 + vk_home = 0x24 + vk_left = 0x25 + vk_up = 0x26 + vk_right = 0x27 + vk_down = 0x28 + vk_insert = 0x2d + vk_delete = 0x2e + vk_f1 = 0x70 + vk_f2 = 0x71 + vk_f3 = 0x72 + vk_f4 = 0x73 + vk_f5 = 0x74 + vk_f6 = 0x75 + vk_f7 = 0x76 + vk_f8 = 0x77 + vk_f9 = 0x78 + vk_f10 = 0x79 + vk_f11 = 0x7a + vk_f12 = 0x7b + bKey = 0x42 + fKey = 0x46 + yKey = 0x59 +) + +const ( + shiftPressed = 0x0010 + leftAltPressed = 0x0002 + leftCtrlPressed = 0x0008 + rightAltPressed = 0x0001 + rightCtrlPressed = 0x0004 + + modKeys = shiftPressed | leftAltPressed | rightAltPressed | leftCtrlPressed | rightCtrlPressed +) + +// inputWaiting only returns true if the next call to readNext will return immediately. +func (s *State) inputWaiting() bool { + var num uint32 + ok, _, _ := procGetNumberOfConsoleInputEvents.Call(uintptr(s.handle), uintptr(unsafe.Pointer(&num))) + if ok == 0 { + // call failed, so we cannot guarantee a non-blocking readNext + return false + } + + // during a "paste" input events are always an odd number, and + // the last one results in a blocking readNext, so return false + // when num is 1 or 0. + return num > 1 +} + +func (s *State) readNext() (interface{}, error) { + if s.repeat > 0 { + s.repeat-- + return s.key, nil + } + + var input input_record + pbuf := uintptr(unsafe.Pointer(&input)) + var rv uint32 + prv := uintptr(unsafe.Pointer(&rv)) + + for { + ok, _, err := procReadConsoleInput.Call(uintptr(s.handle), pbuf, 1, prv) + + if ok == 0 { + return nil, err + } + + if input.eventType == window_buffer_size_event { + xy := (*coord)(unsafe.Pointer(&input.blob[0])) + s.columns = int(xy.x) + if s.columns > 1 { + s.columns-- + } + return winch, nil + } + if input.eventType != key_event { + continue + } + ke := (*key_event_record)(unsafe.Pointer(&input.blob[0])) + if ke.KeyDown == 0 { + continue + } + + if ke.VirtualKeyCode == vk_tab && ke.ControlKeyState&modKeys == shiftPressed { + s.key = shiftTab + } else if ke.VirtualKeyCode == bKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altB + } else if ke.VirtualKeyCode == fKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altF + } else if ke.VirtualKeyCode == yKey && (ke.ControlKeyState&modKeys == leftAltPressed || + ke.ControlKeyState&modKeys == rightAltPressed) { + s.key = altY + } else if ke.Char > 0 { + s.key = rune(ke.Char) + } else { + switch ke.VirtualKeyCode { + case vk_prior: + s.key = pageUp + case vk_next: + s.key = pageDown + case vk_end: + s.key = end + case vk_home: + s.key = home + case vk_left: + s.key = left + if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { + if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { + s.key = wordLeft + } + } + case vk_right: + s.key = right + if ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) != 0 { + if ke.ControlKeyState&modKeys == ke.ControlKeyState&(leftCtrlPressed|rightCtrlPressed) { + s.key = wordRight + } + } + case vk_up: + s.key = up + case vk_down: + s.key = down + case vk_insert: + s.key = insert + case vk_delete: + s.key = del + case vk_f1: + s.key = f1 + case vk_f2: + s.key = f2 + case vk_f3: + s.key = f3 + case vk_f4: + s.key = f4 + case vk_f5: + s.key = f5 + case vk_f6: + s.key = f6 + case vk_f7: + s.key = f7 + case vk_f8: + s.key = f8 + case vk_f9: + s.key = f9 + case vk_f10: + s.key = f10 + case vk_f11: + s.key = f11 + case vk_f12: + s.key = f12 + default: + // Eat modifier keys + // TODO: return Action(Unknown) if the key isn't a + // modifier. + continue + } + } + + if ke.RepeatCount > 1 { + s.repeat = ke.RepeatCount - 1 + } + return s.key, nil + } +} + +// Close returns the terminal to its previous mode +func (s *State) Close() error { + s.origMode.ApplyMode() + return nil +} + +func (s *State) startPrompt() { + if m, err := TerminalMode(); err == nil { + s.defaultMode = m.(inputMode) + mode := s.defaultMode + mode &^= enableProcessedInput + mode.ApplyMode() + } +} + +func (s *State) restartPrompt() { +} + +func (s *State) stopPrompt() { + s.defaultMode.ApplyMode() +} + +// TerminalSupported returns true because line editing is always +// supported on Windows. +func TerminalSupported() bool { + return true +} + +func (mode inputMode) ApplyMode() error { + hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) + if hIn == invalid_handle_value || hIn == 0 { + return err + } + ok, _, err := procSetConsoleMode.Call(hIn, uintptr(mode)) + if ok != 0 { + err = nil + } + return err +} + +// TerminalMode returns the current terminal input mode as an InputModeSetter. +// +// This function is provided for convenience, and should +// not be necessary for most users of liner. +func TerminalMode() (ModeApplier, error) { + var mode inputMode + hIn, _, err := procGetStdHandle.Call(uintptr(std_input_handle)) + if hIn == invalid_handle_value || hIn == 0 { + return nil, err + } + ok, _, err := procGetConsoleMode.Call(hIn, uintptr(unsafe.Pointer(&mode))) + if ok != 0 { + err = nil + } + return mode, err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/line.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/line.go new file mode 100644 index 0000000..cc147d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/line.go @@ -0,0 +1,1070 @@ +// +build windows linux darwin openbsd freebsd netbsd + +package liner + +import ( + "container/ring" + "errors" + "fmt" + "io" + "strings" + "unicode" + "unicode/utf8" +) + +type action int + +const ( + left action = iota + right + up + down + home + end + insert + del + pageUp + pageDown + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + f10 + f11 + f12 + altB + altF + altY + shiftTab + wordLeft + wordRight + winch + unknown +) + +const ( + ctrlA = 1 + ctrlB = 2 + ctrlC = 3 + ctrlD = 4 + ctrlE = 5 + ctrlF = 6 + ctrlG = 7 + ctrlH = 8 + tab = 9 + lf = 10 + ctrlK = 11 + ctrlL = 12 + cr = 13 + ctrlN = 14 + ctrlO = 15 + ctrlP = 16 + ctrlQ = 17 + ctrlR = 18 + ctrlS = 19 + ctrlT = 20 + ctrlU = 21 + ctrlV = 22 + ctrlW = 23 + ctrlX = 24 + ctrlY = 25 + ctrlZ = 26 + esc = 27 + bs = 127 +) + +const ( + beep = "\a" +) + +type tabDirection int + +const ( + tabForward tabDirection = iota + tabReverse +) + +func (s *State) refresh(prompt []rune, buf []rune, pos int) error { + s.needRefresh = false + if s.multiLineMode { + return s.refreshMultiLine(prompt, buf, pos) + } + return s.refreshSingleLine(prompt, buf, pos) +} + +func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error { + s.cursorPos(0) + _, err := fmt.Print(string(prompt)) + if err != nil { + return err + } + + pLen := countGlyphs(prompt) + bLen := countGlyphs(buf) + pos = countGlyphs(buf[:pos]) + if pLen+bLen < s.columns { + _, err = fmt.Print(string(buf)) + s.eraseLine() + s.cursorPos(pLen + pos) + } else { + // Find space available + space := s.columns - pLen + space-- // space for cursor + start := pos - space/2 + end := start + space + if end > bLen { + end = bLen + start = end - space + } + if start < 0 { + start = 0 + end = space + } + pos -= start + + // Leave space for markers + if start > 0 { + start++ + } + if end < bLen { + end-- + } + startRune := len(getPrefixGlyphs(buf, start)) + line := getPrefixGlyphs(buf[startRune:], end-start) + + // Output + if start > 0 { + fmt.Print("{") + } + fmt.Print(string(line)) + if end < bLen { + fmt.Print("}") + } + + // Set cursor position + s.eraseLine() + s.cursorPos(pLen + pos) + } + return err +} + +func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error { + promptColumns := countMultiLineGlyphs(prompt, s.columns, 0) + totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns) + totalRows := (totalColumns + s.columns - 1) / s.columns + maxRows := s.maxRows + if totalRows > s.maxRows { + s.maxRows = totalRows + } + cursorRows := s.cursorRows + if cursorRows == 0 { + cursorRows = 1 + } + + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + if maxRows-cursorRows > 0 { + s.moveDown(maxRows - cursorRows) + } + + /* Now for every row clear it, go up. */ + for i := 0; i < maxRows-1; i++ { + s.cursorPos(0) + s.eraseLine() + s.moveUp(1) + } + + /* Clean the top line. */ + s.cursorPos(0) + s.eraseLine() + + /* Write the prompt and the current buffer content */ + if _, err := fmt.Print(string(prompt)); err != nil { + return err + } + if _, err := fmt.Print(string(buf)); err != nil { + return err + } + + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns) + if cursorColumns == totalColumns && totalColumns%s.columns == 0 { + s.emitNewLine() + s.cursorPos(0) + totalRows++ + if totalRows > s.maxRows { + s.maxRows = totalRows + } + } + + /* Move cursor to right position. */ + cursorRows = (cursorColumns + s.columns) / s.columns + if s.cursorRows > 0 && totalRows-cursorRows > 0 { + s.moveUp(totalRows - cursorRows) + } + /* Set column. */ + s.cursorPos(cursorColumns % s.columns) + + s.cursorRows = cursorRows + return nil +} + +func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) { + columns := countMultiLineGlyphs(prompt, s.columns, 0) + columns = countMultiLineGlyphs(buf[:pos], s.columns, columns) + columns += 2 // ^C + cursorRows := (columns + s.columns) / s.columns + if s.maxRows-cursorRows > 0 { + for i := 0; i < s.maxRows-cursorRows; i++ { + fmt.Println() // always moves the cursor down or scrolls the window up as needed + } + } + s.maxRows = 1 + s.cursorRows = 0 +} + +func longestCommonPrefix(strs []string) string { + if len(strs) == 0 { + return "" + } + longest := strs[0] + + for _, str := range strs[1:] { + for !strings.HasPrefix(str, longest) { + longest = longest[:len(longest)-1] + } + } + // Remove trailing partial runes + longest = strings.TrimRight(longest, "\uFFFD") + return longest +} + +func (s *State) circularTabs(items []string) func(tabDirection) (string, error) { + item := -1 + return func(direction tabDirection) (string, error) { + if direction == tabForward { + if item < len(items)-1 { + item++ + } else { + item = 0 + } + } else if direction == tabReverse { + if item > 0 { + item-- + } else { + item = len(items) - 1 + } + } + return items[item], nil + } +} + +func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) { + for _, item := range items { + if len(item) >= screenWidth { + return 1, len(items), screenWidth - 1 + } + if len(item) >= maxWidth { + maxWidth = len(item) + 1 + } + } + + numColumns = screenWidth / maxWidth + numRows = len(items) / numColumns + if len(items)%numColumns > 0 { + numRows++ + } + + if len(items) <= numColumns { + maxWidth = 0 + } + + return +} + +func (s *State) printedTabs(items []string) func(tabDirection) (string, error) { + numTabs := 1 + prefix := longestCommonPrefix(items) + return func(direction tabDirection) (string, error) { + if len(items) == 1 { + return items[0], nil + } + + if numTabs == 2 { + if len(items) > 100 { + fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items)) + prompt: + for { + next, err := s.readNext() + if err != nil { + return prefix, err + } + + if key, ok := next.(rune); ok { + switch key { + case 'n', 'N': + return prefix, nil + case 'y', 'Y': + break prompt + case ctrlC, ctrlD, cr, lf: + s.restartPrompt() + } + } + } + } + fmt.Println("") + + numColumns, numRows, maxWidth := calculateColumns(s.columns, items) + + for i := 0; i < numRows; i++ { + for j := 0; j < numColumns*numRows; j += numRows { + if i+j < len(items) { + if maxWidth > 0 { + fmt.Printf("%-*.[1]*s", maxWidth, items[i+j]) + } else { + fmt.Printf("%v ", items[i+j]) + } + } + } + fmt.Println("") + } + } else { + numTabs++ + } + return prefix, nil + } +} + +func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) { + if s.completer == nil { + return line, pos, rune(esc), nil + } + head, list, tail := s.completer(string(line), pos) + if len(list) <= 0 { + return line, pos, rune(esc), nil + } + hl := utf8.RuneCountInString(head) + if len(list) == 1 { + s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0])) + return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), nil + } + + direction := tabForward + tabPrinter := s.circularTabs(list) + if s.tabStyle == TabPrints { + tabPrinter = s.printedTabs(list) + } + + for { + pick, err := tabPrinter(direction) + if err != nil { + return line, pos, rune(esc), err + } + s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick)) + + next, err := s.readNext() + if err != nil { + return line, pos, rune(esc), err + } + if key, ok := next.(rune); ok { + if key == tab { + direction = tabForward + continue + } + if key == esc { + return line, pos, rune(esc), nil + } + } + if a, ok := next.(action); ok && a == shiftTab { + direction = tabReverse + continue + } + return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil + } +} + +// reverse intelligent search, implements a bash-like history search. +func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) { + p := "(reverse-i-search)`': " + s.refresh([]rune(p), origLine, origPos) + + line := []rune{} + pos := 0 + foundLine := string(origLine) + foundPos := origPos + + getLine := func() ([]rune, []rune, int) { + search := string(line) + prompt := "(reverse-i-search)`%s': " + return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos + } + + history, positions := s.getHistoryByPattern(string(line)) + historyPos := len(history) - 1 + + for { + next, err := s.readNext() + if err != nil { + return []rune(foundLine), foundPos, rune(esc), err + } + + switch v := next.(type) { + case rune: + switch v { + case ctrlR: // Search backwards + if historyPos > 0 && historyPos < len(history) { + historyPos-- + foundLine = history[historyPos] + foundPos = positions[historyPos] + } else { + fmt.Print(beep) + } + case ctrlS: // Search forward + if historyPos < len(history)-1 && historyPos >= 0 { + historyPos++ + foundLine = history[historyPos] + foundPos = positions[historyPos] + } else { + fmt.Print(beep) + } + case ctrlH, bs: // Backspace + if pos <= 0 { + fmt.Print(beep) + } else { + n := len(getSuffixGlyphs(line[:pos], 1)) + line = append(line[:pos-n], line[pos:]...) + pos -= n + + // For each char deleted, display the last matching line of history + history, positions := s.getHistoryByPattern(string(line)) + historyPos = len(history) - 1 + if len(history) > 0 { + foundLine = history[historyPos] + foundPos = positions[historyPos] + } else { + foundLine = "" + foundPos = 0 + } + } + case ctrlG: // Cancel + return origLine, origPos, rune(esc), err + + case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK, + ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ: + fallthrough + case 0, ctrlC, esc, 28, 29, 30, 31: + return []rune(foundLine), foundPos, next, err + default: + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ + + // For each keystroke typed, display the last matching line of history + history, positions = s.getHistoryByPattern(string(line)) + historyPos = len(history) - 1 + if len(history) > 0 { + foundLine = history[historyPos] + foundPos = positions[historyPos] + } else { + foundLine = "" + foundPos = 0 + } + } + case action: + return []rune(foundLine), foundPos, next, err + } + s.refresh(getLine()) + } +} + +// addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a +// new node in the end of the kill ring, and move the current pointer to the new +// node. If mode is 1 or 2 it appends or prepends the text to the current entry +// of the killRing. +func (s *State) addToKillRing(text []rune, mode int) { + // Don't use the same underlying array as text + killLine := make([]rune, len(text)) + copy(killLine, text) + + // Point killRing to a newNode, procedure depends on the killring state and + // append mode. + if mode == 0 { // Add new node to killRing + if s.killRing == nil { // if killring is empty, create a new one + s.killRing = ring.New(1) + } else if s.killRing.Len() >= KillRingMax { // if killring is "full" + s.killRing = s.killRing.Next() + } else { // Normal case + s.killRing.Link(ring.New(1)) + s.killRing = s.killRing.Next() + } + } else { + if s.killRing == nil { // if killring is empty, create a new one + s.killRing = ring.New(1) + s.killRing.Value = []rune{} + } + if mode == 1 { // Append to last entry + killLine = append(s.killRing.Value.([]rune), killLine...) + } else if mode == 2 { // Prepend to last entry + killLine = append(killLine, s.killRing.Value.([]rune)...) + } + } + + // Save text in the current killring node + s.killRing.Value = killLine +} + +func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) { + if s.killRing == nil { + return text, pos, rune(esc), nil + } + + lineStart := text[:pos] + lineEnd := text[pos:] + var line []rune + + for { + value := s.killRing.Value.([]rune) + line = make([]rune, 0) + line = append(line, lineStart...) + line = append(line, value...) + line = append(line, lineEnd...) + + pos = len(lineStart) + len(value) + s.refresh(p, line, pos) + + next, err := s.readNext() + if err != nil { + return line, pos, next, err + } + + switch v := next.(type) { + case rune: + return line, pos, next, nil + case action: + switch v { + case altY: + s.killRing = s.killRing.Prev() + default: + return line, pos, next, nil + } + } + } +} + +// Prompt displays p and returns a line of user input, not including a trailing +// newline character. An io.EOF error is returned if the user signals end-of-file +// by pressing Ctrl-D. Prompt allows line editing if the terminal supports it. +func (s *State) Prompt(prompt string) (string, error) { + return s.PromptWithSuggestion(prompt, "", 0) +} + +// PromptWithSuggestion displays prompt and an editable text with cursor at +// given position. The cursor will be set to the end of the line if given position +// is negative or greater than length of text. Returns a line of user input, not +// including a trailing newline character. An io.EOF error is returned if the user +// signals end-of-file by pressing Ctrl-D. +func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) { + for _, r := range prompt { + if unicode.Is(unicode.C, r) { + return "", ErrInvalidPrompt + } + } + if s.inputRedirected || !s.terminalSupported { + return s.promptUnsupported(prompt) + } + if s.outputRedirected { + return "", ErrNotTerminalOutput + } + + s.historyMutex.RLock() + defer s.historyMutex.RUnlock() + + fmt.Print(prompt) + p := []rune(prompt) + var line = []rune(text) + historyEnd := "" + var historyPrefix []string + historyPos := 0 + historyStale := true + historyAction := false // used to mark history related actions + killAction := 0 // used to mark kill related actions + + defer s.stopPrompt() + + if pos < 0 || len(text) < pos { + pos = len(text) + } + if len(line) > 0 { + s.refresh(p, line, pos) + } + +restart: + s.startPrompt() + s.getColumns() + +mainLoop: + for { + next, err := s.readNext() + haveNext: + if err != nil { + if s.shouldRestart != nil && s.shouldRestart(err) { + goto restart + } + return "", err + } + + historyAction = false + switch v := next.(type) { + case rune: + switch v { + case cr, lf: + if s.needRefresh { + s.refresh(p, line, pos) + } + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + fmt.Println() + break mainLoop + case ctrlA: // Start of line + pos = 0 + s.needRefresh = true + case ctrlE: // End of line + pos = len(line) + s.needRefresh = true + case ctrlB: // left + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + s.needRefresh = true + } else { + fmt.Print(beep) + } + case ctrlF: // right + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + s.needRefresh = true + } else { + fmt.Print(beep) + } + case ctrlD: // del + if pos == 0 && len(line) == 0 { + // exit + return "", io.EOF + } + + // ctrlD is a potential EOF, so the rune reader shuts down. + // Therefore, if it isn't actually an EOF, we must re-startPrompt. + s.restartPrompt() + + if pos >= len(line) { + fmt.Print(beep) + } else { + n := len(getPrefixGlyphs(line[pos:], 1)) + line = append(line[:pos], line[pos+n:]...) + s.needRefresh = true + } + case ctrlK: // delete remainder of line + if pos >= len(line) { + fmt.Print(beep) + } else { + if killAction > 0 { + s.addToKillRing(line[pos:], 1) // Add in apend mode + } else { + s.addToKillRing(line[pos:], 0) // Add in normal mode + } + + killAction = 2 // Mark that there was a kill action + line = line[:pos] + s.needRefresh = true + } + case ctrlP: // up + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos > 0 { + if historyPos == len(historyPrefix) { + historyEnd = string(line) + } + historyPos-- + line = []rune(historyPrefix[historyPos]) + pos = len(line) + s.needRefresh = true + } else { + fmt.Print(beep) + } + case ctrlN: // down + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos < len(historyPrefix) { + historyPos++ + if historyPos == len(historyPrefix) { + line = []rune(historyEnd) + } else { + line = []rune(historyPrefix[historyPos]) + } + pos = len(line) + s.needRefresh = true + } else { + fmt.Print(beep) + } + case ctrlT: // transpose prev glyph with glyph under cursor + if len(line) < 2 || pos < 1 { + fmt.Print(beep) + } else { + if pos == len(line) { + pos -= len(getSuffixGlyphs(line, 1)) + } + prev := getSuffixGlyphs(line[:pos], 1) + next := getPrefixGlyphs(line[pos:], 1) + scratch := make([]rune, len(prev)) + copy(scratch, prev) + copy(line[pos-len(prev):], next) + copy(line[pos-len(prev)+len(next):], scratch) + pos += len(next) + s.needRefresh = true + } + case ctrlL: // clear screen + s.eraseScreen() + s.needRefresh = true + case ctrlC: // reset + fmt.Println("^C") + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + if s.ctrlCAborts { + return "", ErrPromptAborted + } + line = line[:0] + pos = 0 + fmt.Print(prompt) + s.restartPrompt() + case ctrlH, bs: // Backspace + if pos <= 0 { + fmt.Print(beep) + } else { + n := len(getSuffixGlyphs(line[:pos], 1)) + line = append(line[:pos-n], line[pos:]...) + pos -= n + s.needRefresh = true + } + case ctrlU: // Erase line before cursor + if killAction > 0 { + s.addToKillRing(line[:pos], 2) // Add in prepend mode + } else { + s.addToKillRing(line[:pos], 0) // Add in normal mode + } + + killAction = 2 // Mark that there was some killing + line = line[pos:] + pos = 0 + s.needRefresh = true + case ctrlW: // Erase word + if pos == 0 { + fmt.Print(beep) + break + } + // Remove whitespace to the left + var buf []rune // Store the deleted chars in a buffer + for { + if pos == 0 || !unicode.IsSpace(line[pos-1]) { + break + } + buf = append(buf, line[pos-1]) + line = append(line[:pos-1], line[pos:]...) + pos-- + } + // Remove non-whitespace to the left + for { + if pos == 0 || unicode.IsSpace(line[pos-1]) { + break + } + buf = append(buf, line[pos-1]) + line = append(line[:pos-1], line[pos:]...) + pos-- + } + // Invert the buffer and save the result on the killRing + var newBuf []rune + for i := len(buf) - 1; i >= 0; i-- { + newBuf = append(newBuf, buf[i]) + } + if killAction > 0 { + s.addToKillRing(newBuf, 2) // Add in prepend mode + } else { + s.addToKillRing(newBuf, 0) // Add in normal mode + } + killAction = 2 // Mark that there was some killing + + s.needRefresh = true + case ctrlY: // Paste from Yank buffer + line, pos, next, err = s.yank(p, line, pos) + goto haveNext + case ctrlR: // Reverse Search + line, pos, next, err = s.reverseISearch(line, pos) + s.needRefresh = true + goto haveNext + case tab: // Tab completion + line, pos, next, err = s.tabComplete(p, line, pos) + goto haveNext + // Catch keys that do nothing, but you don't want them to beep + case esc: + // DO NOTHING + // Unused keys + case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ: + fallthrough + // Catch unhandled control codes (anything <= 31) + case 0, 28, 29, 30, 31: + fmt.Print(beep) + default: + if pos == len(line) && !s.multiLineMode && + len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines + countGlyphs(p)+countGlyphs(line) < s.columns-1 { + line = append(line, v) + fmt.Printf("%c", v) + pos++ + } else { + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ + s.needRefresh = true + } + } + case action: + switch v { + case del: + if pos >= len(line) { + fmt.Print(beep) + } else { + n := len(getPrefixGlyphs(line[pos:], 1)) + line = append(line[:pos], line[pos+n:]...) + } + case left: + if pos > 0 { + pos -= len(getSuffixGlyphs(line[:pos], 1)) + } else { + fmt.Print(beep) + } + case wordLeft, altB: + if pos > 0 { + var spaceHere, spaceLeft, leftKnown bool + for { + pos-- + if pos == 0 { + break + } + if leftKnown { + spaceHere = spaceLeft + } else { + spaceHere = unicode.IsSpace(line[pos]) + } + spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true + if !spaceHere && spaceLeft { + break + } + } + } else { + fmt.Print(beep) + } + case right: + if pos < len(line) { + pos += len(getPrefixGlyphs(line[pos:], 1)) + } else { + fmt.Print(beep) + } + case wordRight, altF: + if pos < len(line) { + var spaceHere, spaceLeft, hereKnown bool + for { + pos++ + if pos == len(line) { + break + } + if hereKnown { + spaceLeft = spaceHere + } else { + spaceLeft = unicode.IsSpace(line[pos-1]) + } + spaceHere, hereKnown = unicode.IsSpace(line[pos]), true + if spaceHere && !spaceLeft { + break + } + } + } else { + fmt.Print(beep) + } + case up: + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos > 0 { + if historyPos == len(historyPrefix) { + historyEnd = string(line) + } + historyPos-- + line = []rune(historyPrefix[historyPos]) + pos = len(line) + } else { + fmt.Print(beep) + } + case down: + historyAction = true + if historyStale { + historyPrefix = s.getHistoryByPrefix(string(line)) + historyPos = len(historyPrefix) + historyStale = false + } + if historyPos < len(historyPrefix) { + historyPos++ + if historyPos == len(historyPrefix) { + line = []rune(historyEnd) + } else { + line = []rune(historyPrefix[historyPos]) + } + pos = len(line) + } else { + fmt.Print(beep) + } + case home: // Start of line + pos = 0 + case end: // End of line + pos = len(line) + case winch: // Window change + if s.multiLineMode { + if s.maxRows-s.cursorRows > 0 { + s.moveDown(s.maxRows - s.cursorRows) + } + for i := 0; i < s.maxRows-1; i++ { + s.cursorPos(0) + s.eraseLine() + s.moveUp(1) + } + s.maxRows = 1 + s.cursorRows = 1 + } + } + s.needRefresh = true + } + if s.needRefresh && !s.inputWaiting() { + s.refresh(p, line, pos) + } + if !historyAction { + historyStale = true + } + if killAction > 0 { + killAction-- + } + } + return string(line), nil +} + +// PasswordPrompt displays p, and then waits for user input. The input typed by +// the user is not displayed in the terminal. +func (s *State) PasswordPrompt(prompt string) (string, error) { + for _, r := range prompt { + if unicode.Is(unicode.C, r) { + return "", ErrInvalidPrompt + } + } + if !s.terminalSupported { + return "", errors.New("liner: function not supported in this terminal") + } + if s.inputRedirected { + return s.promptUnsupported(prompt) + } + if s.outputRedirected { + return "", ErrNotTerminalOutput + } + + defer s.stopPrompt() + +restart: + s.startPrompt() + s.getColumns() + + fmt.Print(prompt) + p := []rune(prompt) + var line []rune + pos := 0 + +mainLoop: + for { + next, err := s.readNext() + if err != nil { + if s.shouldRestart != nil && s.shouldRestart(err) { + goto restart + } + return "", err + } + + switch v := next.(type) { + case rune: + switch v { + case cr, lf: + if s.needRefresh { + s.refresh(p, line, pos) + } + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + fmt.Println() + break mainLoop + case ctrlD: // del + if pos == 0 && len(line) == 0 { + // exit + return "", io.EOF + } + + // ctrlD is a potential EOF, so the rune reader shuts down. + // Therefore, if it isn't actually an EOF, we must re-startPrompt. + s.restartPrompt() + case ctrlL: // clear screen + s.eraseScreen() + s.refresh(p, []rune{}, 0) + case ctrlH, bs: // Backspace + if pos <= 0 { + fmt.Print(beep) + } else { + n := len(getSuffixGlyphs(line[:pos], 1)) + line = append(line[:pos-n], line[pos:]...) + pos -= n + } + case ctrlC: + fmt.Println("^C") + if s.multiLineMode { + s.resetMultiLine(p, line, pos) + } + if s.ctrlCAborts { + return "", ErrPromptAborted + } + line = line[:0] + pos = 0 + fmt.Print(prompt) + s.restartPrompt() + // Unused keys + case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS, + ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ: + fallthrough + // Catch unhandled control codes (anything <= 31) + case 0, 28, 29, 30, 31: + fmt.Print(beep) + default: + line = append(line[:pos], append([]rune{v}, line[pos:]...)...) + pos++ + } + } + } + return string(line), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output.go new file mode 100644 index 0000000..6d83d4e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output.go @@ -0,0 +1,79 @@ +// +build linux darwin openbsd freebsd netbsd + +package liner + +import ( + "fmt" + "os" + "strings" + "syscall" + "unsafe" +) + +func (s *State) cursorPos(x int) { + if s.useCHA { + // 'G' is "Cursor Character Absolute (CHA)" + fmt.Printf("\x1b[%dG", x+1) + } else { + // 'C' is "Cursor Forward (CUF)" + fmt.Print("\r") + if x > 0 { + fmt.Printf("\x1b[%dC", x) + } + } +} + +func (s *State) eraseLine() { + fmt.Print("\x1b[0K") +} + +func (s *State) eraseScreen() { + fmt.Print("\x1b[H\x1b[2J") +} + +func (s *State) moveUp(lines int) { + fmt.Printf("\x1b[%dA", lines) +} + +func (s *State) moveDown(lines int) { + fmt.Printf("\x1b[%dB", lines) +} + +func (s *State) emitNewLine() { + fmt.Print("\n") +} + +type winSize struct { + row, col uint16 + xpixel, ypixel uint16 +} + +func (s *State) getColumns() bool { + var ws winSize + ok, _, _ := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdout), + syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))) + if int(ok) < 0 { + return false + } + s.columns = int(ws.col) + if cursorColumn && s.columns > 1 { + s.columns-- + } + return true +} + +func (s *State) checkOutput() { + // xterm is known to support CHA + if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") { + s.useCHA = true + return + } + + // The test for functional ANSI CHA is unreliable (eg the Windows + // telnet command does not support reading the cursor position with + // an ANSI DSR request, despite setting TERM=ansi) + + // Assume CHA isn't supported (which should be safe, although it + // does result in occasional visible cursor jitter) + s.useCHA = false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output_windows.go new file mode 100644 index 0000000..63c9c5d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/output_windows.go @@ -0,0 +1,76 @@ +package liner + +import ( + "unsafe" +) + +type coord struct { + x, y int16 +} +type smallRect struct { + left, top, right, bottom int16 +} + +type consoleScreenBufferInfo struct { + dwSize coord + dwCursorPosition coord + wAttributes int16 + srWindow smallRect + dwMaximumWindowSize coord +} + +func (s *State) cursorPos(x int) { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), + uintptr(int(x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16)) +} + +func (s *State) eraseLine() { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + var numWritten uint32 + procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '), + uintptr(sbi.dwSize.x-sbi.dwCursorPosition.x), + uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|int(sbi.dwCursorPosition.y)<<16), + uintptr(unsafe.Pointer(&numWritten))) +} + +func (s *State) eraseScreen() { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + var numWritten uint32 + procFillConsoleOutputCharacter.Call(uintptr(s.hOut), uintptr(' '), + uintptr(sbi.dwSize.x)*uintptr(sbi.dwSize.y), + 0, + uintptr(unsafe.Pointer(&numWritten))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), 0) +} + +func (s *State) moveUp(lines int) { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), + uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)-lines)<<16)) +} + +func (s *State) moveDown(lines int) { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + procSetConsoleCursorPosition.Call(uintptr(s.hOut), + uintptr(int(sbi.dwCursorPosition.x)&0xFFFF|(int(sbi.dwCursorPosition.y)+lines)<<16)) +} + +func (s *State) emitNewLine() { + // windows doesn't need to omit a new line +} + +func (s *State) getColumns() { + var sbi consoleScreenBufferInfo + procGetConsoleScreenBufferInfo.Call(uintptr(s.hOut), uintptr(unsafe.Pointer(&sbi))) + s.columns = int(sbi.dwSize.x) + if s.columns > 1 { + // Windows 10 needs a spare column for the cursor + s.columns-- + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/unixmode.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/unixmode.go new file mode 100644 index 0000000..9838923 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/unixmode.go @@ -0,0 +1,37 @@ +// +build linux darwin freebsd openbsd netbsd + +package liner + +import ( + "syscall" + "unsafe" +) + +func (mode *termios) ApplyMode() error { + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), setTermios, uintptr(unsafe.Pointer(mode))) + + if errno != 0 { + return errno + } + return nil +} + +// TerminalMode returns the current terminal input mode as an InputModeSetter. +// +// This function is provided for convenience, and should +// not be necessary for most users of liner. +func TerminalMode() (ModeApplier, error) { + mode, errno := getMode(syscall.Stdin) + + if errno != 0 { + return nil, errno + } + return mode, nil +} + +func getMode(handle int) (*termios, syscall.Errno) { + var mode termios + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(handle), getTermios, uintptr(unsafe.Pointer(&mode))) + + return &mode, errno +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/width.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/width.go new file mode 100644 index 0000000..42e8999 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/peterh/liner/width.go @@ -0,0 +1,99 @@ +package liner + +import "unicode" + +// These character classes are mostly zero width (when combined). +// A few might not be, depending on the user's font. Fixing this +// is non-trivial, given that some terminals don't support +// ANSI DSR/CPR +var zeroWidth = []*unicode.RangeTable{ + unicode.Mn, + unicode.Me, + unicode.Cc, + unicode.Cf, +} + +var doubleWidth = []*unicode.RangeTable{ + unicode.Han, + unicode.Hangul, + unicode.Hiragana, + unicode.Katakana, +} + +// countGlyphs considers zero-width characters to be zero glyphs wide, +// and members of Chinese, Japanese, and Korean scripts to be 2 glyphs wide. +func countGlyphs(s []rune) int { + n := 0 + for _, r := range s { + // speed up the common case + if r < 127 { + n++ + continue + } + + switch { + case unicode.IsOneOf(zeroWidth, r): + case unicode.IsOneOf(doubleWidth, r): + n += 2 + default: + n++ + } + } + return n +} + +func countMultiLineGlyphs(s []rune, columns int, start int) int { + n := start + for _, r := range s { + if r < 127 { + n++ + continue + } + switch { + case unicode.IsOneOf(zeroWidth, r): + case unicode.IsOneOf(doubleWidth, r): + n += 2 + // no room for a 2-glyphs-wide char in the ending + // so skip a column and display it at the beginning + if n%columns == 1 { + n++ + } + default: + n++ + } + } + return n +} + +func getPrefixGlyphs(s []rune, num int) []rune { + p := 0 + for n := 0; n < num && p < len(s); p++ { + // speed up the common case + if s[p] < 127 { + n++ + continue + } + if !unicode.IsOneOf(zeroWidth, s[p]) { + n++ + } + } + for p < len(s) && unicode.IsOneOf(zeroWidth, s[p]) { + p++ + } + return s[:p] +} + +func getSuffixGlyphs(s []rune, num int) []rune { + p := len(s) + for n := 0; n < num && p > 0; p-- { + // speed up the common case + if s[p-1] < 127 { + n++ + continue + } + if !unicode.IsOneOf(zeroWidth, s[p-1]) { + n++ + } + } + return s[p:] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/LICENSE new file mode 100644 index 0000000..363fa9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/LICENSE @@ -0,0 +1,29 @@ +Copyright 2012 Richard Crowley. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. 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. + +THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``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 RICHARD CROWLEY 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. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of Richard Crowley. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/README.md new file mode 100644 index 0000000..2d1a6dc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/README.md @@ -0,0 +1,153 @@ +go-metrics +========== + +![travis build status](https://travis-ci.org/rcrowley/go-metrics.svg?branch=master) + +Go port of Coda Hale's Metrics library: . + +Documentation: . + +Usage +----- + +Create and update metrics: + +```go +c := metrics.NewCounter() +metrics.Register("foo", c) +c.Inc(47) + +g := metrics.NewGauge() +metrics.Register("bar", g) +g.Update(47) + +r := NewRegistry() +g := metrics.NewRegisteredFunctionalGauge("cache-evictions", r, func() int64 { return cache.getEvictionsCount() }) + +s := metrics.NewExpDecaySample(1028, 0.015) // or metrics.NewUniformSample(1028) +h := metrics.NewHistogram(s) +metrics.Register("baz", h) +h.Update(47) + +m := metrics.NewMeter() +metrics.Register("quux", m) +m.Mark(47) + +t := metrics.NewTimer() +metrics.Register("bang", t) +t.Time(func() {}) +t.Update(47) +``` + +Register() is not threadsafe. For threadsafe metric registration use +GetOrRegister: + +``` +t := metrics.GetOrRegisterTimer("account.create.latency", nil) +t.Time(func() {}) +t.Update(47) +``` + +Periodically log every metric in human-readable form to standard error: + +```go +go metrics.Log(metrics.DefaultRegistry, 5 * time.Second, log.New(os.Stderr, "metrics: ", log.Lmicroseconds)) +``` + +Periodically log every metric in slightly-more-parseable form to syslog: + +```go +w, _ := syslog.Dial("unixgram", "/dev/log", syslog.LOG_INFO, "metrics") +go metrics.Syslog(metrics.DefaultRegistry, 60e9, w) +``` + +Periodically emit every metric to Graphite using the [Graphite client](https://github.com/cyberdelia/go-metrics-graphite): + +```go + +import "github.com/cyberdelia/go-metrics-graphite" + +addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003") +go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr) +``` + +Periodically emit every metric into InfluxDB: + +**NOTE:** this has been pulled out of the library due to constant fluctuations +in the InfluxDB API. In fact, all client libraries are on their way out. see +issues [#121](https://github.com/rcrowley/go-metrics/issues/121) and +[#124](https://github.com/rcrowley/go-metrics/issues/124) for progress and details. + +```go +import "github.com/vrischmann/go-metrics-influxdb" + +go influxdb.Influxdb(metrics.DefaultRegistry, 10e9, &influxdb.Config{ + Host: "127.0.0.1:8086", + Database: "metrics", + Username: "test", + Password: "test", +}) +``` + +Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato): + +**Note**: the client included with this repository under the `librato` package +has been deprecated and moved to the repository linked above. + +```go +import "github.com/mihasya/go-metrics-librato" + +go librato.Librato(metrics.DefaultRegistry, + 10e9, // interval + "example@example.com", // account owner email address + "token", // Librato API token + "hostname", // source + []float64{0.95}, // percentiles to send + time.Millisecond, // time unit +) +``` + +Periodically emit every metric to StatHat: + +```go +import "github.com/rcrowley/go-metrics/stathat" + +go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com") +``` + +Maintain all metrics along with expvars at `/debug/metrics`: + +This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/) +but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars +as well as all your go-metrics. + + +```go +import "github.com/rcrowley/go-metrics/exp" + +exp.Exp(metrics.DefaultRegistry) +``` + +Installation +------------ + +```sh +go get github.com/rcrowley/go-metrics +``` + +StatHat support additionally requires their Go client: + +```sh +go get github.com/stathat/go +``` + +Publishing Metrics +------------------ + +Clients are available for the following destinations: + +* Librato - [https://github.com/mihasya/go-metrics-librato](https://github.com/mihasya/go-metrics-librato) +* Graphite - [https://github.com/cyberdelia/go-metrics-graphite](https://github.com/cyberdelia/go-metrics-graphite) +* InfluxDB - [https://github.com/vrischmann/go-metrics-influxdb](https://github.com/vrischmann/go-metrics-influxdb) +* Ganglia - [https://github.com/appscode/metlia](https://github.com/appscode/metlia) +* Prometheus - [https://github.com/deathowl/go-metrics-prometheus](https://github.com/deathowl/go-metrics-prometheus) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/counter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/counter.go new file mode 100644 index 0000000..bb7b039 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/counter.go @@ -0,0 +1,112 @@ +package metrics + +import "sync/atomic" + +// Counters hold an int64 value that can be incremented and decremented. +type Counter interface { + Clear() + Count() int64 + Dec(int64) + Inc(int64) + Snapshot() Counter +} + +// GetOrRegisterCounter returns an existing Counter or constructs and registers +// a new StandardCounter. +func GetOrRegisterCounter(name string, r Registry) Counter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewCounter).(Counter) +} + +// NewCounter constructs a new StandardCounter. +func NewCounter() Counter { + if UseNilMetrics { + return NilCounter{} + } + return &StandardCounter{0} +} + +// NewRegisteredCounter constructs and registers a new StandardCounter. +func NewRegisteredCounter(name string, r Registry) Counter { + c := NewCounter() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// CounterSnapshot is a read-only copy of another Counter. +type CounterSnapshot int64 + +// Clear panics. +func (CounterSnapshot) Clear() { + panic("Clear called on a CounterSnapshot") +} + +// Count returns the count at the time the snapshot was taken. +func (c CounterSnapshot) Count() int64 { return int64(c) } + +// Dec panics. +func (CounterSnapshot) Dec(int64) { + panic("Dec called on a CounterSnapshot") +} + +// Inc panics. +func (CounterSnapshot) Inc(int64) { + panic("Inc called on a CounterSnapshot") +} + +// Snapshot returns the snapshot. +func (c CounterSnapshot) Snapshot() Counter { return c } + +// NilCounter is a no-op Counter. +type NilCounter struct{} + +// Clear is a no-op. +func (NilCounter) Clear() {} + +// Count is a no-op. +func (NilCounter) Count() int64 { return 0 } + +// Dec is a no-op. +func (NilCounter) Dec(i int64) {} + +// Inc is a no-op. +func (NilCounter) Inc(i int64) {} + +// Snapshot is a no-op. +func (NilCounter) Snapshot() Counter { return NilCounter{} } + +// StandardCounter is the standard implementation of a Counter and uses the +// sync/atomic package to manage a single int64 value. +type StandardCounter struct { + count int64 +} + +// Clear sets the counter to zero. +func (c *StandardCounter) Clear() { + atomic.StoreInt64(&c.count, 0) +} + +// Count returns the current count. +func (c *StandardCounter) Count() int64 { + return atomic.LoadInt64(&c.count) +} + +// Dec decrements the counter by the given amount. +func (c *StandardCounter) Dec(i int64) { + atomic.AddInt64(&c.count, -i) +} + +// Inc increments the counter by the given amount. +func (c *StandardCounter) Inc(i int64) { + atomic.AddInt64(&c.count, i) +} + +// Snapshot returns a read-only copy of the counter. +func (c *StandardCounter) Snapshot() Counter { + return CounterSnapshot(c.Count()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/debug.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/debug.go new file mode 100644 index 0000000..043ccef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/debug.go @@ -0,0 +1,76 @@ +package metrics + +import ( + "runtime/debug" + "time" +) + +var ( + debugMetrics struct { + GCStats struct { + LastGC Gauge + NumGC Gauge + Pause Histogram + //PauseQuantiles Histogram + PauseTotal Gauge + } + ReadGCStats Timer + } + gcStats debug.GCStats +) + +// Capture new values for the Go garbage collector statistics exported in +// debug.GCStats. This is designed to be called as a goroutine. +func CaptureDebugGCStats(r Registry, d time.Duration) { + for _ = range time.Tick(d) { + CaptureDebugGCStatsOnce(r) + } +} + +// Capture new values for the Go garbage collector statistics exported in +// debug.GCStats. This is designed to be called in a background goroutine. +// Giving a registry which has not been given to RegisterDebugGCStats will +// panic. +// +// Be careful (but much less so) with this because debug.ReadGCStats calls +// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world +// operation, isn't something you want to be doing all the time. +func CaptureDebugGCStatsOnce(r Registry) { + lastGC := gcStats.LastGC + t := time.Now() + debug.ReadGCStats(&gcStats) + debugMetrics.ReadGCStats.UpdateSince(t) + + debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano())) + debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC)) + if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { + debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) + } + //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) + debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) +} + +// Register metrics for the Go garbage collector statistics exported in +// debug.GCStats. The metrics are named by their fully-qualified Go symbols, +// i.e. debug.GCStats.PauseTotal. +func RegisterDebugGCStats(r Registry) { + debugMetrics.GCStats.LastGC = NewGauge() + debugMetrics.GCStats.NumGC = NewGauge() + debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015)) + //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015)) + debugMetrics.GCStats.PauseTotal = NewGauge() + debugMetrics.ReadGCStats = NewTimer() + + r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) + r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) + r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) + //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles) + r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) + r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) +} + +// Allocate an initial slice for gcStats.Pause to avoid allocations during +// normal operation. +func init() { + gcStats.Pause = make([]time.Duration, 11) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/ewma.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/ewma.go new file mode 100644 index 0000000..694a1d0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/ewma.go @@ -0,0 +1,118 @@ +package metrics + +import ( + "math" + "sync" + "sync/atomic" +) + +// EWMAs continuously calculate an exponentially-weighted moving average +// based on an outside source of clock ticks. +type EWMA interface { + Rate() float64 + Snapshot() EWMA + Tick() + Update(int64) +} + +// NewEWMA constructs a new EWMA with the given alpha. +func NewEWMA(alpha float64) EWMA { + if UseNilMetrics { + return NilEWMA{} + } + return &StandardEWMA{alpha: alpha} +} + +// NewEWMA1 constructs a new EWMA for a one-minute moving average. +func NewEWMA1() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/1)) +} + +// NewEWMA5 constructs a new EWMA for a five-minute moving average. +func NewEWMA5() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/5)) +} + +// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. +func NewEWMA15() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/15)) +} + +// EWMASnapshot is a read-only copy of another EWMA. +type EWMASnapshot float64 + +// Rate returns the rate of events per second at the time the snapshot was +// taken. +func (a EWMASnapshot) Rate() float64 { return float64(a) } + +// Snapshot returns the snapshot. +func (a EWMASnapshot) Snapshot() EWMA { return a } + +// Tick panics. +func (EWMASnapshot) Tick() { + panic("Tick called on an EWMASnapshot") +} + +// Update panics. +func (EWMASnapshot) Update(int64) { + panic("Update called on an EWMASnapshot") +} + +// NilEWMA is a no-op EWMA. +type NilEWMA struct{} + +// Rate is a no-op. +func (NilEWMA) Rate() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilEWMA) Snapshot() EWMA { return NilEWMA{} } + +// Tick is a no-op. +func (NilEWMA) Tick() {} + +// Update is a no-op. +func (NilEWMA) Update(n int64) {} + +// StandardEWMA is the standard implementation of an EWMA and tracks the number +// of uncounted events and processes them on each tick. It uses the +// sync/atomic package to manage uncounted events. +type StandardEWMA struct { + uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment + alpha float64 + rate float64 + init bool + mutex sync.Mutex +} + +// Rate returns the moving average rate of events per second. +func (a *StandardEWMA) Rate() float64 { + a.mutex.Lock() + defer a.mutex.Unlock() + return a.rate * float64(1e9) +} + +// Snapshot returns a read-only copy of the EWMA. +func (a *StandardEWMA) Snapshot() EWMA { + return EWMASnapshot(a.Rate()) +} + +// Tick ticks the clock to update the moving average. It assumes it is called +// every five seconds. +func (a *StandardEWMA) Tick() { + count := atomic.LoadInt64(&a.uncounted) + atomic.AddInt64(&a.uncounted, -count) + instantRate := float64(count) / float64(5e9) + a.mutex.Lock() + defer a.mutex.Unlock() + if a.init { + a.rate += a.alpha * (instantRate - a.rate) + } else { + a.init = true + a.rate = instantRate + } +} + +// Update adds n uncounted events. +func (a *StandardEWMA) Update(n int64) { + atomic.AddInt64(&a.uncounted, n) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/exp/exp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/exp/exp.go new file mode 100644 index 0000000..11dd3f8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/exp/exp.go @@ -0,0 +1,156 @@ +// Hook go-metrics into expvar +// on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler +package exp + +import ( + "expvar" + "fmt" + "net/http" + "sync" + + "github.com/rcrowley/go-metrics" +) + +type exp struct { + expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely + registry metrics.Registry +} + +func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) { + // load our variables into expvar + exp.syncToExpvar() + + // now just run the official expvar handler code (which is not publicly callable, so pasted inline) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + fmt.Fprintf(w, "{\n") + first := true + expvar.Do(func(kv expvar.KeyValue) { + if !first { + fmt.Fprintf(w, ",\n") + } + first = false + fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) + }) + fmt.Fprintf(w, "\n}\n") +} + +// Exp will register an expvar powered metrics handler with http.DefaultServeMux on "/debug/vars" +func Exp(r metrics.Registry) { + h := ExpHandler(r) + // this would cause a panic: + // panic: http: multiple registrations for /debug/vars + // http.HandleFunc("/debug/vars", e.expHandler) + // haven't found an elegant way, so just use a different endpoint + http.Handle("/debug/metrics", h) +} + +// ExpHandler will return an expvar powered metrics handler. +func ExpHandler(r metrics.Registry) http.Handler { + e := exp{sync.Mutex{}, r} + return http.HandlerFunc(e.expHandler) +} + +func (exp *exp) getInt(name string) *expvar.Int { + var v *expvar.Int + exp.expvarLock.Lock() + p := expvar.Get(name) + if p != nil { + v = p.(*expvar.Int) + } else { + v = new(expvar.Int) + expvar.Publish(name, v) + } + exp.expvarLock.Unlock() + return v +} + +func (exp *exp) getFloat(name string) *expvar.Float { + var v *expvar.Float + exp.expvarLock.Lock() + p := expvar.Get(name) + if p != nil { + v = p.(*expvar.Float) + } else { + v = new(expvar.Float) + expvar.Publish(name, v) + } + exp.expvarLock.Unlock() + return v +} + +func (exp *exp) publishCounter(name string, metric metrics.Counter) { + v := exp.getInt(name) + v.Set(metric.Count()) +} + +func (exp *exp) publishGauge(name string, metric metrics.Gauge) { + v := exp.getInt(name) + v.Set(metric.Value()) +} +func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) { + exp.getFloat(name).Set(metric.Value()) +} + +func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + exp.getInt(name + ".count").Set(h.Count()) + exp.getFloat(name + ".min").Set(float64(h.Min())) + exp.getFloat(name + ".max").Set(float64(h.Max())) + exp.getFloat(name + ".mean").Set(float64(h.Mean())) + exp.getFloat(name + ".std-dev").Set(float64(h.StdDev())) + exp.getFloat(name + ".50-percentile").Set(float64(ps[0])) + exp.getFloat(name + ".75-percentile").Set(float64(ps[1])) + exp.getFloat(name + ".95-percentile").Set(float64(ps[2])) + exp.getFloat(name + ".99-percentile").Set(float64(ps[3])) + exp.getFloat(name + ".999-percentile").Set(float64(ps[4])) +} + +func (exp *exp) publishMeter(name string, metric metrics.Meter) { + m := metric.Snapshot() + exp.getInt(name + ".count").Set(m.Count()) + exp.getFloat(name + ".one-minute").Set(float64(m.Rate1())) + exp.getFloat(name + ".five-minute").Set(float64(m.Rate5())) + exp.getFloat(name + ".fifteen-minute").Set(float64((m.Rate15()))) + exp.getFloat(name + ".mean").Set(float64(m.RateMean())) +} + +func (exp *exp) publishTimer(name string, metric metrics.Timer) { + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + exp.getInt(name + ".count").Set(t.Count()) + exp.getFloat(name + ".min").Set(float64(t.Min())) + exp.getFloat(name + ".max").Set(float64(t.Max())) + exp.getFloat(name + ".mean").Set(float64(t.Mean())) + exp.getFloat(name + ".std-dev").Set(float64(t.StdDev())) + exp.getFloat(name + ".50-percentile").Set(float64(ps[0])) + exp.getFloat(name + ".75-percentile").Set(float64(ps[1])) + exp.getFloat(name + ".95-percentile").Set(float64(ps[2])) + exp.getFloat(name + ".99-percentile").Set(float64(ps[3])) + exp.getFloat(name + ".999-percentile").Set(float64(ps[4])) + exp.getFloat(name + ".one-minute").Set(float64(t.Rate1())) + exp.getFloat(name + ".five-minute").Set(float64(t.Rate5())) + exp.getFloat(name + ".fifteen-minute").Set(float64((t.Rate15()))) + exp.getFloat(name + ".mean-rate").Set(float64(t.RateMean())) +} + +func (exp *exp) syncToExpvar() { + exp.registry.Each(func(name string, i interface{}) { + switch i.(type) { + case metrics.Counter: + exp.publishCounter(name, i.(metrics.Counter)) + case metrics.Gauge: + exp.publishGauge(name, i.(metrics.Gauge)) + case metrics.GaugeFloat64: + exp.publishGaugeFloat64(name, i.(metrics.GaugeFloat64)) + case metrics.Histogram: + exp.publishHistogram(name, i.(metrics.Histogram)) + case metrics.Meter: + exp.publishMeter(name, i.(metrics.Meter)) + case metrics.Timer: + exp.publishTimer(name, i.(metrics.Timer)) + default: + panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) + } + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge.go new file mode 100644 index 0000000..cb57a93 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge.go @@ -0,0 +1,120 @@ +package metrics + +import "sync/atomic" + +// Gauges hold an int64 value that can be set arbitrarily. +type Gauge interface { + Snapshot() Gauge + Update(int64) + Value() int64 +} + +// GetOrRegisterGauge returns an existing Gauge or constructs and registers a +// new StandardGauge. +func GetOrRegisterGauge(name string, r Registry) Gauge { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGauge).(Gauge) +} + +// NewGauge constructs a new StandardGauge. +func NewGauge() Gauge { + if UseNilMetrics { + return NilGauge{} + } + return &StandardGauge{0} +} + +// NewRegisteredGauge constructs and registers a new StandardGauge. +func NewRegisteredGauge(name string, r Registry) Gauge { + c := NewGauge() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGauge(f func() int64) Gauge { + if UseNilMetrics { + return NilGauge{} + } + return &FunctionalGauge{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge { + c := NewFunctionalGauge(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeSnapshot is a read-only copy of another Gauge. +type GaugeSnapshot int64 + +// Snapshot returns the snapshot. +func (g GaugeSnapshot) Snapshot() Gauge { return g } + +// Update panics. +func (GaugeSnapshot) Update(int64) { + panic("Update called on a GaugeSnapshot") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeSnapshot) Value() int64 { return int64(g) } + +// NilGauge is a no-op Gauge. +type NilGauge struct{} + +// Snapshot is a no-op. +func (NilGauge) Snapshot() Gauge { return NilGauge{} } + +// Update is a no-op. +func (NilGauge) Update(v int64) {} + +// Value is a no-op. +func (NilGauge) Value() int64 { return 0 } + +// StandardGauge is the standard implementation of a Gauge and uses the +// sync/atomic package to manage a single int64 value. +type StandardGauge struct { + value int64 +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGauge) Snapshot() Gauge { + return GaugeSnapshot(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGauge) Update(v int64) { + atomic.StoreInt64(&g.value, v) +} + +// Value returns the gauge's current value. +func (g *StandardGauge) Value() int64 { + return atomic.LoadInt64(&g.value) +} + +// FunctionalGauge returns value from given function +type FunctionalGauge struct { + value func() int64 +} + +// Value returns the gauge's current value. +func (g FunctionalGauge) Value() int64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } + +// Update panics. +func (FunctionalGauge) Update(int64) { + panic("Update called on a FunctionalGauge") +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge_float64.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge_float64.go new file mode 100644 index 0000000..6f93920 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/gauge_float64.go @@ -0,0 +1,127 @@ +package metrics + +import "sync" + +// GaugeFloat64s hold a float64 value that can be set arbitrarily. +type GaugeFloat64 interface { + Snapshot() GaugeFloat64 + Update(float64) + Value() float64 +} + +// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a +// new StandardGaugeFloat64. +func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) +} + +// NewGaugeFloat64 constructs a new StandardGaugeFloat64. +func NewGaugeFloat64() GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &StandardGaugeFloat64{ + value: 0.0, + } +} + +// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. +func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { + c := NewGaugeFloat64() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &FunctionalGaugeFloat64{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 { + c := NewFunctionalGaugeFloat64(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. +type GaugeFloat64Snapshot float64 + +// Snapshot returns the snapshot. +func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g } + +// Update panics. +func (GaugeFloat64Snapshot) Update(float64) { + panic("Update called on a GaugeFloat64Snapshot") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } + +// NilGauge is a no-op Gauge. +type NilGaugeFloat64 struct{} + +// Snapshot is a no-op. +func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } + +// Update is a no-op. +func (NilGaugeFloat64) Update(v float64) {} + +// Value is a no-op. +func (NilGaugeFloat64) Value() float64 { return 0.0 } + +// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses +// sync.Mutex to manage a single float64 value. +type StandardGaugeFloat64 struct { + mutex sync.Mutex + value float64 +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { + return GaugeFloat64Snapshot(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGaugeFloat64) Update(v float64) { + g.mutex.Lock() + defer g.mutex.Unlock() + g.value = v +} + +// Value returns the gauge's current value. +func (g *StandardGaugeFloat64) Value() float64 { + g.mutex.Lock() + defer g.mutex.Unlock() + return g.value +} + +// FunctionalGaugeFloat64 returns value from given function +type FunctionalGaugeFloat64 struct { + value func() float64 +} + +// Value returns the gauge's current value. +func (g FunctionalGaugeFloat64) Value() float64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) } + +// Update panics. +func (FunctionalGaugeFloat64) Update(float64) { + panic("Update called on a FunctionalGaugeFloat64") +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/graphite.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/graphite.go new file mode 100644 index 0000000..abd0a7d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/graphite.go @@ -0,0 +1,113 @@ +package metrics + +import ( + "bufio" + "fmt" + "log" + "net" + "strconv" + "strings" + "time" +) + +// GraphiteConfig provides a container with configuration parameters for +// the Graphite exporter +type GraphiteConfig struct { + Addr *net.TCPAddr // Network address to connect to + Registry Registry // Registry to be exported + FlushInterval time.Duration // Flush interval + DurationUnit time.Duration // Time conversion unit for durations + Prefix string // Prefix to be prepended to metric names + Percentiles []float64 // Percentiles to export from timers and histograms +} + +// Graphite is a blocking exporter function which reports metrics in r +// to a graphite server located at addr, flushing them every d duration +// and prepending metric names with prefix. +func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { + GraphiteWithConfig(GraphiteConfig{ + Addr: addr, + Registry: r, + FlushInterval: d, + DurationUnit: time.Nanosecond, + Prefix: prefix, + Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, + }) +} + +// GraphiteWithConfig is a blocking exporter function just like Graphite, +// but it takes a GraphiteConfig instead. +func GraphiteWithConfig(c GraphiteConfig) { + log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") + for _ = range time.Tick(c.FlushInterval) { + if err := graphite(&c); nil != err { + log.Println(err) + } + } +} + +// GraphiteOnce performs a single submission to Graphite, returning a +// non-nil error on failed connections. This can be used in a loop +// similar to GraphiteWithConfig for custom error handling. +func GraphiteOnce(c GraphiteConfig) error { + log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") + return graphite(&c) +} + +func graphite(c *GraphiteConfig) error { + now := time.Now().Unix() + du := float64(c.DurationUnit) + conn, err := net.DialTCP("tcp", nil, c.Addr) + if nil != err { + return err + } + defer conn.Close() + w := bufio.NewWriter(conn) + c.Registry.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) + case Gauge: + fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) + case GaugeFloat64: + fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles(c.Percentiles) + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) + fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) + fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) + fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) + for psIdx, psKey := range c.Percentiles { + key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) + fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) + } + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) + fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) + fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) + fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles(c.Percentiles) + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) + fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) + fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) + fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) + for psIdx, psKey := range c.Percentiles { + key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) + fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) + } + fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) + fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) + fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) + fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) + } + w.Flush() + }) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/healthcheck.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/healthcheck.go new file mode 100644 index 0000000..445131c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/healthcheck.go @@ -0,0 +1,61 @@ +package metrics + +// Healthchecks hold an error value describing an arbitrary up/down status. +type Healthcheck interface { + Check() + Error() error + Healthy() + Unhealthy(error) +} + +// NewHealthcheck constructs a new Healthcheck which will use the given +// function to update its status. +func NewHealthcheck(f func(Healthcheck)) Healthcheck { + if UseNilMetrics { + return NilHealthcheck{} + } + return &StandardHealthcheck{nil, f} +} + +// NilHealthcheck is a no-op. +type NilHealthcheck struct{} + +// Check is a no-op. +func (NilHealthcheck) Check() {} + +// Error is a no-op. +func (NilHealthcheck) Error() error { return nil } + +// Healthy is a no-op. +func (NilHealthcheck) Healthy() {} + +// Unhealthy is a no-op. +func (NilHealthcheck) Unhealthy(error) {} + +// StandardHealthcheck is the standard implementation of a Healthcheck and +// stores the status and a function to call to update the status. +type StandardHealthcheck struct { + err error + f func(Healthcheck) +} + +// Check runs the healthcheck function to update the healthcheck's status. +func (h *StandardHealthcheck) Check() { + h.f(h) +} + +// Error returns the healthcheck's status, which will be nil if it is healthy. +func (h *StandardHealthcheck) Error() error { + return h.err +} + +// Healthy marks the healthcheck as healthy. +func (h *StandardHealthcheck) Healthy() { + h.err = nil +} + +// Unhealthy marks the healthcheck as unhealthy. The error is stored and +// may be retrieved by the Error method. +func (h *StandardHealthcheck) Unhealthy(err error) { + h.err = err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/histogram.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/histogram.go new file mode 100644 index 0000000..dbc837f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/histogram.go @@ -0,0 +1,202 @@ +package metrics + +// Histograms calculate distribution statistics from a series of int64 values. +type Histogram interface { + Clear() + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Sample() Sample + Snapshot() Histogram + StdDev() float64 + Sum() int64 + Update(int64) + Variance() float64 +} + +// GetOrRegisterHistogram returns an existing Histogram or constructs and +// registers a new StandardHistogram. +func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) +} + +// NewHistogram constructs a new StandardHistogram from a Sample. +func NewHistogram(s Sample) Histogram { + if UseNilMetrics { + return NilHistogram{} + } + return &StandardHistogram{sample: s} +} + +// NewRegisteredHistogram constructs and registers a new StandardHistogram from +// a Sample. +func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { + c := NewHistogram(s) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// HistogramSnapshot is a read-only copy of another Histogram. +type HistogramSnapshot struct { + sample *SampleSnapshot +} + +// Clear panics. +func (*HistogramSnapshot) Clear() { + panic("Clear called on a HistogramSnapshot") +} + +// Count returns the number of samples recorded at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() } + +// Max returns the maximum value in the sample at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() } + +// Mean returns the mean of the values in the sample at the time the snapshot +// was taken. +func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() } + +// Min returns the minimum value in the sample at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() } + +// Percentile returns an arbitrary percentile of values in the sample at the +// time the snapshot was taken. +func (h *HistogramSnapshot) Percentile(p float64) float64 { + return h.sample.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the sample +// at the time the snapshot was taken. +func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 { + return h.sample.Percentiles(ps) +} + +// Sample returns the Sample underlying the histogram. +func (h *HistogramSnapshot) Sample() Sample { return h.sample } + +// Snapshot returns the snapshot. +func (h *HistogramSnapshot) Snapshot() Histogram { return h } + +// StdDev returns the standard deviation of the values in the sample at the +// time the snapshot was taken. +func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() } + +// Sum returns the sum in the sample at the time the snapshot was taken. +func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() } + +// Update panics. +func (*HistogramSnapshot) Update(int64) { + panic("Update called on a HistogramSnapshot") +} + +// Variance returns the variance of inputs at the time the snapshot was taken. +func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() } + +// NilHistogram is a no-op Histogram. +type NilHistogram struct{} + +// Clear is a no-op. +func (NilHistogram) Clear() {} + +// Count is a no-op. +func (NilHistogram) Count() int64 { return 0 } + +// Max is a no-op. +func (NilHistogram) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilHistogram) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilHistogram) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilHistogram) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilHistogram) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Sample is a no-op. +func (NilHistogram) Sample() Sample { return NilSample{} } + +// Snapshot is a no-op. +func (NilHistogram) Snapshot() Histogram { return NilHistogram{} } + +// StdDev is a no-op. +func (NilHistogram) StdDev() float64 { return 0.0 } + +// Sum is a no-op. +func (NilHistogram) Sum() int64 { return 0 } + +// Update is a no-op. +func (NilHistogram) Update(v int64) {} + +// Variance is a no-op. +func (NilHistogram) Variance() float64 { return 0.0 } + +// StandardHistogram is the standard implementation of a Histogram and uses a +// Sample to bound its memory use. +type StandardHistogram struct { + sample Sample +} + +// Clear clears the histogram and its sample. +func (h *StandardHistogram) Clear() { h.sample.Clear() } + +// Count returns the number of samples recorded since the histogram was last +// cleared. +func (h *StandardHistogram) Count() int64 { return h.sample.Count() } + +// Max returns the maximum value in the sample. +func (h *StandardHistogram) Max() int64 { return h.sample.Max() } + +// Mean returns the mean of the values in the sample. +func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() } + +// Min returns the minimum value in the sample. +func (h *StandardHistogram) Min() int64 { return h.sample.Min() } + +// Percentile returns an arbitrary percentile of the values in the sample. +func (h *StandardHistogram) Percentile(p float64) float64 { + return h.sample.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. +func (h *StandardHistogram) Percentiles(ps []float64) []float64 { + return h.sample.Percentiles(ps) +} + +// Sample returns the Sample underlying the histogram. +func (h *StandardHistogram) Sample() Sample { return h.sample } + +// Snapshot returns a read-only copy of the histogram. +func (h *StandardHistogram) Snapshot() Histogram { + return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)} +} + +// StdDev returns the standard deviation of the values in the sample. +func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() } + +// Sum returns the sum in the sample. +func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() } + +// Update samples a new value. +func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) } + +// Variance returns the variance of the values in the sample. +func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/json.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/json.go new file mode 100644 index 0000000..2fdcbcf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/json.go @@ -0,0 +1,87 @@ +package metrics + +import ( + "encoding/json" + "io" + "time" +) + +// MarshalJSON returns a byte slice containing a JSON representation of all +// the metrics in the Registry. +func (r *StandardRegistry) MarshalJSON() ([]byte, error) { + data := make(map[string]map[string]interface{}) + r.Each(func(name string, i interface{}) { + values := make(map[string]interface{}) + switch metric := i.(type) { + case Counter: + values["count"] = metric.Count() + case Gauge: + values["value"] = metric.Value() + case GaugeFloat64: + values["value"] = metric.Value() + case Healthcheck: + values["error"] = nil + metric.Check() + if err := metric.Error(); nil != err { + values["error"] = metric.Error().Error() + } + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + values["count"] = h.Count() + values["min"] = h.Min() + values["max"] = h.Max() + values["mean"] = h.Mean() + values["stddev"] = h.StdDev() + values["median"] = ps[0] + values["75%"] = ps[1] + values["95%"] = ps[2] + values["99%"] = ps[3] + values["99.9%"] = ps[4] + case Meter: + m := metric.Snapshot() + values["count"] = m.Count() + values["1m.rate"] = m.Rate1() + values["5m.rate"] = m.Rate5() + values["15m.rate"] = m.Rate15() + values["mean.rate"] = m.RateMean() + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + values["count"] = t.Count() + values["min"] = t.Min() + values["max"] = t.Max() + values["mean"] = t.Mean() + values["stddev"] = t.StdDev() + values["median"] = ps[0] + values["75%"] = ps[1] + values["95%"] = ps[2] + values["99%"] = ps[3] + values["99.9%"] = ps[4] + values["1m.rate"] = t.Rate1() + values["5m.rate"] = t.Rate5() + values["15m.rate"] = t.Rate15() + values["mean.rate"] = t.RateMean() + } + data[name] = values + }) + return json.Marshal(data) +} + +// WriteJSON writes metrics from the given registry periodically to the +// specified io.Writer as JSON. +func WriteJSON(r Registry, d time.Duration, w io.Writer) { + for _ = range time.Tick(d) { + WriteJSONOnce(r, w) + } +} + +// WriteJSONOnce writes metrics from the given registry to the specified +// io.Writer as JSON. +func WriteJSONOnce(r Registry, w io.Writer) { + json.NewEncoder(w).Encode(r) +} + +func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { + return json.Marshal(p.underlying) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/log.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/log.go new file mode 100644 index 0000000..f8074c0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/log.go @@ -0,0 +1,80 @@ +package metrics + +import ( + "time" +) + +type Logger interface { + Printf(format string, v ...interface{}) +} + +func Log(r Registry, freq time.Duration, l Logger) { + LogScaled(r, freq, time.Nanosecond, l) +} + +// Output each metric in the given registry periodically using the given +// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. +func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { + du := float64(scale) + duSuffix := scale.String()[1:] + + for _ = range time.Tick(freq) { + r.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + l.Printf("counter %s\n", name) + l.Printf(" count: %9d\n", metric.Count()) + case Gauge: + l.Printf("gauge %s\n", name) + l.Printf(" value: %9d\n", metric.Value()) + case GaugeFloat64: + l.Printf("gauge %s\n", name) + l.Printf(" value: %f\n", metric.Value()) + case Healthcheck: + metric.Check() + l.Printf("healthcheck %s\n", name) + l.Printf(" error: %v\n", metric.Error()) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + l.Printf("histogram %s\n", name) + l.Printf(" count: %9d\n", h.Count()) + l.Printf(" min: %9d\n", h.Min()) + l.Printf(" max: %9d\n", h.Max()) + l.Printf(" mean: %12.2f\n", h.Mean()) + l.Printf(" stddev: %12.2f\n", h.StdDev()) + l.Printf(" median: %12.2f\n", ps[0]) + l.Printf(" 75%%: %12.2f\n", ps[1]) + l.Printf(" 95%%: %12.2f\n", ps[2]) + l.Printf(" 99%%: %12.2f\n", ps[3]) + l.Printf(" 99.9%%: %12.2f\n", ps[4]) + case Meter: + m := metric.Snapshot() + l.Printf("meter %s\n", name) + l.Printf(" count: %9d\n", m.Count()) + l.Printf(" 1-min rate: %12.2f\n", m.Rate1()) + l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) + l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) + l.Printf(" mean rate: %12.2f\n", m.RateMean()) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + l.Printf("timer %s\n", name) + l.Printf(" count: %9d\n", t.Count()) + l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix) + l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix) + l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix) + l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix) + l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix) + l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix) + l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix) + l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix) + l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix) + l.Printf(" 1-min rate: %12.2f\n", t.Rate1()) + l.Printf(" 5-min rate: %12.2f\n", t.Rate5()) + l.Printf(" 15-min rate: %12.2f\n", t.Rate15()) + l.Printf(" mean rate: %12.2f\n", t.RateMean()) + } + }) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/memory.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/memory.md new file mode 100644 index 0000000..47454f5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/memory.md @@ -0,0 +1,285 @@ +Memory usage +============ + +(Highly unscientific.) + +Command used to gather static memory usage: + +```sh +grep ^Vm "/proc/$(ps fax | grep [m]etrics-bench | awk '{print $1}')/status" +``` + +Program used to gather baseline memory usage: + +```go +package main + +import "time" + +func main() { + time.Sleep(600e9) +} +``` + +Baseline +-------- + +``` +VmPeak: 42604 kB +VmSize: 42604 kB +VmLck: 0 kB +VmHWM: 1120 kB +VmRSS: 1120 kB +VmData: 35460 kB +VmStk: 136 kB +VmExe: 1020 kB +VmLib: 1848 kB +VmPTE: 36 kB +VmSwap: 0 kB +``` + +Program used to gather metric memory usage (with other metrics being similar): + +```go +package main + +import ( + "fmt" + "metrics" + "time" +) + +func main() { + fmt.Sprintf("foo") + metrics.NewRegistry() + time.Sleep(600e9) +} +``` + +1000 counters registered +------------------------ + +``` +VmPeak: 44016 kB +VmSize: 44016 kB +VmLck: 0 kB +VmHWM: 1928 kB +VmRSS: 1928 kB +VmData: 36868 kB +VmStk: 136 kB +VmExe: 1024 kB +VmLib: 1848 kB +VmPTE: 40 kB +VmSwap: 0 kB +``` + +**1.412 kB virtual, TODO 0.808 kB resident per counter.** + +100000 counters registered +-------------------------- + +``` +VmPeak: 55024 kB +VmSize: 55024 kB +VmLck: 0 kB +VmHWM: 12440 kB +VmRSS: 12440 kB +VmData: 47876 kB +VmStk: 136 kB +VmExe: 1024 kB +VmLib: 1848 kB +VmPTE: 64 kB +VmSwap: 0 kB +``` + +**0.1242 kB virtual, 0.1132 kB resident per counter.** + +1000 gauges registered +---------------------- + +``` +VmPeak: 44012 kB +VmSize: 44012 kB +VmLck: 0 kB +VmHWM: 1928 kB +VmRSS: 1928 kB +VmData: 36868 kB +VmStk: 136 kB +VmExe: 1020 kB +VmLib: 1848 kB +VmPTE: 40 kB +VmSwap: 0 kB +``` + +**1.408 kB virtual, 0.808 kB resident per counter.** + +100000 gauges registered +------------------------ + +``` +VmPeak: 55020 kB +VmSize: 55020 kB +VmLck: 0 kB +VmHWM: 12432 kB +VmRSS: 12432 kB +VmData: 47876 kB +VmStk: 136 kB +VmExe: 1020 kB +VmLib: 1848 kB +VmPTE: 60 kB +VmSwap: 0 kB +``` + +**0.12416 kB virtual, 0.11312 resident per gauge.** + +1000 histograms with a uniform sample size of 1028 +-------------------------------------------------- + +``` +VmPeak: 72272 kB +VmSize: 72272 kB +VmLck: 0 kB +VmHWM: 16204 kB +VmRSS: 16204 kB +VmData: 65100 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 80 kB +VmSwap: 0 kB +``` + +**29.668 kB virtual, TODO 15.084 resident per histogram.** + +10000 histograms with a uniform sample size of 1028 +--------------------------------------------------- + +``` +VmPeak: 256912 kB +VmSize: 256912 kB +VmLck: 0 kB +VmHWM: 146204 kB +VmRSS: 146204 kB +VmData: 249740 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 448 kB +VmSwap: 0 kB +``` + +**21.4308 kB virtual, 14.5084 kB resident per histogram.** + +50000 histograms with a uniform sample size of 1028 +--------------------------------------------------- + +``` +VmPeak: 908112 kB +VmSize: 908112 kB +VmLck: 0 kB +VmHWM: 645832 kB +VmRSS: 645588 kB +VmData: 900940 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 1716 kB +VmSwap: 1544 kB +``` + +**17.31016 kB virtual, 12.88936 kB resident per histogram.** + +1000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 +------------------------------------------------------------------------------------- + +``` +VmPeak: 62480 kB +VmSize: 62480 kB +VmLck: 0 kB +VmHWM: 11572 kB +VmRSS: 11572 kB +VmData: 55308 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 64 kB +VmSwap: 0 kB +``` + +**19.876 kB virtual, 10.452 kB resident per histogram.** + +10000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 +-------------------------------------------------------------------------------------- + +``` +VmPeak: 153296 kB +VmSize: 153296 kB +VmLck: 0 kB +VmHWM: 101176 kB +VmRSS: 101176 kB +VmData: 146124 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 240 kB +VmSwap: 0 kB +``` + +**11.0692 kB virtual, 10.0056 kB resident per histogram.** + +50000 histograms with an exponentially-decaying sample size of 1028 and alpha of 0.015 +-------------------------------------------------------------------------------------- + +``` +VmPeak: 557264 kB +VmSize: 557264 kB +VmLck: 0 kB +VmHWM: 501056 kB +VmRSS: 501056 kB +VmData: 550092 kB +VmStk: 136 kB +VmExe: 1048 kB +VmLib: 1848 kB +VmPTE: 1032 kB +VmSwap: 0 kB +``` + +**10.2932 kB virtual, 9.99872 kB resident per histogram.** + +1000 meters +----------- + +``` +VmPeak: 74504 kB +VmSize: 74504 kB +VmLck: 0 kB +VmHWM: 24124 kB +VmRSS: 24124 kB +VmData: 67340 kB +VmStk: 136 kB +VmExe: 1040 kB +VmLib: 1848 kB +VmPTE: 92 kB +VmSwap: 0 kB +``` + +**31.9 kB virtual, 23.004 kB resident per meter.** + +10000 meters +------------ + +``` +VmPeak: 278920 kB +VmSize: 278920 kB +VmLck: 0 kB +VmHWM: 227300 kB +VmRSS: 227300 kB +VmData: 271756 kB +VmStk: 136 kB +VmExe: 1040 kB +VmLib: 1848 kB +VmPTE: 488 kB +VmSwap: 0 kB +``` + +**23.6316 kB virtual, 22.618 kB resident per meter.** diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/meter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/meter.go new file mode 100644 index 0000000..0389ab0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/meter.go @@ -0,0 +1,233 @@ +package metrics + +import ( + "sync" + "time" +) + +// Meters count events to produce exponentially-weighted moving average rates +// at one-, five-, and fifteen-minutes and a mean rate. +type Meter interface { + Count() int64 + Mark(int64) + Rate1() float64 + Rate5() float64 + Rate15() float64 + RateMean() float64 + Snapshot() Meter +} + +// GetOrRegisterMeter returns an existing Meter or constructs and registers a +// new StandardMeter. +func GetOrRegisterMeter(name string, r Registry) Meter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewMeter).(Meter) +} + +// NewMeter constructs a new StandardMeter and launches a goroutine. +func NewMeter() Meter { + if UseNilMetrics { + return NilMeter{} + } + m := newStandardMeter() + arbiter.Lock() + defer arbiter.Unlock() + arbiter.meters = append(arbiter.meters, m) + if !arbiter.started { + arbiter.started = true + go arbiter.tick() + } + return m +} + +// NewMeter constructs and registers a new StandardMeter and launches a +// goroutine. +func NewRegisteredMeter(name string, r Registry) Meter { + c := NewMeter() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// MeterSnapshot is a read-only copy of another Meter. +type MeterSnapshot struct { + count int64 + rate1, rate5, rate15, rateMean float64 +} + +// Count returns the count of events at the time the snapshot was taken. +func (m *MeterSnapshot) Count() int64 { return m.count } + +// Mark panics. +func (*MeterSnapshot) Mark(n int64) { + panic("Mark called on a MeterSnapshot") +} + +// Rate1 returns the one-minute moving average rate of events per second at the +// time the snapshot was taken. +func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } + +// Rate5 returns the five-minute moving average rate of events per second at +// the time the snapshot was taken. +func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } + +// Rate15 returns the fifteen-minute moving average rate of events per second +// at the time the snapshot was taken. +func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } + +// RateMean returns the meter's mean rate of events per second at the time the +// snapshot was taken. +func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } + +// Snapshot returns the snapshot. +func (m *MeterSnapshot) Snapshot() Meter { return m } + +// NilMeter is a no-op Meter. +type NilMeter struct{} + +// Count is a no-op. +func (NilMeter) Count() int64 { return 0 } + +// Mark is a no-op. +func (NilMeter) Mark(n int64) {} + +// Rate1 is a no-op. +func (NilMeter) Rate1() float64 { return 0.0 } + +// Rate5 is a no-op. +func (NilMeter) Rate5() float64 { return 0.0 } + +// Rate15is a no-op. +func (NilMeter) Rate15() float64 { return 0.0 } + +// RateMean is a no-op. +func (NilMeter) RateMean() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilMeter) Snapshot() Meter { return NilMeter{} } + +// StandardMeter is the standard implementation of a Meter. +type StandardMeter struct { + lock sync.RWMutex + snapshot *MeterSnapshot + a1, a5, a15 EWMA + startTime time.Time +} + +func newStandardMeter() *StandardMeter { + return &StandardMeter{ + snapshot: &MeterSnapshot{}, + a1: NewEWMA1(), + a5: NewEWMA5(), + a15: NewEWMA15(), + startTime: time.Now(), + } +} + +// Count returns the number of events recorded. +func (m *StandardMeter) Count() int64 { + m.lock.RLock() + count := m.snapshot.count + m.lock.RUnlock() + return count +} + +// Mark records the occurance of n events. +func (m *StandardMeter) Mark(n int64) { + m.lock.Lock() + defer m.lock.Unlock() + m.snapshot.count += n + m.a1.Update(n) + m.a5.Update(n) + m.a15.Update(n) + m.updateSnapshot() +} + +// Rate1 returns the one-minute moving average rate of events per second. +func (m *StandardMeter) Rate1() float64 { + m.lock.RLock() + rate1 := m.snapshot.rate1 + m.lock.RUnlock() + return rate1 +} + +// Rate5 returns the five-minute moving average rate of events per second. +func (m *StandardMeter) Rate5() float64 { + m.lock.RLock() + rate5 := m.snapshot.rate5 + m.lock.RUnlock() + return rate5 +} + +// Rate15 returns the fifteen-minute moving average rate of events per second. +func (m *StandardMeter) Rate15() float64 { + m.lock.RLock() + rate15 := m.snapshot.rate15 + m.lock.RUnlock() + return rate15 +} + +// RateMean returns the meter's mean rate of events per second. +func (m *StandardMeter) RateMean() float64 { + m.lock.RLock() + rateMean := m.snapshot.rateMean + m.lock.RUnlock() + return rateMean +} + +// Snapshot returns a read-only copy of the meter. +func (m *StandardMeter) Snapshot() Meter { + m.lock.RLock() + snapshot := *m.snapshot + m.lock.RUnlock() + return &snapshot +} + +func (m *StandardMeter) updateSnapshot() { + // should run with write lock held on m.lock + snapshot := m.snapshot + snapshot.rate1 = m.a1.Rate() + snapshot.rate5 = m.a5.Rate() + snapshot.rate15 = m.a15.Rate() + snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds() +} + +func (m *StandardMeter) tick() { + m.lock.Lock() + defer m.lock.Unlock() + m.a1.Tick() + m.a5.Tick() + m.a15.Tick() + m.updateSnapshot() +} + +type meterArbiter struct { + sync.RWMutex + started bool + meters []*StandardMeter + ticker *time.Ticker +} + +var arbiter = meterArbiter{ticker: time.NewTicker(5e9)} + +// Ticks meters on the scheduled interval +func (ma *meterArbiter) tick() { + for { + select { + case <-ma.ticker.C: + ma.tickMeters() + } + } +} + +func (ma *meterArbiter) tickMeters() { + ma.RLock() + defer ma.RUnlock() + for _, meter := range ma.meters { + meter.tick() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/metrics.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/metrics.go new file mode 100644 index 0000000..b97a49e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/metrics.go @@ -0,0 +1,13 @@ +// Go port of Coda Hale's Metrics library +// +// +// +// Coda Hale's original work: +package metrics + +// UseNilMetrics is checked by the constructor functions for all of the +// standard metrics. If it is true, the metric returned is a stub. +// +// This global kill-switch helps quantify the observer effect and makes +// for less cluttered pprof profiles. +var UseNilMetrics bool = false diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/opentsdb.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/opentsdb.go new file mode 100644 index 0000000..266b6c9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/opentsdb.go @@ -0,0 +1,119 @@ +package metrics + +import ( + "bufio" + "fmt" + "log" + "net" + "os" + "strings" + "time" +) + +var shortHostName string = "" + +// OpenTSDBConfig provides a container with configuration parameters for +// the OpenTSDB exporter +type OpenTSDBConfig struct { + Addr *net.TCPAddr // Network address to connect to + Registry Registry // Registry to be exported + FlushInterval time.Duration // Flush interval + DurationUnit time.Duration // Time conversion unit for durations + Prefix string // Prefix to be prepended to metric names +} + +// OpenTSDB is a blocking exporter function which reports metrics in r +// to a TSDB server located at addr, flushing them every d duration +// and prepending metric names with prefix. +func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { + OpenTSDBWithConfig(OpenTSDBConfig{ + Addr: addr, + Registry: r, + FlushInterval: d, + DurationUnit: time.Nanosecond, + Prefix: prefix, + }) +} + +// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB, +// but it takes a OpenTSDBConfig instead. +func OpenTSDBWithConfig(c OpenTSDBConfig) { + for _ = range time.Tick(c.FlushInterval) { + if err := openTSDB(&c); nil != err { + log.Println(err) + } + } +} + +func getShortHostname() string { + if shortHostName == "" { + host, _ := os.Hostname() + if index := strings.Index(host, "."); index > 0 { + shortHostName = host[:index] + } else { + shortHostName = host + } + } + return shortHostName +} + +func openTSDB(c *OpenTSDBConfig) error { + shortHostname := getShortHostname() + now := time.Now().Unix() + du := float64(c.DurationUnit) + conn, err := net.DialTCP("tcp", nil, c.Addr) + if nil != err { + return err + } + defer conn.Close() + w := bufio.NewWriter(conn) + c.Registry.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) + case Gauge: + fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + case GaugeFloat64: + fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname) + fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname) + fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname) + fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname) + fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname) + fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) + fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) + fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) + fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) + fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname) + fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname) + fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname) + fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname) + } + w.Flush() + }) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/registry.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/registry.go new file mode 100644 index 0000000..2bb7a1e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/registry.go @@ -0,0 +1,270 @@ +package metrics + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +// DuplicateMetric is the error returned by Registry.Register when a metric +// already exists. If you mean to Register that metric you must first +// Unregister the existing metric. +type DuplicateMetric string + +func (err DuplicateMetric) Error() string { + return fmt.Sprintf("duplicate metric: %s", string(err)) +} + +// A Registry holds references to a set of metrics by name and can iterate +// over them, calling callback functions provided by the user. +// +// This is an interface so as to encourage other structs to implement +// the Registry API as appropriate. +type Registry interface { + + // Call the given function for each registered metric. + Each(func(string, interface{})) + + // Get the metric by the given name or nil if none is registered. + Get(string) interface{} + + // Gets an existing metric or registers the given one. + // The interface can be the metric to register if not found in registry, + // or a function returning the metric for lazy instantiation. + GetOrRegister(string, interface{}) interface{} + + // Register the given metric under the given name. + Register(string, interface{}) error + + // Run all registered healthchecks. + RunHealthchecks() + + // Unregister the metric with the given name. + Unregister(string) + + // Unregister all metrics. (Mostly for testing.) + UnregisterAll() +} + +// The standard implementation of a Registry is a mutex-protected map +// of names to metrics. +type StandardRegistry struct { + metrics map[string]interface{} + mutex sync.Mutex +} + +// Create a new registry. +func NewRegistry() Registry { + return &StandardRegistry{metrics: make(map[string]interface{})} +} + +// Call the given function for each registered metric. +func (r *StandardRegistry) Each(f func(string, interface{})) { + for name, i := range r.registered() { + f(name, i) + } +} + +// Get the metric by the given name or nil if none is registered. +func (r *StandardRegistry) Get(name string) interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + return r.metrics[name] +} + +// Gets an existing metric or creates and registers a new one. Threadsafe +// alternative to calling Get and Register on failure. +// The interface can be the metric to register if not found in registry, +// or a function returning the metric for lazy instantiation. +func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + if metric, ok := r.metrics[name]; ok { + return metric + } + if v := reflect.ValueOf(i); v.Kind() == reflect.Func { + i = v.Call(nil)[0].Interface() + } + r.register(name, i) + return i +} + +// Register the given metric under the given name. Returns a DuplicateMetric +// if a metric by the given name is already registered. +func (r *StandardRegistry) Register(name string, i interface{}) error { + r.mutex.Lock() + defer r.mutex.Unlock() + return r.register(name, i) +} + +// Run all registered healthchecks. +func (r *StandardRegistry) RunHealthchecks() { + r.mutex.Lock() + defer r.mutex.Unlock() + for _, i := range r.metrics { + if h, ok := i.(Healthcheck); ok { + h.Check() + } + } +} + +// Unregister the metric with the given name. +func (r *StandardRegistry) Unregister(name string) { + r.mutex.Lock() + defer r.mutex.Unlock() + delete(r.metrics, name) +} + +// Unregister all metrics. (Mostly for testing.) +func (r *StandardRegistry) UnregisterAll() { + r.mutex.Lock() + defer r.mutex.Unlock() + for name, _ := range r.metrics { + delete(r.metrics, name) + } +} + +func (r *StandardRegistry) register(name string, i interface{}) error { + if _, ok := r.metrics[name]; ok { + return DuplicateMetric(name) + } + switch i.(type) { + case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer: + r.metrics[name] = i + } + return nil +} + +func (r *StandardRegistry) registered() map[string]interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + metrics := make(map[string]interface{}, len(r.metrics)) + for name, i := range r.metrics { + metrics[name] = i + } + return metrics +} + +type PrefixedRegistry struct { + underlying Registry + prefix string +} + +func NewPrefixedRegistry(prefix string) Registry { + return &PrefixedRegistry{ + underlying: NewRegistry(), + prefix: prefix, + } +} + +func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { + return &PrefixedRegistry{ + underlying: parent, + prefix: prefix, + } +} + +// Call the given function for each registered metric. +func (r *PrefixedRegistry) Each(fn func(string, interface{})) { + wrappedFn := func(prefix string) func(string, interface{}) { + return func(name string, iface interface{}) { + if strings.HasPrefix(name, prefix) { + fn(name, iface) + } else { + return + } + } + } + + baseRegistry, prefix := findPrefix(r, "") + baseRegistry.Each(wrappedFn(prefix)) +} + +func findPrefix(registry Registry, prefix string) (Registry, string) { + switch r := registry.(type) { + case *PrefixedRegistry: + return findPrefix(r.underlying, r.prefix+prefix) + case *StandardRegistry: + return r, prefix + } + return nil, "" +} + +// Get the metric by the given name or nil if none is registered. +func (r *PrefixedRegistry) Get(name string) interface{} { + realName := r.prefix + name + return r.underlying.Get(realName) +} + +// Gets an existing metric or registers the given one. +// The interface can be the metric to register if not found in registry, +// or a function returning the metric for lazy instantiation. +func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} { + realName := r.prefix + name + return r.underlying.GetOrRegister(realName, metric) +} + +// Register the given metric under the given name. The name will be prefixed. +func (r *PrefixedRegistry) Register(name string, metric interface{}) error { + realName := r.prefix + name + return r.underlying.Register(realName, metric) +} + +// Run all registered healthchecks. +func (r *PrefixedRegistry) RunHealthchecks() { + r.underlying.RunHealthchecks() +} + +// Unregister the metric with the given name. The name will be prefixed. +func (r *PrefixedRegistry) Unregister(name string) { + realName := r.prefix + name + r.underlying.Unregister(realName) +} + +// Unregister all metrics. (Mostly for testing.) +func (r *PrefixedRegistry) UnregisterAll() { + r.underlying.UnregisterAll() +} + +var DefaultRegistry Registry = NewRegistry() + +// Call the given function for each registered metric. +func Each(f func(string, interface{})) { + DefaultRegistry.Each(f) +} + +// Get the metric by the given name or nil if none is registered. +func Get(name string) interface{} { + return DefaultRegistry.Get(name) +} + +// Gets an existing metric or creates and registers a new one. Threadsafe +// alternative to calling Get and Register on failure. +func GetOrRegister(name string, i interface{}) interface{} { + return DefaultRegistry.GetOrRegister(name, i) +} + +// Register the given metric under the given name. Returns a DuplicateMetric +// if a metric by the given name is already registered. +func Register(name string, i interface{}) error { + return DefaultRegistry.Register(name, i) +} + +// Register the given metric under the given name. Panics if a metric by the +// given name is already registered. +func MustRegister(name string, i interface{}) { + if err := Register(name, i); err != nil { + panic(err) + } +} + +// Run all registered healthchecks. +func RunHealthchecks() { + DefaultRegistry.RunHealthchecks() +} + +// Unregister the metric with the given name. +func Unregister(name string) { + DefaultRegistry.Unregister(name) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime.go new file mode 100644 index 0000000..11c6b78 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime.go @@ -0,0 +1,212 @@ +package metrics + +import ( + "runtime" + "runtime/pprof" + "time" +) + +var ( + memStats runtime.MemStats + runtimeMetrics struct { + MemStats struct { + Alloc Gauge + BuckHashSys Gauge + DebugGC Gauge + EnableGC Gauge + Frees Gauge + HeapAlloc Gauge + HeapIdle Gauge + HeapInuse Gauge + HeapObjects Gauge + HeapReleased Gauge + HeapSys Gauge + LastGC Gauge + Lookups Gauge + Mallocs Gauge + MCacheInuse Gauge + MCacheSys Gauge + MSpanInuse Gauge + MSpanSys Gauge + NextGC Gauge + NumGC Gauge + GCCPUFraction GaugeFloat64 + PauseNs Histogram + PauseTotalNs Gauge + StackInuse Gauge + StackSys Gauge + Sys Gauge + TotalAlloc Gauge + } + NumCgoCall Gauge + NumGoroutine Gauge + NumThread Gauge + ReadMemStats Timer + } + frees uint64 + lookups uint64 + mallocs uint64 + numGC uint32 + numCgoCalls int64 + + threadCreateProfile = pprof.Lookup("threadcreate") +) + +// Capture new values for the Go runtime statistics exported in +// runtime.MemStats. This is designed to be called as a goroutine. +func CaptureRuntimeMemStats(r Registry, d time.Duration) { + for _ = range time.Tick(d) { + CaptureRuntimeMemStatsOnce(r) + } +} + +// Capture new values for the Go runtime statistics exported in +// runtime.MemStats. This is designed to be called in a background +// goroutine. Giving a registry which has not been given to +// RegisterRuntimeMemStats will panic. +// +// Be very careful with this because runtime.ReadMemStats calls the C +// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() +// and that last one does what it says on the tin. +func CaptureRuntimeMemStatsOnce(r Registry) { + t := time.Now() + runtime.ReadMemStats(&memStats) // This takes 50-200us. + runtimeMetrics.ReadMemStats.UpdateSince(t) + + runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) + runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) + if memStats.DebugGC { + runtimeMetrics.MemStats.DebugGC.Update(1) + } else { + runtimeMetrics.MemStats.DebugGC.Update(0) + } + if memStats.EnableGC { + runtimeMetrics.MemStats.EnableGC.Update(1) + } else { + runtimeMetrics.MemStats.EnableGC.Update(0) + } + + runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) + runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) + runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) + runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) + runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) + runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) + runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) + runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) + runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) + runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) + runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) + runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) + runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) + runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) + runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) + runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) + runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) + + // + i := numGC % uint32(len(memStats.PauseNs)) + ii := memStats.NumGC % uint32(len(memStats.PauseNs)) + if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { + for i = 0; i < uint32(len(memStats.PauseNs)); i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + } else { + if i > ii { + for ; i < uint32(len(memStats.PauseNs)); i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + i = 0 + } + for ; i < ii; i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + } + frees = memStats.Frees + lookups = memStats.Lookups + mallocs = memStats.Mallocs + numGC = memStats.NumGC + + runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) + runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) + runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) + runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) + runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) + + currentNumCgoCalls := numCgoCall() + runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) + numCgoCalls = currentNumCgoCalls + + runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) + + runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count())) +} + +// Register runtimeMetrics for the Go runtime statistics exported in runtime and +// specifically runtime.MemStats. The runtimeMetrics are named by their +// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. +func RegisterRuntimeMemStats(r Registry) { + runtimeMetrics.MemStats.Alloc = NewGauge() + runtimeMetrics.MemStats.BuckHashSys = NewGauge() + runtimeMetrics.MemStats.DebugGC = NewGauge() + runtimeMetrics.MemStats.EnableGC = NewGauge() + runtimeMetrics.MemStats.Frees = NewGauge() + runtimeMetrics.MemStats.HeapAlloc = NewGauge() + runtimeMetrics.MemStats.HeapIdle = NewGauge() + runtimeMetrics.MemStats.HeapInuse = NewGauge() + runtimeMetrics.MemStats.HeapObjects = NewGauge() + runtimeMetrics.MemStats.HeapReleased = NewGauge() + runtimeMetrics.MemStats.HeapSys = NewGauge() + runtimeMetrics.MemStats.LastGC = NewGauge() + runtimeMetrics.MemStats.Lookups = NewGauge() + runtimeMetrics.MemStats.Mallocs = NewGauge() + runtimeMetrics.MemStats.MCacheInuse = NewGauge() + runtimeMetrics.MemStats.MCacheSys = NewGauge() + runtimeMetrics.MemStats.MSpanInuse = NewGauge() + runtimeMetrics.MemStats.MSpanSys = NewGauge() + runtimeMetrics.MemStats.NextGC = NewGauge() + runtimeMetrics.MemStats.NumGC = NewGauge() + runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64() + runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015)) + runtimeMetrics.MemStats.PauseTotalNs = NewGauge() + runtimeMetrics.MemStats.StackInuse = NewGauge() + runtimeMetrics.MemStats.StackSys = NewGauge() + runtimeMetrics.MemStats.Sys = NewGauge() + runtimeMetrics.MemStats.TotalAlloc = NewGauge() + runtimeMetrics.NumCgoCall = NewGauge() + runtimeMetrics.NumGoroutine = NewGauge() + runtimeMetrics.NumThread = NewGauge() + runtimeMetrics.ReadMemStats = NewTimer() + + r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) + r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) + r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) + r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) + r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) + r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) + r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) + r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) + r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) + r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) + r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) + r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) + r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) + r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) + r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) + r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) + r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) + r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) + r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) + r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) + r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) + r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) + r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) + r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) + r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) + r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) + r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) + r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) + r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) + r.Register("runtime.NumThread", runtimeMetrics.NumThread) + r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go new file mode 100644 index 0000000..e3391f4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go @@ -0,0 +1,10 @@ +// +build cgo +// +build !appengine + +package metrics + +import "runtime" + +func numCgoCall() int64 { + return runtime.NumCgoCall() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go new file mode 100644 index 0000000..ca12c05 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go @@ -0,0 +1,9 @@ +// +build go1.5 + +package metrics + +import "runtime" + +func gcCPUFraction(memStats *runtime.MemStats) float64 { + return memStats.GCCPUFraction +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go new file mode 100644 index 0000000..616a3b4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go @@ -0,0 +1,7 @@ +// +build !cgo appengine + +package metrics + +func numCgoCall() int64 { + return 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go new file mode 100644 index 0000000..be96aa6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go @@ -0,0 +1,9 @@ +// +build !go1.5 + +package metrics + +import "runtime" + +func gcCPUFraction(memStats *runtime.MemStats) float64 { + return 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/sample.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/sample.go new file mode 100644 index 0000000..fecee5e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/sample.go @@ -0,0 +1,616 @@ +package metrics + +import ( + "math" + "math/rand" + "sort" + "sync" + "time" +) + +const rescaleThreshold = time.Hour + +// Samples maintain a statistically-significant selection of values from +// a stream. +type Sample interface { + Clear() + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Size() int + Snapshot() Sample + StdDev() float64 + Sum() int64 + Update(int64) + Values() []int64 + Variance() float64 +} + +// ExpDecaySample is an exponentially-decaying sample using a forward-decaying +// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time +// Decay Model for Streaming Systems". +// +// +type ExpDecaySample struct { + alpha float64 + count int64 + mutex sync.Mutex + reservoirSize int + t0, t1 time.Time + values *expDecaySampleHeap +} + +// NewExpDecaySample constructs a new exponentially-decaying sample with the +// given reservoir size and alpha. +func NewExpDecaySample(reservoirSize int, alpha float64) Sample { + if UseNilMetrics { + return NilSample{} + } + s := &ExpDecaySample{ + alpha: alpha, + reservoirSize: reservoirSize, + t0: time.Now(), + values: newExpDecaySampleHeap(reservoirSize), + } + s.t1 = s.t0.Add(rescaleThreshold) + return s +} + +// Clear clears all samples. +func (s *ExpDecaySample) Clear() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count = 0 + s.t0 = time.Now() + s.t1 = s.t0.Add(rescaleThreshold) + s.values.Clear() +} + +// Count returns the number of samples recorded, which may exceed the +// reservoir size. +func (s *ExpDecaySample) Count() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.count +} + +// Max returns the maximum value in the sample, which may not be the maximum +// value ever to be part of the sample. +func (s *ExpDecaySample) Max() int64 { + return SampleMax(s.Values()) +} + +// Mean returns the mean of the values in the sample. +func (s *ExpDecaySample) Mean() float64 { + return SampleMean(s.Values()) +} + +// Min returns the minimum value in the sample, which may not be the minimum +// value ever to be part of the sample. +func (s *ExpDecaySample) Min() int64 { + return SampleMin(s.Values()) +} + +// Percentile returns an arbitrary percentile of values in the sample. +func (s *ExpDecaySample) Percentile(p float64) float64 { + return SamplePercentile(s.Values(), p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. +func (s *ExpDecaySample) Percentiles(ps []float64) []float64 { + return SamplePercentiles(s.Values(), ps) +} + +// Size returns the size of the sample, which is at most the reservoir size. +func (s *ExpDecaySample) Size() int { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.values.Size() +} + +// Snapshot returns a read-only copy of the sample. +func (s *ExpDecaySample) Snapshot() Sample { + s.mutex.Lock() + defer s.mutex.Unlock() + vals := s.values.Values() + values := make([]int64, len(vals)) + for i, v := range vals { + values[i] = v.v + } + return &SampleSnapshot{ + count: s.count, + values: values, + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (s *ExpDecaySample) StdDev() float64 { + return SampleStdDev(s.Values()) +} + +// Sum returns the sum of the values in the sample. +func (s *ExpDecaySample) Sum() int64 { + return SampleSum(s.Values()) +} + +// Update samples a new value. +func (s *ExpDecaySample) Update(v int64) { + s.update(time.Now(), v) +} + +// Values returns a copy of the values in the sample. +func (s *ExpDecaySample) Values() []int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + vals := s.values.Values() + values := make([]int64, len(vals)) + for i, v := range vals { + values[i] = v.v + } + return values +} + +// Variance returns the variance of the values in the sample. +func (s *ExpDecaySample) Variance() float64 { + return SampleVariance(s.Values()) +} + +// update samples a new value at a particular timestamp. This is a method all +// its own to facilitate testing. +func (s *ExpDecaySample) update(t time.Time, v int64) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count++ + if s.values.Size() == s.reservoirSize { + s.values.Pop() + } + s.values.Push(expDecaySample{ + k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(), + v: v, + }) + if t.After(s.t1) { + values := s.values.Values() + t0 := s.t0 + s.values.Clear() + s.t0 = t + s.t1 = s.t0.Add(rescaleThreshold) + for _, v := range values { + v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) + s.values.Push(v) + } + } +} + +// NilSample is a no-op Sample. +type NilSample struct{} + +// Clear is a no-op. +func (NilSample) Clear() {} + +// Count is a no-op. +func (NilSample) Count() int64 { return 0 } + +// Max is a no-op. +func (NilSample) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilSample) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilSample) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilSample) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilSample) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Size is a no-op. +func (NilSample) Size() int { return 0 } + +// Sample is a no-op. +func (NilSample) Snapshot() Sample { return NilSample{} } + +// StdDev is a no-op. +func (NilSample) StdDev() float64 { return 0.0 } + +// Sum is a no-op. +func (NilSample) Sum() int64 { return 0 } + +// Update is a no-op. +func (NilSample) Update(v int64) {} + +// Values is a no-op. +func (NilSample) Values() []int64 { return []int64{} } + +// Variance is a no-op. +func (NilSample) Variance() float64 { return 0.0 } + +// SampleMax returns the maximum value of the slice of int64. +func SampleMax(values []int64) int64 { + if 0 == len(values) { + return 0 + } + var max int64 = math.MinInt64 + for _, v := range values { + if max < v { + max = v + } + } + return max +} + +// SampleMean returns the mean value of the slice of int64. +func SampleMean(values []int64) float64 { + if 0 == len(values) { + return 0.0 + } + return float64(SampleSum(values)) / float64(len(values)) +} + +// SampleMin returns the minimum value of the slice of int64. +func SampleMin(values []int64) int64 { + if 0 == len(values) { + return 0 + } + var min int64 = math.MaxInt64 + for _, v := range values { + if min > v { + min = v + } + } + return min +} + +// SamplePercentiles returns an arbitrary percentile of the slice of int64. +func SamplePercentile(values int64Slice, p float64) float64 { + return SamplePercentiles(values, []float64{p})[0] +} + +// SamplePercentiles returns a slice of arbitrary percentiles of the slice of +// int64. +func SamplePercentiles(values int64Slice, ps []float64) []float64 { + scores := make([]float64, len(ps)) + size := len(values) + if size > 0 { + sort.Sort(values) + for i, p := range ps { + pos := p * float64(size+1) + if pos < 1.0 { + scores[i] = float64(values[0]) + } else if pos >= float64(size) { + scores[i] = float64(values[size-1]) + } else { + lower := float64(values[int(pos)-1]) + upper := float64(values[int(pos)]) + scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) + } + } + } + return scores +} + +// SampleSnapshot is a read-only copy of another Sample. +type SampleSnapshot struct { + count int64 + values []int64 +} + +func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot { + return &SampleSnapshot{ + count: count, + values: values, + } +} + +// Clear panics. +func (*SampleSnapshot) Clear() { + panic("Clear called on a SampleSnapshot") +} + +// Count returns the count of inputs at the time the snapshot was taken. +func (s *SampleSnapshot) Count() int64 { return s.count } + +// Max returns the maximal value at the time the snapshot was taken. +func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) } + +// Mean returns the mean value at the time the snapshot was taken. +func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) } + +// Min returns the minimal value at the time the snapshot was taken. +func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) } + +// Percentile returns an arbitrary percentile of values at the time the +// snapshot was taken. +func (s *SampleSnapshot) Percentile(p float64) float64 { + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values at the time +// the snapshot was taken. +func (s *SampleSnapshot) Percentiles(ps []float64) []float64 { + return SamplePercentiles(s.values, ps) +} + +// Size returns the size of the sample at the time the snapshot was taken. +func (s *SampleSnapshot) Size() int { return len(s.values) } + +// Snapshot returns the snapshot. +func (s *SampleSnapshot) Snapshot() Sample { return s } + +// StdDev returns the standard deviation of values at the time the snapshot was +// taken. +func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) } + +// Sum returns the sum of values at the time the snapshot was taken. +func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) } + +// Update panics. +func (*SampleSnapshot) Update(int64) { + panic("Update called on a SampleSnapshot") +} + +// Values returns a copy of the values in the sample. +func (s *SampleSnapshot) Values() []int64 { + values := make([]int64, len(s.values)) + copy(values, s.values) + return values +} + +// Variance returns the variance of values at the time the snapshot was taken. +func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) } + +// SampleStdDev returns the standard deviation of the slice of int64. +func SampleStdDev(values []int64) float64 { + return math.Sqrt(SampleVariance(values)) +} + +// SampleSum returns the sum of the slice of int64. +func SampleSum(values []int64) int64 { + var sum int64 + for _, v := range values { + sum += v + } + return sum +} + +// SampleVariance returns the variance of the slice of int64. +func SampleVariance(values []int64) float64 { + if 0 == len(values) { + return 0.0 + } + m := SampleMean(values) + var sum float64 + for _, v := range values { + d := float64(v) - m + sum += d * d + } + return sum / float64(len(values)) +} + +// A uniform sample using Vitter's Algorithm R. +// +// +type UniformSample struct { + count int64 + mutex sync.Mutex + reservoirSize int + values []int64 +} + +// NewUniformSample constructs a new uniform sample with the given reservoir +// size. +func NewUniformSample(reservoirSize int) Sample { + if UseNilMetrics { + return NilSample{} + } + return &UniformSample{ + reservoirSize: reservoirSize, + values: make([]int64, 0, reservoirSize), + } +} + +// Clear clears all samples. +func (s *UniformSample) Clear() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count = 0 + s.values = make([]int64, 0, s.reservoirSize) +} + +// Count returns the number of samples recorded, which may exceed the +// reservoir size. +func (s *UniformSample) Count() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.count +} + +// Max returns the maximum value in the sample, which may not be the maximum +// value ever to be part of the sample. +func (s *UniformSample) Max() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMax(s.values) +} + +// Mean returns the mean of the values in the sample. +func (s *UniformSample) Mean() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMean(s.values) +} + +// Min returns the minimum value in the sample, which may not be the minimum +// value ever to be part of the sample. +func (s *UniformSample) Min() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMin(s.values) +} + +// Percentile returns an arbitrary percentile of values in the sample. +func (s *UniformSample) Percentile(p float64) float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. +func (s *UniformSample) Percentiles(ps []float64) []float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SamplePercentiles(s.values, ps) +} + +// Size returns the size of the sample, which is at most the reservoir size. +func (s *UniformSample) Size() int { + s.mutex.Lock() + defer s.mutex.Unlock() + return len(s.values) +} + +// Snapshot returns a read-only copy of the sample. +func (s *UniformSample) Snapshot() Sample { + s.mutex.Lock() + defer s.mutex.Unlock() + values := make([]int64, len(s.values)) + copy(values, s.values) + return &SampleSnapshot{ + count: s.count, + values: values, + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (s *UniformSample) StdDev() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleStdDev(s.values) +} + +// Sum returns the sum of the values in the sample. +func (s *UniformSample) Sum() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleSum(s.values) +} + +// Update samples a new value. +func (s *UniformSample) Update(v int64) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count++ + if len(s.values) < s.reservoirSize { + s.values = append(s.values, v) + } else { + r := rand.Int63n(s.count) + if r < int64(len(s.values)) { + s.values[int(r)] = v + } + } +} + +// Values returns a copy of the values in the sample. +func (s *UniformSample) Values() []int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + values := make([]int64, len(s.values)) + copy(values, s.values) + return values +} + +// Variance returns the variance of the values in the sample. +func (s *UniformSample) Variance() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleVariance(s.values) +} + +// expDecaySample represents an individual sample in a heap. +type expDecaySample struct { + k float64 + v int64 +} + +func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap { + return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)} +} + +// expDecaySampleHeap is a min-heap of expDecaySamples. +// The internal implementation is copied from the standard library's container/heap +type expDecaySampleHeap struct { + s []expDecaySample +} + +func (h *expDecaySampleHeap) Clear() { + h.s = h.s[:0] +} + +func (h *expDecaySampleHeap) Push(s expDecaySample) { + n := len(h.s) + h.s = h.s[0 : n+1] + h.s[n] = s + h.up(n) +} + +func (h *expDecaySampleHeap) Pop() expDecaySample { + n := len(h.s) - 1 + h.s[0], h.s[n] = h.s[n], h.s[0] + h.down(0, n) + + n = len(h.s) + s := h.s[n-1] + h.s = h.s[0 : n-1] + return s +} + +func (h *expDecaySampleHeap) Size() int { + return len(h.s) +} + +func (h *expDecaySampleHeap) Values() []expDecaySample { + return h.s +} + +func (h *expDecaySampleHeap) up(j int) { + for { + i := (j - 1) / 2 // parent + if i == j || !(h.s[j].k < h.s[i].k) { + break + } + h.s[i], h.s[j] = h.s[j], h.s[i] + j = i + } +} + +func (h *expDecaySampleHeap) down(i, n int) { + for { + j1 := 2*i + 1 + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow + break + } + j := j1 // left child + if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) { + j = j2 // = 2*i + 2 // right child + } + if !(h.s[j].k < h.s[i].k) { + break + } + h.s[i], h.s[j] = h.s[j], h.s[i] + i = j + } +} + +type int64Slice []int64 + +func (p int64Slice) Len() int { return len(p) } +func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/syslog.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/syslog.go new file mode 100644 index 0000000..693f190 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/syslog.go @@ -0,0 +1,78 @@ +// +build !windows + +package metrics + +import ( + "fmt" + "log/syslog" + "time" +) + +// Output each metric in the given registry to syslog periodically using +// the given syslogger. +func Syslog(r Registry, d time.Duration, w *syslog.Writer) { + for _ = range time.Tick(d) { + r.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) + case Gauge: + w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) + case GaugeFloat64: + w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) + case Healthcheck: + metric.Check() + w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + w.Info(fmt.Sprintf( + "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f", + name, + h.Count(), + h.Min(), + h.Max(), + h.Mean(), + h.StdDev(), + ps[0], + ps[1], + ps[2], + ps[3], + ps[4], + )) + case Meter: + m := metric.Snapshot() + w.Info(fmt.Sprintf( + "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", + name, + m.Count(), + m.Rate1(), + m.Rate5(), + m.Rate15(), + m.RateMean(), + )) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + w.Info(fmt.Sprintf( + "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f", + name, + t.Count(), + t.Min(), + t.Max(), + t.Mean(), + t.StdDev(), + ps[0], + ps[1], + ps[2], + ps[3], + ps[4], + t.Rate1(), + t.Rate5(), + t.Rate15(), + t.RateMean(), + )) + } + }) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/timer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/timer.go new file mode 100644 index 0000000..17db8f8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/timer.go @@ -0,0 +1,311 @@ +package metrics + +import ( + "sync" + "time" +) + +// Timers capture the duration and rate of events. +type Timer interface { + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Rate1() float64 + Rate5() float64 + Rate15() float64 + RateMean() float64 + Snapshot() Timer + StdDev() float64 + Sum() int64 + Time(func()) + Update(time.Duration) + UpdateSince(time.Time) + Variance() float64 +} + +// GetOrRegisterTimer returns an existing Timer or constructs and registers a +// new StandardTimer. +func GetOrRegisterTimer(name string, r Registry) Timer { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewTimer).(Timer) +} + +// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +func NewCustomTimer(h Histogram, m Meter) Timer { + if UseNilMetrics { + return NilTimer{} + } + return &StandardTimer{ + histogram: h, + meter: m, + } +} + +// NewRegisteredTimer constructs and registers a new StandardTimer. +func NewRegisteredTimer(name string, r Registry) Timer { + c := NewTimer() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewTimer constructs a new StandardTimer using an exponentially-decaying +// sample with the same reservoir size and alpha as UNIX load averages. +func NewTimer() Timer { + if UseNilMetrics { + return NilTimer{} + } + return &StandardTimer{ + histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), + meter: NewMeter(), + } +} + +// NilTimer is a no-op Timer. +type NilTimer struct { + h Histogram + m Meter +} + +// Count is a no-op. +func (NilTimer) Count() int64 { return 0 } + +// Max is a no-op. +func (NilTimer) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilTimer) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilTimer) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilTimer) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilTimer) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Rate1 is a no-op. +func (NilTimer) Rate1() float64 { return 0.0 } + +// Rate5 is a no-op. +func (NilTimer) Rate5() float64 { return 0.0 } + +// Rate15 is a no-op. +func (NilTimer) Rate15() float64 { return 0.0 } + +// RateMean is a no-op. +func (NilTimer) RateMean() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilTimer) Snapshot() Timer { return NilTimer{} } + +// StdDev is a no-op. +func (NilTimer) StdDev() float64 { return 0.0 } + +// Sum is a no-op. +func (NilTimer) Sum() int64 { return 0 } + +// Time is a no-op. +func (NilTimer) Time(func()) {} + +// Update is a no-op. +func (NilTimer) Update(time.Duration) {} + +// UpdateSince is a no-op. +func (NilTimer) UpdateSince(time.Time) {} + +// Variance is a no-op. +func (NilTimer) Variance() float64 { return 0.0 } + +// StandardTimer is the standard implementation of a Timer and uses a Histogram +// and Meter. +type StandardTimer struct { + histogram Histogram + meter Meter + mutex sync.Mutex +} + +// Count returns the number of events recorded. +func (t *StandardTimer) Count() int64 { + return t.histogram.Count() +} + +// Max returns the maximum value in the sample. +func (t *StandardTimer) Max() int64 { + return t.histogram.Max() +} + +// Mean returns the mean of the values in the sample. +func (t *StandardTimer) Mean() float64 { + return t.histogram.Mean() +} + +// Min returns the minimum value in the sample. +func (t *StandardTimer) Min() int64 { + return t.histogram.Min() +} + +// Percentile returns an arbitrary percentile of the values in the sample. +func (t *StandardTimer) Percentile(p float64) float64 { + return t.histogram.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. +func (t *StandardTimer) Percentiles(ps []float64) []float64 { + return t.histogram.Percentiles(ps) +} + +// Rate1 returns the one-minute moving average rate of events per second. +func (t *StandardTimer) Rate1() float64 { + return t.meter.Rate1() +} + +// Rate5 returns the five-minute moving average rate of events per second. +func (t *StandardTimer) Rate5() float64 { + return t.meter.Rate5() +} + +// Rate15 returns the fifteen-minute moving average rate of events per second. +func (t *StandardTimer) Rate15() float64 { + return t.meter.Rate15() +} + +// RateMean returns the meter's mean rate of events per second. +func (t *StandardTimer) RateMean() float64 { + return t.meter.RateMean() +} + +// Snapshot returns a read-only copy of the timer. +func (t *StandardTimer) Snapshot() Timer { + t.mutex.Lock() + defer t.mutex.Unlock() + return &TimerSnapshot{ + histogram: t.histogram.Snapshot().(*HistogramSnapshot), + meter: t.meter.Snapshot().(*MeterSnapshot), + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (t *StandardTimer) StdDev() float64 { + return t.histogram.StdDev() +} + +// Sum returns the sum in the sample. +func (t *StandardTimer) Sum() int64 { + return t.histogram.Sum() +} + +// Record the duration of the execution of the given function. +func (t *StandardTimer) Time(f func()) { + ts := time.Now() + f() + t.Update(time.Since(ts)) +} + +// Record the duration of an event. +func (t *StandardTimer) Update(d time.Duration) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.histogram.Update(int64(d)) + t.meter.Mark(1) +} + +// Record the duration of an event that started at a time and ends now. +func (t *StandardTimer) UpdateSince(ts time.Time) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.histogram.Update(int64(time.Since(ts))) + t.meter.Mark(1) +} + +// Variance returns the variance of the values in the sample. +func (t *StandardTimer) Variance() float64 { + return t.histogram.Variance() +} + +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { + histogram *HistogramSnapshot + meter *MeterSnapshot +} + +// Count returns the number of events recorded at the time the snapshot was +// taken. +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } + +// Max returns the maximum value at the time the snapshot was taken. +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } + +// Mean returns the mean value at the time the snapshot was taken. +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } + +// Min returns the minimum value at the time the snapshot was taken. +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } + +// Percentile returns an arbitrary percentile of sampled values at the time the +// snapshot was taken. +func (t *TimerSnapshot) Percentile(p float64) float64 { + return t.histogram.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of sampled values at +// the time the snapshot was taken. +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { + return t.histogram.Percentiles(ps) +} + +// Rate1 returns the one-minute moving average rate of events per second at the +// time the snapshot was taken. +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } + +// Rate5 returns the five-minute moving average rate of events per second at +// the time the snapshot was taken. +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } + +// Rate15 returns the fifteen-minute moving average rate of events per second +// at the time the snapshot was taken. +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } + +// RateMean returns the meter's mean rate of events per second at the time the +// snapshot was taken. +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } + +// Snapshot returns the snapshot. +func (t *TimerSnapshot) Snapshot() Timer { return t } + +// StdDev returns the standard deviation of the values at the time the snapshot +// was taken. +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } + +// Sum returns the sum at the time the snapshot was taken. +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } + +// Time panics. +func (*TimerSnapshot) Time(func()) { + panic("Time called on a TimerSnapshot") +} + +// Update panics. +func (*TimerSnapshot) Update(time.Duration) { + panic("Update called on a TimerSnapshot") +} + +// UpdateSince panics. +func (*TimerSnapshot) UpdateSince(time.Time) { + panic("UpdateSince called on a TimerSnapshot") +} + +// Variance returns the variance of the values at the time the snapshot was +// taken. +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/validate.sh b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/validate.sh new file mode 100755 index 0000000..f649998 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/validate.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +# check there are no formatting issues +GOFMT_LINES=`gofmt -l . | wc -l | xargs` +test $GOFMT_LINES -eq 0 || echo "gofmt needs to be run, ${GOFMT_LINES} files have issues" + +# run the tests for the root package +go test . diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/writer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/writer.go new file mode 100644 index 0000000..091e971 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rcrowley/go-metrics/writer.go @@ -0,0 +1,100 @@ +package metrics + +import ( + "fmt" + "io" + "sort" + "time" +) + +// Write sorts writes each metric in the given registry periodically to the +// given io.Writer. +func Write(r Registry, d time.Duration, w io.Writer) { + for _ = range time.Tick(d) { + WriteOnce(r, w) + } +} + +// WriteOnce sorts and writes metrics in the given registry to the given +// io.Writer. +func WriteOnce(r Registry, w io.Writer) { + var namedMetrics namedMetricSlice + r.Each(func(name string, i interface{}) { + namedMetrics = append(namedMetrics, namedMetric{name, i}) + }) + + sort.Sort(namedMetrics) + for _, namedMetric := range namedMetrics { + switch metric := namedMetric.m.(type) { + case Counter: + fmt.Fprintf(w, "counter %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", metric.Count()) + case Gauge: + fmt.Fprintf(w, "gauge %s\n", namedMetric.name) + fmt.Fprintf(w, " value: %9d\n", metric.Value()) + case GaugeFloat64: + fmt.Fprintf(w, "gauge %s\n", namedMetric.name) + fmt.Fprintf(w, " value: %f\n", metric.Value()) + case Healthcheck: + metric.Check() + fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) + fmt.Fprintf(w, " error: %v\n", metric.Error()) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "histogram %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", h.Count()) + fmt.Fprintf(w, " min: %9d\n", h.Min()) + fmt.Fprintf(w, " max: %9d\n", h.Max()) + fmt.Fprintf(w, " mean: %12.2f\n", h.Mean()) + fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev()) + fmt.Fprintf(w, " median: %12.2f\n", ps[0]) + fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) + fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) + fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) + fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "meter %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", m.Count()) + fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1()) + fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) + fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) + fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "timer %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", t.Count()) + fmt.Fprintf(w, " min: %9d\n", t.Min()) + fmt.Fprintf(w, " max: %9d\n", t.Max()) + fmt.Fprintf(w, " mean: %12.2f\n", t.Mean()) + fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev()) + fmt.Fprintf(w, " median: %12.2f\n", ps[0]) + fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) + fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) + fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) + fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) + fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1()) + fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5()) + fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15()) + fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean()) + } + } +} + +type namedMetric struct { + name string + m interface{} +} + +// namedMetricSlice is a slice of namedMetrics that implements sort.Interface. +type namedMetricSlice []namedMetric + +func (nms namedMetricSlice) Len() int { return len(nms) } + +func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] } + +func (nms namedMetricSlice) Less(i, j int) bool { + return nms[i].name < nms[j].name +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/AUTHORS new file mode 100644 index 0000000..9262eae --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/AUTHORS @@ -0,0 +1,10 @@ +# List of individuals who contributed to the Notify package. +# +# The up-to-date list of the authors one may obtain with: +# +# ~ $ git shortlog -es | cut -f2 | rev | uniq -f1 | rev +# + +Pawel Blaszczyk +Pawel Knap +Rafal Jeczalik diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/LICENSE new file mode 100644 index 0000000..3e67881 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2015 The Notify Authors + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/README.md new file mode 100644 index 0000000..a728d1d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/README.md @@ -0,0 +1,21 @@ +notify [![GoDoc](https://godoc.org/github.com/rjeczalik/notify?status.svg)](https://godoc.org/github.com/rjeczalik/notify) [![Build Status](https://img.shields.io/travis/rjeczalik/notify/master.svg)](https://travis-ci.org/rjeczalik/notify "inotify + FSEvents + kqueue") [![Build status](https://img.shields.io/appveyor/ci/rjeczalik/notify-246.svg)](https://ci.appveyor.com/project/rjeczalik/notify-246 "ReadDirectoryChangesW") [![Coverage Status](https://img.shields.io/coveralls/rjeczalik/notify/master.svg)](https://coveralls.io/r/rjeczalik/notify?branch=master) +====== + +Filesystem event notification library on steroids. (under active development) + +*Documentation* + +[godoc.org/github.com/rjeczalik/notify](https://godoc.org/github.com/rjeczalik/notify) + +*Installation* + +``` +~ $ go get -u github.com/rjeczalik/notify +``` + +*Projects using notify* + +- [github.com/rjeczalik/cmd/notify](https://godoc.org/github.com/rjeczalik/cmd/notify) +- [github.com/cortesi/devd](https://github.com/cortesi/devd) +- [github.com/cortesi/modd](https://github.com/cortesi/modd) + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/appveyor.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/appveyor.yml new file mode 100644 index 0000000..8e762d0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/appveyor.yml @@ -0,0 +1,23 @@ +version: "{build}" + +os: Windows Server 2012 R2 + +clone_folder: c:\projects\src\github.com\rjeczalik\notify + +environment: + PATH: c:\projects\bin;%PATH% + GOPATH: c:\projects + NOTIFY_TIMEOUT: 5s + +install: + - go version + - go get -v -t ./... + +build_script: + - go tool vet -all . + - go build ./... + - go test -v -race ./... + +test: off + +deploy: off diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug.go new file mode 100644 index 0000000..bd9bc46 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug.go @@ -0,0 +1,11 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build !debug + +package notify + +func dbgprint(...interface{}) {} + +func dbgprintf(string, ...interface{}) {} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug_debug.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug_debug.go new file mode 100644 index 0000000..f062291 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/debug_debug.go @@ -0,0 +1,43 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build debug + +package notify + +import ( + "fmt" + "os" + "runtime" + "strings" +) + +func dbgprint(v ...interface{}) { + fmt.Printf("[D] ") + fmt.Print(v...) + fmt.Printf("\n\n") +} + +func dbgprintf(format string, v ...interface{}) { + fmt.Printf("[D] ") + fmt.Printf(format, v...) + fmt.Printf("\n\n") +} + +func dbgcallstack(max int) []string { + pc, stack := make([]uintptr, max), make([]string, 0, max) + runtime.Callers(2, pc) + for _, pc := range pc { + if f := runtime.FuncForPC(pc); f != nil { + fname := f.Name() + idx := strings.LastIndex(fname, string(os.PathSeparator)) + if idx != -1 { + stack = append(stack, fname[idx+1:]) + } else { + stack = append(stack, fname) + } + } + } + return stack +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/doc.go new file mode 100644 index 0000000..8a99ddd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/doc.go @@ -0,0 +1,40 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// Package notify implements access to filesystem events. +// +// Notify is a high-level abstraction over filesystem watchers like inotify, +// kqueue, FSEvents, FEN or ReadDirectoryChangesW. Watcher implementations are +// split into two groups: ones that natively support recursive notifications +// (FSEvents and ReadDirectoryChangesW) and ones that do not (inotify, kqueue, FEN). +// For more details see watcher and recursiveWatcher interfaces in watcher.go +// source file. +// +// On top of filesystem watchers notify maintains a watchpoint tree, which provides +// strategy for creating and closing filesystem watches and dispatching filesystem +// events to user channels. +// +// An event set is just an event list joint using bitwise OR operator +// into a single event value. +// +// A filesystem watch or just a watch is platform-specific entity which represents +// a single path registered for notifications for specific event set. Setting a watch +// means using platform-specific API calls for creating / initializing said watch. +// For each watcher the API call is: +// +// - FSEvents: FSEventStreamCreate +// - inotify: notify_add_watch +// - kqueue: kevent +// - ReadDirectoryChangesW: CreateFile+ReadDirectoryChangesW +// - FEN: port_get +// +// To rewatch means to either shrink or expand an event set that was previously +// registered during watch operation for particular filesystem watch. +// +// A watchpoint is a list of user channel and event set pairs for particular +// path (watchpoint tree's node). A single watchpoint can contain multiple +// different user channels registered to listen for one or more events. A single +// user channel can be registered in one or more watchpoints, recurisve and +// non-recursive ones as well. +package notify diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event.go new file mode 100644 index 0000000..e045edc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event.go @@ -0,0 +1,143 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import ( + "fmt" + "strings" +) + +// Event represents the type of filesystem action. +// +// Number of available event values is dependent on the target system or the +// watcher implmenetation used (e.g. it's possible to use either kqueue or +// FSEvents on Darwin). +// +// Please consult documentation for your target platform to see list of all +// available events. +type Event uint32 + +// Create, Remove, Write and Rename are the only event values guaranteed to be +// present on all platforms. +const ( + Create = osSpecificCreate + Remove = osSpecificRemove + Write = osSpecificWrite + Rename = osSpecificRename + + // All is handful alias for all platform-independent event values. + All = Create | Remove | Write | Rename +) + +const internal = recursive | omit + +// String implements fmt.Stringer interface. +func (e Event) String() string { + var s []string + for _, strmap := range []map[Event]string{estr, osestr} { + for ev, str := range strmap { + if e&ev == ev { + s = append(s, str) + } + } + } + return strings.Join(s, "|") +} + +// EventInfo describes an event reported by the underlying filesystem notification +// subsystem. +// +// It always describes single event, even if the OS reported a coalesced action. +// Reported path is absolute and clean. +// +// For non-recursive watchpoints its base is always equal to the path passed +// to corresponding Watch call. +// +// The value of Sys if system-dependent and can be nil. +// +// Sys +// +// Under Darwin (FSEvents) Sys() always returns a non-nil *notify.FSEvent value, +// which is defined as: +// +// type FSEvent struct { +// Path string // real path of the file or directory +// ID uint64 // ID of the event (FSEventStreamEventId) +// Flags uint32 // joint FSEvents* flags (FSEventStreamEventFlags) +// } +// +// For possible values of Flags see Darwin godoc for notify or FSEvents +// documentation for FSEventStreamEventFlags constants: +// +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html#//apple_ref/doc/constant_group/FSEventStreamEventFlags +// +// Under Linux (inotify) Sys() always returns a non-nil *syscall.InotifyEvent +// value, defined as: +// +// type InotifyEvent struct { +// Wd int32 // Watch descriptor +// Mask uint32 // Mask describing event +// Cookie uint32 // Unique cookie associating related events (for rename(2)) +// Len uint32 // Size of name field +// Name [0]uint8 // Optional null-terminated name +// } +// +// More information about inotify masks and the usage of inotify_event structure +// can be found at: +// +// http://man7.org/linux/man-pages/man7/inotify.7.html +// +// Under Darwin, DragonFlyBSD, FreeBSD, NetBSD, OpenBSD (kqueue) Sys() always +// returns a non-nil *notify.Kevent value, which is defined as: +// +// type Kevent struct { +// Kevent *syscall.Kevent_t // Kevent is a kqueue specific structure +// FI os.FileInfo // FI describes file/dir +// } +// +// More information about syscall.Kevent_t can be found at: +// +// https://www.freebsd.org/cgi/man.cgi?query=kqueue +// +// Under Windows (ReadDirectoryChangesW) Sys() always returns nil. The documentation +// of watcher's WinAPI function can be found at: +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx +type EventInfo interface { + Event() Event // event value for the filesystem action + Path() string // real path of the file or directory + Sys() interface{} // underlying data source (can return nil) +} + +type isDirer interface { + isDir() (bool, error) +} + +var _ fmt.Stringer = (*event)(nil) +var _ isDirer = (*event)(nil) + +// String implements fmt.Stringer interface. +func (e *event) String() string { + return e.Event().String() + `: "` + e.Path() + `"` +} + +var estr = map[Event]string{ + Create: "notify.Create", + Remove: "notify.Remove", + Write: "notify.Write", + Rename: "notify.Rename", + // Display name for recursive event is added only for debugging + // purposes. It's an internal event after all and won't be exposed to the + // user. Having Recursive event printable is helpful, e.g. for reading + // testing failure messages: + // + // --- FAIL: TestWatchpoint (0.00 seconds) + // watchpoint_test.go:64: want diff=[notify.Remove notify.Create|notify.Remove]; + // got [notify.Remove notify.Remove|notify.Create] (i=1) + // + // Yup, here the diff have Recursive event inside. Go figure. + recursive: "recursive", + omit: "omit", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fen.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fen.go new file mode 100644 index 0000000..767f04f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fen.go @@ -0,0 +1,57 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build solaris + +package notify + +const ( + osSpecificCreate Event = 0x00000100 << iota + osSpecificRemove + osSpecificWrite + osSpecificRename + // internal + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit +) + +const ( + // FileAccess is an event reported when monitored file/directory was accessed. + FileAccess = fileAccess + // FileModified is an event reported when monitored file/directory was modified. + FileModified = fileModified + // FileAttrib is an event reported when monitored file/directory's ATTRIB + // was changed. + FileAttrib = fileAttrib + // FileDelete is an event reported when monitored file/directory was deleted. + FileDelete = fileDelete + // FileRenameTo to is an event reported when monitored file/directory was renamed. + FileRenameTo = fileRenameTo + // FileRenameFrom is an event reported when monitored file/directory was renamed. + FileRenameFrom = fileRenameFrom + // FileTrunc is an event reported when monitored file/directory was truncated. + FileTrunc = fileTrunc + // FileNoFollow is an flag to indicate not to follow symbolic links. + FileNoFollow = fileNoFollow + // Unmounted is an event reported when monitored filesystem was unmounted. + Unmounted = unmounted + // MountedOver is an event reported when monitored file/directory was mounted on. + MountedOver = mountedOver +) + +var osestr = map[Event]string{ + FileAccess: "notify.FileAccess", + FileModified: "notify.FileModified", + FileAttrib: "notify.FileAttrib", + FileDelete: "notify.FileDelete", + FileRenameTo: "notify.FileRenameTo", + FileRenameFrom: "notify.FileRenameFrom", + FileTrunc: "notify.FileTrunc", + FileNoFollow: "notify.FileNoFollow", + Unmounted: "notify.Unmounted", + MountedOver: "notify.MountedOver", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fsevents.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fsevents.go new file mode 100644 index 0000000..6ded80b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_fsevents.go @@ -0,0 +1,71 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,!kqueue + +package notify + +const ( + osSpecificCreate = Event(FSEventsCreated) + osSpecificRemove = Event(FSEventsRemoved) + osSpecificWrite = Event(FSEventsModified) + osSpecificRename = Event(FSEventsRenamed) + // internal = Event(0x100000) + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive = Event(0x200000) + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit = Event(0x400000) +) + +// FSEvents specific event values. +const ( + FSEventsMustScanSubDirs Event = 0x00001 + FSEventsUserDropped = 0x00002 + FSEventsKernelDropped = 0x00004 + FSEventsEventIdsWrapped = 0x00008 + FSEventsHistoryDone = 0x00010 + FSEventsRootChanged = 0x00020 + FSEventsMount = 0x00040 + FSEventsUnmount = 0x00080 + FSEventsCreated = 0x00100 + FSEventsRemoved = 0x00200 + FSEventsInodeMetaMod = 0x00400 + FSEventsRenamed = 0x00800 + FSEventsModified = 0x01000 + FSEventsFinderInfoMod = 0x02000 + FSEventsChangeOwner = 0x04000 + FSEventsXattrMod = 0x08000 + FSEventsIsFile = 0x10000 + FSEventsIsDir = 0x20000 + FSEventsIsSymlink = 0x40000 +) + +var osestr = map[Event]string{ + FSEventsMustScanSubDirs: "notify.FSEventsMustScanSubDirs", + FSEventsUserDropped: "notify.FSEventsUserDropped", + FSEventsKernelDropped: "notify.FSEventsKernelDropped", + FSEventsEventIdsWrapped: "notify.FSEventsEventIdsWrapped", + FSEventsHistoryDone: "notify.FSEventsHistoryDone", + FSEventsRootChanged: "notify.FSEventsRootChanged", + FSEventsMount: "notify.FSEventsMount", + FSEventsUnmount: "notify.FSEventsUnmount", + FSEventsInodeMetaMod: "notify.FSEventsInodeMetaMod", + FSEventsFinderInfoMod: "notify.FSEventsFinderInfoMod", + FSEventsChangeOwner: "notify.FSEventsChangeOwner", + FSEventsXattrMod: "notify.FSEventsXattrMod", + FSEventsIsFile: "notify.FSEventsIsFile", + FSEventsIsDir: "notify.FSEventsIsDir", + FSEventsIsSymlink: "notify.FSEventsIsSymlink", +} + +type event struct { + fse FSEvent + event Event +} + +func (ei *event) Event() Event { return ei.event } +func (ei *event) Path() string { return ei.fse.Path } +func (ei *event) Sys() interface{} { return &ei.fse } +func (ei *event) isDir() (bool, error) { return ei.fse.Flags&FSEventsIsDir != 0, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_inotify.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_inotify.go new file mode 100644 index 0000000..82954a9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_inotify.go @@ -0,0 +1,75 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build linux + +package notify + +import "syscall" + +// Platform independent event values. +const ( + osSpecificCreate Event = 0x100000 << iota + osSpecificRemove + osSpecificWrite + osSpecificRename + // internal + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit +) + +// Inotify specific masks are legal, implemented events that are guaranteed to +// work with notify package on linux-based systems. +const ( + InAccess = Event(syscall.IN_ACCESS) // File was accessed + InModify = Event(syscall.IN_MODIFY) // File was modified + InAttrib = Event(syscall.IN_ATTRIB) // Metadata changed + InCloseWrite = Event(syscall.IN_CLOSE_WRITE) // Writtable file was closed + InCloseNowrite = Event(syscall.IN_CLOSE_NOWRITE) // Unwrittable file closed + InOpen = Event(syscall.IN_OPEN) // File was opened + InMovedFrom = Event(syscall.IN_MOVED_FROM) // File was moved from X + InMovedTo = Event(syscall.IN_MOVED_TO) // File was moved to Y + InCreate = Event(syscall.IN_CREATE) // Subfile was created + InDelete = Event(syscall.IN_DELETE) // Subfile was deleted + InDeleteSelf = Event(syscall.IN_DELETE_SELF) // Self was deleted + InMoveSelf = Event(syscall.IN_MOVE_SELF) // Self was moved +) + +var osestr = map[Event]string{ + InAccess: "notify.InAccess", + InModify: "notify.InModify", + InAttrib: "notify.InAttrib", + InCloseWrite: "notify.InCloseWrite", + InCloseNowrite: "notify.InCloseNowrite", + InOpen: "notify.InOpen", + InMovedFrom: "notify.InMovedFrom", + InMovedTo: "notify.InMovedTo", + InCreate: "notify.InCreate", + InDelete: "notify.InDelete", + InDeleteSelf: "notify.InDeleteSelf", + InMoveSelf: "notify.InMoveSelf", +} + +// Inotify behavior events are not **currently** supported by notify package. +const ( + inDontFollow = Event(syscall.IN_DONT_FOLLOW) + inExclUnlink = Event(syscall.IN_EXCL_UNLINK) + inMaskAdd = Event(syscall.IN_MASK_ADD) + inOneshot = Event(syscall.IN_ONESHOT) + inOnlydir = Event(syscall.IN_ONLYDIR) +) + +type event struct { + sys syscall.InotifyEvent + path string + event Event +} + +func (e *event) Event() Event { return e.event } +func (e *event) Path() string { return e.path } +func (e *event) Sys() interface{} { return &e.sys } +func (e *event) isDir() (bool, error) { return e.sys.Mask&syscall.IN_ISDIR != 0, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_kqueue.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_kqueue.go new file mode 100644 index 0000000..422ac87 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_kqueue.go @@ -0,0 +1,59 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,kqueue dragonfly freebsd netbsd openbsd + +package notify + +import "syscall" + +// TODO(pblaszczyk): ensure in runtime notify built-in event values do not +// overlap with platform-defined ones. + +// Platform independent event values. +const ( + osSpecificCreate Event = 0x0100 << iota + osSpecificRemove + osSpecificWrite + osSpecificRename + // internal + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit +) + +const ( + // NoteDelete is an event reported when the unlink() system call was called + // on the file referenced by the descriptor. + NoteDelete = Event(syscall.NOTE_DELETE) + // NoteWrite is an event reported when a write occurred on the file + // referenced by the descriptor. + NoteWrite = Event(syscall.NOTE_WRITE) + // NoteExtend is an event reported when the file referenced by the + // descriptor was extended. + NoteExtend = Event(syscall.NOTE_EXTEND) + // NoteAttrib is an event reported when the file referenced + // by the descriptor had its attributes changed. + NoteAttrib = Event(syscall.NOTE_ATTRIB) + // NoteLink is an event reported when the link count on the file changed. + NoteLink = Event(syscall.NOTE_LINK) + // NoteRename is an event reported when the file referenced + // by the descriptor was renamed. + NoteRename = Event(syscall.NOTE_RENAME) + // NoteRevoke is an event reported when access to the file was revoked via + // revoke(2) or the underlying file system was unmounted. + NoteRevoke = Event(syscall.NOTE_REVOKE) +) + +var osestr = map[Event]string{ + NoteDelete: "notify.NoteDelete", + NoteWrite: "notify.NoteWrite", + NoteExtend: "notify.NoteExtend", + NoteAttrib: "notify.NoteAttrib", + NoteLink: "notify.NoteLink", + NoteRename: "notify.NoteRename", + NoteRevoke: "notify.NoteRevoke", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_readdcw.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_readdcw.go new file mode 100644 index 0000000..11ead9e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_readdcw.go @@ -0,0 +1,108 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build windows + +package notify + +import ( + "os" + "path/filepath" + "syscall" +) + +// Platform independent event values. +const ( + osSpecificCreate Event = 1 << (20 + iota) + osSpecificRemove + osSpecificWrite + osSpecificRename + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit + // dirmarker TODO(pknap) + dirmarker +) + +// ReadDirectoryChangesW filters. +const ( + FileNotifyChangeFileName = Event(syscall.FILE_NOTIFY_CHANGE_FILE_NAME) + FileNotifyChangeDirName = Event(syscall.FILE_NOTIFY_CHANGE_DIR_NAME) + FileNotifyChangeAttributes = Event(syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES) + FileNotifyChangeSize = Event(syscall.FILE_NOTIFY_CHANGE_SIZE) + FileNotifyChangeLastWrite = Event(syscall.FILE_NOTIFY_CHANGE_LAST_WRITE) + FileNotifyChangeLastAccess = Event(syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS) + FileNotifyChangeCreation = Event(syscall.FILE_NOTIFY_CHANGE_CREATION) + FileNotifyChangeSecurity = Event(syscallFileNotifyChangeSecurity) +) + +const ( + fileNotifyChangeAll = 0x17f // logical sum of all FileNotifyChange* events. + fileNotifyChangeModified = fileNotifyChangeAll &^ (FileNotifyChangeFileName | FileNotifyChangeDirName) +) + +// according to: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx +// this flag should be declared in: http://golang.org/src/pkg/syscall/ztypes_windows.go +const syscallFileNotifyChangeSecurity = 0x00000100 + +// ReadDirectoryChangesW actions. +const ( + FileActionAdded = Event(syscall.FILE_ACTION_ADDED) << 12 + FileActionRemoved = Event(syscall.FILE_ACTION_REMOVED) << 12 + FileActionModified = Event(syscall.FILE_ACTION_MODIFIED) << 14 + FileActionRenamedOldName = Event(syscall.FILE_ACTION_RENAMED_OLD_NAME) << 15 + FileActionRenamedNewName = Event(syscall.FILE_ACTION_RENAMED_NEW_NAME) << 16 +) + +const fileActionAll = 0x7f000 // logical sum of all FileAction* events. + +var osestr = map[Event]string{ + FileNotifyChangeFileName: "notify.FileNotifyChangeFileName", + FileNotifyChangeDirName: "notify.FileNotifyChangeDirName", + FileNotifyChangeAttributes: "notify.FileNotifyChangeAttributes", + FileNotifyChangeSize: "notify.FileNotifyChangeSize", + FileNotifyChangeLastWrite: "notify.FileNotifyChangeLastWrite", + FileNotifyChangeLastAccess: "notify.FileNotifyChangeLastAccess", + FileNotifyChangeCreation: "notify.FileNotifyChangeCreation", + FileNotifyChangeSecurity: "notify.FileNotifyChangeSecurity", + + FileActionAdded: "notify.FileActionAdded", + FileActionRemoved: "notify.FileActionRemoved", + FileActionModified: "notify.FileActionModified", + FileActionRenamedOldName: "notify.FileActionRenamedOldName", + FileActionRenamedNewName: "notify.FileActionRenamedNewName", +} + +const ( + fTypeUnknown uint8 = iota + fTypeFile + fTypeDirectory +) + +// TODO(ppknap) : doc. +type event struct { + pathw []uint16 + name string + ftype uint8 + action uint32 + filter uint32 + e Event +} + +func (e *event) Event() Event { return e.e } +func (e *event) Path() string { return filepath.Join(syscall.UTF16ToString(e.pathw), e.name) } +func (e *event) Sys() interface{} { return e.ftype } + +func (e *event) isDir() (bool, error) { + if e.ftype != fTypeUnknown { + return e.ftype == fTypeDirectory, nil + } + fi, err := os.Stat(e.Path()) + if err != nil { + return false, err + } + return fi.IsDir(), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_stub.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_stub.go new file mode 100644 index 0000000..faac7c7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_stub.go @@ -0,0 +1,31 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build !darwin,!linux,!freebsd,!dragonfly,!netbsd,!openbsd,!windows +// +build !kqueue,!solaris + +package notify + +// Platform independent event values. +const ( + osSpecificCreate Event = 1 << iota + osSpecificRemove + osSpecificWrite + osSpecificRename + // internal + // recursive is used to distinguish recursive eventsets from non-recursive ones + recursive + // omit is used for dispatching internal events; only those events are sent + // for which both the event and the watchpoint has omit in theirs event sets. + omit +) + +var osestr = map[Event]string{} + +type event struct{} + +func (e *event) Event() (_ Event) { return } +func (e *event) Path() (_ string) { return } +func (e *event) Sys() (_ interface{}) { return } +func (e *event) isDir() (_ bool, _ error) { return } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_trigger.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_trigger.go new file mode 100644 index 0000000..94470fd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/event_trigger.go @@ -0,0 +1,22 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,kqueue dragonfly freebsd netbsd openbsd solaris + +package notify + +type event struct { + p string + e Event + d bool + pe interface{} +} + +func (e *event) Event() Event { return e.e } + +func (e *event) Path() string { return e.p } + +func (e *event) Sys() interface{} { return e.pe } + +func (e *event) isDir() (bool, error) { return e.d, nil } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/node.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/node.go new file mode 100644 index 0000000..29c1bb2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/node.go @@ -0,0 +1,272 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +var errSkip = errors.New("notify: skip") + +type walkPathFunc func(nd node, isbase bool) error + +type walkFunc func(node) error + +func errnotexist(name string) error { + return &os.PathError{ + Op: "Node", + Path: name, + Err: os.ErrNotExist, + } +} + +type node struct { + Name string + Watch watchpoint + Child map[string]node +} + +func newnode(name string) node { + return node{ + Name: name, + Watch: make(watchpoint), + Child: make(map[string]node), + } +} + +func (nd node) addchild(name, base string) node { + child, ok := nd.Child[base] + if !ok { + child = newnode(name) + nd.Child[base] = child + } + return child +} + +func (nd node) Add(name string) node { + i := indexbase(nd.Name, name) + if i == -1 { + return node{} + } + for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) { + nd = nd.addchild(name[:i+j], name[i:i+j]) + i += j + 1 + } + return nd.addchild(name, name[i:]) +} + +func (nd node) AddDir(fn walkFunc) error { + stack := []node{nd} +Traverse: + for n := len(stack); n != 0; n = len(stack) { + nd, stack = stack[n-1], stack[:n-1] + switch err := fn(nd); err { + case nil: + case errSkip: + continue Traverse + default: + return fmt.Errorf("error while traversing %q: %v", nd.Name, err) + } + // TODO(rjeczalik): tolerate open failures - add failed names to + // AddDirError and notify users which names are not added to the tree. + fi, err := ioutil.ReadDir(nd.Name) + if err != nil { + return err + } + for _, fi := range fi { + if fi.Mode()&(os.ModeSymlink|os.ModeDir) == os.ModeDir { + name := filepath.Join(nd.Name, fi.Name()) + stack = append(stack, nd.addchild(name, name[len(nd.Name)+1:])) + } + } + } + return nil +} + +func (nd node) Get(name string) (node, error) { + i := indexbase(nd.Name, name) + if i == -1 { + return node{}, errnotexist(name) + } + ok := false + for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) { + if nd, ok = nd.Child[name[i:i+j]]; !ok { + return node{}, errnotexist(name) + } + i += j + 1 + } + if nd, ok = nd.Child[name[i:]]; !ok { + return node{}, errnotexist(name) + } + return nd, nil +} + +func (nd node) Del(name string) error { + i := indexbase(nd.Name, name) + if i == -1 { + return errnotexist(name) + } + stack := []node{nd} + ok := false + for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) { + if nd, ok = nd.Child[name[i:i+j]]; !ok { + return errnotexist(name[:i+j]) + } + stack = append(stack, nd) + } + if nd, ok = nd.Child[name[i:]]; !ok { + return errnotexist(name) + } + nd.Child = nil + nd.Watch = nil + for name, i = base(nd.Name), len(stack); i != 0; name, i = base(nd.Name), i-1 { + nd = stack[i-1] + if nd := nd.Child[name]; len(nd.Watch) > 1 || len(nd.Child) != 0 { + break + } else { + nd.Child = nil + nd.Watch = nil + } + delete(nd.Child, name) + } + return nil +} + +func (nd node) Walk(fn walkFunc) error { + stack := []node{nd} +Traverse: + for n := len(stack); n != 0; n = len(stack) { + nd, stack = stack[n-1], stack[:n-1] + switch err := fn(nd); err { + case nil: + case errSkip: + continue Traverse + default: + return err + } + for name, nd := range nd.Child { + if name == "" { + // Node storing inactive watchpoints has empty name, skip it + // form traversing. Root node has also an empty name, but it + // never has a parent node. + continue + } + stack = append(stack, nd) + } + } + return nil +} + +func (nd node) WalkPath(name string, fn walkPathFunc) error { + i := indexbase(nd.Name, name) + if i == -1 { + return errnotexist(name) + } + ok := false + for j := indexSep(name[i:]); j != -1; j = indexSep(name[i:]) { + switch err := fn(nd, false); err { + case nil: + case errSkip: + return nil + default: + return err + } + if nd, ok = nd.Child[name[i:i+j]]; !ok { + return errnotexist(name[:i+j]) + } + i += j + 1 + } + switch err := fn(nd, false); err { + case nil: + case errSkip: + return nil + default: + return err + } + if nd, ok = nd.Child[name[i:]]; !ok { + return errnotexist(name) + } + switch err := fn(nd, true); err { + case nil, errSkip: + return nil + default: + return err + } +} + +type root struct { + nd node +} + +func (r root) addroot(name string) node { + if vol := filepath.VolumeName(name); vol != "" { + root, ok := r.nd.Child[vol] + if !ok { + root = r.nd.addchild(vol, vol) + } + return root + } + return r.nd +} + +func (r root) root(name string) (node, error) { + if vol := filepath.VolumeName(name); vol != "" { + nd, ok := r.nd.Child[vol] + if !ok { + return node{}, errnotexist(name) + } + return nd, nil + } + return r.nd, nil +} + +func (r root) Add(name string) node { + return r.addroot(name).Add(name) +} + +func (r root) AddDir(dir string, fn walkFunc) error { + return r.Add(dir).AddDir(fn) +} + +func (r root) Del(name string) error { + nd, err := r.root(name) + if err != nil { + return err + } + return nd.Del(name) +} + +func (r root) Get(name string) (node, error) { + nd, err := r.root(name) + if err != nil { + return node{}, err + } + if nd.Name != name { + if nd, err = nd.Get(name); err != nil { + return node{}, err + } + } + return nd, nil +} + +func (r root) Walk(name string, fn walkFunc) error { + nd, err := r.Get(name) + if err != nil { + return err + } + return nd.Walk(fn) +} + +func (r root) WalkPath(name string, fn walkPathFunc) error { + nd, err := r.root(name) + if err != nil { + return err + } + return nd.WalkPath(name, fn) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/notify.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/notify.go new file mode 100644 index 0000000..7d2eec3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/notify.go @@ -0,0 +1,74 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// BUG(rjeczalik): Notify does not collect watchpoints, when underlying watches +// were removed by their os-specific watcher implementations. Instead users are +// advised to listen on persistent paths to have guarantee they receive events +// for the whole lifetime of their applications (to discuss see #69). + +// BUG(ppknap): Linux (inotify) does not support watcher behavior masks like +// InOneshot, InOnlydir etc. Instead users are advised to perform the filtering +// themselves (to discuss see #71). + +// BUG(ppknap): Notify was not tested for short path name support under Windows +// (ReadDirectoryChangesW). + +// BUG(ppknap): Windows (ReadDirectoryChangesW) cannot recognize which notification +// triggers FileActionModified event. (to discuss see #75). + +package notify + +var defaultTree = newTree() + +// Watch sets up a watchpoint on path listening for events given by the events +// argument. +// +// File or directory given by the path must exist, otherwise Watch will fail +// with non-nil error. Notify resolves, for its internal purpose, any symlinks +// the provided path may contain, so it may fail if the symlinks form a cycle. +// It does so, since not all watcher implementations treat passed paths as-is. +// E.g. FSEvents reports a real path for every event, setting a watchpoint +// on /tmp will report events with paths rooted at /private/tmp etc. +// +// The c almost always is a buffered channel. Watch will not block sending to c +// - the caller must ensure that c has sufficient buffer space to keep up with +// the expected event rate. +// +// It is allowed to pass the same channel multiple times with different event +// list or different paths. Calling Watch with different event lists for a single +// watchpoint expands its event set. The only way to shrink it, is to call +// Stop on its channel. +// +// Calling Watch with empty event list does expand nor shrink watchpoint's event +// set. If c is the first channel to listen for events on the given path, Watch +// will seamlessly create a watch on the filesystem. +// +// Notify dispatches copies of single filesystem event to all channels registered +// for each path. If a single filesystem event contains multiple coalesced events, +// each of them is dispatched separately. E.g. the following filesystem change: +// +// ~ $ echo Hello > Notify.txt +// +// dispatches two events - notify.Create and notify.Write. However, it may depend +// on the underlying watcher implementation whether OS reports both of them. +// +// Windows and recursive watches +// +// If a directory which path was used to create recursive watch under Windows +// gets deleted, the OS will not report such event. It is advised to keep in +// mind this limitation while setting recursive watchpoints for your application, +// e.g. use persistent paths like %userprofile% or watch additionally parent +// directory of a recursive watchpoint in order to receive delete events for it. +func Watch(path string, c chan<- EventInfo, events ...Event) error { + return defaultTree.Watch(path, c, events...) +} + +// Stop removes all watchpoints registered for c. All underlying watches are +// also removed, for which c was the last channel listening for events. +// +// Stop does not close c. When Stop returns, it is guaranteed that c will +// receive no more signals. +func Stop(c chan<- EventInfo) { + defaultTree.Stop(c) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree.go new file mode 100644 index 0000000..cd6afd6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree.go @@ -0,0 +1,22 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +const buffer = 128 + +type tree interface { + Watch(string, chan<- EventInfo, ...Event) error + Stop(chan<- EventInfo) + Close() error +} + +func newTree() tree { + c := make(chan EventInfo, buffer) + w := newWatcher(c) + if rw, ok := w.(recursiveWatcher); ok { + return newRecursiveTree(rw, c) + } + return newNonrecursiveTree(w, c, make(chan EventInfo, buffer)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_nonrecursive.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_nonrecursive.go new file mode 100644 index 0000000..dfa72d1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_nonrecursive.go @@ -0,0 +1,292 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import "sync" + +// nonrecursiveTree TODO(rjeczalik) +type nonrecursiveTree struct { + rw sync.RWMutex // protects root + root root + w watcher + c chan EventInfo + rec chan EventInfo +} + +// newNonrecursiveTree TODO(rjeczalik) +func newNonrecursiveTree(w watcher, c, rec chan EventInfo) *nonrecursiveTree { + if rec == nil { + rec = make(chan EventInfo, buffer) + } + t := &nonrecursiveTree{ + root: root{nd: newnode("")}, + w: w, + c: c, + rec: rec, + } + go t.dispatch(c) + go t.internal(rec) + return t +} + +// dispatch TODO(rjeczalik) +func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) { + for ei := range c { + dbgprintf("dispatching %v on %q", ei.Event(), ei.Path()) + go func(ei EventInfo) { + var nd node + var isrec bool + dir, base := split(ei.Path()) + fn := func(it node, isbase bool) error { + isrec = isrec || it.Watch.IsRecursive() + if isbase { + nd = it + } else { + it.Watch.Dispatch(ei, recursive) + } + return nil + } + t.rw.RLock() + // Notify recursive watchpoints found on the path. + if err := t.root.WalkPath(dir, fn); err != nil { + dbgprint("dispatch did not reach leaf:", err) + t.rw.RUnlock() + return + } + // Notify parent watchpoint. + nd.Watch.Dispatch(ei, 0) + isrec = isrec || nd.Watch.IsRecursive() + // If leaf watchpoint exists, notify it. + if nd, ok := nd.Child[base]; ok { + isrec = isrec || nd.Watch.IsRecursive() + nd.Watch.Dispatch(ei, 0) + } + t.rw.RUnlock() + // If the event describes newly leaf directory created within + if !isrec || ei.Event() != Create { + return + } + if ok, err := ei.(isDirer).isDir(); !ok || err != nil { + return + } + t.rec <- ei + }(ei) + } +} + +// internal TODO(rjeczalik) +func (t *nonrecursiveTree) internal(rec <-chan EventInfo) { + for ei := range rec { + var nd node + var eset = internal + t.rw.Lock() + t.root.WalkPath(ei.Path(), func(it node, _ bool) error { + if e := it.Watch[t.rec]; e != 0 && e > eset { + eset = e + } + nd = it + return nil + }) + if eset == internal { + t.rw.Unlock() + continue + } + err := nd.Add(ei.Path()).AddDir(t.recFunc(eset)) + t.rw.Unlock() + if err != nil { + dbgprintf("internal(%p) error: %v", rec, err) + } + } +} + +// watchAdd TODO(rjeczalik) +func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff { + if e&recursive != 0 { + diff := nd.Watch.Add(t.rec, e|Create|omit) + nd.Watch.Add(c, e) + return diff + } + return nd.Watch.Add(c, e) +} + +// watchDelMin TODO(rjeczalik) +func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff { + old, ok := nd.Watch[t.rec] + if ok { + nd.Watch[t.rec] = min + } + diff := nd.Watch.Del(c, e) + if ok { + switch old &^= diff[0] &^ diff[1]; { + case old|internal == internal: + delete(nd.Watch, t.rec) + if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 { + delete(nd.Watch, nil) + } + default: + nd.Watch.Add(t.rec, old|Create) + switch { + case diff == none: + case diff[1]|Create == diff[0]: + diff = none + default: + diff[1] |= Create + } + } + } + return diff +} + +// watchDel TODO(rjeczalik) +func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff { + return t.watchDelMin(0, nd, c, e) +} + +// Watch TODO(rjeczalik) +func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error { + if c == nil { + panic("notify: Watch using nil channel") + } + // Expanding with empty event set is a nop. + if len(events) == 0 { + return nil + } + path, isrec, err := cleanpath(path) + if err != nil { + return err + } + eset := joinevents(events) + t.rw.Lock() + defer t.rw.Unlock() + nd := t.root.Add(path) + if isrec { + return t.watchrec(nd, c, eset|recursive) + } + return t.watch(nd, c, eset) +} + +func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) { + diff := nd.Watch.Add(c, e) + switch { + case diff == none: + return nil + case diff[1] == 0: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("eset is empty: " + nd.Name) + case diff[0] == 0: + err = t.w.Watch(nd.Name, diff[1]) + default: + err = t.w.Rewatch(nd.Name, diff[0], diff[1]) + } + if err != nil { + nd.Watch.Del(c, diff.Event()) + return err + } + return nil +} + +func (t *nonrecursiveTree) recFunc(e Event) walkFunc { + return func(nd node) error { + switch diff := nd.Watch.Add(t.rec, e|omit|Create); { + case diff == none: + case diff[1] == 0: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("eset is empty: " + nd.Name) + case diff[0] == 0: + t.w.Watch(nd.Name, diff[1]) + default: + t.w.Rewatch(nd.Name, diff[0], diff[1]) + } + return nil + } +} + +func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event) error { + var traverse func(walkFunc) error + // Non-recursive tree listens on Create event for every recursive + // watchpoint in order to automagically set a watch for every + // created directory. + switch diff := nd.Watch.dryAdd(t.rec, e|Create); { + case diff == none: + t.watchAdd(nd, c, e) + nd.Watch.Add(t.rec, e|omit|Create) + return nil + case diff[1] == 0: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("eset is empty: " + nd.Name) + case diff[0] == 0: + // TODO(rjeczalik): BFS into directories and skip subtree as soon as first + // recursive watchpoint is encountered. + traverse = nd.AddDir + default: + traverse = nd.Walk + } + // TODO(rjeczalik): account every path that failed to be (re)watched + // and retry. + if err := traverse(t.recFunc(e)); err != nil { + return err + } + t.watchAdd(nd, c, e) + return nil +} + +type walkWatchpointFunc func(Event, node) error + +func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error { + type minode struct { + min Event + nd node + } + mnd := minode{nd: nd} + stack := []minode{mnd} +Traverse: + for n := len(stack); n != 0; n = len(stack) { + mnd, stack = stack[n-1], stack[:n-1] + // There must be no recursive watchpoints if the node has no watchpoints + // itself (every node in subtree rooted at recursive watchpoints must + // have at least nil (total) and t.rec watchpoints). + if len(mnd.nd.Watch) != 0 { + switch err := fn(mnd.min, mnd.nd); err { + case nil: + case errSkip: + continue Traverse + default: + return err + } + } + for _, nd := range mnd.nd.Child { + stack = append(stack, minode{mnd.nd.Watch[t.rec], nd}) + } + } + return nil +} + +// Stop TODO(rjeczalik) +func (t *nonrecursiveTree) Stop(c chan<- EventInfo) { + fn := func(min Event, nd node) error { + // TODO(rjeczalik): aggregate watcher errors and retry; in worst case + // forward to the user. + switch diff := t.watchDelMin(min, nd, c, all); { + case diff == none: + return nil + case diff[1] == 0: + t.w.Unwatch(nd.Name) + default: + t.w.Rewatch(nd.Name, diff[0], diff[1]) + } + return nil + } + t.rw.Lock() + err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c + t.rw.Unlock() + dbgprintf("Stop(%p) error: %v\n", c, err) +} + +// Close TODO(rjeczalik) +func (t *nonrecursiveTree) Close() error { + err := t.w.Close() + close(t.c) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_recursive.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_recursive.go new file mode 100644 index 0000000..7f00dfe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/tree_recursive.go @@ -0,0 +1,354 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import "sync" + +// watchAdd TODO(rjeczalik) +func watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff { + diff := nd.Watch.Add(c, e) + if wp := nd.Child[""].Watch; len(wp) != 0 { + e = wp.Total() + diff[0] |= e + diff[1] |= e + if diff[0] == diff[1] { + return none + } + } + return diff +} + +// watchAddInactive TODO(rjeczalik) +func watchAddInactive(nd node, c chan<- EventInfo, e Event) eventDiff { + wp := nd.Child[""].Watch + if wp == nil { + wp = make(watchpoint) + nd.Child[""] = node{Watch: wp} + } + diff := wp.Add(c, e) + e = nd.Watch.Total() + diff[0] |= e + diff[1] |= e + if diff[0] == diff[1] { + return none + } + return diff +} + +// watchCopy TODO(rjeczalik) +func watchCopy(src, dst node) { + for c, e := range src.Watch { + if c == nil { + continue + } + watchAddInactive(dst, c, e) + } + if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 { + wpdst := dst.Child[""].Watch + for c, e := range wpsrc { + if c == nil { + continue + } + wpdst.Add(c, e) + } + } +} + +// watchDel TODO(rjeczalik) +func watchDel(nd node, c chan<- EventInfo, e Event) eventDiff { + diff := nd.Watch.Del(c, e) + if wp := nd.Child[""].Watch; len(wp) != 0 { + diffInactive := wp.Del(c, e) + e = wp.Total() + // TODO(rjeczalik): add e if e != all? + diff[0] |= diffInactive[0] | e + diff[1] |= diffInactive[1] | e + if diff[0] == diff[1] { + return none + } + } + return diff +} + +// watchTotal TODO(rjeczalik) +func watchTotal(nd node) Event { + e := nd.Watch.Total() + if wp := nd.Child[""].Watch; len(wp) != 0 { + e |= wp.Total() + } + return e +} + +// watchIsRecursive TODO(rjeczalik) +func watchIsRecursive(nd node) bool { + ok := nd.Watch.IsRecursive() + // TODO(rjeczalik): add a test for len(wp) != 0 change the condition. + if wp := nd.Child[""].Watch; len(wp) != 0 { + // If a watchpoint holds inactive watchpoints, it means it's a parent + // one, which is recursive by nature even though it may be not recursive + // itself. + ok = true + } + return ok +} + +// recursiveTree TODO(rjeczalik) +type recursiveTree struct { + rw sync.RWMutex // protects root + root root + // TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #6 + w interface { + watcher + recursiveWatcher + } + c chan EventInfo +} + +// newRecursiveTree TODO(rjeczalik) +func newRecursiveTree(w recursiveWatcher, c chan EventInfo) *recursiveTree { + t := &recursiveTree{ + root: root{nd: newnode("")}, + w: struct { + watcher + recursiveWatcher + }{w.(watcher), w}, + c: c, + } + go t.dispatch() + return t +} + +// dispatch TODO(rjeczalik) +func (t *recursiveTree) dispatch() { + for ei := range t.c { + dbgprintf("dispatching %v on %q", ei.Event(), ei.Path()) + go func(ei EventInfo) { + nd, ok := node{}, false + dir, base := split(ei.Path()) + fn := func(it node, isbase bool) error { + if isbase { + nd = it + } else { + it.Watch.Dispatch(ei, recursive) + } + return nil + } + t.rw.RLock() + defer t.rw.RUnlock() + // Notify recursive watchpoints found on the path. + if err := t.root.WalkPath(dir, fn); err != nil { + dbgprint("dispatch did not reach leaf:", err) + return + } + // Notify parent watchpoint. + nd.Watch.Dispatch(ei, 0) + // If leaf watchpoint exists, notify it. + if nd, ok = nd.Child[base]; ok { + nd.Watch.Dispatch(ei, 0) + } + }(ei) + } +} + +// Watch TODO(rjeczalik) +func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error { + if c == nil { + panic("notify: Watch using nil channel") + } + // Expanding with empty event set is a nop. + if len(events) == 0 { + return nil + } + path, isrec, err := cleanpath(path) + if err != nil { + return err + } + eventset := joinevents(events) + if isrec { + eventset |= recursive + } + t.rw.Lock() + defer t.rw.Unlock() + // case 1: cur is a child + // + // Look for parent watch which already covers the given path. + parent := node{} + self := false + err = t.root.WalkPath(path, func(nd node, isbase bool) error { + if watchTotal(nd) != 0 { + parent = nd + self = isbase + return errSkip + } + return nil + }) + cur := t.root.Add(path) // add after the walk, so it's less to traverse + if err == nil && parent.Watch != nil { + // Parent watch found. Register inactive watchpoint, so we have enough + // information to shrink the eventset on eventual Stop. + // return t.resetwatchpoint(parent, parent, c, eventset|inactive) + var diff eventDiff + if self { + diff = watchAdd(cur, c, eventset) + } else { + diff = watchAddInactive(parent, c, eventset) + } + switch { + case diff == none: + // the parent watchpoint already covers requested subtree with its + // eventset + case diff[0] == 0: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("dangling watchpoint: " + parent.Name) + default: + if isrec || watchIsRecursive(parent) { + err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1]) + } else { + err = t.w.Rewatch(parent.Name, diff[0], diff[1]) + } + if err != nil { + watchDel(parent, c, diff.Event()) + return err + } + watchAdd(cur, c, eventset) + // TODO(rjeczalik): account top-most path for c + return nil + } + if !self { + watchAdd(cur, c, eventset) + } + return nil + } + // case 2: cur is new parent + // + // Look for children nodes, unwatch n-1 of them and rewatch the last one. + var children []node + fn := func(nd node) error { + if len(nd.Watch) == 0 { + return nil + } + children = append(children, nd) + return errSkip + } + switch must(cur.Walk(fn)); len(children) { + case 0: + // no child watches, cur holds a new watch + case 1: + watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root? + watchCopy(children[0], cur) + err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]), + watchTotal(cur)) + if err != nil { + // Clean inactive watchpoint. The c chan did not exist before. + cur.Child[""] = node{} + delete(cur.Watch, c) + return err + } + return nil + default: + watchAdd(cur, c, eventset) + // Copy children inactive watchpoints to the new parent. + for _, nd := range children { + watchCopy(nd, cur) + } + // Watch parent subtree. + if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil { + // Clean inactive watchpoint. The c chan did not exist before. + cur.Child[""] = node{} + delete(cur.Watch, c) + return err + } + // Unwatch children subtrees. + var e error + for _, nd := range children { + if watchIsRecursive(nd) { + e = t.w.RecursiveUnwatch(nd.Name) + } else { + e = t.w.Unwatch(nd.Name) + } + if e != nil { + err = nonil(err, e) + // TODO(rjeczalik): child is still watched, warn all its watchpoints + // about possible duplicate events via Error event + } + } + return err + } + // case 3: cur is new, alone node + switch diff := watchAdd(cur, c, eventset); { + case diff == none: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("watch requested but no parent watchpoint found: " + cur.Name) + case diff[0] == 0: + if isrec { + err = t.w.RecursiveWatch(cur.Name, diff[1]) + } else { + err = t.w.Watch(cur.Name, diff[1]) + } + if err != nil { + watchDel(cur, c, diff.Event()) + return err + } + default: + // TODO(rjeczalik): cleanup this panic after implementation is stable + panic("watch requested but no parent watchpoint found: " + cur.Name) + } + return nil +} + +// Stop TODO(rjeczalik) +// +// TODO(rjeczalik): Split parent watchpoint - transfer watches to children +// if parent is no longer needed. This carries a risk that underlying +// watcher calls could fail - reconsider if it's worth the effort. +func (t *recursiveTree) Stop(c chan<- EventInfo) { + var err error + fn := func(nd node) (e error) { + diff := watchDel(nd, c, all) + switch { + case diff == none && watchTotal(nd) == 0: + // TODO(rjeczalik): There's no watchpoints deeper in the tree, + // probably we should remove the nodes as well. + return nil + case diff == none: + // Removing c from nd does not require shrinking its eventset. + case diff[1] == 0: + if watchIsRecursive(nd) { + e = t.w.RecursiveUnwatch(nd.Name) + } else { + e = t.w.Unwatch(nd.Name) + } + default: + if watchIsRecursive(nd) { + e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1]) + } else { + e = t.w.Rewatch(nd.Name, diff[0], diff[1]) + } + } + fn := func(nd node) error { + watchDel(nd, c, all) + return nil + } + err = nonil(err, e, nd.Walk(fn)) + // TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to + // retry un/rewatching next time and/or let the user handle the failure + // vie Error event? + return errSkip + } + t.rw.Lock() + e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c + t.rw.Unlock() + if e != nil { + err = nonil(err, e) + } + dbgprintf("Stop(%p) error: %v\n", c, err) +} + +// Close TODO(rjeczalik) +func (t *recursiveTree) Close() error { + err := t.w.Close() + close(t.c) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/util.go new file mode 100644 index 0000000..67e01fb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/util.go @@ -0,0 +1,150 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import ( + "errors" + "os" + "path/filepath" + "strings" +) + +const all = ^Event(0) +const sep = string(os.PathSeparator) + +var errDepth = errors.New("exceeded allowed iteration count (circular symlink?)") + +func min(i, j int) int { + if i > j { + return j + } + return i +} + +func max(i, j int) int { + if i < j { + return j + } + return i +} + +// must panics if err is non-nil. +func must(err error) { + if err != nil { + panic(err) + } +} + +// nonil gives first non-nil error from the given arguments. +func nonil(err ...error) error { + for _, err := range err { + if err != nil { + return err + } + } + return nil +} + +func cleanpath(path string) (realpath string, isrec bool, err error) { + if strings.HasSuffix(path, "...") { + isrec = true + path = path[:len(path)-3] + } + if path, err = filepath.Abs(path); err != nil { + return "", false, err + } + if path, err = canonical(path); err != nil { + return "", false, err + } + return path, isrec, nil +} + +// canonical resolves any symlink in the given path and returns it in a clean form. +// It expects the path to be absolute. It fails to resolve circular symlinks by +// maintaining a simple iteration limit. +func canonical(p string) (string, error) { + p, err := filepath.Abs(p) + if err != nil { + return "", err + } + for i, j, depth := 1, 0, 1; i < len(p); i, depth = i+1, depth+1 { + if depth > 128 { + return "", &os.PathError{Op: "canonical", Path: p, Err: errDepth} + } + if j = strings.IndexRune(p[i:], '/'); j == -1 { + j, i = i, len(p) + } else { + j, i = i, i+j + } + fi, err := os.Lstat(p[:i]) + if err != nil { + return "", err + } + if fi.Mode()&os.ModeSymlink == os.ModeSymlink { + s, err := os.Readlink(p[:i]) + if err != nil { + return "", err + } + if filepath.IsAbs(s) { + p = "/" + s + p[i:] + } else { + p = p[:j] + s + p[i:] + } + i = 1 // no guarantee s is canonical, start all over + } + } + return filepath.Clean(p), nil +} + +func joinevents(events []Event) (e Event) { + if len(events) == 0 { + e = All + } else { + for _, event := range events { + e |= event + } + } + return +} + +func split(s string) (string, string) { + if i := lastIndexSep(s); i != -1 { + return s[:i], s[i+1:] + } + return "", s +} + +func base(s string) string { + if i := lastIndexSep(s); i != -1 { + return s[i+1:] + } + return s +} + +func indexbase(root, name string) int { + if n, m := len(root), len(name); m >= n && name[:n] == root && + (n == m || name[n] == os.PathSeparator) { + return min(n+1, m) + } + return -1 +} + +func indexSep(s string) int { + for i := 0; i < len(s); i++ { + if s[i] == os.PathSeparator { + return i + } + } + return -1 +} + +func lastIndexSep(s string) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == os.PathSeparator { + return i + } + } + return -1 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher.go new file mode 100644 index 0000000..34148ef --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher.go @@ -0,0 +1,85 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +import "errors" + +var ( + errAlreadyWatched = errors.New("path is already watched") + errNotWatched = errors.New("path is not being watched") + errInvalidEventSet = errors.New("invalid event set provided") +) + +// Watcher is a intermediate interface for wrapping inotify, ReadDirChangesW, +// FSEvents, kqueue and poller implementations. +// +// The watcher implementation is expected to do its own mapping between paths and +// create watchers if underlying event notification does not support it. For +// the ease of implementation it is guaranteed that paths provided via Watch and +// Unwatch methods are absolute and clean. +type watcher interface { + // Watch requests a watcher creation for the given path and given event set. + Watch(path string, event Event) error + + // Unwatch requests a watcher deletion for the given path and given event set. + Unwatch(path string) error + + // Rewatch provides a functionality for modifying existing watch-points, like + // expanding its event set. + // + // Rewatch modifies existing watch-point under for the given path. It passes + // the existing event set currently registered for the given path, and the + // new, requested event set. + // + // It is guaranteed that Tree will not pass to Rewatch zero value for any + // of its arguments. If old == new and watcher can be upgraded to + // recursiveWatcher interface, a watch for the corresponding path is expected + // to be changed from recursive to the non-recursive one. + Rewatch(path string, old, new Event) error + + // Close unwatches all paths that are registered. When Close returns, it + // is expected it will report no more events. + Close() error +} + +// RecursiveWatcher is an interface for a Watcher for those OS, which do support +// recursive watching over directories. +type recursiveWatcher interface { + RecursiveWatch(path string, event Event) error + + // RecursiveUnwatch removes a recursive watch-point given by the path. For + // native recursive implementation there is no difference in functionality + // between Unwatch and RecursiveUnwatch, however for those platforms, that + // requires emulation for recursive watch-points, the implementation differs. + RecursiveUnwatch(path string) error + + // RecursiveRewatcher provides a functionality for modifying and/or relocating + // existing recursive watch-points. + // + // To relocate a watch-point means to unwatch oldpath and set a watch-point on + // newpath. + // + // To modify a watch-point means either to expand or shrink its event set. + // + // Tree can want to either relocate, modify or relocate and modify a watch-point + // via single RecursiveRewatch call. + // + // If oldpath == newpath, the watch-point is expected to change its event set value + // from oldevent to newevent. + // + // If oldevent == newevent, the watch-point is expected to relocate from oldpath + // to the newpath. + // + // If oldpath != newpath and oldevent != newevent, the watch-point is expected + // to relocate from oldpath to the newpath first and then change its event set + // value from oldevent to the newevent. In other words the end result must be + // a watch-point set on newpath with newevent value of its event set. + // + // It is guaranteed that Tree will not pass to RecurisveRewatcha zero value + // for any of its arguments. If oldpath == newpath and oldevent == newevent, + // a watch for the corresponding path is expected to be changed for + // non-recursive to the recursive one. + RecursiveRewatch(oldpath, newpath string, oldevent, newevent Event) error +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen.go new file mode 100644 index 0000000..dd069f2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen.go @@ -0,0 +1,168 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build solaris + +package notify + +import ( + "fmt" + "os" + "syscall" +) + +// newTrigger returns implementation of trigger. +func newTrigger(pthLkp map[string]*watched) trigger { + return &fen{ + pthLkp: pthLkp, + cf: newCfen(), + } +} + +// fen is a structure implementing trigger for FEN. +type fen struct { + // p is a FEN port identifier + p int + // pthLkp is a structure mapping monitored files/dir with data about them, + // shared with parent trg structure + pthLkp map[string]*watched + // cf wraps C operations for FEN + cf cfen +} + +// watched is a data structure representing watched file/directory. +type watched struct { + // p is a path to watched file/directory + p string + // fi provides information about watched file/dir + fi os.FileInfo + // eDir represents events watched directly + eDir Event + // eNonDir represents events watched indirectly + eNonDir Event +} + +// Stop implements trigger. +func (f *fen) Stop() error { + return f.cf.portAlert(f.p) +} + +// Close implements trigger. +func (f *fen) Close() (err error) { + return syscall.Close(f.p) +} + +// NewWatched implements trigger. +func (*fen) NewWatched(p string, fi os.FileInfo) (*watched, error) { + return &watched{p: p, fi: fi}, nil +} + +// Record implements trigger. +func (f *fen) Record(w *watched) { + f.pthLkp[w.p] = w +} + +// Del implements trigger. +func (f *fen) Del(w *watched) { + delete(f.pthLkp, w.p) +} + +func inter2pe(n interface{}) PortEvent { + pe, ok := n.(PortEvent) + if !ok { + panic(fmt.Sprintf("fen: type should be PortEvent, %T instead", n)) + } + return pe +} + +// Watched implements trigger. +func (f *fen) Watched(n interface{}) (*watched, int64, error) { + pe := inter2pe(n) + fo, ok := pe.PortevObject.(*FileObj) + if !ok || fo == nil { + panic(fmt.Sprintf("fen: type should be *FileObj, %T instead", fo)) + } + w, ok := f.pthLkp[fo.Name] + if !ok { + return nil, 0, errNotWatched + } + return w, int64(pe.PortevEvents), nil +} + +// init initializes FEN. +func (f *fen) Init() (err error) { + f.p, err = f.cf.portCreate() + return +} + +func fi2fo(fi os.FileInfo, p string) FileObj { + st, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + panic(fmt.Sprintf("fen: type should be *syscall.Stat_t, %T instead", st)) + } + return FileObj{Name: p, Atim: st.Atim, Mtim: st.Mtim, Ctim: st.Ctim} +} + +// Unwatch implements trigger. +func (f *fen) Unwatch(w *watched) error { + return f.cf.portDissociate(f.p, FileObj{Name: w.p}) +} + +// Watch implements trigger. +func (f *fen) Watch(fi os.FileInfo, w *watched, e int64) error { + return f.cf.portAssociate(f.p, fi2fo(fi, w.p), int(e)) +} + +// Wait implements trigger. +func (f *fen) Wait() (interface{}, error) { + var ( + pe PortEvent + err error + ) + err = f.cf.portGet(f.p, &pe) + return pe, err +} + +// IsStop implements trigger. +func (f *fen) IsStop(n interface{}, err error) bool { + return err == syscall.EBADF || inter2pe(n).PortevSource == srcAlert +} + +func init() { + encode = func(e Event, dir bool) (o int64) { + // Create event is not supported by FEN. Instead FileModified event will + // be registered. If this event will be reported on dir which is to be + // monitored for Create, dir will be rescanned and Create events will + // be generated and returned for new files. In case of files, + // if not requested FileModified event is reported, it will be ignored. + o = int64(e &^ Create) + if (e&Create != 0 && dir) || e&Write != 0 { + o = (o &^ int64(Write)) | int64(FileModified) + } + // Following events are 'exception events' and as such cannot be requested + // explicitly for monitoring or filtered out. If the will be reported + // by FEN and not subscribed with by user, they will be filtered out by + // watcher's logic. + o &= int64(^Rename & ^Remove &^ FileDelete &^ FileRenameTo &^ + FileRenameFrom &^ Unmounted &^ MountedOver) + return + } + nat2not = map[Event]Event{ + FileModified: Write, + FileRenameFrom: Rename, + FileDelete: Remove, + FileAccess: Event(0), + FileAttrib: Event(0), + FileRenameTo: Event(0), + FileTrunc: Event(0), + FileNoFollow: Event(0), + Unmounted: Event(0), + MountedOver: Event(0), + } + not2nat = map[Event]Event{ + Write: FileModified, + Rename: FileRenameFrom, + Remove: FileDelete, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen_cgo.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen_cgo.go new file mode 100644 index 0000000..8ec8ead --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fen_cgo.go @@ -0,0 +1,141 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build solaris + +package notify + +// #include +// #include +// #include +// struct file_obj* newFo() { return (struct file_obj*) malloc(sizeof(struct file_obj)); } +// port_event_t* newPe() { return (port_event_t*) malloc(sizeof(port_event_t)); } +// uintptr_t conv(struct file_obj* fo) { return (uintptr_t) fo; } +// struct file_obj* dconv(uintptr_t fo) { return (struct file_obj*) fo; } +import "C" + +import ( + "syscall" + "unsafe" +) + +const ( + fileAccess = Event(C.FILE_ACCESS) + fileModified = Event(C.FILE_MODIFIED) + fileAttrib = Event(C.FILE_ATTRIB) + fileDelete = Event(C.FILE_DELETE) + fileRenameTo = Event(C.FILE_RENAME_TO) + fileRenameFrom = Event(C.FILE_RENAME_FROM) + fileTrunc = Event(C.FILE_TRUNC) + fileNoFollow = Event(C.FILE_NOFOLLOW) + unmounted = Event(C.UNMOUNTED) + mountedOver = Event(C.MOUNTEDOVER) +) + +// PortEvent is a notify's equivalent of port_event_t. +type PortEvent struct { + PortevEvents int // PortevEvents is an equivalent of portev_events. + PortevSource uint8 // PortevSource is an equivalent of portev_source. + PortevPad uint8 // Portevpad is an equivalent of portev_pad. + PortevObject interface{} // PortevObject is an equivalent of portev_object. + PortevUser uintptr // PortevUser is an equivalent of portev_user. +} + +// FileObj is a notify's equivalent of file_obj. +type FileObj struct { + Atim syscall.Timespec // Atim is an equivalent of fo_atime. + Mtim syscall.Timespec // Mtim is an equivalent of fo_mtime. + Ctim syscall.Timespec // Ctim is an equivalent of fo_ctime. + Pad [3]uintptr // Pad is an equivalent of fo_pad. + Name string // Name is an equivalent of fo_name. +} + +type cfen struct { + p2pe map[string]*C.port_event_t + p2fo map[string]*C.struct_file_obj +} + +func newCfen() cfen { + return cfen{ + p2pe: make(map[string]*C.port_event_t), + p2fo: make(map[string]*C.struct_file_obj), + } +} + +func unix2C(sec int64, nsec int64) (C.time_t, C.long) { + return C.time_t(sec), C.long(nsec) +} + +func (c *cfen) portAssociate(p int, fo FileObj, e int) (err error) { + cfo := C.newFo() + cfo.fo_atime.tv_sec, cfo.fo_atime.tv_nsec = unix2C(fo.Atim.Unix()) + cfo.fo_mtime.tv_sec, cfo.fo_mtime.tv_nsec = unix2C(fo.Mtim.Unix()) + cfo.fo_ctime.tv_sec, cfo.fo_ctime.tv_nsec = unix2C(fo.Ctim.Unix()) + cfo.fo_name = C.CString(fo.Name) + c.p2fo[fo.Name] = cfo + _, err = C.port_associate(C.int(p), srcFile, C.conv(cfo), C.int(e), nil) + return +} + +func (c *cfen) portDissociate(port int, fo FileObj) (err error) { + cfo, ok := c.p2fo[fo.Name] + if !ok { + return errNotWatched + } + _, err = C.port_dissociate(C.int(port), srcFile, C.conv(cfo)) + C.free(unsafe.Pointer(cfo.fo_name)) + C.free(unsafe.Pointer(cfo)) + delete(c.p2fo, fo.Name) + return +} + +const srcAlert = C.PORT_SOURCE_ALERT +const srcFile = C.PORT_SOURCE_FILE +const alertSet = C.PORT_ALERT_SET + +func cfo2fo(cfo *C.struct_file_obj) *FileObj { + // Currently remaining attributes are not used. + if cfo == nil { + return nil + } + var fo FileObj + fo.Name = C.GoString(cfo.fo_name) + return &fo +} + +func (c *cfen) portGet(port int, pe *PortEvent) (err error) { + cpe := C.newPe() + if _, err = C.port_get(C.int(port), cpe, nil); err != nil { + C.free(unsafe.Pointer(cpe)) + return + } + pe.PortevEvents, pe.PortevSource, pe.PortevPad = + int(cpe.portev_events), uint8(cpe.portev_source), uint8(cpe.portev_pad) + pe.PortevObject = cfo2fo(C.dconv(cpe.portev_object)) + pe.PortevUser = uintptr(cpe.portev_user) + C.free(unsafe.Pointer(cpe)) + return +} + +func (c *cfen) portCreate() (int, error) { + p, err := C.port_create() + return int(p), err +} + +func (c *cfen) portAlert(p int) (err error) { + _, err = C.port_alert(C.int(p), alertSet, C.int(666), nil) + return +} + +func (c *cfen) free() { + for i := range c.p2fo { + C.free(unsafe.Pointer(c.p2fo[i].fo_name)) + C.free(unsafe.Pointer(c.p2fo[i])) + } + for i := range c.p2pe { + C.free(unsafe.Pointer(c.p2pe[i])) + } + c.p2fo = make(map[string]*C.struct_file_obj) + c.p2pe = make(map[string]*C.port_event_t) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents.go new file mode 100644 index 0000000..9062c17 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents.go @@ -0,0 +1,319 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,!kqueue + +package notify + +import ( + "errors" + "strings" + "sync/atomic" +) + +// TODO(rjeczalik): get rid of calls to canonical, it's tree responsibility + +const ( + failure = uint32(FSEventsMustScanSubDirs | FSEventsUserDropped | FSEventsKernelDropped) + filter = uint32(FSEventsCreated | FSEventsRemoved | FSEventsRenamed | + FSEventsModified | FSEventsInodeMetaMod) +) + +// FSEvent represents single file event. It is created out of values passed by +// FSEvents to FSEventStreamCallback function. +type FSEvent struct { + Path string // real path of the file or directory + ID uint64 // ID of the event (FSEventStreamEventId) + Flags uint32 // joint FSEvents* flags (FSEventStreamEventFlags) +} + +// splitflags separates event flags from single set into slice of flags. +func splitflags(set uint32) (e []uint32) { + for i := uint32(1); set != 0; i, set = i<<1, set>>1 { + if (set & 1) != 0 { + e = append(e, i) + } + } + return +} + +// watch represents a filesystem watchpoint. It is a higher level abstraction +// over FSEvents' stream, which implements filtering of file events based +// on path and event set. It emulates non-recursive watch-point by filtering out +// events which paths are more than 1 level deeper than the watched path. +type watch struct { + // prev stores last event set per path in order to filter out old flags + // for new events, which appratenly FSEvents likes to retain. It's a disgusting + // hack, it should be researched how to get rid of it. + prev map[string]uint32 + c chan<- EventInfo + stream *stream + path string + events uint32 + isrec int32 + flushed bool +} + +// Example format: +// +// ~ $ (trigger command) # (event set) -> (effective event set) +// +// Heuristics: +// +// 1. Create event is removed when it was present in previous event set. +// Example: +// +// ~ $ echo > file # Create|Write -> Create|Write +// ~ $ echo > file # Create|Write|InodeMetaMod -> Write|InodeMetaMod +// +// 2. Remove event is removed if it was present in previouse event set. +// Example: +// +// ~ $ touch file # Create -> Create +// ~ $ rm file # Create|Remove -> Remove +// ~ $ touch file # Create|Remove -> Create +// +// 3. Write event is removed if not followed by InodeMetaMod on existing +// file. Example: +// +// ~ $ echo > file # Create|Write -> Create|Write +// ~ $ chmod +x file # Create|Write|ChangeOwner -> ChangeOwner +// +// 4. Write&InodeMetaMod is removed when effective event set contain Remove event. +// Example: +// +// ~ $ echo > file # Write|InodeMetaMod -> Write|InodeMetaMod +// ~ $ rm file # Remove|Write|InodeMetaMod -> Remove +// +func (w *watch) strip(base string, set uint32) uint32 { + const ( + write = FSEventsModified | FSEventsInodeMetaMod + both = FSEventsCreated | FSEventsRemoved + ) + switch w.prev[base] { + case FSEventsCreated: + set &^= FSEventsCreated + if set&FSEventsRemoved != 0 { + w.prev[base] = FSEventsRemoved + set &^= write + } + case FSEventsRemoved: + set &^= FSEventsRemoved + if set&FSEventsCreated != 0 { + w.prev[base] = FSEventsCreated + } + default: + switch set & both { + case FSEventsCreated: + w.prev[base] = FSEventsCreated + case FSEventsRemoved: + w.prev[base] = FSEventsRemoved + set &^= write + } + } + dbgprintf("split()=%v\n", Event(set)) + return set +} + +// Dispatch is a stream function which forwards given file events for the watched +// path to underlying FileInfo channel. +func (w *watch) Dispatch(ev []FSEvent) { + events := atomic.LoadUint32(&w.events) + isrec := (atomic.LoadInt32(&w.isrec) == 1) + for i := range ev { + if ev[i].Flags&FSEventsHistoryDone != 0 { + w.flushed = true + continue + } + if !w.flushed { + continue + } + dbgprintf("%v (0x%x) (%s, i=%d, ID=%d, len=%d)\n", Event(ev[i].Flags), + ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev)) + if ev[i].Flags&failure != 0 { + // TODO(rjeczalik): missing error handling + continue + } + if !strings.HasPrefix(ev[i].Path, w.path) { + continue + } + n := len(w.path) + base := "" + if len(ev[i].Path) > n { + if ev[i].Path[n] != '/' { + continue + } + base = ev[i].Path[n+1:] + if !isrec && strings.IndexByte(base, '/') != -1 { + continue + } + } + // TODO(rjeczalik): get diff only from filtered events? + e := w.strip(string(base), ev[i].Flags) & events + if e == 0 { + continue + } + for _, e := range splitflags(e) { + dbgprintf("%d: single event: %v", ev[i].ID, Event(e)) + w.c <- &event{ + fse: ev[i], + event: Event(e), + } + } + } +} + +// Stop closes underlying FSEvents stream and stops dispatching events. +func (w *watch) Stop() { + w.stream.Stop() + // TODO(rjeczalik): make (*stream).Stop flush synchronously undelivered events, + // so the following hack can be removed. It should flush all the streams + // concurrently as we care not to block too much here. + atomic.StoreUint32(&w.events, 0) + atomic.StoreInt32(&w.isrec, 0) +} + +// fsevents implements Watcher and RecursiveWatcher interfaces backed by FSEvents +// framework. +type fsevents struct { + watches map[string]*watch + c chan<- EventInfo +} + +func newWatcher(c chan<- EventInfo) watcher { + return &fsevents{ + watches: make(map[string]*watch), + c: c, + } +} + +func (fse *fsevents) watch(path string, event Event, isrec int32) (err error) { + if path, err = canonical(path); err != nil { + return err + } + if _, ok := fse.watches[path]; ok { + return errAlreadyWatched + } + w := &watch{ + prev: make(map[string]uint32), + c: fse.c, + path: path, + events: uint32(event), + isrec: isrec, + } + w.stream = newStream(path, w.Dispatch) + if err = w.stream.Start(); err != nil { + return err + } + fse.watches[path] = w + return nil +} + +func (fse *fsevents) unwatch(path string) (err error) { + if path, err = canonical(path); err != nil { + return + } + w, ok := fse.watches[path] + if !ok { + return errNotWatched + } + w.stream.Stop() + delete(fse.watches, path) + return nil +} + +// Watch implements Watcher interface. It fails with non-nil error when setting +// the watch-point by FSEvents fails or with errAlreadyWatched error when +// the given path is already watched. +func (fse *fsevents) Watch(path string, event Event) error { + return fse.watch(path, event, 0) +} + +// Unwatch implements Watcher interface. It fails with errNotWatched when +// the given path is not being watched. +func (fse *fsevents) Unwatch(path string) error { + return fse.unwatch(path) +} + +// Rewatch implements Watcher interface. It fails with errNotWatched when +// the given path is not being watched or with errInvalidEventSet when oldevent +// does not match event set the watch-point currently holds. +func (fse *fsevents) Rewatch(path string, oldevent, newevent Event) error { + w, ok := fse.watches[path] + if !ok { + return errNotWatched + } + if !atomic.CompareAndSwapUint32(&w.events, uint32(oldevent), uint32(newevent)) { + return errInvalidEventSet + } + atomic.StoreInt32(&w.isrec, 0) + return nil +} + +// RecursiveWatch implements RecursiveWatcher interface. It fails with non-nil +// error when setting the watch-point by FSEvents fails or with errAlreadyWatched +// error when the given path is already watched. +func (fse *fsevents) RecursiveWatch(path string, event Event) error { + return fse.watch(path, event, 1) +} + +// RecursiveUnwatch implements RecursiveWatcher interface. It fails with +// errNotWatched when the given path is not being watched. +// +// TODO(rjeczalik): fail if w.isrec == 0? +func (fse *fsevents) RecursiveUnwatch(path string) error { + return fse.unwatch(path) +} + +// RecrusiveRewatch implements RecursiveWatcher interface. It fails: +// +// * with errNotWatched when the given path is not being watched +// * with errInvalidEventSet when oldevent does not match the current event set +// * with errAlreadyWatched when watch-point given by the oldpath was meant to +// be relocated to newpath, but the newpath is already watched +// * a non-nil error when setting the watch-point with FSEvents fails +// +// TODO(rjeczalik): Improve handling of watch-point relocation? See two TODOs +// that follows. +func (fse *fsevents) RecursiveRewatch(oldpath, newpath string, oldevent, newevent Event) error { + switch [2]bool{oldpath == newpath, oldevent == newevent} { + case [2]bool{true, true}: + w, ok := fse.watches[oldpath] + if !ok { + return errNotWatched + } + atomic.StoreInt32(&w.isrec, 1) + return nil + case [2]bool{true, false}: + w, ok := fse.watches[oldpath] + if !ok { + return errNotWatched + } + if !atomic.CompareAndSwapUint32(&w.events, uint32(oldevent), uint32(newevent)) { + return errors.New("invalid event state diff") + } + atomic.StoreInt32(&w.isrec, 1) + return nil + default: + // TODO(rjeczalik): rewatch newpath only if exists? + // TODO(rjeczalik): migrate w.prev to new watch? + if _, ok := fse.watches[newpath]; ok { + return errAlreadyWatched + } + if err := fse.Unwatch(oldpath); err != nil { + return err + } + // TODO(rjeczalik): revert unwatch if watch fails? + return fse.watch(newpath, newevent, 1) + } +} + +// Close unwatches all watch-points. +func (fse *fsevents) Close() error { + for _, w := range fse.watches { + w.Stop() + } + fse.watches = nil + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go new file mode 100644 index 0000000..5be6463 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go @@ -0,0 +1,190 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,!kqueue + +package notify + +/* +#include + +typedef void (*CFRunLoopPerformCallBack)(void*); + +void gosource(void *); +void gostream(uintptr_t, uintptr_t, size_t, uintptr_t, uintptr_t, uintptr_t); + +static FSEventStreamRef EventStreamCreate(FSEventStreamContext * context, uintptr_t info, CFArrayRef paths, FSEventStreamEventId since, CFTimeInterval latency, FSEventStreamCreateFlags flags) { + context->info = (void*) info; + return FSEventStreamCreate(NULL, (FSEventStreamCallback) gostream, context, paths, since, latency, flags); +} + +#cgo LDFLAGS: -framework CoreServices +*/ +import "C" + +import ( + "errors" + "os" + "sync" + "sync/atomic" + "time" + "unsafe" +) + +var nilstream C.FSEventStreamRef + +// Default arguments for FSEventStreamCreate function. +var ( + latency C.CFTimeInterval + flags = C.FSEventStreamCreateFlags(C.kFSEventStreamCreateFlagFileEvents | C.kFSEventStreamCreateFlagNoDefer) + since = uint64(C.FSEventsGetCurrentEventId()) +) + +var runloop C.CFRunLoopRef // global runloop which all streams are registered with +var wg sync.WaitGroup // used to wait until the runloop starts + +// source is used for synchronization purposes - it signals when runloop has +// started and is ready via the wg. It also serves purpose of a dummy source, +// thanks to it the runloop does not return as it also has at least one source +// registered. +var source = C.CFRunLoopSourceCreate(nil, 0, &C.CFRunLoopSourceContext{ + perform: (C.CFRunLoopPerformCallBack)(C.gosource), +}) + +// Errors returned when FSEvents functions fail. +var ( + errCreate = os.NewSyscallError("FSEventStreamCreate", errors.New("NULL")) + errStart = os.NewSyscallError("FSEventStreamStart", errors.New("false")) +) + +// initializes the global runloop and ensures any created stream awaits its +// readiness. +func init() { + wg.Add(1) + go func() { + runloop = C.CFRunLoopGetCurrent() + C.CFRunLoopAddSource(runloop, source, C.kCFRunLoopDefaultMode) + C.CFRunLoopRun() + panic("runloop has just unexpectedly stopped") + }() + C.CFRunLoopSourceSignal(source) +} + +//export gosource +func gosource(unsafe.Pointer) { + time.Sleep(time.Second) + wg.Done() +} + +//export gostream +func gostream(_, info uintptr, n C.size_t, paths, flags, ids uintptr) { + const ( + offchar = unsafe.Sizeof((*C.char)(nil)) + offflag = unsafe.Sizeof(C.FSEventStreamEventFlags(0)) + offid = unsafe.Sizeof(C.FSEventStreamEventId(0)) + ) + if n == 0 { + return + } + ev := make([]FSEvent, 0, int(n)) + for i := uintptr(0); i < uintptr(n); i++ { + switch flags := *(*uint32)(unsafe.Pointer((flags + i*offflag))); { + case flags&uint32(FSEventsEventIdsWrapped) != 0: + atomic.StoreUint64(&since, uint64(C.FSEventsGetCurrentEventId())) + default: + ev = append(ev, FSEvent{ + Path: C.GoString(*(**C.char)(unsafe.Pointer(paths + i*offchar))), + Flags: flags, + ID: *(*uint64)(unsafe.Pointer(ids + i*offid)), + }) + } + + } + streamFuncs.get(info)(ev) +} + +// StreamFunc is a callback called when stream receives file events. +type streamFunc func([]FSEvent) + +var streamFuncs = streamFuncRegistry{m: map[uintptr]streamFunc{}} + +type streamFuncRegistry struct { + mu sync.Mutex + m map[uintptr]streamFunc + i uintptr +} + +func (r *streamFuncRegistry) get(id uintptr) streamFunc { + r.mu.Lock() + defer r.mu.Unlock() + return r.m[id] +} + +func (r *streamFuncRegistry) add(fn streamFunc) uintptr { + r.mu.Lock() + defer r.mu.Unlock() + r.i++ + r.m[r.i] = fn + return r.i +} + +func (r *streamFuncRegistry) delete(id uintptr) { + r.mu.Lock() + defer r.mu.Unlock() + delete(r.m, id) +} + +// Stream represents single watch-point which listens for events scheduled by +// the global runloop. +type stream struct { + path string + ref C.FSEventStreamRef + info uintptr +} + +// NewStream creates a stream for given path, listening for file events and +// calling fn upon receiving any. +func newStream(path string, fn streamFunc) *stream { + return &stream{ + path: path, + info: streamFuncs.add(fn), + } +} + +// Start creates a FSEventStream for the given path and schedules it with +// global runloop. It's a nop if the stream was already started. +func (s *stream) Start() error { + if s.ref != nilstream { + return nil + } + wg.Wait() + p := C.CFStringCreateWithCStringNoCopy(nil, C.CString(s.path), C.kCFStringEncodingUTF8, nil) + path := C.CFArrayCreate(nil, (*unsafe.Pointer)(unsafe.Pointer(&p)), 1, nil) + ctx := C.FSEventStreamContext{} + ref := C.EventStreamCreate(&ctx, C.uintptr_t(s.info), path, C.FSEventStreamEventId(atomic.LoadUint64(&since)), latency, flags) + if ref == nilstream { + return errCreate + } + C.FSEventStreamScheduleWithRunLoop(ref, runloop, C.kCFRunLoopDefaultMode) + if C.FSEventStreamStart(ref) == C.Boolean(0) { + C.FSEventStreamInvalidate(ref) + return errStart + } + C.CFRunLoopWakeUp(runloop) + s.ref = ref + return nil +} + +// Stop stops underlying FSEventStream and unregisters it from global runloop. +func (s *stream) Stop() { + if s.ref == nilstream { + return + } + wg.Wait() + C.FSEventStreamStop(s.ref) + C.FSEventStreamInvalidate(s.ref) + C.CFRunLoopWakeUp(runloop) + s.ref = nilstream + streamFuncs.delete(s.info) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_inotify.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_inotify.go new file mode 100644 index 0000000..3ceaa8f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_inotify.go @@ -0,0 +1,396 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build linux + +package notify + +import ( + "bytes" + "errors" + "path/filepath" + "runtime" + "sync" + "sync/atomic" + "syscall" + "unsafe" +) + +// eventBufferSize defines the size of the buffer given to read(2) function. One +// should not depend on this value, since it was arbitrary chosen and may be +// changed in the future. +const eventBufferSize = 64 * (syscall.SizeofInotifyEvent + syscall.PathMax + 1) + +// consumersCount defines the number of consumers in producer-consumer based +// implementation. Each consumer is run in a separate goroutine and has read +// access to watched files map. +const consumersCount = 2 + +const invalidDescriptor = -1 + +// watched is a pair of file path and inotify mask used as a value in +// watched files map. +type watched struct { + path string + mask uint32 +} + +// inotify implements Watcher interface. +type inotify struct { + sync.RWMutex // protects inotify.m map + m map[int32]*watched // watch descriptor to watched object + fd int32 // inotify file descriptor + pipefd []int // pipe's read and write descriptors + epfd int // epoll descriptor + epes []syscall.EpollEvent // epoll events + buffer [eventBufferSize]byte // inotify event buffer + wg sync.WaitGroup // wait group used to close main loop + c chan<- EventInfo // event dispatcher channel +} + +// NewWatcher creates new non-recursive inotify backed by inotify. +func newWatcher(c chan<- EventInfo) watcher { + i := &inotify{ + m: make(map[int32]*watched), + fd: invalidDescriptor, + pipefd: []int{invalidDescriptor, invalidDescriptor}, + epfd: invalidDescriptor, + epes: make([]syscall.EpollEvent, 0), + c: c, + } + runtime.SetFinalizer(i, func(i *inotify) { + i.epollclose() + if i.fd != invalidDescriptor { + syscall.Close(int(i.fd)) + } + }) + return i +} + +// Watch implements notify.watcher interface. +func (i *inotify) Watch(path string, e Event) error { + return i.watch(path, e) +} + +// Rewatch implements notify.watcher interface. +func (i *inotify) Rewatch(path string, _, newevent Event) error { + return i.watch(path, newevent) +} + +// watch adds a new watcher to the set of watched objects or modifies the existing +// one. If called for the first time, this function initializes inotify filesystem +// monitor and starts producer-consumers goroutines. +func (i *inotify) watch(path string, e Event) (err error) { + if e&^(All|Event(syscall.IN_ALL_EVENTS)) != 0 { + return errors.New("notify: unknown event") + } + if err = i.lazyinit(); err != nil { + return + } + iwd, err := syscall.InotifyAddWatch(int(i.fd), path, encode(e)) + if err != nil { + return + } + i.RLock() + wd := i.m[int32(iwd)] + i.RUnlock() + if wd == nil { + i.Lock() + if i.m[int32(iwd)] == nil { + i.m[int32(iwd)] = &watched{path: path, mask: uint32(e)} + } + i.Unlock() + } else { + i.Lock() + wd.mask = uint32(e) + i.Unlock() + } + return nil +} + +// lazyinit sets up all required file descriptors and starts 1+consumersCount +// goroutines. The producer goroutine blocks until file-system notifications +// occur. Then, all events are read from system buffer and sent to consumer +// goroutines which construct valid notify events. This method uses +// Double-Checked Locking optimization. +func (i *inotify) lazyinit() error { + if atomic.LoadInt32(&i.fd) == invalidDescriptor { + i.Lock() + defer i.Unlock() + if atomic.LoadInt32(&i.fd) == invalidDescriptor { + fd, err := syscall.InotifyInit() + if err != nil { + return err + } + i.fd = int32(fd) + if err = i.epollinit(); err != nil { + _, _ = i.epollclose(), syscall.Close(int(fd)) // Ignore errors. + i.fd = invalidDescriptor + return err + } + esch := make(chan []*event) + go i.loop(esch) + i.wg.Add(consumersCount) + for n := 0; n < consumersCount; n++ { + go i.send(esch) + } + } + } + return nil +} + +// epollinit opens an epoll file descriptor and creates a pipe which will be +// used to wake up the epoll_wait(2) function. Then, file descriptor associated +// with inotify event queue and the read end of the pipe are added to epoll set. +// Note that `fd` member must be set before this function is called. +func (i *inotify) epollinit() (err error) { + if i.epfd, err = syscall.EpollCreate1(0); err != nil { + return + } + if err = syscall.Pipe(i.pipefd); err != nil { + return + } + i.epes = []syscall.EpollEvent{ + {Events: syscall.EPOLLIN, Fd: i.fd}, + {Events: syscall.EPOLLIN, Fd: int32(i.pipefd[0])}, + } + if err = syscall.EpollCtl(i.epfd, syscall.EPOLL_CTL_ADD, int(i.fd), &i.epes[0]); err != nil { + return + } + return syscall.EpollCtl(i.epfd, syscall.EPOLL_CTL_ADD, i.pipefd[0], &i.epes[1]) +} + +// epollclose closes the file descriptor created by the call to epoll_create(2) +// and two file descriptors opened by pipe(2) function. +func (i *inotify) epollclose() (err error) { + if i.epfd != invalidDescriptor { + if err = syscall.Close(i.epfd); err == nil { + i.epfd = invalidDescriptor + } + } + for n, fd := range i.pipefd { + if fd != invalidDescriptor { + switch e := syscall.Close(fd); { + case e != nil && err == nil: + err = e + case e == nil: + i.pipefd[n] = invalidDescriptor + } + } + } + return +} + +// loop blocks until either inotify or pipe file descriptor is ready for I/O. +// All read operations triggered by filesystem notifications are forwarded to +// one of the event's consumers. If pipe fd became ready, loop function closes +// all file descriptors opened by lazyinit method and returns afterwards. +func (i *inotify) loop(esch chan<- []*event) { + epes := make([]syscall.EpollEvent, 1) + fd := atomic.LoadInt32(&i.fd) + for { + switch _, err := syscall.EpollWait(i.epfd, epes, -1); err { + case nil: + switch epes[0].Fd { + case fd: + esch <- i.read() + epes[0].Fd = 0 + case int32(i.pipefd[0]): + i.Lock() + defer i.Unlock() + if err = syscall.Close(int(fd)); err != nil && err != syscall.EINTR { + panic("notify: close(2) error " + err.Error()) + } + atomic.StoreInt32(&i.fd, invalidDescriptor) + if err = i.epollclose(); err != nil && err != syscall.EINTR { + panic("notify: epollclose error " + err.Error()) + } + close(esch) + return + } + case syscall.EINTR: + continue + default: // We should never reach this line. + panic("notify: epoll_wait(2) error " + err.Error()) + } + } +} + +// read reads events from an inotify file descriptor. It does not handle errors +// returned from read(2) function since they are not critical to watcher logic. +func (i *inotify) read() (es []*event) { + n, err := syscall.Read(int(i.fd), i.buffer[:]) + if err != nil || n < syscall.SizeofInotifyEvent { + return + } + var sys *syscall.InotifyEvent + nmin := n - syscall.SizeofInotifyEvent + for pos, path := 0, ""; pos <= nmin; { + sys = (*syscall.InotifyEvent)(unsafe.Pointer(&i.buffer[pos])) + pos += syscall.SizeofInotifyEvent + if path = ""; sys.Len > 0 { + endpos := pos + int(sys.Len) + path = string(bytes.TrimRight(i.buffer[pos:endpos], "\x00")) + pos = endpos + } + es = append(es, &event{ + sys: syscall.InotifyEvent{ + Wd: sys.Wd, + Mask: sys.Mask, + Cookie: sys.Cookie, + }, + path: path, + }) + } + return +} + +// send is a consumer function which sends events to event dispatcher channel. +// It is run in a separate goroutine in order to not block loop method when +// possibly expensive write operations are performed on inotify map. +func (i *inotify) send(esch <-chan []*event) { + for es := range esch { + for _, e := range i.transform(es) { + if e != nil { + i.c <- e + } + } + } + i.wg.Done() +} + +// transform prepares events read from inotify file descriptor for sending to +// user. It removes invalid events and these which are no longer present in +// inotify map. This method may also split one raw event into two different ones +// when system-dependent result is required. +func (i *inotify) transform(es []*event) []*event { + var multi []*event + i.RLock() + for idx, e := range es { + if e.sys.Mask&(syscall.IN_IGNORED|syscall.IN_Q_OVERFLOW) != 0 { + es[idx] = nil + continue + } + wd, ok := i.m[e.sys.Wd] + if !ok || e.sys.Mask&encode(Event(wd.mask)) == 0 { + es[idx] = nil + continue + } + if e.path == "" { + e.path = wd.path + } else { + e.path = filepath.Join(wd.path, e.path) + } + multi = append(multi, decode(Event(wd.mask), e)) + if e.event == 0 { + es[idx] = nil + } + } + i.RUnlock() + es = append(es, multi...) + return es +} + +// encode converts notify system-independent events to valid inotify mask +// which can be passed to inotify_add_watch(2) function. +func encode(e Event) uint32 { + if e&Create != 0 { + e = (e ^ Create) | InCreate | InMovedTo + } + if e&Remove != 0 { + e = (e ^ Remove) | InDelete | InDeleteSelf + } + if e&Write != 0 { + e = (e ^ Write) | InModify + } + if e&Rename != 0 { + e = (e ^ Rename) | InMovedFrom | InMoveSelf + } + return uint32(e) +} + +// decode uses internally stored mask to distinguish whether system-independent +// or system-dependent event is requested. The first one is created by modifying +// `e` argument. decode method sets e.event value to 0 when an event should be +// skipped. System-dependent event is set as the function's return value which +// can be nil when the event should not be passed on. +func decode(mask Event, e *event) (syse *event) { + if sysmask := uint32(mask) & e.sys.Mask; sysmask != 0 { + syse = &event{sys: syscall.InotifyEvent{ + Wd: e.sys.Wd, + Mask: e.sys.Mask, + Cookie: e.sys.Cookie, + }, event: Event(sysmask), path: e.path} + } + imask := encode(mask) + switch { + case mask&Create != 0 && imask&uint32(InCreate|InMovedTo)&e.sys.Mask != 0: + e.event = Create + case mask&Remove != 0 && imask&uint32(InDelete|InDeleteSelf)&e.sys.Mask != 0: + e.event = Remove + case mask&Write != 0 && imask&uint32(InModify)&e.sys.Mask != 0: + e.event = Write + case mask&Rename != 0 && imask&uint32(InMovedFrom|InMoveSelf)&e.sys.Mask != 0: + e.event = Rename + default: + e.event = 0 + } + return +} + +// Unwatch implements notify.watcher interface. It looks for watch descriptor +// related to registered path and if found, calls inotify_rm_watch(2) function. +// This method is allowed to return EINVAL error when concurrently requested to +// delete identical path. +func (i *inotify) Unwatch(path string) (err error) { + iwd := int32(invalidDescriptor) + i.RLock() + for iwdkey, wd := range i.m { + if wd.path == path { + iwd = iwdkey + break + } + } + i.RUnlock() + if iwd == invalidDescriptor { + return errors.New("notify: path " + path + " is already watched") + } + fd := atomic.LoadInt32(&i.fd) + if _, err = syscall.InotifyRmWatch(int(fd), uint32(iwd)); err != nil { + return + } + i.Lock() + delete(i.m, iwd) + i.Unlock() + return nil +} + +// Close implements notify.watcher interface. It removes all existing watch +// descriptors and wakes up producer goroutine by sending data to the write end +// of the pipe. The function waits for a signal from producer which means that +// all operations on current monitoring instance are done. +func (i *inotify) Close() (err error) { + i.Lock() + if fd := atomic.LoadInt32(&i.fd); fd == invalidDescriptor { + i.Unlock() + return nil + } + for iwd := range i.m { + if _, e := syscall.InotifyRmWatch(int(i.fd), uint32(iwd)); e != nil && err == nil { + err = e + } + delete(i.m, iwd) + } + switch _, errwrite := syscall.Write(i.pipefd[1], []byte{0x00}); { + case errwrite != nil && err == nil: + err = errwrite + fallthrough + case errwrite != nil: + i.Unlock() + default: + i.Unlock() + i.wg.Wait() + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_kqueue.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_kqueue.go new file mode 100644 index 0000000..6d500b7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_kqueue.go @@ -0,0 +1,193 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,kqueue dragonfly freebsd netbsd openbsd + +package notify + +import ( + "fmt" + "os" + "syscall" +) + +// newTrigger returns implementation of trigger. +func newTrigger(pthLkp map[string]*watched) trigger { + return &kq{ + pthLkp: pthLkp, + idLkp: make(map[int]*watched), + } +} + +// kq is a structure implementing trigger for kqueue. +type kq struct { + // fd is a kqueue file descriptor + fd int + // pipefds are file descriptors used to stop `Kevent` call. + pipefds [2]int + // idLkp is a data structure mapping file descriptors with data about watching + // represented by them files/directories. + idLkp map[int]*watched + // pthLkp is a structure mapping monitored files/dir with data about them, + // shared with parent trg structure + pthLkp map[string]*watched +} + +// watched is a data structure representing watched file/directory. +type watched struct { + // p is a path to watched file/directory. + p string + // fd is a file descriptor for watched file/directory. + fd int + // fi provides information about watched file/dir. + fi os.FileInfo + // eDir represents events watched directly. + eDir Event + // eNonDir represents events watched indirectly. + eNonDir Event +} + +// Stop implements trigger. +func (k *kq) Stop() (err error) { + // trigger event used to interrupt Kevent call. + _, err = syscall.Write(k.pipefds[1], []byte{0x00}) + return +} + +// Close implements trigger. +func (k *kq) Close() error { + return syscall.Close(k.fd) +} + +// NewWatched implements trigger. +func (*kq) NewWatched(p string, fi os.FileInfo) (*watched, error) { + fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + return &watched{fd: fd, p: p, fi: fi}, nil +} + +// Record implements trigger. +func (k *kq) Record(w *watched) { + k.idLkp[w.fd], k.pthLkp[w.p] = w, w +} + +// Del implements trigger. +func (k *kq) Del(w *watched) { + syscall.Close(w.fd) + delete(k.idLkp, w.fd) + delete(k.pthLkp, w.p) +} + +func inter2kq(n interface{}) syscall.Kevent_t { + kq, ok := n.(syscall.Kevent_t) + if !ok { + panic(fmt.Sprintf("kqueue: type should be Kevent_t, %T instead", n)) + } + return kq +} + +// Init implements trigger. +func (k *kq) Init() (err error) { + if k.fd, err = syscall.Kqueue(); err != nil { + return + } + // Creates pipe used to stop `Kevent` call by registering it, + // watching read end and writing to other end of it. + if err = syscall.Pipe(k.pipefds[:]); err != nil { + return nonil(err, k.Close()) + } + var kevn [1]syscall.Kevent_t + syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD) + if _, err = syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil { + return nonil(err, k.Close()) + } + return +} + +// Unwatch implements trigger. +func (k *kq) Unwatch(w *watched) (err error) { + var kevn [1]syscall.Kevent_t + syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE) + + _, err = syscall.Kevent(k.fd, kevn[:], nil, nil) + return +} + +// Watch implements trigger. +func (k *kq) Watch(fi os.FileInfo, w *watched, e int64) (err error) { + var kevn [1]syscall.Kevent_t + syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, + syscall.EV_ADD|syscall.EV_CLEAR) + kevn[0].Fflags = uint32(e) + + _, err = syscall.Kevent(k.fd, kevn[:], nil, nil) + return +} + +// Wait implements trigger. +func (k *kq) Wait() (interface{}, error) { + var ( + kevn [1]syscall.Kevent_t + err error + ) + kevn[0] = syscall.Kevent_t{} + _, err = syscall.Kevent(k.fd, nil, kevn[:], nil) + + return kevn[0], err +} + +// Watched implements trigger. +func (k *kq) Watched(n interface{}) (*watched, int64, error) { + kevn, ok := n.(syscall.Kevent_t) + if !ok { + panic(fmt.Sprintf("kq: type should be syscall.Kevent_t, %T instead", kevn)) + } + if _, ok = k.idLkp[int(kevn.Ident)]; !ok { + return nil, 0, errNotWatched + } + return k.idLkp[int(kevn.Ident)], int64(kevn.Fflags), nil +} + +// IsStop implements trigger. +func (k *kq) IsStop(n interface{}, err error) bool { + return int(inter2kq(n).Ident) == k.pipefds[0] +} + +func init() { + encode = func(e Event, dir bool) (o int64) { + // Create event is not supported by kqueue. Instead NoteWrite event will + // be registered for a directory. If this event will be reported on dir + // which is to be monitored for Create, dir will be rescanned + // and Create events will be generated and returned for new files. + // In case of files, if not requested NoteRename event is reported, + // it will be ignored. + o = int64(e &^ Create) + if (e&Create != 0 && dir) || e&Write != 0 { + o = (o &^ int64(Write)) | int64(NoteWrite) + } + if e&Rename != 0 { + o = (o &^ int64(Rename)) | int64(NoteRename) + } + if e&Remove != 0 { + o = (o &^ int64(Remove)) | int64(NoteDelete) + } + return + } + nat2not = map[Event]Event{ + NoteWrite: Write, + NoteRename: Rename, + NoteDelete: Remove, + NoteExtend: Event(0), + NoteAttrib: Event(0), + NoteRevoke: Event(0), + NoteLink: Event(0), + } + not2nat = map[Event]Event{ + Write: NoteWrite, + Rename: NoteRename, + Remove: NoteDelete, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_readdcw.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_readdcw.go new file mode 100644 index 0000000..5923bfd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_readdcw.go @@ -0,0 +1,574 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build windows + +package notify + +import ( + "errors" + "runtime" + "sync" + "sync/atomic" + "syscall" + "unsafe" +) + +// readBufferSize defines the size of an array in which read statuses are stored. +// The buffer have to be DWORD-aligned and, if notify is used in monitoring a +// directory over the network, its size must not be greater than 64KB. Each of +// watched directories uses its own buffer for storing events. +const readBufferSize = 4096 + +// Since all operations which go through the Windows completion routine are done +// asynchronously, filter may set one of the constants belor. They were defined +// in order to distinguish whether current folder should be re-registered in +// ReadDirectoryChangesW function or some control operations need to be executed. +const ( + stateRewatch uint32 = 1 << (28 + iota) + stateUnwatch + stateCPClose +) + +// Filter used in current implementation was split into four segments: +// - bits 0-11 store ReadDirectoryChangesW filters, +// - bits 12-19 store File notify actions, +// - bits 20-27 store notify specific events and flags, +// - bits 28-31 store states which are used in loop's FSM. +// Constants below are used as masks to retrieve only specific filter parts. +const ( + onlyNotifyChanges uint32 = 0x00000FFF + onlyNGlobalEvents uint32 = 0x0FF00000 + onlyMachineStates uint32 = 0xF0000000 +) + +// grip represents a single watched directory. It stores the data required by +// ReadDirectoryChangesW function. Only the filter, recursive, and handle members +// may by modified by watcher implementation. Rest of the them have to remain +// constant since they are used by Windows completion routine. This indicates that +// grip can be removed only when all operations on the file handle are finished. +type grip struct { + handle syscall.Handle + filter uint32 + recursive bool + pathw []uint16 + buffer [readBufferSize]byte + parent *watched + ovlapped *overlappedEx +} + +// overlappedEx stores information used in asynchronous input and output. +// Additionally, overlappedEx contains a pointer to 'grip' item which is used in +// order to gather the structure in which the overlappedEx object was created. +type overlappedEx struct { + syscall.Overlapped + parent *grip +} + +// newGrip creates a new file handle that can be used in overlapped operations. +// Then, the handle is associated with I/O completion port 'cph' and its value +// is stored in newly created 'grip' object. +func newGrip(cph syscall.Handle, parent *watched, filter uint32) (*grip, error) { + g := &grip{ + handle: syscall.InvalidHandle, + filter: filter, + recursive: parent.recursive, + pathw: parent.pathw, + parent: parent, + ovlapped: &overlappedEx{}, + } + if err := g.register(cph); err != nil { + return nil, err + } + g.ovlapped.parent = g + return g, nil +} + +// NOTE : Thread safe +func (g *grip) register(cph syscall.Handle) (err error) { + if g.handle, err = syscall.CreateFile( + &g.pathw[0], + syscall.FILE_LIST_DIRECTORY, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + nil, + syscall.OPEN_EXISTING, + syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, + 0, + ); err != nil { + return + } + if _, err = syscall.CreateIoCompletionPort(g.handle, cph, 0, 0); err != nil { + syscall.CloseHandle(g.handle) + return + } + return g.readDirChanges() +} + +// readDirChanges tells the system to store file change information in grip's +// buffer. Directory changes that occur between calls to this function are added +// to the buffer and then, returned with the next call. +func (g *grip) readDirChanges() error { + return syscall.ReadDirectoryChanges( + g.handle, + &g.buffer[0], + uint32(unsafe.Sizeof(g.buffer)), + g.recursive, + encode(g.filter), + nil, + (*syscall.Overlapped)(unsafe.Pointer(g.ovlapped)), + 0, + ) +} + +// encode transforms a generic filter, which contains platform independent and +// implementation specific bit fields, to value that can be used as NotifyFilter +// parameter in ReadDirectoryChangesW function. +func encode(filter uint32) uint32 { + e := Event(filter & (onlyNGlobalEvents | onlyNotifyChanges)) + if e&dirmarker != 0 { + return uint32(FileNotifyChangeDirName) + } + if e&Create != 0 { + e = (e ^ Create) | FileNotifyChangeFileName + } + if e&Remove != 0 { + e = (e ^ Remove) | FileNotifyChangeFileName + } + if e&Write != 0 { + e = (e ^ Write) | FileNotifyChangeAttributes | FileNotifyChangeSize | + FileNotifyChangeCreation | FileNotifyChangeSecurity + } + if e&Rename != 0 { + e = (e ^ Rename) | FileNotifyChangeFileName + } + return uint32(e) +} + +// watched is made in order to check whether an action comes from a directory or +// file. This approach requires two file handlers per single monitored folder. The +// second grip handles actions which include creating or deleting a directory. If +// these processes are not monitored, only the first grip is created. +type watched struct { + filter uint32 + recursive bool + count uint8 + pathw []uint16 + digrip [2]*grip +} + +// newWatched creates a new watched instance. It splits the filter variable into +// two parts. The first part is responsible for watching all events which can be +// created for a file in watched directory structure and the second one watches +// only directory Create/Remove actions. If all operations succeed, the Create +// message is sent to I/O completion port queue for further processing. +func newWatched(cph syscall.Handle, filter uint32, recursive bool, + path string) (wd *watched, err error) { + wd = &watched{ + filter: filter, + recursive: recursive, + } + if wd.pathw, err = syscall.UTF16FromString(path); err != nil { + return + } + if err = wd.recreate(cph); err != nil { + return + } + return wd, nil +} + +// TODO : doc +func (wd *watched) recreate(cph syscall.Handle) (err error) { + filefilter := wd.filter &^ uint32(FileNotifyChangeDirName) + if err = wd.updateGrip(0, cph, filefilter == 0, filefilter); err != nil { + return + } + dirfilter := wd.filter & uint32(FileNotifyChangeDirName|Create|Remove) + if err = wd.updateGrip(1, cph, dirfilter == 0, wd.filter|uint32(dirmarker)); err != nil { + return + } + wd.filter &^= onlyMachineStates + return +} + +// TODO : doc +func (wd *watched) updateGrip(idx int, cph syscall.Handle, reset bool, + newflag uint32) (err error) { + if reset { + wd.digrip[idx] = nil + } else { + if wd.digrip[idx] == nil { + if wd.digrip[idx], err = newGrip(cph, wd, newflag); err != nil { + wd.closeHandle() + return + } + } else { + wd.digrip[idx].filter = newflag + wd.digrip[idx].recursive = wd.recursive + if err = wd.digrip[idx].register(cph); err != nil { + wd.closeHandle() + return + } + } + wd.count++ + } + return +} + +// closeHandle closes handles that are stored in digrip array. Function always +// tries to close all of the handlers before it exits, even when there are errors +// returned from the operating system kernel. +func (wd *watched) closeHandle() (err error) { + for _, g := range wd.digrip { + if g != nil && g.handle != syscall.InvalidHandle { + switch suberr := syscall.CloseHandle(g.handle); { + case suberr == nil: + g.handle = syscall.InvalidHandle + case err == nil: + err = suberr + } + } + } + return +} + +// watcher implements Watcher interface. It stores a set of watched directories. +// All operations which remove watched objects from map `m` must be performed in +// loop goroutine since these structures are used internally by operating system. +type readdcw struct { + sync.Mutex + m map[string]*watched + cph syscall.Handle + start bool + wg sync.WaitGroup + c chan<- EventInfo +} + +// NewWatcher creates new non-recursive watcher backed by ReadDirectoryChangesW. +func newWatcher(c chan<- EventInfo) watcher { + r := &readdcw{ + m: make(map[string]*watched), + cph: syscall.InvalidHandle, + c: c, + } + runtime.SetFinalizer(r, func(r *readdcw) { + if r.cph != syscall.InvalidHandle { + syscall.CloseHandle(r.cph) + } + }) + return r +} + +// Watch implements notify.Watcher interface. +func (r *readdcw) Watch(path string, event Event) error { + return r.watch(path, event, false) +} + +// RecursiveWatch implements notify.RecursiveWatcher interface. +func (r *readdcw) RecursiveWatch(path string, event Event) error { + return r.watch(path, event, true) +} + +// watch inserts a directory to the group of watched folders. If watched folder +// already exists, function tries to rewatch it with new filters(NOT VALID). Moreover, +// watch starts the main event loop goroutine when called for the first time. +func (r *readdcw) watch(path string, event Event, recursive bool) (err error) { + if event&^(All|fileNotifyChangeAll) != 0 { + return errors.New("notify: unknown event") + } + r.Lock() + wd, ok := r.m[path] + r.Unlock() + if !ok { + if err = r.lazyinit(); err != nil { + return + } + r.Lock() + if wd, ok = r.m[path]; ok { + r.Unlock() + return + } + if wd, err = newWatched(r.cph, uint32(event), recursive, path); err != nil { + r.Unlock() + return + } + r.m[path] = wd + r.Unlock() + } + return nil +} + +// lazyinit creates an I/O completion port and starts the main event processing +// loop. This method uses Double-Checked Locking optimization. +func (r *readdcw) lazyinit() (err error) { + invalid := uintptr(syscall.InvalidHandle) + if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid { + r.Lock() + defer r.Unlock() + if atomic.LoadUintptr((*uintptr)(&r.cph)) == invalid { + cph := syscall.InvalidHandle + if cph, err = syscall.CreateIoCompletionPort(cph, 0, 0, 0); err != nil { + return + } + r.cph, r.start = cph, true + go r.loop() + } + } + return +} + +// TODO(pknap) : doc +func (r *readdcw) loop() { + var n, key uint32 + var overlapped *syscall.Overlapped + for { + err := syscall.GetQueuedCompletionStatus(r.cph, &n, &key, &overlapped, syscall.INFINITE) + if key == stateCPClose { + r.Lock() + handle := r.cph + r.cph = syscall.InvalidHandle + r.Unlock() + syscall.CloseHandle(handle) + r.wg.Done() + return + } + if overlapped == nil { + // TODO: check key == rewatch delete or 0(panic) + continue + } + overEx := (*overlappedEx)(unsafe.Pointer(overlapped)) + if n == 0 { + r.loopstate(overEx) + } else { + r.loopevent(n, overEx) + if err = overEx.parent.readDirChanges(); err != nil { + // TODO: error handling + } + } + } +} + +// TODO(pknap) : doc +func (r *readdcw) loopstate(overEx *overlappedEx) { + filter := atomic.LoadUint32(&overEx.parent.parent.filter) + if filter&onlyMachineStates == 0 { + return + } + if overEx.parent.parent.count--; overEx.parent.parent.count == 0 { + switch filter & onlyMachineStates { + case stateRewatch: + r.Lock() + overEx.parent.parent.recreate(r.cph) + r.Unlock() + case stateUnwatch: + r.Lock() + delete(r.m, syscall.UTF16ToString(overEx.parent.pathw)) + r.Unlock() + case stateCPClose: + default: + panic(`notify: windows loopstate logic error`) + } + } +} + +// TODO(pknap) : doc +func (r *readdcw) loopevent(n uint32, overEx *overlappedEx) { + events := []*event{} + var currOffset uint32 + for { + raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&overEx.parent.buffer[currOffset])) + name := syscall.UTF16ToString((*[syscall.MAX_LONG_PATH]uint16)(unsafe.Pointer(&raw.FileName))[:raw.FileNameLength>>1]) + events = append(events, &event{ + pathw: overEx.parent.pathw, + filter: overEx.parent.filter, + action: raw.Action, + name: name, + }) + if raw.NextEntryOffset == 0 { + break + } + if currOffset += raw.NextEntryOffset; currOffset >= n { + break + } + } + r.send(events) +} + +// TODO(pknap) : doc +func (r *readdcw) send(es []*event) { + for _, e := range es { + var syse Event + if e.e, syse = decode(e.filter, e.action); e.e == 0 && syse == 0 { + continue + } + switch { + case e.action == syscall.FILE_ACTION_MODIFIED: + e.ftype = fTypeUnknown + case e.filter&uint32(dirmarker) != 0: + e.ftype = fTypeDirectory + default: + e.ftype = fTypeFile + } + switch { + case e.e == 0: + e.e = syse + case syse != 0: + r.c <- &event{ + pathw: e.pathw, + name: e.name, + ftype: e.ftype, + action: e.action, + filter: e.filter, + e: syse, + } + } + r.c <- e + } +} + +// Rewatch implements notify.Rewatcher interface. +func (r *readdcw) Rewatch(path string, oldevent, newevent Event) error { + return r.rewatch(path, uint32(oldevent), uint32(newevent), false) +} + +// RecursiveRewatch implements notify.RecursiveRewatcher interface. +func (r *readdcw) RecursiveRewatch(oldpath, newpath string, oldevent, + newevent Event) error { + if oldpath != newpath { + if err := r.unwatch(oldpath); err != nil { + return err + } + return r.watch(newpath, newevent, true) + } + return r.rewatch(newpath, uint32(oldevent), uint32(newevent), true) +} + +// TODO : (pknap) doc. +func (r *readdcw) rewatch(path string, oldevent, newevent uint32, recursive bool) (err error) { + if Event(newevent)&^(All|fileNotifyChangeAll) != 0 { + return errors.New("notify: unknown event") + } + var wd *watched + r.Lock() + if wd, err = r.nonStateWatched(path); err != nil { + r.Unlock() + return + } + if wd.filter&(onlyNotifyChanges|onlyNGlobalEvents) != oldevent { + panic(`notify: windows re-watcher logic error`) + } + wd.filter = stateRewatch | newevent + wd.recursive, recursive = recursive, wd.recursive + if err = wd.closeHandle(); err != nil { + wd.filter = oldevent + wd.recursive = recursive + r.Unlock() + return + } + r.Unlock() + return +} + +// TODO : pknap +func (r *readdcw) nonStateWatched(path string) (wd *watched, err error) { + wd, ok := r.m[path] + if !ok || wd == nil { + err = errors.New(`notify: ` + path + ` path is unwatched`) + return + } + if filter := atomic.LoadUint32(&wd.filter); filter&onlyMachineStates != 0 { + err = errors.New(`notify: another re/unwatching operation in progress`) + return + } + return +} + +// Unwatch implements notify.Watcher interface. +func (r *readdcw) Unwatch(path string) error { + return r.unwatch(path) +} + +// RecursiveUnwatch implements notify.RecursiveWatcher interface. +func (r *readdcw) RecursiveUnwatch(path string) error { + return r.unwatch(path) +} + +// TODO : pknap +func (r *readdcw) unwatch(path string) (err error) { + var wd *watched + r.Lock() + if wd, err = r.nonStateWatched(path); err != nil { + r.Unlock() + return + } + wd.filter |= stateUnwatch + if err = wd.closeHandle(); err != nil { + wd.filter &^= stateUnwatch + r.Unlock() + return + } + r.Unlock() + return +} + +// Close resets the whole watcher object, closes all existing file descriptors, +// and sends stateCPClose state as completion key to the main watcher's loop. +func (r *readdcw) Close() (err error) { + r.Lock() + if !r.start { + r.Unlock() + return nil + } + for _, wd := range r.m { + wd.filter &^= onlyMachineStates + wd.filter |= stateCPClose + if e := wd.closeHandle(); e != nil && err == nil { + err = e + } + } + r.start = false + r.Unlock() + r.wg.Add(1) + if e := syscall.PostQueuedCompletionStatus(r.cph, 0, stateCPClose, nil); e != nil && err == nil { + return e + } + r.wg.Wait() + return +} + +// decode creates a notify event from both non-raw filter and action which was +// returned from completion routine. Function may return Event(0) in case when +// filter was replaced by a new value which does not contain fields that are +// valid with passed action. +func decode(filter, action uint32) (Event, Event) { + switch action { + case syscall.FILE_ACTION_ADDED: + return gensys(filter, Create, FileActionAdded) + case syscall.FILE_ACTION_REMOVED: + return gensys(filter, Remove, FileActionRemoved) + case syscall.FILE_ACTION_MODIFIED: + return gensys(filter, Write, FileActionModified) + case syscall.FILE_ACTION_RENAMED_OLD_NAME: + return gensys(filter, Rename, FileActionRenamedOldName) + case syscall.FILE_ACTION_RENAMED_NEW_NAME: + return gensys(filter, Rename, FileActionRenamedNewName) + } + panic(`notify: cannot decode internal mask`) +} + +// gensys decides whether the Windows action, system-independent event or both +// of them should be returned. Since the grip's filter may be atomically changed +// during watcher lifetime, it is possible that neither Windows nor notify masks +// are watched by the user when this function is called. +func gensys(filter uint32, ge, se Event) (gene, syse Event) { + isdir := filter&uint32(dirmarker) != 0 + if isdir && filter&uint32(FileNotifyChangeDirName) != 0 || + !isdir && filter&uint32(FileNotifyChangeFileName) != 0 || + filter&uint32(fileNotifyChangeModified) != 0 { + syse = se + } + if filter&uint32(ge) != 0 { + gene = ge + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_stub.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_stub.go new file mode 100644 index 0000000..68b9c13 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_stub.go @@ -0,0 +1,23 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build !darwin,!linux,!freebsd,!dragonfly,!netbsd,!openbsd,!windows +// +build !kqueue,!solaris + +package notify + +import "errors" + +type stub struct{ error } + +// newWatcher stub. +func newWatcher(chan<- EventInfo) watcher { + return stub{errors.New("notify: not implemented")} +} + +// Following methods implement notify.watcher interface. +func (s stub) Watch(string, Event) error { return s } +func (s stub) Rewatch(string, Event, Event) error { return s } +func (s stub) Unwatch(string) (err error) { return s } +func (s stub) Close() error { return s } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_trigger.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_trigger.go new file mode 100644 index 0000000..d079d59 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watcher_trigger.go @@ -0,0 +1,432 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build darwin,kqueue dragonfly freebsd netbsd openbsd solaris + +// watcher_trigger is used for FEN and kqueue which behave similarly: +// only files and dirs can be watched directly, but not files inside dirs. +// As a result Create events have to be generated by implementation when +// after Write event is returned for watched dir, it is rescanned and Create +// event is returned for new files and these are automatically added +// to watchlist. In case of removal of watched directory, native system returns +// events for all files, but for Rename, they also need to be generated. +// As a result native system works as something like trigger for rescan, +// but contains additional data about dir in which changes occurred. For files +// detailed data is returned. +// Usage of watcher_trigger requires: +// - trigger implementation, +// - encode func, +// - not2nat, nat2not maps. +// Required manual operations on filesystem can lead to loss of precision. + +package notify + +import ( + "os" + "path/filepath" + "strings" + "sync" + "syscall" +) + +// trigger is to be implemented by platform implementation like FEN or kqueue. +type trigger interface { + // Close closes watcher's main native file descriptor. + Close() error + // Stop waiting for new events. + Stop() error + // Create new instance of watched. + NewWatched(string, os.FileInfo) (*watched, error) + // Record internally new *watched instance. + Record(*watched) + // Del removes internal copy of *watched instance. + Del(*watched) + // Watched returns *watched instance and native events for native type. + Watched(interface{}) (*watched, int64, error) + // Init initializes native watcher call. + Init() error + // Watch starts watching provided file/dir. + Watch(os.FileInfo, *watched, int64) error + // Unwatch stops watching provided file/dir. + Unwatch(*watched) error + // Wait for new events. + Wait() (interface{}, error) + // IsStop checks if Wait finished because of request watcher's stop. + IsStop(n interface{}, err error) bool +} + +// encode Event to native representation. Implementation is to be provided by +// platform specific implementation. +var encode func(Event, bool) int64 + +var ( + // nat2not matches native events to notify's ones. To be initialized by + // platform dependent implementation. + nat2not map[Event]Event + // not2nat matches notify's events to native ones. To be initialized by + // platform dependent implementation. + not2nat map[Event]Event +) + +// trg is a main structure implementing watcher. +type trg struct { + sync.Mutex + // s is a channel used to stop monitoring. + s chan struct{} + // c is a channel used to pass events further. + c chan<- EventInfo + // pthLkp is a data structure mapping file names with data about watching + // represented by them files/directories. + pthLkp map[string]*watched + // t is a platform dependent implementation of trigger. + t trigger +} + +// newWatcher returns new watcher's implementation. +func newWatcher(c chan<- EventInfo) watcher { + t := &trg{ + s: make(chan struct{}, 1), + pthLkp: make(map[string]*watched, 0), + c: c, + } + t.t = newTrigger(t.pthLkp) + if err := t.t.Init(); err != nil { + panic(err) + } + go t.monitor() + return t +} + +// Close implements watcher. +func (t *trg) Close() (err error) { + t.Lock() + if err = t.t.Stop(); err != nil { + t.Unlock() + return + } + <-t.s + var e error + for _, w := range t.pthLkp { + if e = t.unwatch(w.p, w.fi); e != nil { + dbgprintf("trg: unwatch %q failed: %q\n", w.p, e) + err = nonil(err, e) + } + } + if e = t.t.Close(); e != nil { + dbgprintf("trg: closing native watch failed: %q\n", e) + err = nonil(err, e) + } + t.Unlock() + return +} + +// send reported events one by one through chan. +func (t *trg) send(evn []event) { + for i := range evn { + t.c <- &evn[i] + } +} + +// singlewatch starts to watch given p file/directory. +func (t *trg) singlewatch(p string, e Event, direct mode, fi os.FileInfo) (err error) { + w, ok := t.pthLkp[p] + if !ok { + if w, err = t.t.NewWatched(p, fi); err != nil { + return + } + } + switch direct { + case dir: + w.eDir |= e + case ndir: + w.eNonDir |= e + case both: + w.eDir |= e + w.eNonDir |= e + } + if err = t.t.Watch(fi, w, encode(w.eDir|w.eNonDir, fi.IsDir())); err != nil { + return + } + if !ok { + t.t.Record(w) + return nil + } + return errAlreadyWatched +} + +// decode converts event received from native to notify.Event +// representation taking into account requested events (w). +func decode(o int64, w Event) (e Event) { + for f, n := range nat2not { + if o&int64(f) != 0 { + if w&f != 0 { + e |= f + } + if w&n != 0 { + e |= n + } + } + } + + return +} + +func (t *trg) watch(p string, e Event, fi os.FileInfo) error { + if err := t.singlewatch(p, e, dir, fi); err != nil { + if err != errAlreadyWatched { + return nil + } + } + if fi.IsDir() { + err := t.walk(p, func(fi os.FileInfo) (err error) { + if err = t.singlewatch(filepath.Join(p, fi.Name()), e, ndir, + fi); err != nil { + if err != errAlreadyWatched { + return + } + } + return nil + }) + if err != nil { + return err + } + } + return nil +} + +// walk runs f func on each file/dir from p directory. +func (t *trg) walk(p string, fn func(os.FileInfo) error) error { + fp, err := os.Open(p) + if err != nil { + return err + } + ls, err := fp.Readdir(0) + fp.Close() + if err != nil { + return err + } + for i := range ls { + if err := fn(ls[i]); err != nil { + return err + } + } + return nil +} + +func (t *trg) unwatch(p string, fi os.FileInfo) error { + if fi.IsDir() { + err := t.walk(p, func(fi os.FileInfo) error { + err := t.singleunwatch(filepath.Join(p, fi.Name()), ndir) + if err != errNotWatched { + return err + } + return nil + }) + if err != nil { + return err + } + } + return t.singleunwatch(p, dir) +} + +// Watch implements Watcher interface. +func (t *trg) Watch(p string, e Event) error { + fi, err := os.Stat(p) + if err != nil { + return err + } + t.Lock() + err = t.watch(p, e, fi) + t.Unlock() + return err +} + +// Unwatch implements Watcher interface. +func (t *trg) Unwatch(p string) error { + fi, err := os.Stat(p) + if err != nil { + return err + } + t.Lock() + err = t.unwatch(p, fi) + t.Unlock() + return err +} + +// Rewatch implements Watcher interface. +// +// TODO(rjeczalik): This is a naive hack. Rewrite might help. +func (t *trg) Rewatch(p string, _, e Event) error { + fi, err := os.Stat(p) + if err != nil { + return err + } + t.Lock() + if err = t.unwatch(p, fi); err == nil { + // TODO(rjeczalik): If watch fails then we leave trigger in inconsistent + // state. Handle? Panic? Native version of rewatch? + err = t.watch(p, e, fi) + } + t.Unlock() + return nil +} + +func (*trg) file(w *watched, n interface{}, e Event) (evn []event) { + evn = append(evn, event{w.p, e, w.fi.IsDir(), n}) + return +} + +func (t *trg) dir(w *watched, n interface{}, e, ge Event) (evn []event) { + // If it's dir and delete we have to send it and continue, because + // other processing relies on opening (in this case not existing) dir. + // Events for contents of this dir are reported by native impl. + // However events for rename must be generated for all monitored files + // inside of moved directory, because native impl does not report it independently + // for each file descriptor being moved in result of move action on + // parent directory. + if (ge & (not2nat[Rename] | not2nat[Remove])) != 0 { + // Write is reported also for Remove on directory. Because of that + // we have to filter it out explicitly. + evn = append(evn, event{w.p, e & ^Write & ^not2nat[Write], true, n}) + if ge¬2nat[Rename] != 0 { + for p := range t.pthLkp { + if strings.HasPrefix(p, w.p+string(os.PathSeparator)) { + if err := t.singleunwatch(p, both); err != nil && err != errNotWatched && + !os.IsNotExist(err) { + dbgprintf("trg: failed stop watching moved file (%q): %q\n", + p, err) + } + if (w.eDir|w.eNonDir)&(not2nat[Rename]|Rename) != 0 { + evn = append(evn, event{ + p, (w.eDir | w.eNonDir) & e &^ Write &^ not2nat[Write], + w.fi.IsDir(), nil, + }) + } + } + } + } + t.t.Del(w) + return + } + if (ge & not2nat[Write]) != 0 { + switch err := t.walk(w.p, func(fi os.FileInfo) error { + p := filepath.Join(w.p, fi.Name()) + switch err := t.singlewatch(p, w.eDir, ndir, fi); { + case os.IsNotExist(err) && ((w.eDir & Remove) != 0): + evn = append(evn, event{p, Remove, fi.IsDir(), n}) + case err == errAlreadyWatched: + case err != nil: + dbgprintf("trg: watching %q failed: %q", p, err) + case (w.eDir & Create) != 0: + evn = append(evn, event{p, Create, fi.IsDir(), n}) + default: + } + return nil + }); { + case os.IsNotExist(err): + return + case err != nil: + dbgprintf("trg: dir processing failed: %q", err) + default: + } + } + return +} + +type mode uint + +const ( + dir mode = iota + ndir + both +) + +// unwatch stops watching p file/directory. +func (t *trg) singleunwatch(p string, direct mode) error { + w, ok := t.pthLkp[p] + if !ok { + return errNotWatched + } + switch direct { + case dir: + w.eDir = 0 + case ndir: + w.eNonDir = 0 + case both: + w.eDir, w.eNonDir = 0, 0 + } + if err := t.t.Unwatch(w); err != nil { + return err + } + if w.eNonDir|w.eDir != 0 { + mod := dir + if w.eNonDir == 0 { + mod = ndir + } + if err := t.singlewatch(p, w.eNonDir|w.eDir, mod, + w.fi); err != nil && err != errAlreadyWatched { + return err + } + } else { + t.t.Del(w) + } + return nil +} + +func (t *trg) monitor() { + var ( + n interface{} + err error + ) + for { + switch n, err = t.t.Wait(); { + case err == syscall.EINTR: + case t.t.IsStop(n, err): + t.s <- struct{}{} + return + case err != nil: + dbgprintf("trg: failed to read events: %q\n", err) + default: + t.send(t.process(n)) + } + } +} + +// process event returned by native call. +func (t *trg) process(n interface{}) (evn []event) { + t.Lock() + w, ge, err := t.t.Watched(n) + if err != nil { + t.Unlock() + dbgprintf("trg: %v event lookup failed: %q", Event(ge), err) + return + } + + e := decode(ge, w.eDir|w.eNonDir) + if ge&int64(not2nat[Remove]|not2nat[Rename]) == 0 { + switch fi, err := os.Stat(w.p); { + case err != nil: + default: + if err = t.t.Watch(fi, w, encode(w.eDir|w.eNonDir, fi.IsDir())); err != nil { + dbgprintf("trg: %q is no longer watched: %q", w.p, err) + t.t.Del(w) + } + } + } + if e == Event(0) && (!w.fi.IsDir() || (ge&int64(not2nat[Write])) == 0) { + t.Unlock() + return + } + + if w.fi.IsDir() { + evn = append(evn, t.dir(w, n, e, Event(ge))...) + } else { + evn = append(evn, t.file(w, n, e)...) + } + if Event(ge)&(not2nat[Remove]|not2nat[Rename]) != 0 { + t.t.Del(w) + } + t.Unlock() + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint.go new file mode 100644 index 0000000..5afc914 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint.go @@ -0,0 +1,103 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +package notify + +// EventDiff describes a change to an event set - EventDiff[0] is an old state, +// while EventDiff[1] is a new state. If event set has not changed (old == new), +// functions typically return the None value. +type eventDiff [2]Event + +func (diff eventDiff) Event() Event { + return diff[1] &^ diff[0] +} + +// Watchpoint +// +// The nil key holds total event set - logical sum for all registered events. +// It speeds up computing EventDiff for Add method. +// +// The rec key holds an event set for a watchpoints created by RecursiveWatch +// for a Watcher implementation which is not natively recursive. +type watchpoint map[chan<- EventInfo]Event + +// None is an empty event diff, think null object. +var none eventDiff + +// rec is just a placeholder +var rec = func() (ch chan<- EventInfo) { + ch = make(chan<- EventInfo) + close(ch) + return +}() + +func (wp watchpoint) dryAdd(ch chan<- EventInfo, e Event) eventDiff { + if e &^= internal; wp[ch]&e == e { + return none + } + total := wp[ch] &^ internal + return eventDiff{total, total | e} +} + +// Add assumes neither c nor e are nil or zero values. +func (wp watchpoint) Add(c chan<- EventInfo, e Event) (diff eventDiff) { + wp[c] |= e + diff[0] = wp[nil] + diff[1] = diff[0] | e + wp[nil] = diff[1] &^ omit + // Strip diff from internal events. + diff[0] &^= internal + diff[1] &^= internal + if diff[0] == diff[1] { + return none + } + return +} + +func (wp watchpoint) Del(c chan<- EventInfo, e Event) (diff eventDiff) { + wp[c] &^= e + if wp[c] == 0 { + delete(wp, c) + } + diff[0] = wp[nil] + delete(wp, nil) + if len(wp) != 0 { + // Recalculate total event set. + for _, e := range wp { + diff[1] |= e + } + wp[nil] = diff[1] &^ omit + } + // Strip diff from internal events. + diff[0] &^= internal + diff[1] &^= internal + if diff[0] == diff[1] { + return none + } + return +} + +func (wp watchpoint) Dispatch(ei EventInfo, extra Event) { + e := eventmask(ei, extra) + if !matches(wp[nil], e) { + return + } + for ch, eset := range wp { + if ch != nil && matches(eset, e) { + select { + case ch <- ei: + default: // Drop event if receiver is too slow + dbgprintf("dropped %s on %q: receiver too slow", ei.Event(), ei.Path()) + } + } + } +} + +func (wp watchpoint) Total() Event { + return wp[nil] &^ internal +} + +func (wp watchpoint) IsRecursive() bool { + return wp[nil]&recursive != 0 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_other.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_other.go new file mode 100644 index 0000000..9bb381d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_other.go @@ -0,0 +1,23 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build !windows + +package notify + +// eventmask uses ei to create a new event which contains internal flags used by +// notify package logic. +func eventmask(ei EventInfo, extra Event) Event { + return ei.Event() | extra +} + +// matches reports a match only when: +// +// - for user events, when event is present in the given set +// - for internal events, when additionally both event and set have omit bit set +// +// Internal events must not be sent to user channels and vice versa. +func matches(set, event Event) bool { + return (set&omit)^(event&omit) == 0 && set&event == event +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_readdcw.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_readdcw.go new file mode 100644 index 0000000..9fd1e1d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rjeczalik/notify/watchpoint_readdcw.go @@ -0,0 +1,38 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// +build windows + +package notify + +// eventmask uses ei to create a new event which contains internal flags used by +// notify package logic. If one of FileAction* masks is detected, this function +// adds corresponding FileNotifyChange* values. This allows non registered +// FileAction* events to be passed on. +func eventmask(ei EventInfo, extra Event) (e Event) { + if e = ei.Event() | extra; e&fileActionAll != 0 { + if ev, ok := ei.(*event); ok { + switch ev.ftype { + case fTypeFile: + e |= FileNotifyChangeFileName + case fTypeDirectory: + e |= FileNotifyChangeDirName + case fTypeUnknown: + e |= fileNotifyChangeModified + } + return e &^ fileActionAll + } + } + return +} + +// matches reports a match only when: +// +// - for user events, when event is present in the given set +// - for internal events, when additionally both event and set have omit bit set +// +// Internal events must not be sent to user channels and vice versa. +func matches(set, event Event) bool { + return (set&omit)^(event&omit) == 0 && (set&event == event || set&fileNotifyChangeModified&event != 0) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/DESIGN.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/DESIGN.markdown new file mode 100644 index 0000000..2887529 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/DESIGN.markdown @@ -0,0 +1 @@ +* Designate the filename of "anonymous" source code by the hash (md5/sha1, etc.) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/LICENSE new file mode 100644 index 0000000..b6179fe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2012 Robert Krimen + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/Makefile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/Makefile new file mode 100644 index 0000000..9868db3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/Makefile @@ -0,0 +1,63 @@ +.PHONY: test test-race test-release release release-check test-262 +.PHONY: parser +.PHONY: otto assets underscore + +TESTS := \ + ~ + +TEST := -v --run +TEST := -v +TEST := -v --run Test\($(subst $(eval) ,\|,$(TESTS))\) +TEST := . + +test: parser inline.go + go test -i + go test $(TEST) + @echo PASS + +parser: + $(MAKE) -C parser + +inline.go: inline.pl + ./$< > $@ + +################# +# release, test # +################# + +release: test-race test-release + for package in . parser token ast file underscore registry; do (cd $$package && godocdown --signature > README.markdown); done + @echo \*\*\* make release-check + @echo PASS + +release-check: .test + $(MAKE) -C test build test + $(MAKE) -C .test/test262 build test + @echo PASS + +test-262: .test + $(MAKE) -C .test/test262 build test + @echo PASS + +test-release: + go test -i + go test + +test-race: + go test -race -i + go test -race + +################################# +# otto, assets, underscore, ... # +################################# + +otto: + $(MAKE) -C otto + +assets: + mkdir -p .assets + for file in underscore/test/*.js; do tr "\`" "_" < $$file > .assets/`basename $$file`; done + +underscore: + $(MAKE) -C $@ + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/README.markdown new file mode 100644 index 0000000..a1ae7d1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/README.markdown @@ -0,0 +1,871 @@ +# otto +-- +```go +import "github.com/robertkrimen/otto" +``` + +Package otto is a JavaScript parser and interpreter written natively in Go. + +http://godoc.org/github.com/robertkrimen/otto + +```go +import ( + "github.com/robertkrimen/otto" +) +``` + +Run something in the VM + +```go +vm := otto.New() +vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 +`) +``` + +Get a value out of the VM + +```go +if value, err := vm.Get("abc"); err == nil { + if value_int, err := value.ToInteger(); err == nil { + fmt.Printf("", value_int, err) + } +} +``` + +Set a number + +```go +vm.Set("def", 11) +vm.Run(` + console.log("The value of def is " + def); + // The value of def is 11 +`) +``` + +Set a string + +```go +vm.Set("xyzzy", "Nothing happens.") +vm.Run(` + console.log(xyzzy.length); // 16 +`) +``` + +Get the value of an expression + +```go +value, _ = vm.Run("xyzzy.length") +{ + // value is an int64 with a value of 16 + value, _ := value.ToInteger() +} +``` + +An error happens + +```go +value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") +if err != nil { + // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined + // If there is an error, then value.IsUndefined() is true + ... +} +``` + +Set a Go function + +```go +vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return otto.Value{} +}) +``` + +Set a Go function that returns something useful + +```go +vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result +}) +``` + +Use the functions in JavaScript + +```go +result, _ = vm.Run(` + sayHello("Xyzzy"); // Hello, Xyzzy. + sayHello(); // Hello, undefined + + result = twoPlus(2.0); // 4 +`) +``` + +### Parser + +A separate parser is available in the parser package if you're just interested +in building an AST. + +http://godoc.org/github.com/robertkrimen/otto/parser + +Parse and return an AST + +```go +filename := "" // A filename is optional +src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); +` + +// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList +program, err := parser.ParseFile(nil, filename, src, 0) +``` + +### otto + +You can run (Go) JavaScript from the commandline with: +http://github.com/robertkrimen/otto/tree/master/otto + + $ go get -v github.com/robertkrimen/otto/otto + +Run JavaScript by entering some source on stdin or by giving otto a filename: + + $ otto example.js + +### underscore + +Optionally include the JavaScript utility-belt library, underscore, with this +import: + +```go +import ( + "github.com/robertkrimen/otto" + _ "github.com/robertkrimen/otto/underscore" +) + +// Now every otto runtime will come loaded with underscore +``` + +For more information: http://github.com/robertkrimen/otto/tree/master/underscore + + +### Caveat Emptor + +The following are some limitations with otto: + + * "use strict" will parse, but does nothing. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. + + +### Regular Expression Incompatibility + +Go translates JavaScript-style regular expressions into something that is +"regexp" compatible via `parser.TransformRegExp`. Unfortunately, RegExp requires +backtracking for some patterns, and backtracking is not supported by the +standard Go engine: https://code.google.com/p/re2/wiki/Syntax + +Therefore, the following syntax is incompatible: + + (?=) // Lookahead (positive), currently a parsing error + (?!) // Lookahead (backhead), currently a parsing error + \1 // Backreference (\1, \2, \3, ...), currently a parsing error + +A brief discussion of these limitations: "Regexp (?!re)" +https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E + +More information about re2: https://code.google.com/p/re2/ + +In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r +]. The JavaScript definition, on the other hand, also includes \v, Unicode +"Separator, Space", etc. + + +### Halting Problem + +If you want to stop long running executions (like third-party code), you can use +the interrupt channel to do this: + +```go +package main + +import ( + "errors" + "fmt" + "os" + "time" + + "github.com/robertkrimen/otto" +) + +var halt = errors.New("Stahp") + +func main() { + runUnsafe(`var abc = [];`) + runUnsafe(` + while (true) { + // Loop forever + }`) +} + +func runUnsafe(unsafe string) { + start := time.Now() + defer func() { + duration := time.Since(start) + if caught := recover(); caught != nil { + if caught == halt { + fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) + return + } + panic(caught) // Something else happened, repanic! + } + fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) + }() + + vm := otto.New() + vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking + + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + + vm.Run(unsafe) // Here be dragons (risky code) +} +``` + +Where is setTimeout/setInterval? + +These timing functions are not actually part of the ECMA-262 specification. +Typically, they belong to the `window` object (in the browser). It would not be +difficult to provide something like these via Go, but you probably want to wrap +otto in an event loop in that case. + +For an example of how this could be done in Go with otto, see natto: + +http://github.com/robertkrimen/natto + +Here is some more discussion of the issue: + +* http://book.mixu.net/node/ch2.html + +* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 + +* http://aaroncrane.co.uk/2009/02/perl_safe_signals/ + +## Usage + +```go +var ErrVersion = errors.New("version mismatch") +``` + +#### type Error + +```go +type Error struct { +} +``` + +An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. + +#### func (Error) Error + +```go +func (err Error) Error() string +``` +Error returns a description of the error + + TypeError: 'def' is not a function + +#### func (Error) String + +```go +func (err Error) String() string +``` +String returns a description of the error and a trace of where the error +occurred. + + TypeError: 'def' is not a function + at xyz (:3:9) + at :7:1/ + +#### type FunctionCall + +```go +type FunctionCall struct { + This Value + ArgumentList []Value + Otto *Otto +} +``` + +FunctionCall is an encapsulation of a JavaScript function call. + +#### func (FunctionCall) Argument + +```go +func (self FunctionCall) Argument(index int) Value +``` +Argument will return the value of the argument at the given index. + +If no such argument exists, undefined is returned. + +#### type Object + +```go +type Object struct { +} +``` + +Object is the representation of a JavaScript object. + +#### func (Object) Call + +```go +func (self Object) Call(name string, argumentList ...interface{}) (Value, error) +``` +Call a method on the object. + +It is essentially equivalent to: + + var method, _ := object.Get(name) + method.Call(object, argumentList...) + +An undefined value and an error will result if: + + 1. There is an error during conversion of the argument list + 2. The property is not actually a function + 3. An (uncaught) exception is thrown + +#### func (Object) Class + +```go +func (self Object) Class() string +``` +Class will return the class string of the object. + +The return value will (generally) be one of: + + Object + Function + Array + String + Number + Boolean + Date + RegExp + +#### func (Object) Get + +```go +func (self Object) Get(name string) (Value, error) +``` +Get the value of the property with the given name. + +#### func (Object) Keys + +```go +func (self Object) Keys() []string +``` +Get the keys for the object + +Equivalent to calling Object.keys on the object + +#### func (Object) Set + +```go +func (self Object) Set(name string, value interface{}) error +``` +Set the property of the given name to the given value. + +An error will result if the setting the property triggers an exception (i.e. +read-only), or there is an error during conversion of the given value. + +#### func (Object) Value + +```go +func (self Object) Value() Value +``` +Value will return self as a value. + +#### type Otto + +```go +type Otto struct { + // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. + // See "Halting Problem" for more information. + Interrupt chan func() +} +``` + +Otto is the representation of the JavaScript runtime. Each instance of Otto has +a self-contained namespace. + +#### func New + +```go +func New() *Otto +``` +New will allocate a new JavaScript runtime + +#### func Run + +```go +func Run(src interface{}) (*Otto, Value, error) +``` +Run will allocate a new JavaScript runtime, run the given source on the +allocated runtime, and return the runtime, resulting value, and error (if any). + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + +src may also be a Script. + +src may also be a Program, but if the AST has been modified, then runtime +behavior is undefined. + +#### func (Otto) Call + +```go +func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) +``` +Call the given JavaScript with a given this and arguments. + +If this is nil, then some special handling takes place to determine the proper +this value, falling back to a "standard" invocation if necessary (where this is +undefined). + +If source begins with "new " (A lowercase new followed by a space), then Call +will invoke the function constructor rather than performing a function call. In +this case, the this argument has no effect. + +```go +// value is a String object +value, _ := vm.Call("Object", nil, "Hello, World.") + +// Likewise... +value, _ := vm.Call("new Object", nil, "Hello, World.") + +// This will perform a concat on the given array and return the result +// value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] +value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") +``` + +#### func (*Otto) Compile + +```go +func (self *Otto) Compile(filename string, src interface{}) (*Script, error) +``` +Compile will parse the given source and return a Script value or nil and an +error if there was a problem during compilation. + +```go +script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) +vm.Run(script) +``` + +#### func (*Otto) Copy + +```go +func (in *Otto) Copy() *Otto +``` +Copy will create a copy/clone of the runtime. + +Copy is useful for saving some time when creating many similar runtimes. + +This method works by walking the original runtime and cloning each object, +scope, stash, etc. into a new runtime. + +Be on the lookout for memory leaks or inadvertent sharing of resources. + +#### func (Otto) Get + +```go +func (self Otto) Get(name string) (Value, error) +``` +Get the value of the top-level binding of the given name. + +If there is an error (like the binding does not exist), then the value will be +undefined. + +#### func (Otto) Object + +```go +func (self Otto) Object(source string) (*Object, error) +``` +Object will run the given source and return the result as an object. + +For example, accessing an existing object: + +```go +object, _ := vm.Object(`Number`) +``` + +Or, creating a new object: + +```go +object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) +``` + +Or, creating and assigning an object: + +```go +object, _ := vm.Object(`xyzzy = {}`) +object.Set("volume", 11) +``` + +If there is an error (like the source does not result in an object), then nil +and an error is returned. + +#### func (Otto) Run + +```go +func (self Otto) Run(src interface{}) (Value, error) +``` +Run will run the given source (parsing it first if necessary), returning the +resulting value and error (if any) + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + +If the runtime is unable to parse source, then this function will return +undefined and the parse error (nothing will be evaluated in this case). + +src may also be a Script. + +src may also be a Program, but if the AST has been modified, then runtime +behavior is undefined. + +#### func (Otto) Set + +```go +func (self Otto) Set(name string, value interface{}) error +``` +Set the top-level binding of the given name to the given value. + +Set will automatically apply ToValue to the given value in order to convert it +to a JavaScript value (type Value). + +If there is an error (like the binding is read-only, or the ToValue conversion +fails), then an error is returned. + +If the top-level binding does not exist, it will be created. + +#### func (Otto) ToValue + +```go +func (self Otto) ToValue(value interface{}) (Value, error) +``` +ToValue will convert an interface{} value to a value digestible by +otto/JavaScript. + +#### type Script + +```go +type Script struct { +} +``` + +Script is a handle for some (reusable) JavaScript. Passing a Script value to a +run method will evaluate the JavaScript. + +#### func (*Script) String + +```go +func (self *Script) String() string +``` + +#### type Value + +```go +type Value struct { +} +``` + +Value is the representation of a JavaScript value. + +#### func FalseValue + +```go +func FalseValue() Value +``` +FalseValue will return a value representing false. + +It is equivalent to: + +```go +ToValue(false) +``` + +#### func NaNValue + +```go +func NaNValue() Value +``` +NaNValue will return a value representing NaN. + +It is equivalent to: + +```go +ToValue(math.NaN()) +``` + +#### func NullValue + +```go +func NullValue() Value +``` +NullValue will return a Value representing null. + +#### func ToValue + +```go +func ToValue(value interface{}) (Value, error) +``` +ToValue will convert an interface{} value to a value digestible by +otto/JavaScript + +This function will not work for advanced types (struct, map, slice/array, etc.) +and you should use Otto.ToValue instead. + +#### func TrueValue + +```go +func TrueValue() Value +``` +TrueValue will return a value representing true. + +It is equivalent to: + +```go +ToValue(true) +``` + +#### func UndefinedValue + +```go +func UndefinedValue() Value +``` +UndefinedValue will return a Value representing undefined. + +#### func (Value) Call + +```go +func (value Value) Call(this Value, argumentList ...interface{}) (Value, error) +``` +Call the value as a function with the given this value and argument list and +return the result of invocation. It is essentially equivalent to: + + value.apply(thisValue, argumentList) + +An undefined value and an error will result if: + + 1. There is an error during conversion of the argument list + 2. The value is not actually a function + 3. An (uncaught) exception is thrown + +#### func (Value) Class + +```go +func (value Value) Class() string +``` +Class will return the class string of the value or the empty string if value is +not an object. + +The return value will (generally) be one of: + + Object + Function + Array + String + Number + Boolean + Date + RegExp + +#### func (Value) Export + +```go +func (self Value) Export() (interface{}, error) +``` +Export will attempt to convert the value to a Go representation and return it +via an interface{} kind. + +Export returns an error, but it will always be nil. It is present for backwards +compatibility. + +If a reasonable conversion is not possible, then the original value is returned. + + undefined -> nil (FIXME?: Should be Value{}) + null -> nil + boolean -> bool + number -> A number type (int, float32, uint64, ...) + string -> string + Array -> []interface{} + Object -> map[string]interface{} + +#### func (Value) IsBoolean + +```go +func (value Value) IsBoolean() bool +``` +IsBoolean will return true if value is a boolean (primitive). + +#### func (Value) IsDefined + +```go +func (value Value) IsDefined() bool +``` +IsDefined will return false if the value is undefined, and true otherwise. + +#### func (Value) IsFunction + +```go +func (value Value) IsFunction() bool +``` +IsFunction will return true if value is a function. + +#### func (Value) IsNaN + +```go +func (value Value) IsNaN() bool +``` +IsNaN will return true if value is NaN (or would convert to NaN). + +#### func (Value) IsNull + +```go +func (value Value) IsNull() bool +``` +IsNull will return true if the value is null, and false otherwise. + +#### func (Value) IsNumber + +```go +func (value Value) IsNumber() bool +``` +IsNumber will return true if value is a number (primitive). + +#### func (Value) IsObject + +```go +func (value Value) IsObject() bool +``` +IsObject will return true if value is an object. + +#### func (Value) IsPrimitive + +```go +func (value Value) IsPrimitive() bool +``` +IsPrimitive will return true if value is a primitive (any kind of primitive). + +#### func (Value) IsString + +```go +func (value Value) IsString() bool +``` +IsString will return true if value is a string (primitive). + +#### func (Value) IsUndefined + +```go +func (value Value) IsUndefined() bool +``` +IsUndefined will return true if the value is undefined, and false otherwise. + +#### func (Value) Object + +```go +func (value Value) Object() *Object +``` +Object will return the object of the value, or nil if value is not an object. + +This method will not do any implicit conversion. For example, calling this +method on a string primitive value will not return a String object. + +#### func (Value) String + +```go +func (value Value) String() string +``` +String will return the value as a string. + +This method will make return the empty string if there is an error. + +#### func (Value) ToBoolean + +```go +func (value Value) ToBoolean() (bool, error) +``` +ToBoolean will convert the value to a boolean (bool). + + ToValue(0).ToBoolean() => false + ToValue("").ToBoolean() => false + ToValue(true).ToBoolean() => true + ToValue(1).ToBoolean() => true + ToValue("Nothing happens").ToBoolean() => true + +If there is an error during the conversion process (like an uncaught exception), +then the result will be false and an error. + +#### func (Value) ToFloat + +```go +func (value Value) ToFloat() (float64, error) +``` +ToFloat will convert the value to a number (float64). + + ToValue(0).ToFloat() => 0. + ToValue(1.1).ToFloat() => 1.1 + ToValue("11").ToFloat() => 11. + +If there is an error during the conversion process (like an uncaught exception), +then the result will be 0 and an error. + +#### func (Value) ToInteger + +```go +func (value Value) ToInteger() (int64, error) +``` +ToInteger will convert the value to a number (int64). + + ToValue(0).ToInteger() => 0 + ToValue(1.1).ToInteger() => 1 + ToValue("11").ToInteger() => 11 + +If there is an error during the conversion process (like an uncaught exception), +then the result will be 0 and an error. + +#### func (Value) ToString + +```go +func (value Value) ToString() (string, error) +``` +ToString will convert the value to a string (string). + + ToValue(0).ToString() => "0" + ToValue(false).ToString() => "false" + ToValue(1.1).ToString() => "1.1" + ToValue("11").ToString() => "11" + ToValue('Nothing happens.').ToString() => "Nothing happens." + +If there is an error during the conversion process (like an uncaught exception), +then the result will be the empty string ("") and an error. + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/README.markdown new file mode 100644 index 0000000..a785da9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/README.markdown @@ -0,0 +1,1068 @@ +# ast +-- + import "github.com/robertkrimen/otto/ast" + +Package ast declares types representing a JavaScript AST. + + +### Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +## Usage + +#### type ArrayLiteral + +```go +type ArrayLiteral struct { + LeftBracket file.Idx + RightBracket file.Idx + Value []Expression +} +``` + + +#### func (*ArrayLiteral) Idx0 + +```go +func (self *ArrayLiteral) Idx0() file.Idx +``` + +#### func (*ArrayLiteral) Idx1 + +```go +func (self *ArrayLiteral) Idx1() file.Idx +``` + +#### type AssignExpression + +```go +type AssignExpression struct { + Operator token.Token + Left Expression + Right Expression +} +``` + + +#### func (*AssignExpression) Idx0 + +```go +func (self *AssignExpression) Idx0() file.Idx +``` + +#### func (*AssignExpression) Idx1 + +```go +func (self *AssignExpression) Idx1() file.Idx +``` + +#### type BadExpression + +```go +type BadExpression struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadExpression) Idx0 + +```go +func (self *BadExpression) Idx0() file.Idx +``` + +#### func (*BadExpression) Idx1 + +```go +func (self *BadExpression) Idx1() file.Idx +``` + +#### type BadStatement + +```go +type BadStatement struct { + From file.Idx + To file.Idx +} +``` + + +#### func (*BadStatement) Idx0 + +```go +func (self *BadStatement) Idx0() file.Idx +``` + +#### func (*BadStatement) Idx1 + +```go +func (self *BadStatement) Idx1() file.Idx +``` + +#### type BinaryExpression + +```go +type BinaryExpression struct { + Operator token.Token + Left Expression + Right Expression + Comparison bool +} +``` + + +#### func (*BinaryExpression) Idx0 + +```go +func (self *BinaryExpression) Idx0() file.Idx +``` + +#### func (*BinaryExpression) Idx1 + +```go +func (self *BinaryExpression) Idx1() file.Idx +``` + +#### type BlockStatement + +```go +type BlockStatement struct { + LeftBrace file.Idx + List []Statement + RightBrace file.Idx +} +``` + + +#### func (*BlockStatement) Idx0 + +```go +func (self *BlockStatement) Idx0() file.Idx +``` + +#### func (*BlockStatement) Idx1 + +```go +func (self *BlockStatement) Idx1() file.Idx +``` + +#### type BooleanLiteral + +```go +type BooleanLiteral struct { + Idx file.Idx + Literal string + Value bool +} +``` + + +#### func (*BooleanLiteral) Idx0 + +```go +func (self *BooleanLiteral) Idx0() file.Idx +``` + +#### func (*BooleanLiteral) Idx1 + +```go +func (self *BooleanLiteral) Idx1() file.Idx +``` + +#### type BracketExpression + +```go +type BracketExpression struct { + Left Expression + Member Expression + LeftBracket file.Idx + RightBracket file.Idx +} +``` + + +#### func (*BracketExpression) Idx0 + +```go +func (self *BracketExpression) Idx0() file.Idx +``` + +#### func (*BracketExpression) Idx1 + +```go +func (self *BracketExpression) Idx1() file.Idx +``` + +#### type BranchStatement + +```go +type BranchStatement struct { + Idx file.Idx + Token token.Token + Label *Identifier +} +``` + + +#### func (*BranchStatement) Idx0 + +```go +func (self *BranchStatement) Idx0() file.Idx +``` + +#### func (*BranchStatement) Idx1 + +```go +func (self *BranchStatement) Idx1() file.Idx +``` + +#### type CallExpression + +```go +type CallExpression struct { + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*CallExpression) Idx0 + +```go +func (self *CallExpression) Idx0() file.Idx +``` + +#### func (*CallExpression) Idx1 + +```go +func (self *CallExpression) Idx1() file.Idx +``` + +#### type CaseStatement + +```go +type CaseStatement struct { + Case file.Idx + Test Expression + Consequent []Statement +} +``` + + +#### func (*CaseStatement) Idx0 + +```go +func (self *CaseStatement) Idx0() file.Idx +``` + +#### func (*CaseStatement) Idx1 + +```go +func (self *CaseStatement) Idx1() file.Idx +``` + +#### type CatchStatement + +```go +type CatchStatement struct { + Catch file.Idx + Parameter *Identifier + Body Statement +} +``` + + +#### func (*CatchStatement) Idx0 + +```go +func (self *CatchStatement) Idx0() file.Idx +``` + +#### func (*CatchStatement) Idx1 + +```go +func (self *CatchStatement) Idx1() file.Idx +``` + +#### type ConditionalExpression + +```go +type ConditionalExpression struct { + Test Expression + Consequent Expression + Alternate Expression +} +``` + + +#### func (*ConditionalExpression) Idx0 + +```go +func (self *ConditionalExpression) Idx0() file.Idx +``` + +#### func (*ConditionalExpression) Idx1 + +```go +func (self *ConditionalExpression) Idx1() file.Idx +``` + +#### type DebuggerStatement + +```go +type DebuggerStatement struct { + Debugger file.Idx +} +``` + + +#### func (*DebuggerStatement) Idx0 + +```go +func (self *DebuggerStatement) Idx0() file.Idx +``` + +#### func (*DebuggerStatement) Idx1 + +```go +func (self *DebuggerStatement) Idx1() file.Idx +``` + +#### type Declaration + +```go +type Declaration interface { + // contains filtered or unexported methods +} +``` + +All declaration nodes implement the Declaration interface. + +#### type DoWhileStatement + +```go +type DoWhileStatement struct { + Do file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*DoWhileStatement) Idx0 + +```go +func (self *DoWhileStatement) Idx0() file.Idx +``` + +#### func (*DoWhileStatement) Idx1 + +```go +func (self *DoWhileStatement) Idx1() file.Idx +``` + +#### type DotExpression + +```go +type DotExpression struct { + Left Expression + Identifier Identifier +} +``` + + +#### func (*DotExpression) Idx0 + +```go +func (self *DotExpression) Idx0() file.Idx +``` + +#### func (*DotExpression) Idx1 + +```go +func (self *DotExpression) Idx1() file.Idx +``` + +#### type EmptyStatement + +```go +type EmptyStatement struct { + Semicolon file.Idx +} +``` + + +#### func (*EmptyStatement) Idx0 + +```go +func (self *EmptyStatement) Idx0() file.Idx +``` + +#### func (*EmptyStatement) Idx1 + +```go +func (self *EmptyStatement) Idx1() file.Idx +``` + +#### type Expression + +```go +type Expression interface { + Node + // contains filtered or unexported methods +} +``` + +All expression nodes implement the Expression interface. + +#### type ExpressionStatement + +```go +type ExpressionStatement struct { + Expression Expression +} +``` + + +#### func (*ExpressionStatement) Idx0 + +```go +func (self *ExpressionStatement) Idx0() file.Idx +``` + +#### func (*ExpressionStatement) Idx1 + +```go +func (self *ExpressionStatement) Idx1() file.Idx +``` + +#### type ForInStatement + +```go +type ForInStatement struct { + For file.Idx + Into Expression + Source Expression + Body Statement +} +``` + + +#### func (*ForInStatement) Idx0 + +```go +func (self *ForInStatement) Idx0() file.Idx +``` + +#### func (*ForInStatement) Idx1 + +```go +func (self *ForInStatement) Idx1() file.Idx +``` + +#### type ForStatement + +```go +type ForStatement struct { + For file.Idx + Initializer Expression + Update Expression + Test Expression + Body Statement +} +``` + + +#### func (*ForStatement) Idx0 + +```go +func (self *ForStatement) Idx0() file.Idx +``` + +#### func (*ForStatement) Idx1 + +```go +func (self *ForStatement) Idx1() file.Idx +``` + +#### type FunctionDeclaration + +```go +type FunctionDeclaration struct { + Function *FunctionLiteral +} +``` + + +#### type FunctionLiteral + +```go +type FunctionLiteral struct { + Function file.Idx + Name *Identifier + ParameterList *ParameterList + Body Statement + Source string + + DeclarationList []Declaration +} +``` + + +#### func (*FunctionLiteral) Idx0 + +```go +func (self *FunctionLiteral) Idx0() file.Idx +``` + +#### func (*FunctionLiteral) Idx1 + +```go +func (self *FunctionLiteral) Idx1() file.Idx +``` + +#### type Identifier + +```go +type Identifier struct { + Name string + Idx file.Idx +} +``` + + +#### func (*Identifier) Idx0 + +```go +func (self *Identifier) Idx0() file.Idx +``` + +#### func (*Identifier) Idx1 + +```go +func (self *Identifier) Idx1() file.Idx +``` + +#### type IfStatement + +```go +type IfStatement struct { + If file.Idx + Test Expression + Consequent Statement + Alternate Statement +} +``` + + +#### func (*IfStatement) Idx0 + +```go +func (self *IfStatement) Idx0() file.Idx +``` + +#### func (*IfStatement) Idx1 + +```go +func (self *IfStatement) Idx1() file.Idx +``` + +#### type LabelledStatement + +```go +type LabelledStatement struct { + Label *Identifier + Colon file.Idx + Statement Statement +} +``` + + +#### func (*LabelledStatement) Idx0 + +```go +func (self *LabelledStatement) Idx0() file.Idx +``` + +#### func (*LabelledStatement) Idx1 + +```go +func (self *LabelledStatement) Idx1() file.Idx +``` + +#### type NewExpression + +```go +type NewExpression struct { + New file.Idx + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx +} +``` + + +#### func (*NewExpression) Idx0 + +```go +func (self *NewExpression) Idx0() file.Idx +``` + +#### func (*NewExpression) Idx1 + +```go +func (self *NewExpression) Idx1() file.Idx +``` + +#### type Node + +```go +type Node interface { + Idx0() file.Idx // The index of the first character belonging to the node + Idx1() file.Idx // The index of the first character immediately after the node +} +``` + +All nodes implement the Node interface. + +#### type NullLiteral + +```go +type NullLiteral struct { + Idx file.Idx + Literal string +} +``` + + +#### func (*NullLiteral) Idx0 + +```go +func (self *NullLiteral) Idx0() file.Idx +``` + +#### func (*NullLiteral) Idx1 + +```go +func (self *NullLiteral) Idx1() file.Idx +``` + +#### type NumberLiteral + +```go +type NumberLiteral struct { + Idx file.Idx + Literal string + Value interface{} +} +``` + + +#### func (*NumberLiteral) Idx0 + +```go +func (self *NumberLiteral) Idx0() file.Idx +``` + +#### func (*NumberLiteral) Idx1 + +```go +func (self *NumberLiteral) Idx1() file.Idx +``` + +#### type ObjectLiteral + +```go +type ObjectLiteral struct { + LeftBrace file.Idx + RightBrace file.Idx + Value []Property +} +``` + + +#### func (*ObjectLiteral) Idx0 + +```go +func (self *ObjectLiteral) Idx0() file.Idx +``` + +#### func (*ObjectLiteral) Idx1 + +```go +func (self *ObjectLiteral) Idx1() file.Idx +``` + +#### type ParameterList + +```go +type ParameterList struct { + Opening file.Idx + List []*Identifier + Closing file.Idx +} +``` + + +#### type Program + +```go +type Program struct { + Body []Statement + + DeclarationList []Declaration + + File *file.File +} +``` + + +#### func (*Program) Idx0 + +```go +func (self *Program) Idx0() file.Idx +``` + +#### func (*Program) Idx1 + +```go +func (self *Program) Idx1() file.Idx +``` + +#### type Property + +```go +type Property struct { + Key string + Kind string + Value Expression +} +``` + + +#### type RegExpLiteral + +```go +type RegExpLiteral struct { + Idx file.Idx + Literal string + Pattern string + Flags string + Value string +} +``` + + +#### func (*RegExpLiteral) Idx0 + +```go +func (self *RegExpLiteral) Idx0() file.Idx +``` + +#### func (*RegExpLiteral) Idx1 + +```go +func (self *RegExpLiteral) Idx1() file.Idx +``` + +#### type ReturnStatement + +```go +type ReturnStatement struct { + Return file.Idx + Argument Expression +} +``` + + +#### func (*ReturnStatement) Idx0 + +```go +func (self *ReturnStatement) Idx0() file.Idx +``` + +#### func (*ReturnStatement) Idx1 + +```go +func (self *ReturnStatement) Idx1() file.Idx +``` + +#### type SequenceExpression + +```go +type SequenceExpression struct { + Sequence []Expression +} +``` + + +#### func (*SequenceExpression) Idx0 + +```go +func (self *SequenceExpression) Idx0() file.Idx +``` + +#### func (*SequenceExpression) Idx1 + +```go +func (self *SequenceExpression) Idx1() file.Idx +``` + +#### type Statement + +```go +type Statement interface { + Node + // contains filtered or unexported methods +} +``` + +All statement nodes implement the Statement interface. + +#### type StringLiteral + +```go +type StringLiteral struct { + Idx file.Idx + Literal string + Value string +} +``` + + +#### func (*StringLiteral) Idx0 + +```go +func (self *StringLiteral) Idx0() file.Idx +``` + +#### func (*StringLiteral) Idx1 + +```go +func (self *StringLiteral) Idx1() file.Idx +``` + +#### type SwitchStatement + +```go +type SwitchStatement struct { + Switch file.Idx + Discriminant Expression + Default int + Body []*CaseStatement +} +``` + + +#### func (*SwitchStatement) Idx0 + +```go +func (self *SwitchStatement) Idx0() file.Idx +``` + +#### func (*SwitchStatement) Idx1 + +```go +func (self *SwitchStatement) Idx1() file.Idx +``` + +#### type ThisExpression + +```go +type ThisExpression struct { + Idx file.Idx +} +``` + + +#### func (*ThisExpression) Idx0 + +```go +func (self *ThisExpression) Idx0() file.Idx +``` + +#### func (*ThisExpression) Idx1 + +```go +func (self *ThisExpression) Idx1() file.Idx +``` + +#### type ThrowStatement + +```go +type ThrowStatement struct { + Throw file.Idx + Argument Expression +} +``` + + +#### func (*ThrowStatement) Idx0 + +```go +func (self *ThrowStatement) Idx0() file.Idx +``` + +#### func (*ThrowStatement) Idx1 + +```go +func (self *ThrowStatement) Idx1() file.Idx +``` + +#### type TryStatement + +```go +type TryStatement struct { + Try file.Idx + Body Statement + Catch *CatchStatement + Finally Statement +} +``` + + +#### func (*TryStatement) Idx0 + +```go +func (self *TryStatement) Idx0() file.Idx +``` + +#### func (*TryStatement) Idx1 + +```go +func (self *TryStatement) Idx1() file.Idx +``` + +#### type UnaryExpression + +```go +type UnaryExpression struct { + Operator token.Token + Idx file.Idx // If a prefix operation + Operand Expression + Postfix bool +} +``` + + +#### func (*UnaryExpression) Idx0 + +```go +func (self *UnaryExpression) Idx0() file.Idx +``` + +#### func (*UnaryExpression) Idx1 + +```go +func (self *UnaryExpression) Idx1() file.Idx +``` + +#### type VariableDeclaration + +```go +type VariableDeclaration struct { + Var file.Idx + List []*VariableExpression +} +``` + + +#### type VariableExpression + +```go +type VariableExpression struct { + Name string + Idx file.Idx + Initializer Expression +} +``` + + +#### func (*VariableExpression) Idx0 + +```go +func (self *VariableExpression) Idx0() file.Idx +``` + +#### func (*VariableExpression) Idx1 + +```go +func (self *VariableExpression) Idx1() file.Idx +``` + +#### type VariableStatement + +```go +type VariableStatement struct { + Var file.Idx + List []Expression +} +``` + + +#### func (*VariableStatement) Idx0 + +```go +func (self *VariableStatement) Idx0() file.Idx +``` + +#### func (*VariableStatement) Idx1 + +```go +func (self *VariableStatement) Idx1() file.Idx +``` + +#### type WhileStatement + +```go +type WhileStatement struct { + While file.Idx + Test Expression + Body Statement +} +``` + + +#### func (*WhileStatement) Idx0 + +```go +func (self *WhileStatement) Idx0() file.Idx +``` + +#### func (*WhileStatement) Idx1 + +```go +func (self *WhileStatement) Idx1() file.Idx +``` + +#### type WithStatement + +```go +type WithStatement struct { + With file.Idx + Object Expression + Body Statement +} +``` + + +#### func (*WithStatement) Idx0 + +```go +func (self *WithStatement) Idx0() file.Idx +``` + +#### func (*WithStatement) Idx1 + +```go +func (self *WithStatement) Idx1() file.Idx +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/comments.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/comments.go new file mode 100644 index 0000000..ef2cc3d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/comments.go @@ -0,0 +1,278 @@ +package ast + +import ( + "fmt" + "github.com/robertkrimen/otto/file" +) + +// CommentPosition determines where the comment is in a given context +type CommentPosition int + +const ( + _ CommentPosition = iota + LEADING // Before the pertinent expression + TRAILING // After the pertinent expression + KEY // Before a key in an object + COLON // After a colon in a field declaration + FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal + IF // After an if keyword + WHILE // After a while keyword + DO // After do keyword + FOR // After a for keyword + WITH // After a with keyword + TBD +) + +// Comment contains the data of the comment +type Comment struct { + Begin file.Idx + Text string + Position CommentPosition +} + +// NewComment creates a new comment +func NewComment(text string, idx file.Idx) *Comment { + comment := &Comment{ + Begin: idx, + Text: text, + Position: TBD, + } + + return comment +} + +// String returns a stringified version of the position +func (cp CommentPosition) String() string { + switch cp { + case LEADING: + return "Leading" + case TRAILING: + return "Trailing" + case KEY: + return "Key" + case COLON: + return "Colon" + case FINAL: + return "Final" + case IF: + return "If" + case WHILE: + return "While" + case DO: + return "Do" + case FOR: + return "For" + case WITH: + return "With" + default: + return "???" + } +} + +// String returns a stringified version of the comment +func (c Comment) String() string { + return fmt.Sprintf("Comment: %v", c.Text) +} + +// Comments defines the current view of comments from the parser +type Comments struct { + // CommentMap is a reference to the parser comment map + CommentMap CommentMap + // Comments lists the comments scanned, not linked to a node yet + Comments []*Comment + // future lists the comments after a line break during a sequence of comments + future []*Comment + // Current is node for which comments are linked to + Current Expression + + // wasLineBreak determines if a line break occured while scanning for comments + wasLineBreak bool + // primary determines whether or not processing a primary expression + primary bool + // afterBlock determines whether or not being after a block statement + afterBlock bool +} + +func NewComments() *Comments { + comments := &Comments{ + CommentMap: CommentMap{}, + } + + return comments +} + +func (c *Comments) String() string { + return fmt.Sprintf("NODE: %v, Comments: %v, Future: %v(LINEBREAK:%v)", c.Current, len(c.Comments), len(c.future), c.wasLineBreak) +} + +// FetchAll returns all the currently scanned comments, +// including those from the next line +func (c *Comments) FetchAll() []*Comment { + defer func() { + c.Comments = nil + c.future = nil + }() + + return append(c.Comments, c.future...) +} + +// Fetch returns all the currently scanned comments +func (c *Comments) Fetch() []*Comment { + defer func() { + c.Comments = nil + }() + + return c.Comments +} + +// ResetLineBreak marks the beginning of a new statement +func (c *Comments) ResetLineBreak() { + c.wasLineBreak = false +} + +// MarkPrimary will mark the context as processing a primary expression +func (c *Comments) MarkPrimary() { + c.primary = true + c.wasLineBreak = false +} + +// AfterBlock will mark the context as being after a block. +func (c *Comments) AfterBlock() { + c.afterBlock = true +} + +// AddComment adds a comment to the view. +// Depending on the context, comments are added normally or as post line break. +func (c *Comments) AddComment(comment *Comment) { + if c.primary { + if !c.wasLineBreak { + c.Comments = append(c.Comments, comment) + } else { + c.future = append(c.future, comment) + } + } else { + if !c.wasLineBreak || (c.Current == nil && !c.afterBlock) { + c.Comments = append(c.Comments, comment) + } else { + c.future = append(c.future, comment) + } + } +} + +// MarkComments will mark the found comments as the given position. +func (c *Comments) MarkComments(position CommentPosition) { + for _, comment := range c.Comments { + if comment.Position == TBD { + comment.Position = position + } + } + for _, c := range c.future { + if c.Position == TBD { + c.Position = position + } + } +} + +// Unset the current node and apply the comments to the current expression. +// Resets context variables. +func (c *Comments) Unset() { + if c.Current != nil { + c.applyComments(c.Current, c.Current, TRAILING) + c.Current = nil + } + c.wasLineBreak = false + c.primary = false + c.afterBlock = false +} + +// SetExpression sets the current expression. +// It is applied the found comments, unless the previous expression has not been unset. +// It is skipped if the node is already set or if it is a part of the previous node. +func (c *Comments) SetExpression(node Expression) { + // Skipping same node + if c.Current == node { + return + } + if c.Current != nil && c.Current.Idx1() == node.Idx1() { + c.Current = node + return + } + previous := c.Current + c.Current = node + + // Apply the found comments and futures to the node and the previous. + c.applyComments(node, previous, TRAILING) +} + +// PostProcessNode applies all found comments to the given node +func (c *Comments) PostProcessNode(node Node) { + c.applyComments(node, nil, TRAILING) +} + +// applyComments applies both the comments and the future comments to the given node and the previous one, +// based on the context. +func (c *Comments) applyComments(node, previous Node, position CommentPosition) { + if previous != nil { + c.CommentMap.AddComments(previous, c.Comments, position) + c.Comments = nil + } else { + c.CommentMap.AddComments(node, c.Comments, position) + c.Comments = nil + } + // Only apply the future comments to the node if the previous is set. + // This is for detecting end of line comments and which node comments on the following lines belongs to + if previous != nil { + c.CommentMap.AddComments(node, c.future, position) + c.future = nil + } +} + +// AtLineBreak will mark a line break +func (c *Comments) AtLineBreak() { + c.wasLineBreak = true +} + +// CommentMap is the data structure where all found comments are stored +type CommentMap map[Node][]*Comment + +// AddComment adds a single comment to the map +func (cm CommentMap) AddComment(node Node, comment *Comment) { + list := cm[node] + list = append(list, comment) + + cm[node] = list +} + +// AddComments adds a slice of comments, given a node and an updated position +func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) { + for _, comment := range comments { + if comment.Position == TBD { + comment.Position = position + } + cm.AddComment(node, comment) + } +} + +// Size returns the size of the map +func (cm CommentMap) Size() int { + size := 0 + for _, comments := range cm { + size += len(comments) + } + + return size +} + +// MoveComments moves comments with a given position from a node to another +func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) { + for i, c := range cm[from] { + if c.Position == position { + cm.AddComment(to, c) + + // Remove the comment from the "from" slice + cm[from][i] = cm[from][len(cm[from])-1] + cm[from][len(cm[from])-1] = nil + cm[from] = cm[from][:len(cm[from])-1] + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/node.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/node.go new file mode 100644 index 0000000..49ae375 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/ast/node.go @@ -0,0 +1,515 @@ +/* +Package ast declares types representing a JavaScript AST. + +Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +*/ +package ast + +import ( + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +// All nodes implement the Node interface. +type Node interface { + Idx0() file.Idx // The index of the first character belonging to the node + Idx1() file.Idx // The index of the first character immediately after the node +} + +// ========== // +// Expression // +// ========== // + +type ( + // All expression nodes implement the Expression interface. + Expression interface { + Node + _expressionNode() + } + + ArrayLiteral struct { + LeftBracket file.Idx + RightBracket file.Idx + Value []Expression + } + + AssignExpression struct { + Operator token.Token + Left Expression + Right Expression + } + + BadExpression struct { + From file.Idx + To file.Idx + } + + BinaryExpression struct { + Operator token.Token + Left Expression + Right Expression + Comparison bool + } + + BooleanLiteral struct { + Idx file.Idx + Literal string + Value bool + } + + BracketExpression struct { + Left Expression + Member Expression + LeftBracket file.Idx + RightBracket file.Idx + } + + CallExpression struct { + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx + } + + ConditionalExpression struct { + Test Expression + Consequent Expression + Alternate Expression + } + + DotExpression struct { + Left Expression + Identifier *Identifier + } + + EmptyExpression struct { + Begin file.Idx + End file.Idx + } + + FunctionLiteral struct { + Function file.Idx + Name *Identifier + ParameterList *ParameterList + Body Statement + Source string + + DeclarationList []Declaration + } + + Identifier struct { + Name string + Idx file.Idx + } + + NewExpression struct { + New file.Idx + Callee Expression + LeftParenthesis file.Idx + ArgumentList []Expression + RightParenthesis file.Idx + } + + NullLiteral struct { + Idx file.Idx + Literal string + } + + NumberLiteral struct { + Idx file.Idx + Literal string + Value interface{} + } + + ObjectLiteral struct { + LeftBrace file.Idx + RightBrace file.Idx + Value []Property + } + + ParameterList struct { + Opening file.Idx + List []*Identifier + Closing file.Idx + } + + Property struct { + Key string + Kind string + Value Expression + } + + RegExpLiteral struct { + Idx file.Idx + Literal string + Pattern string + Flags string + Value string + } + + SequenceExpression struct { + Sequence []Expression + } + + StringLiteral struct { + Idx file.Idx + Literal string + Value string + } + + ThisExpression struct { + Idx file.Idx + } + + UnaryExpression struct { + Operator token.Token + Idx file.Idx // If a prefix operation + Operand Expression + Postfix bool + } + + VariableExpression struct { + Name string + Idx file.Idx + Initializer Expression + } +) + +// _expressionNode + +func (*ArrayLiteral) _expressionNode() {} +func (*AssignExpression) _expressionNode() {} +func (*BadExpression) _expressionNode() {} +func (*BinaryExpression) _expressionNode() {} +func (*BooleanLiteral) _expressionNode() {} +func (*BracketExpression) _expressionNode() {} +func (*CallExpression) _expressionNode() {} +func (*ConditionalExpression) _expressionNode() {} +func (*DotExpression) _expressionNode() {} +func (*EmptyExpression) _expressionNode() {} +func (*FunctionLiteral) _expressionNode() {} +func (*Identifier) _expressionNode() {} +func (*NewExpression) _expressionNode() {} +func (*NullLiteral) _expressionNode() {} +func (*NumberLiteral) _expressionNode() {} +func (*ObjectLiteral) _expressionNode() {} +func (*RegExpLiteral) _expressionNode() {} +func (*SequenceExpression) _expressionNode() {} +func (*StringLiteral) _expressionNode() {} +func (*ThisExpression) _expressionNode() {} +func (*UnaryExpression) _expressionNode() {} +func (*VariableExpression) _expressionNode() {} + +// ========= // +// Statement // +// ========= // + +type ( + // All statement nodes implement the Statement interface. + Statement interface { + Node + _statementNode() + } + + BadStatement struct { + From file.Idx + To file.Idx + } + + BlockStatement struct { + LeftBrace file.Idx + List []Statement + RightBrace file.Idx + } + + BranchStatement struct { + Idx file.Idx + Token token.Token + Label *Identifier + } + + CaseStatement struct { + Case file.Idx + Test Expression + Consequent []Statement + } + + CatchStatement struct { + Catch file.Idx + Parameter *Identifier + Body Statement + } + + DebuggerStatement struct { + Debugger file.Idx + } + + DoWhileStatement struct { + Do file.Idx + Test Expression + Body Statement + } + + EmptyStatement struct { + Semicolon file.Idx + } + + ExpressionStatement struct { + Expression Expression + } + + ForInStatement struct { + For file.Idx + Into Expression + Source Expression + Body Statement + } + + ForStatement struct { + For file.Idx + Initializer Expression + Update Expression + Test Expression + Body Statement + } + + FunctionStatement struct { + Function *FunctionLiteral + } + + IfStatement struct { + If file.Idx + Test Expression + Consequent Statement + Alternate Statement + } + + LabelledStatement struct { + Label *Identifier + Colon file.Idx + Statement Statement + } + + ReturnStatement struct { + Return file.Idx + Argument Expression + } + + SwitchStatement struct { + Switch file.Idx + Discriminant Expression + Default int + Body []*CaseStatement + } + + ThrowStatement struct { + Throw file.Idx + Argument Expression + } + + TryStatement struct { + Try file.Idx + Body Statement + Catch *CatchStatement + Finally Statement + } + + VariableStatement struct { + Var file.Idx + List []Expression + } + + WhileStatement struct { + While file.Idx + Test Expression + Body Statement + } + + WithStatement struct { + With file.Idx + Object Expression + Body Statement + } +) + +// _statementNode + +func (*BadStatement) _statementNode() {} +func (*BlockStatement) _statementNode() {} +func (*BranchStatement) _statementNode() {} +func (*CaseStatement) _statementNode() {} +func (*CatchStatement) _statementNode() {} +func (*DebuggerStatement) _statementNode() {} +func (*DoWhileStatement) _statementNode() {} +func (*EmptyStatement) _statementNode() {} +func (*ExpressionStatement) _statementNode() {} +func (*ForInStatement) _statementNode() {} +func (*ForStatement) _statementNode() {} +func (*FunctionStatement) _statementNode() {} +func (*IfStatement) _statementNode() {} +func (*LabelledStatement) _statementNode() {} +func (*ReturnStatement) _statementNode() {} +func (*SwitchStatement) _statementNode() {} +func (*ThrowStatement) _statementNode() {} +func (*TryStatement) _statementNode() {} +func (*VariableStatement) _statementNode() {} +func (*WhileStatement) _statementNode() {} +func (*WithStatement) _statementNode() {} + +// =========== // +// Declaration // +// =========== // + +type ( + // All declaration nodes implement the Declaration interface. + Declaration interface { + _declarationNode() + } + + FunctionDeclaration struct { + Function *FunctionLiteral + } + + VariableDeclaration struct { + Var file.Idx + List []*VariableExpression + } +) + +// _declarationNode + +func (*FunctionDeclaration) _declarationNode() {} +func (*VariableDeclaration) _declarationNode() {} + +// ==== // +// Node // +// ==== // + +type Program struct { + Body []Statement + + DeclarationList []Declaration + + File *file.File + + Comments CommentMap +} + +// ==== // +// Idx0 // +// ==== // + +func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket } +func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *BadExpression) Idx0() file.Idx { return self.From } +func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx } +func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() } +func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() } +func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *EmptyExpression) Idx0() file.Idx { return self.Begin } +func (self *FunctionLiteral) Idx0() file.Idx { return self.Function } +func (self *Identifier) Idx0() file.Idx { return self.Idx } +func (self *NewExpression) Idx0() file.Idx { return self.New } +func (self *NullLiteral) Idx0() file.Idx { return self.Idx } +func (self *NumberLiteral) Idx0() file.Idx { return self.Idx } +func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace } +func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx } +func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() } +func (self *StringLiteral) Idx0() file.Idx { return self.Idx } +func (self *ThisExpression) Idx0() file.Idx { return self.Idx } +func (self *UnaryExpression) Idx0() file.Idx { return self.Idx } +func (self *VariableExpression) Idx0() file.Idx { return self.Idx } + +func (self *BadStatement) Idx0() file.Idx { return self.From } +func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace } +func (self *BranchStatement) Idx0() file.Idx { return self.Idx } +func (self *CaseStatement) Idx0() file.Idx { return self.Case } +func (self *CatchStatement) Idx0() file.Idx { return self.Catch } +func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger } +func (self *DoWhileStatement) Idx0() file.Idx { return self.Do } +func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon } +func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() } +func (self *ForInStatement) Idx0() file.Idx { return self.For } +func (self *ForStatement) Idx0() file.Idx { return self.For } +func (self *FunctionStatement) Idx0() file.Idx { return self.Function.Idx0() } +func (self *IfStatement) Idx0() file.Idx { return self.If } +func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() } +func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() } +func (self *ReturnStatement) Idx0() file.Idx { return self.Return } +func (self *SwitchStatement) Idx0() file.Idx { return self.Switch } +func (self *ThrowStatement) Idx0() file.Idx { return self.Throw } +func (self *TryStatement) Idx0() file.Idx { return self.Try } +func (self *VariableStatement) Idx0() file.Idx { return self.Var } +func (self *WhileStatement) Idx0() file.Idx { return self.While } +func (self *WithStatement) Idx0() file.Idx { return self.With } + +// ==== // +// Idx1 // +// ==== // + +func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket } +func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() } +func (self *BadExpression) Idx1() file.Idx { return self.To } +func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() } +func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 } +func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } +func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() } +func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() } +func (self *EmptyExpression) Idx1() file.Idx { return self.End } +func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() } +func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) } +func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } +func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null" +func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } +func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } +func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } +func (self *ThisExpression) Idx1() file.Idx { return self.Idx } +func (self *UnaryExpression) Idx1() file.Idx { + if self.Postfix { + return self.Operand.Idx1() + 2 // ++ -- + } + return self.Operand.Idx1() +} +func (self *VariableExpression) Idx1() file.Idx { + if self.Initializer == nil { + return file.Idx(int(self.Idx) + len(self.Name) + 1) + } + return self.Initializer.Idx1() +} + +func (self *BadStatement) Idx1() file.Idx { return self.To } +func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 } +func (self *BranchStatement) Idx1() file.Idx { return self.Idx } +func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() } +func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 } +func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() } +func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 } +func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() } +func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *FunctionStatement) Idx1() file.Idx { return self.Function.Idx1() } +func (self *IfStatement) Idx1() file.Idx { + if self.Alternate != nil { + return self.Alternate.Idx1() + } + return self.Consequent.Idx1() +} +func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 } +func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } +func (self *ReturnStatement) Idx1() file.Idx { return self.Return } +func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } +func (self *ThrowStatement) Idx1() file.Idx { return self.Throw } +func (self *TryStatement) Idx1() file.Idx { return self.Try } +func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() } +func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() } +func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin.go new file mode 100644 index 0000000..256ee3c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin.go @@ -0,0 +1,354 @@ +package otto + +import ( + "encoding/hex" + "math" + "net/url" + "regexp" + "strconv" + "strings" + "unicode/utf16" + "unicode/utf8" +) + +// Global +func builtinGlobal_eval(call FunctionCall) Value { + src := call.Argument(0) + if !src.IsString() { + return src + } + runtime := call.runtime + program := runtime.cmpl_parseOrThrow(src.string(), nil) + if !call.eval { + // Not a direct call to eval, so we enter the global ExecutionContext + runtime.enterGlobalScope() + defer runtime.leaveScope() + } + returnValue := runtime.cmpl_evaluate_nodeProgram(program, true) + if returnValue.isEmpty() { + return Value{} + } + return returnValue +} + +func builtinGlobal_isNaN(call FunctionCall) Value { + value := call.Argument(0).float64() + return toValue_bool(math.IsNaN(value)) +} + +func builtinGlobal_isFinite(call FunctionCall) Value { + value := call.Argument(0).float64() + return toValue_bool(!math.IsNaN(value) && !math.IsInf(value, 0)) +} + +// radix 3 => 2 (ASCII 50) +47 +// radix 11 => A/a (ASCII 65/97) +54/+86 +var parseInt_alphabetTable = func() []string { + table := []string{"", "", "01"} + for radix := 3; radix <= 36; radix += 1 { + alphabet := table[radix-1] + if radix <= 10 { + alphabet += string(radix + 47) + } else { + alphabet += string(radix+54) + string(radix+86) + } + table = append(table, alphabet) + } + return table +}() + +func digitValue(chr rune) int { + switch { + case '0' <= chr && chr <= '9': + return int(chr - '0') + case 'a' <= chr && chr <= 'z': + return int(chr - 'a' + 10) + case 'A' <= chr && chr <= 'Z': + return int(chr - 'A' + 10) + } + return 36 // Larger than any legal digit value +} + +func builtinGlobal_parseInt(call FunctionCall) Value { + input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) + if len(input) == 0 { + return NaNValue() + } + + radix := int(toInt32(call.Argument(1))) + + negative := false + switch input[0] { + case '+': + input = input[1:] + case '-': + negative = true + input = input[1:] + } + + strip := true + if radix == 0 { + radix = 10 + } else { + if radix < 2 || radix > 36 { + return NaNValue() + } else if radix != 16 { + strip = false + } + } + + switch len(input) { + case 0: + return NaNValue() + case 1: + default: + if strip { + if input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + input = input[2:] + radix = 16 + } + } + } + + base := radix + index := 0 + for ; index < len(input); index++ { + digit := digitValue(rune(input[index])) // If not ASCII, then an error anyway + if digit >= base { + break + } + } + input = input[0:index] + + value, err := strconv.ParseInt(input, radix, 64) + if err != nil { + if err.(*strconv.NumError).Err == strconv.ErrRange { + base := float64(base) + // Could just be a very large number (e.g. 0x8000000000000000) + var value float64 + for _, chr := range input { + digit := float64(digitValue(chr)) + if digit >= base { + goto error + } + value = value*base + digit + } + if negative { + value *= -1 + } + return toValue_float64(value) + } + error: + return NaNValue() + } + if negative { + value *= -1 + } + + return toValue_int64(value) +} + +var parseFloat_matchBadSpecial = regexp.MustCompile(`[\+\-]?(?:[Ii]nf$|infinity)`) +var parseFloat_matchValid = regexp.MustCompile(`[0-9eE\+\-\.]|Infinity`) + +func builtinGlobal_parseFloat(call FunctionCall) Value { + // Caveat emptor: This implementation does NOT match the specification + input := strings.Trim(call.Argument(0).string(), builtinString_trim_whitespace) + + if parseFloat_matchBadSpecial.MatchString(input) { + return NaNValue() + } + value, err := strconv.ParseFloat(input, 64) + if err != nil { + for end := len(input); end > 0; end-- { + input := input[0:end] + if !parseFloat_matchValid.MatchString(input) { + return NaNValue() + } + value, err = strconv.ParseFloat(input, 64) + if err == nil { + break + } + } + if err != nil { + return NaNValue() + } + } + return toValue_float64(value) +} + +// encodeURI/decodeURI + +func _builtinGlobal_encodeURI(call FunctionCall, escape *regexp.Regexp) Value { + value := call.Argument(0) + var input []uint16 + switch vl := value.value.(type) { + case []uint16: + input = vl + default: + input = utf16.Encode([]rune(value.string())) + } + if len(input) == 0 { + return toValue_string("") + } + output := []byte{} + length := len(input) + encode := make([]byte, 4) + for index := 0; index < length; { + value := input[index] + decode := utf16.Decode(input[index : index+1]) + if value >= 0xDC00 && value <= 0xDFFF { + panic(call.runtime.panicURIError("URI malformed")) + } + if value >= 0xD800 && value <= 0xDBFF { + index += 1 + if index >= length { + panic(call.runtime.panicURIError("URI malformed")) + } + // input = ..., value, value1, ... + value1 := input[index] + if value1 < 0xDC00 || value1 > 0xDFFF { + panic(call.runtime.panicURIError("URI malformed")) + } + decode = []rune{((rune(value) - 0xD800) * 0x400) + (rune(value1) - 0xDC00) + 0x10000} + } + index += 1 + size := utf8.EncodeRune(encode, decode[0]) + encode := encode[0:size] + output = append(output, encode...) + } + { + value := escape.ReplaceAllFunc(output, func(target []byte) []byte { + // Probably a better way of doing this + if target[0] == ' ' { + return []byte("%20") + } + return []byte(url.QueryEscape(string(target))) + }) + return toValue_string(string(value)) + } +} + +var encodeURI_Regexp = regexp.MustCompile(`([^~!@#$&*()=:/,;?+'])`) + +func builtinGlobal_encodeURI(call FunctionCall) Value { + return _builtinGlobal_encodeURI(call, encodeURI_Regexp) +} + +var encodeURIComponent_Regexp = regexp.MustCompile(`([^~!*()'])`) + +func builtinGlobal_encodeURIComponent(call FunctionCall) Value { + return _builtinGlobal_encodeURI(call, encodeURIComponent_Regexp) +} + +// 3B/2F/3F/3A/40/26/3D/2B/24/2C/23 +var decodeURI_guard = regexp.MustCompile(`(?i)(?:%)(3B|2F|3F|3A|40|26|3D|2B|24|2C|23)`) + +func _decodeURI(input string, reserve bool) (string, bool) { + if reserve { + input = decodeURI_guard.ReplaceAllString(input, "%25$1") + } + input = strings.Replace(input, "+", "%2B", -1) // Ugly hack to make QueryUnescape work with our use case + output, err := url.QueryUnescape(input) + if err != nil || !utf8.ValidString(output) { + return "", true + } + return output, false +} + +func builtinGlobal_decodeURI(call FunctionCall) Value { + output, err := _decodeURI(call.Argument(0).string(), true) + if err { + panic(call.runtime.panicURIError("URI malformed")) + } + return toValue_string(output) +} + +func builtinGlobal_decodeURIComponent(call FunctionCall) Value { + output, err := _decodeURI(call.Argument(0).string(), false) + if err { + panic(call.runtime.panicURIError("URI malformed")) + } + return toValue_string(output) +} + +// escape/unescape + +func builtin_shouldEscape(chr byte) bool { + if 'A' <= chr && chr <= 'Z' || 'a' <= chr && chr <= 'z' || '0' <= chr && chr <= '9' { + return false + } + return !strings.ContainsRune("*_+-./", rune(chr)) +} + +const escapeBase16 = "0123456789ABCDEF" + +func builtin_escape(input string) string { + output := make([]byte, 0, len(input)) + length := len(input) + for index := 0; index < length; { + if builtin_shouldEscape(input[index]) { + chr, width := utf8.DecodeRuneInString(input[index:]) + chr16 := utf16.Encode([]rune{chr})[0] + if 256 > chr16 { + output = append(output, '%', + escapeBase16[chr16>>4], + escapeBase16[chr16&15], + ) + } else { + output = append(output, '%', 'u', + escapeBase16[chr16>>12], + escapeBase16[(chr16>>8)&15], + escapeBase16[(chr16>>4)&15], + escapeBase16[chr16&15], + ) + } + index += width + + } else { + output = append(output, input[index]) + index += 1 + } + } + return string(output) +} + +func builtin_unescape(input string) string { + output := make([]rune, 0, len(input)) + length := len(input) + for index := 0; index < length; { + if input[index] == '%' { + if index <= length-6 && input[index+1] == 'u' { + byte16, err := hex.DecodeString(input[index+2 : index+6]) + if err == nil { + value := uint16(byte16[0])<<8 + uint16(byte16[1]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 6 + continue + } + } + if index <= length-3 { + byte8, err := hex.DecodeString(input[index+1 : index+3]) + if err == nil { + value := uint16(byte8[0]) + chr := utf16.Decode([]uint16{value})[0] + output = append(output, chr) + index += 3 + continue + } + } + } + output = append(output, rune(input[index])) + index += 1 + } + return string(output) +} + +func builtinGlobal_escape(call FunctionCall) Value { + return toValue_string(builtin_escape(call.Argument(0).string())) +} + +func builtinGlobal_unescape(call FunctionCall) Value { + return toValue_string(builtin_unescape(call.Argument(0).string())) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_array.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_array.go new file mode 100644 index 0000000..557ffc0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_array.go @@ -0,0 +1,681 @@ +package otto + +import ( + "strconv" + "strings" +) + +// Array + +func builtinArray(call FunctionCall) Value { + return toValue_object(builtinNewArrayNative(call.runtime, call.ArgumentList)) +} + +func builtinNewArray(self *_object, argumentList []Value) Value { + return toValue_object(builtinNewArrayNative(self.runtime, argumentList)) +} + +func builtinNewArrayNative(runtime *_runtime, argumentList []Value) *_object { + if len(argumentList) == 1 { + firstArgument := argumentList[0] + if firstArgument.IsNumber() { + return runtime.newArray(arrayUint32(runtime, firstArgument)) + } + } + return runtime.newArrayOf(argumentList) +} + +func builtinArray_toString(call FunctionCall) Value { + thisObject := call.thisObject() + join := thisObject.get("join") + if join.isCallable() { + join := join._object() + return join.call(call.This, call.ArgumentList, false, nativeFrame) + } + return builtinObject_toString(call) +} + +func builtinArray_toLocaleString(call FunctionCall) Value { + separator := "," + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if length == 0 { + return toValue_string("") + } + stringList := make([]string, 0, length) + for index := int64(0); index < length; index += 1 { + value := thisObject.get(arrayIndexToString(index)) + stringValue := "" + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + default: + object := call.runtime.toObject(value) + toLocaleString := object.get("toLocaleString") + if !toLocaleString.isCallable() { + panic(call.runtime.panicTypeError()) + } + stringValue = toLocaleString.call(call.runtime, toValue_object(object)).string() + } + stringList = append(stringList, stringValue) + } + return toValue_string(strings.Join(stringList, separator)) +} + +func builtinArray_concat(call FunctionCall) Value { + thisObject := call.thisObject() + valueArray := []Value{} + source := append([]Value{toValue_object(thisObject)}, call.ArgumentList...) + for _, item := range source { + switch item.kind { + case valueObject: + object := item._object() + if isArray(object) { + length := object.get("length").number().int64 + for index := int64(0); index < length; index += 1 { + name := strconv.FormatInt(index, 10) + if object.hasProperty(name) { + valueArray = append(valueArray, object.get(name)) + } else { + valueArray = append(valueArray, Value{}) + } + } + continue + } + fallthrough + default: + valueArray = append(valueArray, item) + } + } + return toValue_object(call.runtime.newArrayOf(valueArray)) +} + +func builtinArray_shift(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if 0 == length { + thisObject.put("length", toValue_int64(0), true) + return Value{} + } + first := thisObject.get("0") + for index := int64(1); index < length; index++ { + from := arrayIndexToString(index) + to := arrayIndexToString(index - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + thisObject.delete(arrayIndexToString(length-1), true) + thisObject.put("length", toValue_int64(length-1), true) + return first +} + +func builtinArray_push(call FunctionCall) Value { + thisObject := call.thisObject() + itemList := call.ArgumentList + index := int64(toUint32(thisObject.get("length"))) + for len(itemList) > 0 { + thisObject.put(arrayIndexToString(index), itemList[0], true) + itemList = itemList[1:] + index += 1 + } + length := toValue_int64(index) + thisObject.put("length", length, true) + return length +} + +func builtinArray_pop(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if 0 == length { + thisObject.put("length", toValue_uint32(0), true) + return Value{} + } + last := thisObject.get(arrayIndexToString(length - 1)) + thisObject.delete(arrayIndexToString(length-1), true) + thisObject.put("length", toValue_int64(length-1), true) + return last +} + +func builtinArray_join(call FunctionCall) Value { + separator := "," + { + argument := call.Argument(0) + if argument.IsDefined() { + separator = argument.string() + } + } + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + if length == 0 { + return toValue_string("") + } + stringList := make([]string, 0, length) + for index := int64(0); index < length; index += 1 { + value := thisObject.get(arrayIndexToString(index)) + stringValue := "" + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + default: + stringValue = value.string() + } + stringList = append(stringList, stringValue) + } + return toValue_string(strings.Join(stringList, separator)) +} + +func builtinArray_splice(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + + start := valueToRangeIndex(call.Argument(0), length, false) + deleteCount := valueToRangeIndex(call.Argument(1), int64(length)-start, true) + valueArray := make([]Value, deleteCount) + + for index := int64(0); index < deleteCount; index++ { + indexString := arrayIndexToString(int64(start + index)) + if thisObject.hasProperty(indexString) { + valueArray[index] = thisObject.get(indexString) + } + } + + // 0, <1, 2, 3, 4>, 5, 6, 7 + // a, b + // length 8 - delete 4 @ start 1 + + itemList := []Value{} + itemCount := int64(len(call.ArgumentList)) + if itemCount > 2 { + itemCount -= 2 // Less the first two arguments + itemList = call.ArgumentList[2:] + } else { + itemCount = 0 + } + if itemCount < deleteCount { + // The Object/Array is shrinking + stop := int64(length) - deleteCount + // The new length of the Object/Array before + // appending the itemList remainder + // Stopping at the lower bound of the insertion: + // Move an item from the after the deleted portion + // to a position after the inserted portion + for index := start; index < stop; index++ { + from := arrayIndexToString(index + deleteCount) // Position just after deletion + to := arrayIndexToString(index + itemCount) // Position just after splice (insertion) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + // Delete off the end + // We don't bother to delete below (if any) since those + // will be overwritten anyway + for index := int64(length); index > (stop + itemCount); index-- { + thisObject.delete(arrayIndexToString(index-1), true) + } + } else if itemCount > deleteCount { + // The Object/Array is growing + // The itemCount is greater than the deleteCount, so we do + // not have to worry about overwriting what we should be moving + // --- + // Starting from the upper bound of the deletion: + // Move an item from the after the deleted portion + // to a position after the inserted portion + for index := int64(length) - deleteCount; index > start; index-- { + from := arrayIndexToString(index + deleteCount - 1) + to := arrayIndexToString(index + itemCount - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + } + + for index := int64(0); index < itemCount; index++ { + thisObject.put(arrayIndexToString(index+start), itemList[index], true) + } + thisObject.put("length", toValue_int64(int64(length)+itemCount-deleteCount), true) + + return toValue_object(call.runtime.newArrayOf(valueArray)) +} + +func builtinArray_slice(call FunctionCall) Value { + thisObject := call.thisObject() + + length := int64(toUint32(thisObject.get("length"))) + start, end := rangeStartEnd(call.ArgumentList, length, false) + + if start >= end { + // Always an empty array + return toValue_object(call.runtime.newArray(0)) + } + sliceLength := end - start + sliceValueArray := make([]Value, sliceLength) + + for index := int64(0); index < sliceLength; index++ { + from := arrayIndexToString(index + start) + if thisObject.hasProperty(from) { + sliceValueArray[index] = thisObject.get(from) + } + } + + return toValue_object(call.runtime.newArrayOf(sliceValueArray)) +} + +func builtinArray_unshift(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + itemList := call.ArgumentList + itemCount := int64(len(itemList)) + + for index := length; index > 0; index-- { + from := arrayIndexToString(index - 1) + to := arrayIndexToString(index + itemCount - 1) + if thisObject.hasProperty(from) { + thisObject.put(to, thisObject.get(from), true) + } else { + thisObject.delete(to, true) + } + } + + for index := int64(0); index < itemCount; index++ { + thisObject.put(arrayIndexToString(index), itemList[index], true) + } + + newLength := toValue_int64(length + itemCount) + thisObject.put("length", newLength, true) + return newLength +} + +func builtinArray_reverse(call FunctionCall) Value { + thisObject := call.thisObject() + length := int64(toUint32(thisObject.get("length"))) + + lower := struct { + name string + index int64 + exists bool + }{} + upper := lower + + lower.index = 0 + middle := length / 2 // Division will floor + + for lower.index != middle { + lower.name = arrayIndexToString(lower.index) + upper.index = length - lower.index - 1 + upper.name = arrayIndexToString(upper.index) + + lower.exists = thisObject.hasProperty(lower.name) + upper.exists = thisObject.hasProperty(upper.name) + + if lower.exists && upper.exists { + lowerValue := thisObject.get(lower.name) + upperValue := thisObject.get(upper.name) + thisObject.put(lower.name, upperValue, true) + thisObject.put(upper.name, lowerValue, true) + } else if !lower.exists && upper.exists { + value := thisObject.get(upper.name) + thisObject.delete(upper.name, true) + thisObject.put(lower.name, value, true) + } else if lower.exists && !upper.exists { + value := thisObject.get(lower.name) + thisObject.delete(lower.name, true) + thisObject.put(upper.name, value, true) + } else { + // Nothing happens. + } + + lower.index += 1 + } + + return call.This +} + +func sortCompare(thisObject *_object, index0, index1 uint, compare *_object) int { + j := struct { + name string + exists bool + defined bool + value string + }{} + k := j + j.name = arrayIndexToString(int64(index0)) + j.exists = thisObject.hasProperty(j.name) + k.name = arrayIndexToString(int64(index1)) + k.exists = thisObject.hasProperty(k.name) + + if !j.exists && !k.exists { + return 0 + } else if !j.exists { + return 1 + } else if !k.exists { + return -1 + } + + x := thisObject.get(j.name) + y := thisObject.get(k.name) + j.defined = x.IsDefined() + k.defined = y.IsDefined() + + if !j.defined && !k.defined { + return 0 + } else if !j.defined { + return 1 + } else if !k.defined { + return -1 + } + + if compare == nil { + j.value = x.string() + k.value = y.string() + + if j.value == k.value { + return 0 + } else if j.value < k.value { + return -1 + } + + return 1 + } + + return int(toInt32(compare.call(Value{}, []Value{x, y}, false, nativeFrame))) +} + +func arraySortSwap(thisObject *_object, index0, index1 uint) { + + j := struct { + name string + exists bool + }{} + k := j + + j.name = arrayIndexToString(int64(index0)) + j.exists = thisObject.hasProperty(j.name) + k.name = arrayIndexToString(int64(index1)) + k.exists = thisObject.hasProperty(k.name) + + if j.exists && k.exists { + jValue := thisObject.get(j.name) + kValue := thisObject.get(k.name) + thisObject.put(j.name, kValue, true) + thisObject.put(k.name, jValue, true) + } else if !j.exists && k.exists { + value := thisObject.get(k.name) + thisObject.delete(k.name, true) + thisObject.put(j.name, value, true) + } else if j.exists && !k.exists { + value := thisObject.get(j.name) + thisObject.delete(j.name, true) + thisObject.put(k.name, value, true) + } else { + // Nothing happens. + } +} + +func arraySortQuickPartition(thisObject *_object, left, right, pivot uint, compare *_object) (uint, uint) { + arraySortSwap(thisObject, pivot, right) // Right is now the pivot value + cursor := left + cursor2 := left + for index := left; index < right; index++ { + comparison := sortCompare(thisObject, index, right, compare) // Compare to the pivot value + if comparison < 0 { + arraySortSwap(thisObject, index, cursor) + if cursor < cursor2 { + arraySortSwap(thisObject, index, cursor2) + } + cursor += 1 + cursor2 += 1 + } else if comparison == 0 { + arraySortSwap(thisObject, index, cursor2) + cursor2 += 1 + } + } + arraySortSwap(thisObject, cursor2, right) + return cursor, cursor2 +} + +func arraySortQuickSort(thisObject *_object, left, right uint, compare *_object) { + if left < right { + middle := left + (right-left)/2 + pivot, pivot2 := arraySortQuickPartition(thisObject, left, right, middle, compare) + if pivot > 0 { + arraySortQuickSort(thisObject, left, pivot-1, compare) + } + arraySortQuickSort(thisObject, pivot2+1, right, compare) + } +} + +func builtinArray_sort(call FunctionCall) Value { + thisObject := call.thisObject() + length := uint(toUint32(thisObject.get("length"))) + compareValue := call.Argument(0) + compare := compareValue._object() + if compareValue.IsUndefined() { + } else if !compareValue.isCallable() { + panic(call.runtime.panicTypeError()) + } + if length > 1 { + arraySortQuickSort(thisObject, 0, length-1, compare) + } + return call.This +} + +func builtinArray_isArray(call FunctionCall) Value { + return toValue_bool(isArray(call.Argument(0)._object())) +} + +func builtinArray_indexOf(call FunctionCall) Value { + thisObject, matchValue := call.thisObject(), call.Argument(0) + if length := int64(toUint32(thisObject.get("length"))); length > 0 { + index := int64(0) + if len(call.ArgumentList) > 1 { + index = call.Argument(1).number().int64 + } + if index < 0 { + if index += length; index < 0 { + index = 0 + } + } else if index >= length { + index = -1 + } + for ; index >= 0 && index < length; index++ { + name := arrayIndexToString(int64(index)) + if !thisObject.hasProperty(name) { + continue + } + value := thisObject.get(name) + if strictEqualityComparison(matchValue, value) { + return toValue_uint32(uint32(index)) + } + } + } + return toValue_int(-1) +} + +func builtinArray_lastIndexOf(call FunctionCall) Value { + thisObject, matchValue := call.thisObject(), call.Argument(0) + length := int64(toUint32(thisObject.get("length"))) + index := length - 1 + if len(call.ArgumentList) > 1 { + index = call.Argument(1).number().int64 + } + if 0 > index { + index += length + } + if index > length { + index = length - 1 + } else if 0 > index { + return toValue_int(-1) + } + for ; index >= 0; index-- { + name := arrayIndexToString(int64(index)) + if !thisObject.hasProperty(name) { + continue + } + value := thisObject.get(name) + if strictEqualityComparison(matchValue, value) { + return toValue_uint32(uint32(index)) + } + } + return toValue_int(-1) +} + +func builtinArray_every(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { + continue + } + return falseValue + } + } + return trueValue + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_some(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + if value := thisObject.get(key); iterator.call(call.runtime, callThis, value, toValue_int64(index), this).bool() { + return trueValue + } + } + } + return falseValue + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_forEach(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + iterator.call(call.runtime, callThis, thisObject.get(key), toValue_int64(index), this) + } + } + return Value{} + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_map(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + values := make([]Value, length) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + values[index] = iterator.call(call.runtime, callThis, thisObject.get(key), index, this) + } else { + values[index] = Value{} + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_filter(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + length := int64(toUint32(thisObject.get("length"))) + callThis := call.Argument(1) + values := make([]Value, 0) + for index := int64(0); index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + value := thisObject.get(key) + if iterator.call(call.runtime, callThis, value, index, this).bool() { + values = append(values, value) + } + } + } + return toValue_object(call.runtime.newArrayOf(values)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_reduce(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + initial := len(call.ArgumentList) > 1 + start := call.Argument(1) + length := int64(toUint32(thisObject.get("length"))) + index := int64(0) + if length > 0 || initial { + var accumulator Value + if !initial { + for ; index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = thisObject.get(key) + index++ + break + } + } + } else { + accumulator = start + } + for ; index < length; index++ { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(call.runtime.panicTypeError()) +} + +func builtinArray_reduceRight(call FunctionCall) Value { + thisObject := call.thisObject() + this := toValue_object(thisObject) + if iterator := call.Argument(0); iterator.isCallable() { + initial := len(call.ArgumentList) > 1 + start := call.Argument(1) + length := int64(toUint32(thisObject.get("length"))) + if length > 0 || initial { + index := length - 1 + var accumulator Value + if !initial { + for ; index >= 0; index-- { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = thisObject.get(key) + index-- + break + } + } + } else { + accumulator = start + } + for ; index >= 0; index-- { + if key := arrayIndexToString(index); thisObject.hasProperty(key) { + accumulator = iterator.call(call.runtime, Value{}, accumulator, thisObject.get(key), key, this) + } + } + return accumulator + } + } + panic(call.runtime.panicTypeError()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_boolean.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_boolean.go new file mode 100644 index 0000000..59b8e78 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_boolean.go @@ -0,0 +1,28 @@ +package otto + +// Boolean + +func builtinBoolean(call FunctionCall) Value { + return toValue_bool(call.Argument(0).bool()) +} + +func builtinNewBoolean(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newBoolean(valueOfArrayIndex(argumentList, 0))) +} + +func builtinBoolean_toString(call FunctionCall) Value { + value := call.This + if !value.IsBoolean() { + // Will throw a TypeError if ThisObject is not a Boolean + value = call.thisClassObject("Boolean").primitiveValue() + } + return toValue_string(value.string()) +} + +func builtinBoolean_valueOf(call FunctionCall) Value { + value := call.This + if !value.IsBoolean() { + value = call.thisClassObject("Boolean").primitiveValue() + } + return value +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_date.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_date.go new file mode 100644 index 0000000..f20bf8e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_date.go @@ -0,0 +1,615 @@ +package otto + +import ( + "math" + Time "time" +) + +// Date + +const ( + // TODO Be like V8? + // builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)" + builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST" + builtinDate_goDateLayout = "Mon, 02 Jan 2006" + builtinDate_goTimeLayout = "15:04:05 MST" +) + +func builtinDate(call FunctionCall) Value { + date := &_dateObject{} + date.Set(newDateTime([]Value{}, Time.Local)) + return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) +} + +func builtinNewDate(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local))) +} + +func builtinDate_toString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout)) +} + +func builtinDate_toDateString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout)) +} + +func builtinDate_toTimeString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout)) +} + +func builtinDate_toUTCString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout)) +} + +func builtinDate_toISOString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z")) +} + +func builtinDate_toJSON(call FunctionCall) Value { + object := call.thisObject() + value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue + { // FIXME value.isFinite + value := value.float64() + if math.IsNaN(value) || math.IsInf(value, 0) { + return nullValue + } + } + toISOString := object.get("toISOString") + if !toISOString.isCallable() { + // FIXME + panic(call.runtime.panicTypeError()) + } + return toISOString.call(call.runtime, toValue_object(object), []Value{}) +} + +func builtinDate_toGMTString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT")) +} + +func builtinDate_getTime(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + // We do this (convert away from a float) so the user + // does not get something back in exponential notation + return toValue_int64(int64(date.Epoch())) +} + +func builtinDate_setTime(call FunctionCall) Value { + object := call.thisObject() + date := dateObjectOf(call.runtime, call.thisObject()) + date.Set(call.Argument(0).float64()) + object.value = date + return date.Value() +} + +func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) { + object := call.thisObject() + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return nil, nil, nil, nil + } + + if argumentLimit > len(call.ArgumentList) { + argumentLimit = len(call.ArgumentList) + } + + if argumentLimit == 0 { + object.value = invalidDateObject + return nil, nil, nil, nil + } + + valueList := make([]int, argumentLimit) + for index := 0; index < argumentLimit; index++ { + value := call.ArgumentList[index] + nm := value.number() + switch nm.kind { + case numberInteger, numberFloat: + default: + object.value = invalidDateObject + return nil, nil, nil, nil + } + valueList[index] = int(nm.int64) + } + baseTime := date.Time() + if timeLocal { + baseTime = baseTime.Local() + } + ecmaTime := ecmaTime(baseTime) + return object, &date, &ecmaTime, valueList +} + +func builtinDate_parse(call FunctionCall) Value { + date := call.Argument(0).string() + return toValue_float64(dateParse(date)) +} + +func builtinDate_UTC(call FunctionCall) Value { + return toValue_float64(newDateTime(call.ArgumentList, Time.UTC)) +} + +func builtinDate_now(call FunctionCall) Value { + call.ArgumentList = []Value(nil) + return builtinDate_UTC(call) +} + +// This is a placeholder +func builtinDate_toLocaleString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05")) +} + +// This is a placeholder +func builtinDate_toLocaleDateString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("2006-01-02")) +} + +// This is a placeholder +func builtinDate_toLocaleTimeString(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return toValue_string("Invalid Date") + } + return toValue_string(date.Time().Local().Format("15:04:05")) +} + +func builtinDate_valueOf(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return date.Value() +} + +func builtinDate_getYear(call FunctionCall) Value { + // Will throw a TypeError is ThisObject is nil or + // does not have Class of "Date" + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Year() - 1900) +} + +func builtinDate_getFullYear(call FunctionCall) Value { + // Will throw a TypeError is ThisObject is nil or + // does not have Class of "Date" + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Year()) +} + +func builtinDate_getUTCFullYear(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Year()) +} + +func builtinDate_getMonth(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Local().Month())) +} + +func builtinDate_getUTCMonth(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoMonth(date.Time().Month())) +} + +func builtinDate_getDate(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Day()) +} + +func builtinDate_getUTCDate(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Day()) +} + +func builtinDate_getDay(call FunctionCall) Value { + // Actually day of the week + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Local().Weekday())) +} + +func builtinDate_getUTCDay(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(dateFromGoDay(date.Time().Weekday())) +} + +func builtinDate_getHours(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Hour()) +} + +func builtinDate_getUTCHours(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Hour()) +} + +func builtinDate_getMinutes(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Minute()) +} + +func builtinDate_getUTCMinutes(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Minute()) +} + +func builtinDate_getSeconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Second()) +} + +func builtinDate_getUTCSeconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Second()) +} + +func builtinDate_getMilliseconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100)) +} + +func builtinDate_getUTCMilliseconds(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100)) +} + +func builtinDate_getTimezoneOffset(call FunctionCall) Value { + date := dateObjectOf(call.runtime, call.thisObject()) + if date.isNaN { + return NaNValue() + } + timeLocal := date.Time().Local() + // Is this kosher? + timeLocalAsUTC := Time.Date( + timeLocal.Year(), + timeLocal.Month(), + timeLocal.Day(), + timeLocal.Hour(), + timeLocal.Minute(), + timeLocal.Second(), + timeLocal.Nanosecond(), + Time.UTC, + ) + return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60) +} + +func builtinDate_setMilliseconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.millisecond = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMilliseconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.millisecond = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setSeconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.millisecond = value[1] + } + ecmaTime.second = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCSeconds(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.millisecond = value[1] + } + ecmaTime.second = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setMinutes(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.millisecond = value[2] + ecmaTime.second = value[1] + } else if len(value) > 1 { + ecmaTime.second = value[1] + } + ecmaTime.minute = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMinutes(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.millisecond = value[2] + ecmaTime.second = value[1] + } else if len(value) > 1 { + ecmaTime.second = value[1] + } + ecmaTime.minute = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setHours(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 3 { + ecmaTime.millisecond = value[3] + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 2 { + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 1 { + ecmaTime.minute = value[1] + } + ecmaTime.hour = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCHours(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 3 { + ecmaTime.millisecond = value[3] + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 2 { + ecmaTime.second = value[2] + ecmaTime.minute = value[1] + } else if len(value) > 1 { + ecmaTime.minute = value[1] + } + ecmaTime.hour = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setDate(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.day = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCDate(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false) + if ecmaTime == nil { + return NaNValue() + } + + ecmaTime.day = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setMonth(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.day = value[1] + } + ecmaTime.month = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCMonth(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 1 { + ecmaTime.day = value[1] + } + ecmaTime.month = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true) + if ecmaTime == nil { + return NaNValue() + } + + year := value[0] + if 0 <= year && year <= 99 { + year += 1900 + } + ecmaTime.year = year + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setFullYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.day = value[2] + ecmaTime.month = value[1] + } else if len(value) > 1 { + ecmaTime.month = value[1] + } + ecmaTime.year = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +func builtinDate_setUTCFullYear(call FunctionCall) Value { + object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false) + if ecmaTime == nil { + return NaNValue() + } + + if len(value) > 2 { + ecmaTime.day = value[2] + ecmaTime.month = value[1] + } else if len(value) > 1 { + ecmaTime.month = value[1] + } + ecmaTime.year = value[0] + + date.SetTime(ecmaTime.goTime()) + object.value = *date + return date.Value() +} + +// toUTCString +// toISOString +// toJSONString +// toJSON diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_error.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_error.go new file mode 100644 index 0000000..41da84e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_error.go @@ -0,0 +1,126 @@ +package otto + +import ( + "fmt" +) + +func builtinError(call FunctionCall) Value { + return toValue_object(call.runtime.newError("Error", call.Argument(0), 1)) +} + +func builtinNewError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newError("Error", valueOfArrayIndex(argumentList, 0), 0)) +} + +func builtinError_toString(call FunctionCall) Value { + thisObject := call.thisObject() + if thisObject == nil { + panic(call.runtime.panicTypeError()) + } + + name := "Error" + nameValue := thisObject.get("name") + if nameValue.IsDefined() { + name = nameValue.string() + } + + message := "" + messageValue := thisObject.get("message") + if messageValue.IsDefined() { + message = messageValue.string() + } + + if len(name) == 0 { + return toValue_string(message) + } + + if len(message) == 0 { + return toValue_string(name) + } + + return toValue_string(fmt.Sprintf("%s: %s", name, message)) +} + +func (runtime *_runtime) newEvalError(message Value) *_object { + self := runtime.newErrorObject("EvalError", message, 0) + self.prototype = runtime.global.EvalErrorPrototype + return self +} + +func builtinEvalError(call FunctionCall) Value { + return toValue_object(call.runtime.newEvalError(call.Argument(0))) +} + +func builtinNewEvalError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newTypeError(message Value) *_object { + self := runtime.newErrorObject("TypeError", message, 0) + self.prototype = runtime.global.TypeErrorPrototype + return self +} + +func builtinTypeError(call FunctionCall) Value { + return toValue_object(call.runtime.newTypeError(call.Argument(0))) +} + +func builtinNewTypeError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newRangeError(message Value) *_object { + self := runtime.newErrorObject("RangeError", message, 0) + self.prototype = runtime.global.RangeErrorPrototype + return self +} + +func builtinRangeError(call FunctionCall) Value { + return toValue_object(call.runtime.newRangeError(call.Argument(0))) +} + +func builtinNewRangeError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newURIError(message Value) *_object { + self := runtime.newErrorObject("URIError", message, 0) + self.prototype = runtime.global.URIErrorPrototype + return self +} + +func (runtime *_runtime) newReferenceError(message Value) *_object { + self := runtime.newErrorObject("ReferenceError", message, 0) + self.prototype = runtime.global.ReferenceErrorPrototype + return self +} + +func builtinReferenceError(call FunctionCall) Value { + return toValue_object(call.runtime.newReferenceError(call.Argument(0))) +} + +func builtinNewReferenceError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) +} + +func (runtime *_runtime) newSyntaxError(message Value) *_object { + self := runtime.newErrorObject("SyntaxError", message, 0) + self.prototype = runtime.global.SyntaxErrorPrototype + return self +} + +func builtinSyntaxError(call FunctionCall) Value { + return toValue_object(call.runtime.newSyntaxError(call.Argument(0))) +} + +func builtinNewSyntaxError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0))) +} + +func builtinURIError(call FunctionCall) Value { + return toValue_object(call.runtime.newURIError(call.Argument(0))) +} + +func builtinNewURIError(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_function.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_function.go new file mode 100644 index 0000000..3d07566 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_function.go @@ -0,0 +1,129 @@ +package otto + +import ( + "fmt" + "regexp" + "strings" + "unicode" + + "github.com/robertkrimen/otto/parser" +) + +// Function + +func builtinFunction(call FunctionCall) Value { + return toValue_object(builtinNewFunctionNative(call.runtime, call.ArgumentList)) +} + +func builtinNewFunction(self *_object, argumentList []Value) Value { + return toValue_object(builtinNewFunctionNative(self.runtime, argumentList)) +} + +func argumentList2parameterList(argumentList []Value) []string { + parameterList := make([]string, 0, len(argumentList)) + for _, value := range argumentList { + tmp := strings.FieldsFunc(value.string(), func(chr rune) bool { + return chr == ',' || unicode.IsSpace(chr) + }) + parameterList = append(parameterList, tmp...) + } + return parameterList +} + +var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) + +func builtinNewFunctionNative(runtime *_runtime, argumentList []Value) *_object { + var parameterList, body string + count := len(argumentList) + if count > 0 { + tmp := make([]string, 0, count-1) + for _, value := range argumentList[0 : count-1] { + tmp = append(tmp, value.string()) + } + parameterList = strings.Join(tmp, ",") + body = argumentList[count-1].string() + } + + // FIXME + function, err := parser.ParseFunction(parameterList, body) + runtime.parseThrow(err) // Will panic/throw appropriately + cmpl := _compiler{} + cmpl_function := cmpl.parseExpression(function) + + return runtime.newNodeFunction(cmpl_function.(*_nodeFunctionLiteral), runtime.globalStash) +} + +func builtinFunction_toString(call FunctionCall) Value { + object := call.thisClassObject("Function") // Should throw a TypeError unless Function + switch fn := object.value.(type) { + case _nativeFunctionObject: + return toValue_string(fmt.Sprintf("function %s() { [native code] }", fn.name)) + case _nodeFunctionObject: + return toValue_string(fn.node.source) + case _bindFunctionObject: + return toValue_string("function () { [native code] }") + } + + panic(call.runtime.panicTypeError("Function.toString()")) +} + +func builtinFunction_apply(call FunctionCall) Value { + if !call.This.isCallable() { + panic(call.runtime.panicTypeError()) + } + this := call.Argument(0) + if this.IsUndefined() { + // FIXME Not ECMA5 + this = toValue_object(call.runtime.globalObject) + } + argumentList := call.Argument(1) + switch argumentList.kind { + case valueUndefined, valueNull: + return call.thisObject().call(this, nil, false, nativeFrame) + case valueObject: + default: + panic(call.runtime.panicTypeError()) + } + + arrayObject := argumentList._object() + thisObject := call.thisObject() + length := int64(toUint32(arrayObject.get("length"))) + valueArray := make([]Value, length) + for index := int64(0); index < length; index++ { + valueArray[index] = arrayObject.get(arrayIndexToString(index)) + } + return thisObject.call(this, valueArray, false, nativeFrame) +} + +func builtinFunction_call(call FunctionCall) Value { + if !call.This.isCallable() { + panic(call.runtime.panicTypeError()) + } + thisObject := call.thisObject() + this := call.Argument(0) + if this.IsUndefined() { + // FIXME Not ECMA5 + this = toValue_object(call.runtime.globalObject) + } + if len(call.ArgumentList) >= 1 { + return thisObject.call(this, call.ArgumentList[1:], false, nativeFrame) + } + return thisObject.call(this, nil, false, nativeFrame) +} + +func builtinFunction_bind(call FunctionCall) Value { + target := call.This + if !target.isCallable() { + panic(call.runtime.panicTypeError()) + } + targetObject := target._object() + + this := call.Argument(0) + argumentList := call.slice(1) + if this.IsUndefined() { + // FIXME Do this elsewhere? + this = toValue_object(call.runtime.globalObject) + } + + return toValue_object(call.runtime.newBoundFunction(targetObject, this, argumentList)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_json.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_json.go new file mode 100644 index 0000000..aed54bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_json.go @@ -0,0 +1,299 @@ +package otto + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type _builtinJSON_parseContext struct { + call FunctionCall + reviver Value +} + +func builtinJSON_parse(call FunctionCall) Value { + ctx := _builtinJSON_parseContext{ + call: call, + } + revive := false + if reviver := call.Argument(1); reviver.isCallable() { + revive = true + ctx.reviver = reviver + } + + var root interface{} + err := json.Unmarshal([]byte(call.Argument(0).string()), &root) + if err != nil { + panic(call.runtime.panicSyntaxError(err.Error())) + } + value, exists := builtinJSON_parseWalk(ctx, root) + if !exists { + value = Value{} + } + if revive { + root := ctx.call.runtime.newObject() + root.put("", value, false) + return builtinJSON_reviveWalk(ctx, root, "") + } + return value +} + +func builtinJSON_reviveWalk(ctx _builtinJSON_parseContext, holder *_object, name string) Value { + value := holder.get(name) + if object := value._object(); object != nil { + if isArray(object) { + length := int64(objectLength(object)) + for index := int64(0); index < length; index += 1 { + name := arrayIndexToString(index) + value := builtinJSON_reviveWalk(ctx, object, name) + if value.IsUndefined() { + object.delete(name, false) + } else { + object.defineProperty(name, value, 0111, false) + } + } + } else { + object.enumerate(false, func(name string) bool { + value := builtinJSON_reviveWalk(ctx, object, name) + if value.IsUndefined() { + object.delete(name, false) + } else { + object.defineProperty(name, value, 0111, false) + } + return true + }) + } + } + return ctx.reviver.call(ctx.call.runtime, toValue_object(holder), name, value) +} + +func builtinJSON_parseWalk(ctx _builtinJSON_parseContext, rawValue interface{}) (Value, bool) { + switch value := rawValue.(type) { + case nil: + return nullValue, true + case bool: + return toValue_bool(value), true + case string: + return toValue_string(value), true + case float64: + return toValue_float64(value), true + case []interface{}: + arrayValue := make([]Value, len(value)) + for index, rawValue := range value { + if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { + arrayValue[index] = value + } + } + return toValue_object(ctx.call.runtime.newArrayOf(arrayValue)), true + case map[string]interface{}: + object := ctx.call.runtime.newObject() + for name, rawValue := range value { + if value, exists := builtinJSON_parseWalk(ctx, rawValue); exists { + object.put(name, value, false) + } + } + return toValue_object(object), true + } + return Value{}, false +} + +type _builtinJSON_stringifyContext struct { + call FunctionCall + stack []*_object + propertyList []string + replacerFunction *Value + gap string +} + +func builtinJSON_stringify(call FunctionCall) Value { + ctx := _builtinJSON_stringifyContext{ + call: call, + stack: []*_object{nil}, + } + replacer := call.Argument(1)._object() + if replacer != nil { + if isArray(replacer) { + length := objectLength(replacer) + seen := map[string]bool{} + propertyList := make([]string, length) + length = 0 + for index, _ := range propertyList { + value := replacer.get(arrayIndexToString(int64(index))) + switch value.kind { + case valueObject: + switch value.value.(*_object).class { + case "String": + case "Number": + default: + continue + } + case valueString: + case valueNumber: + default: + continue + } + name := value.string() + if seen[name] { + continue + } + seen[name] = true + length += 1 + propertyList[index] = name + } + ctx.propertyList = propertyList[0:length] + } else if replacer.class == "Function" { + value := toValue_object(replacer) + ctx.replacerFunction = &value + } + } + if spaceValue, exists := call.getArgument(2); exists { + if spaceValue.kind == valueObject { + switch spaceValue.value.(*_object).class { + case "String": + spaceValue = toValue_string(spaceValue.string()) + case "Number": + spaceValue = spaceValue.numberValue() + } + } + switch spaceValue.kind { + case valueString: + value := spaceValue.string() + if len(value) > 10 { + ctx.gap = value[0:10] + } else { + ctx.gap = value + } + case valueNumber: + value := spaceValue.number().int64 + if value > 10 { + value = 10 + } else if value < 0 { + value = 0 + } + ctx.gap = strings.Repeat(" ", int(value)) + } + } + holder := call.runtime.newObject() + holder.put("", call.Argument(0), false) + value, exists := builtinJSON_stringifyWalk(ctx, "", holder) + if !exists { + return Value{} + } + valueJSON, err := json.Marshal(value) + if err != nil { + panic(call.runtime.panicTypeError(err.Error())) + } + if ctx.gap != "" { + valueJSON1 := bytes.Buffer{} + json.Indent(&valueJSON1, valueJSON, "", ctx.gap) + valueJSON = valueJSON1.Bytes() + } + return toValue_string(string(valueJSON)) +} + +func builtinJSON_stringifyWalk(ctx _builtinJSON_stringifyContext, key string, holder *_object) (interface{}, bool) { + value := holder.get(key) + + if value.IsObject() { + object := value._object() + if toJSON := object.get("toJSON"); toJSON.IsFunction() { + value = toJSON.call(ctx.call.runtime, value, key) + } else { + // If the object is a GoStruct or something that implements json.Marshaler + if object.objectClass.marshalJSON != nil { + marshaler := object.objectClass.marshalJSON(object) + if marshaler != nil { + return marshaler, true + } + } + } + } + + if ctx.replacerFunction != nil { + value = (*ctx.replacerFunction).call(ctx.call.runtime, toValue_object(holder), key, value) + } + + if value.kind == valueObject { + switch value.value.(*_object).class { + case "Boolean": + value = value._object().value.(Value) + case "String": + value = toValue_string(value.string()) + case "Number": + value = value.numberValue() + } + } + + switch value.kind { + case valueBoolean: + return value.bool(), true + case valueString: + return value.string(), true + case valueNumber: + integer := value.number() + switch integer.kind { + case numberInteger: + return integer.int64, true + case numberFloat: + return integer.float64, true + default: + return nil, true + } + case valueNull: + return nil, true + case valueObject: + holder := value._object() + if value := value._object(); nil != value { + for _, object := range ctx.stack { + if holder == object { + panic(ctx.call.runtime.panicTypeError("Converting circular structure to JSON")) + } + } + ctx.stack = append(ctx.stack, value) + defer func() { ctx.stack = ctx.stack[:len(ctx.stack)-1] }() + } + if isArray(holder) { + var length uint32 + switch value := holder.get("length").value.(type) { + case uint32: + length = value + case int: + if value >= 0 { + length = uint32(value) + } + default: + panic(ctx.call.runtime.panicTypeError(fmt.Sprintf("JSON.stringify: invalid length: %v (%[1]T)", value))) + } + array := make([]interface{}, length) + for index, _ := range array { + name := arrayIndexToString(int64(index)) + value, _ := builtinJSON_stringifyWalk(ctx, name, holder) + array[index] = value + } + return array, true + } else if holder.class != "Function" { + object := map[string]interface{}{} + if ctx.propertyList != nil { + for _, name := range ctx.propertyList { + value, exists := builtinJSON_stringifyWalk(ctx, name, holder) + if exists { + object[name] = value + } + } + } else { + // Go maps are without order, so this doesn't conform to the ECMA ordering + // standard, but oh well... + holder.enumerate(false, func(name string) bool { + value, exists := builtinJSON_stringifyWalk(ctx, name, holder) + if exists { + object[name] = value + } + return true + }) + } + return object, true + } + } + return nil, false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_math.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_math.go new file mode 100644 index 0000000..7ce90c3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_math.go @@ -0,0 +1,151 @@ +package otto + +import ( + "math" + "math/rand" +) + +// Math + +func builtinMath_abs(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Abs(number)) +} + +func builtinMath_acos(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Acos(number)) +} + +func builtinMath_asin(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Asin(number)) +} + +func builtinMath_atan(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Atan(number)) +} + +func builtinMath_atan2(call FunctionCall) Value { + y := call.Argument(0).float64() + if math.IsNaN(y) { + return NaNValue() + } + x := call.Argument(1).float64() + if math.IsNaN(x) { + return NaNValue() + } + return toValue_float64(math.Atan2(y, x)) +} + +func builtinMath_cos(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Cos(number)) +} + +func builtinMath_ceil(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Ceil(number)) +} + +func builtinMath_exp(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Exp(number)) +} + +func builtinMath_floor(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Floor(number)) +} + +func builtinMath_log(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Log(number)) +} + +func builtinMath_max(call FunctionCall) Value { + switch len(call.ArgumentList) { + case 0: + return negativeInfinityValue() + case 1: + return toValue_float64(call.ArgumentList[0].float64()) + } + result := call.ArgumentList[0].float64() + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := value.float64() + if math.IsNaN(value) { + return NaNValue() + } + result = math.Max(result, value) + } + return toValue_float64(result) +} + +func builtinMath_min(call FunctionCall) Value { + switch len(call.ArgumentList) { + case 0: + return positiveInfinityValue() + case 1: + return toValue_float64(call.ArgumentList[0].float64()) + } + result := call.ArgumentList[0].float64() + if math.IsNaN(result) { + return NaNValue() + } + for _, value := range call.ArgumentList[1:] { + value := value.float64() + if math.IsNaN(value) { + return NaNValue() + } + result = math.Min(result, value) + } + return toValue_float64(result) +} + +func builtinMath_pow(call FunctionCall) Value { + // TODO Make sure this works according to the specification (15.8.2.13) + x := call.Argument(0).float64() + y := call.Argument(1).float64() + if math.Abs(x) == 1 && math.IsInf(y, 0) { + return NaNValue() + } + return toValue_float64(math.Pow(x, y)) +} + +func builtinMath_random(call FunctionCall) Value { + var v float64 + if call.runtime.random != nil { + v = call.runtime.random() + } else { + v = rand.Float64() + } + return toValue_float64(v) +} + +func builtinMath_round(call FunctionCall) Value { + number := call.Argument(0).float64() + value := math.Floor(number + 0.5) + if value == 0 { + value = math.Copysign(0, number) + } + return toValue_float64(value) +} + +func builtinMath_sin(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Sin(number)) +} + +func builtinMath_sqrt(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Sqrt(number)) +} + +func builtinMath_tan(call FunctionCall) Value { + number := call.Argument(0).float64() + return toValue_float64(math.Tan(number)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_number.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_number.go new file mode 100644 index 0000000..f99a42a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_number.go @@ -0,0 +1,93 @@ +package otto + +import ( + "math" + "strconv" +) + +// Number + +func numberValueFromNumberArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return argumentList[0].numberValue() + } + return toValue_int(0) +} + +func builtinNumber(call FunctionCall) Value { + return numberValueFromNumberArgumentList(call.ArgumentList) +} + +func builtinNewNumber(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newNumber(numberValueFromNumberArgumentList(argumentList))) +} + +func builtinNumber_toString(call FunctionCall) Value { + // Will throw a TypeError if ThisObject is not a Number + value := call.thisClassObject("Number").primitiveValue() + radix := 10 + radixArgument := call.Argument(0) + if radixArgument.IsDefined() { + integer := toIntegerFloat(radixArgument) + if integer < 2 || integer > 36 { + panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) + } + radix = int(integer) + } + if radix == 10 { + return toValue_string(value.string()) + } + return toValue_string(numberToStringRadix(value, radix)) +} + +func builtinNumber_valueOf(call FunctionCall) Value { + return call.thisClassObject("Number").primitiveValue() +} + +func builtinNumber_toFixed(call FunctionCall) Value { + precision := toIntegerFloat(call.Argument(0)) + if 20 < precision || 0 > precision { + panic(call.runtime.panicRangeError("toFixed() precision must be between 0 and 20")) + } + if call.This.IsNaN() { + return toValue_string("NaN") + } + value := call.This.float64() + if math.Abs(value) >= 1e21 { + return toValue_string(floatToString(value, 64)) + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'f', int(precision), 64)) +} + +func builtinNumber_toExponential(call FunctionCall) Value { + if call.This.IsNaN() { + return toValue_string("NaN") + } + precision := float64(-1) + if value := call.Argument(0); value.IsDefined() { + precision = toIntegerFloat(value) + if 0 > precision { + panic(call.runtime.panicRangeError("toString() radix must be between 2 and 36")) + } + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'e', int(precision), 64)) +} + +func builtinNumber_toPrecision(call FunctionCall) Value { + if call.This.IsNaN() { + return toValue_string("NaN") + } + value := call.Argument(0) + if value.IsUndefined() { + return toValue_string(call.This.string()) + } + precision := toIntegerFloat(value) + if 1 > precision { + panic(call.runtime.panicRangeError("toPrecision() precision must be greater than 1")) + } + return toValue_string(strconv.FormatFloat(call.This.float64(), 'g', int(precision), 64)) +} + +func builtinNumber_toLocaleString(call FunctionCall) Value { + return builtinNumber_toString(call) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_object.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_object.go new file mode 100644 index 0000000..c2433f7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_object.go @@ -0,0 +1,289 @@ +package otto + +import ( + "fmt" +) + +// Object + +func builtinObject(call FunctionCall) Value { + value := call.Argument(0) + switch value.kind { + case valueUndefined, valueNull: + return toValue_object(call.runtime.newObject()) + } + + return toValue_object(call.runtime.toObject(value)) +} + +func builtinNewObject(self *_object, argumentList []Value) Value { + value := valueOfArrayIndex(argumentList, 0) + switch value.kind { + case valueNull, valueUndefined: + case valueNumber, valueString, valueBoolean: + return toValue_object(self.runtime.toObject(value)) + case valueObject: + return value + default: + } + return toValue_object(self.runtime.newObject()) +} + +func builtinObject_valueOf(call FunctionCall) Value { + return toValue_object(call.thisObject()) +} + +func builtinObject_hasOwnProperty(call FunctionCall) Value { + propertyName := call.Argument(0).string() + thisObject := call.thisObject() + return toValue_bool(thisObject.hasOwnProperty(propertyName)) +} + +func builtinObject_isPrototypeOf(call FunctionCall) Value { + value := call.Argument(0) + if !value.IsObject() { + return falseValue + } + prototype := call.toObject(value).prototype + thisObject := call.thisObject() + for prototype != nil { + if thisObject == prototype { + return trueValue + } + prototype = prototype.prototype + } + return falseValue +} + +func builtinObject_propertyIsEnumerable(call FunctionCall) Value { + propertyName := call.Argument(0).string() + thisObject := call.thisObject() + property := thisObject.getOwnProperty(propertyName) + if property != nil && property.enumerable() { + return trueValue + } + return falseValue +} + +func builtinObject_toString(call FunctionCall) Value { + result := "" + if call.This.IsUndefined() { + result = "[object Undefined]" + } else if call.This.IsNull() { + result = "[object Null]" + } else { + result = fmt.Sprintf("[object %s]", call.thisObject().class) + } + return toValue_string(result) +} + +func builtinObject_toLocaleString(call FunctionCall) Value { + toString := call.thisObject().get("toString") + if !toString.isCallable() { + panic(call.runtime.panicTypeError()) + } + return toString.call(call.runtime, call.This) +} + +func builtinObject_getPrototypeOf(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + if object.prototype == nil { + return nullValue + } + + return toValue_object(object.prototype) +} + +func builtinObject_getOwnPropertyDescriptor(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + name := call.Argument(1).string() + descriptor := object.getOwnProperty(name) + if descriptor == nil { + return Value{} + } + return toValue_object(call.runtime.fromPropertyDescriptor(*descriptor)) +} + +func builtinObject_defineProperty(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + name := call.Argument(1).string() + descriptor := toPropertyDescriptor(call.runtime, call.Argument(2)) + object.defineOwnProperty(name, descriptor, true) + return objectValue +} + +func builtinObject_defineProperties(call FunctionCall) Value { + objectValue := call.Argument(0) + object := objectValue._object() + if object == nil { + panic(call.runtime.panicTypeError()) + } + + properties := call.runtime.toObject(call.Argument(1)) + properties.enumerate(false, func(name string) bool { + descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) + object.defineOwnProperty(name, descriptor, true) + return true + }) + + return objectValue +} + +func builtinObject_create(call FunctionCall) Value { + prototypeValue := call.Argument(0) + if !prototypeValue.IsNull() && !prototypeValue.IsObject() { + panic(call.runtime.panicTypeError()) + } + + object := call.runtime.newObject() + object.prototype = prototypeValue._object() + + propertiesValue := call.Argument(1) + if propertiesValue.IsDefined() { + properties := call.runtime.toObject(propertiesValue) + properties.enumerate(false, func(name string) bool { + descriptor := toPropertyDescriptor(call.runtime, properties.get(name)) + object.defineOwnProperty(name, descriptor, true) + return true + }) + } + + return toValue_object(object) +} + +func builtinObject_isExtensible(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + return toValue_bool(object.extensible) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_preventExtensions(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_isSealed(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + if object.extensible { + return toValue_bool(false) + } + result := true + object.enumerate(true, func(name string) bool { + property := object.getProperty(name) + if property.configurable() { + result = false + } + return true + }) + return toValue_bool(result) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_seal(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.enumerate(true, func(name string) bool { + if property := object.getOwnProperty(name); nil != property && property.configurable() { + property.configureOff() + object.defineOwnProperty(name, *property, true) + } + return true + }) + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_isFrozen(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + if object.extensible { + return toValue_bool(false) + } + result := true + object.enumerate(true, func(name string) bool { + property := object.getProperty(name) + if property.configurable() || property.writable() { + result = false + } + return true + }) + return toValue_bool(result) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_freeze(call FunctionCall) Value { + object := call.Argument(0) + if object := object._object(); object != nil { + object.enumerate(true, func(name string) bool { + if property, update := object.getOwnProperty(name), false; nil != property { + if property.isDataDescriptor() && property.writable() { + property.writeOff() + update = true + } + if property.configurable() { + property.configureOff() + update = true + } + if update { + object.defineOwnProperty(name, *property, true) + } + } + return true + }) + object.extensible = false + } else { + panic(call.runtime.panicTypeError()) + } + return object +} + +func builtinObject_keys(call FunctionCall) Value { + if object, keys := call.Argument(0)._object(), []Value(nil); nil != object { + object.enumerate(false, func(name string) bool { + keys = append(keys, toValue_string(name)) + return true + }) + return toValue_object(call.runtime.newArrayOf(keys)) + } + panic(call.runtime.panicTypeError()) +} + +func builtinObject_getOwnPropertyNames(call FunctionCall) Value { + if object, propertyNames := call.Argument(0)._object(), []Value(nil); nil != object { + object.enumerate(true, func(name string) bool { + if object.hasOwnProperty(name) { + propertyNames = append(propertyNames, toValue_string(name)) + } + return true + }) + return toValue_object(call.runtime.newArrayOf(propertyNames)) + } + panic(call.runtime.panicTypeError()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_regexp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_regexp.go new file mode 100644 index 0000000..9942251 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_regexp.go @@ -0,0 +1,65 @@ +package otto + +import ( + "fmt" +) + +// RegExp + +func builtinRegExp(call FunctionCall) Value { + pattern := call.Argument(0) + flags := call.Argument(1) + if object := pattern._object(); object != nil { + if object.class == "RegExp" && flags.IsUndefined() { + return pattern + } + } + return toValue_object(call.runtime.newRegExp(pattern, flags)) +} + +func builtinNewRegExp(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newRegExp( + valueOfArrayIndex(argumentList, 0), + valueOfArrayIndex(argumentList, 1), + )) +} + +func builtinRegExp_toString(call FunctionCall) Value { + thisObject := call.thisObject() + source := thisObject.get("source").string() + flags := []byte{} + if thisObject.get("global").bool() { + flags = append(flags, 'g') + } + if thisObject.get("ignoreCase").bool() { + flags = append(flags, 'i') + } + if thisObject.get("multiline").bool() { + flags = append(flags, 'm') + } + return toValue_string(fmt.Sprintf("/%s/%s", source, flags)) +} + +func builtinRegExp_exec(call FunctionCall) Value { + thisObject := call.thisObject() + target := call.Argument(0).string() + match, result := execRegExp(thisObject, target) + if !match { + return nullValue + } + return toValue_object(execResultToArray(call.runtime, target, result)) +} + +func builtinRegExp_test(call FunctionCall) Value { + thisObject := call.thisObject() + target := call.Argument(0).string() + match, _ := execRegExp(thisObject, target) + return toValue_bool(match) +} + +func builtinRegExp_compile(call FunctionCall) Value { + // This (useless) function is deprecated, but is here to provide some + // semblance of compatibility. + // Caveat emptor: it may not be around for long. + return Value{} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_string.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_string.go new file mode 100644 index 0000000..6a17184 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/builtin_string.go @@ -0,0 +1,500 @@ +package otto + +import ( + "bytes" + "regexp" + "strconv" + "strings" + "unicode/utf8" +) + +// String + +func stringValueFromStringArgumentList(argumentList []Value) Value { + if len(argumentList) > 0 { + return toValue_string(argumentList[0].string()) + } + return toValue_string("") +} + +func builtinString(call FunctionCall) Value { + return stringValueFromStringArgumentList(call.ArgumentList) +} + +func builtinNewString(self *_object, argumentList []Value) Value { + return toValue_object(self.runtime.newString(stringValueFromStringArgumentList(argumentList))) +} + +func builtinString_toString(call FunctionCall) Value { + return call.thisClassObject("String").primitiveValue() +} +func builtinString_valueOf(call FunctionCall) Value { + return call.thisClassObject("String").primitiveValue() +} + +func builtinString_fromCharCode(call FunctionCall) Value { + chrList := make([]uint16, len(call.ArgumentList)) + for index, value := range call.ArgumentList { + chrList[index] = toUint16(value) + } + return toValue_string16(chrList) +} + +func builtinString_charAt(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + idx := int(call.Argument(0).number().int64) + chr := stringAt(call.This._object().stringValue(), idx) + if chr == utf8.RuneError { + return toValue_string("") + } + return toValue_string(string(chr)) +} + +func builtinString_charCodeAt(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + idx := int(call.Argument(0).number().int64) + chr := stringAt(call.This._object().stringValue(), idx) + if chr == utf8.RuneError { + return NaNValue() + } + return toValue_uint16(uint16(chr)) +} + +func builtinString_concat(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + var value bytes.Buffer + value.WriteString(call.This.string()) + for _, item := range call.ArgumentList { + value.WriteString(item.string()) + } + return toValue_string(value.String()) +} + +func builtinString_indexOf(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + value := call.This.string() + target := call.Argument(0).string() + if 2 > len(call.ArgumentList) { + return toValue_int(strings.Index(value, target)) + } + start := toIntegerFloat(call.Argument(1)) + if 0 > start { + start = 0 + } else if start >= float64(len(value)) { + if target == "" { + return toValue_int(len(value)) + } + return toValue_int(-1) + } + index := strings.Index(value[int(start):], target) + if index >= 0 { + index += int(start) + } + return toValue_int(index) +} + +func builtinString_lastIndexOf(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + value := call.This.string() + target := call.Argument(0).string() + if 2 > len(call.ArgumentList) || call.ArgumentList[1].IsUndefined() { + return toValue_int(strings.LastIndex(value, target)) + } + length := len(value) + if length == 0 { + return toValue_int(strings.LastIndex(value, target)) + } + start := call.ArgumentList[1].number() + if start.kind == numberInfinity { // FIXME + // startNumber is infinity, so start is the end of string (start = length) + return toValue_int(strings.LastIndex(value, target)) + } + if 0 > start.int64 { + start.int64 = 0 + } + end := int(start.int64) + len(target) + if end > length { + end = length + } + return toValue_int(strings.LastIndex(value[:end], target)) +} + +func builtinString_match(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + matcherValue := call.Argument(0) + matcher := matcherValue._object() + if !matcherValue.IsObject() || matcher.class != "RegExp" { + matcher = call.runtime.newRegExp(matcherValue, Value{}) + } + global := matcher.get("global").bool() + if !global { + match, result := execRegExp(matcher, target) + if !match { + return nullValue + } + return toValue_object(execResultToArray(call.runtime, target, result)) + } + + { + result := matcher.regExpValue().regularExpression.FindAllStringIndex(target, -1) + matchCount := len(result) + if result == nil { + matcher.put("lastIndex", toValue_int(0), true) + return Value{} // !match + } + matchCount = len(result) + valueArray := make([]Value, matchCount) + for index := 0; index < matchCount; index++ { + valueArray[index] = toValue_string(target[result[index][0]:result[index][1]]) + } + matcher.put("lastIndex", toValue_int(result[matchCount-1][1]), true) + return toValue_object(call.runtime.newArrayOf(valueArray)) + } +} + +var builtinString_replace_Regexp = regexp.MustCompile("\\$(?:[\\$\\&\\'\\`1-9]|0[1-9]|[1-9][0-9])") + +func builtinString_findAndReplaceString(input []byte, lastIndex int, match []int, target []byte, replaceValue []byte) (output []byte) { + matchCount := len(match) / 2 + output = input + if match[0] != lastIndex { + output = append(output, target[lastIndex:match[0]]...) + } + replacement := builtinString_replace_Regexp.ReplaceAllFunc(replaceValue, func(part []byte) []byte { + // TODO Check if match[0] or match[1] can be -1 in this scenario + switch part[1] { + case '$': + return []byte{'$'} + case '&': + return target[match[0]:match[1]] + case '`': + return target[:match[0]] + case '\'': + return target[match[1]:len(target)] + } + matchNumberParse, error := strconv.ParseInt(string(part[1:]), 10, 64) + matchNumber := int(matchNumberParse) + if error != nil || matchNumber >= matchCount { + return []byte{} + } + offset := 2 * matchNumber + if match[offset] != -1 { + return target[match[offset]:match[offset+1]] + } + return []byte{} // The empty string + }) + output = append(output, replacement...) + return output +} + +func builtinString_replace(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := []byte(call.This.string()) + searchValue := call.Argument(0) + searchObject := searchValue._object() + + // TODO If a capture is -1? + var search *regexp.Regexp + global := false + find := 1 + if searchValue.IsObject() && searchObject.class == "RegExp" { + regExp := searchObject.regExpValue() + search = regExp.regularExpression + if regExp.global { + find = -1 + } + } else { + search = regexp.MustCompile(regexp.QuoteMeta(searchValue.string())) + } + + found := search.FindAllSubmatchIndex(target, find) + if found == nil { + return toValue_string(string(target)) // !match + } + + { + lastIndex := 0 + result := []byte{} + + replaceValue := call.Argument(1) + if replaceValue.isCallable() { + target := string(target) + replace := replaceValue._object() + for _, match := range found { + if match[0] != lastIndex { + result = append(result, target[lastIndex:match[0]]...) + } + matchCount := len(match) / 2 + argumentList := make([]Value, matchCount+2) + for index := 0; index < matchCount; index++ { + offset := 2 * index + if match[offset] != -1 { + argumentList[index] = toValue_string(target[match[offset]:match[offset+1]]) + } else { + argumentList[index] = Value{} + } + } + argumentList[matchCount+0] = toValue_int(match[0]) + argumentList[matchCount+1] = toValue_string(target) + replacement := replace.call(Value{}, argumentList, false, nativeFrame).string() + result = append(result, []byte(replacement)...) + lastIndex = match[1] + } + + } else { + replace := []byte(replaceValue.string()) + for _, match := range found { + result = builtinString_findAndReplaceString(result, lastIndex, match, target, replace) + lastIndex = match[1] + } + } + + if lastIndex != len(target) { + result = append(result, target[lastIndex:]...) + } + + if global && searchObject != nil { + searchObject.put("lastIndex", toValue_int(lastIndex), true) + } + + return toValue_string(string(result)) + } +} + +func builtinString_search(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + searchValue := call.Argument(0) + search := searchValue._object() + if !searchValue.IsObject() || search.class != "RegExp" { + search = call.runtime.newRegExp(searchValue, Value{}) + } + result := search.regExpValue().regularExpression.FindStringIndex(target) + if result == nil { + return toValue_int(-1) + } + return toValue_int(result[0]) +} + +func stringSplitMatch(target string, targetLength int64, index uint, search string, searchLength int64) (bool, uint) { + if int64(index)+searchLength > searchLength { + return false, 0 + } + found := strings.Index(target[index:], search) + if 0 > found { + return false, 0 + } + return true, uint(found) +} + +func builtinString_split(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + separatorValue := call.Argument(0) + limitValue := call.Argument(1) + limit := -1 + if limitValue.IsDefined() { + limit = int(toUint32(limitValue)) + } + + if limit == 0 { + return toValue_object(call.runtime.newArray(0)) + } + + if separatorValue.IsUndefined() { + return toValue_object(call.runtime.newArrayOf([]Value{toValue_string(target)})) + } + + if separatorValue.isRegExp() { + targetLength := len(target) + search := separatorValue._object().regExpValue().regularExpression + valueArray := []Value{} + result := search.FindAllStringSubmatchIndex(target, -1) + lastIndex := 0 + found := 0 + + for _, match := range result { + if match[0] == match[1] { + // FIXME Ugh, this is a hack + if match[0] == 0 || match[0] == targetLength { + continue + } + } + + if lastIndex != match[0] { + valueArray = append(valueArray, toValue_string(target[lastIndex:match[0]])) + found++ + } else if lastIndex == match[0] { + if lastIndex != -1 { + valueArray = append(valueArray, toValue_string("")) + found++ + } + } + + lastIndex = match[1] + if found == limit { + goto RETURN + } + + captureCount := len(match) / 2 + for index := 1; index < captureCount; index++ { + offset := index * 2 + value := Value{} + if match[offset] != -1 { + value = toValue_string(target[match[offset]:match[offset+1]]) + } + valueArray = append(valueArray, value) + found++ + if found == limit { + goto RETURN + } + } + } + + if found != limit { + if lastIndex != targetLength { + valueArray = append(valueArray, toValue_string(target[lastIndex:targetLength])) + } else { + valueArray = append(valueArray, toValue_string("")) + } + } + + RETURN: + return toValue_object(call.runtime.newArrayOf(valueArray)) + + } else { + separator := separatorValue.string() + + splitLimit := limit + excess := false + if limit > 0 { + splitLimit = limit + 1 + excess = true + } + + split := strings.SplitN(target, separator, splitLimit) + + if excess && len(split) > limit { + split = split[:limit] + } + + valueArray := make([]Value, len(split)) + for index, value := range split { + valueArray[index] = toValue_string(value) + } + + return toValue_object(call.runtime.newArrayOf(valueArray)) + } +} + +func builtinString_slice(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + length := int64(len(target)) + start, end := rangeStartEnd(call.ArgumentList, length, false) + if end-start <= 0 { + return toValue_string("") + } + return toValue_string(target[start:end]) +} + +func builtinString_substring(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + target := call.This.string() + + length := int64(len(target)) + start, end := rangeStartEnd(call.ArgumentList, length, true) + if start > end { + start, end = end, start + } + return toValue_string(target[start:end]) +} + +func builtinString_substr(call FunctionCall) Value { + target := call.This.string() + + size := int64(len(target)) + start, length := rangeStartLength(call.ArgumentList, size) + + if start >= size { + return toValue_string("") + } + + if length <= 0 { + return toValue_string("") + } + + if start+length >= size { + // Cap length to be to the end of the string + // start = 3, length = 5, size = 4 [0, 1, 2, 3] + // 4 - 3 = 1 + // target[3:4] + length = size - start + } + + return toValue_string(target[start : start+length]) +} + +func builtinString_toLowerCase(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue_string(strings.ToLower(call.This.string())) +} + +func builtinString_toUpperCase(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue_string(strings.ToUpper(call.This.string())) +} + +// 7.2 Table 2 — Whitespace Characters & 7.3 Table 3 - Line Terminator Characters +const builtinString_trim_whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF" + +func builtinString_trim(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.Trim(call.This.string(), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimLeft(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.TrimLeft(call.This.string(), + builtinString_trim_whitespace)) +} + +// Mozilla extension, not ECMAScript 5 +func builtinString_trimRight(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + return toValue(strings.TrimRight(call.This.string(), + builtinString_trim_whitespace)) +} + +func builtinString_localeCompare(call FunctionCall) Value { + checkObjectCoercible(call.runtime, call.This) + this := call.This.string() + that := call.Argument(0).string() + if this < that { + return toValue_int(-1) + } else if this == that { + return toValue_int(0) + } + return toValue_int(1) +} + +/* +An alternate version of String.trim +func builtinString_trim(call FunctionCall) Value { + checkObjectCoercible(call.This) + return toValue_string(strings.TrimFunc(call.string(.This), isWhiteSpaceOrLineTerminator)) +} +*/ + +func builtinString_toLocaleLowerCase(call FunctionCall) Value { + return builtinString_toLowerCase(call) +} + +func builtinString_toLocaleUpperCase(call FunctionCall) Value { + return builtinString_toUpperCase(call) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/clone.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/clone.go new file mode 100644 index 0000000..23b59f8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/clone.go @@ -0,0 +1,173 @@ +package otto + +import ( + "fmt" +) + +type _clone struct { + runtime *_runtime + _object map[*_object]*_object + _objectStash map[*_objectStash]*_objectStash + _dclStash map[*_dclStash]*_dclStash + _fnStash map[*_fnStash]*_fnStash +} + +func (in *_runtime) clone() *_runtime { + + in.lck.Lock() + defer in.lck.Unlock() + + out := &_runtime{ + debugger: in.debugger, + random: in.random, + stackLimit: in.stackLimit, + traceLimit: in.traceLimit, + } + + clone := _clone{ + runtime: out, + _object: make(map[*_object]*_object), + _objectStash: make(map[*_objectStash]*_objectStash), + _dclStash: make(map[*_dclStash]*_dclStash), + _fnStash: make(map[*_fnStash]*_fnStash), + } + + globalObject := clone.object(in.globalObject) + out.globalStash = out.newObjectStash(globalObject, nil) + out.globalObject = globalObject + out.global = _global{ + clone.object(in.global.Object), + clone.object(in.global.Function), + clone.object(in.global.Array), + clone.object(in.global.String), + clone.object(in.global.Boolean), + clone.object(in.global.Number), + clone.object(in.global.Math), + clone.object(in.global.Date), + clone.object(in.global.RegExp), + clone.object(in.global.Error), + clone.object(in.global.EvalError), + clone.object(in.global.TypeError), + clone.object(in.global.RangeError), + clone.object(in.global.ReferenceError), + clone.object(in.global.SyntaxError), + clone.object(in.global.URIError), + clone.object(in.global.JSON), + + clone.object(in.global.ObjectPrototype), + clone.object(in.global.FunctionPrototype), + clone.object(in.global.ArrayPrototype), + clone.object(in.global.StringPrototype), + clone.object(in.global.BooleanPrototype), + clone.object(in.global.NumberPrototype), + clone.object(in.global.DatePrototype), + clone.object(in.global.RegExpPrototype), + clone.object(in.global.ErrorPrototype), + clone.object(in.global.EvalErrorPrototype), + clone.object(in.global.TypeErrorPrototype), + clone.object(in.global.RangeErrorPrototype), + clone.object(in.global.ReferenceErrorPrototype), + clone.object(in.global.SyntaxErrorPrototype), + clone.object(in.global.URIErrorPrototype), + } + + out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object) + out.globalObject.prototype = out.global.ObjectPrototype + + // Not sure if this is necessary, but give some help to the GC + clone.runtime = nil + clone._object = nil + clone._objectStash = nil + clone._dclStash = nil + clone._fnStash = nil + + return out +} + +func (clone *_clone) object(in *_object) *_object { + if out, exists := clone._object[in]; exists { + return out + } + out := &_object{} + clone._object[in] = out + return in.objectClass.clone(in, out, clone) +} + +func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) { + if out, exists := clone._dclStash[in]; exists { + return out, true + } + out := &_dclStash{} + clone._dclStash[in] = out + return out, false +} + +func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) { + if out, exists := clone._objectStash[in]; exists { + return out, true + } + out := &_objectStash{} + clone._objectStash[in] = out + return out, false +} + +func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) { + if out, exists := clone._fnStash[in]; exists { + return out, true + } + out := &_fnStash{} + clone._fnStash[in] = out + return out, false +} + +func (clone *_clone) value(in Value) Value { + out := in + switch value := in.value.(type) { + case *_object: + out.value = clone.object(value) + } + return out +} + +func (clone *_clone) valueArray(in []Value) []Value { + out := make([]Value, len(in)) + for index, value := range in { + out[index] = clone.value(value) + } + return out +} + +func (clone *_clone) stash(in _stash) _stash { + if in == nil { + return nil + } + return in.clone(clone) +} + +func (clone *_clone) property(in _property) _property { + out := in + + switch value := in.value.(type) { + case Value: + out.value = clone.value(value) + case _propertyGetSet: + p := _propertyGetSet{} + if value[0] != nil { + p[0] = clone.object(value[0]) + } + if value[1] != nil { + p[1] = clone.object(value[1]) + } + out.value = p + default: + panic(fmt.Errorf("in.value.(Value) != true; in.value is %T", in.value)) + } + + return out +} + +func (clone *_clone) dclProperty(in _dclProperty) _dclProperty { + out := in + out.value = clone.value(in.value) + return out +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl.go new file mode 100644 index 0000000..c191b45 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl.go @@ -0,0 +1,24 @@ +package otto + +import ( + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" +) + +type _file struct { + name string + src string + base int // This will always be 1 or greater +} + +type _compiler struct { + file *file.File + program *ast.Program +} + +func (cmpl *_compiler) parse() *_nodeProgram { + if cmpl.program != nil { + cmpl.file = cmpl.program.File + } + return cmpl._parse(cmpl.program) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go new file mode 100644 index 0000000..6741bf3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate.go @@ -0,0 +1,96 @@ +package otto + +import ( + "strconv" +) + +func (self *_runtime) cmpl_evaluate_nodeProgram(node *_nodeProgram, eval bool) Value { + if !eval { + self.enterGlobalScope() + defer func() { + self.leaveScope() + }() + } + self.cmpl_functionDeclaration(node.functionList) + self.cmpl_variableDeclaration(node.varList) + self.scope.frame.file = node.file + return self.cmpl_evaluate_nodeStatementList(node.body) +} + +func (self *_runtime) cmpl_call_nodeFunction(function *_object, stash *_fnStash, node *_nodeFunctionLiteral, this Value, argumentList []Value) Value { + + indexOfParameterName := make([]string, len(argumentList)) + // function(abc, def, ghi) + // indexOfParameterName[0] = "abc" + // indexOfParameterName[1] = "def" + // indexOfParameterName[2] = "ghi" + // ... + + argumentsFound := false + for index, name := range node.parameterList { + if name == "arguments" { + argumentsFound = true + } + value := Value{} + if index < len(argumentList) { + value = argumentList[index] + indexOfParameterName[index] = name + } + // strict = false + self.scope.lexical.setValue(name, value, false) + } + + if !argumentsFound { + arguments := self.newArgumentsObject(indexOfParameterName, stash, len(argumentList)) + arguments.defineProperty("callee", toValue_object(function), 0101, false) + stash.arguments = arguments + // strict = false + self.scope.lexical.setValue("arguments", toValue_object(arguments), false) + for index, _ := range argumentList { + if index < len(node.parameterList) { + continue + } + indexAsString := strconv.FormatInt(int64(index), 10) + arguments.defineProperty(indexAsString, argumentList[index], 0111, false) + } + } + + self.cmpl_functionDeclaration(node.functionList) + self.cmpl_variableDeclaration(node.varList) + + result := self.cmpl_evaluate_nodeStatement(node.body) + if result.kind == valueResult { + return result + } + + return Value{} +} + +func (self *_runtime) cmpl_functionDeclaration(list []*_nodeFunctionLiteral) { + executionContext := self.scope + eval := executionContext.eval + stash := executionContext.variable + + for _, function := range list { + name := function.name + value := self.cmpl_evaluate_nodeExpression(function) + if !stash.hasBinding(name) { + stash.createBinding(name, eval == true, value) + } else { + // TODO 10.5.5.e + stash.setBinding(name, value, false) // TODO strict + } + } +} + +func (self *_runtime) cmpl_variableDeclaration(list []string) { + executionContext := self.scope + eval := executionContext.eval + stash := executionContext.variable + + for _, name := range list { + if !stash.hasBinding(name) { + stash.createBinding(name, eval == true, Value{}) // TODO strict? + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go new file mode 100644 index 0000000..8586a48 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_expression.go @@ -0,0 +1,460 @@ +package otto + +import ( + "fmt" + "math" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeExpression(node _nodeExpression) Value { + // Allow interpreter interruption + // If the Interrupt channel is nil, then + // we avoid runtime.Gosched() overhead (if any) + // FIXME: Test this + if self.otto.Interrupt != nil { + runtime.Gosched() + select { + case value := <-self.otto.Interrupt: + value() + default: + } + } + + switch node := node.(type) { + + case *_nodeArrayLiteral: + return self.cmpl_evaluate_nodeArrayLiteral(node) + + case *_nodeAssignExpression: + return self.cmpl_evaluate_nodeAssignExpression(node) + + case *_nodeBinaryExpression: + if node.comparison { + return self.cmpl_evaluate_nodeBinaryExpression_comparison(node) + } else { + return self.cmpl_evaluate_nodeBinaryExpression(node) + } + + case *_nodeBracketExpression: + return self.cmpl_evaluate_nodeBracketExpression(node) + + case *_nodeCallExpression: + return self.cmpl_evaluate_nodeCallExpression(node, nil) + + case *_nodeConditionalExpression: + return self.cmpl_evaluate_nodeConditionalExpression(node) + + case *_nodeDotExpression: + return self.cmpl_evaluate_nodeDotExpression(node) + + case *_nodeFunctionLiteral: + var local = self.scope.lexical + if node.name != "" { + local = self.newDeclarationStash(local) + } + + value := toValue_object(self.newNodeFunction(node, local)) + if node.name != "" { + local.createBinding(node.name, false, value) + } + return value + + case *_nodeIdentifier: + name := node.name + // TODO Should be true or false (strictness) depending on context + // getIdentifierReference should not return nil, but we check anyway and panic + // so as not to propagate the nil into something else + reference := getIdentifierReference(self, self.scope.lexical, name, false, _at(node.idx)) + if reference == nil { + // Should never get here! + panic(hereBeDragons("referenceError == nil: " + name)) + } + return toValue(reference) + + case *_nodeLiteral: + return node.value + + case *_nodeNewExpression: + return self.cmpl_evaluate_nodeNewExpression(node) + + case *_nodeObjectLiteral: + return self.cmpl_evaluate_nodeObjectLiteral(node) + + case *_nodeRegExpLiteral: + return toValue_object(self._newRegExp(node.pattern, node.flags)) + + case *_nodeSequenceExpression: + return self.cmpl_evaluate_nodeSequenceExpression(node) + + case *_nodeThisExpression: + return toValue_object(self.scope.this) + + case *_nodeUnaryExpression: + return self.cmpl_evaluate_nodeUnaryExpression(node) + + case *_nodeVariableExpression: + return self.cmpl_evaluate_nodeVariableExpression(node) + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeExpression(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeArrayLiteral(node *_nodeArrayLiteral) Value { + + valueArray := []Value{} + + for _, node := range node.value { + if node == nil { + valueArray = append(valueArray, emptyValue) + } else { + valueArray = append(valueArray, self.cmpl_evaluate_nodeExpression(node).resolve()) + } + } + + result := self.newArrayOf(valueArray) + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeAssignExpression(node *_nodeAssignExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + right := self.cmpl_evaluate_nodeExpression(node.right) + rightValue := right.resolve() + + result := rightValue + if node.operator != token.ASSIGN { + result = self.calculateBinaryExpression(node.operator, left, rightValue) + } + + self.putValue(left.reference(), result) + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left) + leftValue := left.resolve() + + switch node.operator { + // Logical + case token.LOGICAL_AND: + if !leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + case token.LOGICAL_OR: + if leftValue.bool() { + return leftValue + } + right := self.cmpl_evaluate_nodeExpression(node.right) + return right.resolve() + } + + return self.calculateBinaryExpression(node.operator, leftValue, self.cmpl_evaluate_nodeExpression(node.right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBinaryExpression_comparison(node *_nodeBinaryExpression) Value { + + left := self.cmpl_evaluate_nodeExpression(node.left).resolve() + right := self.cmpl_evaluate_nodeExpression(node.right).resolve() + + return toValue_bool(self.calculateComparison(node.operator, left, right)) +} + +func (self *_runtime) cmpl_evaluate_nodeBracketExpression(node *_nodeBracketExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + member := self.cmpl_evaluate_nodeExpression(node.member) + memberValue := member.resolve() + + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(self.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), _at(node.idx))) + } + return toValue(newPropertyReference(self, object, memberValue.string(), false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeCallExpression(node *_nodeCallExpression, withArgumentList []interface{}) Value { + rt := self + this := Value{} + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + if withArgumentList != nil { + argumentList = self.toValueArray(withArgumentList...) + } else { + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + } + + rf := callee.reference() + vl := callee.resolve() + + eval := false // Whether this call is a (candidate for) direct call to eval + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + object := rf.base + this = toValue_object(object) + eval = rf.name == "eval" // Possible direct eval + case *_stashReference: + // TODO ImplicitThisValue + name = rf.name + eval = rf.name == "eval" // Possible direct eval + default: + // FIXME? + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + frame := _frame{ + callee: name, + file: self.scope.frame.file, + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().call(this, argumentList, eval, frame) +} + +func (self *_runtime) cmpl_evaluate_nodeConditionalExpression(node *_nodeConditionalExpression) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeExpression(node.consequent) + } + return self.cmpl_evaluate_nodeExpression(node.alternate) +} + +func (self *_runtime) cmpl_evaluate_nodeDotExpression(node *_nodeDotExpression) Value { + target := self.cmpl_evaluate_nodeExpression(node.left) + targetValue := target.resolve() + // TODO Pass in base value as-is, and defer toObject till later? + object, err := self.objectCoerce(targetValue) + if err != nil { + panic(self.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), _at(node.idx))) + } + return toValue(newPropertyReference(self, object, node.identifier, false, _at(node.idx))) +} + +func (self *_runtime) cmpl_evaluate_nodeNewExpression(node *_nodeNewExpression) Value { + rt := self + callee := self.cmpl_evaluate_nodeExpression(node.callee) + + argumentList := []Value{} + for _, argumentNode := range node.argumentList { + argumentList = append(argumentList, self.cmpl_evaluate_nodeExpression(argumentNode).resolve()) + } + + rf := callee.reference() + vl := callee.resolve() + + name := "" + if rf != nil { + switch rf := rf.(type) { + case *_propertyReference: + name = rf.name + case *_stashReference: + name = rf.name + default: + panic(rt.panicTypeError("Here be dragons")) + } + } + + at := _at(-1) + switch callee := node.callee.(type) { + case *_nodeIdentifier: + at = _at(callee.idx) + case *_nodeDotExpression: + at = _at(callee.idx) + case *_nodeBracketExpression: + at = _at(callee.idx) + } + + if !vl.IsFunction() { + if name == "" { + // FIXME Maybe typeof? + panic(rt.panicTypeError("%v is not a function", vl, at)) + } + panic(rt.panicTypeError("'%s' is not a function", name, at)) + } + + self.scope.frame.offset = int(at) + + return vl._object().construct(argumentList) +} + +func (self *_runtime) cmpl_evaluate_nodeObjectLiteral(node *_nodeObjectLiteral) Value { + + result := self.newObject() + + for _, property := range node.value { + switch property.kind { + case "value": + result.defineProperty(property.key, self.cmpl_evaluate_nodeExpression(property.value).resolve(), 0111, false) + case "get": + getter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{getter, nil} + result.defineOwnProperty(property.key, descriptor, false) + case "set": + setter := self.newNodeFunction(property.value.(*_nodeFunctionLiteral), self.scope.lexical) + descriptor := _property{} + descriptor.mode = 0211 + descriptor.value = _propertyGetSet{nil, setter} + result.defineOwnProperty(property.key, descriptor, false) + default: + panic(fmt.Errorf("Here be dragons: evaluate_nodeObjectLiteral: invalid property.Kind: %v", property.kind)) + } + } + + return toValue_object(result) +} + +func (self *_runtime) cmpl_evaluate_nodeSequenceExpression(node *_nodeSequenceExpression) Value { + var result Value + for _, node := range node.sequence { + result = self.cmpl_evaluate_nodeExpression(node) + result = result.resolve() + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeUnaryExpression(node *_nodeUnaryExpression) Value { + + target := self.cmpl_evaluate_nodeExpression(node.operand) + switch node.operator { + case token.TYPEOF, token.DELETE: + if target.kind == valueReference && target.reference().invalid() { + if node.operator == token.TYPEOF { + return toValue_string("undefined") + } + return trueValue + } + } + + switch node.operator { + case token.NOT: + targetValue := target.resolve() + if targetValue.bool() { + return falseValue + } + return trueValue + case token.BITWISE_NOT: + targetValue := target.resolve() + integerValue := toInt32(targetValue) + return toValue_int32(^integerValue) + case token.PLUS: + targetValue := target.resolve() + return toValue_float64(targetValue.float64()) + case token.MINUS: + targetValue := target.resolve() + value := targetValue.float64() + // TODO Test this + sign := float64(-1) + if math.Signbit(value) { + sign = 1 + } + return toValue_float64(math.Copysign(value, sign)) + case token.INCREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix++ + oldValue := targetValue.float64() + newValue := toValue_float64(+1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // ++Prefix + newValue := toValue_float64(+1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.DECREMENT: + targetValue := target.resolve() + if node.postfix { + // Postfix-- + oldValue := targetValue.float64() + newValue := toValue_float64(-1 + oldValue) + self.putValue(target.reference(), newValue) + return toValue_float64(oldValue) + } else { + // --Prefix + newValue := toValue_float64(-1 + targetValue.float64()) + self.putValue(target.reference(), newValue) + return newValue + } + case token.VOID: + target.resolve() // FIXME Side effect? + return Value{} + case token.DELETE: + reference := target.reference() + if reference == nil { + return trueValue + } + return toValue_bool(target.reference().delete()) + case token.TYPEOF: + targetValue := target.resolve() + switch targetValue.kind { + case valueUndefined: + return toValue_string("undefined") + case valueNull: + return toValue_string("object") + case valueBoolean: + return toValue_string("boolean") + case valueNumber: + return toValue_string("number") + case valueString: + return toValue_string("string") + case valueObject: + if targetValue._object().isCall() { + return toValue_string("function") + } + return toValue_string("object") + default: + // FIXME ? + } + } + + panic(hereBeDragons()) +} + +func (self *_runtime) cmpl_evaluate_nodeVariableExpression(node *_nodeVariableExpression) Value { + if node.initializer != nil { + // FIXME If reference is nil + left := getIdentifierReference(self, self.scope.lexical, node.name, false, _at(node.idx)) + right := self.cmpl_evaluate_nodeExpression(node.initializer) + rightValue := right.resolve() + + self.putValue(left, rightValue) + } + return toValue_string(node.name) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go new file mode 100644 index 0000000..e16c6ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_evaluate_statement.go @@ -0,0 +1,424 @@ +package otto + +import ( + "fmt" + "runtime" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) cmpl_evaluate_nodeStatement(node _nodeStatement) Value { + // Allow interpreter interruption + // If the Interrupt channel is nil, then + // we avoid runtime.Gosched() overhead (if any) + // FIXME: Test this + if self.otto.Interrupt != nil { + runtime.Gosched() + select { + case value := <-self.otto.Interrupt: + value() + default: + } + } + + switch node := node.(type) { + + case *_nodeBlockStatement: + labels := self.labels + self.labels = nil + + value := self.cmpl_evaluate_nodeStatementList(node.list) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultBreak: + return emptyValue + } + } + return value + + case *_nodeBranchStatement: + target := node.label + switch node.branch { // FIXME Maybe node.kind? node.operator? + case token.BREAK: + return toValue(newBreakResult(target)) + case token.CONTINUE: + return toValue(newContinueResult(target)) + } + + case *_nodeDebuggerStatement: + if self.debugger != nil { + self.debugger(self.otto) + } + return emptyValue // Nothing happens. + + case *_nodeDoWhileStatement: + return self.cmpl_evaluate_nodeDoWhileStatement(node) + + case *_nodeEmptyStatement: + return emptyValue + + case *_nodeExpressionStatement: + return self.cmpl_evaluate_nodeExpression(node.expression) + + case *_nodeForInStatement: + return self.cmpl_evaluate_nodeForInStatement(node) + + case *_nodeForStatement: + return self.cmpl_evaluate_nodeForStatement(node) + + case *_nodeIfStatement: + return self.cmpl_evaluate_nodeIfStatement(node) + + case *_nodeLabelledStatement: + self.labels = append(self.labels, node.label) + defer func() { + if len(self.labels) > 0 { + self.labels = self.labels[:len(self.labels)-1] // Pop the label + } else { + self.labels = nil + } + }() + return self.cmpl_evaluate_nodeStatement(node.statement) + + case *_nodeReturnStatement: + if node.argument != nil { + return toValue(newReturnResult(self.cmpl_evaluate_nodeExpression(node.argument).resolve())) + } + return toValue(newReturnResult(Value{})) + + case *_nodeSwitchStatement: + return self.cmpl_evaluate_nodeSwitchStatement(node) + + case *_nodeThrowStatement: + value := self.cmpl_evaluate_nodeExpression(node.argument).resolve() + panic(newException(value)) + + case *_nodeTryStatement: + return self.cmpl_evaluate_nodeTryStatement(node) + + case *_nodeVariableStatement: + // Variables are already defined, this is initialization only + for _, variable := range node.list { + self.cmpl_evaluate_nodeVariableExpression(variable.(*_nodeVariableExpression)) + } + return emptyValue + + case *_nodeWhileStatement: + return self.cmpl_evaluate_nodeWhileStatement(node) + + case *_nodeWithStatement: + return self.cmpl_evaluate_nodeWithStatement(node) + + } + + panic(fmt.Errorf("Here be dragons: evaluate_nodeStatement(%T)", node)) +} + +func (self *_runtime) cmpl_evaluate_nodeStatementList(list []_nodeStatement) Value { + var result Value + for _, node := range list { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + return value + case valueEmpty: + default: + // We have getValue here to (for example) trigger a + // ReferenceError (of the not defined variety) + // Not sure if this is the best way to error out early + // for such errors or if there is a better way + // TODO Do we still need this? + result = value.resolve() + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeDoWhileStatement(node *_nodeDoWhileStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + test := node.test + + result := emptyValue +resultBreak: + for { + for _, node := range node.body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: do ... while (false) + break + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForInStatement(node *_nodeForInStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + source := self.cmpl_evaluate_nodeExpression(node.source) + sourceValue := source.resolve() + + switch sourceValue.kind { + case valueUndefined, valueNull: + return emptyValue + } + + sourceObject := self.toObject(sourceValue) + + into := node.into + body := node.body + + result := emptyValue + object := sourceObject + for object != nil { + enumerateValue := emptyValue + object.enumerate(false, func(name string) bool { + into := self.cmpl_evaluate_nodeExpression(into) + // In the case of: for (var abc in def) ... + if into.reference() == nil { + identifier := into.string() + // TODO Should be true or false (strictness) depending on context + into = toValue(getIdentifierReference(self, self.scope.lexical, identifier, false, -1)) + } + self.putValue(into.reference(), toValue_string(name)) + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + enumerateValue = value + return false + case resultBreak: + object = nil + return false + case resultContinue: + return true + } + case valueEmpty: + default: + enumerateValue = value + } + } + return true + }) + if object == nil { + break + } + object = object.prototype + if !enumerateValue.isEmpty() { + result = enumerateValue + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeForStatement(node *_nodeForStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + initializer := node.initializer + test := node.test + update := node.update + body := node.body + + if initializer != nil { + initialResult := self.cmpl_evaluate_nodeExpression(initializer) + initialResult.resolve() // Side-effect trigger + } + + result := emptyValue +resultBreak: + for { + if test != nil { + testResult := self.cmpl_evaluate_nodeExpression(test) + testResultValue := testResult.resolve() + if testResultValue.bool() == false { + break + } + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreak + case resultContinue: + goto resultContinue + } + case valueEmpty: + default: + result = value + } + } + resultContinue: + if update != nil { + updateResult := self.cmpl_evaluate_nodeExpression(update) + updateResult.resolve() // Side-effect trigger + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeIfStatement(node *_nodeIfStatement) Value { + test := self.cmpl_evaluate_nodeExpression(node.test) + testValue := test.resolve() + if testValue.bool() { + return self.cmpl_evaluate_nodeStatement(node.consequent) + } else if node.alternate != nil { + return self.cmpl_evaluate_nodeStatement(node.alternate) + } + + return emptyValue +} + +func (self *_runtime) cmpl_evaluate_nodeSwitchStatement(node *_nodeSwitchStatement) Value { + + labels := append(self.labels, "") + self.labels = nil + + discriminantResult := self.cmpl_evaluate_nodeExpression(node.discriminant) + target := node.default_ + + for index, clause := range node.body { + test := clause.test + if test != nil { + if self.calculateComparison(token.STRICT_EQUAL, discriminantResult, self.cmpl_evaluate_nodeExpression(test)) { + target = index + break + } + } + } + + result := emptyValue + if target != -1 { + for _, clause := range node.body[target:] { + for _, statement := range clause.consequent { + value := self.cmpl_evaluate_nodeStatement(statement) + switch value.kind { + case valueResult: + switch value.evaluateBreak(labels) { + case resultReturn: + return value + case resultBreak: + return emptyValue + } + case valueEmpty: + default: + result = value + } + } + } + } + + return result +} + +func (self *_runtime) cmpl_evaluate_nodeTryStatement(node *_nodeTryStatement) Value { + tryCatchValue, exception := self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.body) + }) + + if exception && node.catch != nil { + outer := self.scope.lexical + self.scope.lexical = self.newDeclarationStash(outer) + defer func() { + self.scope.lexical = outer + }() + // TODO If necessary, convert TypeError => TypeError + // That, is, such errors can be thrown despite not being JavaScript "native" + // strict = false + self.scope.lexical.setValue(node.catch.parameter, tryCatchValue, false) + + // FIXME node.CatchParameter + // FIXME node.Catch + tryCatchValue, exception = self.tryCatchEvaluate(func() Value { + return self.cmpl_evaluate_nodeStatement(node.catch.body) + }) + } + + if node.finally != nil { + finallyValue := self.cmpl_evaluate_nodeStatement(node.finally) + if finallyValue.kind == valueResult { + return finallyValue + } + } + + if exception { + panic(newException(tryCatchValue)) + } + + return tryCatchValue +} + +func (self *_runtime) cmpl_evaluate_nodeWhileStatement(node *_nodeWhileStatement) Value { + + test := node.test + body := node.body + labels := append(self.labels, "") + self.labels = nil + + result := emptyValue +resultBreakContinue: + for { + if !self.cmpl_evaluate_nodeExpression(test).resolve().bool() { + // Stahp: while (false) ... + break + } + for _, node := range body { + value := self.cmpl_evaluate_nodeStatement(node) + switch value.kind { + case valueResult: + switch value.evaluateBreakContinue(labels) { + case resultReturn: + return value + case resultBreak: + break resultBreakContinue + case resultContinue: + continue resultBreakContinue + } + case valueEmpty: + default: + result = value + } + } + } + return result +} + +func (self *_runtime) cmpl_evaluate_nodeWithStatement(node *_nodeWithStatement) Value { + object := self.cmpl_evaluate_nodeExpression(node.object) + outer := self.scope.lexical + lexical := self.newObjectStash(self.toObject(object.resolve()), outer) + self.scope.lexical = lexical + defer func() { + self.scope.lexical = outer + }() + + return self.cmpl_evaluate_nodeStatement(node.body) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_parse.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_parse.go new file mode 100644 index 0000000..dc5baa1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/cmpl_parse.go @@ -0,0 +1,656 @@ +package otto + +import ( + "fmt" + "regexp" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +var trueLiteral = &_nodeLiteral{value: toValue_bool(true)} +var falseLiteral = &_nodeLiteral{value: toValue_bool(false)} +var nullLiteral = &_nodeLiteral{value: nullValue} +var emptyStatement = &_nodeEmptyStatement{} + +func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression { + if in == nil { + return nil + } + + switch in := in.(type) { + + case *ast.ArrayLiteral: + out := &_nodeArrayLiteral{ + value: make([]_nodeExpression, len(in.Value)), + } + for i, value := range in.Value { + out.value[i] = cmpl.parseExpression(value) + } + return out + + case *ast.AssignExpression: + return &_nodeAssignExpression{ + operator: in.Operator, + left: cmpl.parseExpression(in.Left), + right: cmpl.parseExpression(in.Right), + } + + case *ast.BinaryExpression: + return &_nodeBinaryExpression{ + operator: in.Operator, + left: cmpl.parseExpression(in.Left), + right: cmpl.parseExpression(in.Right), + comparison: in.Comparison, + } + + case *ast.BooleanLiteral: + if in.Value { + return trueLiteral + } + return falseLiteral + + case *ast.BracketExpression: + return &_nodeBracketExpression{ + idx: in.Left.Idx0(), + left: cmpl.parseExpression(in.Left), + member: cmpl.parseExpression(in.Member), + } + + case *ast.CallExpression: + out := &_nodeCallExpression{ + callee: cmpl.parseExpression(in.Callee), + argumentList: make([]_nodeExpression, len(in.ArgumentList)), + } + for i, value := range in.ArgumentList { + out.argumentList[i] = cmpl.parseExpression(value) + } + return out + + case *ast.ConditionalExpression: + return &_nodeConditionalExpression{ + test: cmpl.parseExpression(in.Test), + consequent: cmpl.parseExpression(in.Consequent), + alternate: cmpl.parseExpression(in.Alternate), + } + + case *ast.DotExpression: + return &_nodeDotExpression{ + idx: in.Left.Idx0(), + left: cmpl.parseExpression(in.Left), + identifier: in.Identifier.Name, + } + + case *ast.EmptyExpression: + return nil + + case *ast.FunctionLiteral: + name := "" + if in.Name != nil { + name = in.Name.Name + } + out := &_nodeFunctionLiteral{ + name: name, + body: cmpl.parseStatement(in.Body), + source: in.Source, + file: cmpl.file, + } + if in.ParameterList != nil { + list := in.ParameterList.List + out.parameterList = make([]string, len(list)) + for i, value := range list { + out.parameterList[i] = value.Name + } + } + for _, value := range in.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + out.varList = append(out.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: parseProgram.declaration(%T)", value)) + } + } + return out + + case *ast.Identifier: + return &_nodeIdentifier{ + idx: in.Idx, + name: in.Name, + } + + case *ast.NewExpression: + out := &_nodeNewExpression{ + callee: cmpl.parseExpression(in.Callee), + argumentList: make([]_nodeExpression, len(in.ArgumentList)), + } + for i, value := range in.ArgumentList { + out.argumentList[i] = cmpl.parseExpression(value) + } + return out + + case *ast.NullLiteral: + return nullLiteral + + case *ast.NumberLiteral: + return &_nodeLiteral{ + value: toValue(in.Value), + } + + case *ast.ObjectLiteral: + out := &_nodeObjectLiteral{ + value: make([]_nodeProperty, len(in.Value)), + } + for i, value := range in.Value { + out.value[i] = _nodeProperty{ + key: value.Key, + kind: value.Kind, + value: cmpl.parseExpression(value.Value), + } + } + return out + + case *ast.RegExpLiteral: + return &_nodeRegExpLiteral{ + flags: in.Flags, + pattern: in.Pattern, + } + + case *ast.SequenceExpression: + out := &_nodeSequenceExpression{ + sequence: make([]_nodeExpression, len(in.Sequence)), + } + for i, value := range in.Sequence { + out.sequence[i] = cmpl.parseExpression(value) + } + return out + + case *ast.StringLiteral: + return &_nodeLiteral{ + value: toValue_string(in.Value), + } + + case *ast.ThisExpression: + return &_nodeThisExpression{} + + case *ast.UnaryExpression: + return &_nodeUnaryExpression{ + operator: in.Operator, + operand: cmpl.parseExpression(in.Operand), + postfix: in.Postfix, + } + + case *ast.VariableExpression: + return &_nodeVariableExpression{ + idx: in.Idx0(), + name: in.Name, + initializer: cmpl.parseExpression(in.Initializer), + } + + } + + panic(fmt.Errorf("Here be dragons: cmpl.parseExpression(%T)", in)) +} + +func (cmpl *_compiler) parseStatement(in ast.Statement) _nodeStatement { + if in == nil { + return nil + } + + switch in := in.(type) { + + case *ast.BlockStatement: + out := &_nodeBlockStatement{ + list: make([]_nodeStatement, len(in.List)), + } + for i, value := range in.List { + out.list[i] = cmpl.parseStatement(value) + } + return out + + case *ast.BranchStatement: + out := &_nodeBranchStatement{ + branch: in.Token, + } + if in.Label != nil { + out.label = in.Label.Name + } + return out + + case *ast.DebuggerStatement: + return &_nodeDebuggerStatement{} + + case *ast.DoWhileStatement: + out := &_nodeDoWhileStatement{ + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.EmptyStatement: + return emptyStatement + + case *ast.ExpressionStatement: + return &_nodeExpressionStatement{ + expression: cmpl.parseExpression(in.Expression), + } + + case *ast.ForInStatement: + out := &_nodeForInStatement{ + into: cmpl.parseExpression(in.Into), + source: cmpl.parseExpression(in.Source), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.ForStatement: + out := &_nodeForStatement{ + initializer: cmpl.parseExpression(in.Initializer), + update: cmpl.parseExpression(in.Update), + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.FunctionStatement: + return emptyStatement + + case *ast.IfStatement: + return &_nodeIfStatement{ + test: cmpl.parseExpression(in.Test), + consequent: cmpl.parseStatement(in.Consequent), + alternate: cmpl.parseStatement(in.Alternate), + } + + case *ast.LabelledStatement: + return &_nodeLabelledStatement{ + label: in.Label.Name, + statement: cmpl.parseStatement(in.Statement), + } + + case *ast.ReturnStatement: + return &_nodeReturnStatement{ + argument: cmpl.parseExpression(in.Argument), + } + + case *ast.SwitchStatement: + out := &_nodeSwitchStatement{ + discriminant: cmpl.parseExpression(in.Discriminant), + default_: in.Default, + body: make([]*_nodeCaseStatement, len(in.Body)), + } + for i, clause := range in.Body { + out.body[i] = &_nodeCaseStatement{ + test: cmpl.parseExpression(clause.Test), + consequent: make([]_nodeStatement, len(clause.Consequent)), + } + for j, value := range clause.Consequent { + out.body[i].consequent[j] = cmpl.parseStatement(value) + } + } + return out + + case *ast.ThrowStatement: + return &_nodeThrowStatement{ + argument: cmpl.parseExpression(in.Argument), + } + + case *ast.TryStatement: + out := &_nodeTryStatement{ + body: cmpl.parseStatement(in.Body), + finally: cmpl.parseStatement(in.Finally), + } + if in.Catch != nil { + out.catch = &_nodeCatchStatement{ + parameter: in.Catch.Parameter.Name, + body: cmpl.parseStatement(in.Catch.Body), + } + } + return out + + case *ast.VariableStatement: + out := &_nodeVariableStatement{ + list: make([]_nodeExpression, len(in.List)), + } + for i, value := range in.List { + out.list[i] = cmpl.parseExpression(value) + } + return out + + case *ast.WhileStatement: + out := &_nodeWhileStatement{ + test: cmpl.parseExpression(in.Test), + } + body := cmpl.parseStatement(in.Body) + if block, ok := body.(*_nodeBlockStatement); ok { + out.body = block.list + } else { + out.body = append(out.body, body) + } + return out + + case *ast.WithStatement: + return &_nodeWithStatement{ + object: cmpl.parseExpression(in.Object), + body: cmpl.parseStatement(in.Body), + } + + } + + panic(fmt.Errorf("Here be dragons: cmpl.parseStatement(%T)", in)) +} + +func cmpl_parse(in *ast.Program) *_nodeProgram { + cmpl := _compiler{ + program: in, + } + return cmpl.parse() +} + +func (cmpl *_compiler) _parse(in *ast.Program) *_nodeProgram { + out := &_nodeProgram{ + body: make([]_nodeStatement, len(in.Body)), + file: in.File, + } + for i, value := range in.Body { + out.body[i] = cmpl.parseStatement(value) + } + for _, value := range in.DeclarationList { + switch value := value.(type) { + case *ast.FunctionDeclaration: + out.functionList = append(out.functionList, cmpl.parseExpression(value.Function).(*_nodeFunctionLiteral)) + case *ast.VariableDeclaration: + for _, value := range value.List { + out.varList = append(out.varList, value.Name) + } + default: + panic(fmt.Errorf("Here be dragons: cmpl.parseProgram.DeclarationList(%T)", value)) + } + } + return out +} + +type _nodeProgram struct { + body []_nodeStatement + + varList []string + functionList []*_nodeFunctionLiteral + + variableList []_nodeDeclaration + + file *file.File +} + +type _nodeDeclaration struct { + name string + definition _node +} + +type _node interface { +} + +type ( + _nodeExpression interface { + _node + _expressionNode() + } + + _nodeArrayLiteral struct { + value []_nodeExpression + } + + _nodeAssignExpression struct { + operator token.Token + left _nodeExpression + right _nodeExpression + } + + _nodeBinaryExpression struct { + operator token.Token + left _nodeExpression + right _nodeExpression + comparison bool + } + + _nodeBracketExpression struct { + idx file.Idx + left _nodeExpression + member _nodeExpression + } + + _nodeCallExpression struct { + callee _nodeExpression + argumentList []_nodeExpression + } + + _nodeConditionalExpression struct { + test _nodeExpression + consequent _nodeExpression + alternate _nodeExpression + } + + _nodeDotExpression struct { + idx file.Idx + left _nodeExpression + identifier string + } + + _nodeFunctionLiteral struct { + name string + body _nodeStatement + source string + parameterList []string + varList []string + functionList []*_nodeFunctionLiteral + file *file.File + } + + _nodeIdentifier struct { + idx file.Idx + name string + } + + _nodeLiteral struct { + value Value + } + + _nodeNewExpression struct { + callee _nodeExpression + argumentList []_nodeExpression + } + + _nodeObjectLiteral struct { + value []_nodeProperty + } + + _nodeProperty struct { + key string + kind string + value _nodeExpression + } + + _nodeRegExpLiteral struct { + flags string + pattern string // Value? + regexp *regexp.Regexp + } + + _nodeSequenceExpression struct { + sequence []_nodeExpression + } + + _nodeThisExpression struct { + } + + _nodeUnaryExpression struct { + operator token.Token + operand _nodeExpression + postfix bool + } + + _nodeVariableExpression struct { + idx file.Idx + name string + initializer _nodeExpression + } +) + +type ( + _nodeStatement interface { + _node + _statementNode() + } + + _nodeBlockStatement struct { + list []_nodeStatement + } + + _nodeBranchStatement struct { + branch token.Token + label string + } + + _nodeCaseStatement struct { + test _nodeExpression + consequent []_nodeStatement + } + + _nodeCatchStatement struct { + parameter string + body _nodeStatement + } + + _nodeDebuggerStatement struct { + } + + _nodeDoWhileStatement struct { + test _nodeExpression + body []_nodeStatement + } + + _nodeEmptyStatement struct { + } + + _nodeExpressionStatement struct { + expression _nodeExpression + } + + _nodeForInStatement struct { + into _nodeExpression + source _nodeExpression + body []_nodeStatement + } + + _nodeForStatement struct { + initializer _nodeExpression + update _nodeExpression + test _nodeExpression + body []_nodeStatement + } + + _nodeIfStatement struct { + test _nodeExpression + consequent _nodeStatement + alternate _nodeStatement + } + + _nodeLabelledStatement struct { + label string + statement _nodeStatement + } + + _nodeReturnStatement struct { + argument _nodeExpression + } + + _nodeSwitchStatement struct { + discriminant _nodeExpression + default_ int + body []*_nodeCaseStatement + } + + _nodeThrowStatement struct { + argument _nodeExpression + } + + _nodeTryStatement struct { + body _nodeStatement + catch *_nodeCatchStatement + finally _nodeStatement + } + + _nodeVariableStatement struct { + list []_nodeExpression + } + + _nodeWhileStatement struct { + test _nodeExpression + body []_nodeStatement + } + + _nodeWithStatement struct { + object _nodeExpression + body _nodeStatement + } +) + +// _expressionNode + +func (*_nodeArrayLiteral) _expressionNode() {} +func (*_nodeAssignExpression) _expressionNode() {} +func (*_nodeBinaryExpression) _expressionNode() {} +func (*_nodeBracketExpression) _expressionNode() {} +func (*_nodeCallExpression) _expressionNode() {} +func (*_nodeConditionalExpression) _expressionNode() {} +func (*_nodeDotExpression) _expressionNode() {} +func (*_nodeFunctionLiteral) _expressionNode() {} +func (*_nodeIdentifier) _expressionNode() {} +func (*_nodeLiteral) _expressionNode() {} +func (*_nodeNewExpression) _expressionNode() {} +func (*_nodeObjectLiteral) _expressionNode() {} +func (*_nodeRegExpLiteral) _expressionNode() {} +func (*_nodeSequenceExpression) _expressionNode() {} +func (*_nodeThisExpression) _expressionNode() {} +func (*_nodeUnaryExpression) _expressionNode() {} +func (*_nodeVariableExpression) _expressionNode() {} + +// _statementNode + +func (*_nodeBlockStatement) _statementNode() {} +func (*_nodeBranchStatement) _statementNode() {} +func (*_nodeCaseStatement) _statementNode() {} +func (*_nodeCatchStatement) _statementNode() {} +func (*_nodeDebuggerStatement) _statementNode() {} +func (*_nodeDoWhileStatement) _statementNode() {} +func (*_nodeEmptyStatement) _statementNode() {} +func (*_nodeExpressionStatement) _statementNode() {} +func (*_nodeForInStatement) _statementNode() {} +func (*_nodeForStatement) _statementNode() {} +func (*_nodeIfStatement) _statementNode() {} +func (*_nodeLabelledStatement) _statementNode() {} +func (*_nodeReturnStatement) _statementNode() {} +func (*_nodeSwitchStatement) _statementNode() {} +func (*_nodeThrowStatement) _statementNode() {} +func (*_nodeTryStatement) _statementNode() {} +func (*_nodeVariableStatement) _statementNode() {} +func (*_nodeWhileStatement) _statementNode() {} +func (*_nodeWithStatement) _statementNode() {} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/console.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/console.go new file mode 100644 index 0000000..948face --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/console.go @@ -0,0 +1,51 @@ +package otto + +import ( + "fmt" + "os" + "strings" +) + +func formatForConsole(argumentList []Value) string { + output := []string{} + for _, argument := range argumentList { + output = append(output, fmt.Sprintf("%v", argument)) + } + return strings.Join(output, " ") +} + +func builtinConsole_log(call FunctionCall) Value { + fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) + return Value{} +} + +func builtinConsole_error(call FunctionCall) Value { + fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) + return Value{} +} + +// Nothing happens. +func builtinConsole_dir(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_time(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_timeEnd(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_trace(call FunctionCall) Value { + return Value{} +} + +func builtinConsole_assert(call FunctionCall) Value { + return Value{} +} + +func (runtime *_runtime) newConsole() *_object { + + return newConsoleObject(runtime) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg.go new file mode 100644 index 0000000..51fbdc2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg.go @@ -0,0 +1,9 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg + +package otto + +import ( + Dbg "github.com/robertkrimen/otto/dbg" +) + +var dbg, dbgf = Dbg.New() diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg/dbg.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg/dbg.go new file mode 100644 index 0000000..8c27fa2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/dbg/dbg.go @@ -0,0 +1,387 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg + +/* +Package dbg is a println/printf/log-debugging utility library. + + import ( + Dbg "github.com/robertkrimen/dbg" + ) + + dbg, dbgf := Dbg.New() + + dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi) + # "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793" + + dbgf("With a %s formatting %.2f", "little", math.Pi) + # "2013/01/28 16:51:55 With a little formatting (3.14)" + + dbgf("%/fatal//A fatal debug statement: should not be here") + # "A fatal debug statement: should not be here" + # ...and then, os.Exit(1) + + dbgf("%/panic//Can also panic %s", "this") + # "Can also panic this" + # ...as a panic, equivalent to: panic("Can also panic this") + + dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()") + # "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()" + + dbgf("%d %d", 1, 2, 3, 4, 5) + # "2013/01/28 17:16:32 Another example: 1 2 3 4 5" + + dbgf("%@: Include the function name for a little context (via %s)", "%@") + # "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)" + +By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output. +However, you can also provide your own output destination by invoking dbg.New with +a customization function: + + import ( + "bytes" + Dbg "github.com/robertkrimen/dbg" + "os" + ) + + # dbg to os.Stderr + dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { + dbgr.SetOutput(os.Stderr) + }) + + # A slightly contrived example: + var buffer bytes.Buffer + dbg, dbgf := New(func(dbgr *Dbgr) { + dbgr.SetOutput(&buffer) + }) + +*/ +package dbg + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "regexp" + "runtime" + "strings" + "unicode" +) + +type _frmt struct { + ctl string + format string + operandCount int + panic bool + fatal bool + check bool +} + +var ( + ctlTest = regexp.MustCompile(`^\s*%/`) + ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`) +) + +func operandCount(format string) int { + count := 0 + end := len(format) + for at := 0; at < end; { + for at < end && format[at] != '%' { + at++ + } + at++ + if at < end { + if format[at] != '%' && format[at] != '@' { + count++ + } + at++ + } + } + return count +} + +func parseFormat(format string) (frmt _frmt) { + if ctlTest.MatchString(format) { + format = strings.TrimLeftFunc(format, unicode.IsSpace) + index := strings.Index(format, "//") + if index != -1 { + frmt.ctl = format[0:index] + format = format[index+2:] // Skip the second slash via +2 (instead of +1) + } else { + frmt.ctl = format + format = "" + } + for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) { + for _, value := range tmp[1:] { + switch value { + case "panic": + frmt.panic = true + case "fatal": + frmt.fatal = true + case "check": + frmt.check = true + } + } + } + } + frmt.format = format + frmt.operandCount = operandCount(format) + return +} + +type Dbgr struct { + emit _emit +} + +type DbgFunction func(values ...interface{}) + +func NewDbgr() *Dbgr { + self := &Dbgr{} + return self +} + +/* +New will create and return a pair of debugging functions. You can customize where +they output to by passing in an (optional) customization function: + + import ( + Dbg "github.com/robertkrimen/dbg" + "os" + ) + + # dbg to os.Stderr + dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { + dbgr.SetOutput(os.Stderr) + }) + +*/ +func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) { + dbgr := NewDbgr() + if len(options) > 0 { + if fn, ok := options[0].(func(*Dbgr)); ok { + fn(dbgr) + } + } + return dbgr.DbgDbgf() +} + +func (self Dbgr) Dbg(values ...interface{}) { + self.getEmit().emit(_frmt{}, "", values...) +} + +func (self Dbgr) Dbgf(values ...interface{}) { + self.dbgf(values...) +} + +func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) { + dbg = func(vl ...interface{}) { + self.Dbg(vl...) + } + dbgf = func(vl ...interface{}) { + self.dbgf(vl...) + } + return dbg, dbgf // Redundant, but... +} + +func (self Dbgr) dbgf(values ...interface{}) { + + var frmt _frmt + if len(values) > 0 { + tmp := fmt.Sprint(values[0]) + frmt = parseFormat(tmp) + values = values[1:] + } + + buffer_f := bytes.Buffer{} + format := frmt.format + end := len(format) + for at := 0; at < end; { + last := at + for at < end && format[at] != '%' { + at++ + } + if at > last { + buffer_f.WriteString(format[last:at]) + } + if at >= end { + break + } + // format[at] == '%' + at++ + // format[at] == ? + if format[at] == '@' { + depth := 2 + pc, _, _, _ := runtime.Caller(depth) + name := runtime.FuncForPC(pc).Name() + buffer_f.WriteString(name) + } else { + buffer_f.WriteString(format[at-1 : at+1]) + } + at++ + } + + //values_f := append([]interface{}{}, values[0:frmt.operandCount]...) + values_f := values[0:frmt.operandCount] + values_dbg := values[frmt.operandCount:] + if len(values_dbg) > 0 { + // Adjust frmt.format: + // (%v instead of %s because: frmt.check) + { + tmp := format + if len(tmp) > 0 { + if unicode.IsSpace(rune(tmp[len(tmp)-1])) { + buffer_f.WriteString("%v") + } else { + buffer_f.WriteString(" %v") + } + } else if frmt.check { + // Performing a check, so no output + } else { + buffer_f.WriteString("%v") + } + } + + // Adjust values_f: + if !frmt.check { + tmp := []string{} + for _, value := range values_dbg { + tmp = append(tmp, fmt.Sprintf("%v", value)) + } + // First, make a copy of values_f, so we avoid overwriting values_dbg when appending + values_f = append([]interface{}{}, values_f...) + values_f = append(values_f, strings.Join(tmp, " ")) + } + } + + format = buffer_f.String() + if frmt.check { + // We do not actually emit to the log, but panic if + // a non-nil value is detected (e.g. a non-nil error) + for _, value := range values_dbg { + if value != nil { + if format == "" { + panic(value) + } else { + panic(fmt.Sprintf(format, append(values_f, value)...)) + } + } + } + } else { + self.getEmit().emit(frmt, format, values_f...) + } +} + +// Idiot-proof &Dbgr{}, etc. +func (self *Dbgr) getEmit() _emit { + if self.emit == nil { + self.emit = standardEmit() + } + return self.emit +} + +// SetOutput will accept the following as a destination for output: +// +// *log.Logger Print*/Panic*/Fatal* of the logger +// io.Writer - +// nil Reset to the default output (os.Stderr) +// "log" Print*/Panic*/Fatal* via the "log" package +// +func (self *Dbgr) SetOutput(output interface{}) { + if output == nil { + self.emit = standardEmit() + return + } + switch output := output.(type) { + case *log.Logger: + self.emit = _emitLogger{ + logger: output, + } + return + case io.Writer: + self.emit = _emitWriter{ + writer: output, + } + return + case string: + if output == "log" { + self.emit = _emitLog{} + return + } + } + panic(output) +} + +// ======== // +// = emit = // +// ======== // + +func standardEmit() _emit { + return _emitWriter{ + writer: os.Stderr, + } +} + +func ln(tmp string) string { + length := len(tmp) + if length > 0 && tmp[length-1] != '\n' { + return tmp + "\n" + } + return tmp +} + +type _emit interface { + emit(_frmt, string, ...interface{}) +} + +type _emitWriter struct { + writer io.Writer +} + +func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + fmt.Fprintln(self.writer, values...) + } else { + if frmt.panic { + panic(fmt.Sprintf(format, values...)) + } + fmt.Fprintf(self.writer, ln(format), values...) + if frmt.fatal { + os.Exit(1) + } + } +} + +type _emitLogger struct { + logger *log.Logger +} + +func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + self.logger.Println(values...) + } else { + if frmt.panic { + self.logger.Panicf(format, values...) + } else if frmt.fatal { + self.logger.Fatalf(format, values...) + } else { + self.logger.Printf(format, values...) + } + } +} + +type _emitLog struct { +} + +func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) { + if format == "" { + log.Println(values...) + } else { + if frmt.panic { + log.Panicf(format, values...) + } else if frmt.fatal { + log.Fatalf(format, values...) + } else { + log.Printf(format, values...) + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/error.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/error.go new file mode 100644 index 0000000..41a5c10 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/error.go @@ -0,0 +1,252 @@ +package otto + +import ( + "errors" + "fmt" + + "github.com/robertkrimen/otto/file" +) + +type _exception struct { + value interface{} +} + +func newException(value interface{}) *_exception { + return &_exception{ + value: value, + } +} + +func (self *_exception) eject() interface{} { + value := self.value + self.value = nil // Prevent Go from holding on to the value, whatever it is + return value +} + +type _error struct { + name string + message string + trace []_frame + + offset int +} + +func (err _error) format() string { + if len(err.name) == 0 { + return err.message + } + if len(err.message) == 0 { + return err.name + } + return fmt.Sprintf("%s: %s", err.name, err.message) +} + +func (err _error) formatWithStack() string { + str := err.format() + "\n" + for _, frame := range err.trace { + str += " at " + frame.location() + "\n" + } + return str +} + +type _frame struct { + native bool + nativeFile string + nativeLine int + file *file.File + offset int + callee string +} + +var ( + nativeFrame = _frame{} +) + +type _at int + +func (fr _frame) location() string { + str := "" + + switch { + case fr.native: + str = "" + if fr.nativeFile != "" && fr.nativeLine != 0 { + str = fmt.Sprintf("%s:%d", fr.nativeFile, fr.nativeLine) + } + case fr.file != nil: + if p := fr.file.Position(file.Idx(fr.offset)); p != nil { + path, line, column := p.Filename, p.Line, p.Column + + if path == "" { + path = "" + } + + str = fmt.Sprintf("%s:%d:%d", path, line, column) + } + } + + if fr.callee != "" { + str = fmt.Sprintf("%s (%s)", fr.callee, str) + } + + return str +} + +// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc. +type Error struct { + _error +} + +// Error returns a description of the error +// +// TypeError: 'def' is not a function +// +func (err Error) Error() string { + return err.format() +} + +// String returns a description of the error and a trace of where the +// error occurred. +// +// TypeError: 'def' is not a function +// at xyz (:3:9) +// at :7:1/ +// +func (err Error) String() string { + return err.formatWithStack() +} + +func (err _error) describe(format string, in ...interface{}) string { + return fmt.Sprintf(format, in...) +} + +func (self _error) messageValue() Value { + if self.message == "" { + return Value{} + } + return toValue_string(self.message) +} + +func (rt *_runtime) typeErrorResult(throw bool) bool { + if throw { + panic(rt.panicTypeError()) + } + return false +} + +func newError(rt *_runtime, name string, stackFramesToPop int, in ...interface{}) _error { + err := _error{ + name: name, + offset: -1, + } + description := "" + length := len(in) + + if rt != nil && rt.scope != nil { + scope := rt.scope + + for i := 0; i < stackFramesToPop; i++ { + if scope.outer != nil { + scope = scope.outer + } + } + + frame := scope.frame + + if length > 0 { + if at, ok := in[length-1].(_at); ok { + in = in[0 : length-1] + if scope != nil { + frame.offset = int(at) + } + length-- + } + if length > 0 { + description, in = in[0].(string), in[1:] + } + } + + limit := rt.traceLimit + + err.trace = append(err.trace, frame) + if scope != nil { + for scope = scope.outer; scope != nil; scope = scope.outer { + if limit--; limit == 0 { + break + } + + if scope.frame.offset >= 0 { + err.trace = append(err.trace, scope.frame) + } + } + } + } else { + if length > 0 { + description, in = in[0].(string), in[1:] + } + } + err.message = err.describe(description, in...) + + return err +} + +func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "TypeError", 0, argumentList...), + } +} + +func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "ReferenceError", 0, argumentList...), + } +} + +func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "URIError", 0, argumentList...), + } +} + +func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "SyntaxError", 0, argumentList...), + } +} + +func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception { + return &_exception{ + value: newError(rt, "RangeError", 0, argumentList...), + } +} + +func catchPanic(function func()) (err error) { + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + switch caught := caught.(type) { + case *Error: + err = caught + return + case _error: + err = &Error{caught} + return + case Value: + if vl := caught._object(); vl != nil { + switch vl := vl.value.(type) { + case _error: + err = &Error{vl} + return + } + } + err = errors.New(caught.string()) + return + } + panic(caught) + } + }() + function() + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/evaluate.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/evaluate.go new file mode 100644 index 0000000..093054c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/evaluate.go @@ -0,0 +1,318 @@ +package otto + +import ( + "fmt" + "math" + "strings" + + "github.com/robertkrimen/otto/token" +) + +func (self *_runtime) evaluateMultiply(left float64, right float64) Value { + // TODO 11.5.1 + return Value{} +} + +func (self *_runtime) evaluateDivide(left float64, right float64) Value { + if math.IsNaN(left) || math.IsNaN(right) { + return NaNValue() + } + if math.IsInf(left, 0) && math.IsInf(right, 0) { + return NaNValue() + } + if left == 0 && right == 0 { + return NaNValue() + } + if math.IsInf(left, 0) { + if math.Signbit(left) == math.Signbit(right) { + return positiveInfinityValue() + } else { + return negativeInfinityValue() + } + } + if math.IsInf(right, 0) { + if math.Signbit(left) == math.Signbit(right) { + return positiveZeroValue() + } else { + return negativeZeroValue() + } + } + if right == 0 { + if math.Signbit(left) == math.Signbit(right) { + return positiveInfinityValue() + } else { + return negativeInfinityValue() + } + } + return toValue_float64(left / right) +} + +func (self *_runtime) evaluateModulo(left float64, right float64) Value { + // TODO 11.5.3 + return Value{} +} + +func (self *_runtime) calculateBinaryExpression(operator token.Token, left Value, right Value) Value { + + leftValue := left.resolve() + + switch operator { + + // Additive + case token.PLUS: + leftValue = toPrimitive(leftValue) + rightValue := right.resolve() + rightValue = toPrimitive(rightValue) + + if leftValue.IsString() || rightValue.IsString() { + return toValue_string(strings.Join([]string{leftValue.string(), rightValue.string()}, "")) + } else { + return toValue_float64(leftValue.float64() + rightValue.float64()) + } + case token.MINUS: + rightValue := right.resolve() + return toValue_float64(leftValue.float64() - rightValue.float64()) + + // Multiplicative + case token.MULTIPLY: + rightValue := right.resolve() + return toValue_float64(leftValue.float64() * rightValue.float64()) + case token.SLASH: + rightValue := right.resolve() + return self.evaluateDivide(leftValue.float64(), rightValue.float64()) + case token.REMAINDER: + rightValue := right.resolve() + return toValue_float64(math.Mod(leftValue.float64(), rightValue.float64())) + + // Logical + case token.LOGICAL_AND: + left := leftValue.bool() + if !left { + return falseValue + } + return toValue_bool(right.resolve().bool()) + case token.LOGICAL_OR: + left := leftValue.bool() + if left { + return trueValue + } + return toValue_bool(right.resolve().bool()) + + // Bitwise + case token.AND: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) & toInt32(rightValue)) + case token.OR: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) | toInt32(rightValue)) + case token.EXCLUSIVE_OR: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) ^ toInt32(rightValue)) + + // Shift + // (Masking of 0x1f is to restrict the shift to a maximum of 31 places) + case token.SHIFT_LEFT: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) << (toUint32(rightValue) & 0x1f)) + case token.SHIFT_RIGHT: + rightValue := right.resolve() + return toValue_int32(toInt32(leftValue) >> (toUint32(rightValue) & 0x1f)) + case token.UNSIGNED_SHIFT_RIGHT: + rightValue := right.resolve() + // Shifting an unsigned integer is a logical shift + return toValue_uint32(toUint32(leftValue) >> (toUint32(rightValue) & 0x1f)) + + case token.INSTANCEOF: + rightValue := right.resolve() + if !rightValue.IsObject() { + panic(self.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue)) + } + return toValue_bool(rightValue._object().hasInstance(leftValue)) + + case token.IN: + rightValue := right.resolve() + if !rightValue.IsObject() { + panic(self.panicTypeError()) + } + return toValue_bool(rightValue._object().hasProperty(leftValue.string())) + } + + panic(hereBeDragons(operator)) +} + +func valueKindDispatchKey(left _valueKind, right _valueKind) int { + return (int(left) << 2) + int(right) +} + +var equalDispatch map[int](func(Value, Value) bool) = makeEqualDispatch() + +func makeEqualDispatch() map[int](func(Value, Value) bool) { + key := valueKindDispatchKey + return map[int](func(Value, Value) bool){ + + key(valueNumber, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueString, valueObject): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueObject, valueNumber): func(x Value, y Value) bool { return x.float64() == y.float64() }, + key(valueObject, valueString): func(x Value, y Value) bool { return x.float64() == y.float64() }, + } +} + +type _lessThanResult int + +const ( + lessThanFalse _lessThanResult = iota + lessThanTrue + lessThanUndefined +) + +func calculateLessThan(left Value, right Value, leftFirst bool) _lessThanResult { + + x := Value{} + y := x + + if leftFirst { + x = toNumberPrimitive(left) + y = toNumberPrimitive(right) + } else { + y = toNumberPrimitive(right) + x = toNumberPrimitive(left) + } + + result := false + if x.kind != valueString || y.kind != valueString { + x, y := x.float64(), y.float64() + if math.IsNaN(x) || math.IsNaN(y) { + return lessThanUndefined + } + result = x < y + } else { + x, y := x.string(), y.string() + result = x < y + } + + if result { + return lessThanTrue + } + + return lessThanFalse +} + +// FIXME Probably a map is not the most efficient way to do this +var lessThanTable [4](map[_lessThanResult]bool) = [4](map[_lessThanResult]bool){ + // < + map[_lessThanResult]bool{ + lessThanFalse: false, + lessThanTrue: true, + lessThanUndefined: false, + }, + + // > + map[_lessThanResult]bool{ + lessThanFalse: false, + lessThanTrue: true, + lessThanUndefined: false, + }, + + // <= + map[_lessThanResult]bool{ + lessThanFalse: true, + lessThanTrue: false, + lessThanUndefined: false, + }, + + // >= + map[_lessThanResult]bool{ + lessThanFalse: true, + lessThanTrue: false, + lessThanUndefined: false, + }, +} + +func (self *_runtime) calculateComparison(comparator token.Token, left Value, right Value) bool { + + // FIXME Use strictEqualityComparison? + // TODO This might be redundant now (with regards to evaluateComparison) + x := left.resolve() + y := right.resolve() + + kindEqualKind := false + result := true + negate := false + + switch comparator { + case token.LESS: + result = lessThanTable[0][calculateLessThan(x, y, true)] + case token.GREATER: + result = lessThanTable[1][calculateLessThan(y, x, false)] + case token.LESS_OR_EQUAL: + result = lessThanTable[2][calculateLessThan(y, x, false)] + case token.GREATER_OR_EQUAL: + result = lessThanTable[3][calculateLessThan(x, y, true)] + case token.STRICT_NOT_EQUAL: + negate = true + fallthrough + case token.STRICT_EQUAL: + if x.kind != y.kind { + result = false + } else { + kindEqualKind = true + } + case token.NOT_EQUAL: + negate = true + fallthrough + case token.EQUAL: + if x.kind == y.kind { + kindEqualKind = true + } else if x.kind <= valueNull && y.kind <= valueNull { + result = true + } else if x.kind <= valueNull || y.kind <= valueNull { + result = false + } else if x.kind <= valueString && y.kind <= valueString { + result = x.float64() == y.float64() + } else if x.kind == valueBoolean { + result = self.calculateComparison(token.EQUAL, toValue_float64(x.float64()), y) + } else if y.kind == valueBoolean { + result = self.calculateComparison(token.EQUAL, x, toValue_float64(y.float64())) + } else if x.kind == valueObject { + result = self.calculateComparison(token.EQUAL, toPrimitive(x), y) + } else if y.kind == valueObject { + result = self.calculateComparison(token.EQUAL, x, toPrimitive(y)) + } else { + panic(hereBeDragons("Unable to test for equality: %v ==? %v", x, y)) + } + default: + panic(fmt.Errorf("Unknown comparator %s", comparator.String())) + } + + if kindEqualKind { + switch x.kind { + case valueUndefined, valueNull: + result = true + case valueNumber: + x := x.float64() + y := y.float64() + if math.IsNaN(x) || math.IsNaN(y) { + result = false + } else { + result = x == y + } + case valueString: + result = x.string() == y.string() + case valueBoolean: + result = x.bool() == y.bool() + case valueObject: + result = x._object() == y._object() + default: + goto ERROR + } + } + + if negate { + result = !result + } + + return result + +ERROR: + panic(hereBeDragons("%v (%v) %s %v (%v)", x, x.kind, comparator, y, y.kind)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/README.markdown new file mode 100644 index 0000000..79757ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/README.markdown @@ -0,0 +1,110 @@ +# file +-- + import "github.com/robertkrimen/otto/file" + +Package file encapsulates the file abstractions used by the ast & parser. + +## Usage + +#### type File + +```go +type File struct { +} +``` + + +#### func NewFile + +```go +func NewFile(filename, src string, base int) *File +``` + +#### func (*File) Base + +```go +func (fl *File) Base() int +``` + +#### func (*File) Name + +```go +func (fl *File) Name() string +``` + +#### func (*File) Source + +```go +func (fl *File) Source() string +``` + +#### type FileSet + +```go +type FileSet struct { +} +``` + +A FileSet represents a set of source files. + +#### func (*FileSet) AddFile + +```go +func (self *FileSet) AddFile(filename, src string) int +``` +AddFile adds a new file with the given filename and src. + +This an internal method, but exported for cross-package use. + +#### func (*FileSet) File + +```go +func (self *FileSet) File(idx Idx) *File +``` + +#### func (*FileSet) Position + +```go +func (self *FileSet) Position(idx Idx) *Position +``` +Position converts an Idx in the FileSet into a Position. + +#### type Idx + +```go +type Idx int +``` + +Idx is a compact encoding of a source position within a file set. It can be +converted into a Position for a more convenient, but much larger, +representation. + +#### type Position + +```go +type Position struct { + Filename string // The filename where the error occurred, if any + Offset int // The src offset + Line int // The line number, starting at 1 + Column int // The column number, starting at 1 (The character count) + +} +``` + +Position describes an arbitrary source position including the filename, line, +and column location. + +#### func (*Position) String + +```go +func (self *Position) String() string +``` +String returns a string in one of several forms: + + file:line:column A valid position with filename + line:column A valid position without filename + file An invalid position with filename + - An invalid position without filename + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/file.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/file.go new file mode 100644 index 0000000..9093206 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/file/file.go @@ -0,0 +1,164 @@ +// Package file encapsulates the file abstractions used by the ast & parser. +// +package file + +import ( + "fmt" + "strings" + + "gopkg.in/sourcemap.v1" +) + +// Idx is a compact encoding of a source position within a file set. +// It can be converted into a Position for a more convenient, but much +// larger, representation. +type Idx int + +// Position describes an arbitrary source position +// including the filename, line, and column location. +type Position struct { + Filename string // The filename where the error occurred, if any + Offset int // The src offset + Line int // The line number, starting at 1 + Column int // The column number, starting at 1 (The character count) + +} + +// A Position is valid if the line number is > 0. + +func (self *Position) isValid() bool { + return self.Line > 0 +} + +// String returns a string in one of several forms: +// +// file:line:column A valid position with filename +// line:column A valid position without filename +// file An invalid position with filename +// - An invalid position without filename +// +func (self *Position) String() string { + str := self.Filename + if self.isValid() { + if str != "" { + str += ":" + } + str += fmt.Sprintf("%d:%d", self.Line, self.Column) + } + if str == "" { + str = "-" + } + return str +} + +// FileSet + +// A FileSet represents a set of source files. +type FileSet struct { + files []*File + last *File +} + +// AddFile adds a new file with the given filename and src. +// +// This an internal method, but exported for cross-package use. +func (self *FileSet) AddFile(filename, src string) int { + base := self.nextBase() + file := &File{ + name: filename, + src: src, + base: base, + } + self.files = append(self.files, file) + self.last = file + return base +} + +func (self *FileSet) nextBase() int { + if self.last == nil { + return 1 + } + return self.last.base + len(self.last.src) + 1 +} + +func (self *FileSet) File(idx Idx) *File { + for _, file := range self.files { + if idx <= Idx(file.base+len(file.src)) { + return file + } + } + return nil +} + +// Position converts an Idx in the FileSet into a Position. +func (self *FileSet) Position(idx Idx) *Position { + for _, file := range self.files { + if idx <= Idx(file.base+len(file.src)) { + return file.Position(idx - Idx(file.base)) + } + } + + return nil +} + +type File struct { + name string + src string + base int // This will always be 1 or greater + sm *sourcemap.Consumer +} + +func NewFile(filename, src string, base int) *File { + return &File{ + name: filename, + src: src, + base: base, + } +} + +func (fl *File) WithSourceMap(sm *sourcemap.Consumer) *File { + fl.sm = sm + return fl +} + +func (fl *File) Name() string { + return fl.name +} + +func (fl *File) Source() string { + return fl.src +} + +func (fl *File) Base() int { + return fl.base +} + +func (fl *File) Position(idx Idx) *Position { + position := &Position{} + + offset := int(idx) - fl.base + + if offset >= len(fl.src) || offset < 0 { + return nil + } + + src := fl.src[:offset] + + position.Filename = fl.name + position.Offset = offset + position.Line = strings.Count(src, "\n") + 1 + + if index := strings.LastIndex(src, "\n"); index >= 0 { + position.Column = offset - index + } else { + position.Column = len(src) + 1 + } + + if fl.sm != nil { + if f, _, l, c, ok := fl.sm.Source(position.Line, position.Column); ok { + position.Filename, position.Line, position.Column = f, l, c + } + } + + return position +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/global.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/global.go new file mode 100644 index 0000000..eb2a2a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/global.go @@ -0,0 +1,221 @@ +package otto + +import ( + "strconv" + "time" +) + +var ( + prototypeValueObject = interface{}(nil) + prototypeValueFunction = _nativeFunctionObject{ + call: func(_ FunctionCall) Value { + return Value{} + }, + } + prototypeValueString = _stringASCII("") + // TODO Make this just false? + prototypeValueBoolean = Value{ + kind: valueBoolean, + value: false, + } + prototypeValueNumber = Value{ + kind: valueNumber, + value: 0, + } + prototypeValueDate = _dateObject{ + epoch: 0, + isNaN: false, + time: time.Unix(0, 0).UTC(), + value: Value{ + kind: valueNumber, + value: 0, + }, + } + prototypeValueRegExp = _regExpObject{ + regularExpression: nil, + global: false, + ignoreCase: false, + multiline: false, + source: "", + flags: "", + } +) + +func newContext() *_runtime { + + self := &_runtime{} + + self.globalStash = self.newObjectStash(nil, nil) + self.globalObject = self.globalStash.object + + _newContext(self) + + self.eval = self.globalObject.property["eval"].value.(Value).value.(*_object) + self.globalObject.prototype = self.global.ObjectPrototype + + return self +} + +func (runtime *_runtime) newBaseObject() *_object { + self := newObject(runtime, "") + return self +} + +func (runtime *_runtime) newClassObject(class string) *_object { + return newObject(runtime, class) +} + +func (runtime *_runtime) newPrimitiveObject(class string, value Value) *_object { + self := runtime.newClassObject(class) + self.value = value + return self +} + +func (self *_object) primitiveValue() Value { + switch value := self.value.(type) { + case Value: + return value + case _stringObject: + return toValue_string(value.String()) + } + return Value{} +} + +func (self *_object) hasPrimitive() bool { + switch self.value.(type) { + case Value, _stringObject: + return true + } + return false +} + +func (runtime *_runtime) newObject() *_object { + self := runtime.newClassObject("Object") + self.prototype = runtime.global.ObjectPrototype + return self +} + +func (runtime *_runtime) newArray(length uint32) *_object { + self := runtime.newArrayObject(length) + self.prototype = runtime.global.ArrayPrototype + return self +} + +func (runtime *_runtime) newArrayOf(valueArray []Value) *_object { + self := runtime.newArray(uint32(len(valueArray))) + for index, value := range valueArray { + if value.isEmpty() { + continue + } + self.defineProperty(strconv.FormatInt(int64(index), 10), value, 0111, false) + } + return self +} + +func (runtime *_runtime) newString(value Value) *_object { + self := runtime.newStringObject(value) + self.prototype = runtime.global.StringPrototype + return self +} + +func (runtime *_runtime) newBoolean(value Value) *_object { + self := runtime.newBooleanObject(value) + self.prototype = runtime.global.BooleanPrototype + return self +} + +func (runtime *_runtime) newNumber(value Value) *_object { + self := runtime.newNumberObject(value) + self.prototype = runtime.global.NumberPrototype + return self +} + +func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_object { + + pattern := "" + flags := "" + if object := patternValue._object(); object != nil && object.class == "RegExp" { + if flagsValue.IsDefined() { + panic(runtime.panicTypeError("Cannot supply flags when constructing one RegExp from another")) + } + regExp := object.regExpValue() + pattern = regExp.source + flags = regExp.flags + } else { + if patternValue.IsDefined() { + pattern = patternValue.string() + } + if flagsValue.IsDefined() { + flags = flagsValue.string() + } + } + + return runtime._newRegExp(pattern, flags) +} + +func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object { + self := runtime.newRegExpObject(pattern, flags) + self.prototype = runtime.global.RegExpPrototype + return self +} + +// TODO Should (probably) be one argument, right? This is redundant +func (runtime *_runtime) newDate(epoch float64) *_object { + self := runtime.newDateObject(epoch) + self.prototype = runtime.global.DatePrototype + return self +} + +func (runtime *_runtime) newError(name string, message Value, stackFramesToPop int) *_object { + var self *_object + switch name { + case "EvalError": + return runtime.newEvalError(message) + case "TypeError": + return runtime.newTypeError(message) + case "RangeError": + return runtime.newRangeError(message) + case "ReferenceError": + return runtime.newReferenceError(message) + case "SyntaxError": + return runtime.newSyntaxError(message) + case "URIError": + return runtime.newURIError(message) + } + + self = runtime.newErrorObject(name, message, stackFramesToPop) + self.prototype = runtime.global.ErrorPrototype + if name != "" { + self.defineProperty("name", toValue_string(name), 0111, false) + } + return self +} + +func (runtime *_runtime) newNativeFunction(name, file string, line int, _nativeFunction _nativeFunction) *_object { + self := runtime.newNativeFunctionObject(name, file, line, _nativeFunction, 0) + self.prototype = runtime.global.FunctionPrototype + prototype := runtime.newObject() + self.defineProperty("prototype", toValue_object(prototype), 0100, false) + prototype.defineProperty("constructor", toValue_object(self), 0100, false) + return self +} + +func (runtime *_runtime) newNodeFunction(node *_nodeFunctionLiteral, scopeEnvironment _stash) *_object { + // TODO Implement 13.2 fully + self := runtime.newNodeFunctionObject(node, scopeEnvironment) + self.prototype = runtime.global.FunctionPrototype + prototype := runtime.newObject() + self.defineProperty("prototype", toValue_object(prototype), 0100, false) + prototype.defineProperty("constructor", toValue_object(self), 0101, false) + return self +} + +// FIXME Only in one place... +func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object { + self := runtime.newBoundFunctionObject(target, this, argumentList) + self.prototype = runtime.global.FunctionPrototype + prototype := runtime.newObject() + self.defineProperty("prototype", toValue_object(prototype), 0100, false) + prototype.defineProperty("constructor", toValue_object(self), 0100, false) + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.go new file mode 100644 index 0000000..6e5df83 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.go @@ -0,0 +1,6649 @@ +package otto + +import ( + "math" +) + +func _newContext(runtime *_runtime) { + { + runtime.global.ObjectPrototype = &_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: nil, + extensible: true, + value: prototypeValueObject, + } + } + { + runtime.global.FunctionPrototype = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueFunction, + } + } + { + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "valueOf", + call: builtinObject_valueOf, + }, + } + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinObject_toString, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleString", + call: builtinObject_toLocaleString, + }, + } + hasOwnProperty_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "hasOwnProperty", + call: builtinObject_hasOwnProperty, + }, + } + isPrototypeOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isPrototypeOf", + call: builtinObject_isPrototypeOf, + }, + } + propertyIsEnumerable_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "propertyIsEnumerable", + call: builtinObject_propertyIsEnumerable, + }, + } + runtime.global.ObjectPrototype.property = map[string]_property{ + "valueOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: valueOf_function, + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleString_function, + }, + }, + "hasOwnProperty": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: hasOwnProperty_function, + }, + }, + "isPrototypeOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isPrototypeOf_function, + }, + }, + "propertyIsEnumerable": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: propertyIsEnumerable_function, + }, + }, + "constructor": _property{ + mode: 0101, + value: Value{}, + }, + } + runtime.global.ObjectPrototype.propertyOrder = []string{ + "valueOf", + "toString", + "toLocaleString", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor", + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinFunction_toString, + }, + } + apply_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "apply", + call: builtinFunction_apply, + }, + } + call_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "call", + call: builtinFunction_call, + }, + } + bind_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "bind", + call: builtinFunction_bind, + }, + } + runtime.global.FunctionPrototype.property = map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "apply": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: apply_function, + }, + }, + "call": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: call_function, + }, + }, + "bind": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: bind_function, + }, + }, + "constructor": _property{ + mode: 0101, + value: Value{}, + }, + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + } + runtime.global.FunctionPrototype.propertyOrder = []string{ + "toString", + "apply", + "call", + "bind", + "constructor", + "length", + } + } + { + getPrototypeOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getPrototypeOf", + call: builtinObject_getPrototypeOf, + }, + } + getOwnPropertyDescriptor_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getOwnPropertyDescriptor", + call: builtinObject_getOwnPropertyDescriptor, + }, + } + defineProperty_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "defineProperty", + call: builtinObject_defineProperty, + }, + } + defineProperties_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "defineProperties", + call: builtinObject_defineProperties, + }, + } + create_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "create", + call: builtinObject_create, + }, + } + isExtensible_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isExtensible", + call: builtinObject_isExtensible, + }, + } + preventExtensions_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "preventExtensions", + call: builtinObject_preventExtensions, + }, + } + isSealed_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isSealed", + call: builtinObject_isSealed, + }, + } + seal_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "seal", + call: builtinObject_seal, + }, + } + isFrozen_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isFrozen", + call: builtinObject_isFrozen, + }, + } + freeze_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "freeze", + call: builtinObject_freeze, + }, + } + keys_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "keys", + call: builtinObject_keys, + }, + } + getOwnPropertyNames_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getOwnPropertyNames", + call: builtinObject_getOwnPropertyNames, + }, + } + runtime.global.Object = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Object", + call: builtinObject, + construct: builtinNewObject, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.ObjectPrototype, + }, + }, + "getPrototypeOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getPrototypeOf_function, + }, + }, + "getOwnPropertyDescriptor": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getOwnPropertyDescriptor_function, + }, + }, + "defineProperty": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: defineProperty_function, + }, + }, + "defineProperties": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: defineProperties_function, + }, + }, + "create": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: create_function, + }, + }, + "isExtensible": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isExtensible_function, + }, + }, + "preventExtensions": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: preventExtensions_function, + }, + }, + "isSealed": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isSealed_function, + }, + }, + "seal": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: seal_function, + }, + }, + "isFrozen": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isFrozen_function, + }, + }, + "freeze": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: freeze_function, + }, + }, + "keys": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: keys_function, + }, + }, + "getOwnPropertyNames": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getOwnPropertyNames_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "getPrototypeOf", + "getOwnPropertyDescriptor", + "defineProperty", + "defineProperties", + "create", + "isExtensible", + "preventExtensions", + "isSealed", + "seal", + "isFrozen", + "freeze", + "keys", + "getOwnPropertyNames", + }, + } + runtime.global.ObjectPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Object, + }, + } + } + { + Function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Function", + call: builtinFunction, + construct: builtinNewFunction, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.FunctionPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.Function = Function + runtime.global.FunctionPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Function, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinArray_toString, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleString", + call: builtinArray_toLocaleString, + }, + } + concat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "concat", + call: builtinArray_concat, + }, + } + join_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "join", + call: builtinArray_join, + }, + } + splice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "splice", + call: builtinArray_splice, + }, + } + shift_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "shift", + call: builtinArray_shift, + }, + } + pop_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "pop", + call: builtinArray_pop, + }, + } + push_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "push", + call: builtinArray_push, + }, + } + slice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "slice", + call: builtinArray_slice, + }, + } + unshift_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "unshift", + call: builtinArray_unshift, + }, + } + reverse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "reverse", + call: builtinArray_reverse, + }, + } + sort_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "sort", + call: builtinArray_sort, + }, + } + indexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "indexOf", + call: builtinArray_indexOf, + }, + } + lastIndexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "lastIndexOf", + call: builtinArray_lastIndexOf, + }, + } + every_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "every", + call: builtinArray_every, + }, + } + some_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "some", + call: builtinArray_some, + }, + } + forEach_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "forEach", + call: builtinArray_forEach, + }, + } + map_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "map", + call: builtinArray_map, + }, + } + filter_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "filter", + call: builtinArray_filter, + }, + } + reduce_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "reduce", + call: builtinArray_reduce, + }, + } + reduceRight_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "reduceRight", + call: builtinArray_reduceRight, + }, + } + isArray_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isArray", + call: builtinArray_isArray, + }, + } + runtime.global.ArrayPrototype = &_object{ + runtime: runtime, + class: "Array", + objectClass: _classArray, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "length": _property{ + mode: 0100, + value: Value{ + kind: valueNumber, + value: uint32(0), + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleString_function, + }, + }, + "concat": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: concat_function, + }, + }, + "join": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: join_function, + }, + }, + "splice": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: splice_function, + }, + }, + "shift": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: shift_function, + }, + }, + "pop": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: pop_function, + }, + }, + "push": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: push_function, + }, + }, + "slice": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: slice_function, + }, + }, + "unshift": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: unshift_function, + }, + }, + "reverse": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: reverse_function, + }, + }, + "sort": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: sort_function, + }, + }, + "indexOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: indexOf_function, + }, + }, + "lastIndexOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: lastIndexOf_function, + }, + }, + "every": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: every_function, + }, + }, + "some": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: some_function, + }, + }, + "forEach": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: forEach_function, + }, + }, + "map": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: map_function, + }, + }, + "filter": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: filter_function, + }, + }, + "reduce": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: reduce_function, + }, + }, + "reduceRight": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: reduceRight_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "toString", + "toLocaleString", + "concat", + "join", + "splice", + "shift", + "pop", + "push", + "slice", + "unshift", + "reverse", + "sort", + "indexOf", + "lastIndexOf", + "every", + "some", + "forEach", + "map", + "filter", + "reduce", + "reduceRight", + }, + } + runtime.global.Array = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Array", + call: builtinArray, + construct: builtinNewArray, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.ArrayPrototype, + }, + }, + "isArray": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isArray_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "isArray", + }, + } + runtime.global.ArrayPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Array, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinString_toString, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "valueOf", + call: builtinString_valueOf, + }, + } + charAt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "charAt", + call: builtinString_charAt, + }, + } + charCodeAt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "charCodeAt", + call: builtinString_charCodeAt, + }, + } + concat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "concat", + call: builtinString_concat, + }, + } + indexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "indexOf", + call: builtinString_indexOf, + }, + } + lastIndexOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "lastIndexOf", + call: builtinString_lastIndexOf, + }, + } + match_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "match", + call: builtinString_match, + }, + } + replace_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "replace", + call: builtinString_replace, + }, + } + search_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "search", + call: builtinString_search, + }, + } + split_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "split", + call: builtinString_split, + }, + } + slice_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "slice", + call: builtinString_slice, + }, + } + substring_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "substring", + call: builtinString_substring, + }, + } + toLowerCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLowerCase", + call: builtinString_toLowerCase, + }, + } + toUpperCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toUpperCase", + call: builtinString_toUpperCase, + }, + } + substr_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "substr", + call: builtinString_substr, + }, + } + trim_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "trim", + call: builtinString_trim, + }, + } + trimLeft_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "trimLeft", + call: builtinString_trimLeft, + }, + } + trimRight_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "trimRight", + call: builtinString_trimRight, + }, + } + localeCompare_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "localeCompare", + call: builtinString_localeCompare, + }, + } + toLocaleLowerCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleLowerCase", + call: builtinString_toLocaleLowerCase, + }, + } + toLocaleUpperCase_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleUpperCase", + call: builtinString_toLocaleUpperCase, + }, + } + fromCharCode_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "fromCharCode", + call: builtinString_fromCharCode, + }, + } + runtime.global.StringPrototype = &_object{ + runtime: runtime, + class: "String", + objectClass: _classString, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueString, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: int(0), + }, + }, + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: valueOf_function, + }, + }, + "charAt": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: charAt_function, + }, + }, + "charCodeAt": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: charCodeAt_function, + }, + }, + "concat": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: concat_function, + }, + }, + "indexOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: indexOf_function, + }, + }, + "lastIndexOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: lastIndexOf_function, + }, + }, + "match": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: match_function, + }, + }, + "replace": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: replace_function, + }, + }, + "search": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: search_function, + }, + }, + "split": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: split_function, + }, + }, + "slice": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: slice_function, + }, + }, + "substring": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: substring_function, + }, + }, + "toLowerCase": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLowerCase_function, + }, + }, + "toUpperCase": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toUpperCase_function, + }, + }, + "substr": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: substr_function, + }, + }, + "trim": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: trim_function, + }, + }, + "trimLeft": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: trimLeft_function, + }, + }, + "trimRight": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: trimRight_function, + }, + }, + "localeCompare": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: localeCompare_function, + }, + }, + "toLocaleLowerCase": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleLowerCase_function, + }, + }, + "toLocaleUpperCase": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleUpperCase_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "toString", + "valueOf", + "charAt", + "charCodeAt", + "concat", + "indexOf", + "lastIndexOf", + "match", + "replace", + "search", + "split", + "slice", + "substring", + "toLowerCase", + "toUpperCase", + "substr", + "trim", + "trimLeft", + "trimRight", + "localeCompare", + "toLocaleLowerCase", + "toLocaleUpperCase", + }, + } + runtime.global.String = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "String", + call: builtinString, + construct: builtinNewString, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.StringPrototype, + }, + }, + "fromCharCode": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: fromCharCode_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "fromCharCode", + }, + } + runtime.global.StringPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.String, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinBoolean_toString, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "valueOf", + call: builtinBoolean_valueOf, + }, + } + runtime.global.BooleanPrototype = &_object{ + runtime: runtime, + class: "Boolean", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueBoolean, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: valueOf_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "valueOf", + }, + } + runtime.global.Boolean = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Boolean", + call: builtinBoolean, + construct: builtinNewBoolean, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.BooleanPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.BooleanPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Boolean, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinNumber_toString, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "valueOf", + call: builtinNumber_valueOf, + }, + } + toFixed_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toFixed", + call: builtinNumber_toFixed, + }, + } + toExponential_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toExponential", + call: builtinNumber_toExponential, + }, + } + toPrecision_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toPrecision", + call: builtinNumber_toPrecision, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleString", + call: builtinNumber_toLocaleString, + }, + } + runtime.global.NumberPrototype = &_object{ + runtime: runtime, + class: "Number", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueNumber, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: valueOf_function, + }, + }, + "toFixed": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toFixed_function, + }, + }, + "toExponential": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toExponential_function, + }, + }, + "toPrecision": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toPrecision_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleString_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "valueOf", + "toFixed", + "toExponential", + "toPrecision", + "toLocaleString", + }, + } + runtime.global.Number = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Number", + call: builtinNumber, + construct: builtinNewNumber, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.NumberPrototype, + }, + }, + "MAX_VALUE": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.MaxFloat64, + }, + }, + "MIN_VALUE": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.SmallestNonzeroFloat64, + }, + }, + "NaN": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.NaN(), + }, + }, + "NEGATIVE_INFINITY": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Inf(-1), + }, + }, + "POSITIVE_INFINITY": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Inf(+1), + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "MAX_VALUE", + "MIN_VALUE", + "NaN", + "NEGATIVE_INFINITY", + "POSITIVE_INFINITY", + }, + } + runtime.global.NumberPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Number, + }, + } + } + { + abs_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "abs", + call: builtinMath_abs, + }, + } + acos_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "acos", + call: builtinMath_acos, + }, + } + asin_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "asin", + call: builtinMath_asin, + }, + } + atan_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "atan", + call: builtinMath_atan, + }, + } + atan2_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "atan2", + call: builtinMath_atan2, + }, + } + ceil_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "ceil", + call: builtinMath_ceil, + }, + } + cos_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "cos", + call: builtinMath_cos, + }, + } + exp_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "exp", + call: builtinMath_exp, + }, + } + floor_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "floor", + call: builtinMath_floor, + }, + } + log_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "log", + call: builtinMath_log, + }, + } + max_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "max", + call: builtinMath_max, + }, + } + min_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "min", + call: builtinMath_min, + }, + } + pow_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "pow", + call: builtinMath_pow, + }, + } + random_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "random", + call: builtinMath_random, + }, + } + round_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "round", + call: builtinMath_round, + }, + } + sin_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "sin", + call: builtinMath_sin, + }, + } + sqrt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "sqrt", + call: builtinMath_sqrt, + }, + } + tan_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "tan", + call: builtinMath_tan, + }, + } + runtime.global.Math = &_object{ + runtime: runtime, + class: "Math", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "abs": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: abs_function, + }, + }, + "acos": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: acos_function, + }, + }, + "asin": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: asin_function, + }, + }, + "atan": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: atan_function, + }, + }, + "atan2": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: atan2_function, + }, + }, + "ceil": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: ceil_function, + }, + }, + "cos": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: cos_function, + }, + }, + "exp": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: exp_function, + }, + }, + "floor": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: floor_function, + }, + }, + "log": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: log_function, + }, + }, + "max": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: max_function, + }, + }, + "min": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: min_function, + }, + }, + "pow": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: pow_function, + }, + }, + "random": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: random_function, + }, + }, + "round": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: round_function, + }, + }, + "sin": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: sin_function, + }, + }, + "sqrt": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: sqrt_function, + }, + }, + "tan": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: tan_function, + }, + }, + "E": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.E, + }, + }, + "LN10": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Ln10, + }, + }, + "LN2": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Ln2, + }, + }, + "LOG2E": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Log2E, + }, + }, + "LOG10E": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Log10E, + }, + }, + "PI": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Pi, + }, + }, + "SQRT1_2": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: sqrt1_2, + }, + }, + "SQRT2": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Sqrt2, + }, + }, + }, + propertyOrder: []string{ + "abs", + "acos", + "asin", + "atan", + "atan2", + "ceil", + "cos", + "exp", + "floor", + "log", + "max", + "min", + "pow", + "random", + "round", + "sin", + "sqrt", + "tan", + "E", + "LN10", + "LN2", + "LOG2E", + "LOG10E", + "PI", + "SQRT1_2", + "SQRT2", + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinDate_toString, + }, + } + toDateString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toDateString", + call: builtinDate_toDateString, + }, + } + toTimeString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toTimeString", + call: builtinDate_toTimeString, + }, + } + toUTCString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toUTCString", + call: builtinDate_toUTCString, + }, + } + toISOString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toISOString", + call: builtinDate_toISOString, + }, + } + toJSON_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toJSON", + call: builtinDate_toJSON, + }, + } + toGMTString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toGMTString", + call: builtinDate_toGMTString, + }, + } + toLocaleString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleString", + call: builtinDate_toLocaleString, + }, + } + toLocaleDateString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleDateString", + call: builtinDate_toLocaleDateString, + }, + } + toLocaleTimeString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toLocaleTimeString", + call: builtinDate_toLocaleTimeString, + }, + } + valueOf_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "valueOf", + call: builtinDate_valueOf, + }, + } + getTime_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getTime", + call: builtinDate_getTime, + }, + } + getYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getYear", + call: builtinDate_getYear, + }, + } + getFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getFullYear", + call: builtinDate_getFullYear, + }, + } + getUTCFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCFullYear", + call: builtinDate_getUTCFullYear, + }, + } + getMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getMonth", + call: builtinDate_getMonth, + }, + } + getUTCMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCMonth", + call: builtinDate_getUTCMonth, + }, + } + getDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getDate", + call: builtinDate_getDate, + }, + } + getUTCDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCDate", + call: builtinDate_getUTCDate, + }, + } + getDay_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getDay", + call: builtinDate_getDay, + }, + } + getUTCDay_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCDay", + call: builtinDate_getUTCDay, + }, + } + getHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getHours", + call: builtinDate_getHours, + }, + } + getUTCHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCHours", + call: builtinDate_getUTCHours, + }, + } + getMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getMinutes", + call: builtinDate_getMinutes, + }, + } + getUTCMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCMinutes", + call: builtinDate_getUTCMinutes, + }, + } + getSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getSeconds", + call: builtinDate_getSeconds, + }, + } + getUTCSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCSeconds", + call: builtinDate_getUTCSeconds, + }, + } + getMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getMilliseconds", + call: builtinDate_getMilliseconds, + }, + } + getUTCMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getUTCMilliseconds", + call: builtinDate_getUTCMilliseconds, + }, + } + getTimezoneOffset_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "getTimezoneOffset", + call: builtinDate_getTimezoneOffset, + }, + } + setTime_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setTime", + call: builtinDate_setTime, + }, + } + setMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setMilliseconds", + call: builtinDate_setMilliseconds, + }, + } + setUTCMilliseconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCMilliseconds", + call: builtinDate_setUTCMilliseconds, + }, + } + setSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setSeconds", + call: builtinDate_setSeconds, + }, + } + setUTCSeconds_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCSeconds", + call: builtinDate_setUTCSeconds, + }, + } + setMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setMinutes", + call: builtinDate_setMinutes, + }, + } + setUTCMinutes_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCMinutes", + call: builtinDate_setUTCMinutes, + }, + } + setHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 4, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setHours", + call: builtinDate_setHours, + }, + } + setUTCHours_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 4, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCHours", + call: builtinDate_setUTCHours, + }, + } + setDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setDate", + call: builtinDate_setDate, + }, + } + setUTCDate_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCDate", + call: builtinDate_setUTCDate, + }, + } + setMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setMonth", + call: builtinDate_setMonth, + }, + } + setUTCMonth_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCMonth", + call: builtinDate_setUTCMonth, + }, + } + setYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setYear", + call: builtinDate_setYear, + }, + } + setFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setFullYear", + call: builtinDate_setFullYear, + }, + } + setUTCFullYear_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "setUTCFullYear", + call: builtinDate_setUTCFullYear, + }, + } + parse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "parse", + call: builtinDate_parse, + }, + } + UTC_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 7, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "UTC", + call: builtinDate_UTC, + }, + } + now_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "now", + call: builtinDate_now, + }, + } + runtime.global.DatePrototype = &_object{ + runtime: runtime, + class: "Date", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueDate, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "toDateString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toDateString_function, + }, + }, + "toTimeString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toTimeString_function, + }, + }, + "toUTCString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toUTCString_function, + }, + }, + "toISOString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toISOString_function, + }, + }, + "toJSON": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toJSON_function, + }, + }, + "toGMTString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toGMTString_function, + }, + }, + "toLocaleString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleString_function, + }, + }, + "toLocaleDateString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleDateString_function, + }, + }, + "toLocaleTimeString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toLocaleTimeString_function, + }, + }, + "valueOf": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: valueOf_function, + }, + }, + "getTime": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getTime_function, + }, + }, + "getYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getYear_function, + }, + }, + "getFullYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getFullYear_function, + }, + }, + "getUTCFullYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCFullYear_function, + }, + }, + "getMonth": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getMonth_function, + }, + }, + "getUTCMonth": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCMonth_function, + }, + }, + "getDate": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getDate_function, + }, + }, + "getUTCDate": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCDate_function, + }, + }, + "getDay": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getDay_function, + }, + }, + "getUTCDay": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCDay_function, + }, + }, + "getHours": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getHours_function, + }, + }, + "getUTCHours": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCHours_function, + }, + }, + "getMinutes": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getMinutes_function, + }, + }, + "getUTCMinutes": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCMinutes_function, + }, + }, + "getSeconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getSeconds_function, + }, + }, + "getUTCSeconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCSeconds_function, + }, + }, + "getMilliseconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getMilliseconds_function, + }, + }, + "getUTCMilliseconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getUTCMilliseconds_function, + }, + }, + "getTimezoneOffset": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: getTimezoneOffset_function, + }, + }, + "setTime": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setTime_function, + }, + }, + "setMilliseconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setMilliseconds_function, + }, + }, + "setUTCMilliseconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCMilliseconds_function, + }, + }, + "setSeconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setSeconds_function, + }, + }, + "setUTCSeconds": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCSeconds_function, + }, + }, + "setMinutes": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setMinutes_function, + }, + }, + "setUTCMinutes": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCMinutes_function, + }, + }, + "setHours": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setHours_function, + }, + }, + "setUTCHours": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCHours_function, + }, + }, + "setDate": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setDate_function, + }, + }, + "setUTCDate": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCDate_function, + }, + }, + "setMonth": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setMonth_function, + }, + }, + "setUTCMonth": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCMonth_function, + }, + }, + "setYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setYear_function, + }, + }, + "setFullYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setFullYear_function, + }, + }, + "setUTCFullYear": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: setUTCFullYear_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "toDateString", + "toTimeString", + "toUTCString", + "toISOString", + "toJSON", + "toGMTString", + "toLocaleString", + "toLocaleDateString", + "toLocaleTimeString", + "valueOf", + "getTime", + "getYear", + "getFullYear", + "getUTCFullYear", + "getMonth", + "getUTCMonth", + "getDate", + "getUTCDate", + "getDay", + "getUTCDay", + "getHours", + "getUTCHours", + "getMinutes", + "getUTCMinutes", + "getSeconds", + "getUTCSeconds", + "getMilliseconds", + "getUTCMilliseconds", + "getTimezoneOffset", + "setTime", + "setMilliseconds", + "setUTCMilliseconds", + "setSeconds", + "setUTCSeconds", + "setMinutes", + "setUTCMinutes", + "setHours", + "setUTCHours", + "setDate", + "setUTCDate", + "setMonth", + "setUTCMonth", + "setYear", + "setFullYear", + "setUTCFullYear", + }, + } + runtime.global.Date = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Date", + call: builtinDate, + construct: builtinNewDate, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 7, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.DatePrototype, + }, + }, + "parse": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: parse_function, + }, + }, + "UTC": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: UTC_function, + }, + }, + "now": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: now_function, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + "parse", + "UTC", + "now", + }, + } + runtime.global.DatePrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Date, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinRegExp_toString, + }, + } + exec_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "exec", + call: builtinRegExp_exec, + }, + } + test_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "test", + call: builtinRegExp_test, + }, + } + compile_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "compile", + call: builtinRegExp_compile, + }, + } + runtime.global.RegExpPrototype = &_object{ + runtime: runtime, + class: "RegExp", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: prototypeValueRegExp, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "exec": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: exec_function, + }, + }, + "test": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: test_function, + }, + }, + "compile": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: compile_function, + }, + }, + }, + propertyOrder: []string{ + "toString", + "exec", + "test", + "compile", + }, + } + runtime.global.RegExp = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "RegExp", + call: builtinRegExp, + construct: builtinNewRegExp, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.RegExpPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.RegExpPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.RegExp, + }, + } + } + { + toString_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "toString", + call: builtinError_toString, + }, + } + runtime.global.ErrorPrototype = &_object{ + runtime: runtime, + class: "Error", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "toString": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: toString_function, + }, + }, + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "Error", + }, + }, + "message": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "", + }, + }, + }, + propertyOrder: []string{ + "toString", + "name", + "message", + }, + } + runtime.global.Error = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "Error", + call: builtinError, + construct: builtinNewError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.ErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.ErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Error, + }, + } + } + { + runtime.global.EvalErrorPrototype = &_object{ + runtime: runtime, + class: "EvalError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "EvalError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.EvalError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "EvalError", + call: builtinEvalError, + construct: builtinNewEvalError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.EvalErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.EvalErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.EvalError, + }, + } + } + { + runtime.global.TypeErrorPrototype = &_object{ + runtime: runtime, + class: "TypeError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "TypeError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.TypeError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "TypeError", + call: builtinTypeError, + construct: builtinNewTypeError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.TypeErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.TypeErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.TypeError, + }, + } + } + { + runtime.global.RangeErrorPrototype = &_object{ + runtime: runtime, + class: "RangeError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "RangeError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.RangeError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "RangeError", + call: builtinRangeError, + construct: builtinNewRangeError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.RangeErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.RangeErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.RangeError, + }, + } + } + { + runtime.global.ReferenceErrorPrototype = &_object{ + runtime: runtime, + class: "ReferenceError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "ReferenceError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.ReferenceError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "ReferenceError", + call: builtinReferenceError, + construct: builtinNewReferenceError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.ReferenceErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.ReferenceErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.ReferenceError, + }, + } + } + { + runtime.global.SyntaxErrorPrototype = &_object{ + runtime: runtime, + class: "SyntaxError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "SyntaxError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.SyntaxError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "SyntaxError", + call: builtinSyntaxError, + construct: builtinNewSyntaxError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.SyntaxErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.SyntaxErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.SyntaxError, + }, + } + } + { + runtime.global.URIErrorPrototype = &_object{ + runtime: runtime, + class: "URIError", + objectClass: _classObject, + prototype: runtime.global.ErrorPrototype, + extensible: true, + value: nil, + property: map[string]_property{ + "name": _property{ + mode: 0101, + value: Value{ + kind: valueString, + value: "URIError", + }, + }, + }, + propertyOrder: []string{ + "name", + }, + } + runtime.global.URIError = &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: _nativeFunctionObject{ + name: "URIError", + call: builtinURIError, + construct: builtinNewURIError, + }, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + "prototype": _property{ + mode: 0, + value: Value{ + kind: valueObject, + value: runtime.global.URIErrorPrototype, + }, + }, + }, + propertyOrder: []string{ + "length", + "prototype", + }, + } + runtime.global.URIErrorPrototype.property["constructor"] = + _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.URIError, + }, + } + } + { + parse_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "parse", + call: builtinJSON_parse, + }, + } + stringify_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 3, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "stringify", + call: builtinJSON_stringify, + }, + } + runtime.global.JSON = &_object{ + runtime: runtime, + class: "JSON", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "parse": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: parse_function, + }, + }, + "stringify": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: stringify_function, + }, + }, + }, + propertyOrder: []string{ + "parse", + "stringify", + }, + } + } + { + eval_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "eval", + call: builtinGlobal_eval, + }, + } + parseInt_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 2, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "parseInt", + call: builtinGlobal_parseInt, + }, + } + parseFloat_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "parseFloat", + call: builtinGlobal_parseFloat, + }, + } + isNaN_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isNaN", + call: builtinGlobal_isNaN, + }, + } + isFinite_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "isFinite", + call: builtinGlobal_isFinite, + }, + } + decodeURI_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "decodeURI", + call: builtinGlobal_decodeURI, + }, + } + decodeURIComponent_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "decodeURIComponent", + call: builtinGlobal_decodeURIComponent, + }, + } + encodeURI_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "encodeURI", + call: builtinGlobal_encodeURI, + }, + } + encodeURIComponent_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "encodeURIComponent", + call: builtinGlobal_encodeURIComponent, + }, + } + escape_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "escape", + call: builtinGlobal_escape, + }, + } + unescape_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 1, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "unescape", + call: builtinGlobal_unescape, + }, + } + runtime.globalObject.property = map[string]_property{ + "eval": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: eval_function, + }, + }, + "parseInt": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: parseInt_function, + }, + }, + "parseFloat": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: parseFloat_function, + }, + }, + "isNaN": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isNaN_function, + }, + }, + "isFinite": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: isFinite_function, + }, + }, + "decodeURI": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: decodeURI_function, + }, + }, + "decodeURIComponent": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: decodeURIComponent_function, + }, + }, + "encodeURI": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: encodeURI_function, + }, + }, + "encodeURIComponent": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: encodeURIComponent_function, + }, + }, + "escape": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: escape_function, + }, + }, + "unescape": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: unescape_function, + }, + }, + "Object": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Object, + }, + }, + "Function": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Function, + }, + }, + "Array": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Array, + }, + }, + "String": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.String, + }, + }, + "Boolean": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Boolean, + }, + }, + "Number": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Number, + }, + }, + "Math": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Math, + }, + }, + "Date": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Date, + }, + }, + "RegExp": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.RegExp, + }, + }, + "Error": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.Error, + }, + }, + "EvalError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.EvalError, + }, + }, + "TypeError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.TypeError, + }, + }, + "RangeError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.RangeError, + }, + }, + "ReferenceError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.ReferenceError, + }, + }, + "SyntaxError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.SyntaxError, + }, + }, + "URIError": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.URIError, + }, + }, + "JSON": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: runtime.global.JSON, + }, + }, + "undefined": _property{ + mode: 0, + value: Value{ + kind: valueUndefined, + }, + }, + "NaN": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.NaN(), + }, + }, + "Infinity": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: math.Inf(+1), + }, + }, + } + runtime.globalObject.propertyOrder = []string{ + "eval", + "parseInt", + "parseFloat", + "isNaN", + "isFinite", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "escape", + "unescape", + "Object", + "Function", + "Array", + "String", + "Boolean", + "Number", + "Math", + "Date", + "RegExp", + "Error", + "EvalError", + "TypeError", + "RangeError", + "ReferenceError", + "SyntaxError", + "URIError", + "JSON", + "undefined", + "NaN", + "Infinity", + } + } +} + +func newConsoleObject(runtime *_runtime) *_object { + { + log_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "log", + call: builtinConsole_log, + }, + } + debug_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "debug", + call: builtinConsole_log, + }, + } + info_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "info", + call: builtinConsole_log, + }, + } + error_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "error", + call: builtinConsole_error, + }, + } + warn_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "warn", + call: builtinConsole_error, + }, + } + dir_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "dir", + call: builtinConsole_dir, + }, + } + time_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "time", + call: builtinConsole_time, + }, + } + timeEnd_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "timeEnd", + call: builtinConsole_timeEnd, + }, + } + trace_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "trace", + call: builtinConsole_trace, + }, + } + assert_function := &_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: map[string]_property{ + "length": _property{ + mode: 0, + value: Value{ + kind: valueNumber, + value: 0, + }, + }, + }, + propertyOrder: []string{ + "length", + }, + value: _nativeFunctionObject{ + name: "assert", + call: builtinConsole_assert, + }, + } + return &_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: map[string]_property{ + "log": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: log_function, + }, + }, + "debug": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: debug_function, + }, + }, + "info": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: info_function, + }, + }, + "error": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: error_function, + }, + }, + "warn": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: warn_function, + }, + }, + "dir": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: dir_function, + }, + }, + "time": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: time_function, + }, + }, + "timeEnd": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: timeEnd_function, + }, + }, + "trace": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: trace_function, + }, + }, + "assert": _property{ + mode: 0101, + value: Value{ + kind: valueObject, + value: assert_function, + }, + }, + }, + propertyOrder: []string{ + "log", + "debug", + "info", + "error", + "warn", + "dir", + "time", + "timeEnd", + "trace", + "assert", + }, + } + } +} + +func toValue_int(value int) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_int8(value int8) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_int16(value int16) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_int32(value int32) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_int64(value int64) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_uint(value uint) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_uint8(value uint8) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_uint16(value uint16) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_uint32(value uint32) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_uint64(value uint64) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_float32(value float32) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_float64(value float64) Value { + return Value{ + kind: valueNumber, + value: value, + } +} + +func toValue_string(value string) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_string16(value []uint16) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_bool(value bool) Value { + return Value{ + kind: valueBoolean, + value: value, + } +} + +func toValue_object(value *_object) Value { + return Value{ + kind: valueObject, + value: value, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.pl b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.pl new file mode 100755 index 0000000..c3620b4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/inline.pl @@ -0,0 +1,1086 @@ +#!/usr/bin/env perl + +my $_fmt; +$_fmt = "gofmt"; +$_fmt = "cat -n" if "cat" eq ($ARGV[0] || ""); + +use strict; +use warnings; +use IO::File; + +my $self = __PACKAGE__; + +sub functionLabel ($) { + return "$_[0]_function"; +} + +sub trim ($) { + local $_ = shift; + s/^\s*//, s/\s*$// for $_; + return $_; +} + +open my $fmt, "|-", "$_fmt" or die $!; + +$fmt->print(<<_END_); +package otto + +import ( + "math" +) + +func _newContext(runtime *_runtime) { +@{[ join "\n", $self->newContext() ]} +} + +func newConsoleObject(runtime *_runtime) *_object { +@{[ join "\n", $self->newConsoleObject() ]} +} +_END_ + +for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) { + $fmt->print(<<_END_); + +func toValue_$_(value $_) Value { + return Value{ + kind: valueNumber, + value: value, + } +} +_END_ +} + +$fmt->print(<<_END_); + +func toValue_string(value string) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_string16(value []uint16) Value { + return Value{ + kind: valueString, + value: value, + } +} + +func toValue_bool(value bool) Value { + return Value{ + kind: valueBoolean, + value: value, + } +} + +func toValue_object(value *_object) Value { + return Value{ + kind: valueObject, + value: value, + } +} +_END_ + +close $fmt; + +sub newConsoleObject { + my $self = shift; + + return + $self->block(sub { + my $class = "Console"; + my @got = $self->functionDeclare( + $class, + "log", 0, + "debug:log", 0, + "info:log", 0, + "error", 0, + "warn:error", 0, + "dir", 0, + "time", 0, + "timeEnd", 0, + "trace", 0, + "assert", 0, + ); + return + "return @{[ $self->newObject(@got) ]}" + }), + ; +} + +sub newContext { + my $self = shift; + return + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + undef, + "prototypeValueObject", + ), + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueFunction", + ), + }), + + # ObjectPrototype + $self->block(sub { + my $class = "Object"; + my @got = $self->functionDeclare( + $class, + "valueOf", 0, + "toString", 0, + "toLocaleString", 0, + "hasOwnProperty", 1, + "isPrototypeOf", 1, + "propertyIsEnumerable", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # FunctionPrototype + $self->block(sub { + my $class = "Function"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "apply", 2, + "call", 1, + "bind", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->property("constructor", undef), + $self->property("length", $self->numberValue(0), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + ".${class}Prototype.property =", @propertyMap, + ".${class}Prototype.propertyOrder =", $propertyOrder, + }), + + # Object + $self->block(sub { + my $class = "Object"; + return + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "getPrototypeOf", 1, + "getOwnPropertyDescriptor", 2, + "defineProperty", 3, + "defineProperties", 2, + "create", 2, + "isExtensible", 1, + "preventExtensions", 1, + "isSealed", 1, + "seal", 1, + "isFrozen", 1, + "freeze", 1, + "keys", 1, + "getOwnPropertyNames", 1, + ), + ), + }), + + # Function + $self->block(sub { + my $class = "Function"; + return + "Function :=", + $self->globalFunction( + $class, + 1, + ), + ".$class = Function", + }), + + # Array + $self->block(sub { + my $class = "Array"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toLocaleString", 0, + "concat", 1, + "join", 1, + "splice", 2, + "shift", 0, + "pop", 0, + "push", 1, + "slice", 2, + "unshift", 1, + "reverse", 0, + "sort", 1, + "indexOf", 1, + "lastIndexOf", 1, + "every", 1, + "some", 1, + "forEach", 1, + "map", 1, + "filter", 1, + "reduce", 1, + "reduceRight", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classArray", + ".ObjectPrototype", + undef, + $self->property("length", $self->numberValue("uint32(0)"), "0100"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "isArray", 1, + ), + ), + }), + + # String + $self->block(sub { + my $class = "String"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "charAt", 1, + "charCodeAt", 1, + "concat", 1, + "indexOf", 1, + "lastIndexOf", 1, + "match", 1, + "replace", 2, + "search", 1, + "split", 2, + "slice", 2, + "substring", 2, + "toLowerCase", 0, + "toUpperCase", 0, + "substr", 2, + "trim", 0, + "trimLeft", 0, + "trimRight", 0, + "localeCompare", 1, + "toLocaleLowerCase", 0, + "toLocaleUpperCase", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classString", + ".ObjectPrototype", + "prototypeValueString", + $self->property("length", $self->numberValue("int(0)"), "0"), + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + "fromCharCode", 1, + ), + ), + }), + + # Boolean + $self->block(sub { + my $class = "Boolean"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueBoolean", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + # Number + $self->block(sub { + my $class = "Number"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "valueOf", 0, + "toFixed", 1, + "toExponential", 1, + "toPrecision", 1, + "toLocaleString", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueNumber", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + $self->numberConstantDeclare( + "MAX_VALUE", "math.MaxFloat64", + "MIN_VALUE", "math.SmallestNonzeroFloat64", + "NaN", "math.NaN()", + "NEGATIVE_INFINITY", "math.Inf(-1)", + "POSITIVE_INFINITY", "math.Inf(+1)", + ), + ), + }), + + # Math + $self->block(sub { + my $class = "Math"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "abs", 1, + "acos", 1, + "asin", 1, + "atan", 1, + "atan2", 1, + "ceil", 1, + "cos", 1, + "exp", 1, + "floor", 1, + "log", 1, + "max", 2, + "min", 2, + "pow", 2, + "random", 0, + "round", 1, + "sin", 1, + "sqrt", 1, + "tan", 1, + ), + $self->numberConstantDeclare( + "E", "math.E", + "LN10", "math.Ln10", + "LN2", "math.Ln2", + "LOG2E", "math.Log2E", + "LOG10E", "math.Log10E", + "PI", "math.Pi", + "SQRT1_2", "sqrt1_2", + "SQRT2", "math.Sqrt2", + ) + ), + }), + + # Date + $self->block(sub { + my $class = "Date"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "toDateString", 0, + "toTimeString", 0, + "toUTCString", 0, + "toISOString", 0, + "toJSON", 1, + "toGMTString", 0, + "toLocaleString", 0, + "toLocaleDateString", 0, + "toLocaleTimeString", 0, + "valueOf", 0, + "getTime", 0, + "getYear", 0, + "getFullYear", 0, + "getUTCFullYear", 0, + "getMonth", 0, + "getUTCMonth", 0, + "getDate", 0, + "getUTCDate", 0, + "getDay", 0, + "getUTCDay", 0, + "getHours", 0, + "getUTCHours", 0, + "getMinutes", 0, + "getUTCMinutes", 0, + "getSeconds", 0, + "getUTCSeconds", 0, + "getMilliseconds", 0, + "getUTCMilliseconds", 0, + "getTimezoneOffset", 0, + "setTime", 1, + "setMilliseconds", 1, + "setUTCMilliseconds", 1, + "setSeconds", 2, + "setUTCSeconds", 2, + "setMinutes", 3, + "setUTCMinutes", 3, + "setHours", 4, + "setUTCHours", 4, + "setDate", 1, + "setUTCDate", 1, + "setMonth", 2, + "setUTCMonth", 2, + "setYear", 1, + "setFullYear", 3, + "setUTCFullYear", 3, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueDate", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 7, + $self->functionDeclare( + $class, + "parse", 1, + "UTC", 7, + "now", 0, + ), + ), + }), + + # RegExp + $self->block(sub { + my $class = "RegExp"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + "exec", 1, + "test", 1, + "compile", 1, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + "prototypeValueRegExp", + @got, + ), + ".$class =", + $self->globalFunction( + $class, + 2, + $self->functionDeclare( + $class, + ), + ), + }), + + # Error + $self->block(sub { + my $class = "Error"; + my @got = $self->functionDeclare( + $class, + "toString", 0, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ObjectPrototype", + undef, + @got, + $self->property("name", $self->stringValue("Error")), + $self->property("message", $self->stringValue("")), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }), + + (map { + my $class = "${_}Error"; + $self->block(sub { + my @got = $self->functionDeclare( + $class, + ); + return + ".${class}Prototype =", + $self->globalPrototype( + $class, + "_classObject", + ".ErrorPrototype", + undef, + @got, + $self->property("name", $self->stringValue($class)), + ), + ".$class =", + $self->globalFunction( + $class, + 1, + $self->functionDeclare( + $class, + ), + ), + }); + } qw/Eval Type Range Reference Syntax URI/), + + # JSON + $self->block(sub { + my $class = "JSON"; + return + ".$class =", + $self->globalObject( + $class, + $self->functionDeclare( + $class, + "parse", 2, + "stringify", 3, + ), + ), + }), + + # Global + $self->block(sub { + my $class = "Global"; + my @got = $self->functionDeclare( + $class, + "eval", 1, + "parseInt", 2, + "parseFloat", 1, + "isNaN", 1, + "isFinite", 1, + "decodeURI", 1, + "decodeURIComponent", 1, + "encodeURI", 1, + "encodeURIComponent", 1, + "escape", 1, + "unescape", 1, + ); + my @propertyMap = $self->propertyMap( + @got, + $self->globalDeclare( + "Object", + "Function", + "Array", + "String", + "Boolean", + "Number", + "Math", + "Date", + "RegExp", + "Error", + "EvalError", + "TypeError", + "RangeError", + "ReferenceError", + "SyntaxError", + "URIError", + "JSON", + ), + $self->property("undefined", $self->undefinedValue(), "0"), + $self->property("NaN", $self->numberValue("math.NaN()"), "0"), + $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"), + ); + my $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder =~ s/^propertyOrder: //; + return + "runtime.globalObject.property =", + @propertyMap, + "runtime.globalObject.propertyOrder =", + $propertyOrder, + ; + }), + ; +} + +sub propertyMap { + my $self = shift; + return "map[string]_property{", (join ",\n", @_, ""), "}", +} + +our (@preblock, @postblock); +sub block { + my $self = shift; + local @preblock = (); + local @postblock = (); + my @input = $_[0]->(); + my @output; + while (@input) { + local $_ = shift @input; + if (m/^\./) { + $_ = "runtime.global$_"; + } + if (m/ :?=$/) { + $_ .= shift @input; + } + push @output, $_; + } + return + "{", + @preblock, + @output, + @postblock, + "}", + ; +} + +sub numberConstantDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + my $value = shift; + push @got, $self->property($name, $self->numberValue($value), "0"), + } + return @got; +} + +sub functionDeclare { + my $self = shift; + my $class = shift; + my $builtin = "builtin${class}"; + my @got; + while (@_) { + my $name = shift; + my $length = shift; + $name = $self->newFunction($name, "${builtin}_", $length); + push @got, $self->functionProperty($name), + } + return @got; +} + +sub globalDeclare { + my $self = shift; + my @got; + while (@_) { + my $name = shift; + push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"), + } + return @got; +} + +sub propertyOrder { + my $self = shift; + my $propertyMap = join "", @_; + + my (@keys) = $propertyMap =~ m/("\w+"):/g; + my $propertyOrder = + join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}"; + return $propertyOrder; +} + +sub globalObject { + my $self = shift; + my $name = shift; + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$name", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + $propertyMap +} +_END_ +} + +sub globalFunction { + my $self = shift; + my $name = shift; + my $length = shift; + + my $builtin = "builtin${name}"; + my $builtinNew = "builtinNew${name}"; + my $prototype = "runtime.global.${name}Prototype"; + my $propertyMap = ""; + unshift @_, + $self->property("length", $self->numberValue($length), "0"), + $self->property("prototype", $self->objectValue($prototype), "0"), + ; + + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + push @postblock, $self->statement( + "$prototype.property[\"constructor\"] =", + $self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"), + ); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]}, + $propertyMap +} +_END_ +} + +sub nativeCallFunction { + my $self = shift; + my $name = shift; + my $func = shift; + return trim <<_END_; +_nativeCallFunction{ "$name", $func } +_END_ +} + +sub globalPrototype { + my $self = shift; + my $class = shift; + my $classObject = shift; + my $prototype = shift; + my $value = shift; + + if (!defined $prototype) { + $prototype = "nil"; + } + + if (!defined $value) { + $value = "nil"; + } + + if ($prototype =~ m/^\./) { + $prototype = "runtime.global$prototype"; + } + + my $propertyMap = ""; + if (@_) { + $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + $propertyMap = "property: $propertyMap,\n$propertyOrder,"; + } + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $classObject, + prototype: $prototype, + extensible: true, + value: $value, + $propertyMap +} +_END_ +} + +sub newFunction { + my $self = shift; + my $name = shift; + my $func = shift; + my $length = shift; + + my @name = ($name, $name); + if ($name =~ m/^(\w+):(\w+)$/) { + @name = ($1, $2); + $name = $name[0]; + } + + if ($func =~ m/^builtin\w+_$/) { + $func = "$func$name[1]"; + } + + my $propertyOrder = ""; + my @propertyMap = ( + $self->property("length", $self->numberValue($length), "0"), + ); + + if (@propertyMap) { + $propertyOrder = $self->propertyOrder(@propertyMap); + $propertyOrder = "$propertyOrder,"; + } + + my $label = functionLabel($name); + push @preblock, $self->statement( + "$label := @{[ trim <<_END_ ]}", +&_object{ + runtime: runtime, + class: "Function", + objectClass: _classObject, + prototype: runtime.global.FunctionPrototype, + extensible: true, + property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, + $propertyOrder + value: @{[ $self->nativeFunctionOf($name, $func) ]}, +} +_END_ + ); + + return $name; +} + +sub newObject { + my $self = shift; + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "Object", + objectClass: _classObject, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, +} +_END_ +} + +sub newPrototypeObject { + my $self = shift; + my $class = shift; + my $objectClass = shift; + my $value = shift; + if (defined $value) { + $value = "value: $value,"; + } + + my $propertyMap = join "\n", $self->propertyMap(@_); + my $propertyOrder = $self->propertyOrder($propertyMap); + + return trim <<_END_; +&_object{ + runtime: runtime, + class: "$class", + objectClass: $objectClass, + prototype: runtime.global.ObjectPrototype, + extensible: true, + property: $propertyMap, + $propertyOrder, + $value +} +_END_ +} + +sub functionProperty { + my $self = shift; + my $name = shift; + + return $self->property( + $name, + $self->objectValue(functionLabel($name)) + ); +} + +sub statement { + my $self = shift; + return join "\n", @_; +} + +sub functionOf { + my $self = shift; + my $call = shift; + my $construct = shift; + if ($construct) { + $construct = "construct: $construct,"; + } else { + $construct = ""; + } + + return trim <<_END_ +_functionObject{ + call: $call, + $construct +} +_END_ +} + +sub nativeFunctionOf { + my $self = shift; + my $name = shift; + my $call = shift; + my $construct = shift; + if ($construct) { + $construct = "construct: $construct,"; + } else { + $construct = ""; + } + + return trim <<_END_ +_nativeFunctionObject{ + name: "$name", + call: $call, + $construct +} +_END_ +} + +sub nameProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: $value, +} +_END_ +} + +sub numberValue { + my $self = shift; + my $value = shift; + return trim <<_END_; +Value{ + kind: valueNumber, + value: $value, +} +_END_ +} + +sub property { + my $self = shift; + my $name = shift; + my $value = shift; + my $mode = shift; + $mode = "0101" unless defined $mode; + if (! defined $value) { + $value = "Value{}"; + } + if (defined $name) { + return trim <<_END_; +"$name": _property{ + mode: $mode, + value: $value, +} +_END_ + } else { + return trim <<_END_; +_property{ + mode: $mode, + value: $value, +} +_END_ + } + +} + +sub objectProperty { + my $self = shift; + my $name = shift; + my $value = shift; + + return trim <<_END_; +"$name": _property{ + mode: 0101, + value: @{[ $self->objectValue($value)]}, +} +_END_ +} + +sub objectValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueObject, + value: $value, +} +_END_ +} + +sub stringValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueString, + value: "$value", +} +_END_ +} + +sub booleanValue { + my $self = shift; + my $value = shift; + return trim <<_END_ +Value{ + kind: valueBoolean, + value: $value, +} +_END_ +} + +sub undefinedValue { + my $self = shift; + return trim <<_END_ +Value{ + kind: valueUndefined, +} +_END_ +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object.go new file mode 100644 index 0000000..849812c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object.go @@ -0,0 +1,156 @@ +package otto + +type _object struct { + runtime *_runtime + + class string + objectClass *_objectClass + value interface{} + + prototype *_object + extensible bool + + property map[string]_property + propertyOrder []string +} + +func newObject(runtime *_runtime, class string) *_object { + self := &_object{ + runtime: runtime, + class: class, + objectClass: _classObject, + property: make(map[string]_property), + extensible: true, + } + return self +} + +// 8.12 + +// 8.12.1 +func (self *_object) getOwnProperty(name string) *_property { + return self.objectClass.getOwnProperty(self, name) +} + +// 8.12.2 +func (self *_object) getProperty(name string) *_property { + return self.objectClass.getProperty(self, name) +} + +// 8.12.3 +func (self *_object) get(name string) Value { + return self.objectClass.get(self, name) +} + +// 8.12.4 +func (self *_object) canPut(name string) bool { + return self.objectClass.canPut(self, name) +} + +// 8.12.5 +func (self *_object) put(name string, value Value, throw bool) { + self.objectClass.put(self, name, value, throw) +} + +// 8.12.6 +func (self *_object) hasProperty(name string) bool { + return self.objectClass.hasProperty(self, name) +} + +func (self *_object) hasOwnProperty(name string) bool { + return self.objectClass.hasOwnProperty(self, name) +} + +type _defaultValueHint int + +const ( + defaultValueNoHint _defaultValueHint = iota + defaultValueHintString + defaultValueHintNumber +) + +// 8.12.8 +func (self *_object) DefaultValue(hint _defaultValueHint) Value { + if hint == defaultValueNoHint { + if self.class == "Date" { + // Date exception + hint = defaultValueHintString + } else { + hint = defaultValueHintNumber + } + } + methodSequence := []string{"valueOf", "toString"} + if hint == defaultValueHintString { + methodSequence = []string{"toString", "valueOf"} + } + for _, methodName := range methodSequence { + method := self.get(methodName) + // FIXME This is redundant... + if method.isCallable() { + result := method._object().call(toValue_object(self), nil, false, nativeFrame) + if result.IsPrimitive() { + return result + } + } + } + + panic(self.runtime.panicTypeError()) +} + +func (self *_object) String() string { + return self.DefaultValue(defaultValueHintString).string() +} + +func (self *_object) defineProperty(name string, value Value, mode _propertyMode, throw bool) bool { + return self.defineOwnProperty(name, _property{value, mode}, throw) +} + +// 8.12.9 +func (self *_object) defineOwnProperty(name string, descriptor _property, throw bool) bool { + return self.objectClass.defineOwnProperty(self, name, descriptor, throw) +} + +func (self *_object) delete(name string, throw bool) bool { + return self.objectClass.delete(self, name, throw) +} + +func (self *_object) enumerate(all bool, each func(string) bool) { + self.objectClass.enumerate(self, all, each) +} + +func (self *_object) _exists(name string) bool { + _, exists := self.property[name] + return exists +} + +func (self *_object) _read(name string) (_property, bool) { + property, exists := self.property[name] + return property, exists +} + +func (self *_object) _write(name string, value interface{}, mode _propertyMode) { + if value == nil { + value = Value{} + } + _, exists := self.property[name] + self.property[name] = _property{value, mode} + if !exists { + self.propertyOrder = append(self.propertyOrder, name) + } +} + +func (self *_object) _delete(name string) { + _, exists := self.property[name] + delete(self.property, name) + if exists { + for index, property := range self.propertyOrder { + if name == property { + if index == len(self.propertyOrder)-1 { + self.propertyOrder = self.propertyOrder[:index] + } else { + self.propertyOrder = append(self.propertyOrder[:index], self.propertyOrder[index+1:]...) + } + } + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object_class.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object_class.go new file mode 100644 index 0000000..d18b9ce --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/object_class.go @@ -0,0 +1,493 @@ +package otto + +import ( + "encoding/json" +) + +type _objectClass struct { + getOwnProperty func(*_object, string) *_property + getProperty func(*_object, string) *_property + get func(*_object, string) Value + canPut func(*_object, string) bool + put func(*_object, string, Value, bool) + hasProperty func(*_object, string) bool + hasOwnProperty func(*_object, string) bool + defineOwnProperty func(*_object, string, _property, bool) bool + delete func(*_object, string, bool) bool + enumerate func(*_object, bool, func(string) bool) + clone func(*_object, *_object, *_clone) *_object + marshalJSON func(*_object) json.Marshaler +} + +func objectEnumerate(self *_object, all bool, each func(string) bool) { + for _, name := range self.propertyOrder { + if all || self.property[name].enumerable() { + if !each(name) { + return + } + } + } +} + +var ( + _classObject, + _classArray, + _classString, + _classArguments, + _classGoStruct, + _classGoMap, + _classGoArray, + _classGoSlice, + _ *_objectClass +) + +func init() { + _classObject = &_objectClass{ + objectGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + objectEnumerate, + objectClone, + nil, + } + + _classArray = &_objectClass{ + objectGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + arrayDefineOwnProperty, + objectDelete, + objectEnumerate, + objectClone, + nil, + } + + _classString = &_objectClass{ + stringGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + stringEnumerate, + objectClone, + nil, + } + + _classArguments = &_objectClass{ + argumentsGetOwnProperty, + objectGetProperty, + argumentsGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + argumentsDefineOwnProperty, + argumentsDelete, + objectEnumerate, + objectClone, + nil, + } + + _classGoStruct = &_objectClass{ + goStructGetOwnProperty, + objectGetProperty, + objectGet, + goStructCanPut, + goStructPut, + objectHasProperty, + objectHasOwnProperty, + objectDefineOwnProperty, + objectDelete, + goStructEnumerate, + objectClone, + goStructMarshalJSON, + } + + _classGoMap = &_objectClass{ + goMapGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goMapDefineOwnProperty, + goMapDelete, + goMapEnumerate, + objectClone, + nil, + } + + _classGoArray = &_objectClass{ + goArrayGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goArrayDefineOwnProperty, + goArrayDelete, + goArrayEnumerate, + objectClone, + nil, + } + + _classGoSlice = &_objectClass{ + goSliceGetOwnProperty, + objectGetProperty, + objectGet, + objectCanPut, + objectPut, + objectHasProperty, + objectHasOwnProperty, + goSliceDefineOwnProperty, + goSliceDelete, + goSliceEnumerate, + objectClone, + nil, + } +} + +// Allons-y + +// 8.12.1 +func objectGetOwnProperty(self *_object, name string) *_property { + // Return a _copy_ of the property + property, exists := self._read(name) + if !exists { + return nil + } + return &property +} + +// 8.12.2 +func objectGetProperty(self *_object, name string) *_property { + property := self.getOwnProperty(name) + if property != nil { + return property + } + if self.prototype != nil { + return self.prototype.getProperty(name) + } + return nil +} + +// 8.12.3 +func objectGet(self *_object, name string) Value { + property := self.getProperty(name) + if property != nil { + return property.get(self) + } + return Value{} +} + +// 8.12.4 +func objectCanPut(self *_object, name string) bool { + canPut, _, _ := _objectCanPut(self, name) + return canPut +} + +func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) { + property = self.getOwnProperty(name) + if property != nil { + switch propertyValue := property.value.(type) { + case Value: + canPut = property.writable() + return + case _propertyGetSet: + setter = propertyValue[1] + canPut = setter != nil + return + default: + panic(self.runtime.panicTypeError()) + } + } + + if self.prototype == nil { + return self.extensible, nil, nil + } + + property = self.prototype.getProperty(name) + if property == nil { + return self.extensible, nil, nil + } + + switch propertyValue := property.value.(type) { + case Value: + if !self.extensible { + return false, nil, nil + } + return property.writable(), nil, nil + case _propertyGetSet: + setter = propertyValue[1] + canPut = setter != nil + return + default: + panic(self.runtime.panicTypeError()) + } +} + +// 8.12.5 +func objectPut(self *_object, name string, value Value, throw bool) { + + if true { + // Shortcut... + // + // So, right now, every class is using objectCanPut and every class + // is using objectPut. + // + // If that were to no longer be the case, we would have to have + // something to detect that here, so that we do not use an + // incompatible canPut routine + canPut, property, setter := _objectCanPut(self, name) + if !canPut { + self.runtime.typeErrorResult(throw) + } else if setter != nil { + setter.call(toValue(self), []Value{value}, false, nativeFrame) + } else if property != nil { + property.value = value + self.defineOwnProperty(name, *property, throw) + } else { + self.defineProperty(name, value, 0111, throw) + } + return + } + + // The long way... + // + // Right now, code should never get here, see above + if !self.canPut(name) { + self.runtime.typeErrorResult(throw) + return + } + + property := self.getOwnProperty(name) + if property == nil { + property = self.getProperty(name) + if property != nil { + if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor { + getSet[1].call(toValue(self), []Value{value}, false, nativeFrame) + return + } + } + self.defineProperty(name, value, 0111, throw) + } else { + switch propertyValue := property.value.(type) { + case Value: + property.value = value + self.defineOwnProperty(name, *property, throw) + case _propertyGetSet: + if propertyValue[1] != nil { + propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame) + return + } + if throw { + panic(self.runtime.panicTypeError()) + } + default: + panic(self.runtime.panicTypeError()) + } + } +} + +// 8.12.6 +func objectHasProperty(self *_object, name string) bool { + return self.getProperty(name) != nil +} + +func objectHasOwnProperty(self *_object, name string) bool { + return self.getOwnProperty(name) != nil +} + +// 8.12.9 +func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + property, exists := self._read(name) + { + if !exists { + if !self.extensible { + goto Reject + } + if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { + if newGetSet[0] == &_nilGetSetObject { + newGetSet[0] = nil + } + if newGetSet[1] == &_nilGetSetObject { + newGetSet[1] = nil + } + descriptor.value = newGetSet + } + self._write(name, descriptor.value, descriptor.mode) + return true + } + if descriptor.isEmpty() { + return true + } + + // TODO Per 8.12.9.6 - We should shortcut here (returning true) if + // the current and new (define) properties are the same + + configurable := property.configurable() + if !configurable { + if descriptor.configurable() { + goto Reject + } + // Test that, if enumerable is set on the property descriptor, then it should + // be the same as the existing property + if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() { + goto Reject + } + } + value, isDataDescriptor := property.value.(Value) + getSet, _ := property.value.(_propertyGetSet) + if descriptor.isGenericDescriptor() { + // GenericDescriptor + } else if isDataDescriptor != descriptor.isDataDescriptor() { + // DataDescriptor <=> AccessorDescriptor + if !configurable { + goto Reject + } + } else if isDataDescriptor && descriptor.isDataDescriptor() { + // DataDescriptor <=> DataDescriptor + if !configurable { + if !property.writable() && descriptor.writable() { + goto Reject + } + if !property.writable() { + if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) { + goto Reject + } + } + } + } else { + // AccessorDescriptor <=> AccessorDescriptor + newGetSet, _ := descriptor.value.(_propertyGetSet) + presentGet, presentSet := true, true + if newGetSet[0] == &_nilGetSetObject { + // Present, but nil + newGetSet[0] = nil + } else if newGetSet[0] == nil { + // Missing, not even nil + newGetSet[0] = getSet[0] + presentGet = false + } + if newGetSet[1] == &_nilGetSetObject { + // Present, but nil + newGetSet[1] = nil + } else if newGetSet[1] == nil { + // Missing, not even nil + newGetSet[1] = getSet[1] + presentSet = false + } + if !configurable { + if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) { + goto Reject + } + } + descriptor.value = newGetSet + } + { + // This section will preserve attributes of + // the original property, if necessary + value1 := descriptor.value + if value1 == nil { + value1 = property.value + } else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor { + if newGetSet[0] == &_nilGetSetObject { + newGetSet[0] = nil + } + if newGetSet[1] == &_nilGetSetObject { + newGetSet[1] = nil + } + value1 = newGetSet + } + mode1 := descriptor.mode + if mode1&0222 != 0 { + // TODO Factor this out into somewhere testable + // (Maybe put into switch ...) + mode0 := property.mode + if mode1&0200 != 0 { + if descriptor.isDataDescriptor() { + mode1 &= ^0200 // Turn off "writable" missing + mode1 |= (mode0 & 0100) + } + } + if mode1&020 != 0 { + mode1 |= (mode0 & 010) + } + if mode1&02 != 0 { + mode1 |= (mode0 & 01) + } + mode1 &= 0311 // 0311 to preserve the non-setting on "writable" + } + self._write(name, value1, mode1) + } + return true + } +Reject: + if throw { + panic(self.runtime.panicTypeError()) + } + return false +} + +func objectDelete(self *_object, name string, throw bool) bool { + property_ := self.getOwnProperty(name) + if property_ == nil { + return true + } + if property_.configurable() { + self._delete(name) + return true + } + return self.runtime.typeErrorResult(throw) +} + +func objectClone(in *_object, out *_object, clone *_clone) *_object { + *out = *in + + out.runtime = clone.runtime + if out.prototype != nil { + out.prototype = clone.object(in.prototype) + } + out.property = make(map[string]_property, len(in.property)) + out.propertyOrder = make([]string, len(in.propertyOrder)) + copy(out.propertyOrder, in.propertyOrder) + for index, property := range in.property { + out.property[index] = clone.property(property) + } + + switch value := in.value.(type) { + case _nativeFunctionObject: + out.value = value + case _bindFunctionObject: + out.value = _bindFunctionObject{ + target: clone.object(value.target), + this: clone.value(value.this), + argumentList: clone.valueArray(value.argumentList), + } + case _nodeFunctionObject: + out.value = _nodeFunctionObject{ + node: value.node, + stash: clone.stash(value.stash), + } + case _argumentsObject: + out.value = value.clone(clone) + } + + return out +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto.go new file mode 100644 index 0000000..b5b528d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto.go @@ -0,0 +1,770 @@ +/* +Package otto is a JavaScript parser and interpreter written natively in Go. + +http://godoc.org/github.com/robertkrimen/otto + + import ( + "github.com/robertkrimen/otto" + ) + +Run something in the VM + + vm := otto.New() + vm.Run(` + abc = 2 + 2; + console.log("The value of abc is " + abc); // 4 + `) + +Get a value out of the VM + + value, err := vm.Get("abc") + value, _ := value.ToInteger() + } + +Set a number + + vm.Set("def", 11) + vm.Run(` + console.log("The value of def is " + def); + // The value of def is 11 + `) + +Set a string + + vm.Set("xyzzy", "Nothing happens.") + vm.Run(` + console.log(xyzzy.length); // 16 + `) + +Get the value of an expression + + value, _ = vm.Run("xyzzy.length") + { + // value is an int64 with a value of 16 + value, _ := value.ToInteger() + } + +An error happens + + value, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length") + if err != nil { + // err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined + // If there is an error, then value.IsUndefined() is true + ... + } + +Set a Go function + + vm.Set("sayHello", func(call otto.FunctionCall) otto.Value { + fmt.Printf("Hello, %s.\n", call.Argument(0).String()) + return otto.Value{} + }) + +Set a Go function that returns something useful + + vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value { + right, _ := call.Argument(0).ToInteger() + result, _ := vm.ToValue(2 + right) + return result + }) + +Use the functions in JavaScript + + result, _ = vm.Run(` + sayHello("Xyzzy"); // Hello, Xyzzy. + sayHello(); // Hello, undefined + + result = twoPlus(2.0); // 4 + `) + +Parser + +A separate parser is available in the parser package if you're just interested in building an AST. + +http://godoc.org/github.com/robertkrimen/otto/parser + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + +otto + +You can run (Go) JavaScript from the commandline with: http://github.com/robertkrimen/otto/tree/master/otto + + $ go get -v github.com/robertkrimen/otto/otto + +Run JavaScript by entering some source on stdin or by giving otto a filename: + + $ otto example.js + +underscore + +Optionally include the JavaScript utility-belt library, underscore, with this import: + + import ( + "github.com/robertkrimen/otto" + _ "github.com/robertkrimen/otto/underscore" + ) + + // Now every otto runtime will come loaded with underscore + +For more information: http://github.com/robertkrimen/otto/tree/master/underscore + +Caveat Emptor + +The following are some limitations with otto: + + * "use strict" will parse, but does nothing. + * The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. + * Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. + +Regular Expression Incompatibility + +Go translates JavaScript-style regular expressions into something that is "regexp" compatible via `parser.TransformRegExp`. +Unfortunately, RegExp requires backtracking for some patterns, and backtracking is not supported by the standard Go engine: https://code.google.com/p/re2/wiki/Syntax + +Therefore, the following syntax is incompatible: + + (?=) // Lookahead (positive), currently a parsing error + (?!) // Lookahead (backhead), currently a parsing error + \1 // Backreference (\1, \2, \3, ...), currently a parsing error + +A brief discussion of these limitations: "Regexp (?!re)" https://groups.google.com/forum/?fromgroups=#%21topic/golang-nuts/7qgSDWPIh_E + +More information about re2: https://code.google.com/p/re2/ + +In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. +The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. + +Halting Problem + +If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this: + + package main + + import ( + "errors" + "fmt" + "os" + "time" + + "github.com/robertkrimen/otto" + ) + + var halt = errors.New("Stahp") + + func main() { + runUnsafe(`var abc = [];`) + runUnsafe(` + while (true) { + // Loop forever + }`) + } + + func runUnsafe(unsafe string) { + start := time.Now() + defer func() { + duration := time.Since(start) + if caught := recover(); caught != nil { + if caught == halt { + fmt.Fprintf(os.Stderr, "Some code took to long! Stopping after: %v\n", duration) + return + } + panic(caught) // Something else happened, repanic! + } + fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration) + }() + + vm := otto.New() + vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking + + go func() { + time.Sleep(2 * time.Second) // Stop after two seconds + vm.Interrupt <- func() { + panic(halt) + } + }() + + vm.Run(unsafe) // Here be dragons (risky code) + } + +Where is setTimeout/setInterval? + +These timing functions are not actually part of the ECMA-262 specification. Typically, they belong to the `windows` object (in the browser). +It would not be difficult to provide something like these via Go, but you probably want to wrap otto in an event loop in that case. + +For an example of how this could be done in Go with otto, see natto: + +http://github.com/robertkrimen/natto + +Here is some more discussion of the issue: + +* http://book.mixu.net/node/ch2.html + +* http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 + +* http://aaroncrane.co.uk/2009/02/perl_safe_signals/ + +*/ +package otto + +import ( + "fmt" + "strings" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/registry" +) + +// Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace. +type Otto struct { + // Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example. + // See "Halting Problem" for more information. + Interrupt chan func() + runtime *_runtime +} + +// New will allocate a new JavaScript runtime +func New() *Otto { + self := &Otto{ + runtime: newContext(), + } + self.runtime.otto = self + self.runtime.traceLimit = 10 + self.Set("console", self.runtime.newConsole()) + + registry.Apply(func(entry registry.Entry) { + self.Run(entry.Source()) + }) + + return self +} + +func (otto *Otto) clone() *Otto { + self := &Otto{ + runtime: otto.runtime.clone(), + } + self.runtime.otto = self + return self +} + +// Run will allocate a new JavaScript runtime, run the given source +// on the allocated runtime, and return the runtime, resulting value, and +// error (if any). +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// src may also be a Script. +// +// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. +// +func Run(src interface{}) (*Otto, Value, error) { + otto := New() + value, err := otto.Run(src) // This already does safety checking + return otto, value, err +} + +// Run will run the given source (parsing it first if necessary), returning the resulting value and error (if any) +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// If the runtime is unable to parse source, then this function will return undefined and the parse error (nothing +// will be evaluated in this case). +// +// src may also be a Script. +// +// src may also be a Program, but if the AST has been modified, then runtime behavior is undefined. +// +func (self Otto) Run(src interface{}) (Value, error) { + value, err := self.runtime.cmpl_run(src, nil) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Eval will do the same thing as Run, except without leaving the current scope. +// +// By staying in the same scope, the code evaluated has access to everything +// already defined in the current stack frame. This is most useful in, for +// example, a debugger call. +func (self Otto) Eval(src interface{}) (Value, error) { + if self.runtime.scope == nil { + self.runtime.enterGlobalScope() + defer self.runtime.leaveScope() + } + + value, err := self.runtime.cmpl_eval(src, nil) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Get the value of the top-level binding of the given name. +// +// If there is an error (like the binding does not exist), then the value +// will be undefined. +func (self Otto) Get(name string) (Value, error) { + value := Value{} + err := catchPanic(func() { + value = self.getValue(name) + }) + if !value.safe() { + value = Value{} + } + return value, err +} + +func (self Otto) getValue(name string) Value { + return self.runtime.globalStash.getBinding(name, false) +} + +// Set the top-level binding of the given name to the given value. +// +// Set will automatically apply ToValue to the given value in order +// to convert it to a JavaScript value (type Value). +// +// If there is an error (like the binding is read-only, or the ToValue conversion +// fails), then an error is returned. +// +// If the top-level binding does not exist, it will be created. +func (self Otto) Set(name string, value interface{}) error { + { + value, err := self.ToValue(value) + if err != nil { + return err + } + err = catchPanic(func() { + self.setValue(name, value) + }) + return err + } +} + +func (self Otto) setValue(name string, value Value) { + self.runtime.globalStash.setValue(name, value, false) +} + +func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { + self.runtime.debugger = fn +} + +func (self Otto) SetRandomSource(fn func() float64) { + self.runtime.random = fn +} + +// SetStackDepthLimit sets an upper limit to the depth of the JavaScript +// stack. In simpler terms, this limits the number of "nested" function calls +// you can make in a particular interpreter instance. +// +// Note that this doesn't take into account the Go stack depth. If your +// JavaScript makes a call to a Go function, otto won't keep track of what +// happens outside the interpreter. So if your Go function is infinitely +// recursive, you're still in trouble. +func (self Otto) SetStackDepthLimit(limit int) { + self.runtime.stackLimit = limit +} + +// SetStackTraceLimit sets an upper limit to the number of stack frames that +// otto will use when formatting an error's stack trace. By default, the limit +// is 10. This is consistent with V8 and SpiderMonkey. +// +// TODO: expose via `Error.stackTraceLimit` +func (self Otto) SetStackTraceLimit(limit int) { + self.runtime.traceLimit = limit +} + +// MakeCustomError creates a new Error object with the given name and message, +// returning it as a Value. +func (self Otto) MakeCustomError(name, message string) Value { + return self.runtime.toValue(self.runtime.newError(name, self.runtime.toValue(message), 0)) +} + +// MakeRangeError creates a new RangeError object with the given message, +// returning it as a Value. +func (self Otto) MakeRangeError(message string) Value { + return self.runtime.toValue(self.runtime.newRangeError(self.runtime.toValue(message))) +} + +// MakeSyntaxError creates a new SyntaxError object with the given message, +// returning it as a Value. +func (self Otto) MakeSyntaxError(message string) Value { + return self.runtime.toValue(self.runtime.newSyntaxError(self.runtime.toValue(message))) +} + +// MakeTypeError creates a new TypeError object with the given message, +// returning it as a Value. +func (self Otto) MakeTypeError(message string) Value { + return self.runtime.toValue(self.runtime.newTypeError(self.runtime.toValue(message))) +} + +// Context is a structure that contains information about the current execution +// context. +type Context struct { + Filename string + Line int + Column int + Callee string + Symbols map[string]Value + This Value + Stacktrace []string +} + +// Context returns the current execution context of the vm, traversing up to +// ten stack frames, and skipping any innermost native function stack frames. +func (self Otto) Context() Context { + return self.ContextSkip(10, true) +} + +// ContextLimit returns the current execution context of the vm, with a +// specific limit on the number of stack frames to traverse, skipping any +// innermost native function stack frames. +func (self Otto) ContextLimit(limit int) Context { + return self.ContextSkip(limit, true) +} + +// ContextSkip returns the current execution context of the vm, with a +// specific limit on the number of stack frames to traverse, optionally +// skipping any innermost native function stack frames. +func (self Otto) ContextSkip(limit int, skipNative bool) (ctx Context) { + // Ensure we are operating in a scope + if self.runtime.scope == nil { + self.runtime.enterGlobalScope() + defer self.runtime.leaveScope() + } + + scope := self.runtime.scope + frame := scope.frame + + for skipNative && frame.native && scope.outer != nil { + scope = scope.outer + frame = scope.frame + } + + // Get location information + ctx.Filename = "" + ctx.Callee = frame.callee + + switch { + case frame.native: + ctx.Filename = frame.nativeFile + ctx.Line = frame.nativeLine + ctx.Column = 0 + case frame.file != nil: + ctx.Filename = "" + + if p := frame.file.Position(file.Idx(frame.offset)); p != nil { + ctx.Line = p.Line + ctx.Column = p.Column + + if p.Filename != "" { + ctx.Filename = p.Filename + } + } + } + + // Get the current scope this Value + ctx.This = toValue_object(scope.this) + + // Build stacktrace (up to 10 levels deep) + ctx.Symbols = make(map[string]Value) + ctx.Stacktrace = append(ctx.Stacktrace, frame.location()) + for limit != 0 { + // Get variables + stash := scope.lexical + for { + for _, name := range getStashProperties(stash) { + if _, ok := ctx.Symbols[name]; !ok { + ctx.Symbols[name] = stash.getBinding(name, true) + } + } + stash = stash.outer() + if stash == nil || stash.outer() == nil { + break + } + } + + scope = scope.outer + if scope == nil { + break + } + if scope.frame.offset >= 0 { + ctx.Stacktrace = append(ctx.Stacktrace, scope.frame.location()) + } + limit-- + } + + return +} + +// Call the given JavaScript with a given this and arguments. +// +// If this is nil, then some special handling takes place to determine the proper +// this value, falling back to a "standard" invocation if necessary (where this is +// undefined). +// +// If source begins with "new " (A lowercase new followed by a space), then +// Call will invoke the function constructor rather than performing a function call. +// In this case, the this argument has no effect. +// +// // value is a String object +// value, _ := vm.Call("Object", nil, "Hello, World.") +// +// // Likewise... +// value, _ := vm.Call("new Object", nil, "Hello, World.") +// +// // This will perform a concat on the given array and return the result +// // value is [ 1, 2, 3, undefined, 4, 5, 6, 7, "abc" ] +// value, _ := vm.Call(`[ 1, 2, 3, undefined, 4 ].concat`, nil, 5, 6, 7, "abc") +// +func (self Otto) Call(source string, this interface{}, argumentList ...interface{}) (Value, error) { + + thisValue := Value{} + + construct := false + if strings.HasPrefix(source, "new ") { + source = source[4:] + construct = true + } + + // FIXME enterGlobalScope + self.runtime.enterGlobalScope() + defer func() { + self.runtime.leaveScope() + }() + + if !construct && this == nil { + program, err := self.runtime.cmpl_parse("", source+"()", nil) + if err == nil { + if node, ok := program.body[0].(*_nodeExpressionStatement); ok { + if node, ok := node.expression.(*_nodeCallExpression); ok { + var value Value + err := catchPanic(func() { + value = self.runtime.cmpl_evaluate_nodeCallExpression(node, argumentList) + }) + if err != nil { + return Value{}, err + } + return value, nil + } + } + } + } else { + value, err := self.ToValue(this) + if err != nil { + return Value{}, err + } + thisValue = value + } + + { + this := thisValue + + fn, err := self.Run(source) + if err != nil { + return Value{}, err + } + + if construct { + result, err := fn.constructSafe(self.runtime, this, argumentList...) + if err != nil { + return Value{}, err + } + return result, nil + } + + result, err := fn.Call(this, argumentList...) + if err != nil { + return Value{}, err + } + return result, nil + } +} + +// Object will run the given source and return the result as an object. +// +// For example, accessing an existing object: +// +// object, _ := vm.Object(`Number`) +// +// Or, creating a new object: +// +// object, _ := vm.Object(`({ xyzzy: "Nothing happens." })`) +// +// Or, creating and assigning an object: +// +// object, _ := vm.Object(`xyzzy = {}`) +// object.Set("volume", 11) +// +// If there is an error (like the source does not result in an object), then +// nil and an error is returned. +func (self Otto) Object(source string) (*Object, error) { + value, err := self.runtime.cmpl_run(source, nil) + if err != nil { + return nil, err + } + if value.IsObject() { + return value.Object(), nil + } + return nil, fmt.Errorf("value is not an object") +} + +// ToValue will convert an interface{} value to a value digestible by otto/JavaScript. +func (self Otto) ToValue(value interface{}) (Value, error) { + return self.runtime.safeToValue(value) +} + +// Copy will create a copy/clone of the runtime. +// +// Copy is useful for saving some time when creating many similar runtimes. +// +// This method works by walking the original runtime and cloning each object, scope, stash, +// etc. into a new runtime. +// +// Be on the lookout for memory leaks or inadvertent sharing of resources. +func (in *Otto) Copy() *Otto { + out := &Otto{ + runtime: in.runtime.clone(), + } + out.runtime.otto = out + return out +} + +// Object{} + +// Object is the representation of a JavaScript object. +type Object struct { + object *_object + value Value +} + +func _newObject(object *_object, value Value) *Object { + // value MUST contain object! + return &Object{ + object: object, + value: value, + } +} + +// Call a method on the object. +// +// It is essentially equivalent to: +// +// var method, _ := object.Get(name) +// method.Call(object, argumentList...) +// +// An undefined value and an error will result if: +// +// 1. There is an error during conversion of the argument list +// 2. The property is not actually a function +// 3. An (uncaught) exception is thrown +// +func (self Object) Call(name string, argumentList ...interface{}) (Value, error) { + // TODO: Insert an example using JavaScript below... + // e.g., Object("JSON").Call("stringify", ...) + + function, err := self.Get(name) + if err != nil { + return Value{}, err + } + return function.Call(self.Value(), argumentList...) +} + +// Value will return self as a value. +func (self Object) Value() Value { + return self.value +} + +// Get the value of the property with the given name. +func (self Object) Get(name string) (Value, error) { + value := Value{} + err := catchPanic(func() { + value = self.object.get(name) + }) + if !value.safe() { + value = Value{} + } + return value, err +} + +// Set the property of the given name to the given value. +// +// An error will result if the setting the property triggers an exception (i.e. read-only), +// or there is an error during conversion of the given value. +func (self Object) Set(name string, value interface{}) error { + { + value, err := self.object.runtime.safeToValue(value) + if err != nil { + return err + } + err = catchPanic(func() { + self.object.put(name, value, true) + }) + return err + } +} + +// Keys gets the keys for the given object. +// +// Equivalent to calling Object.keys on the object. +func (self Object) Keys() []string { + var keys []string + self.object.enumerate(false, func(name string) bool { + keys = append(keys, name) + return true + }) + return keys +} + +// KeysByParent gets the keys (and those of the parents) for the given object, +// in order of "closest" to "furthest". +func (self Object) KeysByParent() [][]string { + var a [][]string + + for o := self.object; o != nil; o = o.prototype { + var l []string + + o.enumerate(false, func(name string) bool { + l = append(l, name) + return true + }) + + a = append(a, l) + } + + return a +} + +// Class will return the class string of the object. +// +// The return value will (generally) be one of: +// +// Object +// Function +// Array +// String +// Number +// Boolean +// Date +// RegExp +// +func (self Object) Class() string { + return self.object.class +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto_.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto_.go new file mode 100644 index 0000000..304a831 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/otto_.go @@ -0,0 +1,178 @@ +package otto + +import ( + "fmt" + "regexp" + runtime_ "runtime" + "strconv" + "strings" +) + +var isIdentifier_Regexp *regexp.Regexp = regexp.MustCompile(`^[a-zA-Z\$][a-zA-Z0-9\$]*$`) + +func isIdentifier(string_ string) bool { + return isIdentifier_Regexp.MatchString(string_) +} + +func (self *_runtime) toValueArray(arguments ...interface{}) []Value { + length := len(arguments) + if length == 1 { + if valueArray, ok := arguments[0].([]Value); ok { + return valueArray + } + return []Value{self.toValue(arguments[0])} + } + + valueArray := make([]Value, length) + for index, value := range arguments { + valueArray[index] = self.toValue(value) + } + + return valueArray +} + +func stringToArrayIndex(name string) int64 { + index, err := strconv.ParseInt(name, 10, 64) + if err != nil { + return -1 + } + if index < 0 { + return -1 + } + if index >= maxUint32 { + // The value 2^32 (or above) is not a valid index because + // you cannot store a uint32 length for an index of uint32 + return -1 + } + return index +} + +func isUint32(value int64) bool { + return value >= 0 && value <= maxUint32 +} + +func arrayIndexToString(index int64) string { + return strconv.FormatInt(index, 10) +} + +func valueOfArrayIndex(array []Value, index int) Value { + value, _ := getValueOfArrayIndex(array, index) + return value +} + +func getValueOfArrayIndex(array []Value, index int) (Value, bool) { + if index >= 0 && index < len(array) { + value := array[index] + if !value.isEmpty() { + return value, true + } + } + return Value{}, false +} + +// A range index can be anything from 0 up to length. It is NOT safe to use as an index +// to an array, but is useful for slicing and in some ECMA algorithms. +func valueToRangeIndex(indexValue Value, length int64, negativeIsZero bool) int64 { + index := indexValue.number().int64 + if negativeIsZero { + if index < 0 { + index = 0 + } + // minimum(index, length) + if index >= length { + index = length + } + return index + } + + if index < 0 { + index += length + if index < 0 { + index = 0 + } + } else { + if index > length { + index = length + } + } + return index +} + +func rangeStartEnd(array []Value, size int64, negativeIsZero bool) (start, end int64) { + start = valueToRangeIndex(valueOfArrayIndex(array, 0), size, negativeIsZero) + if len(array) == 1 { + // If there is only the start argument, then end = size + end = size + return + } + + // Assuming the argument is undefined... + end = size + endValue := valueOfArrayIndex(array, 1) + if !endValue.IsUndefined() { + // Which it is not, so get the value as an array index + end = valueToRangeIndex(endValue, size, negativeIsZero) + } + return +} + +func rangeStartLength(source []Value, size int64) (start, length int64) { + start = valueToRangeIndex(valueOfArrayIndex(source, 0), size, false) + + // Assume the second argument is missing or undefined + length = int64(size) + if len(source) == 1 { + // If there is only the start argument, then length = size + return + } + + lengthValue := valueOfArrayIndex(source, 1) + if !lengthValue.IsUndefined() { + // Which it is not, so get the value as an array index + length = lengthValue.number().int64 + } + return +} + +func boolFields(input string) (result map[string]bool) { + result = map[string]bool{} + for _, word := range strings.Fields(input) { + result[word] = true + } + return result +} + +func hereBeDragons(arguments ...interface{}) string { + pc, _, _, _ := runtime_.Caller(1) + name := runtime_.FuncForPC(pc).Name() + message := fmt.Sprintf("Here be dragons -- %s", name) + if len(arguments) > 0 { + message += ": " + argument0 := fmt.Sprintf("%s", arguments[0]) + if len(arguments) == 1 { + message += argument0 + } else { + message += fmt.Sprintf(argument0, arguments[1:]...) + } + } else { + message += "." + } + return message +} + +func throwHereBeDragons(arguments ...interface{}) { + panic(hereBeDragons(arguments...)) +} + +func eachPair(list []interface{}, fn func(_0, _1 interface{})) { + for len(list) > 0 { + var _0, _1 interface{} + _0 = list[0] + list = list[1:] // Pop off first + if len(list) > 0 { + _1 = list[0] + list = list[1:] // Pop off second + } + fn(_0, _1) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/Makefile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/Makefile new file mode 100644 index 0000000..766fd4d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/Makefile @@ -0,0 +1,4 @@ +.PHONY: test + +test: + go test diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/README.markdown new file mode 100644 index 0000000..c3cae5b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/README.markdown @@ -0,0 +1,190 @@ +# parser +-- + import "github.com/robertkrimen/otto/parser" + +Package parser implements a parser for JavaScript. + + import ( + "github.com/robertkrimen/otto/parser" + ) + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + + +### Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +## Usage + +#### func ParseFile + +```go +func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) +``` +ParseFile parses the source code of a single JavaScript/ECMAScript source file +and returns the corresponding ast.Program node. + +If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil, +ParseFile first adds filename and src to fileSet. + +The filename argument is optional and is used for labelling errors, etc. + +src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST +always be in UTF-8. + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) + +#### func ParseFunction + +```go +func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) +``` +ParseFunction parses a given parameter list and body as a function and returns +the corresponding ast.FunctionLiteral node. + +The parameter list, if any, should be a comma-separated list of identifiers. + +#### func ReadSource + +```go +func ReadSource(filename string, src interface{}) ([]byte, error) +``` + +#### func TransformRegExp + +```go +func TransformRegExp(pattern string) (string, error) +``` +TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. + +re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or +backreference (\1, \2, ...) will cause an error. + +re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript +definition, on the other hand, also includes \v, Unicode "Separator, Space", +etc. + +If the pattern is invalid (not valid even in JavaScript), then this function +returns the empty string and an error. + +If the pattern is valid, but incompatible (contains a lookahead or +backreference), then this function returns the transformation (a non-empty +string) AND an error. + +#### type Error + +```go +type Error struct { + Position file.Position + Message string +} +``` + +An Error represents a parsing error. It includes the position where the error +occurred and a message/description. + +#### func (Error) Error + +```go +func (self Error) Error() string +``` + +#### type ErrorList + +```go +type ErrorList []*Error +``` + +ErrorList is a list of *Errors. + +#### func (*ErrorList) Add + +```go +func (self *ErrorList) Add(position file.Position, msg string) +``` +Add adds an Error with given position and message to an ErrorList. + +#### func (ErrorList) Err + +```go +func (self ErrorList) Err() error +``` +Err returns an error equivalent to this ErrorList. If the list is empty, Err +returns nil. + +#### func (ErrorList) Error + +```go +func (self ErrorList) Error() string +``` +Error implements the Error interface. + +#### func (ErrorList) Len + +```go +func (self ErrorList) Len() int +``` + +#### func (ErrorList) Less + +```go +func (self ErrorList) Less(i, j int) bool +``` + +#### func (*ErrorList) Reset + +```go +func (self *ErrorList) Reset() +``` +Reset resets an ErrorList to no errors. + +#### func (ErrorList) Sort + +```go +func (self ErrorList) Sort() +``` + +#### func (ErrorList) Swap + +```go +func (self ErrorList) Swap(i, j int) +``` + +#### type Mode + +```go +type Mode uint +``` + +A Mode value is a set of flags (or 0). They control optional parser +functionality. + +```go +const ( + IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) +) +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/dbg.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/dbg.go new file mode 100644 index 0000000..3c5f2f6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/dbg.go @@ -0,0 +1,9 @@ +// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg + +package parser + +import ( + Dbg "github.com/robertkrimen/otto/dbg" +) + +var dbg, dbgf = Dbg.New() diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/error.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/error.go new file mode 100644 index 0000000..e0f74a5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/error.go @@ -0,0 +1,175 @@ +package parser + +import ( + "fmt" + "sort" + + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +const ( + err_UnexpectedToken = "Unexpected token %v" + err_UnexpectedEndOfInput = "Unexpected end of input" + err_UnexpectedEscape = "Unexpected escape" +) + +// UnexpectedNumber: 'Unexpected number', +// UnexpectedString: 'Unexpected string', +// UnexpectedIdentifier: 'Unexpected identifier', +// UnexpectedReserved: 'Unexpected reserved word', +// NewlineAfterThrow: 'Illegal newline after throw', +// InvalidRegExp: 'Invalid regular expression', +// UnterminatedRegExp: 'Invalid regular expression: missing /', +// InvalidLHSInAssignment: 'Invalid left-hand side in assignment', +// InvalidLHSInForIn: 'Invalid left-hand side in for-in', +// MultipleDefaultsInSwitch: 'More than one default clause in switch statement', +// NoCatchOrFinally: 'Missing catch or finally after try', +// UnknownLabel: 'Undefined label \'%0\'', +// Redeclaration: '%0 \'%1\' has already been declared', +// IllegalContinue: 'Illegal continue statement', +// IllegalBreak: 'Illegal break statement', +// IllegalReturn: 'Illegal return statement', +// StrictModeWith: 'Strict mode code may not include a with statement', +// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', +// StrictVarName: 'Variable name may not be eval or arguments in strict mode', +// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', +// StrictParamDupe: 'Strict mode function may not have duplicate parameter names', +// StrictFunctionName: 'Function name may not be eval or arguments in strict mode', +// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', +// StrictDelete: 'Delete of an unqualified identifier in strict mode.', +// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode', +// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name', +// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name', +// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', +// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', +// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', +// StrictReservedWord: 'Use of future reserved word in strict mode' + +// A SyntaxError is a description of an ECMAScript syntax error. + +// An Error represents a parsing error. It includes the position where the error occurred and a message/description. +type Error struct { + Position file.Position + Message string +} + +// FIXME Should this be "SyntaxError"? + +func (self Error) Error() string { + filename := self.Position.Filename + if filename == "" { + filename = "(anonymous)" + } + return fmt.Sprintf("%s: Line %d:%d %s", + filename, + self.Position.Line, + self.Position.Column, + self.Message, + ) +} + +func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error { + idx := file.Idx(0) + switch place := place.(type) { + case int: + idx = self.idxOf(place) + case file.Idx: + if place == 0 { + idx = self.idxOf(self.chrOffset) + } else { + idx = place + } + default: + panic(fmt.Errorf("error(%T, ...)", place)) + } + + position := self.position(idx) + msg = fmt.Sprintf(msg, msgValues...) + self.errors.Add(position, msg) + return self.errors[len(self.errors)-1] +} + +func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error { + if chr == -1 { + return self.error(idx, err_UnexpectedEndOfInput) + } + return self.error(idx, err_UnexpectedToken, token.ILLEGAL) +} + +func (self *_parser) errorUnexpectedToken(tkn token.Token) error { + switch tkn { + case token.EOF: + return self.error(file.Idx(0), err_UnexpectedEndOfInput) + } + value := tkn.String() + switch tkn { + case token.BOOLEAN, token.NULL: + value = self.literal + case token.IDENTIFIER: + return self.error(self.idx, "Unexpected identifier") + case token.KEYWORD: + // TODO Might be a future reserved word + return self.error(self.idx, "Unexpected reserved word") + case token.NUMBER: + return self.error(self.idx, "Unexpected number") + case token.STRING: + return self.error(self.idx, "Unexpected string") + } + return self.error(self.idx, err_UnexpectedToken, value) +} + +// ErrorList is a list of *Errors. +// +type ErrorList []*Error + +// Add adds an Error with given position and message to an ErrorList. +func (self *ErrorList) Add(position file.Position, msg string) { + *self = append(*self, &Error{position, msg}) +} + +// Reset resets an ErrorList to no errors. +func (self *ErrorList) Reset() { *self = (*self)[0:0] } + +func (self ErrorList) Len() int { return len(self) } +func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] } +func (self ErrorList) Less(i, j int) bool { + x := &self[i].Position + y := &self[j].Position + if x.Filename < y.Filename { + return true + } + if x.Filename == y.Filename { + if x.Line < y.Line { + return true + } + if x.Line == y.Line { + return x.Column < y.Column + } + } + return false +} + +func (self ErrorList) Sort() { + sort.Sort(self) +} + +// Error implements the Error interface. +func (self ErrorList) Error() string { + switch len(self) { + case 0: + return "no errors" + case 1: + return self[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1) +} + +// Err returns an error equivalent to this ErrorList. +// If the list is empty, Err returns nil. +func (self ErrorList) Err() error { + if len(self) == 0 { + return nil + } + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/expression.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/expression.go new file mode 100644 index 0000000..63a169d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/expression.go @@ -0,0 +1,1005 @@ +package parser + +import ( + "regexp" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseIdentifier() *ast.Identifier { + literal := self.literal + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + } + self.next() + exp := &ast.Identifier{ + Name: literal, + Idx: idx, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp +} + +func (self *_parser) parsePrimaryExpression() ast.Expression { + literal := self.literal + idx := self.idx + switch self.token { + case token.IDENTIFIER: + self.next() + if len(literal) > 1 { + tkn, strict := token.IsKeyword(literal) + if tkn == token.KEYWORD { + if !strict { + self.error(idx, "Unexpected reserved word") + } + } + } + return &ast.Identifier{ + Name: literal, + Idx: idx, + } + case token.NULL: + self.next() + return &ast.NullLiteral{ + Idx: idx, + Literal: literal, + } + case token.BOOLEAN: + self.next() + value := false + switch literal { + case "true": + value = true + case "false": + value = false + default: + self.error(idx, "Illegal boolean literal") + } + return &ast.BooleanLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.STRING: + self.next() + value, err := parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + return &ast.StringLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.NUMBER: + self.next() + value, err := parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + value = 0 + } + return &ast.NumberLiteral{ + Idx: idx, + Literal: literal, + Value: value, + } + case token.SLASH, token.QUOTIENT_ASSIGN: + return self.parseRegExpLiteral() + case token.LEFT_BRACE: + return self.parseObjectLiteral() + case token.LEFT_BRACKET: + return self.parseArrayLiteral() + case token.LEFT_PARENTHESIS: + self.expect(token.LEFT_PARENTHESIS) + expression := self.parseExpression() + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.RIGHT_PARENTHESIS) + return expression + case token.THIS: + self.next() + return &ast.ThisExpression{ + Idx: idx, + } + case token.FUNCTION: + return self.parseFunction(false) + } + + self.errorUnexpectedToken(self.token) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} +} + +func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { + + offset := self.chrOffset - 1 // Opening slash already gotten + if self.token == token.QUOTIENT_ASSIGN { + offset -= 1 // = + } + idx := self.idxOf(offset) + + pattern, err := self.scanString(offset) + endOffset := self.chrOffset + + self.next() + if err == nil { + pattern = pattern[1 : len(pattern)-1] + } + + flags := "" + if self.token == token.IDENTIFIER { // gim + + flags = self.literal + self.next() + endOffset = self.chrOffset - 1 + } + + var value string + // TODO 15.10 + { + // Test during parsing that this is a valid regular expression + // Sorry, (?=) and (?!) are invalid (for now) + pattern, err := TransformRegExp(pattern) + if err != nil { + if pattern == "" || self.mode&IgnoreRegExpErrors == 0 { + self.error(idx, "Invalid regular expression: %s", err.Error()) + } + } else { + _, err = regexp.Compile(pattern) + if err != nil { + // We should not get here, ParseRegExp should catch any errors + self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error" + } else { + value = pattern + } + } + } + + literal := self.str[offset:endOffset] + + return &ast.RegExpLiteral{ + Idx: idx, + Literal: literal, + Pattern: pattern, + Flags: flags, + Value: value, + } +} + +func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression { + + if self.token != token.IDENTIFIER { + idx := self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + + literal := self.literal + idx := self.idx + self.next() + node := &ast.VariableExpression{ + Name: literal, + Idx: idx, + } + if self.mode&StoreComments != 0 { + self.comments.SetExpression(node) + } + + if declarationList != nil { + *declarationList = append(*declarationList, node) + } + + if self.token == token.ASSIGN { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + node.Initializer = self.parseAssignmentExpression() + } + + return node +} + +func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression { + + var declarationList []*ast.VariableExpression // Avoid bad expressions + var list []ast.Expression + + for { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + } + decl := self.parseVariableDeclaration(&declarationList) + list = append(list, decl) + if self.token != token.COMMA { + break + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + } + + self.scope.declare(&ast.VariableDeclaration{ + Var: var_, + List: declarationList, + }) + + return list +} + +func (self *_parser) parseObjectPropertyKey() (string, string) { + idx, tkn, literal := self.idx, self.token, self.literal + value := "" + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.KEY) + } + self.next() + + switch tkn { + case token.IDENTIFIER: + value = literal + case token.NUMBER: + var err error + _, err = parseNumberLiteral(literal) + if err != nil { + self.error(idx, err.Error()) + } else { + value = literal + } + case token.STRING: + var err error + value, err = parseStringLiteral(literal[1 : len(literal)-1]) + if err != nil { + self.error(idx, err.Error()) + } + default: + // null, false, class, etc. + if matchIdentifier.MatchString(literal) { + value = literal + } + } + return literal, value +} + +func (self *_parser) parseObjectProperty() ast.Property { + literal, value := self.parseObjectPropertyKey() + if literal == "get" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "get", + Value: node, + } + } else if literal == "set" && self.token != token.COLON { + idx := self.idx + _, value := self.parseObjectPropertyKey() + parameterList := self.parseFunctionParameterList() + + node := &ast.FunctionLiteral{ + Function: idx, + ParameterList: parameterList, + } + self.parseFunctionBlock(node) + return ast.Property{ + Key: value, + Kind: "set", + Value: node, + } + } + + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.COLON) + } + self.expect(token.COLON) + + exp := ast.Property{ + Key: value, + Kind: "value", + Value: self.parseAssignmentExpression(), + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp.Value) + } + return exp +} + +func (self *_parser) parseObjectLiteral() ast.Expression { + var value []ast.Property + idx0 := self.expect(token.LEFT_BRACE) + for self.token != token.RIGHT_BRACE && self.token != token.EOF { + value = append(value, self.parseObjectProperty()) + if self.token == token.COMMA { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + continue + } + } + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.FINAL) + } + idx1 := self.expect(token.RIGHT_BRACE) + + return &ast.ObjectLiteral{ + LeftBrace: idx0, + RightBrace: idx1, + Value: value, + } +} + +func (self *_parser) parseArrayLiteral() ast.Expression { + idx0 := self.expect(token.LEFT_BRACKET) + var value []ast.Expression + for self.token != token.RIGHT_BRACKET && self.token != token.EOF { + if self.token == token.COMMA { + // This kind of comment requires a special empty expression node. + empty := &ast.EmptyExpression{self.idx, self.idx} + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(empty) + self.comments.Unset() + } + value = append(value, empty) + self.next() + continue + } + + exp := self.parseAssignmentExpression() + + value = append(value, exp) + if self.token != token.RIGHT_BRACKET { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COMMA) + } + } + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.FINAL) + } + idx1 := self.expect(token.RIGHT_BRACKET) + + return &ast.ArrayLiteral{ + LeftBracket: idx0, + RightBracket: idx1, + Value: value, + } +} + +func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + idx0 = self.expect(token.LEFT_PARENTHESIS) + if self.token != token.RIGHT_PARENTHESIS { + for { + exp := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + argumentList = append(argumentList, exp) + if self.token != token.COMMA { + break + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + } + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + idx1 = self.expect(token.RIGHT_PARENTHESIS) + return +} + +func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { + argumentList, idx0, idx1 := self.parseArgumentList() + exp := &ast.CallExpression{ + Callee: left, + LeftParenthesis: idx0, + ArgumentList: argumentList, + RightParenthesis: idx1, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + return exp +} + +func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { + period := self.expect(token.PERIOD) + + literal := self.literal + idx := self.idx + + if !matchIdentifier.MatchString(literal) { + self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadExpression{From: period, To: self.idx} + } + + self.next() + + return &ast.DotExpression{ + Left: left, + Identifier: &ast.Identifier{ + Idx: idx, + Name: literal, + }, + } +} + +func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression { + idx0 := self.expect(token.LEFT_BRACKET) + member := self.parseExpression() + idx1 := self.expect(token.RIGHT_BRACKET) + return &ast.BracketExpression{ + LeftBracket: idx0, + Left: left, + Member: member, + RightBracket: idx1, + } +} + +func (self *_parser) parseNewExpression() ast.Expression { + idx := self.expect(token.NEW) + callee := self.parseLeftHandSideExpression() + node := &ast.NewExpression{ + New: idx, + Callee: callee, + } + if self.token == token.LEFT_PARENTHESIS { + argumentList, idx0, idx1 := self.parseArgumentList() + node.ArgumentList = argumentList + node.LeftParenthesis = idx0 + node.RightParenthesis = idx1 + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(node) + } + + return node +} + +func (self *_parser) parseLeftHandSideExpression() ast.Expression { + + var left ast.Expression + if self.token == token.NEW { + left = self.parseNewExpression() + } else { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parsePrimaryExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(left) + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACKET { + left = self.parseBracketMember(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + var left ast.Expression + if self.token == token.NEW { + var newComments []*ast.Comment + if self.mode&StoreComments != 0 { + newComments = self.comments.FetchAll() + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parseNewExpression() + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(left, newComments, ast.LEADING) + } + } else { + if self.mode&StoreComments != 0 { + self.comments.MarkComments(ast.LEADING) + self.comments.MarkPrimary() + } + left = self.parsePrimaryExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(left) + } + + for { + if self.token == token.PERIOD { + left = self.parseDotMember(left) + } else if self.token == token.LEFT_BRACKET { + left = self.parseBracketMember(left) + } else if self.token == token.LEFT_PARENTHESIS { + left = self.parseCallExpression(left) + } else { + break + } + } + + return left +} + +func (self *_parser) parsePostfixExpression() ast.Expression { + operand := self.parseLeftHandSideExpressionAllowCall() + + switch self.token { + case token.INCREMENT, token.DECREMENT: + // Make sure there is no line terminator here + if self.implicitSemicolon { + break + } + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + exp := &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + Postfix: true, + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp + } + + return operand +} + +func (self *_parser) parseUnaryExpression() ast.Expression { + + switch self.token { + case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: + fallthrough + case token.DELETE, token.VOID, token.TYPEOF: + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: self.parseUnaryExpression(), + } + case token.INCREMENT, token.DECREMENT: + tkn := self.token + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + operand := self.parseUnaryExpression() + switch operand.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(idx, "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + return &ast.UnaryExpression{ + Operator: tkn, + Idx: idx, + Operand: operand, + } + } + + return self.parsePostfixExpression() +} + +func (self *_parser) parseMultiplicativeExpression() ast.Expression { + next := self.parseUnaryExpression + left := next() + + for self.token == token.MULTIPLY || self.token == token.SLASH || + self.token == token.REMAINDER { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseAdditiveExpression() ast.Expression { + next := self.parseMultiplicativeExpression + left := next() + + for self.token == token.PLUS || self.token == token.MINUS { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseShiftExpression() ast.Expression { + next := self.parseAdditiveExpression + left := next() + + for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT || + self.token == token.UNSIGNED_SHIFT_RIGHT { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseRelationalExpression() ast.Expression { + next := self.parseShiftExpression + left := next() + + allowIn := self.scope.allowIn + self.scope.allowIn = true + defer func() { + self.scope.allowIn = allowIn + }() + + switch self.token { + case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + Comparison: true, + } + return exp + case token.INSTANCEOF: + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + return exp + case token.IN: + if !allowIn { + return left + } + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + exp := &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: self.parseRelationalExpression(), + } + return exp + } + + return left +} + +func (self *_parser) parseEqualityExpression() ast.Expression { + next := self.parseRelationalExpression + left := next() + + for self.token == token.EQUAL || self.token == token.NOT_EQUAL || + self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { + tkn := self.token + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + Comparison: true, + } + } + + return left +} + +func (self *_parser) parseBitwiseAndExpression() ast.Expression { + next := self.parseEqualityExpression + left := next() + + for self.token == token.AND { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { + next := self.parseBitwiseAndExpression + left := next() + + for self.token == token.EXCLUSIVE_OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseBitwiseOrExpression() ast.Expression { + next := self.parseBitwiseExclusiveOrExpression + left := next() + + for self.token == token.OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalAndExpression() ast.Expression { + next := self.parseBitwiseOrExpression + left := next() + + for self.token == token.LOGICAL_AND { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseLogicalOrExpression() ast.Expression { + next := self.parseLogicalAndExpression + left := next() + + for self.token == token.LOGICAL_OR { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + tkn := self.token + self.next() + + left = &ast.BinaryExpression{ + Operator: tkn, + Left: left, + Right: next(), + } + } + + return left +} + +func (self *_parser) parseConditionlExpression() ast.Expression { + left := self.parseLogicalOrExpression() + + if self.token == token.QUESTION_MARK { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + + consequent := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COLON) + exp := &ast.ConditionalExpression{ + Test: left, + Consequent: consequent, + Alternate: self.parseAssignmentExpression(), + } + + return exp + } + + return left +} + +func (self *_parser) parseAssignmentExpression() ast.Expression { + left := self.parseConditionlExpression() + var operator token.Token + switch self.token { + case token.ASSIGN: + operator = self.token + case token.ADD_ASSIGN: + operator = token.PLUS + case token.SUBTRACT_ASSIGN: + operator = token.MINUS + case token.MULTIPLY_ASSIGN: + operator = token.MULTIPLY + case token.QUOTIENT_ASSIGN: + operator = token.SLASH + case token.REMAINDER_ASSIGN: + operator = token.REMAINDER + case token.AND_ASSIGN: + operator = token.AND + case token.AND_NOT_ASSIGN: + operator = token.AND_NOT + case token.OR_ASSIGN: + operator = token.OR + case token.EXCLUSIVE_OR_ASSIGN: + operator = token.EXCLUSIVE_OR + case token.SHIFT_LEFT_ASSIGN: + operator = token.SHIFT_LEFT + case token.SHIFT_RIGHT_ASSIGN: + operator = token.SHIFT_RIGHT + case token.UNSIGNED_SHIFT_RIGHT_ASSIGN: + operator = token.UNSIGNED_SHIFT_RIGHT + } + + if operator != 0 { + idx := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + switch left.(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: + default: + self.error(left.Idx0(), "Invalid left-hand side in assignment") + self.nextStatement() + return &ast.BadExpression{From: idx, To: self.idx} + } + + exp := &ast.AssignExpression{ + Left: left, + Operator: operator, + Right: self.parseAssignmentExpression(), + } + + if self.mode&StoreComments != 0 { + self.comments.SetExpression(exp) + } + + return exp + } + + return left +} + +func (self *_parser) parseExpression() ast.Expression { + next := self.parseAssignmentExpression + left := next() + + if self.token == token.COMMA { + sequence := []ast.Expression{left} + for { + if self.token != token.COMMA { + break + } + self.next() + sequence = append(sequence, next()) + } + return &ast.SequenceExpression{ + Sequence: sequence, + } + } + + return left +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/lexer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/lexer.go new file mode 100644 index 0000000..d9d69e1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/lexer.go @@ -0,0 +1,866 @@ +package parser + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" +) + +type _chr struct { + value rune + width int +} + +var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`) + +func isDecimalDigit(chr rune) bool { + return '0' <= chr && chr <= '9' +} + +func digitValue(chr rune) int { + switch { + case '0' <= chr && chr <= '9': + return int(chr - '0') + case 'a' <= chr && chr <= 'f': + return int(chr - 'a' + 10) + case 'A' <= chr && chr <= 'F': + return int(chr - 'A' + 10) + } + return 16 // Larger than any legal digit value +} + +func isDigit(chr rune, base int) bool { + return digitValue(chr) < base +} + +func isIdentifierStart(chr rune) bool { + return chr == '$' || chr == '_' || chr == '\\' || + 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || + chr >= utf8.RuneSelf && unicode.IsLetter(chr) +} + +func isIdentifierPart(chr rune) bool { + return chr == '$' || chr == '_' || chr == '\\' || + 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' || + '0' <= chr && chr <= '9' || + chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr)) +} + +func (self *_parser) scanIdentifier() (string, error) { + offset := self.chrOffset + parse := false + for isIdentifierPart(self.chr) { + if self.chr == '\\' { + distance := self.chrOffset - offset + self.read() + if self.chr != 'u' { + return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) + } + parse = true + var value rune + for j := 0; j < 4; j++ { + self.read() + decimal, ok := hex2decimal(byte(self.chr)) + if !ok { + return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr)) + } + value = value<<4 | decimal + } + if value == '\\' { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } else if distance == 0 { + if !isIdentifierStart(value) { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } + } else if distance > 0 { + if !isIdentifierPart(value) { + return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value)) + } + } + } + self.read() + } + literal := string(self.str[offset:self.chrOffset]) + if parse { + return parseStringLiteral(literal) + } + return literal, nil +} + +// 7.2 +func isLineWhiteSpace(chr rune) bool { + switch chr { + case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff': + return true + case '\u000a', '\u000d', '\u2028', '\u2029': + return false + case '\u0085': + return false + } + return unicode.IsSpace(chr) +} + +// 7.3 +func isLineTerminator(chr rune) bool { + switch chr { + case '\u000a', '\u000d', '\u2028', '\u2029': + return true + } + return false +} + +func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { + + self.implicitSemicolon = false + + for { + self.skipWhiteSpace() + + idx = self.idxOf(self.chrOffset) + insertSemicolon := false + + switch chr := self.chr; { + case isIdentifierStart(chr): + var err error + literal, err = self.scanIdentifier() + if err != nil { + tkn = token.ILLEGAL + break + } + if len(literal) > 1 { + // Keywords are longer than 1 character, avoid lookup otherwise + var strict bool + tkn, strict = token.IsKeyword(literal) + + switch tkn { + + case 0: // Not a keyword + if literal == "true" || literal == "false" { + self.insertSemicolon = true + tkn = token.BOOLEAN + return + } else if literal == "null" { + self.insertSemicolon = true + tkn = token.NULL + return + } + + case token.KEYWORD: + tkn = token.KEYWORD + if strict { + // TODO If strict and in strict mode, then this is not a break + break + } + return + + case + token.THIS, + token.BREAK, + token.THROW, // A newline after a throw is not allowed, but we need to detect it + token.RETURN, + token.CONTINUE, + token.DEBUGGER: + self.insertSemicolon = true + return + + default: + return + + } + } + self.insertSemicolon = true + tkn = token.IDENTIFIER + return + case '0' <= chr && chr <= '9': + self.insertSemicolon = true + tkn, literal = self.scanNumericLiteral(false) + return + default: + self.read() + switch chr { + case -1: + if self.insertSemicolon { + self.insertSemicolon = false + self.implicitSemicolon = true + } + tkn = token.EOF + case '\r', '\n', '\u2028', '\u2029': + self.insertSemicolon = false + self.implicitSemicolon = true + self.comments.AtLineBreak() + continue + case ':': + tkn = token.COLON + case '.': + if digitValue(self.chr) < 10 { + insertSemicolon = true + tkn, literal = self.scanNumericLiteral(true) + } else { + tkn = token.PERIOD + } + case ',': + tkn = token.COMMA + case ';': + tkn = token.SEMICOLON + case '(': + tkn = token.LEFT_PARENTHESIS + case ')': + tkn = token.RIGHT_PARENTHESIS + insertSemicolon = true + case '[': + tkn = token.LEFT_BRACKET + case ']': + tkn = token.RIGHT_BRACKET + insertSemicolon = true + case '{': + tkn = token.LEFT_BRACE + case '}': + tkn = token.RIGHT_BRACE + insertSemicolon = true + case '+': + tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT) + if tkn == token.INCREMENT { + insertSemicolon = true + } + case '-': + tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT) + if tkn == token.DECREMENT { + insertSemicolon = true + } + case '*': + tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN) + case '/': + if self.chr == '/' { + if self.mode&StoreComments != 0 { + literal := string(self.readSingleLineComment()) + self.comments.AddComment(ast.NewComment(literal, self.idx)) + continue + } + self.skipSingleLineComment() + continue + } else if self.chr == '*' { + if self.mode&StoreComments != 0 { + literal = string(self.readMultiLineComment()) + self.comments.AddComment(ast.NewComment(literal, self.idx)) + continue + } + self.skipMultiLineComment() + continue + } else { + // Could be division, could be RegExp literal + tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN) + insertSemicolon = true + } + case '%': + tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN) + case '^': + tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN) + case '<': + tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN) + case '>': + tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN) + case '=': + tkn = self.switch2(token.ASSIGN, token.EQUAL) + if tkn == token.EQUAL && self.chr == '=' { + self.read() + tkn = token.STRICT_EQUAL + } + case '!': + tkn = self.switch2(token.NOT, token.NOT_EQUAL) + if tkn == token.NOT_EQUAL && self.chr == '=' { + self.read() + tkn = token.STRICT_NOT_EQUAL + } + case '&': + if self.chr == '^' { + self.read() + tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN) + } else { + tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND) + } + case '|': + tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR) + case '~': + tkn = token.BITWISE_NOT + case '?': + tkn = token.QUESTION_MARK + case '"', '\'': + insertSemicolon = true + tkn = token.STRING + var err error + literal, err = self.scanString(self.chrOffset - 1) + if err != nil { + tkn = token.ILLEGAL + } + default: + self.errorUnexpected(idx, chr) + tkn = token.ILLEGAL + } + } + self.insertSemicolon = insertSemicolon + return + } +} + +func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + return tkn0 +} + +func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + return tkn2 + } + return tkn0 +} + +func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + if self.chr == '=' { + self.read() + return tkn3 + } + return tkn2 + } + return tkn0 +} + +func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token { + if self.chr == '=' { + self.read() + return tkn1 + } + if self.chr == chr2 { + self.read() + if self.chr == '=' { + self.read() + return tkn3 + } + if self.chr == chr3 { + self.read() + if self.chr == '=' { + self.read() + return tkn5 + } + return tkn4 + } + return tkn2 + } + return tkn0 +} + +func (self *_parser) chrAt(index int) _chr { + value, width := utf8.DecodeRuneInString(self.str[index:]) + return _chr{ + value: value, + width: width, + } +} + +func (self *_parser) _peek() rune { + if self.offset+1 < self.length { + return rune(self.str[self.offset+1]) + } + return -1 +} + +func (self *_parser) read() { + if self.offset < self.length { + self.chrOffset = self.offset + chr, width := rune(self.str[self.offset]), 1 + if chr >= utf8.RuneSelf { // !ASCII + chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) + if chr == utf8.RuneError && width == 1 { + self.error(self.chrOffset, "Invalid UTF-8 character") + } + } + self.offset += width + self.chr = chr + } else { + self.chrOffset = self.length + self.chr = -1 // EOF + } +} + +// This is here since the functions are so similar +func (self *_RegExp_parser) read() { + if self.offset < self.length { + self.chrOffset = self.offset + chr, width := rune(self.str[self.offset]), 1 + if chr >= utf8.RuneSelf { // !ASCII + chr, width = utf8.DecodeRuneInString(self.str[self.offset:]) + if chr == utf8.RuneError && width == 1 { + self.error(self.chrOffset, "Invalid UTF-8 character") + } + } + self.offset += width + self.chr = chr + } else { + self.chrOffset = self.length + self.chr = -1 // EOF + } +} + +func (self *_parser) readSingleLineComment() (result []rune) { + for self.chr != -1 { + self.read() + if isLineTerminator(self.chr) { + return + } + result = append(result, self.chr) + } + + // Get rid of the trailing -1 + result = result[:len(result)-1] + + return +} + +func (self *_parser) readMultiLineComment() (result []rune) { + self.read() + for self.chr >= 0 { + chr := self.chr + self.read() + if chr == '*' && self.chr == '/' { + self.read() + return + } + + result = append(result, chr) + } + + self.errorUnexpected(0, self.chr) + + return +} + +func (self *_parser) skipSingleLineComment() { + for self.chr != -1 { + self.read() + if isLineTerminator(self.chr) { + return + } + } +} + +func (self *_parser) skipMultiLineComment() { + self.read() + for self.chr >= 0 { + chr := self.chr + self.read() + if chr == '*' && self.chr == '/' { + self.read() + return + } + } + + self.errorUnexpected(0, self.chr) +} + +func (self *_parser) skipWhiteSpace() { + for { + switch self.chr { + case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff': + self.read() + continue + case '\r': + if self._peek() == '\n' { + self.comments.AtLineBreak() + self.read() + } + fallthrough + case '\u2028', '\u2029', '\n': + if self.insertSemicolon { + return + } + self.comments.AtLineBreak() + self.read() + continue + } + if self.chr >= utf8.RuneSelf { + if unicode.IsSpace(self.chr) { + self.read() + continue + } + } + break + } +} + +func (self *_parser) skipLineWhiteSpace() { + for isLineWhiteSpace(self.chr) { + self.read() + } +} + +func (self *_parser) scanMantissa(base int) { + for digitValue(self.chr) < base { + self.read() + } +} + +func (self *_parser) scanEscape(quote rune) { + + var length, base uint32 + switch self.chr { + //case '0', '1', '2', '3', '4', '5', '6', '7': + // Octal: + // length, base, limit = 3, 8, 255 + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0': + self.read() + return + case '\r', '\n', '\u2028', '\u2029': + self.scanNewline() + return + case 'x': + self.read() + length, base = 2, 16 + case 'u': + self.read() + length, base = 4, 16 + default: + self.read() // Always make progress + return + } + + var value uint32 + for ; length > 0 && self.chr != quote && self.chr >= 0; length-- { + digit := uint32(digitValue(self.chr)) + if digit >= base { + break + } + value = value*base + digit + self.read() + } +} + +func (self *_parser) scanString(offset int) (string, error) { + // " ' / + quote := rune(self.str[offset]) + + for self.chr != quote { + chr := self.chr + if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 { + goto newline + } + self.read() + if chr == '\\' { + if quote == '/' { + if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 { + goto newline + } + self.read() + } else { + self.scanEscape(quote) + } + } else if chr == '[' && quote == '/' { + // Allow a slash (/) in a bracket character class ([...]) + // TODO Fix this, this is hacky... + quote = -1 + } else if chr == ']' && quote == -1 { + quote = '/' + } + } + + // " ' / + self.read() + + return string(self.str[offset:self.chrOffset]), nil + +newline: + self.scanNewline() + err := "String not terminated" + if quote == '/' { + err = "Invalid regular expression: missing /" + self.error(self.idxOf(offset), err) + } + return "", errors.New(err) +} + +func (self *_parser) scanNewline() { + if self.chr == '\r' { + self.read() + if self.chr != '\n' { + return + } + } + self.read() +} + +func hex2decimal(chr byte) (value rune, ok bool) { + { + chr := rune(chr) + switch { + case '0' <= chr && chr <= '9': + return chr - '0', true + case 'a' <= chr && chr <= 'f': + return chr - 'a' + 10, true + case 'A' <= chr && chr <= 'F': + return chr - 'A' + 10, true + } + return + } +} + +func parseNumberLiteral(literal string) (value interface{}, err error) { + // TODO Is Uint okay? What about -MAX_UINT + value, err = strconv.ParseInt(literal, 0, 64) + if err == nil { + return + } + + parseIntErr := err // Save this first error, just in case + + value, err = strconv.ParseFloat(literal, 64) + if err == nil { + return + } else if err.(*strconv.NumError).Err == strconv.ErrRange { + // Infinity, etc. + return value, nil + } + + err = parseIntErr + + if err.(*strconv.NumError).Err == strconv.ErrRange { + if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') { + // Could just be a very large number (e.g. 0x8000000000000000) + var value float64 + literal = literal[2:] + for _, chr := range literal { + digit := digitValue(chr) + if digit >= 16 { + goto error + } + value = value*16 + float64(digit) + } + return value, nil + } + } + +error: + return nil, errors.New("Illegal numeric literal") +} + +func parseStringLiteral(literal string) (string, error) { + // Best case scenario... + if literal == "" { + return "", nil + } + + // Slightly less-best case scenario... + if !strings.ContainsRune(literal, '\\') { + return literal, nil + } + + str := literal + buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2)) + + for len(str) > 0 { + switch chr := str[0]; { + // We do not explicitly handle the case of the quote + // value, which can be: " ' / + // This assumes we're already passed a partially well-formed literal + case chr >= utf8.RuneSelf: + chr, size := utf8.DecodeRuneInString(str) + buffer.WriteRune(chr) + str = str[size:] + continue + case chr != '\\': + buffer.WriteByte(chr) + str = str[1:] + continue + } + + if len(str) <= 1 { + panic("len(str) <= 1") + } + chr := str[1] + var value rune + if chr >= utf8.RuneSelf { + str = str[1:] + var size int + value, size = utf8.DecodeRuneInString(str) + str = str[size:] // \ + + } else { + str = str[2:] // \ + switch chr { + case 'b': + value = '\b' + case 'f': + value = '\f' + case 'n': + value = '\n' + case 'r': + value = '\r' + case 't': + value = '\t' + case 'v': + value = '\v' + case 'x', 'u': + size := 0 + switch chr { + case 'x': + size = 2 + case 'u': + size = 4 + } + if len(str) < size { + return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size) + } + for j := 0; j < size; j++ { + decimal, ok := hex2decimal(str[j]) + if !ok { + return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size]) + } + value = value<<4 | decimal + } + str = str[size:] + if chr == 'x' { + break + } + if value > utf8.MaxRune { + panic("value > utf8.MaxRune") + } + case '0': + if len(str) == 0 || '0' > str[0] || str[0] > '7' { + value = 0 + break + } + fallthrough + case '1', '2', '3', '4', '5', '6', '7': + // TODO strict + value = rune(chr) - '0' + j := 0 + for ; j < 2; j++ { + if len(str) < j+1 { + break + } + chr := str[j] + if '0' > chr || chr > '7' { + break + } + decimal := rune(str[j]) - '0' + value = (value << 3) | decimal + } + str = str[j:] + case '\\': + value = '\\' + case '\'', '"': + value = rune(chr) + case '\r': + if len(str) > 0 { + if str[0] == '\n' { + str = str[1:] + } + } + fallthrough + case '\n': + continue + default: + value = rune(chr) + } + } + buffer.WriteRune(value) + } + + return buffer.String(), nil +} + +func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) { + + offset := self.chrOffset + tkn := token.NUMBER + + if decimalPoint { + offset-- + self.scanMantissa(10) + goto exponent + } + + if self.chr == '0' { + offset := self.chrOffset + self.read() + if self.chr == 'x' || self.chr == 'X' { + // Hexadecimal + self.read() + if isDigit(self.chr, 16) { + self.read() + } else { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + self.scanMantissa(16) + + if self.chrOffset-offset <= 2 { + // Only "0x" or "0X" + self.error(0, "Illegal hexadecimal number") + } + + goto hexadecimal + } else if self.chr == '.' { + // Float + goto float + } else { + // Octal, Float + if self.chr == 'e' || self.chr == 'E' { + goto exponent + } + self.scanMantissa(8) + if self.chr == '8' || self.chr == '9' { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + goto octal + } + } + + self.scanMantissa(10) + +float: + if self.chr == '.' { + self.read() + self.scanMantissa(10) + } + +exponent: + if self.chr == 'e' || self.chr == 'E' { + self.read() + if self.chr == '-' || self.chr == '+' { + self.read() + } + if isDecimalDigit(self.chr) { + self.read() + self.scanMantissa(10) + } else { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + } + +hexadecimal: +octal: + if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) { + return token.ILLEGAL, self.str[offset:self.chrOffset] + } + + return tkn, self.str[offset:self.chrOffset] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/parser.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/parser.go new file mode 100644 index 0000000..75b7c50 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/parser.go @@ -0,0 +1,344 @@ +/* +Package parser implements a parser for JavaScript. + + import ( + "github.com/robertkrimen/otto/parser" + ) + +Parse and return an AST + + filename := "" // A filename is optional + src := ` + // Sample xyzzy example + (function(){ + if (3.14159 > 0) { + console.log("Hello, World."); + return; + } + + var xyzzy = NaN; + console.log("Nothing happens."); + return xyzzy; + })(); + ` + + // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList + program, err := parser.ParseFile(nil, filename, src, 0) + +Warning + +The parser and AST interfaces are still works-in-progress (particularly where +node types are concerned) and may change in the future. + +*/ +package parser + +import ( + "bytes" + "encoding/base64" + "errors" + "io" + "io/ioutil" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/file" + "github.com/robertkrimen/otto/token" + "gopkg.in/sourcemap.v1" +) + +// A Mode value is a set of flags (or 0). They control optional parser functionality. +type Mode uint + +const ( + IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking) + StoreComments // Store the comments from source to the comments map +) + +type _parser struct { + str string + length int + base int + + chr rune // The current character + chrOffset int // The offset of current character + offset int // The offset after current character (may be greater than 1) + + idx file.Idx // The index of token + token token.Token // The token + literal string // The literal of the token, if any + + scope *_scope + insertSemicolon bool // If we see a newline, then insert an implicit semicolon + implicitSemicolon bool // An implicit semicolon exists + + errors ErrorList + + recover struct { + // Scratch when trying to seek to the next statement, etc. + idx file.Idx + count int + } + + mode Mode + + file *file.File + + comments *ast.Comments +} + +type Parser interface { + Scan() (tkn token.Token, literal string, idx file.Idx) +} + +func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser { + return &_parser{ + chr: ' ', // This is set so we can start scanning by skipping whitespace + str: src, + length: len(src), + base: base, + file: file.NewFile(filename, src, base).WithSourceMap(sm), + comments: ast.NewComments(), + } +} + +// Returns a new Parser. +func NewParser(filename, src string) Parser { + return _newParser(filename, src, 1, nil) +} + +func ReadSource(filename string, src interface{}) ([]byte, error) { + if src != nil { + switch src := src.(type) { + case string: + return []byte(src), nil + case []byte: + return src, nil + case *bytes.Buffer: + if src != nil { + return src.Bytes(), nil + } + case io.Reader: + var bfr bytes.Buffer + if _, err := io.Copy(&bfr, src); err != nil { + return nil, err + } + return bfr.Bytes(), nil + } + return nil, errors.New("invalid source") + } + return ioutil.ReadFile(filename) +} + +func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) { + if src == nil { + return nil, nil + } + + switch src := src.(type) { + case string: + return sourcemap.Parse(filename, []byte(src)) + case []byte: + return sourcemap.Parse(filename, src) + case *bytes.Buffer: + if src != nil { + return sourcemap.Parse(filename, src.Bytes()) + } + case io.Reader: + var bfr bytes.Buffer + if _, err := io.Copy(&bfr, src); err != nil { + return nil, err + } + return sourcemap.Parse(filename, bfr.Bytes()) + case *sourcemap.Consumer: + return src, nil + } + + return nil, errors.New("invalid sourcemap type") +} + +func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) { + src, err := ReadSource(filename, javascriptSource) + if err != nil { + return nil, err + } + + if sourcemapSource == nil { + lines := bytes.Split(src, []byte("\n")) + lastLine := lines[len(lines)-1] + if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) { + bits := bytes.SplitN(lastLine, []byte(","), 2) + if len(bits) == 2 { + if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil { + sourcemapSource = d + } + } + } + } + + sm, err := ReadSourceMap(filename, sourcemapSource) + if err != nil { + return nil, err + } + + base := 1 + if fileSet != nil { + base = fileSet.AddFile(filename, string(src)) + } + + parser := _newParser(filename, string(src), base, sm) + parser.mode = mode + program, err := parser.parse() + program.Comments = parser.comments.CommentMap + + return program, err +} + +// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns +// the corresponding ast.Program node. +// +// If fileSet == nil, ParseFile parses source without a FileSet. +// If fileSet != nil, ParseFile first adds filename and src to fileSet. +// +// The filename argument is optional and is used for labelling errors, etc. +// +// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8. +// +// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList +// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0) +// +func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) { + return ParseFileWithSourceMap(fileSet, filename, src, nil, mode) +} + +// ParseFunction parses a given parameter list and body as a function and returns the +// corresponding ast.FunctionLiteral node. +// +// The parameter list, if any, should be a comma-separated list of identifiers. +// +func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) { + + src := "(function(" + parameterList + ") {\n" + body + "\n})" + + parser := _newParser("", src, 1, nil) + program, err := parser.parse() + if err != nil { + return nil, err + } + + return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil +} + +// Scan reads a single token from the source at the current offset, increments the offset and +// returns the token.Token token, a string literal representing the value of the token (if applicable) +// and it's current file.Idx index. +func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) { + return self.scan() +} + +func (self *_parser) slice(idx0, idx1 file.Idx) string { + from := int(idx0) - self.base + to := int(idx1) - self.base + if from >= 0 && to <= len(self.str) { + return self.str[from:to] + } + + return "" +} + +func (self *_parser) parse() (*ast.Program, error) { + self.next() + program := self.parseProgram() + if false { + self.errors.Sort() + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING) + } + + return program, self.errors.Err() +} + +func (self *_parser) next() { + self.token, self.literal, self.idx = self.scan() +} + +func (self *_parser) optionalSemicolon() { + if self.token == token.SEMICOLON { + self.next() + return + } + + if self.implicitSemicolon { + self.implicitSemicolon = false + return + } + + if self.token != token.EOF && self.token != token.RIGHT_BRACE { + self.expect(token.SEMICOLON) + } +} + +func (self *_parser) semicolon() { + if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE { + if self.implicitSemicolon { + self.implicitSemicolon = false + return + } + + self.expect(token.SEMICOLON) + } +} + +func (self *_parser) idxOf(offset int) file.Idx { + return file.Idx(self.base + offset) +} + +func (self *_parser) expect(value token.Token) file.Idx { + idx := self.idx + if self.token != value { + self.errorUnexpectedToken(self.token) + } + self.next() + return idx +} + +func lineCount(str string) (int, int) { + line, last := 0, -1 + pair := false + for index, chr := range str { + switch chr { + case '\r': + line += 1 + last = index + pair = true + continue + case '\n': + if !pair { + line += 1 + } + last = index + case '\u2028', '\u2029': + line += 1 + last = index + 2 + } + pair = false + } + return line, last +} + +func (self *_parser) position(idx file.Idx) file.Position { + position := file.Position{} + offset := int(idx) - self.base + str := self.str[:offset] + position.Filename = self.file.Name() + line, last := lineCount(str) + position.Line = 1 + line + if last >= 0 { + position.Column = offset - last + } else { + position.Column = 1 + len(str) + } + + return position +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/regexp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/regexp.go new file mode 100644 index 0000000..f614dae --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/regexp.go @@ -0,0 +1,358 @@ +package parser + +import ( + "bytes" + "fmt" + "strconv" +) + +type _RegExp_parser struct { + str string + length int + + chr rune // The current character + chrOffset int // The offset of current character + offset int // The offset after current character (may be greater than 1) + + errors []error + invalid bool // The input is an invalid JavaScript RegExp + + goRegexp *bytes.Buffer +} + +// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern. +// +// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or +// backreference (\1, \2, ...) will cause an error. +// +// re2 (Go) has a different definition for \s: [\t\n\f\r ]. +// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc. +// +// If the pattern is invalid (not valid even in JavaScript), then this function +// returns the empty string and an error. +// +// If the pattern is valid, but incompatible (contains a lookahead or backreference), +// then this function returns the transformation (a non-empty string) AND an error. +func TransformRegExp(pattern string) (string, error) { + + if pattern == "" { + return "", nil + } + + // TODO If without \, if without (?=, (?!, then another shortcut + + parser := _RegExp_parser{ + str: pattern, + length: len(pattern), + goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)), + } + parser.read() // Pull in the first character + parser.scan() + var err error + if len(parser.errors) > 0 { + err = parser.errors[0] + } + if parser.invalid { + return "", err + } + + // Might not be re2 compatible, but is still a valid JavaScript RegExp + return parser.goRegexp.String(), err +} + +func (self *_RegExp_parser) scan() { + for self.chr != -1 { + switch self.chr { + case '\\': + self.read() + self.scanEscape(false) + case '(': + self.pass() + self.scanGroup() + case '[': + self.pass() + self.scanBracket() + case ')': + self.error(-1, "Unmatched ')'") + self.invalid = true + self.pass() + default: + self.pass() + } + } +} + +// (...) +func (self *_RegExp_parser) scanGroup() { + str := self.str[self.chrOffset:] + if len(str) > 1 { // A possibility of (?= or (?! + if str[0] == '?' { + if str[1] == '=' || str[1] == '!' { + self.error(-1, "re2: Invalid (%s) ", self.str[self.chrOffset:self.chrOffset+2]) + } + } + } + for self.chr != -1 && self.chr != ')' { + switch self.chr { + case '\\': + self.read() + self.scanEscape(false) + case '(': + self.pass() + self.scanGroup() + case '[': + self.pass() + self.scanBracket() + default: + self.pass() + continue + } + } + if self.chr != ')' { + self.error(-1, "Unterminated group") + self.invalid = true + return + } + self.pass() +} + +// [...] +func (self *_RegExp_parser) scanBracket() { + for self.chr != -1 { + if self.chr == ']' { + break + } else if self.chr == '\\' { + self.read() + self.scanEscape(true) + continue + } + self.pass() + } + if self.chr != ']' { + self.error(-1, "Unterminated character class") + self.invalid = true + return + } + self.pass() +} + +// \... +func (self *_RegExp_parser) scanEscape(inClass bool) { + offset := self.chrOffset + + var length, base uint32 + switch self.chr { + + case '0', '1', '2', '3', '4', '5', '6', '7': + var value int64 + size := 0 + for { + digit := int64(digitValue(self.chr)) + if digit >= 8 { + // Not a valid digit + break + } + value = value*8 + digit + self.read() + size += 1 + } + if size == 1 { // The number of characters read + _, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'}) + if err != nil { + self.errors = append(self.errors, err) + } + if value != 0 { + // An invalid backreference + self.error(-1, "re2: Invalid \\%d ", value) + } + return + } + tmp := []byte{'\\', 'x', '0', 0} + if value >= 16 { + tmp = tmp[0:2] + } else { + tmp = tmp[0:3] + } + tmp = strconv.AppendInt(tmp, value, 16) + _, err := self.goRegexp.Write(tmp) + if err != nil { + self.errors = append(self.errors, err) + } + return + + case '8', '9': + size := 0 + for { + digit := digitValue(self.chr) + if digit >= 10 { + // Not a valid digit + break + } + self.read() + size += 1 + } + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + _, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset]) + if err != nil { + self.errors = append(self.errors, err) + } + self.error(-1, "re2: Invalid \\%s ", self.str[offset:self.chrOffset]) + return + + case 'x': + self.read() + length, base = 2, 16 + + case 'u': + self.read() + length, base = 4, 16 + + case 'b': + if inClass { + _, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'}) + if err != nil { + self.errors = append(self.errors, err) + } + self.read() + return + } + fallthrough + + case 'B': + fallthrough + + case 'd', 'D', 's', 'S', 'w', 'W': + // This is slightly broken, because ECMAScript + // includes \v in \s, \S, while re2 does not + fallthrough + + case '\\': + fallthrough + + case 'f', 'n', 'r', 't', 'v': + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + self.pass() + return + + case 'c': + self.read() + var value int64 + if 'a' <= self.chr && self.chr <= 'z' { + value = int64(self.chr) - 'a' + 1 + } else if 'A' <= self.chr && self.chr <= 'Z' { + value = int64(self.chr) - 'A' + 1 + } else { + err := self.goRegexp.WriteByte('c') + if err != nil { + self.errors = append(self.errors, err) + } + return + } + tmp := []byte{'\\', 'x', '0', 0} + if value >= 16 { + tmp = tmp[0:2] + } else { + tmp = tmp[0:3] + } + tmp = strconv.AppendInt(tmp, value, 16) + _, err := self.goRegexp.Write(tmp) + if err != nil { + self.errors = append(self.errors, err) + } + self.read() + return + + default: + // $ is an identifier character, so we have to have + // a special case for it here + if self.chr == '$' || !isIdentifierPart(self.chr) { + // A non-identifier character needs escaping + err := self.goRegexp.WriteByte('\\') + if err != nil { + self.errors = append(self.errors, err) + } + } else { + // Unescape the character for re2 + } + self.pass() + return + } + + // Otherwise, we're a \u.... or \x... + valueOffset := self.chrOffset + + var value uint32 + { + length := length + for ; length > 0; length-- { + digit := uint32(digitValue(self.chr)) + if digit >= base { + // Not a valid digit + goto skip + } + value = value*base + digit + self.read() + } + } + + if length == 4 { + _, err := self.goRegexp.Write([]byte{ + '\\', + 'x', + '{', + self.str[valueOffset+0], + self.str[valueOffset+1], + self.str[valueOffset+2], + self.str[valueOffset+3], + '}', + }) + if err != nil { + self.errors = append(self.errors, err) + } + } else if length == 2 { + _, err := self.goRegexp.Write([]byte{ + '\\', + 'x', + self.str[valueOffset+0], + self.str[valueOffset+1], + }) + if err != nil { + self.errors = append(self.errors, err) + } + } else { + // Should never, ever get here... + self.error(-1, "re2: Illegal branch in scanEscape") + goto skip + } + + return + +skip: + _, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset]) + if err != nil { + self.errors = append(self.errors, err) + } +} + +func (self *_RegExp_parser) pass() { + if self.chr != -1 { + _, err := self.goRegexp.WriteRune(self.chr) + if err != nil { + self.errors = append(self.errors, err) + } + } + self.read() +} + +// TODO Better error reporting, use the offset, etc. +func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error { + err := fmt.Errorf(msg, msgValues...) + self.errors = append(self.errors, err) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/scope.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/scope.go new file mode 100644 index 0000000..e1dbdda --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/scope.go @@ -0,0 +1,44 @@ +package parser + +import ( + "github.com/robertkrimen/otto/ast" +) + +type _scope struct { + outer *_scope + allowIn bool + inIteration bool + inSwitch bool + inFunction bool + declarationList []ast.Declaration + + labels []string +} + +func (self *_parser) openScope() { + self.scope = &_scope{ + outer: self.scope, + allowIn: true, + } +} + +func (self *_parser) closeScope() { + self.scope = self.scope.outer +} + +func (self *_scope) declare(declaration ast.Declaration) { + self.declarationList = append(self.declarationList, declaration) +} + +func (self *_scope) hasLabel(name string) bool { + for _, label := range self.labels { + if label == name { + return true + } + } + if self.outer != nil && !self.inFunction { + // Crossing a function boundary to look for a label is verboten + return self.outer.hasLabel(name) + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/statement.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/statement.go new file mode 100644 index 0000000..6ff19d9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/parser/statement.go @@ -0,0 +1,938 @@ +package parser + +import ( + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/token" +) + +func (self *_parser) parseBlockStatement() *ast.BlockStatement { + node := &ast.BlockStatement{} + + // Find comments before the leading brace + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING) + self.comments.Unset() + } + + node.LeftBrace = self.expect(token.LEFT_BRACE) + node.List = self.parseStatementList() + + if self.mode&StoreComments != 0 { + self.comments.Unset() + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL) + self.comments.AfterBlock() + } + + node.RightBrace = self.expect(token.RIGHT_BRACE) + + // Find comments after the trailing brace + if self.mode&StoreComments != 0 { + self.comments.ResetLineBreak() + self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING) + } + + return node +} + +func (self *_parser) parseEmptyStatement() ast.Statement { + idx := self.expect(token.SEMICOLON) + return &ast.EmptyStatement{Semicolon: idx} +} + +func (self *_parser) parseStatementList() (list []ast.Statement) { + for self.token != token.RIGHT_BRACE && self.token != token.EOF { + statement := self.parseStatement() + list = append(list, statement) + } + + return +} + +func (self *_parser) parseStatement() ast.Statement { + + if self.token == token.EOF { + self.errorUnexpectedToken(self.token) + return &ast.BadStatement{From: self.idx, To: self.idx + 1} + } + + if self.mode&StoreComments != 0 { + self.comments.ResetLineBreak() + } + + switch self.token { + case token.SEMICOLON: + return self.parseEmptyStatement() + case token.LEFT_BRACE: + return self.parseBlockStatement() + case token.IF: + return self.parseIfStatement() + case token.DO: + statement := self.parseDoWhileStatement() + self.comments.PostProcessNode(statement) + return statement + case token.WHILE: + return self.parseWhileStatement() + case token.FOR: + return self.parseForOrForInStatement() + case token.BREAK: + return self.parseBreakStatement() + case token.CONTINUE: + return self.parseContinueStatement() + case token.DEBUGGER: + return self.parseDebuggerStatement() + case token.WITH: + return self.parseWithStatement() + case token.VAR: + return self.parseVariableStatement() + case token.FUNCTION: + return self.parseFunctionStatement() + case token.SWITCH: + return self.parseSwitchStatement() + case token.RETURN: + return self.parseReturnStatement() + case token.THROW: + return self.parseThrowStatement() + case token.TRY: + return self.parseTryStatement() + } + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + + expression := self.parseExpression() + + if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { + // LabelledStatement + colon := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() // : + + label := identifier.Name + for _, value := range self.scope.labels { + if label == value { + self.error(identifier.Idx0(), "Label '%s' already exists", label) + } + } + var labelComments []*ast.Comment + if self.mode&StoreComments != 0 { + labelComments = self.comments.FetchAll() + } + self.scope.labels = append(self.scope.labels, label) // Push the label + statement := self.parseStatement() + self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label + exp := &ast.LabelledStatement{ + Label: identifier, + Colon: colon, + Statement: statement, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING) + } + + return exp + } + + self.optionalSemicolon() + + statement := &ast.ExpressionStatement{ + Expression: expression, + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) + } + return statement +} + +func (self *_parser) parseTryStatement() ast.Statement { + var tryComments []*ast.Comment + if self.mode&StoreComments != 0 { + tryComments = self.comments.FetchAll() + } + node := &ast.TryStatement{ + Try: self.expect(token.TRY), + Body: self.parseBlockStatement(), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING) + self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING) + } + + if self.token == token.CATCH { + catch := self.idx + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + self.expect(token.LEFT_PARENTHESIS) + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + self.nextStatement() + return &ast.BadStatement{From: catch, To: self.idx} + } else { + identifier := self.parseIdentifier() + self.expect(token.RIGHT_PARENTHESIS) + node.Catch = &ast.CatchStatement{ + Catch: catch, + Parameter: identifier, + Body: self.parseBlockStatement(), + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING) + } + } + } + + if self.token == token.FINALLY { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() + if self.mode&StoreComments != 0 { + tryComments = self.comments.FetchAll() + } + + node.Finally = self.parseBlockStatement() + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING) + } + } + + if node.Catch == nil && node.Finally == nil { + self.error(node.Try, "Missing catch or finally after try") + return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()} + } + + return node +} + +func (self *_parser) parseFunctionParameterList() *ast.ParameterList { + opening := self.expect(token.LEFT_PARENTHESIS) + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + var list []*ast.Identifier + for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + } else { + identifier := self.parseIdentifier() + list = append(list, identifier) + } + if self.token != token.RIGHT_PARENTHESIS { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COMMA) + } + } + closing := self.expect(token.RIGHT_PARENTHESIS) + + return &ast.ParameterList{ + Opening: opening, + List: list, + Closing: closing, + } +} + +func (self *_parser) parseParameterList() (list []string) { + for self.token != token.EOF { + if self.token != token.IDENTIFIER { + self.expect(token.IDENTIFIER) + } + list = append(list, self.literal) + self.next() + if self.token != token.EOF { + self.expect(token.COMMA) + } + } + return +} + +func (self *_parser) parseFunctionStatement() *ast.FunctionStatement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + function := &ast.FunctionStatement{ + Function: self.parseFunction(true), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(function, comments, ast.LEADING) + } + + return function +} + +func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral { + + node := &ast.FunctionLiteral{ + Function: self.expect(token.FUNCTION), + } + + var name *ast.Identifier + if self.token == token.IDENTIFIER { + name = self.parseIdentifier() + if declaration { + self.scope.declare(&ast.FunctionDeclaration{ + Function: node, + }) + } + } else if declaration { + // Use expect error handling + self.expect(token.IDENTIFIER) + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + node.Name = name + node.ParameterList = self.parseFunctionParameterList() + self.parseFunctionBlock(node) + node.Source = self.slice(node.Idx0(), node.Idx1()) + + return node +} + +func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) { + { + self.openScope() + inFunction := self.scope.inFunction + self.scope.inFunction = true + defer func() { + self.scope.inFunction = inFunction + self.closeScope() + }() + node.Body = self.parseBlockStatement() + node.DeclarationList = self.scope.declarationList + } +} + +func (self *_parser) parseDebuggerStatement() ast.Statement { + idx := self.expect(token.DEBUGGER) + + node := &ast.DebuggerStatement{ + Debugger: idx, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING) + } + + self.semicolon() + return node +} + +func (self *_parser) parseReturnStatement() ast.Statement { + idx := self.expect(token.RETURN) + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + + if !self.scope.inFunction { + self.error(idx, "Illegal return statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + node := &ast.ReturnStatement{ + Return: idx, + } + + if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF { + node.Argument = self.parseExpression() + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + self.semicolon() + + return node +} + +func (self *_parser) parseThrowStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.THROW) + + if self.implicitSemicolon { + if self.chr == -1 { // Hackish + self.error(idx, "Unexpected end of input") + } else { + self.error(idx, "Illegal newline after throw") + } + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + node := &ast.ThrowStatement{ + Argument: self.parseExpression(), + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + self.semicolon() + + return node +} + +func (self *_parser) parseSwitchStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.SWITCH) + if self.mode&StoreComments != 0 { + comments = append(comments, self.comments.FetchAll()...) + } + self.expect(token.LEFT_PARENTHESIS) + node := &ast.SwitchStatement{ + Discriminant: self.parseExpression(), + Default: -1, + } + self.expect(token.RIGHT_PARENTHESIS) + if self.mode&StoreComments != 0 { + comments = append(comments, self.comments.FetchAll()...) + } + + self.expect(token.LEFT_BRACE) + + inSwitch := self.scope.inSwitch + self.scope.inSwitch = true + defer func() { + self.scope.inSwitch = inSwitch + }() + + for index := 0; self.token != token.EOF; index++ { + if self.token == token.RIGHT_BRACE { + self.next() + break + } + + clause := self.parseCaseStatement() + if clause.Test == nil { + if node.Default != -1 { + self.error(clause.Case, "Already saw a default in switch") + } + node.Default = index + } + node.Body = append(node.Body, clause) + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + return node +} + +func (self *_parser) parseWithStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.WITH) + var withComments []*ast.Comment + if self.mode&StoreComments != 0 { + withComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + + node := &ast.WithStatement{ + Object: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + + if self.mode&StoreComments != 0 { + //comments = append(comments, self.comments.FetchAll()...) + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, withComments, ast.WITH) + } + + node.Body = self.parseStatement() + + return node +} + +func (self *_parser) parseCaseStatement() *ast.CaseStatement { + node := &ast.CaseStatement{ + Case: self.idx, + } + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + self.comments.Unset() + } + + if self.token == token.DEFAULT { + self.next() + } else { + self.expect(token.CASE) + node.Test = self.parseExpression() + } + + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.COLON) + + for { + if self.token == token.EOF || + self.token == token.RIGHT_BRACE || + self.token == token.CASE || + self.token == token.DEFAULT { + break + } + consequent := self.parseStatement() + node.Consequent = append(node.Consequent, consequent) + } + + // Link the comments to the case statement + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + } + + return node +} + +func (self *_parser) parseIterationStatement() ast.Statement { + inIteration := self.scope.inIteration + self.scope.inIteration = true + defer func() { + self.scope.inIteration = inIteration + }() + return self.parseStatement() +} + +func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement { + + // Already have consumed " in" + + source := self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + body := self.parseIterationStatement() + + forin := &ast.ForInStatement{ + Into: into, + Source: source, + Body: body, + } + + return forin +} + +func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement { + + // Already have consumed " ;" + + var test, update ast.Expression + + if self.token != token.SEMICOLON { + test = self.parseExpression() + } + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.SEMICOLON) + + if self.token != token.RIGHT_PARENTHESIS { + update = self.parseExpression() + } + self.expect(token.RIGHT_PARENTHESIS) + body := self.parseIterationStatement() + + forstatement := &ast.ForStatement{ + Initializer: initializer, + Test: test, + Update: update, + Body: body, + } + + return forstatement +} + +func (self *_parser) parseForOrForInStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.FOR) + var forComments []*ast.Comment + if self.mode&StoreComments != 0 { + forComments = self.comments.FetchAll() + } + self.expect(token.LEFT_PARENTHESIS) + + var left []ast.Expression + + forIn := false + if self.token != token.SEMICOLON { + + allowIn := self.scope.allowIn + self.scope.allowIn = false + if self.token == token.VAR { + var_ := self.idx + var varComments []*ast.Comment + if self.mode&StoreComments != 0 { + varComments = self.comments.FetchAll() + self.comments.Unset() + } + self.next() + list := self.parseVariableDeclarationList(var_) + if len(list) == 1 && self.token == token.IN { + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.next() // in + forIn = true + left = []ast.Expression{list[0]} // There is only one declaration + } else { + left = list + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING) + } + } else { + left = append(left, self.parseExpression()) + if self.token == token.IN { + self.next() + forIn = true + } + } + self.scope.allowIn = allowIn + } + + if forIn { + switch left[0].(type) { + case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression: + // These are all acceptable + default: + self.error(idx, "Invalid left-hand side in for-in") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} + } + + forin := self.parseForIn(left[0]) + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(forin, comments, ast.LEADING) + self.comments.CommentMap.AddComments(forin, forComments, ast.FOR) + } + return forin + } + + if self.mode&StoreComments != 0 { + self.comments.Unset() + } + self.expect(token.SEMICOLON) + initializer := &ast.SequenceExpression{Sequence: left} + forstatement := self.parseFor(initializer) + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING) + self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR) + } + return forstatement +} + +func (self *_parser) parseVariableStatement() *ast.VariableStatement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.VAR) + + list := self.parseVariableDeclarationList(idx) + + statement := &ast.VariableStatement{ + Var: idx, + List: list, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(statement, comments, ast.LEADING) + self.comments.Unset() + } + self.semicolon() + + return statement +} + +func (self *_parser) parseDoWhileStatement() ast.Statement { + inIteration := self.scope.inIteration + self.scope.inIteration = true + defer func() { + self.scope.inIteration = inIteration + }() + + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.DO) + var doComments []*ast.Comment + if self.mode&StoreComments != 0 { + doComments = self.comments.FetchAll() + } + + node := &ast.DoWhileStatement{} + if self.token == token.LEFT_BRACE { + node.Body = self.parseBlockStatement() + } else { + node.Body = self.parseStatement() + } + + self.expect(token.WHILE) + var whileComments []*ast.Comment + if self.mode&StoreComments != 0 { + whileComments = self.comments.FetchAll() + } + self.expect(token.LEFT_PARENTHESIS) + node.Test = self.parseExpression() + self.expect(token.RIGHT_PARENTHESIS) + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, doComments, ast.DO) + self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) + } + + return node +} + +func (self *_parser) parseWhileStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.WHILE) + + var whileComments []*ast.Comment + if self.mode&StoreComments != 0 { + whileComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + node := &ast.WhileStatement{ + Test: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + node.Body = self.parseIterationStatement() + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE) + } + + return node +} + +func (self *_parser) parseIfStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + self.expect(token.IF) + var ifComments []*ast.Comment + if self.mode&StoreComments != 0 { + ifComments = self.comments.FetchAll() + } + + self.expect(token.LEFT_PARENTHESIS) + node := &ast.IfStatement{ + Test: self.parseExpression(), + } + self.expect(token.RIGHT_PARENTHESIS) + if self.token == token.LEFT_BRACE { + node.Consequent = self.parseBlockStatement() + } else { + node.Consequent = self.parseStatement() + } + + if self.token == token.ELSE { + self.next() + node.Alternate = self.parseStatement() + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(node, comments, ast.LEADING) + self.comments.CommentMap.AddComments(node, ifComments, ast.IF) + } + + return node +} + +func (self *_parser) parseSourceElement() ast.Statement { + statement := self.parseStatement() + //self.comments.Unset() + return statement +} + +func (self *_parser) parseSourceElements() []ast.Statement { + body := []ast.Statement(nil) + + for { + if self.token != token.STRING { + break + } + body = append(body, self.parseSourceElement()) + } + + for self.token != token.EOF { + body = append(body, self.parseSourceElement()) + } + + return body +} + +func (self *_parser) parseProgram() *ast.Program { + self.openScope() + defer self.closeScope() + return &ast.Program{ + Body: self.parseSourceElements(), + DeclarationList: self.scope.declarationList, + File: self.file, + } +} + +func (self *_parser) parseBreakStatement() ast.Statement { + var comments []*ast.Comment + if self.mode&StoreComments != 0 { + comments = self.comments.FetchAll() + } + idx := self.expect(token.BREAK) + semicolon := self.implicitSemicolon + if self.token == token.SEMICOLON { + semicolon = true + self.next() + } + + if semicolon || self.token == token.RIGHT_BRACE { + self.implicitSemicolon = false + if !self.scope.inIteration && !self.scope.inSwitch { + goto illegal + } + breakStatement := &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + } + + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) + self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING) + } + + return breakStatement + } + + if self.token == token.IDENTIFIER { + identifier := self.parseIdentifier() + if !self.scope.hasLabel(identifier.Name) { + self.error(idx, "Undefined label '%s'", identifier.Name) + return &ast.BadStatement{From: idx, To: identifier.Idx1()} + } + self.semicolon() + breakStatement := &ast.BranchStatement{ + Idx: idx, + Token: token.BREAK, + Label: identifier, + } + if self.mode&StoreComments != 0 { + self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING) + } + + return breakStatement + } + + self.expect(token.IDENTIFIER) + +illegal: + self.error(idx, "Illegal break statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} +} + +func (self *_parser) parseContinueStatement() ast.Statement { + idx := self.expect(token.CONTINUE) + semicolon := self.implicitSemicolon + if self.token == token.SEMICOLON { + semicolon = true + self.next() + } + + if semicolon || self.token == token.RIGHT_BRACE { + self.implicitSemicolon = false + if !self.scope.inIteration { + goto illegal + } + return &ast.BranchStatement{ + Idx: idx, + Token: token.CONTINUE, + } + } + + if self.token == token.IDENTIFIER { + identifier := self.parseIdentifier() + if !self.scope.hasLabel(identifier.Name) { + self.error(idx, "Undefined label '%s'", identifier.Name) + return &ast.BadStatement{From: idx, To: identifier.Idx1()} + } + if !self.scope.inIteration { + goto illegal + } + self.semicolon() + return &ast.BranchStatement{ + Idx: idx, + Token: token.CONTINUE, + Label: identifier, + } + } + + self.expect(token.IDENTIFIER) + +illegal: + self.error(idx, "Illegal continue statement") + self.nextStatement() + return &ast.BadStatement{From: idx, To: self.idx} +} + +// Find the next statement after an error (recover) +func (self *_parser) nextStatement() { + for { + switch self.token { + case token.BREAK, token.CONTINUE, + token.FOR, token.IF, token.RETURN, token.SWITCH, + token.VAR, token.DO, token.TRY, token.WITH, + token.WHILE, token.THROW, token.CATCH, token.FINALLY: + // Return only if parser made some progress since last + // sync or if it has not reached 10 next calls without + // progress. Otherwise consume at least one token to + // avoid an endless parser loop + if self.idx == self.recover.idx && self.recover.count < 10 { + self.recover.count++ + return + } + if self.idx > self.recover.idx { + self.recover.idx = self.idx + self.recover.count = 0 + return + } + // Reaching here indicates a parser bug, likely an + // incorrect token list in this function, but it only + // leads to skipping of possibly correct code if a + // previous error is present, and thus is preferred + // over a non-terminating parse. + case token.EOF: + return + } + self.next() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/property.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/property.go new file mode 100644 index 0000000..5445ecc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/property.go @@ -0,0 +1,220 @@ +package otto + +// property + +type _propertyMode int + +const ( + modeWriteMask _propertyMode = 0700 + modeEnumerateMask = 0070 + modeConfigureMask = 0007 + modeOnMask = 0111 + modeOffMask = 0000 + modeSetMask = 0222 // If value is 2, then mode is neither "On" nor "Off" +) + +type _propertyGetSet [2]*_object + +var _nilGetSetObject _object = _object{} + +type _property struct { + value interface{} + mode _propertyMode +} + +func (self _property) writable() bool { + return self.mode&modeWriteMask == modeWriteMask&modeOnMask +} + +func (self *_property) writeOn() { + self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeOnMask) +} + +func (self *_property) writeOff() { + self.mode &= ^modeWriteMask +} + +func (self *_property) writeClear() { + self.mode = (self.mode & ^modeWriteMask) | (modeWriteMask & modeSetMask) +} + +func (self _property) writeSet() bool { + return 0 == self.mode&modeWriteMask&modeSetMask +} + +func (self _property) enumerable() bool { + return self.mode&modeEnumerateMask == modeEnumerateMask&modeOnMask +} + +func (self *_property) enumerateOn() { + self.mode = (self.mode & ^modeEnumerateMask) | (modeEnumerateMask & modeOnMask) +} + +func (self *_property) enumerateOff() { + self.mode &= ^modeEnumerateMask +} + +func (self _property) enumerateSet() bool { + return 0 == self.mode&modeEnumerateMask&modeSetMask +} + +func (self _property) configurable() bool { + return self.mode&modeConfigureMask == modeConfigureMask&modeOnMask +} + +func (self *_property) configureOn() { + self.mode = (self.mode & ^modeConfigureMask) | (modeConfigureMask & modeOnMask) +} + +func (self *_property) configureOff() { + self.mode &= ^modeConfigureMask +} + +func (self _property) configureSet() bool { + return 0 == self.mode&modeConfigureMask&modeSetMask +} + +func (self _property) copy() *_property { + property := self + return &property +} + +func (self _property) get(this *_object) Value { + switch value := self.value.(type) { + case Value: + return value + case _propertyGetSet: + if value[0] != nil { + return value[0].call(toValue(this), nil, false, nativeFrame) + } + } + return Value{} +} + +func (self _property) isAccessorDescriptor() bool { + setGet, test := self.value.(_propertyGetSet) + return test && (setGet[0] != nil || setGet[1] != nil) +} + +func (self _property) isDataDescriptor() bool { + if self.writeSet() { // Either "On" or "Off" + return true + } + value, valid := self.value.(Value) + return valid && !value.isEmpty() +} + +func (self _property) isGenericDescriptor() bool { + return !(self.isDataDescriptor() || self.isAccessorDescriptor()) +} + +func (self _property) isEmpty() bool { + return self.mode == 0222 && self.isGenericDescriptor() +} + +// _enumerableValue, _enumerableTrue, _enumerableFalse? +// .enumerableValue() .enumerableExists() + +func toPropertyDescriptor(rt *_runtime, value Value) (descriptor _property) { + objectDescriptor := value._object() + if objectDescriptor == nil { + panic(rt.panicTypeError()) + } + + { + descriptor.mode = modeSetMask // Initially nothing is set + if objectDescriptor.hasProperty("enumerable") { + if objectDescriptor.get("enumerable").bool() { + descriptor.enumerateOn() + } else { + descriptor.enumerateOff() + } + } + + if objectDescriptor.hasProperty("configurable") { + if objectDescriptor.get("configurable").bool() { + descriptor.configureOn() + } else { + descriptor.configureOff() + } + } + + if objectDescriptor.hasProperty("writable") { + if objectDescriptor.get("writable").bool() { + descriptor.writeOn() + } else { + descriptor.writeOff() + } + } + } + + var getter, setter *_object + getterSetter := false + + if objectDescriptor.hasProperty("get") { + value := objectDescriptor.get("get") + if value.IsDefined() { + if !value.isCallable() { + panic(rt.panicTypeError()) + } + getter = value._object() + getterSetter = true + } else { + getter = &_nilGetSetObject + getterSetter = true + } + } + + if objectDescriptor.hasProperty("set") { + value := objectDescriptor.get("set") + if value.IsDefined() { + if !value.isCallable() { + panic(rt.panicTypeError()) + } + setter = value._object() + getterSetter = true + } else { + setter = &_nilGetSetObject + getterSetter = true + } + } + + if getterSetter { + if descriptor.writeSet() { + panic(rt.panicTypeError()) + } + descriptor.value = _propertyGetSet{getter, setter} + } + + if objectDescriptor.hasProperty("value") { + if getterSetter { + panic(rt.panicTypeError()) + } + descriptor.value = objectDescriptor.get("value") + } + + return +} + +func (self *_runtime) fromPropertyDescriptor(descriptor _property) *_object { + object := self.newObject() + if descriptor.isDataDescriptor() { + object.defineProperty("value", descriptor.value.(Value), 0111, false) + object.defineProperty("writable", toValue_bool(descriptor.writable()), 0111, false) + } else if descriptor.isAccessorDescriptor() { + getSet := descriptor.value.(_propertyGetSet) + get := Value{} + if getSet[0] != nil { + get = toValue_object(getSet[0]) + } + set := Value{} + if getSet[1] != nil { + set = toValue_object(getSet[1]) + } + object.defineProperty("get", get, 0111, false) + object.defineProperty("set", set, 0111, false) + } + object.defineProperty("enumerable", toValue_bool(descriptor.enumerable()), 0111, false) + object.defineProperty("configurable", toValue_bool(descriptor.configurable()), 0111, false) + return object +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/README.markdown new file mode 100644 index 0000000..ba2d389 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/README.markdown @@ -0,0 +1,51 @@ +# registry +-- + import "github.com/robertkrimen/otto/registry" + +Package registry is an expirmental package to facillitate altering the otto +runtime via import. + +This interface can change at any time. + +## Usage + +#### func Apply + +```go +func Apply(callback func(Entry)) +``` + +#### type Entry + +```go +type Entry struct { +} +``` + + +#### func Register + +```go +func Register(source func() string) *Entry +``` + +#### func (*Entry) Disable + +```go +func (self *Entry) Disable() +``` + +#### func (*Entry) Enable + +```go +func (self *Entry) Enable() +``` + +#### func (Entry) Source + +```go +func (self Entry) Source() string +``` + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/registry.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/registry.go new file mode 100644 index 0000000..966638a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/registry/registry.go @@ -0,0 +1,47 @@ +/* +Package registry is an expirmental package to facillitate altering the otto runtime via import. + +This interface can change at any time. +*/ +package registry + +var registry []*Entry = make([]*Entry, 0) + +type Entry struct { + active bool + source func() string +} + +func newEntry(source func() string) *Entry { + return &Entry{ + active: true, + source: source, + } +} + +func (self *Entry) Enable() { + self.active = true +} + +func (self *Entry) Disable() { + self.active = false +} + +func (self Entry) Source() string { + return self.source() +} + +func Apply(callback func(Entry)) { + for _, entry := range registry { + if !entry.active { + continue + } + callback(*entry) + } +} + +func Register(source func() string) *Entry { + entry := newEntry(source) + registry = append(registry, entry) + return entry +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/result.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/result.go new file mode 100644 index 0000000..63642e7 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/result.go @@ -0,0 +1,30 @@ +package otto + +import () + +type _resultKind int + +const ( + resultNormal _resultKind = iota + resultReturn + resultBreak + resultContinue +) + +type _result struct { + kind _resultKind + value Value + target string +} + +func newReturnResult(value Value) _result { + return _result{resultReturn, value, ""} +} + +func newContinueResult(target string) _result { + return _result{resultContinue, emptyValue, target} +} + +func newBreakResult(target string) _result { + return _result{resultBreak, emptyValue, target} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/runtime.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/runtime.go new file mode 100644 index 0000000..5762bd0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/runtime.go @@ -0,0 +1,711 @@ +package otto + +import ( + "errors" + "fmt" + "math" + "path" + "reflect" + "runtime" + "strconv" + "sync" + + "github.com/robertkrimen/otto/ast" + "github.com/robertkrimen/otto/parser" +) + +type _global struct { + Object *_object // Object( ... ), new Object( ... ) - 1 (length) + Function *_object // Function( ... ), new Function( ... ) - 1 + Array *_object // Array( ... ), new Array( ... ) - 1 + String *_object // String( ... ), new String( ... ) - 1 + Boolean *_object // Boolean( ... ), new Boolean( ... ) - 1 + Number *_object // Number( ... ), new Number( ... ) - 1 + Math *_object + Date *_object // Date( ... ), new Date( ... ) - 7 + RegExp *_object // RegExp( ... ), new RegExp( ... ) - 2 + Error *_object // Error( ... ), new Error( ... ) - 1 + EvalError *_object + TypeError *_object + RangeError *_object + ReferenceError *_object + SyntaxError *_object + URIError *_object + JSON *_object + + ObjectPrototype *_object // Object.prototype + FunctionPrototype *_object // Function.prototype + ArrayPrototype *_object // Array.prototype + StringPrototype *_object // String.prototype + BooleanPrototype *_object // Boolean.prototype + NumberPrototype *_object // Number.prototype + DatePrototype *_object // Date.prototype + RegExpPrototype *_object // RegExp.prototype + ErrorPrototype *_object // Error.prototype + EvalErrorPrototype *_object + TypeErrorPrototype *_object + RangeErrorPrototype *_object + ReferenceErrorPrototype *_object + SyntaxErrorPrototype *_object + URIErrorPrototype *_object +} + +type _runtime struct { + global _global + globalObject *_object + globalStash *_objectStash + scope *_scope + otto *Otto + eval *_object // The builtin eval, for determine indirect versus direct invocation + debugger func(*Otto) + random func() float64 + stackLimit int + traceLimit int + + labels []string // FIXME + lck sync.Mutex +} + +func (self *_runtime) enterScope(scope *_scope) { + scope.outer = self.scope + if self.scope != nil { + if self.stackLimit != 0 && self.scope.depth+1 >= self.stackLimit { + panic(self.panicRangeError("Maximum call stack size exceeded")) + } + + scope.depth = self.scope.depth + 1 + } + + self.scope = scope +} + +func (self *_runtime) leaveScope() { + self.scope = self.scope.outer +} + +// FIXME This is used in two places (cloning) +func (self *_runtime) enterGlobalScope() { + self.enterScope(newScope(self.globalStash, self.globalStash, self.globalObject)) +} + +func (self *_runtime) enterFunctionScope(outer _stash, this Value) *_fnStash { + if outer == nil { + outer = self.globalStash + } + stash := self.newFunctionStash(outer) + var thisObject *_object + switch this.kind { + case valueUndefined, valueNull: + thisObject = self.globalObject + default: + thisObject = self.toObject(this) + } + self.enterScope(newScope(stash, stash, thisObject)) + return stash +} + +func (self *_runtime) putValue(reference _reference, value Value) { + name := reference.putValue(value) + if name != "" { + // Why? -- If reference.base == nil + // strict = false + self.globalObject.defineProperty(name, value, 0111, false) + } +} + +func (self *_runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, exception bool) { + // resultValue = The value of the block (e.g. the last statement) + // throw = Something was thrown + // throwValue = The value of what was thrown + // other = Something that changes flow (return, break, continue) that is not a throw + // Otherwise, some sort of unknown panic happened, we'll just propagate it + defer func() { + if caught := recover(); caught != nil { + if exception, ok := caught.(*_exception); ok { + caught = exception.eject() + } + switch caught := caught.(type) { + case _error: + exception = true + tryValue = toValue_object(self.newError(caught.name, caught.messageValue(), 0)) + case Value: + exception = true + tryValue = caught + default: + panic(caught) + } + } + }() + + tryValue = inner() + return +} + +// toObject + +func (self *_runtime) toObject(value Value) *_object { + switch value.kind { + case valueEmpty, valueUndefined, valueNull: + panic(self.panicTypeError()) + case valueBoolean: + return self.newBoolean(value) + case valueString: + return self.newString(value) + case valueNumber: + return self.newNumber(value) + case valueObject: + return value._object() + } + panic(self.panicTypeError()) +} + +func (self *_runtime) objectCoerce(value Value) (*_object, error) { + switch value.kind { + case valueUndefined: + return nil, errors.New("undefined") + case valueNull: + return nil, errors.New("null") + case valueBoolean: + return self.newBoolean(value), nil + case valueString: + return self.newString(value), nil + case valueNumber: + return self.newNumber(value), nil + case valueObject: + return value._object(), nil + } + panic(self.panicTypeError()) +} + +func checkObjectCoercible(rt *_runtime, value Value) { + isObject, mustCoerce := testObjectCoercible(value) + if !isObject && !mustCoerce { + panic(rt.panicTypeError()) + } +} + +// testObjectCoercible + +func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) { + switch value.kind { + case valueReference, valueEmpty, valueNull, valueUndefined: + return false, false + case valueNumber, valueString, valueBoolean: + return false, true + case valueObject: + return true, false + default: + panic("this should never happen") + } +} + +func (self *_runtime) safeToValue(value interface{}) (Value, error) { + result := Value{} + err := catchPanic(func() { + result = self.toValue(value) + }) + return result, err +} + +// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics. +// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily. +func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value { + val := reflect.ValueOf(v.export()) + + if val.Kind() == t.Kind() { + return val + } + + if val.Kind() == reflect.Interface { + val = reflect.ValueOf(val.Interface()) + } + + switch val.Kind() { + case reflect.Float32, reflect.Float64: + f64 := val.Float() + switch t.Kind() { + case reflect.Float64: + return reflect.ValueOf(f64) + case reflect.Float32: + if reflect.Zero(t).OverflowFloat(f64) { + panic(self.panicRangeError("converting float64 to float32 would overflow")) + } + + return val.Convert(t) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + i64 := int64(f64) + if float64(i64) != f64 { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t))) + } + + // The float represents an integer + val = reflect.ValueOf(i64) + default: + panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t))) + } + } + + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := val.Int() + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if reflect.Zero(t).OverflowInt(i64) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if i64 < 0 { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t))) + } + if reflect.Zero(t).OverflowUint(uint64(i64)) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := val.Uint() + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if reflect.Zero(t).OverflowUint(u64) { + panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))) + } + return val.Convert(t) + } + } + + panic(self.panicTypeError(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type()))) +} + +var typeOfValue = reflect.TypeOf(Value{}) + +// convertCallParameter converts request val to type t if possible. +// If the conversion fails due to overflow or type miss-match then it panics. +// If no conversion is known then the original value is returned. +func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value { + if t == typeOfValue { + return reflect.ValueOf(v) + } + + if v.kind == valueObject { + if gso, ok := v._object().value.(*_goStructObject); ok { + if gso.value.Type().AssignableTo(t) { + return gso.value + } + } + } + + if t.Kind() == reflect.Interface { + iv := reflect.ValueOf(v.export()) + if iv.Type().AssignableTo(t) { + return iv + } + } + + tk := t.Kind() + + if tk == reflect.Ptr { + switch v.kind { + case valueEmpty, valueNull, valueUndefined: + return reflect.Zero(t) + default: + var vv reflect.Value + if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil { + if vv.CanAddr() { + return vv.Addr() + } + + pv := reflect.New(vv.Type()) + pv.Elem().Set(vv) + return pv + } + } + } + + switch tk { + case reflect.Bool: + return reflect.ValueOf(v.bool()) + case reflect.String: + switch v.kind { + case valueString: + return reflect.ValueOf(v.value) + case valueNumber: + return reflect.ValueOf(fmt.Sprintf("%v", v.value)) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + switch v.kind { + case valueNumber: + return self.convertNumeric(v, t) + } + case reflect.Slice: + if o := v._object(); o != nil { + if lv := o.get("length"); lv.IsNumber() { + l := lv.number().int64 + + s := reflect.MakeSlice(t, int(l), int(l)) + + tt := t.Elem() + + if o.class == "Array" { + for i := int64(0); i < l; i++ { + p, ok := o.property[strconv.FormatInt(i, 10)] + if !ok { + continue + } + + e, ok := p.value.(Value) + if !ok { + continue + } + + ev := self.convertCallParameter(e, tt) + + s.Index(int(i)).Set(ev) + } + } else if o.class == "GoArray" { + + var gslice bool + switch o.value.(type) { + case *_goSliceObject: + gslice = true + case *_goArrayObject: + gslice = false + } + + for i := int64(0); i < l; i++ { + var p *_property + if gslice { + p = goSliceGetOwnProperty(o, strconv.FormatInt(i, 10)) + } else { + p = goArrayGetOwnProperty(o, strconv.FormatInt(i, 10)) + } + if p == nil { + continue + } + + e, ok := p.value.(Value) + if !ok { + continue + } + + ev := self.convertCallParameter(e, tt) + + s.Index(int(i)).Set(ev) + } + } + + return s + } + } + case reflect.Map: + if o := v._object(); o != nil && t.Key().Kind() == reflect.String { + m := reflect.MakeMap(t) + + o.enumerate(false, func(k string) bool { + m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem())) + return true + }) + + return m + } + case reflect.Func: + if t.NumOut() > 1 { + panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported")) + } + + if o := v._object(); o != nil && o.class == "Function" { + return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value { + l := make([]interface{}, len(args)) + for i, a := range args { + if a.CanInterface() { + l[i] = a.Interface() + } + } + + rv, err := v.Call(nullValue, l...) + if err != nil { + panic(err) + } + + if t.NumOut() == 0 { + return nil + } + + return []reflect.Value{self.convertCallParameter(rv, t.Out(0))} + }) + } + } + + if tk == reflect.String { + if o := v._object(); o != nil && o.hasProperty("toString") { + if fn := o.get("toString"); fn.IsFunction() { + sv, err := fn.Call(v) + if err != nil { + panic(err) + } + + var r reflect.Value + if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil { + return r + } + } + } + + return reflect.ValueOf(v.String()) + } + + s := "OTTO DOES NOT UNDERSTAND THIS TYPE" + switch v.kind { + case valueBoolean: + s = "boolean" + case valueNull: + s = "null" + case valueNumber: + s = "number" + case valueString: + s = "string" + case valueUndefined: + s = "undefined" + case valueObject: + s = v.Class() + } + + panic(self.panicTypeError("can't convert from %q to %q", s, t.String())) +} + +func (self *_runtime) toValue(value interface{}) Value { + switch value := value.(type) { + case Value: + return value + case func(FunctionCall) Value: + var name, file string + var line int + pc := reflect.ValueOf(value).Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + return toValue_object(self.newNativeFunction(name, file, line, value)) + case _nativeFunction: + var name, file string + var line int + pc := reflect.ValueOf(value).Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + return toValue_object(self.newNativeFunction(name, file, line, value)) + case Object, *Object, _object, *_object: + // Nothing happens. + // FIXME We should really figure out what can come here. + // This catch-all is ugly. + default: + { + value := reflect.ValueOf(value) + + switch value.Kind() { + case reflect.Ptr: + switch reflect.Indirect(value).Kind() { + case reflect.Struct: + return toValue_object(self.newGoStructObject(value)) + case reflect.Array: + return toValue_object(self.newGoArray(value)) + } + case reflect.Struct: + return toValue_object(self.newGoStructObject(value)) + case reflect.Map: + return toValue_object(self.newGoMapObject(value)) + case reflect.Slice: + return toValue_object(self.newGoSlice(value)) + case reflect.Array: + return toValue_object(self.newGoArray(value)) + case reflect.Func: + var name, file string + var line int + if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr { + pc := v.Pointer() + fn := runtime.FuncForPC(pc) + if fn != nil { + name = fn.Name() + file, line = fn.FileLine(pc) + file = path.Base(file) + } + } + + typ := value.Type() + + return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value { + nargs := typ.NumIn() + + if len(c.ArgumentList) != nargs { + if typ.IsVariadic() { + if len(c.ArgumentList) < nargs-1 { + panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList)))) + } + } else { + panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList)))) + } + } + + in := make([]reflect.Value, len(c.ArgumentList)) + + callSlice := false + + for i, a := range c.ArgumentList { + var t reflect.Type + + n := i + if n >= nargs-1 && typ.IsVariadic() { + if n > nargs-1 { + n = nargs - 1 + } + + t = typ.In(n).Elem() + } else { + t = typ.In(n) + } + + // if this is a variadic Go function, and the caller has supplied + // exactly the number of JavaScript arguments required, and this + // is the last JavaScript argument, try treating the it as the + // actual set of variadic Go arguments. if that succeeds, break + // out of the loop. + if typ.IsVariadic() && len(c.ArgumentList) == nargs && i == nargs-1 { + var v reflect.Value + if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil { + in[i] = v + callSlice = true + break + } + } + + in[i] = self.convertCallParameter(a, t) + } + + var out []reflect.Value + if callSlice { + out = value.CallSlice(in) + } else { + out = value.Call(in) + } + + switch len(out) { + case 0: + return Value{} + case 1: + return self.toValue(out[0].Interface()) + default: + s := make([]interface{}, len(out)) + for i, v := range out { + s[i] = self.toValue(v.Interface()) + } + + return self.toValue(s) + } + })) + } + } + } + + return toValue(value) +} + +func (runtime *_runtime) newGoSlice(value reflect.Value) *_object { + self := runtime.newGoSliceObject(value) + self.prototype = runtime.global.ArrayPrototype + return self +} + +func (runtime *_runtime) newGoArray(value reflect.Value) *_object { + self := runtime.newGoArrayObject(value) + self.prototype = runtime.global.ArrayPrototype + return self +} + +func (runtime *_runtime) parse(filename string, src, sm interface{}) (*ast.Program, error) { + return parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) +} + +func (runtime *_runtime) cmpl_parse(filename string, src, sm interface{}) (*_nodeProgram, error) { + program, err := parser.ParseFileWithSourceMap(nil, filename, src, sm, 0) + if err != nil { + return nil, err + } + + return cmpl_parse(program), nil +} + +func (self *_runtime) parseSource(src, sm interface{}) (*_nodeProgram, *ast.Program, error) { + switch src := src.(type) { + case *ast.Program: + return nil, src, nil + case *Script: + return src.program, nil, nil + } + + program, err := self.parse("", src, sm) + + return nil, program, err +} + +func (self *_runtime) cmpl_runOrEval(src, sm interface{}, eval bool) (Value, error) { + result := Value{} + cmpl_program, program, err := self.parseSource(src, sm) + if err != nil { + return result, err + } + if cmpl_program == nil { + cmpl_program = cmpl_parse(program) + } + err = catchPanic(func() { + result = self.cmpl_evaluate_nodeProgram(cmpl_program, eval) + }) + switch result.kind { + case valueEmpty: + result = Value{} + case valueReference: + result = result.resolve() + } + return result, err +} + +func (self *_runtime) cmpl_run(src, sm interface{}) (Value, error) { + return self.cmpl_runOrEval(src, sm, false) +} + +func (self *_runtime) cmpl_eval(src, sm interface{}) (Value, error) { + return self.cmpl_runOrEval(src, sm, true) +} + +func (self *_runtime) parseThrow(err error) { + if err == nil { + return + } + switch err := err.(type) { + case parser.ErrorList: + { + err := err[0] + if err.Message == "Invalid left-hand side in assignment" { + panic(self.panicReferenceError(err.Message)) + } + panic(self.panicSyntaxError(err.Message)) + } + } + panic(self.panicSyntaxError(err.Error())) +} + +func (self *_runtime) cmpl_parseOrThrow(src, sm interface{}) *_nodeProgram { + program, err := self.cmpl_parse("", src, sm) + self.parseThrow(err) // Will panic/throw appropriately + return program +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/scope.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/scope.go new file mode 100644 index 0000000..465e6b9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/scope.go @@ -0,0 +1,35 @@ +package otto + +// _scope: +// entryFile +// entryIdx +// top? +// outer => nil + +// _stash: +// lexical +// variable +// +// _thisStash (ObjectEnvironment) +// _fnStash +// _dclStash + +// An ECMA-262 ExecutionContext +type _scope struct { + lexical _stash + variable _stash + this *_object + eval bool // Replace this with kind? + outer *_scope + depth int + + frame _frame +} + +func newScope(lexical _stash, variable _stash, this *_object) *_scope { + return &_scope{ + lexical: lexical, + variable: variable, + this: this, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/script.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/script.go new file mode 100644 index 0000000..2ae890e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/script.go @@ -0,0 +1,119 @@ +package otto + +import ( + "bytes" + "encoding/gob" + "errors" +) + +var ErrVersion = errors.New("version mismatch") + +var scriptVersion = "2014-04-13/1" + +// Script is a handle for some (reusable) JavaScript. +// Passing a Script value to a run method will evaluate the JavaScript. +// +type Script struct { + version string + program *_nodeProgram + filename string + src string +} + +// Compile will parse the given source and return a Script value or nil and +// an error if there was a problem during compilation. +// +// script, err := vm.Compile("", `var abc; if (!abc) abc = 0; abc += 2; abc;`) +// vm.Run(script) +// +func (self *Otto) Compile(filename string, src interface{}) (*Script, error) { + return self.CompileWithSourceMap(filename, src, nil) +} + +// CompileWithSourceMap does the same thing as Compile, but with the obvious +// difference of applying a source map. +func (self *Otto) CompileWithSourceMap(filename string, src, sm interface{}) (*Script, error) { + program, err := self.runtime.parse(filename, src, sm) + if err != nil { + return nil, err + } + + cmpl_program := cmpl_parse(program) + + script := &Script{ + version: scriptVersion, + program: cmpl_program, + filename: filename, + src: program.File.Source(), + } + + return script, nil +} + +func (self *Script) String() string { + return "// " + self.filename + "\n" + self.src +} + +// MarshalBinary will marshal a script into a binary form. A marshalled script +// that is later unmarshalled can be executed on the same version of the otto runtime. +// +// The binary format can change at any time and should be considered unspecified and opaque. +// +func (self *Script) marshalBinary() ([]byte, error) { + var bfr bytes.Buffer + encoder := gob.NewEncoder(&bfr) + err := encoder.Encode(self.version) + if err != nil { + return nil, err + } + err = encoder.Encode(self.program) + if err != nil { + return nil, err + } + err = encoder.Encode(self.filename) + if err != nil { + return nil, err + } + err = encoder.Encode(self.src) + if err != nil { + return nil, err + } + return bfr.Bytes(), nil +} + +// UnmarshalBinary will vivify a marshalled script into something usable. If the script was +// originally marshalled on a different version of the otto runtime, then this method +// will return an error. +// +// The binary format can change at any time and should be considered unspecified and opaque. +// +func (self *Script) unmarshalBinary(data []byte) error { + decoder := gob.NewDecoder(bytes.NewReader(data)) + err := decoder.Decode(&self.version) + if err != nil { + goto error + } + if self.version != scriptVersion { + err = ErrVersion + goto error + } + err = decoder.Decode(&self.program) + if err != nil { + goto error + } + err = decoder.Decode(&self.filename) + if err != nil { + goto error + } + err = decoder.Decode(&self.src) + if err != nil { + goto error + } + return nil +error: + self.version = "" + self.program = nil + self.filename = "" + self.src = "" + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/stash.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/stash.go new file mode 100644 index 0000000..0d3ffa5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/stash.go @@ -0,0 +1,296 @@ +package otto + +import ( + "fmt" +) + +// ====== +// _stash +// ====== + +type _stash interface { + hasBinding(string) bool // + createBinding(string, bool, Value) // CreateMutableBinding + setBinding(string, Value, bool) // SetMutableBinding + getBinding(string, bool) Value // GetBindingValue + deleteBinding(string) bool // + setValue(string, Value, bool) // createBinding + setBinding + + outer() _stash + runtime() *_runtime + + newReference(string, bool, _at) _reference + + clone(clone *_clone) _stash +} + +// ========== +// _objectStash +// ========== + +type _objectStash struct { + _runtime *_runtime + _outer _stash + object *_object +} + +func (self *_objectStash) runtime() *_runtime { + return self._runtime +} + +func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash { + if object == nil { + object = runtime.newBaseObject() + object.class = "environment" + } + return &_objectStash{ + _runtime: runtime, + _outer: outer, + object: object, + } +} + +func (in *_objectStash) clone(clone *_clone) _stash { + out, exists := clone.objectStash(in) + if exists { + return out + } + *out = _objectStash{ + clone.runtime, + clone.stash(in._outer), + clone.object(in.object), + } + return out +} + +func (self *_objectStash) hasBinding(name string) bool { + return self.object.hasProperty(name) +} + +func (self *_objectStash) createBinding(name string, deletable bool, value Value) { + if self.object.hasProperty(name) { + panic(hereBeDragons()) + } + mode := _propertyMode(0111) + if !deletable { + mode = _propertyMode(0110) + } + // TODO False? + self.object.defineProperty(name, value, mode, false) +} + +func (self *_objectStash) setBinding(name string, value Value, strict bool) { + self.object.put(name, value, strict) +} + +func (self *_objectStash) setValue(name string, value Value, throw bool) { + if !self.hasBinding(name) { + self.createBinding(name, true, value) // Configurable by default + } else { + self.setBinding(name, value, throw) + } +} + +func (self *_objectStash) getBinding(name string, throw bool) Value { + if self.object.hasProperty(name) { + return self.object.get(name) + } + if throw { // strict? + panic(self._runtime.panicReferenceError("Not Defined", name)) + } + return Value{} +} + +func (self *_objectStash) deleteBinding(name string) bool { + return self.object.delete(name, false) +} + +func (self *_objectStash) outer() _stash { + return self._outer +} + +func (self *_objectStash) newReference(name string, strict bool, at _at) _reference { + return newPropertyReference(self._runtime, self.object, name, strict, at) +} + +// ========= +// _dclStash +// ========= + +type _dclStash struct { + _runtime *_runtime + _outer _stash + property map[string]_dclProperty +} + +type _dclProperty struct { + value Value + mutable bool + deletable bool + readable bool +} + +func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash { + return &_dclStash{ + _runtime: runtime, + _outer: outer, + property: map[string]_dclProperty{}, + } +} + +func (in *_dclStash) clone(clone *_clone) _stash { + out, exists := clone.dclStash(in) + if exists { + return out + } + property := make(map[string]_dclProperty, len(in.property)) + for index, value := range in.property { + property[index] = clone.dclProperty(value) + } + *out = _dclStash{ + clone.runtime, + clone.stash(in._outer), + property, + } + return out +} + +func (self *_dclStash) hasBinding(name string) bool { + _, exists := self.property[name] + return exists +} + +func (self *_dclStash) runtime() *_runtime { + return self._runtime +} + +func (self *_dclStash) createBinding(name string, deletable bool, value Value) { + _, exists := self.property[name] + if exists { + panic(fmt.Errorf("createBinding: %s: already exists", name)) + } + self.property[name] = _dclProperty{ + value: value, + mutable: true, + deletable: deletable, + readable: false, + } +} + +func (self *_dclStash) setBinding(name string, value Value, strict bool) { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("setBinding: %s: missing", name)) + } + if property.mutable { + property.value = value + self.property[name] = property + } else { + self._runtime.typeErrorResult(strict) + } +} + +func (self *_dclStash) setValue(name string, value Value, throw bool) { + if !self.hasBinding(name) { + self.createBinding(name, false, value) // NOT deletable by default + } else { + self.setBinding(name, value, throw) + } +} + +// FIXME This is called a __lot__ +func (self *_dclStash) getBinding(name string, throw bool) Value { + property, exists := self.property[name] + if !exists { + panic(fmt.Errorf("getBinding: %s: missing", name)) + } + if !property.mutable && !property.readable { + if throw { // strict? + panic(self._runtime.panicTypeError()) + } + return Value{} + } + return property.value +} + +func (self *_dclStash) deleteBinding(name string) bool { + property, exists := self.property[name] + if !exists { + return true + } + if !property.deletable { + return false + } + delete(self.property, name) + return true +} + +func (self *_dclStash) outer() _stash { + return self._outer +} + +func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference { + return &_stashReference{ + name: name, + base: self, + } +} + +// ======== +// _fnStash +// ======== + +type _fnStash struct { + _dclStash + arguments *_object + indexOfArgumentName map[string]string +} + +func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash { + return &_fnStash{ + _dclStash: _dclStash{ + _runtime: runtime, + _outer: outer, + property: map[string]_dclProperty{}, + }, + } +} + +func (in *_fnStash) clone(clone *_clone) _stash { + out, exists := clone.fnStash(in) + if exists { + return out + } + dclStash := in._dclStash.clone(clone).(*_dclStash) + index := make(map[string]string, len(in.indexOfArgumentName)) + for name, value := range in.indexOfArgumentName { + index[name] = value + } + *out = _fnStash{ + _dclStash: *dclStash, + arguments: clone.object(in.arguments), + indexOfArgumentName: index, + } + return out +} + +func getStashProperties(stash _stash) (keys []string) { + switch vars := stash.(type) { + case *_dclStash: + for k := range vars.property { + keys = append(keys, k) + } + case *_fnStash: + for k := range vars.property { + keys = append(keys, k) + } + case *_objectStash: + for k := range vars.object.property { + keys = append(keys, k) + } + default: + panic("unknown stash type") + } + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/Makefile b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/Makefile new file mode 100644 index 0000000..1e85c73 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/Makefile @@ -0,0 +1,2 @@ +token_const.go: tokenfmt + ./$^ | gofmt > $@ diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/README.markdown b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/README.markdown new file mode 100644 index 0000000..ff3b161 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/README.markdown @@ -0,0 +1,171 @@ +# token +-- + import "github.com/robertkrimen/otto/token" + +Package token defines constants representing the lexical tokens of JavaScript +(ECMA5). + +## Usage + +```go +const ( + ILLEGAL + EOF + COMMENT + KEYWORD + + STRING + BOOLEAN + NULL + NUMBER + IDENTIFIER + + PLUS // + + MINUS // - + MULTIPLY // * + SLASH // / + REMAINDER // % + + AND // & + OR // | + EXCLUSIVE_OR // ^ + SHIFT_LEFT // << + SHIFT_RIGHT // >> + UNSIGNED_SHIFT_RIGHT // >>> + AND_NOT // &^ + + ADD_ASSIGN // += + SUBTRACT_ASSIGN // -= + MULTIPLY_ASSIGN // *= + QUOTIENT_ASSIGN // /= + REMAINDER_ASSIGN // %= + + AND_ASSIGN // &= + OR_ASSIGN // |= + EXCLUSIVE_OR_ASSIGN // ^= + SHIFT_LEFT_ASSIGN // <<= + SHIFT_RIGHT_ASSIGN // >>= + UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= + AND_NOT_ASSIGN // &^= + + LOGICAL_AND // && + LOGICAL_OR // || + INCREMENT // ++ + DECREMENT // -- + + EQUAL // == + STRICT_EQUAL // === + LESS // < + GREATER // > + ASSIGN // = + NOT // ! + + BITWISE_NOT // ~ + + NOT_EQUAL // != + STRICT_NOT_EQUAL // !== + LESS_OR_EQUAL // <= + GREATER_OR_EQUAL // >= + + LEFT_PARENTHESIS // ( + LEFT_BRACKET // [ + LEFT_BRACE // { + COMMA // , + PERIOD // . + + RIGHT_PARENTHESIS // ) + RIGHT_BRACKET // ] + RIGHT_BRACE // } + SEMICOLON // ; + COLON // : + QUESTION_MARK // ? + + IF + IN + DO + + VAR + FOR + NEW + TRY + + THIS + ELSE + CASE + VOID + WITH + + WHILE + BREAK + CATCH + THROW + + RETURN + TYPEOF + DELETE + SWITCH + + DEFAULT + FINALLY + + FUNCTION + CONTINUE + DEBUGGER + + INSTANCEOF +) +``` + +#### type Token + +```go +type Token int +``` + +Token is the set of lexical tokens in JavaScript (ECMA5). + +#### func IsKeyword + +```go +func IsKeyword(literal string) (Token, bool) +``` +IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if +the literal is a future keyword (const, let, class, super, ...), or 0 if the +literal is not a keyword. + +If the literal is a keyword, IsKeyword returns a second value indicating if the +literal is considered a future keyword in strict-mode only. + +7.6.1.2 Future Reserved Words: + + const + class + enum + export + extends + import + super + +7.6.1.2 Future Reserved Words (strict): + + implements + interface + let + package + private + protected + public + static + +#### func (Token) String + +```go +func (tkn Token) String() string +``` +String returns the string corresponding to the token. For operators, delimiters, +and keywords the string is the actual token string (e.g., for the token PLUS, +the String() is "+"). For all other tokens the string corresponds to the token +name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). + +-- +**godocdown** http://github.com/robertkrimen/godocdown diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token.go new file mode 100644 index 0000000..0e941ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token.go @@ -0,0 +1,116 @@ +// Package token defines constants representing the lexical tokens of JavaScript (ECMA5). +package token + +import ( + "strconv" +) + +// Token is the set of lexical tokens in JavaScript (ECMA5). +type Token int + +// String returns the string corresponding to the token. +// For operators, delimiters, and keywords the string is the actual +// token string (e.g., for the token PLUS, the String() is +// "+"). For all other tokens the string corresponds to the token +// name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). +// +func (tkn Token) String() string { + if 0 == tkn { + return "UNKNOWN" + } + if tkn < Token(len(token2string)) { + return token2string[tkn] + } + return "token(" + strconv.Itoa(int(tkn)) + ")" +} + +// This is not used for anything +func (tkn Token) precedence(in bool) int { + + switch tkn { + case LOGICAL_OR: + return 1 + + case LOGICAL_AND: + return 2 + + case OR, OR_ASSIGN: + return 3 + + case EXCLUSIVE_OR: + return 4 + + case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN: + return 5 + + case EQUAL, + NOT_EQUAL, + STRICT_EQUAL, + STRICT_NOT_EQUAL: + return 6 + + case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF: + return 7 + + case IN: + if in { + return 7 + } + return 0 + + case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT: + fallthrough + case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN: + return 8 + + case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN: + return 9 + + case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN: + return 11 + } + return 0 +} + +type _keyword struct { + token Token + futureKeyword bool + strict bool +} + +// IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token +// if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword. +// +// If the literal is a keyword, IsKeyword returns a second value indicating if the literal +// is considered a future keyword in strict-mode only. +// +// 7.6.1.2 Future Reserved Words: +// +// const +// class +// enum +// export +// extends +// import +// super +// +// 7.6.1.2 Future Reserved Words (strict): +// +// implements +// interface +// let +// package +// private +// protected +// public +// static +// +func IsKeyword(literal string) (Token, bool) { + if keyword, exists := keywordTable[literal]; exists { + if keyword.futureKeyword { + return KEYWORD, keyword.strict + } + return keyword.token, false + } + return 0, false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token_const.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token_const.go new file mode 100644 index 0000000..b1d83c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/token_const.go @@ -0,0 +1,349 @@ +package token + +const ( + _ Token = iota + + ILLEGAL + EOF + COMMENT + KEYWORD + + STRING + BOOLEAN + NULL + NUMBER + IDENTIFIER + + PLUS // + + MINUS // - + MULTIPLY // * + SLASH // / + REMAINDER // % + + AND // & + OR // | + EXCLUSIVE_OR // ^ + SHIFT_LEFT // << + SHIFT_RIGHT // >> + UNSIGNED_SHIFT_RIGHT // >>> + AND_NOT // &^ + + ADD_ASSIGN // += + SUBTRACT_ASSIGN // -= + MULTIPLY_ASSIGN // *= + QUOTIENT_ASSIGN // /= + REMAINDER_ASSIGN // %= + + AND_ASSIGN // &= + OR_ASSIGN // |= + EXCLUSIVE_OR_ASSIGN // ^= + SHIFT_LEFT_ASSIGN // <<= + SHIFT_RIGHT_ASSIGN // >>= + UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= + AND_NOT_ASSIGN // &^= + + LOGICAL_AND // && + LOGICAL_OR // || + INCREMENT // ++ + DECREMENT // -- + + EQUAL // == + STRICT_EQUAL // === + LESS // < + GREATER // > + ASSIGN // = + NOT // ! + + BITWISE_NOT // ~ + + NOT_EQUAL // != + STRICT_NOT_EQUAL // !== + LESS_OR_EQUAL // <= + GREATER_OR_EQUAL // >= + + LEFT_PARENTHESIS // ( + LEFT_BRACKET // [ + LEFT_BRACE // { + COMMA // , + PERIOD // . + + RIGHT_PARENTHESIS // ) + RIGHT_BRACKET // ] + RIGHT_BRACE // } + SEMICOLON // ; + COLON // : + QUESTION_MARK // ? + + firstKeyword + IF + IN + DO + + VAR + FOR + NEW + TRY + + THIS + ELSE + CASE + VOID + WITH + + WHILE + BREAK + CATCH + THROW + + RETURN + TYPEOF + DELETE + SWITCH + + DEFAULT + FINALLY + + FUNCTION + CONTINUE + DEBUGGER + + INSTANCEOF + lastKeyword +) + +var token2string = [...]string{ + ILLEGAL: "ILLEGAL", + EOF: "EOF", + COMMENT: "COMMENT", + KEYWORD: "KEYWORD", + STRING: "STRING", + BOOLEAN: "BOOLEAN", + NULL: "NULL", + NUMBER: "NUMBER", + IDENTIFIER: "IDENTIFIER", + PLUS: "+", + MINUS: "-", + MULTIPLY: "*", + SLASH: "/", + REMAINDER: "%", + AND: "&", + OR: "|", + EXCLUSIVE_OR: "^", + SHIFT_LEFT: "<<", + SHIFT_RIGHT: ">>", + UNSIGNED_SHIFT_RIGHT: ">>>", + AND_NOT: "&^", + ADD_ASSIGN: "+=", + SUBTRACT_ASSIGN: "-=", + MULTIPLY_ASSIGN: "*=", + QUOTIENT_ASSIGN: "/=", + REMAINDER_ASSIGN: "%=", + AND_ASSIGN: "&=", + OR_ASSIGN: "|=", + EXCLUSIVE_OR_ASSIGN: "^=", + SHIFT_LEFT_ASSIGN: "<<=", + SHIFT_RIGHT_ASSIGN: ">>=", + UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=", + AND_NOT_ASSIGN: "&^=", + LOGICAL_AND: "&&", + LOGICAL_OR: "||", + INCREMENT: "++", + DECREMENT: "--", + EQUAL: "==", + STRICT_EQUAL: "===", + LESS: "<", + GREATER: ">", + ASSIGN: "=", + NOT: "!", + BITWISE_NOT: "~", + NOT_EQUAL: "!=", + STRICT_NOT_EQUAL: "!==", + LESS_OR_EQUAL: "<=", + GREATER_OR_EQUAL: ">=", + LEFT_PARENTHESIS: "(", + LEFT_BRACKET: "[", + LEFT_BRACE: "{", + COMMA: ",", + PERIOD: ".", + RIGHT_PARENTHESIS: ")", + RIGHT_BRACKET: "]", + RIGHT_BRACE: "}", + SEMICOLON: ";", + COLON: ":", + QUESTION_MARK: "?", + IF: "if", + IN: "in", + DO: "do", + VAR: "var", + FOR: "for", + NEW: "new", + TRY: "try", + THIS: "this", + ELSE: "else", + CASE: "case", + VOID: "void", + WITH: "with", + WHILE: "while", + BREAK: "break", + CATCH: "catch", + THROW: "throw", + RETURN: "return", + TYPEOF: "typeof", + DELETE: "delete", + SWITCH: "switch", + DEFAULT: "default", + FINALLY: "finally", + FUNCTION: "function", + CONTINUE: "continue", + DEBUGGER: "debugger", + INSTANCEOF: "instanceof", +} + +var keywordTable = map[string]_keyword{ + "if": _keyword{ + token: IF, + }, + "in": _keyword{ + token: IN, + }, + "do": _keyword{ + token: DO, + }, + "var": _keyword{ + token: VAR, + }, + "for": _keyword{ + token: FOR, + }, + "new": _keyword{ + token: NEW, + }, + "try": _keyword{ + token: TRY, + }, + "this": _keyword{ + token: THIS, + }, + "else": _keyword{ + token: ELSE, + }, + "case": _keyword{ + token: CASE, + }, + "void": _keyword{ + token: VOID, + }, + "with": _keyword{ + token: WITH, + }, + "while": _keyword{ + token: WHILE, + }, + "break": _keyword{ + token: BREAK, + }, + "catch": _keyword{ + token: CATCH, + }, + "throw": _keyword{ + token: THROW, + }, + "return": _keyword{ + token: RETURN, + }, + "typeof": _keyword{ + token: TYPEOF, + }, + "delete": _keyword{ + token: DELETE, + }, + "switch": _keyword{ + token: SWITCH, + }, + "default": _keyword{ + token: DEFAULT, + }, + "finally": _keyword{ + token: FINALLY, + }, + "function": _keyword{ + token: FUNCTION, + }, + "continue": _keyword{ + token: CONTINUE, + }, + "debugger": _keyword{ + token: DEBUGGER, + }, + "instanceof": _keyword{ + token: INSTANCEOF, + }, + "const": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "class": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "enum": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "export": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "extends": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "import": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "super": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, + "implements": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "interface": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "let": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "package": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "private": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "protected": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "public": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, + "static": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/tokenfmt b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/tokenfmt new file mode 100755 index 0000000..63dd5d9 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/token/tokenfmt @@ -0,0 +1,222 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +my (%token, @order, @keywords); + +{ + my $keywords; + my @const; + push @const, <<_END_; +package token + +const( + _ Token = iota +_END_ + + for (split m/\n/, <<_END_) { +ILLEGAL +EOF +COMMENT +KEYWORD + +STRING +BOOLEAN +NULL +NUMBER +IDENTIFIER + +PLUS + +MINUS - +MULTIPLY * +SLASH / +REMAINDER % + +AND & +OR | +EXCLUSIVE_OR ^ +SHIFT_LEFT << +SHIFT_RIGHT >> +UNSIGNED_SHIFT_RIGHT >>> +AND_NOT &^ + +ADD_ASSIGN += +SUBTRACT_ASSIGN -= +MULTIPLY_ASSIGN *= +QUOTIENT_ASSIGN /= +REMAINDER_ASSIGN %= + +AND_ASSIGN &= +OR_ASSIGN |= +EXCLUSIVE_OR_ASSIGN ^= +SHIFT_LEFT_ASSIGN <<= +SHIFT_RIGHT_ASSIGN >>= +UNSIGNED_SHIFT_RIGHT_ASSIGN >>>= +AND_NOT_ASSIGN &^= + +LOGICAL_AND && +LOGICAL_OR || +INCREMENT ++ +DECREMENT -- + +EQUAL == +STRICT_EQUAL === +LESS < +GREATER > +ASSIGN = +NOT ! + +BITWISE_NOT ~ + +NOT_EQUAL != +STRICT_NOT_EQUAL !== +LESS_OR_EQUAL <= +GREATER_OR_EQUAL <= + +LEFT_PARENTHESIS ( +LEFT_BRACKET [ +LEFT_BRACE { +COMMA , +PERIOD . + +RIGHT_PARENTHESIS ) +RIGHT_BRACKET ] +RIGHT_BRACE } +SEMICOLON ; +COLON : +QUESTION_MARK ? + +firstKeyword +IF +IN +DO + +VAR +FOR +NEW +TRY + +THIS +ELSE +CASE +VOID +WITH + +WHILE +BREAK +CATCH +THROW + +RETURN +TYPEOF +DELETE +SWITCH + +DEFAULT +FINALLY + +FUNCTION +CONTINUE +DEBUGGER + +INSTANCEOF +lastKeyword +_END_ + chomp; + + next if m/^\s*#/; + + my ($name, $symbol) = m/(\w+)\s*(\S+)?/; + + if (defined $symbol) { + push @order, $name; + push @const, "$name // $symbol"; + $token{$name} = $symbol; + } elsif (defined $name) { + $keywords ||= $name eq 'firstKeyword'; + + push @const, $name; + #$const[-1] .= " Token = iota" if 2 == @const; + if ($name =~ m/^([A-Z]+)/) { + push @keywords, $name if $keywords; + push @order, $name; + if ($token{SEMICOLON}) { + $token{$name} = lc $1; + } else { + $token{$name} = $name; + } + } + } else { + push @const, ""; + } + + } + push @const, ")"; + print join "\n", @const, ""; +} + +{ + print <<_END_; + +var token2string = [...]string{ +_END_ + for my $name (@order) { + print "$name: \"$token{$name}\",\n"; + } + print <<_END_; +} +_END_ + + print <<_END_; + +var keywordTable = map[string]_keyword{ +_END_ + for my $name (@keywords) { + print <<_END_ + "@{[ lc $name ]}": _keyword{ + token: $name, + }, +_END_ + } + + for my $name (qw/ + const + class + enum + export + extends + import + super + /) { + print <<_END_ + "$name": _keyword{ + token: KEYWORD, + futureKeyword: true, + }, +_END_ + } + + for my $name (qw/ + implements + interface + let + package + private + protected + public + static + /) { + print <<_END_ + "$name": _keyword{ + token: KEYWORD, + futureKeyword: true, + strict: true, + }, +_END_ + } + + print <<_END_; +} +_END_ +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_arguments.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_arguments.go new file mode 100644 index 0000000..841d758 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_arguments.go @@ -0,0 +1,106 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newArgumentsObject(indexOfParameterName []string, stash _stash, length int) *_object { + self := runtime.newClassObject("Arguments") + + for index, _ := range indexOfParameterName { + name := strconv.FormatInt(int64(index), 10) + objectDefineOwnProperty(self, name, _property{Value{}, 0111}, false) + } + + self.objectClass = _classArguments + self.value = _argumentsObject{ + indexOfParameterName: indexOfParameterName, + stash: stash, + } + + self.prototype = runtime.global.ObjectPrototype + + self.defineProperty("length", toValue_int(length), 0101, false) + + return self +} + +type _argumentsObject struct { + indexOfParameterName []string + // function(abc, def, ghi) + // indexOfParameterName[0] = "abc" + // indexOfParameterName[1] = "def" + // indexOfParameterName[2] = "ghi" + // ... + stash _stash +} + +func (in _argumentsObject) clone(clone *_clone) _argumentsObject { + indexOfParameterName := make([]string, len(in.indexOfParameterName)) + copy(indexOfParameterName, in.indexOfParameterName) + return _argumentsObject{ + indexOfParameterName, + clone.stash(in.stash), + } +} + +func (self _argumentsObject) get(name string) (Value, bool) { + index := stringToArrayIndex(name) + if index >= 0 && index < int64(len(self.indexOfParameterName)) { + name := self.indexOfParameterName[index] + if name == "" { + return Value{}, false + } + return self.stash.getBinding(name, false), true + } + return Value{}, false +} + +func (self _argumentsObject) put(name string, value Value) { + index := stringToArrayIndex(name) + name = self.indexOfParameterName[index] + self.stash.setBinding(name, value, false) +} + +func (self _argumentsObject) delete(name string) { + index := stringToArrayIndex(name) + self.indexOfParameterName[index] = "" +} + +func argumentsGet(self *_object, name string) Value { + if value, exists := self.value.(_argumentsObject).get(name); exists { + return value + } + return objectGet(self, name) +} + +func argumentsGetOwnProperty(self *_object, name string) *_property { + property := objectGetOwnProperty(self, name) + if value, exists := self.value.(_argumentsObject).get(name); exists { + property.value = value + } + return property +} + +func argumentsDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if _, exists := self.value.(_argumentsObject).get(name); exists { + if !objectDefineOwnProperty(self, name, descriptor, false) { + return self.runtime.typeErrorResult(throw) + } + if value, valid := descriptor.value.(Value); valid { + self.value.(_argumentsObject).put(name, value) + } + return true + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func argumentsDelete(self *_object, name string, throw bool) bool { + if !objectDelete(self, name, throw) { + return false + } + if _, exists := self.value.(_argumentsObject).get(name); exists { + self.value.(_argumentsObject).delete(name) + } + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_array.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_array.go new file mode 100644 index 0000000..c8a974b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_array.go @@ -0,0 +1,109 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newArrayObject(length uint32) *_object { + self := runtime.newObject() + self.class = "Array" + self.defineProperty("length", toValue_uint32(length), 0100, false) + self.objectClass = _classArray + return self +} + +func isArray(object *_object) bool { + return object != nil && (object.class == "Array" || object.class == "GoArray") +} + +func objectLength(object *_object) uint32 { + if object == nil { + return 0 + } + switch object.class { + case "Array": + return object.get("length").value.(uint32) + case "String": + return uint32(object.get("length").value.(int)) + case "GoArray": + return uint32(object.get("length").value.(int)) + } + return 0 +} + +func arrayUint32(rt *_runtime, value Value) uint32 { + nm := value.number() + if nm.kind != numberInteger || !isUint32(nm.int64) { + // FIXME + panic(rt.panicRangeError()) + } + return uint32(nm.int64) +} + +func arrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + lengthProperty := self.getOwnProperty("length") + lengthValue, valid := lengthProperty.value.(Value) + if !valid { + panic("Array.length != Value{}") + } + length := lengthValue.value.(uint32) + if name == "length" { + if descriptor.value == nil { + return objectDefineOwnProperty(self, name, descriptor, throw) + } + newLengthValue, isValue := descriptor.value.(Value) + if !isValue { + panic(self.runtime.panicTypeError()) + } + newLength := arrayUint32(self.runtime, newLengthValue) + descriptor.value = toValue_uint32(newLength) + if newLength > length { + return objectDefineOwnProperty(self, name, descriptor, throw) + } + if !lengthProperty.writable() { + goto Reject + } + newWritable := true + if descriptor.mode&0700 == 0 { + // If writable is off + newWritable = false + descriptor.mode |= 0100 + } + if !objectDefineOwnProperty(self, name, descriptor, throw) { + return false + } + for newLength < length { + length-- + if !self.delete(strconv.FormatInt(int64(length), 10), false) { + descriptor.value = toValue_uint32(length + 1) + if !newWritable { + descriptor.mode &= 0077 + } + objectDefineOwnProperty(self, name, descriptor, false) + goto Reject + } + } + if !newWritable { + descriptor.mode &= 0077 + objectDefineOwnProperty(self, name, descriptor, false) + } + } else if index := stringToArrayIndex(name); index >= 0 { + if index >= int64(length) && !lengthProperty.writable() { + goto Reject + } + if !objectDefineOwnProperty(self, strconv.FormatInt(index, 10), descriptor, false) { + goto Reject + } + if index >= int64(length) { + lengthProperty.value = toValue_uint32(uint32(index + 1)) + objectDefineOwnProperty(self, "length", *lengthProperty, false) + return true + } + } + return objectDefineOwnProperty(self, name, descriptor, throw) +Reject: + if throw { + panic(self.runtime.panicTypeError()) + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_boolean.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_boolean.go new file mode 100644 index 0000000..afc45c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_boolean.go @@ -0,0 +1,13 @@ +package otto + +import ( + "strconv" +) + +func (runtime *_runtime) newBooleanObject(value Value) *_object { + return runtime.newPrimitiveObject("Boolean", toValue_bool(value.bool())) +} + +func booleanToString(value bool) string { + return strconv.FormatBool(value) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_date.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_date.go new file mode 100644 index 0000000..7079e64 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_date.go @@ -0,0 +1,299 @@ +package otto + +import ( + "fmt" + "math" + "regexp" + Time "time" +) + +type _dateObject struct { + time Time.Time // Time from the "time" package, a cached version of time + epoch int64 + value Value + isNaN bool +} + +var ( + invalidDateObject = _dateObject{ + time: Time.Time{}, + epoch: -1, + value: NaNValue(), + isNaN: true, + } +) + +type _ecmaTime struct { + year int + month int + day int + hour int + minute int + second int + millisecond int + location *Time.Location // Basically, either local or UTC +} + +func ecmaTime(goTime Time.Time) _ecmaTime { + return _ecmaTime{ + goTime.Year(), + dateFromGoMonth(goTime.Month()), + goTime.Day(), + goTime.Hour(), + goTime.Minute(), + goTime.Second(), + goTime.Nanosecond() / (100 * 100 * 100), + goTime.Location(), + } +} + +func (self *_ecmaTime) goTime() Time.Time { + return Time.Date( + self.year, + dateToGoMonth(self.month), + self.day, + self.hour, + self.minute, + self.second, + self.millisecond*(100*100*100), + self.location, + ) +} + +func (self *_dateObject) Time() Time.Time { + return self.time +} + +func (self *_dateObject) Epoch() int64 { + return self.epoch +} + +func (self *_dateObject) Value() Value { + return self.value +} + +// FIXME A date should only be in the range of -100,000,000 to +100,000,000 (1970): 15.9.1.1 +func (self *_dateObject) SetNaN() { + self.time = Time.Time{} + self.epoch = -1 + self.value = NaNValue() + self.isNaN = true +} + +func (self *_dateObject) SetTime(time Time.Time) { + self.Set(timeToEpoch(time)) +} + +func epoch2dateObject(epoch float64) _dateObject { + date := _dateObject{} + date.Set(epoch) + return date +} + +func (self *_dateObject) Set(epoch float64) { + // epoch + self.epoch = epochToInteger(epoch) + + // time + time, err := epochToTime(epoch) + self.time = time // Is either a valid time, or the zero-value for time.Time + + // value & isNaN + if err != nil { + self.isNaN = true + self.epoch = -1 + self.value = NaNValue() + } else { + self.value = toValue_int64(self.epoch) + } +} + +func epochToInteger(value float64) int64 { + if value > 0 { + return int64(math.Floor(value)) + } + return int64(math.Ceil(value)) +} + +func epochToTime(value float64) (time Time.Time, err error) { + epochWithMilli := value + if math.IsNaN(epochWithMilli) || math.IsInf(epochWithMilli, 0) { + err = fmt.Errorf("Invalid time %v", value) + return + } + + epoch := int64(epochWithMilli / 1000) + milli := int64(epochWithMilli) % 1000 + + time = Time.Unix(int64(epoch), milli*1000000).UTC() + return +} + +func timeToEpoch(time Time.Time) float64 { + return float64(time.UnixNano() / (1000 * 1000)) +} + +func (runtime *_runtime) newDateObject(epoch float64) *_object { + self := runtime.newObject() + self.class = "Date" + + // FIXME This is ugly... + date := _dateObject{} + date.Set(epoch) + self.value = date + return self +} + +func (self *_object) dateValue() _dateObject { + value, _ := self.value.(_dateObject) + return value +} + +func dateObjectOf(rt *_runtime, _dateObject *_object) _dateObject { + if _dateObject == nil || _dateObject.class != "Date" { + panic(rt.panicTypeError()) + } + return _dateObject.dateValue() +} + +// JavaScript is 0-based, Go is 1-based (15.9.1.4) +func dateToGoMonth(month int) Time.Month { + return Time.Month(month + 1) +} + +func dateFromGoMonth(month Time.Month) int { + return int(month) - 1 +} + +// Both JavaScript & Go are 0-based (Sunday == 0) +func dateToGoDay(day int) Time.Weekday { + return Time.Weekday(day) +} + +func dateFromGoDay(day Time.Weekday) int { + return int(day) +} + +func newDateTime(argumentList []Value, location *Time.Location) (epoch float64) { + + pick := func(index int, default_ float64) (float64, bool) { + if index >= len(argumentList) { + return default_, false + } + value := argumentList[index].float64() + if math.IsNaN(value) || math.IsInf(value, 0) { + return 0, true + } + return value, false + } + + if len(argumentList) >= 2 { // 2-argument, 3-argument, ... + var year, month, day, hour, minute, second, millisecond float64 + var invalid bool + if year, invalid = pick(0, 1900.0); invalid { + goto INVALID + } + if month, invalid = pick(1, 0.0); invalid { + goto INVALID + } + if day, invalid = pick(2, 1.0); invalid { + goto INVALID + } + if hour, invalid = pick(3, 0.0); invalid { + goto INVALID + } + if minute, invalid = pick(4, 0.0); invalid { + goto INVALID + } + if second, invalid = pick(5, 0.0); invalid { + goto INVALID + } + if millisecond, invalid = pick(6, 0.0); invalid { + goto INVALID + } + + if year >= 0 && year <= 99 { + year += 1900 + } + + time := Time.Date(int(year), dateToGoMonth(int(month)), int(day), int(hour), int(minute), int(second), int(millisecond)*1000*1000, location) + return timeToEpoch(time) + + } else if len(argumentList) == 0 { // 0-argument + time := Time.Now().UTC() + return timeToEpoch(time) + } else { // 1-argument + value := valueOfArrayIndex(argumentList, 0) + value = toPrimitive(value) + if value.IsString() { + return dateParse(value.string()) + } + + return value.float64() + } + +INVALID: + epoch = math.NaN() + return +} + +var ( + dateLayoutList = []string{ + "2006", + "2006-01", + "2006-01-02", + + "2006T15:04", + "2006-01T15:04", + "2006-01-02T15:04", + + "2006T15:04:05", + "2006-01T15:04:05", + "2006-01-02T15:04:05", + + "2006T15:04:05.000", + "2006-01T15:04:05.000", + "2006-01-02T15:04:05.000", + + "2006T15:04-0700", + "2006-01T15:04-0700", + "2006-01-02T15:04-0700", + + "2006T15:04:05-0700", + "2006-01T15:04:05-0700", + "2006-01-02T15:04:05-0700", + + "2006T15:04:05.000-0700", + "2006-01T15:04:05.000-0700", + "2006-01-02T15:04:05.000-0700", + + Time.RFC1123, + } + matchDateTimeZone = regexp.MustCompile(`^(.*)(?:(Z)|([\+\-]\d{2}):(\d{2}))$`) +) + +func dateParse(date string) (epoch float64) { + // YYYY-MM-DDTHH:mm:ss.sssZ + var time Time.Time + var err error + { + date := date + if match := matchDateTimeZone.FindStringSubmatch(date); match != nil { + if match[2] == "Z" { + date = match[1] + "+0000" + } else { + date = match[1] + match[3] + match[4] + } + } + for _, layout := range dateLayoutList { + time, err = Time.Parse(layout, date) + if err == nil { + break + } + } + } + if err != nil { + return math.NaN() + } + return float64(time.UnixNano()) / (1000 * 1000) // UnixMilli() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_error.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_error.go new file mode 100644 index 0000000..84e5d79 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_error.go @@ -0,0 +1,24 @@ +package otto + +func (rt *_runtime) newErrorObject(name string, message Value, stackFramesToPop int) *_object { + self := rt.newClassObject("Error") + if message.IsDefined() { + msg := message.string() + self.defineProperty("message", toValue_string(msg), 0111, false) + self.value = newError(rt, name, stackFramesToPop, msg) + } else { + self.value = newError(rt, name, stackFramesToPop) + } + + self.defineOwnProperty("stack", _property{ + value: _propertyGetSet{ + rt.newNativeFunction("get", "internal", 0, func(FunctionCall) Value { + return toValue_string(self.value.(_error).formatWithStack()) + }), + &_nilGetSetObject, + }, + mode: modeConfigureMask & modeOnMask, + }, false) + + return self +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_function.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_function.go new file mode 100644 index 0000000..5637dc6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_function.go @@ -0,0 +1,292 @@ +package otto + +// _constructFunction +type _constructFunction func(*_object, []Value) Value + +// 13.2.2 [[Construct]] +func defaultConstruct(fn *_object, argumentList []Value) Value { + object := fn.runtime.newObject() + object.class = "Object" + + prototype := fn.get("prototype") + if prototype.kind != valueObject { + prototype = toValue_object(fn.runtime.global.ObjectPrototype) + } + object.prototype = prototype._object() + + this := toValue_object(object) + value := fn.call(this, argumentList, false, nativeFrame) + if value.kind == valueObject { + return value + } + return this +} + +// _nativeFunction +type _nativeFunction func(FunctionCall) Value + +// ===================== // +// _nativeFunctionObject // +// ===================== // + +type _nativeFunctionObject struct { + name string + file string + line int + call _nativeFunction // [[Call]] + construct _constructFunction // [[Construct]] +} + +func (runtime *_runtime) newNativeFunctionObject(name, file string, line int, native _nativeFunction, length int) *_object { + self := runtime.newClassObject("Function") + self.value = _nativeFunctionObject{ + name: name, + file: file, + line: line, + call: native, + construct: defaultConstruct, + } + self.defineProperty("length", toValue_int(length), 0000, false) + return self +} + +// =================== // +// _bindFunctionObject // +// =================== // + +type _bindFunctionObject struct { + target *_object + this Value + argumentList []Value +} + +func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object { + self := runtime.newClassObject("Function") + self.value = _bindFunctionObject{ + target: target, + this: this, + argumentList: argumentList, + } + length := int(toInt32(target.get("length"))) + length -= len(argumentList) + if length < 0 { + length = 0 + } + self.defineProperty("length", toValue_int(length), 0000, false) + self.defineProperty("caller", Value{}, 0000, false) // TODO Should throw a TypeError + self.defineProperty("arguments", Value{}, 0000, false) // TODO Should throw a TypeError + return self +} + +// [[Construct]] +func (fn _bindFunctionObject) construct(argumentList []Value) Value { + object := fn.target + switch value := object.value.(type) { + case _nativeFunctionObject: + return value.construct(object, fn.argumentList) + case _nodeFunctionObject: + argumentList = append(fn.argumentList, argumentList...) + return object.construct(argumentList) + } + panic(fn.target.runtime.panicTypeError()) +} + +// =================== // +// _nodeFunctionObject // +// =================== // + +type _nodeFunctionObject struct { + node *_nodeFunctionLiteral + stash _stash +} + +func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, stash _stash) *_object { + self := runtime.newClassObject("Function") + self.value = _nodeFunctionObject{ + node: node, + stash: stash, + } + self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) + return self +} + +// ======= // +// _object // +// ======= // + +func (self *_object) isCall() bool { + switch fn := self.value.(type) { + case _nativeFunctionObject: + return fn.call != nil + case _bindFunctionObject: + return true + case _nodeFunctionObject: + return true + } + return false +} + +func (self *_object) call(this Value, argumentList []Value, eval bool, frame _frame) Value { + switch fn := self.value.(type) { + + case _nativeFunctionObject: + // Since eval is a native function, we only have to check for it here + if eval { + eval = self == self.runtime.eval // If eval is true, then it IS a direct eval + } + + // Enter a scope, name from the native object... + rt := self.runtime + if rt.scope != nil && !eval { + rt.enterFunctionScope(rt.scope.lexical, this) + rt.scope.frame = _frame{ + native: true, + nativeFile: fn.file, + nativeLine: fn.line, + callee: fn.name, + file: nil, + } + defer func() { + rt.leaveScope() + }() + } + + return fn.call(FunctionCall{ + runtime: self.runtime, + eval: eval, + + This: this, + ArgumentList: argumentList, + Otto: self.runtime.otto, + }) + + case _bindFunctionObject: + // TODO Passthrough site, do not enter a scope + argumentList = append(fn.argumentList, argumentList...) + return fn.target.call(fn.this, argumentList, false, frame) + + case _nodeFunctionObject: + rt := self.runtime + stash := rt.enterFunctionScope(fn.stash, this) + rt.scope.frame = _frame{ + callee: fn.node.name, + file: fn.node.file, + } + defer func() { + rt.leaveScope() + }() + callValue := rt.cmpl_call_nodeFunction(self, stash, fn.node, this, argumentList) + if value, valid := callValue.value.(_result); valid { + return value.value + } + return callValue + } + + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) +} + +func (self *_object) construct(argumentList []Value) Value { + switch fn := self.value.(type) { + + case _nativeFunctionObject: + if fn.call == nil { + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) + } + if fn.construct == nil { + panic(self.runtime.panicTypeError("%v is not a constructor", toValue_object(self))) + } + return fn.construct(self, argumentList) + + case _bindFunctionObject: + return fn.construct(argumentList) + + case _nodeFunctionObject: + return defaultConstruct(self, argumentList) + } + + panic(self.runtime.panicTypeError("%v is not a function", toValue_object(self))) +} + +// 15.3.5.3 +func (self *_object) hasInstance(of Value) bool { + if !self.isCall() { + // We should not have a hasInstance method + panic(self.runtime.panicTypeError()) + } + if !of.IsObject() { + return false + } + prototype := self.get("prototype") + if !prototype.IsObject() { + panic(self.runtime.panicTypeError()) + } + prototypeObject := prototype._object() + + value := of._object().prototype + for value != nil { + if value == prototypeObject { + return true + } + value = value.prototype + } + return false +} + +// ============ // +// FunctionCall // +// ============ // + +// FunctionCall is an encapsulation of a JavaScript function call. +type FunctionCall struct { + runtime *_runtime + _thisObject *_object + eval bool // This call is a direct call to eval + + This Value + ArgumentList []Value + Otto *Otto +} + +// Argument will return the value of the argument at the given index. +// +// If no such argument exists, undefined is returned. +func (self FunctionCall) Argument(index int) Value { + return valueOfArrayIndex(self.ArgumentList, index) +} + +func (self FunctionCall) getArgument(index int) (Value, bool) { + return getValueOfArrayIndex(self.ArgumentList, index) +} + +func (self FunctionCall) slice(index int) []Value { + if index < len(self.ArgumentList) { + return self.ArgumentList[index:] + } + return []Value{} +} + +func (self *FunctionCall) thisObject() *_object { + if self._thisObject == nil { + this := self.This.resolve() // FIXME Is this right? + self._thisObject = self.runtime.toObject(this) + } + return self._thisObject +} + +func (self *FunctionCall) thisClassObject(class string) *_object { + thisObject := self.thisObject() + if thisObject.class != class { + panic(self.runtime.panicTypeError()) + } + return self._thisObject +} + +func (self FunctionCall) toObject(value Value) *_object { + return self.runtime.toObject(value) +} + +// CallerLocation will return file location information (file:line:pos) where this function is being called. +func (self FunctionCall) CallerLocation() string { + // see error.go for location() + return self.runtime.scope.outer.frame.location() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_array.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_array.go new file mode 100644 index 0000000..13a0b10 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_array.go @@ -0,0 +1,134 @@ +package otto + +import ( + "reflect" + "strconv" +) + +func (runtime *_runtime) newGoArrayObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "GoArray" + self.objectClass = _classGoArray + self.value = _newGoArrayObject(value) + return self +} + +type _goArrayObject struct { + value reflect.Value + writable bool + propertyMode _propertyMode +} + +func _newGoArrayObject(value reflect.Value) *_goArrayObject { + writable := value.Kind() == reflect.Ptr // The Array is addressable (like a Slice) + mode := _propertyMode(0010) + if writable { + mode = 0110 + } + self := &_goArrayObject{ + value: value, + writable: writable, + propertyMode: mode, + } + return self +} + +func (self _goArrayObject) getValue(index int64) (reflect.Value, bool) { + value := reflect.Indirect(self.value) + if index < int64(value.Len()) { + return value.Index(int(index)), true + } + return reflect.Value{}, false +} + +func (self _goArrayObject) setValue(index int64, value Value) bool { + indexValue, exists := self.getValue(index) + if !exists { + return false + } + reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind()) + if err != nil { + panic(err) + } + indexValue.Set(reflectValue) + return true +} + +func goArrayGetOwnProperty(self *_object, name string) *_property { + // length + if name == "length" { + return &_property{ + value: toValue(reflect.Indirect(self.value.(*_goArrayObject).value).Len()), + mode: 0, + } + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + value := Value{} + reflectValue, exists := object.getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + mode: object.propertyMode, + } + } + + return objectGetOwnProperty(self, name) +} + +func goArrayEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goArrayObject) + // .0, .1, .2, ... + + for index, length := 0, object.value.Len(); index < length; index++ { + name := strconv.FormatInt(int64(index), 10) + if !each(name) { + return + } + } + + objectEnumerate(self, all, each) +} + +func goArrayDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if name == "length" { + return self.runtime.typeErrorResult(throw) + } else if index := stringToArrayIndex(name); index >= 0 { + object := self.value.(*_goArrayObject) + if object.writable { + if self.value.(*_goArrayObject).setValue(index, descriptor.value.(Value)) { + return true + } + } + return self.runtime.typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goArrayDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return self.runtime.typeErrorResult(throw) + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goArrayObject) + if object.writable { + indexValue, exists := object.getValue(index) + if exists { + indexValue.Set(reflect.Zero(reflect.Indirect(object.value).Type().Elem())) + return true + } + } + return self.runtime.typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_map.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_map.go new file mode 100644 index 0000000..3e204a0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_map.go @@ -0,0 +1,87 @@ +package otto + +import ( + "reflect" +) + +func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "Object" // TODO Should this be something else? + self.objectClass = _classGoMap + self.value = _newGoMapObject(value) + return self +} + +type _goMapObject struct { + value reflect.Value + keyKind reflect.Kind + valueKind reflect.Kind +} + +func _newGoMapObject(value reflect.Value) *_goMapObject { + if value.Kind() != reflect.Map { + dbgf("%/panic//%@: %v != reflect.Map", value.Kind()) + } + self := &_goMapObject{ + value: value, + keyKind: value.Type().Key().Kind(), + valueKind: value.Type().Elem().Kind(), + } + return self +} + +func (self _goMapObject) toKey(name string) reflect.Value { + reflectValue, err := stringToReflectValue(name, self.keyKind) + if err != nil { + panic(err) + } + return reflectValue +} + +func (self _goMapObject) toValue(value Value) reflect.Value { + reflectValue, err := value.toReflectValue(self.valueKind) + if err != nil { + panic(err) + } + return reflectValue +} + +func goMapGetOwnProperty(self *_object, name string) *_property { + object := self.value.(*_goMapObject) + value := object.value.MapIndex(object.toKey(name)) + if value.IsValid() { + return &_property{self.runtime.toValue(value.Interface()), 0111} + } + + return nil +} + +func goMapEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goMapObject) + keys := object.value.MapKeys() + for _, key := range keys { + if !each(toValue(key).String()) { + return + } + } +} + +func goMapDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + object := self.value.(*_goMapObject) + // TODO ...or 0222 + if descriptor.mode != 0111 { + return self.runtime.typeErrorResult(throw) + } + if !descriptor.isDataDescriptor() { + return self.runtime.typeErrorResult(throw) + } + object.value.SetMapIndex(object.toKey(name), object.toValue(descriptor.value.(Value))) + return true +} + +func goMapDelete(self *_object, name string, throw bool) bool { + object := self.value.(*_goMapObject) + object.value.SetMapIndex(object.toKey(name), reflect.Value{}) + // FIXME + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_slice.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_slice.go new file mode 100644 index 0000000..78c69e6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_slice.go @@ -0,0 +1,126 @@ +package otto + +import ( + "reflect" + "strconv" +) + +func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "GoArray" // TODO GoSlice? + self.objectClass = _classGoSlice + self.value = _newGoSliceObject(value) + return self +} + +type _goSliceObject struct { + value reflect.Value +} + +func _newGoSliceObject(value reflect.Value) *_goSliceObject { + self := &_goSliceObject{ + value: value, + } + return self +} + +func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) { + if index < int64(self.value.Len()) { + return self.value.Index(int(index)), true + } + return reflect.Value{}, false +} + +func (self _goSliceObject) setValue(index int64, value Value) bool { + indexValue, exists := self.getValue(index) + if !exists { + return false + } + reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind()) + if err != nil { + panic(err) + } + indexValue.Set(reflectValue) + return true +} + +func goSliceGetOwnProperty(self *_object, name string) *_property { + // length + if name == "length" { + return &_property{ + value: toValue(self.value.(*_goSliceObject).value.Len()), + mode: 0, + } + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + value := Value{} + reflectValue, exists := self.value.(*_goSliceObject).getValue(index) + if exists { + value = self.runtime.toValue(reflectValue.Interface()) + } + return &_property{ + value: value, + mode: 0110, + } + } + + // Other methods + if method := self.value.(*_goSliceObject).value.MethodByName(name); (method != reflect.Value{}) { + return &_property{ + value: self.runtime.toValue(method.Interface()), + mode: 0110, + } + } + + return objectGetOwnProperty(self, name) +} + +func goSliceEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goSliceObject) + // .0, .1, .2, ... + + for index, length := 0, object.value.Len(); index < length; index++ { + name := strconv.FormatInt(int64(index), 10) + if !each(name) { + return + } + } + + objectEnumerate(self, all, each) +} + +func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool { + if name == "length" { + return self.runtime.typeErrorResult(throw) + } else if index := stringToArrayIndex(name); index >= 0 { + if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) { + return true + } + return self.runtime.typeErrorResult(throw) + } + return objectDefineOwnProperty(self, name, descriptor, throw) +} + +func goSliceDelete(self *_object, name string, throw bool) bool { + // length + if name == "length" { + return self.runtime.typeErrorResult(throw) + } + + // .0, .1, .2, ... + index := stringToArrayIndex(name) + if index >= 0 { + object := self.value.(*_goSliceObject) + indexValue, exists := object.getValue(index) + if exists { + indexValue.Set(reflect.Zero(object.value.Type().Elem())) + return true + } + return self.runtime.typeErrorResult(throw) + } + + return self.delete(name, throw) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_struct.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_struct.go new file mode 100644 index 0000000..608ac66 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_go_struct.go @@ -0,0 +1,146 @@ +package otto + +import ( + "encoding/json" + "reflect" +) + +// FIXME Make a note about not being able to modify a struct unless it was +// passed as a pointer-to: &struct{ ... } +// This seems to be a limitation of the reflect package. +// This goes for the other Go constructs too. +// I guess we could get around it by either: +// 1. Creating a new struct every time +// 2. Creating an addressable? struct in the constructor + +func (runtime *_runtime) newGoStructObject(value reflect.Value) *_object { + self := runtime.newObject() + self.class = "Object" // TODO Should this be something else? + self.objectClass = _classGoStruct + self.value = _newGoStructObject(value) + return self +} + +type _goStructObject struct { + value reflect.Value +} + +func _newGoStructObject(value reflect.Value) *_goStructObject { + if reflect.Indirect(value).Kind() != reflect.Struct { + dbgf("%/panic//%@: %v != reflect.Struct", value.Kind()) + } + self := &_goStructObject{ + value: value, + } + return self +} + +func (self _goStructObject) getValue(name string) reflect.Value { + if validGoStructName(name) { + // Do not reveal hidden or unexported fields + if field := reflect.Indirect(self.value).FieldByName(name); (field != reflect.Value{}) { + return field + } + + if method := self.value.MethodByName(name); (method != reflect.Value{}) { + return method + } + } + + return reflect.Value{} +} + +func (self _goStructObject) field(name string) (reflect.StructField, bool) { + return reflect.Indirect(self.value).Type().FieldByName(name) +} + +func (self _goStructObject) method(name string) (reflect.Method, bool) { + return reflect.Indirect(self.value).Type().MethodByName(name) +} + +func (self _goStructObject) setValue(name string, value Value) bool { + field, exists := self.field(name) + if !exists { + return false + } + fieldValue := self.getValue(name) + reflectValue, err := value.toReflectValue(field.Type.Kind()) + if err != nil { + panic(err) + } + fieldValue.Set(reflectValue) + + return true +} + +func goStructGetOwnProperty(self *_object, name string) *_property { + object := self.value.(*_goStructObject) + value := object.getValue(name) + if value.IsValid() { + return &_property{self.runtime.toValue(value.Interface()), 0110} + } + + return objectGetOwnProperty(self, name) +} + +func validGoStructName(name string) bool { + if name == "" { + return false + } + return 'A' <= name[0] && name[0] <= 'Z' // TODO What about Unicode? +} + +func goStructEnumerate(self *_object, all bool, each func(string) bool) { + object := self.value.(*_goStructObject) + + // Enumerate fields + for index := 0; index < reflect.Indirect(object.value).NumField(); index++ { + name := reflect.Indirect(object.value).Type().Field(index).Name + if validGoStructName(name) { + if !each(name) { + return + } + } + } + + // Enumerate methods + for index := 0; index < object.value.NumMethod(); index++ { + name := object.value.Type().Method(index).Name + if validGoStructName(name) { + if !each(name) { + return + } + } + } + + objectEnumerate(self, all, each) +} + +func goStructCanPut(self *_object, name string) bool { + object := self.value.(*_goStructObject) + value := object.getValue(name) + if value.IsValid() { + return true + } + + return objectCanPut(self, name) +} + +func goStructPut(self *_object, name string, value Value, throw bool) { + object := self.value.(*_goStructObject) + if object.setValue(name, value) { + return + } + + objectPut(self, name, value, throw) +} + +func goStructMarshalJSON(self *_object) json.Marshaler { + object := self.value.(*_goStructObject) + goValue := reflect.Indirect(object.value).Interface() + switch marshaler := goValue.(type) { + case json.Marshaler: + return marshaler + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_number.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_number.go new file mode 100644 index 0000000..28de444 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_number.go @@ -0,0 +1,5 @@ +package otto + +func (runtime *_runtime) newNumberObject(value Value) *_object { + return runtime.newPrimitiveObject("Number", value.numberValue()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_reference.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_reference.go new file mode 100644 index 0000000..fd770c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_reference.go @@ -0,0 +1,103 @@ +package otto + +type _reference interface { + invalid() bool // IsUnresolvableReference + getValue() Value // getValue + putValue(Value) string // PutValue + delete() bool +} + +// PropertyReference + +type _propertyReference struct { + name string + strict bool + base *_object + runtime *_runtime + at _at +} + +func newPropertyReference(rt *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { + return &_propertyReference{ + runtime: rt, + name: name, + strict: strict, + base: base, + at: at, + } +} + +func (self *_propertyReference) invalid() bool { + return self.base == nil +} + +func (self *_propertyReference) getValue() Value { + if self.base == nil { + panic(self.runtime.panicReferenceError("'%s' is not defined", self.name, self.at)) + } + return self.base.get(self.name) +} + +func (self *_propertyReference) putValue(value Value) string { + if self.base == nil { + return self.name + } + self.base.put(self.name, value, self.strict) + return "" +} + +func (self *_propertyReference) delete() bool { + if self.base == nil { + // TODO Throw an error if strict + return true + } + return self.base.delete(self.name, self.strict) +} + +// ArgumentReference + +func newArgumentReference(runtime *_runtime, base *_object, name string, strict bool, at _at) *_propertyReference { + if base == nil { + panic(hereBeDragons()) + } + return newPropertyReference(runtime, base, name, strict, at) +} + +type _stashReference struct { + name string + strict bool + base _stash +} + +func (self *_stashReference) invalid() bool { + return false // The base (an environment) will never be nil +} + +func (self *_stashReference) getValue() Value { + return self.base.getBinding(self.name, self.strict) +} + +func (self *_stashReference) putValue(value Value) string { + self.base.setValue(self.name, value, self.strict) + return "" +} + +func (self *_stashReference) delete() bool { + if self.base == nil { + // This should never be reached, but just in case + return false + } + return self.base.deleteBinding(self.name) +} + +// getIdentifierReference + +func getIdentifierReference(runtime *_runtime, stash _stash, name string, strict bool, at _at) _reference { + if stash == nil { + return newPropertyReference(runtime, nil, name, strict, at) + } + if stash.hasBinding(name) { + return stash.newReference(name, strict, at) + } + return getIdentifierReference(runtime, stash.outer(), name, strict, at) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_regexp.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_regexp.go new file mode 100644 index 0000000..57fe316 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_regexp.go @@ -0,0 +1,146 @@ +package otto + +import ( + "fmt" + "regexp" + "unicode/utf8" + + "github.com/robertkrimen/otto/parser" +) + +type _regExpObject struct { + regularExpression *regexp.Regexp + global bool + ignoreCase bool + multiline bool + source string + flags string +} + +func (runtime *_runtime) newRegExpObject(pattern string, flags string) *_object { + self := runtime.newObject() + self.class = "RegExp" + + global := false + ignoreCase := false + multiline := false + re2flags := "" + + // TODO Maybe clean up the panicking here... TypeError, SyntaxError, ? + + for _, chr := range flags { + switch chr { + case 'g': + if global { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + global = true + case 'm': + if multiline { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + multiline = true + re2flags += "m" + case 'i': + if ignoreCase { + panic(runtime.panicSyntaxError("newRegExpObject: %s %s", pattern, flags)) + } + ignoreCase = true + re2flags += "i" + } + } + + re2pattern, err := parser.TransformRegExp(pattern) + if err != nil { + panic(runtime.panicTypeError("Invalid regular expression: %s", err.Error())) + } + if len(re2flags) > 0 { + re2pattern = fmt.Sprintf("(?%s:%s)", re2flags, re2pattern) + } + + regularExpression, err := regexp.Compile(re2pattern) + if err != nil { + panic(runtime.panicSyntaxError("Invalid regular expression: %s", err.Error()[22:])) + } + + self.value = _regExpObject{ + regularExpression: regularExpression, + global: global, + ignoreCase: ignoreCase, + multiline: multiline, + source: pattern, + flags: flags, + } + self.defineProperty("global", toValue_bool(global), 0, false) + self.defineProperty("ignoreCase", toValue_bool(ignoreCase), 0, false) + self.defineProperty("multiline", toValue_bool(multiline), 0, false) + self.defineProperty("lastIndex", toValue_int(0), 0100, false) + self.defineProperty("source", toValue_string(pattern), 0, false) + return self +} + +func (self *_object) regExpValue() _regExpObject { + value, _ := self.value.(_regExpObject) + return value +} + +func execRegExp(this *_object, target string) (match bool, result []int) { + if this.class != "RegExp" { + panic(this.runtime.panicTypeError("Calling RegExp.exec on a non-RegExp object")) + } + lastIndex := this.get("lastIndex").number().int64 + index := lastIndex + global := this.get("global").bool() + if !global { + index = 0 + } + if 0 > index || index > int64(len(target)) { + } else { + result = this.regExpValue().regularExpression.FindStringSubmatchIndex(target[index:]) + } + if result == nil { + //this.defineProperty("lastIndex", toValue_(0), 0111, true) + this.put("lastIndex", toValue_int(0), true) + return // !match + } + match = true + startIndex := index + endIndex := int(lastIndex) + result[1] + // We do this shift here because the .FindStringSubmatchIndex above + // was done on a local subordinate slice of the string, not the whole string + for index, _ := range result { + result[index] += int(startIndex) + } + if global { + //this.defineProperty("lastIndex", toValue_(endIndex), 0111, true) + this.put("lastIndex", toValue_int(endIndex), true) + } + return // match +} + +func execResultToArray(runtime *_runtime, target string, result []int) *_object { + captureCount := len(result) / 2 + valueArray := make([]Value, captureCount) + for index := 0; index < captureCount; index++ { + offset := 2 * index + if result[offset] != -1 { + valueArray[index] = toValue_string(target[result[offset]:result[offset+1]]) + } else { + valueArray[index] = Value{} + } + } + matchIndex := result[0] + if matchIndex != 0 { + matchIndex = 0 + // Find the rune index in the string, not the byte index + for index := 0; index < result[0]; { + _, size := utf8.DecodeRuneInString(target[index:]) + matchIndex += 1 + index += size + } + } + match := runtime.newArrayOf(valueArray) + match.defineProperty("input", toValue_string(target), 0111, false) + match.defineProperty("index", toValue_int(matchIndex), 0111, false) + return match +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_string.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_string.go new file mode 100644 index 0000000..ef3afa4 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/type_string.go @@ -0,0 +1,112 @@ +package otto + +import ( + "strconv" + "unicode/utf8" +) + +type _stringObject interface { + Length() int + At(int) rune + String() string +} + +type _stringASCII string + +func (str _stringASCII) Length() int { + return len(str) +} + +func (str _stringASCII) At(at int) rune { + return rune(str[at]) +} + +func (str _stringASCII) String() string { + return string(str) +} + +type _stringWide struct { + string string + length int + runes []rune +} + +func (str _stringWide) Length() int { + return str.length +} + +func (str _stringWide) At(at int) rune { + if str.runes == nil { + str.runes = []rune(str.string) + } + return str.runes[at] +} + +func (str _stringWide) String() string { + return str.string +} + +func _newStringObject(str string) _stringObject { + for i := 0; i < len(str); i++ { + if str[i] >= utf8.RuneSelf { + goto wide + } + } + + return _stringASCII(str) + +wide: + return &_stringWide{ + string: str, + length: utf8.RuneCountInString(str), + } +} + +func stringAt(str _stringObject, index int) rune { + if 0 <= index && index < str.Length() { + return str.At(index) + } + return utf8.RuneError +} + +func (runtime *_runtime) newStringObject(value Value) *_object { + str := _newStringObject(value.string()) + + self := runtime.newClassObject("String") + self.defineProperty("length", toValue_int(str.Length()), 0, false) + self.objectClass = _classString + self.value = str + return self +} + +func (self *_object) stringValue() _stringObject { + if str, ok := self.value.(_stringObject); ok { + return str + } + return nil +} + +func stringEnumerate(self *_object, all bool, each func(string) bool) { + if str := self.stringValue(); str != nil { + length := str.Length() + for index := 0; index < length; index++ { + if !each(strconv.FormatInt(int64(index), 10)) { + return + } + } + } + objectEnumerate(self, all, each) +} + +func stringGetOwnProperty(self *_object, name string) *_property { + if property := objectGetOwnProperty(self, name); property != nil { + return property + } + // TODO Test a string of length >= +int32 + 1? + if index := stringToArrayIndex(name); index >= 0 { + if chr := stringAt(self.stringValue(), int(index)); chr != utf8.RuneError { + return &_property{toValue_string(string(chr)), 0} + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value.go new file mode 100644 index 0000000..90c1406 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value.go @@ -0,0 +1,1033 @@ +package otto + +import ( + "fmt" + "math" + "reflect" + "strconv" + "unicode/utf16" +) + +type _valueKind int + +const ( + valueUndefined _valueKind = iota + valueNull + valueNumber + valueString + valueBoolean + valueObject + + // These are invalid outside of the runtime + valueEmpty + valueResult + valueReference +) + +// Value is the representation of a JavaScript value. +type Value struct { + kind _valueKind + value interface{} +} + +func (value Value) safe() bool { + return value.kind < valueEmpty +} + +var ( + emptyValue = Value{kind: valueEmpty} + nullValue = Value{kind: valueNull} + falseValue = Value{kind: valueBoolean, value: false} + trueValue = Value{kind: valueBoolean, value: true} +) + +// ToValue will convert an interface{} value to a value digestible by otto/JavaScript +// +// This function will not work for advanced types (struct, map, slice/array, etc.) and +// you should use Otto.ToValue instead. +func ToValue(value interface{}) (Value, error) { + result := Value{} + err := catchPanic(func() { + result = toValue(value) + }) + return result, err +} + +func (value Value) isEmpty() bool { + return value.kind == valueEmpty +} + +// Undefined + +// UndefinedValue will return a Value representing undefined. +func UndefinedValue() Value { + return Value{} +} + +// IsDefined will return false if the value is undefined, and true otherwise. +func (value Value) IsDefined() bool { + return value.kind != valueUndefined +} + +// IsUndefined will return true if the value is undefined, and false otherwise. +func (value Value) IsUndefined() bool { + return value.kind == valueUndefined +} + +// NullValue will return a Value representing null. +func NullValue() Value { + return Value{kind: valueNull} +} + +// IsNull will return true if the value is null, and false otherwise. +func (value Value) IsNull() bool { + return value.kind == valueNull +} + +// --- + +func (value Value) isCallable() bool { + switch value := value.value.(type) { + case *_object: + return value.isCall() + } + return false +} + +// Call the value as a function with the given this value and argument list and +// return the result of invocation. It is essentially equivalent to: +// +// value.apply(thisValue, argumentList) +// +// An undefined value and an error will result if: +// +// 1. There is an error during conversion of the argument list +// 2. The value is not actually a function +// 3. An (uncaught) exception is thrown +// +func (value Value) Call(this Value, argumentList ...interface{}) (Value, error) { + result := Value{} + err := catchPanic(func() { + // FIXME + result = value.call(nil, this, argumentList...) + }) + if !value.safe() { + value = Value{} + } + return result, err +} + +func (value Value) call(rt *_runtime, this Value, argumentList ...interface{}) Value { + switch function := value.value.(type) { + case *_object: + return function.call(this, function.runtime.toValueArray(argumentList...), false, nativeFrame) + } + if rt == nil { + panic("FIXME TypeError") + } + panic(rt.panicTypeError()) +} + +func (value Value) constructSafe(rt *_runtime, this Value, argumentList ...interface{}) (Value, error) { + result := Value{} + err := catchPanic(func() { + result = value.construct(rt, this, argumentList...) + }) + return result, err +} + +func (value Value) construct(rt *_runtime, this Value, argumentList ...interface{}) Value { + switch fn := value.value.(type) { + case *_object: + return fn.construct(fn.runtime.toValueArray(argumentList...)) + } + if rt == nil { + panic("FIXME TypeError") + } + panic(rt.panicTypeError()) +} + +// IsPrimitive will return true if value is a primitive (any kind of primitive). +func (value Value) IsPrimitive() bool { + return !value.IsObject() +} + +// IsBoolean will return true if value is a boolean (primitive). +func (value Value) IsBoolean() bool { + return value.kind == valueBoolean +} + +// IsNumber will return true if value is a number (primitive). +func (value Value) IsNumber() bool { + return value.kind == valueNumber +} + +// IsNaN will return true if value is NaN (or would convert to NaN). +func (value Value) IsNaN() bool { + switch value := value.value.(type) { + case float64: + return math.IsNaN(value) + case float32: + return math.IsNaN(float64(value)) + case int, int8, int32, int64: + return false + case uint, uint8, uint32, uint64: + return false + } + + return math.IsNaN(value.float64()) +} + +// IsString will return true if value is a string (primitive). +func (value Value) IsString() bool { + return value.kind == valueString +} + +// IsObject will return true if value is an object. +func (value Value) IsObject() bool { + return value.kind == valueObject +} + +// IsFunction will return true if value is a function. +func (value Value) IsFunction() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "Function" +} + +// Class will return the class string of the value or the empty string if value is not an object. +// +// The return value will (generally) be one of: +// +// Object +// Function +// Array +// String +// Number +// Boolean +// Date +// RegExp +// +func (value Value) Class() string { + if value.kind != valueObject { + return "" + } + return value.value.(*_object).class +} + +func (value Value) isArray() bool { + if value.kind != valueObject { + return false + } + return isArray(value.value.(*_object)) +} + +func (value Value) isStringObject() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "String" +} + +func (value Value) isBooleanObject() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "Boolean" +} + +func (value Value) isNumberObject() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "Number" +} + +func (value Value) isDate() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "Date" +} + +func (value Value) isRegExp() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "RegExp" +} + +func (value Value) isError() bool { + if value.kind != valueObject { + return false + } + return value.value.(*_object).class == "Error" +} + +// --- + +func toValue_reflectValuePanic(value interface{}, kind reflect.Kind) { + // FIXME? + switch kind { + case reflect.Struct: + panic(newError(nil, "TypeError", 0, "invalid value (struct): missing runtime: %v (%T)", value, value)) + case reflect.Map: + panic(newError(nil, "TypeError", 0, "invalid value (map): missing runtime: %v (%T)", value, value)) + case reflect.Slice: + panic(newError(nil, "TypeError", 0, "invalid value (slice): missing runtime: %v (%T)", value, value)) + } +} + +func toValue(value interface{}) Value { + switch value := value.(type) { + case Value: + return value + case bool: + return Value{valueBoolean, value} + case int: + return Value{valueNumber, value} + case int8: + return Value{valueNumber, value} + case int16: + return Value{valueNumber, value} + case int32: + return Value{valueNumber, value} + case int64: + return Value{valueNumber, value} + case uint: + return Value{valueNumber, value} + case uint8: + return Value{valueNumber, value} + case uint16: + return Value{valueNumber, value} + case uint32: + return Value{valueNumber, value} + case uint64: + return Value{valueNumber, value} + case float32: + return Value{valueNumber, float64(value)} + case float64: + return Value{valueNumber, value} + case []uint16: + return Value{valueString, value} + case string: + return Value{valueString, value} + // A rune is actually an int32, which is handled above + case *_object: + return Value{valueObject, value} + case *Object: + return Value{valueObject, value.object} + case Object: + return Value{valueObject, value.object} + case _reference: // reference is an interface (already a pointer) + return Value{valueReference, value} + case _result: + return Value{valueResult, value} + case nil: + // TODO Ugh. + return Value{} + case reflect.Value: + for value.Kind() == reflect.Ptr { + // We were given a pointer, so we'll drill down until we get a non-pointer + // + // These semantics might change if we want to start supporting pointers to values transparently + // (It would be best not to depend on this behavior) + // FIXME: UNDEFINED + if value.IsNil() { + return Value{} + } + value = value.Elem() + } + switch value.Kind() { + case reflect.Bool: + return Value{valueBoolean, bool(value.Bool())} + case reflect.Int: + return Value{valueNumber, int(value.Int())} + case reflect.Int8: + return Value{valueNumber, int8(value.Int())} + case reflect.Int16: + return Value{valueNumber, int16(value.Int())} + case reflect.Int32: + return Value{valueNumber, int32(value.Int())} + case reflect.Int64: + return Value{valueNumber, int64(value.Int())} + case reflect.Uint: + return Value{valueNumber, uint(value.Uint())} + case reflect.Uint8: + return Value{valueNumber, uint8(value.Uint())} + case reflect.Uint16: + return Value{valueNumber, uint16(value.Uint())} + case reflect.Uint32: + return Value{valueNumber, uint32(value.Uint())} + case reflect.Uint64: + return Value{valueNumber, uint64(value.Uint())} + case reflect.Float32: + return Value{valueNumber, float32(value.Float())} + case reflect.Float64: + return Value{valueNumber, float64(value.Float())} + case reflect.String: + return Value{valueString, string(value.String())} + default: + toValue_reflectValuePanic(value.Interface(), value.Kind()) + } + default: + return toValue(reflect.ValueOf(value)) + } + // FIXME? + panic(newError(nil, "TypeError", 0, "invalid value: %v (%T)", value, value)) +} + +// String will return the value as a string. +// +// This method will make return the empty string if there is an error. +func (value Value) String() string { + result := "" + catchPanic(func() { + result = value.string() + }) + return result +} + +// ToBoolean will convert the value to a boolean (bool). +// +// ToValue(0).ToBoolean() => false +// ToValue("").ToBoolean() => false +// ToValue(true).ToBoolean() => true +// ToValue(1).ToBoolean() => true +// ToValue("Nothing happens").ToBoolean() => true +// +// If there is an error during the conversion process (like an uncaught exception), then the result will be false and an error. +func (value Value) ToBoolean() (bool, error) { + result := false + err := catchPanic(func() { + result = value.bool() + }) + return result, err +} + +func (value Value) numberValue() Value { + if value.kind == valueNumber { + return value + } + return Value{valueNumber, value.float64()} +} + +// ToFloat will convert the value to a number (float64). +// +// ToValue(0).ToFloat() => 0. +// ToValue(1.1).ToFloat() => 1.1 +// ToValue("11").ToFloat() => 11. +// +// If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error. +func (value Value) ToFloat() (float64, error) { + result := float64(0) + err := catchPanic(func() { + result = value.float64() + }) + return result, err +} + +// ToInteger will convert the value to a number (int64). +// +// ToValue(0).ToInteger() => 0 +// ToValue(1.1).ToInteger() => 1 +// ToValue("11").ToInteger() => 11 +// +// If there is an error during the conversion process (like an uncaught exception), then the result will be 0 and an error. +func (value Value) ToInteger() (int64, error) { + result := int64(0) + err := catchPanic(func() { + result = value.number().int64 + }) + return result, err +} + +// ToString will convert the value to a string (string). +// +// ToValue(0).ToString() => "0" +// ToValue(false).ToString() => "false" +// ToValue(1.1).ToString() => "1.1" +// ToValue("11").ToString() => "11" +// ToValue('Nothing happens.').ToString() => "Nothing happens." +// +// If there is an error during the conversion process (like an uncaught exception), then the result will be the empty string ("") and an error. +func (value Value) ToString() (string, error) { + result := "" + err := catchPanic(func() { + result = value.string() + }) + return result, err +} + +func (value Value) _object() *_object { + switch value := value.value.(type) { + case *_object: + return value + } + return nil +} + +// Object will return the object of the value, or nil if value is not an object. +// +// This method will not do any implicit conversion. For example, calling this method on a string primitive value will not return a String object. +func (value Value) Object() *Object { + switch object := value.value.(type) { + case *_object: + return _newObject(object, value) + } + return nil +} + +func (value Value) reference() _reference { + switch value := value.value.(type) { + case _reference: + return value + } + return nil +} + +func (value Value) resolve() Value { + switch value := value.value.(type) { + case _reference: + return value.getValue() + } + return value +} + +var ( + __NaN__ float64 = math.NaN() + __PositiveInfinity__ float64 = math.Inf(+1) + __NegativeInfinity__ float64 = math.Inf(-1) + __PositiveZero__ float64 = 0 + __NegativeZero__ float64 = math.Float64frombits(0 | (1 << 63)) +) + +func positiveInfinity() float64 { + return __PositiveInfinity__ +} + +func negativeInfinity() float64 { + return __NegativeInfinity__ +} + +func positiveZero() float64 { + return __PositiveZero__ +} + +func negativeZero() float64 { + return __NegativeZero__ +} + +// NaNValue will return a value representing NaN. +// +// It is equivalent to: +// +// ToValue(math.NaN()) +// +func NaNValue() Value { + return Value{valueNumber, __NaN__} +} + +func positiveInfinityValue() Value { + return Value{valueNumber, __PositiveInfinity__} +} + +func negativeInfinityValue() Value { + return Value{valueNumber, __NegativeInfinity__} +} + +func positiveZeroValue() Value { + return Value{valueNumber, __PositiveZero__} +} + +func negativeZeroValue() Value { + return Value{valueNumber, __NegativeZero__} +} + +// TrueValue will return a value representing true. +// +// It is equivalent to: +// +// ToValue(true) +// +func TrueValue() Value { + return Value{valueBoolean, true} +} + +// FalseValue will return a value representing false. +// +// It is equivalent to: +// +// ToValue(false) +// +func FalseValue() Value { + return Value{valueBoolean, false} +} + +func sameValue(x Value, y Value) bool { + if x.kind != y.kind { + return false + } + result := false + switch x.kind { + case valueUndefined, valueNull: + result = true + case valueNumber: + x := x.float64() + y := y.float64() + if math.IsNaN(x) && math.IsNaN(y) { + result = true + } else { + result = x == y + if result && x == 0 { + // Since +0 != -0 + result = math.Signbit(x) == math.Signbit(y) + } + } + case valueString: + result = x.string() == y.string() + case valueBoolean: + result = x.bool() == y.bool() + case valueObject: + result = x._object() == y._object() + default: + panic(hereBeDragons()) + } + + return result +} + +func strictEqualityComparison(x Value, y Value) bool { + if x.kind != y.kind { + return false + } + result := false + switch x.kind { + case valueUndefined, valueNull: + result = true + case valueNumber: + x := x.float64() + y := y.float64() + if math.IsNaN(x) && math.IsNaN(y) { + result = false + } else { + result = x == y + } + case valueString: + result = x.string() == y.string() + case valueBoolean: + result = x.bool() == y.bool() + case valueObject: + result = x._object() == y._object() + default: + panic(hereBeDragons()) + } + + return result +} + +// Export will attempt to convert the value to a Go representation +// and return it via an interface{} kind. +// +// Export returns an error, but it will always be nil. It is present +// for backwards compatibility. +// +// If a reasonable conversion is not possible, then the original +// value is returned. +// +// undefined -> nil (FIXME?: Should be Value{}) +// null -> nil +// boolean -> bool +// number -> A number type (int, float32, uint64, ...) +// string -> string +// Array -> []interface{} +// Object -> map[string]interface{} +// +func (self Value) Export() (interface{}, error) { + return self.export(), nil +} + +func (self Value) export() interface{} { + + switch self.kind { + case valueUndefined: + return nil + case valueNull: + return nil + case valueNumber, valueBoolean: + return self.value + case valueString: + switch value := self.value.(type) { + case string: + return value + case []uint16: + return string(utf16.Decode(value)) + } + case valueObject: + object := self._object() + switch value := object.value.(type) { + case *_goStructObject: + return value.value.Interface() + case *_goMapObject: + return value.value.Interface() + case *_goArrayObject: + return value.value.Interface() + case *_goSliceObject: + return value.value.Interface() + } + if object.class == "Array" { + result := make([]interface{}, 0) + lengthValue := object.get("length") + length := lengthValue.value.(uint32) + kind := reflect.Invalid + state := 0 + var t reflect.Type + for index := uint32(0); index < length; index += 1 { + name := strconv.FormatInt(int64(index), 10) + if !object.hasProperty(name) { + continue + } + value := object.get(name).export() + + t = reflect.TypeOf(value) + + var k reflect.Kind + if t != nil { + k = t.Kind() + } + + if state == 0 { + kind = k + state = 1 + } else if state == 1 && kind != k { + state = 2 + } + + result = append(result, value) + } + + if state != 1 || kind == reflect.Interface || t == nil { + // No common type + return result + } + + // Convert to the common type + val := reflect.MakeSlice(reflect.SliceOf(t), len(result), len(result)) + for i, v := range result { + val.Index(i).Set(reflect.ValueOf(v)) + } + return val.Interface() + } else { + result := make(map[string]interface{}) + // TODO Should we export everything? Or just what is enumerable? + object.enumerate(false, func(name string) bool { + value := object.get(name) + if value.IsDefined() { + result[name] = value.export() + } + return true + }) + return result + } + } + + if self.safe() { + return self + } + + return Value{} +} + +func (self Value) evaluateBreakContinue(labels []string) _resultKind { + result := self.value.(_result) + if result.kind == resultBreak || result.kind == resultContinue { + for _, label := range labels { + if label == result.target { + return result.kind + } + } + } + return resultReturn +} + +func (self Value) evaluateBreak(labels []string) _resultKind { + result := self.value.(_result) + if result.kind == resultBreak { + for _, label := range labels { + if label == result.target { + return result.kind + } + } + } + return resultReturn +} + +func (self Value) exportNative() interface{} { + + switch self.kind { + case valueUndefined: + return self + case valueNull: + return nil + case valueNumber, valueBoolean: + return self.value + case valueString: + switch value := self.value.(type) { + case string: + return value + case []uint16: + return string(utf16.Decode(value)) + } + case valueObject: + object := self._object() + switch value := object.value.(type) { + case *_goStructObject: + return value.value.Interface() + case *_goMapObject: + return value.value.Interface() + case *_goArrayObject: + return value.value.Interface() + case *_goSliceObject: + return value.value.Interface() + } + } + + return self +} + +// Make a best effort to return a reflect.Value corresponding to reflect.Kind, but +// fallback to just returning the Go value we have handy. +func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) { + if kind != reflect.Float32 && kind != reflect.Float64 && kind != reflect.Interface { + switch value := value.value.(type) { + case float32: + _, frac := math.Modf(float64(value)) + if frac > 0 { + return reflect.Value{}, fmt.Errorf("RangeError: %v to reflect.Kind: %v", value, kind) + } + case float64: + _, frac := math.Modf(value) + if frac > 0 { + return reflect.Value{}, fmt.Errorf("RangeError: %v to reflect.Kind: %v", value, kind) + } + } + } + + switch kind { + case reflect.Bool: // Bool + return reflect.ValueOf(value.bool()), nil + case reflect.Int: // Int + // We convert to float64 here because converting to int64 will not tell us + // if a value is outside the range of int64 + tmp := toIntegerFloat(value) + if tmp < float_minInt || tmp > float_maxInt { + return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value) + } else { + return reflect.ValueOf(int(tmp)), nil + } + case reflect.Int8: // Int8 + tmp := value.number().int64 + if tmp < int64_minInt8 || tmp > int64_maxInt8 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int8", tmp, value) + } else { + return reflect.ValueOf(int8(tmp)), nil + } + case reflect.Int16: // Int16 + tmp := value.number().int64 + if tmp < int64_minInt16 || tmp > int64_maxInt16 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int16", tmp, value) + } else { + return reflect.ValueOf(int16(tmp)), nil + } + case reflect.Int32: // Int32 + tmp := value.number().int64 + if tmp < int64_minInt32 || tmp > int64_maxInt32 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int32", tmp, value) + } else { + return reflect.ValueOf(int32(tmp)), nil + } + case reflect.Int64: // Int64 + // We convert to float64 here because converting to int64 will not tell us + // if a value is outside the range of int64 + tmp := toIntegerFloat(value) + if tmp < float_minInt64 || tmp > float_maxInt64 { + return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value) + } else { + return reflect.ValueOf(int64(tmp)), nil + } + case reflect.Uint: // Uint + // We convert to float64 here because converting to int64 will not tell us + // if a value is outside the range of uint + tmp := toIntegerFloat(value) + if tmp < 0 || tmp > float_maxUint { + return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint", tmp, value) + } else { + return reflect.ValueOf(uint(tmp)), nil + } + case reflect.Uint8: // Uint8 + tmp := value.number().int64 + if tmp < 0 || tmp > int64_maxUint8 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint8", tmp, value) + } else { + return reflect.ValueOf(uint8(tmp)), nil + } + case reflect.Uint16: // Uint16 + tmp := value.number().int64 + if tmp < 0 || tmp > int64_maxUint16 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint16", tmp, value) + } else { + return reflect.ValueOf(uint16(tmp)), nil + } + case reflect.Uint32: // Uint32 + tmp := value.number().int64 + if tmp < 0 || tmp > int64_maxUint32 { + return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint32", tmp, value) + } else { + return reflect.ValueOf(uint32(tmp)), nil + } + case reflect.Uint64: // Uint64 + // We convert to float64 here because converting to int64 will not tell us + // if a value is outside the range of uint64 + tmp := toIntegerFloat(value) + if tmp < 0 || tmp > float_maxUint64 { + return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint64", tmp, value) + } else { + return reflect.ValueOf(uint64(tmp)), nil + } + case reflect.Float32: // Float32 + tmp := value.float64() + tmp1 := tmp + if 0 > tmp1 { + tmp1 = -tmp1 + } + if tmp1 > 0 && (tmp1 < math.SmallestNonzeroFloat32 || tmp1 > math.MaxFloat32) { + return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to float32", tmp, value) + } else { + return reflect.ValueOf(float32(tmp)), nil + } + case reflect.Float64: // Float64 + value := value.float64() + return reflect.ValueOf(float64(value)), nil + case reflect.String: // String + return reflect.ValueOf(value.string()), nil + case reflect.Invalid: // Invalid + case reflect.Complex64: // FIXME? Complex64 + case reflect.Complex128: // FIXME? Complex128 + case reflect.Chan: // FIXME? Chan + case reflect.Func: // FIXME? Func + case reflect.Ptr: // FIXME? Ptr + case reflect.UnsafePointer: // FIXME? UnsafePointer + default: + switch value.kind { + case valueObject: + object := value._object() + switch vl := object.value.(type) { + case *_goStructObject: // Struct + return reflect.ValueOf(vl.value.Interface()), nil + case *_goMapObject: // Map + return reflect.ValueOf(vl.value.Interface()), nil + case *_goArrayObject: // Array + return reflect.ValueOf(vl.value.Interface()), nil + case *_goSliceObject: // Slice + return reflect.ValueOf(vl.value.Interface()), nil + } + return reflect.ValueOf(value.exportNative()), nil + case valueEmpty, valueResult, valueReference: + // These are invalid, and should panic + default: + return reflect.ValueOf(value.value), nil + } + } + + // FIXME Should this end up as a TypeError? + panic(fmt.Errorf("invalid conversion of %v (%v) to reflect.Kind: %v", value.kind, value, kind)) +} + +func stringToReflectValue(value string, kind reflect.Kind) (reflect.Value, error) { + switch kind { + case reflect.Bool: + value, err := strconv.ParseBool(value) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(value), nil + case reflect.Int: + value, err := strconv.ParseInt(value, 0, 0) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(int(value)), nil + case reflect.Int8: + value, err := strconv.ParseInt(value, 0, 8) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(int8(value)), nil + case reflect.Int16: + value, err := strconv.ParseInt(value, 0, 16) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(int16(value)), nil + case reflect.Int32: + value, err := strconv.ParseInt(value, 0, 32) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(int32(value)), nil + case reflect.Int64: + value, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(int64(value)), nil + case reflect.Uint: + value, err := strconv.ParseUint(value, 0, 0) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(uint(value)), nil + case reflect.Uint8: + value, err := strconv.ParseUint(value, 0, 8) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(uint8(value)), nil + case reflect.Uint16: + value, err := strconv.ParseUint(value, 0, 16) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(uint16(value)), nil + case reflect.Uint32: + value, err := strconv.ParseUint(value, 0, 32) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(uint32(value)), nil + case reflect.Uint64: + value, err := strconv.ParseUint(value, 0, 64) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(uint64(value)), nil + case reflect.Float32: + value, err := strconv.ParseFloat(value, 32) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(float32(value)), nil + case reflect.Float64: + value, err := strconv.ParseFloat(value, 64) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(float64(value)), nil + case reflect.String: + return reflect.ValueOf(value), nil + } + + // FIXME This should end up as a TypeError? + panic(fmt.Errorf("invalid conversion of %q to reflect.Kind: %v", value, kind)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_boolean.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_boolean.go new file mode 100644 index 0000000..b631507 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_boolean.go @@ -0,0 +1,43 @@ +package otto + +import ( + "fmt" + "math" + "reflect" + "unicode/utf16" +) + +func (value Value) bool() bool { + if value.kind == valueBoolean { + return value.value.(bool) + } + if value.IsUndefined() { + return false + } + if value.IsNull() { + return false + } + switch value := value.value.(type) { + case bool: + return value + case int, int8, int16, int32, int64: + return 0 != reflect.ValueOf(value).Int() + case uint, uint8, uint16, uint32, uint64: + return 0 != reflect.ValueOf(value).Uint() + case float32: + return 0 != value + case float64: + if math.IsNaN(value) || value == 0 { + return false + } + return true + case string: + return 0 != len(value) + case []uint16: + return 0 != len(utf16.Decode(value)) + } + if value.IsObject() { + return true + } + panic(fmt.Errorf("toBoolean(%T)", value.value)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_number.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_number.go new file mode 100644 index 0000000..8cbf136 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_number.go @@ -0,0 +1,324 @@ +package otto + +import ( + "fmt" + "math" + "regexp" + "strconv" + "strings" +) + +var stringToNumberParseInteger = regexp.MustCompile(`^(?:0[xX])`) + +func parseNumber(value string) float64 { + value = strings.Trim(value, builtinString_trim_whitespace) + + if value == "" { + return 0 + } + + parseFloat := false + if strings.IndexRune(value, '.') != -1 { + parseFloat = true + } else if stringToNumberParseInteger.MatchString(value) { + parseFloat = false + } else { + parseFloat = true + } + + if parseFloat { + number, err := strconv.ParseFloat(value, 64) + if err != nil && err.(*strconv.NumError).Err != strconv.ErrRange { + return math.NaN() + } + return number + } + + number, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return math.NaN() + } + return float64(number) +} + +func (value Value) float64() float64 { + switch value.kind { + case valueUndefined: + return math.NaN() + case valueNull: + return 0 + } + switch value := value.value.(type) { + case bool: + if value { + return 1 + } + return 0 + case int: + return float64(value) + case int8: + return float64(value) + case int16: + return float64(value) + case int32: + return float64(value) + case int64: + return float64(value) + case uint: + return float64(value) + case uint8: + return float64(value) + case uint16: + return float64(value) + case uint32: + return float64(value) + case uint64: + return float64(value) + case float64: + return value + case string: + return parseNumber(value) + case *_object: + return value.DefaultValue(defaultValueHintNumber).float64() + } + panic(fmt.Errorf("toFloat(%T)", value.value)) +} + +const ( + float_2_64 float64 = 18446744073709551616.0 + float_2_63 float64 = 9223372036854775808.0 + float_2_32 float64 = 4294967296.0 + float_2_31 float64 = 2147483648.0 + float_2_16 float64 = 65536.0 + integer_2_32 int64 = 4294967296 + integer_2_31 int64 = 2146483648 + sqrt1_2 float64 = math.Sqrt2 / 2 +) + +const ( + maxInt8 = math.MaxInt8 + minInt8 = math.MinInt8 + maxInt16 = math.MaxInt16 + minInt16 = math.MinInt16 + maxInt32 = math.MaxInt32 + minInt32 = math.MinInt32 + maxInt64 = math.MaxInt64 + minInt64 = math.MinInt64 + maxUint8 = math.MaxUint8 + maxUint16 = math.MaxUint16 + maxUint32 = math.MaxUint32 + maxUint64 = math.MaxUint64 + maxUint = ^uint(0) + minUint = 0 + maxInt = int(^uint(0) >> 1) + minInt = -maxInt - 1 + + // int64 + int64_maxInt int64 = int64(maxInt) + int64_minInt int64 = int64(minInt) + int64_maxInt8 int64 = math.MaxInt8 + int64_minInt8 int64 = math.MinInt8 + int64_maxInt16 int64 = math.MaxInt16 + int64_minInt16 int64 = math.MinInt16 + int64_maxInt32 int64 = math.MaxInt32 + int64_minInt32 int64 = math.MinInt32 + int64_maxUint8 int64 = math.MaxUint8 + int64_maxUint16 int64 = math.MaxUint16 + int64_maxUint32 int64 = math.MaxUint32 + + // float64 + float_maxInt float64 = float64(int(^uint(0) >> 1)) + float_minInt float64 = float64(int(-maxInt - 1)) + float_minUint float64 = float64(0) + float_maxUint float64 = float64(uint(^uint(0))) + float_minUint64 float64 = float64(0) + float_maxUint64 float64 = math.MaxUint64 + float_maxInt64 float64 = math.MaxInt64 + float_minInt64 float64 = math.MinInt64 +) + +func toIntegerFloat(value Value) float64 { + float := value.float64() + if math.IsInf(float, 0) { + } else if math.IsNaN(float) { + float = 0 + } else if float > 0 { + float = math.Floor(float) + } else { + float = math.Ceil(float) + } + return float +} + +type _numberKind int + +const ( + numberInteger _numberKind = iota // 3.0 => 3.0 + numberFloat // 3.14159 => 3.0, 1+2**63 > 2**63-1 + numberInfinity // Infinity => 2**63-1 + numberNaN // NaN => 0 +) + +type _number struct { + kind _numberKind + int64 int64 + float64 float64 +} + +// FIXME +// http://www.goinggo.net/2013/08/gustavos-ieee-754-brain-teaser.html +// http://bazaar.launchpad.net/~niemeyer/strepr/trunk/view/6/strepr.go#L160 +func (value Value) number() (number _number) { + switch value := value.value.(type) { + case int8: + number.int64 = int64(value) + return + case int16: + number.int64 = int64(value) + return + case uint8: + number.int64 = int64(value) + return + case uint16: + number.int64 = int64(value) + return + case uint32: + number.int64 = int64(value) + return + case int: + number.int64 = int64(value) + return + case int64: + number.int64 = value + return + } + + float := value.float64() + if float == 0 { + return + } + + number.kind = numberFloat + number.float64 = float + + if math.IsNaN(float) { + number.kind = numberNaN + return + } + + if math.IsInf(float, 0) { + number.kind = numberInfinity + } + + if float >= float_maxInt64 { + number.int64 = math.MaxInt64 + return + } + + if float <= float_minInt64 { + number.int64 = math.MinInt64 + return + } + + integer := float64(0) + if float > 0 { + integer = math.Floor(float) + } else { + integer = math.Ceil(float) + } + + if float == integer { + number.kind = numberInteger + } + number.int64 = int64(float) + return +} + +// ECMA 262: 9.5 +func toInt32(value Value) int32 { + { + switch value := value.value.(type) { + case int8: + return int32(value) + case int16: + return int32(value) + case int32: + return value + } + } + floatValue := value.float64() + if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) { + return 0 + } + if floatValue == 0 { // This will work for +0 & -0 + return 0 + } + remainder := math.Mod(floatValue, float_2_32) + if remainder > 0 { + remainder = math.Floor(remainder) + } else { + remainder = math.Ceil(remainder) + float_2_32 + } + if remainder > float_2_31 { + return int32(remainder - float_2_32) + } + return int32(remainder) +} + +func toUint32(value Value) uint32 { + { + switch value := value.value.(type) { + case int8: + return uint32(value) + case int16: + return uint32(value) + case uint8: + return uint32(value) + case uint16: + return uint32(value) + case uint32: + return value + } + } + floatValue := value.float64() + if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) { + return 0 + } + if floatValue == 0 { + return 0 + } + remainder := math.Mod(floatValue, float_2_32) + if remainder > 0 { + remainder = math.Floor(remainder) + } else { + remainder = math.Ceil(remainder) + float_2_32 + } + return uint32(remainder) +} + +func toUint16(value Value) uint16 { + { + switch value := value.value.(type) { + case int8: + return uint16(value) + case uint8: + return uint16(value) + case uint16: + return value + } + } + floatValue := value.float64() + if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) { + return 0 + } + if floatValue == 0 { + return 0 + } + remainder := math.Mod(floatValue, float_2_16) + if remainder > 0 { + remainder = math.Floor(remainder) + } else { + remainder = math.Ceil(remainder) + float_2_16 + } + return uint16(remainder) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_primitive.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_primitive.go new file mode 100644 index 0000000..11ed329 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_primitive.go @@ -0,0 +1,23 @@ +package otto + +func toStringPrimitive(value Value) Value { + return _toPrimitive(value, defaultValueHintString) +} + +func toNumberPrimitive(value Value) Value { + return _toPrimitive(value, defaultValueHintNumber) +} + +func toPrimitive(value Value) Value { + return _toPrimitive(value, defaultValueNoHint) +} + +func _toPrimitive(value Value, hint _defaultValueHint) Value { + switch value.kind { + case valueNull, valueUndefined, valueNumber, valueString, valueBoolean: + return value + case valueObject: + return value._object().DefaultValue(hint) + } + panic(hereBeDragons(value.kind, value)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_string.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_string.go new file mode 100644 index 0000000..0fbfd6b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/robertkrimen/otto/value_string.go @@ -0,0 +1,103 @@ +package otto + +import ( + "fmt" + "math" + "regexp" + "strconv" + "unicode/utf16" +) + +var matchLeading0Exponent = regexp.MustCompile(`([eE][\+\-])0+([1-9])`) // 1e-07 => 1e-7 + +// FIXME +// https://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/conversions.cc?spec=svn18082&r=18082 +func floatToString(value float64, bitsize int) string { + // TODO Fit to ECMA-262 9.8.1 specification + if math.IsNaN(value) { + return "NaN" + } else if math.IsInf(value, 0) { + if math.Signbit(value) { + return "-Infinity" + } + return "Infinity" + } + exponent := math.Log10(math.Abs(value)) + if exponent >= 21 || exponent < -6 { + return matchLeading0Exponent.ReplaceAllString(strconv.FormatFloat(value, 'g', -1, bitsize), "$1$2") + } + return strconv.FormatFloat(value, 'f', -1, bitsize) +} + +func numberToStringRadix(value Value, radix int) string { + float := value.float64() + if math.IsNaN(float) { + return "NaN" + } else if math.IsInf(float, 1) { + return "Infinity" + } else if math.IsInf(float, -1) { + return "-Infinity" + } + // FIXME This is very broken + // Need to do proper radix conversion for floats, ... + // This truncates large floats (so bad). + return strconv.FormatInt(int64(float), radix) +} + +func (value Value) string() string { + if value.kind == valueString { + switch value := value.value.(type) { + case string: + return value + case []uint16: + return string(utf16.Decode(value)) + } + } + if value.IsUndefined() { + return "undefined" + } + if value.IsNull() { + return "null" + } + switch value := value.value.(type) { + case bool: + return strconv.FormatBool(value) + case int: + return strconv.FormatInt(int64(value), 10) + case int8: + return strconv.FormatInt(int64(value), 10) + case int16: + return strconv.FormatInt(int64(value), 10) + case int32: + return strconv.FormatInt(int64(value), 10) + case int64: + return strconv.FormatInt(value, 10) + case uint: + return strconv.FormatUint(uint64(value), 10) + case uint8: + return strconv.FormatUint(uint64(value), 10) + case uint16: + return strconv.FormatUint(uint64(value), 10) + case uint32: + return strconv.FormatUint(uint64(value), 10) + case uint64: + return strconv.FormatUint(value, 10) + case float32: + if value == 0 { + return "0" // Take care not to return -0 + } + return floatToString(float64(value), 32) + case float64: + if value == 0 { + return "0" // Take care not to return -0 + } + return floatToString(value, 64) + case []uint16: + return string(utf16.Decode(value)) + case string: + return value + case *_object: + return value.DefaultValue(defaultValueHintString).string() + } + panic(fmt.Errorf("%v.string( %T)", value.value, value.value)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/LICENSE new file mode 100644 index 0000000..d8e2df5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Olivier Poitrey + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/README.md new file mode 100644 index 0000000..4bf5672 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/README.md @@ -0,0 +1,99 @@ +# Go CORS handler [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/cors) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![build](https://img.shields.io/travis/rs/cors.svg?style=flat)](https://travis-ci.org/rs/cors) [![Coverage](http://gocover.io/_badge/github.com/rs/cors)](http://gocover.io/github.com/rs/cors) + +CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang. + +## Getting Started + +After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`. + +```go +package main + +import ( + "net/http" + + "github.com/rs/cors" +) + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte("{\"hello\": \"world\"}")) + }) + + // cors.Default() setup the middleware with default options being + // all origins accepted with simple methods (GET, POST). See + // documentation below for more options. + handler := cors.Default().Handler(mux) + http.ListenAndServe(":8080", handler) +} +``` + +Install `cors`: + + go get github.com/rs/cors + +Then run your server: + + go run server.go + +The server now runs on `localhost:8080`: + + $ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/ + HTTP/1.1 200 OK + Access-Control-Allow-Origin: foo.com + Content-Type: application/json + Date: Sat, 25 Oct 2014 03:43:57 GMT + Content-Length: 18 + + {"hello": "world"} + +### More Examples + +* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go) +* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go) +* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go) +* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go) +* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go) + +## Parameters + +Parameters are passed to the middleware thru the `cors.New` method as follow: + +```go +c := cors.New(cors.Options{ + AllowedOrigins: []string{"http://foo.com"}, + AllowCredentials: true, +}) + +// Insert the middleware +handler = c.Handler(handler) +``` + +* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. An origin may contain a wildcard (`*`) to replace 0 or more characters (i.e.: `http://*.domain.com`). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin. The default value is `*`. +* **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It take the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored +* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`). +* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. +* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification +* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`. +* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age. +* **OptionsPassthrough** `bool`: Instructs preflight to let other potential next handlers to process the `OPTIONS` method. Turn this on if your application handles `OPTIONS`. +* **Debug** `bool`: Debugging flag adds additional output to debug server side CORS issues. + +See [API documentation](http://godoc.org/github.com/rs/cors) for more info. + +## Benchmarks + + BenchmarkWithout 20000000 64.6 ns/op 8 B/op 1 allocs/op + BenchmarkDefault 3000000 469 ns/op 114 B/op 2 allocs/op + BenchmarkAllowedOrigin 3000000 608 ns/op 114 B/op 2 allocs/op + BenchmarkPreflight 20000000 73.2 ns/op 0 B/op 0 allocs/op + BenchmarkPreflightHeader 20000000 73.6 ns/op 0 B/op 0 allocs/op + BenchmarkParseHeaderList 2000000 847 ns/op 184 B/op 6 allocs/op + BenchmarkParse…Single 5000000 290 ns/op 32 B/op 3 allocs/op + BenchmarkParse…Normalized 2000000 776 ns/op 160 B/op 6 allocs/op + +## Licenses + +All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE). diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/cors.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/cors.go new file mode 100644 index 0000000..4bb22d8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/cors.go @@ -0,0 +1,412 @@ +/* +Package cors is net/http handler to handle CORS related requests +as defined by http://www.w3.org/TR/cors/ + +You can configure it by passing an option struct to cors.New: + + c := cors.New(cors.Options{ + AllowedOrigins: []string{"foo.com"}, + AllowedMethods: []string{"GET", "POST", "DELETE"}, + AllowCredentials: true, + }) + +Then insert the handler in the chain: + + handler = c.Handler(handler) + +See Options documentation for more options. + +The resulting handler is a standard net/http handler. +*/ +package cors + +import ( + "log" + "net/http" + "os" + "strconv" + "strings" + + "github.com/rs/xhandler" + "golang.org/x/net/context" +) + +// Options is a configuration container to setup the CORS middleware. +type Options struct { + // AllowedOrigins is a list of origins a cross-domain request can be executed from. + // If the special "*" value is present in the list, all origins will be allowed. + // An origin may contain a wildcard (*) to replace 0 or more characters + // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. + // Only one wildcard can be used per origin. + // Default value is ["*"] + AllowedOrigins []string + // AllowOriginFunc is a custom function to validate the origin. It take the origin + // as argument and returns true if allowed or false otherwise. If this option is + // set, the content of AllowedOrigins is ignored. + AllowOriginFunc func(origin string) bool + // AllowedMethods is a list of methods the client is allowed to use with + // cross-domain requests. Default value is simple methods (GET and POST) + AllowedMethods []string + // AllowedHeaders is list of non simple headers the client is allowed to use with + // cross-domain requests. + // If the special "*" value is present in the list, all headers will be allowed. + // Default value is [] but "Origin" is always appended to the list. + AllowedHeaders []string + // ExposedHeaders indicates which headers are safe to expose to the API of a CORS + // API specification + ExposedHeaders []string + // AllowCredentials indicates whether the request can include user credentials like + // cookies, HTTP authentication or client side SSL certificates. + AllowCredentials bool + // MaxAge indicates how long (in seconds) the results of a preflight request + // can be cached + MaxAge int + // OptionsPassthrough instructs preflight to let other potential next handlers to + // process the OPTIONS method. Turn this on if your application handles OPTIONS. + OptionsPassthrough bool + // Debugging flag adds additional output to debug server side CORS issues + Debug bool +} + +// Cors http handler +type Cors struct { + // Debug logger + Log *log.Logger + // Set to true when allowed origins contains a "*" + allowedOriginsAll bool + // Normalized list of plain allowed origins + allowedOrigins []string + // List of allowed origins containing wildcards + allowedWOrigins []wildcard + // Optional origin validator function + allowOriginFunc func(origin string) bool + // Set to true when allowed headers contains a "*" + allowedHeadersAll bool + // Normalized list of allowed headers + allowedHeaders []string + // Normalized list of allowed methods + allowedMethods []string + // Normalized list of exposed headers + exposedHeaders []string + allowCredentials bool + maxAge int + optionPassthrough bool +} + +// New creates a new Cors handler with the provided options. +func New(options Options) *Cors { + c := &Cors{ + exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), + allowOriginFunc: options.AllowOriginFunc, + allowCredentials: options.AllowCredentials, + maxAge: options.MaxAge, + optionPassthrough: options.OptionsPassthrough, + } + if options.Debug { + c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags) + } + + // Normalize options + // Note: for origins and methods matching, the spec requires a case-sensitive matching. + // As it may error prone, we chose to ignore the spec here. + + // Allowed Origins + if len(options.AllowedOrigins) == 0 { + // Default is all origins + c.allowedOriginsAll = true + } else { + c.allowedOrigins = []string{} + c.allowedWOrigins = []wildcard{} + for _, origin := range options.AllowedOrigins { + // Normalize + origin = strings.ToLower(origin) + if origin == "*" { + // If "*" is present in the list, turn the whole list into a match all + c.allowedOriginsAll = true + c.allowedOrigins = nil + c.allowedWOrigins = nil + break + } else if i := strings.IndexByte(origin, '*'); i >= 0 { + // Split the origin in two: start and end string without the * + w := wildcard{origin[0:i], origin[i+1 : len(origin)]} + c.allowedWOrigins = append(c.allowedWOrigins, w) + } else { + c.allowedOrigins = append(c.allowedOrigins, origin) + } + } + } + + // Allowed Headers + if len(options.AllowedHeaders) == 0 { + // Use sensible defaults + c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"} + } else { + // Origin is always appended as some browsers will always request for this header at preflight + c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey) + for _, h := range options.AllowedHeaders { + if h == "*" { + c.allowedHeadersAll = true + c.allowedHeaders = nil + break + } + } + } + + // Allowed Methods + if len(options.AllowedMethods) == 0 { + // Default is spec's "simple" methods + c.allowedMethods = []string{"GET", "POST"} + } else { + c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper) + } + + return c +} + +// Default creates a new Cors handler with default options +func Default() *Cors { + return New(Options{}) +} + +// Handler apply the CORS specification on the request, and add relevant CORS headers +// as necessary. +func (c *Cors) Handler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "OPTIONS" { + c.logf("Handler: Preflight request") + c.handlePreflight(w, r) + // Preflight requests are standalone and should stop the chain as some other + // middleware may not handle OPTIONS requests correctly. One typical example + // is authentication middleware ; OPTIONS requests won't carry authentication + // headers (see #1) + if c.optionPassthrough { + h.ServeHTTP(w, r) + } else { + w.WriteHeader(http.StatusOK) + } + } else { + c.logf("Handler: Actual request") + c.handleActualRequest(w, r) + h.ServeHTTP(w, r) + } + }) +} + +// HandlerC is net/context aware handler +func (c *Cors) HandlerC(h xhandler.HandlerC) xhandler.HandlerC { + return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + if r.Method == "OPTIONS" { + c.logf("Handler: Preflight request") + c.handlePreflight(w, r) + // Preflight requests are standalone and should stop the chain as some other + // middleware may not handle OPTIONS requests correctly. One typical example + // is authentication middleware ; OPTIONS requests won't carry authentication + // headers (see #1) + if c.optionPassthrough { + h.ServeHTTPC(ctx, w, r) + } else { + w.WriteHeader(http.StatusOK) + } + } else { + c.logf("Handler: Actual request") + c.handleActualRequest(w, r) + h.ServeHTTPC(ctx, w, r) + } + }) +} + +// HandlerFunc provides Martini compatible handler +func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { + if r.Method == "OPTIONS" { + c.logf("HandlerFunc: Preflight request") + c.handlePreflight(w, r) + } else { + c.logf("HandlerFunc: Actual request") + c.handleActualRequest(w, r) + } +} + +// Negroni compatible interface +func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if r.Method == "OPTIONS" { + c.logf("ServeHTTP: Preflight request") + c.handlePreflight(w, r) + // Preflight requests are standalone and should stop the chain as some other + // middleware may not handle OPTIONS requests correctly. One typical example + // is authentication middleware ; OPTIONS requests won't carry authentication + // headers (see #1) + if c.optionPassthrough { + next(w, r) + } else { + w.WriteHeader(http.StatusOK) + } + } else { + c.logf("ServeHTTP: Actual request") + c.handleActualRequest(w, r) + next(w, r) + } +} + +// handlePreflight handles pre-flight CORS requests +func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + origin := r.Header.Get("Origin") + + if r.Method != "OPTIONS" { + c.logf(" Preflight aborted: %s!=OPTIONS", r.Method) + return + } + // Always set Vary headers + // see https://github.com/rs/cors/issues/10, + // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001 + headers.Add("Vary", "Origin") + headers.Add("Vary", "Access-Control-Request-Method") + headers.Add("Vary", "Access-Control-Request-Headers") + + if origin == "" { + c.logf(" Preflight aborted: empty origin") + return + } + if !c.isOriginAllowed(origin) { + c.logf(" Preflight aborted: origin '%s' not allowed", origin) + return + } + + reqMethod := r.Header.Get("Access-Control-Request-Method") + if !c.isMethodAllowed(reqMethod) { + c.logf(" Preflight aborted: method '%s' not allowed", reqMethod) + return + } + reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) + if !c.areHeadersAllowed(reqHeaders) { + c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders) + return + } + headers.Set("Access-Control-Allow-Origin", origin) + // Spec says: Since the list of methods can be unbounded, simply returning the method indicated + // by Access-Control-Request-Method (if supported) can be enough + headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) + if len(reqHeaders) > 0 { + + // Spec says: Since the list of headers can be unbounded, simply returning supported headers + // from Access-Control-Request-Headers can be enough + headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", ")) + } + if c.allowCredentials { + headers.Set("Access-Control-Allow-Credentials", "true") + } + if c.maxAge > 0 { + headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge)) + } + c.logf(" Preflight response headers: %v", headers) +} + +// handleActualRequest handles simple cross-origin requests, actual request or redirects +func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + origin := r.Header.Get("Origin") + + if r.Method == "OPTIONS" { + c.logf(" Actual request no headers added: method == %s", r.Method) + return + } + // Always set Vary, see https://github.com/rs/cors/issues/10 + headers.Add("Vary", "Origin") + if origin == "" { + c.logf(" Actual request no headers added: missing origin") + return + } + if !c.isOriginAllowed(origin) { + c.logf(" Actual request no headers added: origin '%s' not allowed", origin) + return + } + + // Note that spec does define a way to specifically disallow a simple method like GET or + // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the + // spec doesn't instruct to check the allowed methods for simple cross-origin requests. + // We think it's a nice feature to be able to have control on those methods though. + if !c.isMethodAllowed(r.Method) { + c.logf(" Actual request no headers added: method '%s' not allowed", r.Method) + + return + } + headers.Set("Access-Control-Allow-Origin", origin) + if len(c.exposedHeaders) > 0 { + headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", ")) + } + if c.allowCredentials { + headers.Set("Access-Control-Allow-Credentials", "true") + } + c.logf(" Actual response added headers: %v", headers) +} + +// convenience method. checks if debugging is turned on before printing +func (c *Cors) logf(format string, a ...interface{}) { + if c.Log != nil { + c.Log.Printf(format, a...) + } +} + +// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests +// on the endpoint +func (c *Cors) isOriginAllowed(origin string) bool { + if c.allowOriginFunc != nil { + return c.allowOriginFunc(origin) + } + if c.allowedOriginsAll { + return true + } + origin = strings.ToLower(origin) + for _, o := range c.allowedOrigins { + if o == origin { + return true + } + } + for _, w := range c.allowedWOrigins { + if w.match(origin) { + return true + } + } + return false +} + +// isMethodAllowed checks if a given method can be used as part of a cross-domain request +// on the endpoing +func (c *Cors) isMethodAllowed(method string) bool { + if len(c.allowedMethods) == 0 { + // If no method allowed, always return false, even for preflight request + return false + } + method = strings.ToUpper(method) + if method == "OPTIONS" { + // Always allow preflight requests + return true + } + for _, m := range c.allowedMethods { + if m == method { + return true + } + } + return false +} + +// areHeadersAllowed checks if a given list of headers are allowed to used within +// a cross-domain request. +func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool { + if c.allowedHeadersAll || len(requestedHeaders) == 0 { + return true + } + for _, header := range requestedHeaders { + header = http.CanonicalHeaderKey(header) + found := false + for _, h := range c.allowedHeaders { + if h == header { + found = true + } + } + if !found { + return false + } + } + return true +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/utils.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/utils.go new file mode 100644 index 0000000..c7a0aa0 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/cors/utils.go @@ -0,0 +1,70 @@ +package cors + +import "strings" + +const toLower = 'a' - 'A' + +type converter func(string) string + +type wildcard struct { + prefix string + suffix string +} + +func (w wildcard) match(s string) bool { + return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix) +} + +// convert converts a list of string using the passed converter function +func convert(s []string, c converter) []string { + out := []string{} + for _, i := range s { + out = append(out, c(i)) + } + return out +} + +// parseHeaderList tokenize + normalize a string containing a list of headers +func parseHeaderList(headerList string) []string { + l := len(headerList) + h := make([]byte, 0, l) + upper := true + // Estimate the number headers in order to allocate the right splice size + t := 0 + for i := 0; i < l; i++ { + if headerList[i] == ',' { + t++ + } + } + headers := make([]string, 0, t) + for i := 0; i < l; i++ { + b := headerList[i] + if b >= 'a' && b <= 'z' { + if upper { + h = append(h, b-toLower) + } else { + h = append(h, b) + } + } else if b >= 'A' && b <= 'Z' { + if !upper { + h = append(h, b+toLower) + } else { + h = append(h, b) + } + } else if b == '-' || b == '_' || (b >= '0' && b <= '9') { + h = append(h, b) + } + + if b == ' ' || b == ',' || i == l-1 { + if len(h) > 0 { + // Flush the found header + headers = append(headers, string(h)) + h = h[:0] + upper = true + } + } else { + upper = b == '-' || b == '_' + } + } + return headers +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/LICENSE new file mode 100644 index 0000000..47c5e9d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Olivier Poitrey + +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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/README.md new file mode 100644 index 0000000..91c594b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/README.md @@ -0,0 +1,134 @@ +# XHandler + +[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xhandler) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xhandler/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xhandler.svg?branch=master)](https://travis-ci.org/rs/xhandler) [![Coverage](http://gocover.io/_badge/github.com/rs/xhandler)](http://gocover.io/github.com/rs/xhandler) + +XHandler is a bridge between [net/context](https://godoc.org/golang.org/x/net/context) and `http.Handler`. + +It lets you enforce `net/context` in your handlers without sacrificing compatibility with existing `http.Handlers` nor imposing a specific router. + +Thanks to `net/context` deadline management, `xhandler` is able to enforce a per request deadline and will cancel the context when the client closes the connection unexpectedly. + +You may create your own `net/context` aware handler pretty much the same way as you would do with http.Handler. + +Read more about xhandler on [Dailymotion engineering blog](http://engineering.dailymotion.com/our-way-to-go/). + +## Installing + + go get -u github.com/rs/xhandler + +## Usage + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/rs/cors" + "github.com/rs/xhandler" + "golang.org/x/net/context" +) + +type myMiddleware struct { + next xhandler.HandlerC +} + +func (h myMiddleware) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { + ctx = context.WithValue(ctx, "test", "World") + h.next.ServeHTTPC(ctx, w, r) +} + +func main() { + c := xhandler.Chain{} + + // Add close notifier handler so context is cancelled when the client closes + // the connection + c.UseC(xhandler.CloseHandler) + + // Add timeout handler + c.UseC(xhandler.TimeoutHandler(2 * time.Second)) + + // Middleware putting something in the context + c.UseC(func(next xhandler.HandlerC) xhandler.HandlerC { + return myMiddleware{next: next} + }) + + // Mix it with a non-context-aware middleware handler + c.Use(cors.Default().Handler) + + // Final handler (using handlerFuncC), reading from the context + xh := xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + value := ctx.Value("test").(string) + w.Write([]byte("Hello " + value)) + }) + + // Bridge context aware handlers with http.Handler using xhandler.Handle() + http.Handle("/test", c.Handler(xh)) + + if err := http.ListenAndServe(":8080", nil); err != nil { + log.Fatal(err) + } +} +``` + +### Using xmux + +Xhandler comes with an optional context aware [muxer](https://github.com/rs/xmux) forked from [httprouter](https://github.com/julienschmidt/httprouter): + +```go +package main + +import ( + "fmt" + "log" + "net/http" + "time" + + "github.com/rs/xhandler" + "github.com/rs/xmux" + "golang.org/x/net/context" +) + +func main() { + c := xhandler.Chain{} + + // Append a context-aware middleware handler + c.UseC(xhandler.CloseHandler) + + // Another context-aware middleware handler + c.UseC(xhandler.TimeoutHandler(2 * time.Second)) + + mux := xmux.New() + + // Use c.Handler to terminate the chain with your final handler + mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name")) + })) + + if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil { + log.Fatal(err) + } +} +``` + +See [xmux](https://github.com/rs/xmux) for more examples. + +## Context Aware Middleware + +Here is a list of `net/context` aware middleware handlers implementing `xhandler.HandlerC` interface. + +Feel free to put up a PR linking your middleware if you have built one: + +| Middleware | Author | Description | +| ---------- | ------ | ----------- | +| [xmux](https://github.com/rs/xmux) | [Olivier Poitrey](https://github.com/rs) | HTTP request muxer | +| [xlog](https://github.com/rs/xlog) | [Olivier Poitrey](https://github.com/rs) | HTTP handler logger | +| [xstats](https://github.com/rs/xstats) | [Olivier Poitrey](https://github.com/rs) | A generic client for service instrumentation | +| [xaccess](https://github.com/rs/xaccess) | [Olivier Poitrey](https://github.com/rs) | HTTP handler access logger with [xlog](https://github.com/rs/xlog) and [xstats](https://github.com/rs/xstats) | +| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support | + +## Licenses + +All source code is licensed under the [MIT License](https://raw.github.com/rs/xhandler/master/LICENSE). diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/chain.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/chain.go new file mode 100644 index 0000000..3e4bd35 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/chain.go @@ -0,0 +1,121 @@ +package xhandler + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// Chain is a helper for chaining middleware handlers together for easier +// management. +type Chain []func(next HandlerC) HandlerC + +// Add appends a variable number of additional middleware handlers +// to the middleware chain. Middleware handlers can either be +// context-aware or non-context aware handlers with the appropriate +// function signatures. +func (c *Chain) Add(f ...interface{}) { + for _, h := range f { + switch v := h.(type) { + case func(http.Handler) http.Handler: + c.Use(v) + case func(HandlerC) HandlerC: + c.UseC(v) + default: + panic("Adding invalid handler to the middleware chain") + } + } +} + +// With creates a new middleware chain from an existing chain, +// extending it with additional middleware. Middleware handlers +// can either be context-aware or non-context aware handlers +// with the appropriate function signatures. +func (c *Chain) With(f ...interface{}) *Chain { + n := make(Chain, len(*c)) + copy(n, *c) + n.Add(f...) + return &n +} + +// UseC appends a context-aware handler to the middleware chain. +func (c *Chain) UseC(f func(next HandlerC) HandlerC) { + *c = append(*c, f) +} + +// Use appends a standard http.Handler to the middleware chain without +// losing track of the context when inserted between two context aware handlers. +// +// Caveat: the f function will be called on each request so you are better off putting +// any initialization sequence outside of this function. +func (c *Chain) Use(f func(next http.Handler) http.Handler) { + xf := func(next HandlerC) HandlerC { + return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + n := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + next.ServeHTTPC(ctx, w, r) + }) + f(n).ServeHTTP(w, r) + }) + } + *c = append(*c, xf) +} + +// Handler wraps the provided final handler with all the middleware appended to +// the chain and returns a new standard http.Handler instance. +// The context.Background() context is injected automatically. +func (c Chain) Handler(xh HandlerC) http.Handler { + ctx := context.Background() + return c.HandlerCtx(ctx, xh) +} + +// HandlerFC is a helper to provide a function (HandlerFuncC) to Handler(). +// +// HandlerFC is equivalent to: +// c.Handler(xhandler.HandlerFuncC(xhc)) +func (c Chain) HandlerFC(xhf HandlerFuncC) http.Handler { + ctx := context.Background() + return c.HandlerCtx(ctx, HandlerFuncC(xhf)) +} + +// HandlerH is a helper to provide a standard http handler (http.HandlerFunc) +// to Handler(). Your final handler won't have access to the context though. +func (c Chain) HandlerH(h http.Handler) http.Handler { + ctx := context.Background() + return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + h.ServeHTTP(w, r) + })) +} + +// HandlerF is a helper to provide a standard http handler function +// (http.HandlerFunc) to Handler(). Your final handler won't have access +// to the context though. +func (c Chain) HandlerF(hf http.HandlerFunc) http.Handler { + ctx := context.Background() + return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + hf(w, r) + })) +} + +// HandlerCtx wraps the provided final handler with all the middleware appended to +// the chain and returns a new standard http.Handler instance. +func (c Chain) HandlerCtx(ctx context.Context, xh HandlerC) http.Handler { + return New(ctx, c.HandlerC(xh)) +} + +// HandlerC wraps the provided final handler with all the middleware appended to +// the chain and returns a HandlerC instance. +func (c Chain) HandlerC(xh HandlerC) HandlerC { + for i := len(c) - 1; i >= 0; i-- { + xh = c[i](xh) + } + return xh +} + +// HandlerCF wraps the provided final handler func with all the middleware appended to +// the chain and returns a HandlerC instance. +// +// HandlerCF is equivalent to: +// c.HandlerC(xhandler.HandlerFuncC(xhc)) +func (c Chain) HandlerCF(xhc HandlerFuncC) HandlerC { + return c.HandlerC(HandlerFuncC(xhc)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/middleware.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/middleware.go new file mode 100644 index 0000000..7ad8fba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/middleware.go @@ -0,0 +1,59 @@ +package xhandler + +import ( + "net/http" + "time" + + "golang.org/x/net/context" +) + +// CloseHandler returns a Handler, cancelling the context when the client +// connection closes unexpectedly. +func CloseHandler(next HandlerC) HandlerC { + return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + // Cancel the context if the client closes the connection + if wcn, ok := w.(http.CloseNotifier); ok { + var cancel context.CancelFunc + ctx, cancel = context.WithCancel(ctx) + defer cancel() + + notify := wcn.CloseNotify() + go func() { + select { + case <-notify: + cancel() + case <-ctx.Done(): + } + }() + } + + next.ServeHTTPC(ctx, w, r) + }) +} + +// TimeoutHandler returns a Handler which adds a timeout to the context. +// +// Child handlers have the responsability of obeying the context deadline and to return +// an appropriate error (or not) response in case of timeout. +func TimeoutHandler(timeout time.Duration) func(next HandlerC) HandlerC { + return func(next HandlerC) HandlerC { + return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + ctx, _ = context.WithTimeout(ctx, timeout) + next.ServeHTTPC(ctx, w, r) + }) + } +} + +// If is a special handler that will skip insert the condNext handler only if a condition +// applies at runtime. +func If(cond func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool, condNext func(next HandlerC) HandlerC) func(next HandlerC) HandlerC { + return func(next HandlerC) HandlerC { + return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { + if cond(ctx, w, r) { + condNext(next).ServeHTTPC(ctx, w, r) + } else { + next.ServeHTTPC(ctx, w, r) + } + }) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/xhandler.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/xhandler.go new file mode 100644 index 0000000..bc832cb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/rs/xhandler/xhandler.go @@ -0,0 +1,42 @@ +// Package xhandler provides a bridge between http.Handler and net/context. +// +// xhandler enforces net/context in your handlers without sacrificing +// compatibility with existing http.Handlers nor imposing a specific router. +// +// Thanks to net/context deadline management, xhandler is able to enforce +// a per request deadline and will cancel the context in when the client close +// the connection unexpectedly. +// +// You may create net/context aware middlewares pretty much the same way as +// you would with http.Handler. +package xhandler // import "github.com/rs/xhandler" + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// HandlerC is a net/context aware http.Handler +type HandlerC interface { + ServeHTTPC(context.Context, http.ResponseWriter, *http.Request) +} + +// HandlerFuncC type is an adapter to allow the use of ordinary functions +// as an xhandler.Handler. If f is a function with the appropriate signature, +// xhandler.HandlerFuncC(f) is a xhandler.Handler object that calls f. +type HandlerFuncC func(context.Context, http.ResponseWriter, *http.Request) + +// ServeHTTPC calls f(ctx, w, r). +func (f HandlerFuncC) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { + f(ctx, w, r) +} + +// New creates a conventional http.Handler injecting the provided root +// context to sub handlers. This handler is used as a bridge between conventional +// http.Handler and context aware handlers. +func New(ctx context.Context, h HandlerC) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h.ServeHTTPC(ctx, w, r) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/.travis.yml b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/.travis.yml new file mode 100644 index 0000000..82de377 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - tip + +script: + - go test -timeout 1h ./... + - go test -timeout 30m -race -run "TestDB_(Concurrent|GoleveldbIssue74)" ./leveldb diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/LICENSE new file mode 100644 index 0000000..4a772d1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/LICENSE @@ -0,0 +1,24 @@ +Copyright 2012 Suryandaru Triandana +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. + +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 THE COPYRIGHT +HOLDER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/README.md b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/README.md new file mode 100644 index 0000000..259286f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/README.md @@ -0,0 +1,105 @@ +This is an implementation of the [LevelDB key/value database](http:code.google.com/p/leveldb) in the [Go programming language](http:golang.org). + +[![Build Status](https://travis-ci.org/syndtr/goleveldb.png?branch=master)](https://travis-ci.org/syndtr/goleveldb) + +Installation +----------- + + go get github.com/syndtr/goleveldb/leveldb + +Requirements +----------- + +* Need at least `go1.4` or newer. + +Usage +----------- + +Create or open a database: +```go +db, err := leveldb.OpenFile("path/to/db", nil) +... +defer db.Close() +... +``` +Read or modify the database content: +```go +// Remember that the contents of the returned slice should not be modified. +data, err := db.Get([]byte("key"), nil) +... +err = db.Put([]byte("key"), []byte("value"), nil) +... +err = db.Delete([]byte("key"), nil) +... +``` + +Iterate over database content: +```go +iter := db.NewIterator(nil, nil) +for iter.Next() { + // Remember that the contents of the returned slice should not be modified, and + // only valid until the next call to Next. + key := iter.Key() + value := iter.Value() + ... +} +iter.Release() +err = iter.Error() +... +``` +Seek-then-Iterate: +```go +iter := db.NewIterator(nil, nil) +for ok := iter.Seek(key); ok; ok = iter.Next() { + // Use key/value. + ... +} +iter.Release() +err = iter.Error() +... +``` +Iterate over subset of database content: +```go +iter := db.NewIterator(&util.Range{Start: []byte("foo"), Limit: []byte("xoo")}, nil) +for iter.Next() { + // Use key/value. + ... +} +iter.Release() +err = iter.Error() +... +``` +Iterate over subset of database content with a particular prefix: +```go +iter := db.NewIterator(util.BytesPrefix([]byte("foo-")), nil) +for iter.Next() { + // Use key/value. + ... +} +iter.Release() +err = iter.Error() +... +``` +Batch writes: +```go +batch := new(leveldb.Batch) +batch.Put([]byte("foo"), []byte("value")) +batch.Put([]byte("bar"), []byte("another value")) +batch.Delete([]byte("baz")) +err = db.Write(batch, nil) +... +``` +Use bloom filter: +```go +o := &opt.Options{ + Filter: filter.NewBloomFilter(10), +} +db, err := leveldb.OpenFile("path/to/db", o) +... +defer db.Close() +... +``` +Documentation +----------- + +You can read package documentation [here](http:godoc.org/github.com/syndtr/goleveldb). diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/batch.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/batch.go new file mode 100644 index 0000000..2259200 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/batch.go @@ -0,0 +1,349 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/memdb" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +// ErrBatchCorrupted records reason of batch corruption. This error will be +// wrapped with errors.ErrCorrupted. +type ErrBatchCorrupted struct { + Reason string +} + +func (e *ErrBatchCorrupted) Error() string { + return fmt.Sprintf("leveldb: batch corrupted: %s", e.Reason) +} + +func newErrBatchCorrupted(reason string) error { + return errors.NewErrCorrupted(storage.FileDesc{}, &ErrBatchCorrupted{reason}) +} + +const ( + batchHeaderLen = 8 + 4 + batchGrowRec = 3000 + batchBufioSize = 16 +) + +// BatchReplay wraps basic batch operations. +type BatchReplay interface { + Put(key, value []byte) + Delete(key []byte) +} + +type batchIndex struct { + keyType keyType + keyPos, keyLen int + valuePos, valueLen int +} + +func (index batchIndex) k(data []byte) []byte { + return data[index.keyPos : index.keyPos+index.keyLen] +} + +func (index batchIndex) v(data []byte) []byte { + if index.valueLen != 0 { + return data[index.valuePos : index.valuePos+index.valueLen] + } + return nil +} + +func (index batchIndex) kv(data []byte) (key, value []byte) { + return index.k(data), index.v(data) +} + +// Batch is a write batch. +type Batch struct { + data []byte + index []batchIndex + + // internalLen is sums of key/value pair length plus 8-bytes internal key. + internalLen int +} + +func (b *Batch) grow(n int) { + o := len(b.data) + if cap(b.data)-o < n { + div := 1 + if len(b.index) > batchGrowRec { + div = len(b.index) / batchGrowRec + } + ndata := make([]byte, o, o+n+o/div) + copy(ndata, b.data) + b.data = ndata + } +} + +func (b *Batch) appendRec(kt keyType, key, value []byte) { + n := 1 + binary.MaxVarintLen32 + len(key) + if kt == keyTypeVal { + n += binary.MaxVarintLen32 + len(value) + } + b.grow(n) + index := batchIndex{keyType: kt} + o := len(b.data) + data := b.data[:o+n] + data[o] = byte(kt) + o++ + o += binary.PutUvarint(data[o:], uint64(len(key))) + index.keyPos = o + index.keyLen = len(key) + o += copy(data[o:], key) + if kt == keyTypeVal { + o += binary.PutUvarint(data[o:], uint64(len(value))) + index.valuePos = o + index.valueLen = len(value) + o += copy(data[o:], value) + } + b.data = data[:o] + b.index = append(b.index, index) + b.internalLen += index.keyLen + index.valueLen + 8 +} + +// Put appends 'put operation' of the given key/value pair to the batch. +// It is safe to modify the contents of the argument after Put returns but not +// before. +func (b *Batch) Put(key, value []byte) { + b.appendRec(keyTypeVal, key, value) +} + +// Delete appends 'delete operation' of the given key to the batch. +// It is safe to modify the contents of the argument after Delete returns but +// not before. +func (b *Batch) Delete(key []byte) { + b.appendRec(keyTypeDel, key, nil) +} + +// Dump dumps batch contents. The returned slice can be loaded into the +// batch using Load method. +// The returned slice is not its own copy, so the contents should not be +// modified. +func (b *Batch) Dump() []byte { + return b.data +} + +// Load loads given slice into the batch. Previous contents of the batch +// will be discarded. +// The given slice will not be copied and will be used as batch buffer, so +// it is not safe to modify the contents of the slice. +func (b *Batch) Load(data []byte) error { + return b.decode(data, -1) +} + +// Replay replays batch contents. +func (b *Batch) Replay(r BatchReplay) error { + for _, index := range b.index { + switch index.keyType { + case keyTypeVal: + r.Put(index.k(b.data), index.v(b.data)) + case keyTypeDel: + r.Delete(index.k(b.data)) + } + } + return nil +} + +// Len returns number of records in the batch. +func (b *Batch) Len() int { + return len(b.index) +} + +// Reset resets the batch. +func (b *Batch) Reset() { + b.data = b.data[:0] + b.index = b.index[:0] + b.internalLen = 0 +} + +func (b *Batch) replayInternal(fn func(i int, kt keyType, k, v []byte) error) error { + for i, index := range b.index { + if err := fn(i, index.keyType, index.k(b.data), index.v(b.data)); err != nil { + return err + } + } + return nil +} + +func (b *Batch) append(p *Batch) { + ob := len(b.data) + oi := len(b.index) + b.data = append(b.data, p.data...) + b.index = append(b.index, p.index...) + b.internalLen += p.internalLen + + // Updating index offset. + if ob != 0 { + for ; oi < len(b.index); oi++ { + index := &b.index[oi] + index.keyPos += ob + if index.valueLen != 0 { + index.valuePos += ob + } + } + } +} + +func (b *Batch) decode(data []byte, expectedLen int) error { + b.data = data + b.index = b.index[:0] + b.internalLen = 0 + err := decodeBatch(data, func(i int, index batchIndex) error { + b.index = append(b.index, index) + b.internalLen += index.keyLen + index.valueLen + 8 + return nil + }) + if err != nil { + return err + } + if expectedLen >= 0 && len(b.index) != expectedLen { + return newErrBatchCorrupted(fmt.Sprintf("invalid records length: %d vs %d", expectedLen, len(b.index))) + } + return nil +} + +func (b *Batch) putMem(seq uint64, mdb *memdb.DB) error { + var ik []byte + for i, index := range b.index { + ik = makeInternalKey(ik, index.k(b.data), seq+uint64(i), index.keyType) + if err := mdb.Put(ik, index.v(b.data)); err != nil { + return err + } + } + return nil +} + +func (b *Batch) revertMem(seq uint64, mdb *memdb.DB) error { + var ik []byte + for i, index := range b.index { + ik = makeInternalKey(ik, index.k(b.data), seq+uint64(i), index.keyType) + if err := mdb.Delete(ik); err != nil { + return err + } + } + return nil +} + +func newBatch() interface{} { + return &Batch{} +} + +func decodeBatch(data []byte, fn func(i int, index batchIndex) error) error { + var index batchIndex + for i, o := 0, 0; o < len(data); i++ { + // Key type. + index.keyType = keyType(data[o]) + if index.keyType > keyTypeVal { + return newErrBatchCorrupted(fmt.Sprintf("bad record: invalid type %#x", uint(index.keyType))) + } + o++ + + // Key. + x, n := binary.Uvarint(data[o:]) + o += n + if n <= 0 || o+int(x) > len(data) { + return newErrBatchCorrupted("bad record: invalid key length") + } + index.keyPos = o + index.keyLen = int(x) + o += index.keyLen + + // Value. + if index.keyType == keyTypeVal { + x, n = binary.Uvarint(data[o:]) + o += n + if n <= 0 || o+int(x) > len(data) { + return newErrBatchCorrupted("bad record: invalid value length") + } + index.valuePos = o + index.valueLen = int(x) + o += index.valueLen + } else { + index.valuePos = 0 + index.valueLen = 0 + } + + if err := fn(i, index); err != nil { + return err + } + } + return nil +} + +func decodeBatchToMem(data []byte, expectSeq uint64, mdb *memdb.DB) (seq uint64, batchLen int, err error) { + seq, batchLen, err = decodeBatchHeader(data) + if err != nil { + return 0, 0, err + } + if seq < expectSeq { + return 0, 0, newErrBatchCorrupted("invalid sequence number") + } + data = data[batchHeaderLen:] + var ik []byte + var decodedLen int + err = decodeBatch(data, func(i int, index batchIndex) error { + if i >= batchLen { + return newErrBatchCorrupted("invalid records length") + } + ik = makeInternalKey(ik, index.k(data), seq+uint64(i), index.keyType) + if err := mdb.Put(ik, index.v(data)); err != nil { + return err + } + decodedLen++ + return nil + }) + if err == nil && decodedLen != batchLen { + err = newErrBatchCorrupted(fmt.Sprintf("invalid records length: %d vs %d", batchLen, decodedLen)) + } + return +} + +func encodeBatchHeader(dst []byte, seq uint64, batchLen int) []byte { + dst = ensureBuffer(dst, batchHeaderLen) + binary.LittleEndian.PutUint64(dst, seq) + binary.LittleEndian.PutUint32(dst[8:], uint32(batchLen)) + return dst +} + +func decodeBatchHeader(data []byte) (seq uint64, batchLen int, err error) { + if len(data) < batchHeaderLen { + return 0, 0, newErrBatchCorrupted("too short") + } + + seq = binary.LittleEndian.Uint64(data) + batchLen = int(binary.LittleEndian.Uint32(data[8:])) + if batchLen < 0 { + return 0, 0, newErrBatchCorrupted("invalid records length") + } + return +} + +func batchesLen(batches []*Batch) int { + batchLen := 0 + for _, batch := range batches { + batchLen += batch.Len() + } + return batchLen +} + +func writeBatchesWithHeader(wr io.Writer, batches []*Batch, seq uint64) error { + if _, err := wr.Write(encodeBatchHeader(nil, seq, batchesLen(batches))); err != nil { + return err + } + for _, batch := range batches { + if _, err := wr.Write(batch.data); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go new file mode 100644 index 0000000..c5940b2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go @@ -0,0 +1,705 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package cache provides interface and implementation of a cache algorithms. +package cache + +import ( + "sync" + "sync/atomic" + "unsafe" + + "github.com/syndtr/goleveldb/leveldb/util" +) + +// Cacher provides interface to implements a caching functionality. +// An implementation must be safe for concurrent use. +type Cacher interface { + // Capacity returns cache capacity. + Capacity() int + + // SetCapacity sets cache capacity. + SetCapacity(capacity int) + + // Promote promotes the 'cache node'. + Promote(n *Node) + + // Ban evicts the 'cache node' and prevent subsequent 'promote'. + Ban(n *Node) + + // Evict evicts the 'cache node'. + Evict(n *Node) + + // EvictNS evicts 'cache node' with the given namespace. + EvictNS(ns uint64) + + // EvictAll evicts all 'cache node'. + EvictAll() + + // Close closes the 'cache tree' + Close() error +} + +// Value is a 'cacheable object'. It may implements util.Releaser, if +// so the the Release method will be called once object is released. +type Value interface{} + +// NamespaceGetter provides convenient wrapper for namespace. +type NamespaceGetter struct { + Cache *Cache + NS uint64 +} + +// Get simply calls Cache.Get() method. +func (g *NamespaceGetter) Get(key uint64, setFunc func() (size int, value Value)) *Handle { + return g.Cache.Get(g.NS, key, setFunc) +} + +// The hash tables implementation is based on: +// "Dynamic-Sized Nonblocking Hash Tables", by Yujie Liu, +// Kunlong Zhang, and Michael Spear. +// ACM Symposium on Principles of Distributed Computing, Jul 2014. + +const ( + mInitialSize = 1 << 4 + mOverflowThreshold = 1 << 5 + mOverflowGrowThreshold = 1 << 7 +) + +type mBucket struct { + mu sync.Mutex + node []*Node + frozen bool +} + +func (b *mBucket) freeze() []*Node { + b.mu.Lock() + defer b.mu.Unlock() + if !b.frozen { + b.frozen = true + } + return b.node +} + +func (b *mBucket) get(r *Cache, h *mNode, hash uint32, ns, key uint64, noset bool) (done, added bool, n *Node) { + b.mu.Lock() + + if b.frozen { + b.mu.Unlock() + return + } + + // Scan the node. + for _, n := range b.node { + if n.hash == hash && n.ns == ns && n.key == key { + atomic.AddInt32(&n.ref, 1) + b.mu.Unlock() + return true, false, n + } + } + + // Get only. + if noset { + b.mu.Unlock() + return true, false, nil + } + + // Create node. + n = &Node{ + r: r, + hash: hash, + ns: ns, + key: key, + ref: 1, + } + // Add node to bucket. + b.node = append(b.node, n) + bLen := len(b.node) + b.mu.Unlock() + + // Update counter. + grow := atomic.AddInt32(&r.nodes, 1) >= h.growThreshold + if bLen > mOverflowThreshold { + grow = grow || atomic.AddInt32(&h.overflow, 1) >= mOverflowGrowThreshold + } + + // Grow. + if grow && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) { + nhLen := len(h.buckets) << 1 + nh := &mNode{ + buckets: make([]unsafe.Pointer, nhLen), + mask: uint32(nhLen) - 1, + pred: unsafe.Pointer(h), + growThreshold: int32(nhLen * mOverflowThreshold), + shrinkThreshold: int32(nhLen >> 1), + } + ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh)) + if !ok { + panic("BUG: failed swapping head") + } + go nh.initBuckets() + } + + return true, true, n +} + +func (b *mBucket) delete(r *Cache, h *mNode, hash uint32, ns, key uint64) (done, deleted bool) { + b.mu.Lock() + + if b.frozen { + b.mu.Unlock() + return + } + + // Scan the node. + var ( + n *Node + bLen int + ) + for i := range b.node { + n = b.node[i] + if n.ns == ns && n.key == key { + if atomic.LoadInt32(&n.ref) == 0 { + deleted = true + + // Call releaser. + if n.value != nil { + if r, ok := n.value.(util.Releaser); ok { + r.Release() + } + n.value = nil + } + + // Remove node from bucket. + b.node = append(b.node[:i], b.node[i+1:]...) + bLen = len(b.node) + } + break + } + } + b.mu.Unlock() + + if deleted { + // Call OnDel. + for _, f := range n.onDel { + f() + } + + // Update counter. + atomic.AddInt32(&r.size, int32(n.size)*-1) + shrink := atomic.AddInt32(&r.nodes, -1) < h.shrinkThreshold + if bLen >= mOverflowThreshold { + atomic.AddInt32(&h.overflow, -1) + } + + // Shrink. + if shrink && len(h.buckets) > mInitialSize && atomic.CompareAndSwapInt32(&h.resizeInProgess, 0, 1) { + nhLen := len(h.buckets) >> 1 + nh := &mNode{ + buckets: make([]unsafe.Pointer, nhLen), + mask: uint32(nhLen) - 1, + pred: unsafe.Pointer(h), + growThreshold: int32(nhLen * mOverflowThreshold), + shrinkThreshold: int32(nhLen >> 1), + } + ok := atomic.CompareAndSwapPointer(&r.mHead, unsafe.Pointer(h), unsafe.Pointer(nh)) + if !ok { + panic("BUG: failed swapping head") + } + go nh.initBuckets() + } + } + + return true, deleted +} + +type mNode struct { + buckets []unsafe.Pointer // []*mBucket + mask uint32 + pred unsafe.Pointer // *mNode + resizeInProgess int32 + + overflow int32 + growThreshold int32 + shrinkThreshold int32 +} + +func (n *mNode) initBucket(i uint32) *mBucket { + if b := (*mBucket)(atomic.LoadPointer(&n.buckets[i])); b != nil { + return b + } + + p := (*mNode)(atomic.LoadPointer(&n.pred)) + if p != nil { + var node []*Node + if n.mask > p.mask { + // Grow. + pb := (*mBucket)(atomic.LoadPointer(&p.buckets[i&p.mask])) + if pb == nil { + pb = p.initBucket(i & p.mask) + } + m := pb.freeze() + // Split nodes. + for _, x := range m { + if x.hash&n.mask == i { + node = append(node, x) + } + } + } else { + // Shrink. + pb0 := (*mBucket)(atomic.LoadPointer(&p.buckets[i])) + if pb0 == nil { + pb0 = p.initBucket(i) + } + pb1 := (*mBucket)(atomic.LoadPointer(&p.buckets[i+uint32(len(n.buckets))])) + if pb1 == nil { + pb1 = p.initBucket(i + uint32(len(n.buckets))) + } + m0 := pb0.freeze() + m1 := pb1.freeze() + // Merge nodes. + node = make([]*Node, 0, len(m0)+len(m1)) + node = append(node, m0...) + node = append(node, m1...) + } + b := &mBucket{node: node} + if atomic.CompareAndSwapPointer(&n.buckets[i], nil, unsafe.Pointer(b)) { + if len(node) > mOverflowThreshold { + atomic.AddInt32(&n.overflow, int32(len(node)-mOverflowThreshold)) + } + return b + } + } + + return (*mBucket)(atomic.LoadPointer(&n.buckets[i])) +} + +func (n *mNode) initBuckets() { + for i := range n.buckets { + n.initBucket(uint32(i)) + } + atomic.StorePointer(&n.pred, nil) +} + +// Cache is a 'cache map'. +type Cache struct { + mu sync.RWMutex + mHead unsafe.Pointer // *mNode + nodes int32 + size int32 + cacher Cacher + closed bool +} + +// NewCache creates a new 'cache map'. The cacher is optional and +// may be nil. +func NewCache(cacher Cacher) *Cache { + h := &mNode{ + buckets: make([]unsafe.Pointer, mInitialSize), + mask: mInitialSize - 1, + growThreshold: int32(mInitialSize * mOverflowThreshold), + shrinkThreshold: 0, + } + for i := range h.buckets { + h.buckets[i] = unsafe.Pointer(&mBucket{}) + } + r := &Cache{ + mHead: unsafe.Pointer(h), + cacher: cacher, + } + return r +} + +func (r *Cache) getBucket(hash uint32) (*mNode, *mBucket) { + h := (*mNode)(atomic.LoadPointer(&r.mHead)) + i := hash & h.mask + b := (*mBucket)(atomic.LoadPointer(&h.buckets[i])) + if b == nil { + b = h.initBucket(i) + } + return h, b +} + +func (r *Cache) delete(n *Node) bool { + for { + h, b := r.getBucket(n.hash) + done, deleted := b.delete(r, h, n.hash, n.ns, n.key) + if done { + return deleted + } + } + return false +} + +// Nodes returns number of 'cache node' in the map. +func (r *Cache) Nodes() int { + return int(atomic.LoadInt32(&r.nodes)) +} + +// Size returns sums of 'cache node' size in the map. +func (r *Cache) Size() int { + return int(atomic.LoadInt32(&r.size)) +} + +// Capacity returns cache capacity. +func (r *Cache) Capacity() int { + if r.cacher == nil { + return 0 + } + return r.cacher.Capacity() +} + +// SetCapacity sets cache capacity. +func (r *Cache) SetCapacity(capacity int) { + if r.cacher != nil { + r.cacher.SetCapacity(capacity) + } +} + +// Get gets 'cache node' with the given namespace and key. +// If cache node is not found and setFunc is not nil, Get will atomically creates +// the 'cache node' by calling setFunc. Otherwise Get will returns nil. +// +// The returned 'cache handle' should be released after use by calling Release +// method. +func (r *Cache) Get(ns, key uint64, setFunc func() (size int, value Value)) *Handle { + r.mu.RLock() + defer r.mu.RUnlock() + if r.closed { + return nil + } + + hash := murmur32(ns, key, 0xf00) + for { + h, b := r.getBucket(hash) + done, _, n := b.get(r, h, hash, ns, key, setFunc == nil) + if done { + if n != nil { + n.mu.Lock() + if n.value == nil { + if setFunc == nil { + n.mu.Unlock() + n.unref() + return nil + } + + n.size, n.value = setFunc() + if n.value == nil { + n.size = 0 + n.mu.Unlock() + n.unref() + return nil + } + atomic.AddInt32(&r.size, int32(n.size)) + } + n.mu.Unlock() + if r.cacher != nil { + r.cacher.Promote(n) + } + return &Handle{unsafe.Pointer(n)} + } + + break + } + } + return nil +} + +// Delete removes and ban 'cache node' with the given namespace and key. +// A banned 'cache node' will never inserted into the 'cache tree'. Ban +// only attributed to the particular 'cache node', so when a 'cache node' +// is recreated it will not be banned. +// +// If onDel is not nil, then it will be executed if such 'cache node' +// doesn't exist or once the 'cache node' is released. +// +// Delete return true is such 'cache node' exist. +func (r *Cache) Delete(ns, key uint64, onDel func()) bool { + r.mu.RLock() + defer r.mu.RUnlock() + if r.closed { + return false + } + + hash := murmur32(ns, key, 0xf00) + for { + h, b := r.getBucket(hash) + done, _, n := b.get(r, h, hash, ns, key, true) + if done { + if n != nil { + if onDel != nil { + n.mu.Lock() + n.onDel = append(n.onDel, onDel) + n.mu.Unlock() + } + if r.cacher != nil { + r.cacher.Ban(n) + } + n.unref() + return true + } + + break + } + } + + if onDel != nil { + onDel() + } + + return false +} + +// Evict evicts 'cache node' with the given namespace and key. This will +// simply call Cacher.Evict. +// +// Evict return true is such 'cache node' exist. +func (r *Cache) Evict(ns, key uint64) bool { + r.mu.RLock() + defer r.mu.RUnlock() + if r.closed { + return false + } + + hash := murmur32(ns, key, 0xf00) + for { + h, b := r.getBucket(hash) + done, _, n := b.get(r, h, hash, ns, key, true) + if done { + if n != nil { + if r.cacher != nil { + r.cacher.Evict(n) + } + n.unref() + return true + } + + break + } + } + + return false +} + +// EvictNS evicts 'cache node' with the given namespace. This will +// simply call Cacher.EvictNS. +func (r *Cache) EvictNS(ns uint64) { + r.mu.RLock() + defer r.mu.RUnlock() + if r.closed { + return + } + + if r.cacher != nil { + r.cacher.EvictNS(ns) + } +} + +// EvictAll evicts all 'cache node'. This will simply call Cacher.EvictAll. +func (r *Cache) EvictAll() { + r.mu.RLock() + defer r.mu.RUnlock() + if r.closed { + return + } + + if r.cacher != nil { + r.cacher.EvictAll() + } +} + +// Close closes the 'cache map' and forcefully releases all 'cache node'. +func (r *Cache) Close() error { + r.mu.Lock() + if !r.closed { + r.closed = true + + h := (*mNode)(r.mHead) + h.initBuckets() + + for i := range h.buckets { + b := (*mBucket)(h.buckets[i]) + for _, n := range b.node { + // Call releaser. + if n.value != nil { + if r, ok := n.value.(util.Releaser); ok { + r.Release() + } + n.value = nil + } + + // Call OnDel. + for _, f := range n.onDel { + f() + } + n.onDel = nil + } + } + } + r.mu.Unlock() + + // Avoid deadlock. + if r.cacher != nil { + if err := r.cacher.Close(); err != nil { + return err + } + } + return nil +} + +// CloseWeak closes the 'cache map' and evict all 'cache node' from cacher, but +// unlike Close it doesn't forcefully releases 'cache node'. +func (r *Cache) CloseWeak() error { + r.mu.Lock() + if !r.closed { + r.closed = true + } + r.mu.Unlock() + + // Avoid deadlock. + if r.cacher != nil { + r.cacher.EvictAll() + if err := r.cacher.Close(); err != nil { + return err + } + } + return nil +} + +// Node is a 'cache node'. +type Node struct { + r *Cache + + hash uint32 + ns, key uint64 + + mu sync.Mutex + size int + value Value + + ref int32 + onDel []func() + + CacheData unsafe.Pointer +} + +// NS returns this 'cache node' namespace. +func (n *Node) NS() uint64 { + return n.ns +} + +// Key returns this 'cache node' key. +func (n *Node) Key() uint64 { + return n.key +} + +// Size returns this 'cache node' size. +func (n *Node) Size() int { + return n.size +} + +// Value returns this 'cache node' value. +func (n *Node) Value() Value { + return n.value +} + +// Ref returns this 'cache node' ref counter. +func (n *Node) Ref() int32 { + return atomic.LoadInt32(&n.ref) +} + +// GetHandle returns an handle for this 'cache node'. +func (n *Node) GetHandle() *Handle { + if atomic.AddInt32(&n.ref, 1) <= 1 { + panic("BUG: Node.GetHandle on zero ref") + } + return &Handle{unsafe.Pointer(n)} +} + +func (n *Node) unref() { + if atomic.AddInt32(&n.ref, -1) == 0 { + n.r.delete(n) + } +} + +func (n *Node) unrefLocked() { + if atomic.AddInt32(&n.ref, -1) == 0 { + n.r.mu.RLock() + if !n.r.closed { + n.r.delete(n) + } + n.r.mu.RUnlock() + } +} + +// Handle is a 'cache handle' of a 'cache node'. +type Handle struct { + n unsafe.Pointer // *Node +} + +// Value returns the value of the 'cache node'. +func (h *Handle) Value() Value { + n := (*Node)(atomic.LoadPointer(&h.n)) + if n != nil { + return n.value + } + return nil +} + +// Release releases this 'cache handle'. +// It is safe to call release multiple times. +func (h *Handle) Release() { + nPtr := atomic.LoadPointer(&h.n) + if nPtr != nil && atomic.CompareAndSwapPointer(&h.n, nPtr, nil) { + n := (*Node)(nPtr) + n.unrefLocked() + } +} + +func murmur32(ns, key uint64, seed uint32) uint32 { + const ( + m = uint32(0x5bd1e995) + r = 24 + ) + + k1 := uint32(ns >> 32) + k2 := uint32(ns) + k3 := uint32(key >> 32) + k4 := uint32(key) + + k1 *= m + k1 ^= k1 >> r + k1 *= m + + k2 *= m + k2 ^= k2 >> r + k2 *= m + + k3 *= m + k3 ^= k3 >> r + k3 *= m + + k4 *= m + k4 ^= k4 >> r + k4 *= m + + h := seed + + h *= m + h ^= k1 + h *= m + h ^= k2 + h *= m + h ^= k3 + h *= m + h ^= k4 + + h ^= h >> 13 + h *= m + h ^= h >> 15 + + return h +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go new file mode 100644 index 0000000..d9a84cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/cache/lru.go @@ -0,0 +1,195 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package cache + +import ( + "sync" + "unsafe" +) + +type lruNode struct { + n *Node + h *Handle + ban bool + + next, prev *lruNode +} + +func (n *lruNode) insert(at *lruNode) { + x := at.next + at.next = n + n.prev = at + n.next = x + x.prev = n +} + +func (n *lruNode) remove() { + if n.prev != nil { + n.prev.next = n.next + n.next.prev = n.prev + n.prev = nil + n.next = nil + } else { + panic("BUG: removing removed node") + } +} + +type lru struct { + mu sync.Mutex + capacity int + used int + recent lruNode +} + +func (r *lru) reset() { + r.recent.next = &r.recent + r.recent.prev = &r.recent + r.used = 0 +} + +func (r *lru) Capacity() int { + r.mu.Lock() + defer r.mu.Unlock() + return r.capacity +} + +func (r *lru) SetCapacity(capacity int) { + var evicted []*lruNode + + r.mu.Lock() + r.capacity = capacity + for r.used > r.capacity { + rn := r.recent.prev + if rn == nil { + panic("BUG: invalid LRU used or capacity counter") + } + rn.remove() + rn.n.CacheData = nil + r.used -= rn.n.Size() + evicted = append(evicted, rn) + } + r.mu.Unlock() + + for _, rn := range evicted { + rn.h.Release() + } +} + +func (r *lru) Promote(n *Node) { + var evicted []*lruNode + + r.mu.Lock() + if n.CacheData == nil { + if n.Size() <= r.capacity { + rn := &lruNode{n: n, h: n.GetHandle()} + rn.insert(&r.recent) + n.CacheData = unsafe.Pointer(rn) + r.used += n.Size() + + for r.used > r.capacity { + rn := r.recent.prev + if rn == nil { + panic("BUG: invalid LRU used or capacity counter") + } + rn.remove() + rn.n.CacheData = nil + r.used -= rn.n.Size() + evicted = append(evicted, rn) + } + } + } else { + rn := (*lruNode)(n.CacheData) + if !rn.ban { + rn.remove() + rn.insert(&r.recent) + } + } + r.mu.Unlock() + + for _, rn := range evicted { + rn.h.Release() + } +} + +func (r *lru) Ban(n *Node) { + r.mu.Lock() + if n.CacheData == nil { + n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true}) + } else { + rn := (*lruNode)(n.CacheData) + if !rn.ban { + rn.remove() + rn.ban = true + r.used -= rn.n.Size() + r.mu.Unlock() + + rn.h.Release() + rn.h = nil + return + } + } + r.mu.Unlock() +} + +func (r *lru) Evict(n *Node) { + r.mu.Lock() + rn := (*lruNode)(n.CacheData) + if rn == nil || rn.ban { + r.mu.Unlock() + return + } + n.CacheData = nil + r.mu.Unlock() + + rn.h.Release() +} + +func (r *lru) EvictNS(ns uint64) { + var evicted []*lruNode + + r.mu.Lock() + for e := r.recent.prev; e != &r.recent; { + rn := e + e = e.prev + if rn.n.NS() == ns { + rn.remove() + rn.n.CacheData = nil + r.used -= rn.n.Size() + evicted = append(evicted, rn) + } + } + r.mu.Unlock() + + for _, rn := range evicted { + rn.h.Release() + } +} + +func (r *lru) EvictAll() { + r.mu.Lock() + back := r.recent.prev + for rn := back; rn != &r.recent; rn = rn.prev { + rn.n.CacheData = nil + } + r.reset() + r.mu.Unlock() + + for rn := back; rn != &r.recent; rn = rn.prev { + rn.h.Release() + } +} + +func (r *lru) Close() error { + return nil +} + +// NewLRU create a new LRU-cache. +func NewLRU(capacity int) Cacher { + r := &lru{capacity: capacity} + r.reset() + return r +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer.go new file mode 100644 index 0000000..448402b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer.go @@ -0,0 +1,67 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "github.com/syndtr/goleveldb/leveldb/comparer" +) + +type iComparer struct { + ucmp comparer.Comparer +} + +func (icmp *iComparer) uName() string { + return icmp.ucmp.Name() +} + +func (icmp *iComparer) uCompare(a, b []byte) int { + return icmp.ucmp.Compare(a, b) +} + +func (icmp *iComparer) uSeparator(dst, a, b []byte) []byte { + return icmp.ucmp.Separator(dst, a, b) +} + +func (icmp *iComparer) uSuccessor(dst, b []byte) []byte { + return icmp.ucmp.Successor(dst, b) +} + +func (icmp *iComparer) Name() string { + return icmp.uName() +} + +func (icmp *iComparer) Compare(a, b []byte) int { + x := icmp.uCompare(internalKey(a).ukey(), internalKey(b).ukey()) + if x == 0 { + if m, n := internalKey(a).num(), internalKey(b).num(); m > n { + return -1 + } else if m < n { + return 1 + } + } + return x +} + +func (icmp *iComparer) Separator(dst, a, b []byte) []byte { + ua, ub := internalKey(a).ukey(), internalKey(b).ukey() + dst = icmp.uSeparator(dst, ua, ub) + if dst != nil && len(dst) < len(ua) && icmp.uCompare(ua, dst) < 0 { + // Append earliest possible number. + return append(dst, keyMaxNumBytes...) + } + return nil +} + +func (icmp *iComparer) Successor(dst, b []byte) []byte { + ub := internalKey(b).ukey() + dst = icmp.uSuccessor(dst, ub) + if dst != nil && len(dst) < len(ub) && icmp.uCompare(ub, dst) < 0 { + // Append earliest possible number. + return append(dst, keyMaxNumBytes...) + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go new file mode 100644 index 0000000..14dddf8 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go @@ -0,0 +1,51 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package comparer + +import "bytes" + +type bytesComparer struct{} + +func (bytesComparer) Compare(a, b []byte) int { + return bytes.Compare(a, b) +} + +func (bytesComparer) Name() string { + return "leveldb.BytewiseComparator" +} + +func (bytesComparer) Separator(dst, a, b []byte) []byte { + i, n := 0, len(a) + if n > len(b) { + n = len(b) + } + for ; i < n && a[i] == b[i]; i++ { + } + if i >= n { + // Do not shorten if one string is a prefix of the other + } else if c := a[i]; c < 0xff && c+1 < b[i] { + dst = append(dst, a[:i+1]...) + dst[i]++ + return dst + } + return nil +} + +func (bytesComparer) Successor(dst, b []byte) []byte { + for i, c := range b { + if c != 0xff { + dst = append(dst, b[:i+1]...) + dst[i]++ + return dst + } + } + return nil +} + +// DefaultComparer are default implementation of the Comparer interface. +// It uses the natural ordering, consistent with bytes.Compare. +var DefaultComparer = bytesComparer{} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go new file mode 100644 index 0000000..14a28f1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go @@ -0,0 +1,57 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package comparer provides interface and implementation for ordering +// sets of data. +package comparer + +// BasicComparer is the interface that wraps the basic Compare method. +type BasicComparer interface { + // Compare returns -1, 0, or +1 depending on whether a is 'less than', + // 'equal to' or 'greater than' b. The two arguments can only be 'equal' + // if their contents are exactly equal. Furthermore, the empty slice + // must be 'less than' any non-empty slice. + Compare(a, b []byte) int +} + +// Comparer defines a total ordering over the space of []byte keys: a 'less +// than' relationship. +type Comparer interface { + BasicComparer + + // Name returns name of the comparer. + // + // The Level-DB on-disk format stores the comparer name, and opening a + // database with a different comparer from the one it was created with + // will result in an error. + // + // An implementation to a new name whenever the comparer implementation + // changes in a way that will cause the relative ordering of any two keys + // to change. + // + // Names starting with "leveldb." are reserved and should not be used + // by any users of this package. + Name() string + + // Bellow are advanced functions used used to reduce the space requirements + // for internal data structures such as index blocks. + + // Separator appends a sequence of bytes x to dst such that a <= x && x < b, + // where 'less than' is consistent with Compare. An implementation should + // return nil if x equal to a. + // + // Either contents of a or b should not by any means modified. Doing so + // may cause corruption on the internal state. + Separator(dst, a, b []byte) []byte + + // Successor appends a sequence of bytes x to dst such that x >= b, where + // 'less than' is consistent with Compare. An implementation should return + // nil if x equal to b. + // + // Contents of b should not by any means modified. Doing so may cause + // corruption on the internal state. + Successor(dst, b []byte) []byte +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go new file mode 100644 index 0000000..a02cb2c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db.go @@ -0,0 +1,1091 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "container/list" + "fmt" + "io" + "os" + "runtime" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/journal" + "github.com/syndtr/goleveldb/leveldb/memdb" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/table" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// DB is a LevelDB database. +type DB struct { + // Need 64-bit alignment. + seq uint64 + + // Session. + s *session + + // MemDB. + memMu sync.RWMutex + memPool chan *memdb.DB + mem, frozenMem *memDB + journal *journal.Writer + journalWriter storage.Writer + journalFd storage.FileDesc + frozenJournalFd storage.FileDesc + frozenSeq uint64 + + // Snapshot. + snapsMu sync.Mutex + snapsList *list.List + + // Stats. + aliveSnaps, aliveIters int32 + + // Write. + batchPool sync.Pool + writeMergeC chan writeMerge + writeMergedC chan bool + writeLockC chan struct{} + writeAckC chan error + writeDelay time.Duration + writeDelayN int + tr *Transaction + + // Compaction. + compCommitLk sync.Mutex + tcompCmdC chan cCmd + tcompPauseC chan chan<- struct{} + mcompCmdC chan cCmd + compErrC chan error + compPerErrC chan error + compErrSetC chan error + compWriteLocking bool + compStats cStats + memdbMaxLevel int // For testing. + + // Close. + closeW sync.WaitGroup + closeC chan struct{} + closed uint32 + closer io.Closer +} + +func openDB(s *session) (*DB, error) { + s.log("db@open opening") + start := time.Now() + db := &DB{ + s: s, + // Initial sequence + seq: s.stSeqNum, + // MemDB + memPool: make(chan *memdb.DB, 1), + // Snapshot + snapsList: list.New(), + // Write + batchPool: sync.Pool{New: newBatch}, + writeMergeC: make(chan writeMerge), + writeMergedC: make(chan bool), + writeLockC: make(chan struct{}, 1), + writeAckC: make(chan error), + // Compaction + tcompCmdC: make(chan cCmd), + tcompPauseC: make(chan chan<- struct{}), + mcompCmdC: make(chan cCmd), + compErrC: make(chan error), + compPerErrC: make(chan error), + compErrSetC: make(chan error), + // Close + closeC: make(chan struct{}), + } + + // Read-only mode. + readOnly := s.o.GetReadOnly() + + if readOnly { + // Recover journals (read-only mode). + if err := db.recoverJournalRO(); err != nil { + return nil, err + } + } else { + // Recover journals. + if err := db.recoverJournal(); err != nil { + return nil, err + } + + // Remove any obsolete files. + if err := db.checkAndCleanFiles(); err != nil { + // Close journal. + if db.journal != nil { + db.journal.Close() + db.journalWriter.Close() + } + return nil, err + } + + } + + // Doesn't need to be included in the wait group. + go db.compactionError() + go db.mpoolDrain() + + if readOnly { + db.SetReadOnly() + } else { + db.closeW.Add(2) + go db.tCompaction() + go db.mCompaction() + // go db.jWriter() + } + + s.logf("db@open done T·%v", time.Since(start)) + + runtime.SetFinalizer(db, (*DB).Close) + return db, nil +} + +// Open opens or creates a DB for the given storage. +// The DB will be created if not exist, unless ErrorIfMissing is true. +// Also, if ErrorIfExist is true and the DB exist Open will returns +// os.ErrExist error. +// +// Open will return an error with type of ErrCorrupted if corruption +// detected in the DB. Use errors.IsCorrupted to test whether an error is +// due to corruption. Corrupted DB can be recovered with Recover function. +// +// The returned DB instance is safe for concurrent use. +// The DB must be closed after use, by calling Close method. +func Open(stor storage.Storage, o *opt.Options) (db *DB, err error) { + s, err := newSession(stor, o) + if err != nil { + return + } + defer func() { + if err != nil { + s.close() + s.release() + } + }() + + err = s.recover() + if err != nil { + if !os.IsNotExist(err) || s.o.GetErrorIfMissing() { + return + } + err = s.create() + if err != nil { + return + } + } else if s.o.GetErrorIfExist() { + err = os.ErrExist + return + } + + return openDB(s) +} + +// OpenFile opens or creates a DB for the given path. +// The DB will be created if not exist, unless ErrorIfMissing is true. +// Also, if ErrorIfExist is true and the DB exist OpenFile will returns +// os.ErrExist error. +// +// OpenFile uses standard file-system backed storage implementation as +// described in the leveldb/storage package. +// +// OpenFile will return an error with type of ErrCorrupted if corruption +// detected in the DB. Use errors.IsCorrupted to test whether an error is +// due to corruption. Corrupted DB can be recovered with Recover function. +// +// The returned DB instance is safe for concurrent use. +// The DB must be closed after use, by calling Close method. +func OpenFile(path string, o *opt.Options) (db *DB, err error) { + stor, err := storage.OpenFile(path, o.GetReadOnly()) + if err != nil { + return + } + db, err = Open(stor, o) + if err != nil { + stor.Close() + } else { + db.closer = stor + } + return +} + +// Recover recovers and opens a DB with missing or corrupted manifest files +// for the given storage. It will ignore any manifest files, valid or not. +// The DB must already exist or it will returns an error. +// Also, Recover will ignore ErrorIfMissing and ErrorIfExist options. +// +// The returned DB instance is safe for concurrent use. +// The DB must be closed after use, by calling Close method. +func Recover(stor storage.Storage, o *opt.Options) (db *DB, err error) { + s, err := newSession(stor, o) + if err != nil { + return + } + defer func() { + if err != nil { + s.close() + s.release() + } + }() + + err = recoverTable(s, o) + if err != nil { + return + } + return openDB(s) +} + +// RecoverFile recovers and opens a DB with missing or corrupted manifest files +// for the given path. It will ignore any manifest files, valid or not. +// The DB must already exist or it will returns an error. +// Also, Recover will ignore ErrorIfMissing and ErrorIfExist options. +// +// RecoverFile uses standard file-system backed storage implementation as described +// in the leveldb/storage package. +// +// The returned DB instance is safe for concurrent use. +// The DB must be closed after use, by calling Close method. +func RecoverFile(path string, o *opt.Options) (db *DB, err error) { + stor, err := storage.OpenFile(path, false) + if err != nil { + return + } + db, err = Recover(stor, o) + if err != nil { + stor.Close() + } else { + db.closer = stor + } + return +} + +func recoverTable(s *session, o *opt.Options) error { + o = dupOptions(o) + // Mask StrictReader, lets StrictRecovery doing its job. + o.Strict &= ^opt.StrictReader + + // Get all tables and sort it by file number. + fds, err := s.stor.List(storage.TypeTable) + if err != nil { + return err + } + sortFds(fds) + + var ( + maxSeq uint64 + recoveredKey, goodKey, corruptedKey, corruptedBlock, droppedTable int + + // We will drop corrupted table. + strict = o.GetStrict(opt.StrictRecovery) + noSync = o.GetNoSync() + + rec = &sessionRecord{} + bpool = util.NewBufferPool(o.GetBlockSize() + 5) + ) + buildTable := func(iter iterator.Iterator) (tmpFd storage.FileDesc, size int64, err error) { + tmpFd = s.newTemp() + writer, err := s.stor.Create(tmpFd) + if err != nil { + return + } + defer func() { + writer.Close() + if err != nil { + s.stor.Remove(tmpFd) + tmpFd = storage.FileDesc{} + } + }() + + // Copy entries. + tw := table.NewWriter(writer, o) + for iter.Next() { + key := iter.Key() + if validInternalKey(key) { + err = tw.Append(key, iter.Value()) + if err != nil { + return + } + } + } + err = iter.Error() + if err != nil { + return + } + err = tw.Close() + if err != nil { + return + } + if !noSync { + err = writer.Sync() + if err != nil { + return + } + } + size = int64(tw.BytesLen()) + return + } + recoverTable := func(fd storage.FileDesc) error { + s.logf("table@recovery recovering @%d", fd.Num) + reader, err := s.stor.Open(fd) + if err != nil { + return err + } + var closed bool + defer func() { + if !closed { + reader.Close() + } + }() + + // Get file size. + size, err := reader.Seek(0, 2) + if err != nil { + return err + } + + var ( + tSeq uint64 + tgoodKey, tcorruptedKey, tcorruptedBlock int + imin, imax []byte + ) + tr, err := table.NewReader(reader, size, fd, nil, bpool, o) + if err != nil { + return err + } + iter := tr.NewIterator(nil, nil) + if itererr, ok := iter.(iterator.ErrorCallbackSetter); ok { + itererr.SetErrorCallback(func(err error) { + if errors.IsCorrupted(err) { + s.logf("table@recovery block corruption @%d %q", fd.Num, err) + tcorruptedBlock++ + } + }) + } + + // Scan the table. + for iter.Next() { + key := iter.Key() + _, seq, _, kerr := parseInternalKey(key) + if kerr != nil { + tcorruptedKey++ + continue + } + tgoodKey++ + if seq > tSeq { + tSeq = seq + } + if imin == nil { + imin = append([]byte{}, key...) + } + imax = append(imax[:0], key...) + } + if err := iter.Error(); err != nil { + iter.Release() + return err + } + iter.Release() + + goodKey += tgoodKey + corruptedKey += tcorruptedKey + corruptedBlock += tcorruptedBlock + + if strict && (tcorruptedKey > 0 || tcorruptedBlock > 0) { + droppedTable++ + s.logf("table@recovery dropped @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", fd.Num, tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq) + return nil + } + + if tgoodKey > 0 { + if tcorruptedKey > 0 || tcorruptedBlock > 0 { + // Rebuild the table. + s.logf("table@recovery rebuilding @%d", fd.Num) + iter := tr.NewIterator(nil, nil) + tmpFd, newSize, err := buildTable(iter) + iter.Release() + if err != nil { + return err + } + closed = true + reader.Close() + if err := s.stor.Rename(tmpFd, fd); err != nil { + return err + } + size = newSize + } + if tSeq > maxSeq { + maxSeq = tSeq + } + recoveredKey += tgoodKey + // Add table to level 0. + rec.addTable(0, fd.Num, size, imin, imax) + s.logf("table@recovery recovered @%d Gk·%d Ck·%d Cb·%d S·%d Q·%d", fd.Num, tgoodKey, tcorruptedKey, tcorruptedBlock, size, tSeq) + } else { + droppedTable++ + s.logf("table@recovery unrecoverable @%d Ck·%d Cb·%d S·%d", fd.Num, tcorruptedKey, tcorruptedBlock, size) + } + + return nil + } + + // Recover all tables. + if len(fds) > 0 { + s.logf("table@recovery F·%d", len(fds)) + + // Mark file number as used. + s.markFileNum(fds[len(fds)-1].Num) + + for _, fd := range fds { + if err := recoverTable(fd); err != nil { + return err + } + } + + s.logf("table@recovery recovered F·%d N·%d Gk·%d Ck·%d Q·%d", len(fds), recoveredKey, goodKey, corruptedKey, maxSeq) + } + + // Set sequence number. + rec.setSeqNum(maxSeq) + + // Create new manifest. + if err := s.create(); err != nil { + return err + } + + // Commit. + return s.commit(rec) +} + +func (db *DB) recoverJournal() error { + // Get all journals and sort it by file number. + rawFds, err := db.s.stor.List(storage.TypeJournal) + if err != nil { + return err + } + sortFds(rawFds) + + // Journals that will be recovered. + var fds []storage.FileDesc + for _, fd := range rawFds { + if fd.Num >= db.s.stJournalNum || fd.Num == db.s.stPrevJournalNum { + fds = append(fds, fd) + } + } + + var ( + ofd storage.FileDesc // Obsolete file. + rec = &sessionRecord{} + ) + + // Recover journals. + if len(fds) > 0 { + db.logf("journal@recovery F·%d", len(fds)) + + // Mark file number as used. + db.s.markFileNum(fds[len(fds)-1].Num) + + var ( + // Options. + strict = db.s.o.GetStrict(opt.StrictJournal) + checksum = db.s.o.GetStrict(opt.StrictJournalChecksum) + writeBuffer = db.s.o.GetWriteBuffer() + + jr *journal.Reader + mdb = memdb.New(db.s.icmp, writeBuffer) + buf = &util.Buffer{} + batchSeq uint64 + batchLen int + ) + + for _, fd := range fds { + db.logf("journal@recovery recovering @%d", fd.Num) + + fr, err := db.s.stor.Open(fd) + if err != nil { + return err + } + + // Create or reset journal reader instance. + if jr == nil { + jr = journal.NewReader(fr, dropper{db.s, fd}, strict, checksum) + } else { + jr.Reset(fr, dropper{db.s, fd}, strict, checksum) + } + + // Flush memdb and remove obsolete journal file. + if !ofd.Zero() { + if mdb.Len() > 0 { + if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil { + fr.Close() + return err + } + } + + rec.setJournalNum(fd.Num) + rec.setSeqNum(db.seq) + if err := db.s.commit(rec); err != nil { + fr.Close() + return err + } + rec.resetAddedTables() + + db.s.stor.Remove(ofd) + ofd = storage.FileDesc{} + } + + // Replay journal to memdb. + mdb.Reset() + for { + r, err := jr.Next() + if err != nil { + if err == io.EOF { + break + } + + fr.Close() + return errors.SetFd(err, fd) + } + + buf.Reset() + if _, err := buf.ReadFrom(r); err != nil { + if err == io.ErrUnexpectedEOF { + // This is error returned due to corruption, with strict == false. + continue + } + + fr.Close() + return errors.SetFd(err, fd) + } + batchSeq, batchLen, err = decodeBatchToMem(buf.Bytes(), db.seq, mdb) + if err != nil { + if !strict && errors.IsCorrupted(err) { + db.s.logf("journal error: %v (skipped)", err) + // We won't apply sequence number as it might be corrupted. + continue + } + + fr.Close() + return errors.SetFd(err, fd) + } + + // Save sequence number. + db.seq = batchSeq + uint64(batchLen) + + // Flush it if large enough. + if mdb.Size() >= writeBuffer { + if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil { + fr.Close() + return err + } + + mdb.Reset() + } + } + + fr.Close() + ofd = fd + } + + // Flush the last memdb. + if mdb.Len() > 0 { + if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil { + return err + } + } + } + + // Create a new journal. + if _, err := db.newMem(0); err != nil { + return err + } + + // Commit. + rec.setJournalNum(db.journalFd.Num) + rec.setSeqNum(db.seq) + if err := db.s.commit(rec); err != nil { + // Close journal on error. + if db.journal != nil { + db.journal.Close() + db.journalWriter.Close() + } + return err + } + + // Remove the last obsolete journal file. + if !ofd.Zero() { + db.s.stor.Remove(ofd) + } + + return nil +} + +func (db *DB) recoverJournalRO() error { + // Get all journals and sort it by file number. + rawFds, err := db.s.stor.List(storage.TypeJournal) + if err != nil { + return err + } + sortFds(rawFds) + + // Journals that will be recovered. + var fds []storage.FileDesc + for _, fd := range rawFds { + if fd.Num >= db.s.stJournalNum || fd.Num == db.s.stPrevJournalNum { + fds = append(fds, fd) + } + } + + var ( + // Options. + strict = db.s.o.GetStrict(opt.StrictJournal) + checksum = db.s.o.GetStrict(opt.StrictJournalChecksum) + writeBuffer = db.s.o.GetWriteBuffer() + + mdb = memdb.New(db.s.icmp, writeBuffer) + ) + + // Recover journals. + if len(fds) > 0 { + db.logf("journal@recovery RO·Mode F·%d", len(fds)) + + var ( + jr *journal.Reader + buf = &util.Buffer{} + batchSeq uint64 + batchLen int + ) + + for _, fd := range fds { + db.logf("journal@recovery recovering @%d", fd.Num) + + fr, err := db.s.stor.Open(fd) + if err != nil { + return err + } + + // Create or reset journal reader instance. + if jr == nil { + jr = journal.NewReader(fr, dropper{db.s, fd}, strict, checksum) + } else { + jr.Reset(fr, dropper{db.s, fd}, strict, checksum) + } + + // Replay journal to memdb. + for { + r, err := jr.Next() + if err != nil { + if err == io.EOF { + break + } + + fr.Close() + return errors.SetFd(err, fd) + } + + buf.Reset() + if _, err := buf.ReadFrom(r); err != nil { + if err == io.ErrUnexpectedEOF { + // This is error returned due to corruption, with strict == false. + continue + } + + fr.Close() + return errors.SetFd(err, fd) + } + batchSeq, batchLen, err = decodeBatchToMem(buf.Bytes(), db.seq, mdb) + if err != nil { + if !strict && errors.IsCorrupted(err) { + db.s.logf("journal error: %v (skipped)", err) + // We won't apply sequence number as it might be corrupted. + continue + } + + fr.Close() + return errors.SetFd(err, fd) + } + + // Save sequence number. + db.seq = batchSeq + uint64(batchLen) + } + + fr.Close() + } + } + + // Set memDB. + db.mem = &memDB{db: db, DB: mdb, ref: 1} + + return nil +} + +func memGet(mdb *memdb.DB, ikey internalKey, icmp *iComparer) (ok bool, mv []byte, err error) { + mk, mv, err := mdb.Find(ikey) + if err == nil { + ukey, _, kt, kerr := parseInternalKey(mk) + if kerr != nil { + // Shouldn't have had happen. + panic(kerr) + } + if icmp.uCompare(ukey, ikey.ukey()) == 0 { + if kt == keyTypeDel { + return true, nil, ErrNotFound + } + return true, mv, nil + + } + } else if err != ErrNotFound { + return true, nil, err + } + return +} + +func (db *DB) get(auxm *memdb.DB, auxt tFiles, key []byte, seq uint64, ro *opt.ReadOptions) (value []byte, err error) { + ikey := makeInternalKey(nil, key, seq, keyTypeSeek) + + if auxm != nil { + if ok, mv, me := memGet(auxm, ikey, db.s.icmp); ok { + return append([]byte{}, mv...), me + } + } + + em, fm := db.getMems() + for _, m := range [...]*memDB{em, fm} { + if m == nil { + continue + } + defer m.decref() + + if ok, mv, me := memGet(m.DB, ikey, db.s.icmp); ok { + return append([]byte{}, mv...), me + } + } + + v := db.s.version() + value, cSched, err := v.get(auxt, ikey, ro, false) + v.release() + if cSched { + // Trigger table compaction. + db.compTrigger(db.tcompCmdC) + } + return +} + +func nilIfNotFound(err error) error { + if err == ErrNotFound { + return nil + } + return err +} + +func (db *DB) has(auxm *memdb.DB, auxt tFiles, key []byte, seq uint64, ro *opt.ReadOptions) (ret bool, err error) { + ikey := makeInternalKey(nil, key, seq, keyTypeSeek) + + if auxm != nil { + if ok, _, me := memGet(auxm, ikey, db.s.icmp); ok { + return me == nil, nilIfNotFound(me) + } + } + + em, fm := db.getMems() + for _, m := range [...]*memDB{em, fm} { + if m == nil { + continue + } + defer m.decref() + + if ok, _, me := memGet(m.DB, ikey, db.s.icmp); ok { + return me == nil, nilIfNotFound(me) + } + } + + v := db.s.version() + _, cSched, err := v.get(auxt, ikey, ro, true) + v.release() + if cSched { + // Trigger table compaction. + db.compTrigger(db.tcompCmdC) + } + if err == nil { + ret = true + } else if err == ErrNotFound { + err = nil + } + return +} + +// Get gets the value for the given key. It returns ErrNotFound if the +// DB does not contains the key. +// +// The returned slice is its own copy, it is safe to modify the contents +// of the returned slice. +// It is safe to modify the contents of the argument after Get returns. +func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) { + err = db.ok() + if err != nil { + return + } + + se := db.acquireSnapshot() + defer db.releaseSnapshot(se) + return db.get(nil, nil, key, se.seq, ro) +} + +// Has returns true if the DB does contains the given key. +// +// It is safe to modify the contents of the argument after Get returns. +func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) { + err = db.ok() + if err != nil { + return + } + + se := db.acquireSnapshot() + defer db.releaseSnapshot(se) + return db.has(nil, nil, key, se.seq, ro) +} + +// NewIterator returns an iterator for the latest snapshot of the +// underlying DB. +// The returned iterator is not safe for concurrent use, but it is safe to use +// multiple iterators concurrently, with each in a dedicated goroutine. +// It is also safe to use an iterator concurrently with modifying its +// underlying DB. The resultant key/value pairs are guaranteed to be +// consistent. +// +// Slice allows slicing the iterator to only contains keys in the given +// range. A nil Range.Start is treated as a key before all keys in the +// DB. And a nil Range.Limit is treated as a key after all keys in +// the DB. +// +// The iterator must be released after use, by calling Release method. +// +// Also read Iterator documentation of the leveldb/iterator package. +func (db *DB) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + if err := db.ok(); err != nil { + return iterator.NewEmptyIterator(err) + } + + se := db.acquireSnapshot() + defer db.releaseSnapshot(se) + // Iterator holds 'version' lock, 'version' is immutable so snapshot + // can be released after iterator created. + return db.newIterator(nil, nil, se.seq, slice, ro) +} + +// GetSnapshot returns a latest snapshot of the underlying DB. A snapshot +// is a frozen snapshot of a DB state at a particular point in time. The +// content of snapshot are guaranteed to be consistent. +// +// The snapshot must be released after use, by calling Release method. +func (db *DB) GetSnapshot() (*Snapshot, error) { + if err := db.ok(); err != nil { + return nil, err + } + + return db.newSnapshot(), nil +} + +// GetProperty returns value of the given property name. +// +// Property names: +// leveldb.num-files-at-level{n} +// Returns the number of files at level 'n'. +// leveldb.stats +// Returns statistics of the underlying DB. +// leveldb.sstables +// Returns sstables list for each level. +// leveldb.blockpool +// Returns block pool stats. +// leveldb.cachedblock +// Returns size of cached block. +// leveldb.openedtables +// Returns number of opened tables. +// leveldb.alivesnaps +// Returns number of alive snapshots. +// leveldb.aliveiters +// Returns number of alive iterators. +func (db *DB) GetProperty(name string) (value string, err error) { + err = db.ok() + if err != nil { + return + } + + const prefix = "leveldb." + if !strings.HasPrefix(name, prefix) { + return "", ErrNotFound + } + p := name[len(prefix):] + + v := db.s.version() + defer v.release() + + numFilesPrefix := "num-files-at-level" + switch { + case strings.HasPrefix(p, numFilesPrefix): + var level uint + var rest string + n, _ := fmt.Sscanf(p[len(numFilesPrefix):], "%d%s", &level, &rest) + if n != 1 { + err = ErrNotFound + } else { + value = fmt.Sprint(v.tLen(int(level))) + } + case p == "stats": + value = "Compactions\n" + + " Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" + + "-------+------------+---------------+---------------+---------------+---------------\n" + for level, tables := range v.levels { + duration, read, write := db.compStats.getStat(level) + if len(tables) == 0 && duration == 0 { + continue + } + value += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", + level, len(tables), float64(tables.size())/1048576.0, duration.Seconds(), + float64(read)/1048576.0, float64(write)/1048576.0) + } + case p == "sstables": + for level, tables := range v.levels { + value += fmt.Sprintf("--- level %d ---\n", level) + for _, t := range tables { + value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.fd.Num, t.size, t.imin, t.imax) + } + } + case p == "blockpool": + value = fmt.Sprintf("%v", db.s.tops.bpool) + case p == "cachedblock": + if db.s.tops.bcache != nil { + value = fmt.Sprintf("%d", db.s.tops.bcache.Size()) + } else { + value = "" + } + case p == "openedtables": + value = fmt.Sprintf("%d", db.s.tops.cache.Size()) + case p == "alivesnaps": + value = fmt.Sprintf("%d", atomic.LoadInt32(&db.aliveSnaps)) + case p == "aliveiters": + value = fmt.Sprintf("%d", atomic.LoadInt32(&db.aliveIters)) + default: + err = ErrNotFound + } + + return +} + +// SizeOf calculates approximate sizes of the given key ranges. +// The length of the returned sizes are equal with the length of the given +// ranges. The returned sizes measure storage space usage, so if the user +// data compresses by a factor of ten, the returned sizes will be one-tenth +// the size of the corresponding user data size. +// The results may not include the sizes of recently written data. +func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) { + if err := db.ok(); err != nil { + return nil, err + } + + v := db.s.version() + defer v.release() + + sizes := make(Sizes, 0, len(ranges)) + for _, r := range ranges { + imin := makeInternalKey(nil, r.Start, keyMaxSeq, keyTypeSeek) + imax := makeInternalKey(nil, r.Limit, keyMaxSeq, keyTypeSeek) + start, err := v.offsetOf(imin) + if err != nil { + return nil, err + } + limit, err := v.offsetOf(imax) + if err != nil { + return nil, err + } + var size int64 + if limit >= start { + size = limit - start + } + sizes = append(sizes, size) + } + + return sizes, nil +} + +// Close closes the DB. This will also releases any outstanding snapshot, +// abort any in-flight compaction and discard open transaction. +// +// It is not safe to close a DB until all outstanding iterators are released. +// It is valid to call Close multiple times. Other methods should not be +// called after the DB has been closed. +func (db *DB) Close() error { + if !db.setClosed() { + return ErrClosed + } + + start := time.Now() + db.log("db@close closing") + + // Clear the finalizer. + runtime.SetFinalizer(db, nil) + + // Get compaction error. + var err error + select { + case err = <-db.compErrC: + if err == ErrReadOnly { + err = nil + } + default: + } + + // Signal all goroutines. + close(db.closeC) + + // Discard open transaction. + if db.tr != nil { + db.tr.Discard() + } + + // Acquire writer lock. + db.writeLockC <- struct{}{} + + // Wait for all gorotines to exit. + db.closeW.Wait() + + // Closes journal. + if db.journal != nil { + db.journal.Close() + db.journalWriter.Close() + db.journal = nil + db.journalWriter = nil + } + + if db.writeDelayN > 0 { + db.logf("db@write was delayed N·%d T·%v", db.writeDelayN, db.writeDelay) + } + + // Close session. + db.s.close() + db.logf("db@close done T·%v", time.Since(start)) + db.s.release() + + if db.closer != nil { + if err1 := db.closer.Close(); err == nil { + err = err1 + } + db.closer = nil + } + + // Clear memdbs. + db.clearMems() + + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go new file mode 100644 index 0000000..2d0ad07 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go @@ -0,0 +1,826 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "sync" + "time" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +var ( + errCompactionTransactExiting = errors.New("leveldb: compaction transact exiting") +) + +type cStat struct { + duration time.Duration + read int64 + write int64 +} + +func (p *cStat) add(n *cStatStaging) { + p.duration += n.duration + p.read += n.read + p.write += n.write +} + +func (p *cStat) get() (duration time.Duration, read, write int64) { + return p.duration, p.read, p.write +} + +type cStatStaging struct { + start time.Time + duration time.Duration + on bool + read int64 + write int64 +} + +func (p *cStatStaging) startTimer() { + if !p.on { + p.start = time.Now() + p.on = true + } +} + +func (p *cStatStaging) stopTimer() { + if p.on { + p.duration += time.Since(p.start) + p.on = false + } +} + +type cStats struct { + lk sync.Mutex + stats []cStat +} + +func (p *cStats) addStat(level int, n *cStatStaging) { + p.lk.Lock() + if level >= len(p.stats) { + newStats := make([]cStat, level+1) + copy(newStats, p.stats) + p.stats = newStats + } + p.stats[level].add(n) + p.lk.Unlock() +} + +func (p *cStats) getStat(level int) (duration time.Duration, read, write int64) { + p.lk.Lock() + defer p.lk.Unlock() + if level < len(p.stats) { + return p.stats[level].get() + } + return +} + +func (db *DB) compactionError() { + var err error +noerr: + // No error. + for { + select { + case err = <-db.compErrSetC: + switch { + case err == nil: + case err == ErrReadOnly, errors.IsCorrupted(err): + goto hasperr + default: + goto haserr + } + case <-db.closeC: + return + } + } +haserr: + // Transient error. + for { + select { + case db.compErrC <- err: + case err = <-db.compErrSetC: + switch { + case err == nil: + goto noerr + case err == ErrReadOnly, errors.IsCorrupted(err): + goto hasperr + default: + } + case <-db.closeC: + return + } + } +hasperr: + // Persistent error. + for { + select { + case db.compErrC <- err: + case db.compPerErrC <- err: + case db.writeLockC <- struct{}{}: + // Hold write lock, so that write won't pass-through. + db.compWriteLocking = true + case <-db.closeC: + if db.compWriteLocking { + // We should release the lock or Close will hang. + <-db.writeLockC + } + return + } + } +} + +type compactionTransactCounter int + +func (cnt *compactionTransactCounter) incr() { + *cnt++ +} + +type compactionTransactInterface interface { + run(cnt *compactionTransactCounter) error + revert() error +} + +func (db *DB) compactionTransact(name string, t compactionTransactInterface) { + defer func() { + if x := recover(); x != nil { + if x == errCompactionTransactExiting { + if err := t.revert(); err != nil { + db.logf("%s revert error %q", name, err) + } + } + panic(x) + } + }() + + const ( + backoffMin = 1 * time.Second + backoffMax = 8 * time.Second + backoffMul = 2 * time.Second + ) + var ( + backoff = backoffMin + backoffT = time.NewTimer(backoff) + lastCnt = compactionTransactCounter(0) + + disableBackoff = db.s.o.GetDisableCompactionBackoff() + ) + for n := 0; ; n++ { + // Check whether the DB is closed. + if db.isClosed() { + db.logf("%s exiting", name) + db.compactionExitTransact() + } else if n > 0 { + db.logf("%s retrying N·%d", name, n) + } + + // Execute. + cnt := compactionTransactCounter(0) + err := t.run(&cnt) + if err != nil { + db.logf("%s error I·%d %q", name, cnt, err) + } + + // Set compaction error status. + select { + case db.compErrSetC <- err: + case perr := <-db.compPerErrC: + if err != nil { + db.logf("%s exiting (persistent error %q)", name, perr) + db.compactionExitTransact() + } + case <-db.closeC: + db.logf("%s exiting", name) + db.compactionExitTransact() + } + if err == nil { + return + } + if errors.IsCorrupted(err) { + db.logf("%s exiting (corruption detected)", name) + db.compactionExitTransact() + } + + if !disableBackoff { + // Reset backoff duration if counter is advancing. + if cnt > lastCnt { + backoff = backoffMin + lastCnt = cnt + } + + // Backoff. + backoffT.Reset(backoff) + if backoff < backoffMax { + backoff *= backoffMul + if backoff > backoffMax { + backoff = backoffMax + } + } + select { + case <-backoffT.C: + case <-db.closeC: + db.logf("%s exiting", name) + db.compactionExitTransact() + } + } + } +} + +type compactionTransactFunc struct { + runFunc func(cnt *compactionTransactCounter) error + revertFunc func() error +} + +func (t *compactionTransactFunc) run(cnt *compactionTransactCounter) error { + return t.runFunc(cnt) +} + +func (t *compactionTransactFunc) revert() error { + if t.revertFunc != nil { + return t.revertFunc() + } + return nil +} + +func (db *DB) compactionTransactFunc(name string, run func(cnt *compactionTransactCounter) error, revert func() error) { + db.compactionTransact(name, &compactionTransactFunc{run, revert}) +} + +func (db *DB) compactionExitTransact() { + panic(errCompactionTransactExiting) +} + +func (db *DB) compactionCommit(name string, rec *sessionRecord) { + db.compCommitLk.Lock() + defer db.compCommitLk.Unlock() // Defer is necessary. + db.compactionTransactFunc(name+"@commit", func(cnt *compactionTransactCounter) error { + return db.s.commit(rec) + }, nil) +} + +func (db *DB) memCompaction() { + mdb := db.getFrozenMem() + if mdb == nil { + return + } + defer mdb.decref() + + db.logf("memdb@flush N·%d S·%s", mdb.Len(), shortenb(mdb.Size())) + + // Don't compact empty memdb. + if mdb.Len() == 0 { + db.logf("memdb@flush skipping") + // drop frozen memdb + db.dropFrozenMem() + return + } + + // Pause table compaction. + resumeC := make(chan struct{}) + select { + case db.tcompPauseC <- (chan<- struct{})(resumeC): + case <-db.compPerErrC: + close(resumeC) + resumeC = nil + case <-db.closeC: + return + } + + var ( + rec = &sessionRecord{} + stats = &cStatStaging{} + flushLevel int + ) + + // Generate tables. + db.compactionTransactFunc("memdb@flush", func(cnt *compactionTransactCounter) (err error) { + stats.startTimer() + flushLevel, err = db.s.flushMemdb(rec, mdb.DB, db.memdbMaxLevel) + stats.stopTimer() + return + }, func() error { + for _, r := range rec.addedTables { + db.logf("memdb@flush revert @%d", r.num) + if err := db.s.stor.Remove(storage.FileDesc{Type: storage.TypeTable, Num: r.num}); err != nil { + return err + } + } + return nil + }) + + rec.setJournalNum(db.journalFd.Num) + rec.setSeqNum(db.frozenSeq) + + // Commit. + stats.startTimer() + db.compactionCommit("memdb", rec) + stats.stopTimer() + + db.logf("memdb@flush committed F·%d T·%v", len(rec.addedTables), stats.duration) + + for _, r := range rec.addedTables { + stats.write += r.size + } + db.compStats.addStat(flushLevel, stats) + + // Drop frozen memdb. + db.dropFrozenMem() + + // Resume table compaction. + if resumeC != nil { + select { + case <-resumeC: + close(resumeC) + case <-db.closeC: + return + } + } + + // Trigger table compaction. + db.compTrigger(db.tcompCmdC) +} + +type tableCompactionBuilder struct { + db *DB + s *session + c *compaction + rec *sessionRecord + stat0, stat1 *cStatStaging + + snapHasLastUkey bool + snapLastUkey []byte + snapLastSeq uint64 + snapIter int + snapKerrCnt int + snapDropCnt int + + kerrCnt int + dropCnt int + + minSeq uint64 + strict bool + tableSize int + + tw *tWriter +} + +func (b *tableCompactionBuilder) appendKV(key, value []byte) error { + // Create new table if not already. + if b.tw == nil { + // Check for pause event. + if b.db != nil { + select { + case ch := <-b.db.tcompPauseC: + b.db.pauseCompaction(ch) + case <-b.db.closeC: + b.db.compactionExitTransact() + default: + } + } + + // Create new table. + var err error + b.tw, err = b.s.tops.create() + if err != nil { + return err + } + } + + // Write key/value into table. + return b.tw.append(key, value) +} + +func (b *tableCompactionBuilder) needFlush() bool { + return b.tw.tw.BytesLen() >= b.tableSize +} + +func (b *tableCompactionBuilder) flush() error { + t, err := b.tw.finish() + if err != nil { + return err + } + b.rec.addTableFile(b.c.sourceLevel+1, t) + b.stat1.write += t.size + b.s.logf("table@build created L%d@%d N·%d S·%s %q:%q", b.c.sourceLevel+1, t.fd.Num, b.tw.tw.EntriesLen(), shortenb(int(t.size)), t.imin, t.imax) + b.tw = nil + return nil +} + +func (b *tableCompactionBuilder) cleanup() { + if b.tw != nil { + b.tw.drop() + b.tw = nil + } +} + +func (b *tableCompactionBuilder) run(cnt *compactionTransactCounter) error { + snapResumed := b.snapIter > 0 + hasLastUkey := b.snapHasLastUkey // The key might has zero length, so this is necessary. + lastUkey := append([]byte{}, b.snapLastUkey...) + lastSeq := b.snapLastSeq + b.kerrCnt = b.snapKerrCnt + b.dropCnt = b.snapDropCnt + // Restore compaction state. + b.c.restore() + + defer b.cleanup() + + b.stat1.startTimer() + defer b.stat1.stopTimer() + + iter := b.c.newIterator() + defer iter.Release() + for i := 0; iter.Next(); i++ { + // Incr transact counter. + cnt.incr() + + // Skip until last state. + if i < b.snapIter { + continue + } + + resumed := false + if snapResumed { + resumed = true + snapResumed = false + } + + ikey := iter.Key() + ukey, seq, kt, kerr := parseInternalKey(ikey) + + if kerr == nil { + shouldStop := !resumed && b.c.shouldStopBefore(ikey) + + if !hasLastUkey || b.s.icmp.uCompare(lastUkey, ukey) != 0 { + // First occurrence of this user key. + + // Only rotate tables if ukey doesn't hop across. + if b.tw != nil && (shouldStop || b.needFlush()) { + if err := b.flush(); err != nil { + return err + } + + // Creates snapshot of the state. + b.c.save() + b.snapHasLastUkey = hasLastUkey + b.snapLastUkey = append(b.snapLastUkey[:0], lastUkey...) + b.snapLastSeq = lastSeq + b.snapIter = i + b.snapKerrCnt = b.kerrCnt + b.snapDropCnt = b.dropCnt + } + + hasLastUkey = true + lastUkey = append(lastUkey[:0], ukey...) + lastSeq = keyMaxSeq + } + + switch { + case lastSeq <= b.minSeq: + // Dropped because newer entry for same user key exist + fallthrough // (A) + case kt == keyTypeDel && seq <= b.minSeq && b.c.baseLevelForKey(lastUkey): + // For this user key: + // (1) there is no data in higher levels + // (2) data in lower levels will have larger seq numbers + // (3) data in layers that are being compacted here and have + // smaller seq numbers will be dropped in the next + // few iterations of this loop (by rule (A) above). + // Therefore this deletion marker is obsolete and can be dropped. + lastSeq = seq + b.dropCnt++ + continue + default: + lastSeq = seq + } + } else { + if b.strict { + return kerr + } + + // Don't drop corrupted keys. + hasLastUkey = false + lastUkey = lastUkey[:0] + lastSeq = keyMaxSeq + b.kerrCnt++ + } + + if err := b.appendKV(ikey, iter.Value()); err != nil { + return err + } + } + + if err := iter.Error(); err != nil { + return err + } + + // Finish last table. + if b.tw != nil && !b.tw.empty() { + return b.flush() + } + return nil +} + +func (b *tableCompactionBuilder) revert() error { + for _, at := range b.rec.addedTables { + b.s.logf("table@build revert @%d", at.num) + if err := b.s.stor.Remove(storage.FileDesc{Type: storage.TypeTable, Num: at.num}); err != nil { + return err + } + } + return nil +} + +func (db *DB) tableCompaction(c *compaction, noTrivial bool) { + defer c.release() + + rec := &sessionRecord{} + rec.addCompPtr(c.sourceLevel, c.imax) + + if !noTrivial && c.trivial() { + t := c.levels[0][0] + db.logf("table@move L%d@%d -> L%d", c.sourceLevel, t.fd.Num, c.sourceLevel+1) + rec.delTable(c.sourceLevel, t.fd.Num) + rec.addTableFile(c.sourceLevel+1, t) + db.compactionCommit("table-move", rec) + return + } + + var stats [2]cStatStaging + for i, tables := range c.levels { + for _, t := range tables { + stats[i].read += t.size + // Insert deleted tables into record + rec.delTable(c.sourceLevel+i, t.fd.Num) + } + } + sourceSize := int(stats[0].read + stats[1].read) + minSeq := db.minSeq() + db.logf("table@compaction L%d·%d -> L%d·%d S·%s Q·%d", c.sourceLevel, len(c.levels[0]), c.sourceLevel+1, len(c.levels[1]), shortenb(sourceSize), minSeq) + + b := &tableCompactionBuilder{ + db: db, + s: db.s, + c: c, + rec: rec, + stat1: &stats[1], + minSeq: minSeq, + strict: db.s.o.GetStrict(opt.StrictCompaction), + tableSize: db.s.o.GetCompactionTableSize(c.sourceLevel + 1), + } + db.compactionTransact("table@build", b) + + // Commit. + stats[1].startTimer() + db.compactionCommit("table", rec) + stats[1].stopTimer() + + resultSize := int(stats[1].write) + db.logf("table@compaction committed F%s S%s Ke·%d D·%d T·%v", sint(len(rec.addedTables)-len(rec.deletedTables)), sshortenb(resultSize-sourceSize), b.kerrCnt, b.dropCnt, stats[1].duration) + + // Save compaction stats + for i := range stats { + db.compStats.addStat(c.sourceLevel+1, &stats[i]) + } +} + +func (db *DB) tableRangeCompaction(level int, umin, umax []byte) error { + db.logf("table@compaction range L%d %q:%q", level, umin, umax) + if level >= 0 { + if c := db.s.getCompactionRange(level, umin, umax, true); c != nil { + db.tableCompaction(c, true) + } + } else { + // Retry until nothing to compact. + for { + compacted := false + + // Scan for maximum level with overlapped tables. + v := db.s.version() + m := 1 + for i := m; i < len(v.levels); i++ { + tables := v.levels[i] + if tables.overlaps(db.s.icmp, umin, umax, false) { + m = i + } + } + v.release() + + for level := 0; level < m; level++ { + if c := db.s.getCompactionRange(level, umin, umax, false); c != nil { + db.tableCompaction(c, true) + compacted = true + } + } + + if !compacted { + break + } + } + } + + return nil +} + +func (db *DB) tableAutoCompaction() { + if c := db.s.pickCompaction(); c != nil { + db.tableCompaction(c, false) + } +} + +func (db *DB) tableNeedCompaction() bool { + v := db.s.version() + defer v.release() + return v.needCompaction() +} + +func (db *DB) pauseCompaction(ch chan<- struct{}) { + select { + case ch <- struct{}{}: + case <-db.closeC: + db.compactionExitTransact() + } +} + +type cCmd interface { + ack(err error) +} + +type cAuto struct { + ackC chan<- error +} + +func (r cAuto) ack(err error) { + if r.ackC != nil { + defer func() { + recover() + }() + r.ackC <- err + } +} + +type cRange struct { + level int + min, max []byte + ackC chan<- error +} + +func (r cRange) ack(err error) { + if r.ackC != nil { + defer func() { + recover() + }() + r.ackC <- err + } +} + +// This will trigger auto compaction but will not wait for it. +func (db *DB) compTrigger(compC chan<- cCmd) { + select { + case compC <- cAuto{}: + default: + } +} + +// This will trigger auto compaction and/or wait for all compaction to be done. +func (db *DB) compTriggerWait(compC chan<- cCmd) (err error) { + ch := make(chan error) + defer close(ch) + // Send cmd. + select { + case compC <- cAuto{ch}: + case err = <-db.compErrC: + return + case <-db.closeC: + return ErrClosed + } + // Wait cmd. + select { + case err = <-ch: + case err = <-db.compErrC: + case <-db.closeC: + return ErrClosed + } + return err +} + +// Send range compaction request. +func (db *DB) compTriggerRange(compC chan<- cCmd, level int, min, max []byte) (err error) { + ch := make(chan error) + defer close(ch) + // Send cmd. + select { + case compC <- cRange{level, min, max, ch}: + case err := <-db.compErrC: + return err + case <-db.closeC: + return ErrClosed + } + // Wait cmd. + select { + case err = <-ch: + case err = <-db.compErrC: + case <-db.closeC: + return ErrClosed + } + return err +} + +func (db *DB) mCompaction() { + var x cCmd + + defer func() { + if x := recover(); x != nil { + if x != errCompactionTransactExiting { + panic(x) + } + } + if x != nil { + x.ack(ErrClosed) + } + db.closeW.Done() + }() + + for { + select { + case x = <-db.mcompCmdC: + switch x.(type) { + case cAuto: + db.memCompaction() + x.ack(nil) + x = nil + default: + panic("leveldb: unknown command") + } + case <-db.closeC: + return + } + } +} + +func (db *DB) tCompaction() { + var x cCmd + var ackQ []cCmd + + defer func() { + if x := recover(); x != nil { + if x != errCompactionTransactExiting { + panic(x) + } + } + for i := range ackQ { + ackQ[i].ack(ErrClosed) + ackQ[i] = nil + } + if x != nil { + x.ack(ErrClosed) + } + db.closeW.Done() + }() + + for { + if db.tableNeedCompaction() { + select { + case x = <-db.tcompCmdC: + case ch := <-db.tcompPauseC: + db.pauseCompaction(ch) + continue + case <-db.closeC: + return + default: + } + } else { + for i := range ackQ { + ackQ[i].ack(nil) + ackQ[i] = nil + } + ackQ = ackQ[:0] + select { + case x = <-db.tcompCmdC: + case ch := <-db.tcompPauseC: + db.pauseCompaction(ch) + continue + case <-db.closeC: + return + } + } + if x != nil { + switch cmd := x.(type) { + case cAuto: + ackQ = append(ackQ, x) + case cRange: + x.ack(db.tableRangeCompaction(cmd.level, cmd.min, cmd.max)) + default: + panic("leveldb: unknown command") + } + x = nil + } + db.tableAutoCompaction() + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go new file mode 100644 index 0000000..03c24cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go @@ -0,0 +1,360 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "errors" + "math/rand" + "runtime" + "sync" + "sync/atomic" + + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +var ( + errInvalidInternalKey = errors.New("leveldb: Iterator: invalid internal key") +) + +type memdbReleaser struct { + once sync.Once + m *memDB +} + +func (mr *memdbReleaser) Release() { + mr.once.Do(func() { + mr.m.decref() + }) +} + +func (db *DB) newRawIterator(auxm *memDB, auxt tFiles, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + strict := opt.GetStrict(db.s.o.Options, ro, opt.StrictReader) + em, fm := db.getMems() + v := db.s.version() + + tableIts := v.getIterators(slice, ro) + n := len(tableIts) + len(auxt) + 3 + its := make([]iterator.Iterator, 0, n) + + if auxm != nil { + ami := auxm.NewIterator(slice) + ami.SetReleaser(&memdbReleaser{m: auxm}) + its = append(its, ami) + } + for _, t := range auxt { + its = append(its, v.s.tops.newIterator(t, slice, ro)) + } + + emi := em.NewIterator(slice) + emi.SetReleaser(&memdbReleaser{m: em}) + its = append(its, emi) + if fm != nil { + fmi := fm.NewIterator(slice) + fmi.SetReleaser(&memdbReleaser{m: fm}) + its = append(its, fmi) + } + its = append(its, tableIts...) + mi := iterator.NewMergedIterator(its, db.s.icmp, strict) + mi.SetReleaser(&versionReleaser{v: v}) + return mi +} + +func (db *DB) newIterator(auxm *memDB, auxt tFiles, seq uint64, slice *util.Range, ro *opt.ReadOptions) *dbIter { + var islice *util.Range + if slice != nil { + islice = &util.Range{} + if slice.Start != nil { + islice.Start = makeInternalKey(nil, slice.Start, keyMaxSeq, keyTypeSeek) + } + if slice.Limit != nil { + islice.Limit = makeInternalKey(nil, slice.Limit, keyMaxSeq, keyTypeSeek) + } + } + rawIter := db.newRawIterator(auxm, auxt, islice, ro) + iter := &dbIter{ + db: db, + icmp: db.s.icmp, + iter: rawIter, + seq: seq, + strict: opt.GetStrict(db.s.o.Options, ro, opt.StrictReader), + key: make([]byte, 0), + value: make([]byte, 0), + } + atomic.AddInt32(&db.aliveIters, 1) + runtime.SetFinalizer(iter, (*dbIter).Release) + return iter +} + +func (db *DB) iterSamplingRate() int { + return rand.Intn(2 * db.s.o.GetIteratorSamplingRate()) +} + +type dir int + +const ( + dirReleased dir = iota - 1 + dirSOI + dirEOI + dirBackward + dirForward +) + +// dbIter represent an interator states over a database session. +type dbIter struct { + db *DB + icmp *iComparer + iter iterator.Iterator + seq uint64 + strict bool + + smaplingGap int + dir dir + key []byte + value []byte + err error + releaser util.Releaser +} + +func (i *dbIter) sampleSeek() { + ikey := i.iter.Key() + i.smaplingGap -= len(ikey) + len(i.iter.Value()) + for i.smaplingGap < 0 { + i.smaplingGap += i.db.iterSamplingRate() + i.db.sampleSeek(ikey) + } +} + +func (i *dbIter) setErr(err error) { + i.err = err + i.key = nil + i.value = nil +} + +func (i *dbIter) iterErr() { + if err := i.iter.Error(); err != nil { + i.setErr(err) + } +} + +func (i *dbIter) Valid() bool { + return i.err == nil && i.dir > dirEOI +} + +func (i *dbIter) First() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if i.iter.First() { + i.dir = dirSOI + return i.next() + } + i.dir = dirEOI + i.iterErr() + return false +} + +func (i *dbIter) Last() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if i.iter.Last() { + return i.prev() + } + i.dir = dirSOI + i.iterErr() + return false +} + +func (i *dbIter) Seek(key []byte) bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + ikey := makeInternalKey(nil, key, i.seq, keyTypeSeek) + if i.iter.Seek(ikey) { + i.dir = dirSOI + return i.next() + } + i.dir = dirEOI + i.iterErr() + return false +} + +func (i *dbIter) next() bool { + for { + if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil { + i.sampleSeek() + if seq <= i.seq { + switch kt { + case keyTypeDel: + // Skip deleted key. + i.key = append(i.key[:0], ukey...) + i.dir = dirForward + case keyTypeVal: + if i.dir == dirSOI || i.icmp.uCompare(ukey, i.key) > 0 { + i.key = append(i.key[:0], ukey...) + i.value = append(i.value[:0], i.iter.Value()...) + i.dir = dirForward + return true + } + } + } + } else if i.strict { + i.setErr(kerr) + break + } + if !i.iter.Next() { + i.dir = dirEOI + i.iterErr() + break + } + } + return false +} + +func (i *dbIter) Next() bool { + if i.dir == dirEOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if !i.iter.Next() || (i.dir == dirBackward && !i.iter.Next()) { + i.dir = dirEOI + i.iterErr() + return false + } + return i.next() +} + +func (i *dbIter) prev() bool { + i.dir = dirBackward + del := true + if i.iter.Valid() { + for { + if ukey, seq, kt, kerr := parseInternalKey(i.iter.Key()); kerr == nil { + i.sampleSeek() + if seq <= i.seq { + if !del && i.icmp.uCompare(ukey, i.key) < 0 { + return true + } + del = (kt == keyTypeDel) + if !del { + i.key = append(i.key[:0], ukey...) + i.value = append(i.value[:0], i.iter.Value()...) + } + } + } else if i.strict { + i.setErr(kerr) + return false + } + if !i.iter.Prev() { + break + } + } + } + if del { + i.dir = dirSOI + i.iterErr() + return false + } + return true +} + +func (i *dbIter) Prev() bool { + if i.dir == dirSOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + switch i.dir { + case dirEOI: + return i.Last() + case dirForward: + for i.iter.Prev() { + if ukey, _, _, kerr := parseInternalKey(i.iter.Key()); kerr == nil { + i.sampleSeek() + if i.icmp.uCompare(ukey, i.key) < 0 { + goto cont + } + } else if i.strict { + i.setErr(kerr) + return false + } + } + i.dir = dirSOI + i.iterErr() + return false + } + +cont: + return i.prev() +} + +func (i *dbIter) Key() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.key +} + +func (i *dbIter) Value() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.value +} + +func (i *dbIter) Release() { + if i.dir != dirReleased { + // Clear the finalizer. + runtime.SetFinalizer(i, nil) + + if i.releaser != nil { + i.releaser.Release() + i.releaser = nil + } + + i.dir = dirReleased + i.key = nil + i.value = nil + i.iter.Release() + i.iter = nil + atomic.AddInt32(&i.db.aliveIters, -1) + i.db = nil + } +} + +func (i *dbIter) SetReleaser(releaser util.Releaser) { + if i.dir == dirReleased { + panic(util.ErrReleased) + } + if i.releaser != nil && releaser != nil { + panic(util.ErrHasReleaser) + } + i.releaser = releaser +} + +func (i *dbIter) Error() error { + return i.err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_snapshot.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_snapshot.go new file mode 100644 index 0000000..2c69d2e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_snapshot.go @@ -0,0 +1,183 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "container/list" + "fmt" + "runtime" + "sync" + "sync/atomic" + + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +type snapshotElement struct { + seq uint64 + ref int + e *list.Element +} + +// Acquires a snapshot, based on latest sequence. +func (db *DB) acquireSnapshot() *snapshotElement { + db.snapsMu.Lock() + defer db.snapsMu.Unlock() + + seq := db.getSeq() + + if e := db.snapsList.Back(); e != nil { + se := e.Value.(*snapshotElement) + if se.seq == seq { + se.ref++ + return se + } else if seq < se.seq { + panic("leveldb: sequence number is not increasing") + } + } + se := &snapshotElement{seq: seq, ref: 1} + se.e = db.snapsList.PushBack(se) + return se +} + +// Releases given snapshot element. +func (db *DB) releaseSnapshot(se *snapshotElement) { + db.snapsMu.Lock() + defer db.snapsMu.Unlock() + + se.ref-- + if se.ref == 0 { + db.snapsList.Remove(se.e) + se.e = nil + } else if se.ref < 0 { + panic("leveldb: Snapshot: negative element reference") + } +} + +// Gets minimum sequence that not being snapshotted. +func (db *DB) minSeq() uint64 { + db.snapsMu.Lock() + defer db.snapsMu.Unlock() + + if e := db.snapsList.Front(); e != nil { + return e.Value.(*snapshotElement).seq + } + + return db.getSeq() +} + +// Snapshot is a DB snapshot. +type Snapshot struct { + db *DB + elem *snapshotElement + mu sync.RWMutex + released bool +} + +// Creates new snapshot object. +func (db *DB) newSnapshot() *Snapshot { + snap := &Snapshot{ + db: db, + elem: db.acquireSnapshot(), + } + atomic.AddInt32(&db.aliveSnaps, 1) + runtime.SetFinalizer(snap, (*Snapshot).Release) + return snap +} + +func (snap *Snapshot) String() string { + return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq) +} + +// Get gets the value for the given key. It returns ErrNotFound if +// the DB does not contains the key. +// +// The caller should not modify the contents of the returned slice, but +// it is safe to modify the contents of the argument after Get returns. +func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) { + err = snap.db.ok() + if err != nil { + return + } + snap.mu.RLock() + defer snap.mu.RUnlock() + if snap.released { + err = ErrSnapshotReleased + return + } + return snap.db.get(nil, nil, key, snap.elem.seq, ro) +} + +// Has returns true if the DB does contains the given key. +// +// It is safe to modify the contents of the argument after Get returns. +func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) { + err = snap.db.ok() + if err != nil { + return + } + snap.mu.RLock() + defer snap.mu.RUnlock() + if snap.released { + err = ErrSnapshotReleased + return + } + return snap.db.has(nil, nil, key, snap.elem.seq, ro) +} + +// NewIterator returns an iterator for the snapshot of the underlying DB. +// The returned iterator is not safe for concurrent use, but it is safe to use +// multiple iterators concurrently, with each in a dedicated goroutine. +// It is also safe to use an iterator concurrently with modifying its +// underlying DB. The resultant key/value pairs are guaranteed to be +// consistent. +// +// Slice allows slicing the iterator to only contains keys in the given +// range. A nil Range.Start is treated as a key before all keys in the +// DB. And a nil Range.Limit is treated as a key after all keys in +// the DB. +// +// The iterator must be released after use, by calling Release method. +// Releasing the snapshot doesn't mean releasing the iterator too, the +// iterator would be still valid until released. +// +// Also read Iterator documentation of the leveldb/iterator package. +func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + if err := snap.db.ok(); err != nil { + return iterator.NewEmptyIterator(err) + } + snap.mu.Lock() + defer snap.mu.Unlock() + if snap.released { + return iterator.NewEmptyIterator(ErrSnapshotReleased) + } + // Since iterator already hold version ref, it doesn't need to + // hold snapshot ref. + return snap.db.newIterator(nil, nil, snap.elem.seq, slice, ro) +} + +// Release releases the snapshot. This will not release any returned +// iterators, the iterators would still be valid until released or the +// underlying DB is closed. +// +// Other methods should not be called after the snapshot has been released. +func (snap *Snapshot) Release() { + snap.mu.Lock() + defer snap.mu.Unlock() + + if !snap.released { + // Clear the finalizer. + runtime.SetFinalizer(snap, nil) + + snap.released = true + snap.db.releaseSnapshot(snap.elem) + atomic.AddInt32(&snap.db.aliveSnaps, -1) + snap.db = nil + snap.elem = nil + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_state.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_state.go new file mode 100644 index 0000000..85b02d2 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_state.go @@ -0,0 +1,234 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "sync/atomic" + "time" + + "github.com/syndtr/goleveldb/leveldb/journal" + "github.com/syndtr/goleveldb/leveldb/memdb" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +type memDB struct { + db *DB + *memdb.DB + ref int32 +} + +func (m *memDB) getref() int32 { + return atomic.LoadInt32(&m.ref) +} + +func (m *memDB) incref() { + atomic.AddInt32(&m.ref, 1) +} + +func (m *memDB) decref() { + if ref := atomic.AddInt32(&m.ref, -1); ref == 0 { + // Only put back memdb with std capacity. + if m.Capacity() == m.db.s.o.GetWriteBuffer() { + m.Reset() + m.db.mpoolPut(m.DB) + } + m.db = nil + m.DB = nil + } else if ref < 0 { + panic("negative memdb ref") + } +} + +// Get latest sequence number. +func (db *DB) getSeq() uint64 { + return atomic.LoadUint64(&db.seq) +} + +// Atomically adds delta to seq. +func (db *DB) addSeq(delta uint64) { + atomic.AddUint64(&db.seq, delta) +} + +func (db *DB) setSeq(seq uint64) { + atomic.StoreUint64(&db.seq, seq) +} + +func (db *DB) sampleSeek(ikey internalKey) { + v := db.s.version() + if v.sampleSeek(ikey) { + // Trigger table compaction. + db.compTrigger(db.tcompCmdC) + } + v.release() +} + +func (db *DB) mpoolPut(mem *memdb.DB) { + if !db.isClosed() { + select { + case db.memPool <- mem: + default: + } + } +} + +func (db *DB) mpoolGet(n int) *memDB { + var mdb *memdb.DB + select { + case mdb = <-db.memPool: + default: + } + if mdb == nil || mdb.Capacity() < n { + mdb = memdb.New(db.s.icmp, maxInt(db.s.o.GetWriteBuffer(), n)) + } + return &memDB{ + db: db, + DB: mdb, + } +} + +func (db *DB) mpoolDrain() { + ticker := time.NewTicker(30 * time.Second) + for { + select { + case <-ticker.C: + select { + case <-db.memPool: + default: + } + case <-db.closeC: + ticker.Stop() + // Make sure the pool is drained. + select { + case <-db.memPool: + case <-time.After(time.Second): + } + close(db.memPool) + return + } + } +} + +// Create new memdb and froze the old one; need external synchronization. +// newMem only called synchronously by the writer. +func (db *DB) newMem(n int) (mem *memDB, err error) { + fd := storage.FileDesc{Type: storage.TypeJournal, Num: db.s.allocFileNum()} + w, err := db.s.stor.Create(fd) + if err != nil { + db.s.reuseFileNum(fd.Num) + return + } + + db.memMu.Lock() + defer db.memMu.Unlock() + + if db.frozenMem != nil { + panic("still has frozen mem") + } + + if db.journal == nil { + db.journal = journal.NewWriter(w) + } else { + db.journal.Reset(w) + db.journalWriter.Close() + db.frozenJournalFd = db.journalFd + } + db.journalWriter = w + db.journalFd = fd + db.frozenMem = db.mem + mem = db.mpoolGet(n) + mem.incref() // for self + mem.incref() // for caller + db.mem = mem + // The seq only incremented by the writer. And whoever called newMem + // should hold write lock, so no need additional synchronization here. + db.frozenSeq = db.seq + return +} + +// Get all memdbs. +func (db *DB) getMems() (e, f *memDB) { + db.memMu.RLock() + defer db.memMu.RUnlock() + if db.mem != nil { + db.mem.incref() + } else if !db.isClosed() { + panic("nil effective mem") + } + if db.frozenMem != nil { + db.frozenMem.incref() + } + return db.mem, db.frozenMem +} + +// Get effective memdb. +func (db *DB) getEffectiveMem() *memDB { + db.memMu.RLock() + defer db.memMu.RUnlock() + if db.mem != nil { + db.mem.incref() + } else if !db.isClosed() { + panic("nil effective mem") + } + return db.mem +} + +// Check whether we has frozen memdb. +func (db *DB) hasFrozenMem() bool { + db.memMu.RLock() + defer db.memMu.RUnlock() + return db.frozenMem != nil +} + +// Get frozen memdb. +func (db *DB) getFrozenMem() *memDB { + db.memMu.RLock() + defer db.memMu.RUnlock() + if db.frozenMem != nil { + db.frozenMem.incref() + } + return db.frozenMem +} + +// Drop frozen memdb; assume that frozen memdb isn't nil. +func (db *DB) dropFrozenMem() { + db.memMu.Lock() + if err := db.s.stor.Remove(db.frozenJournalFd); err != nil { + db.logf("journal@remove removing @%d %q", db.frozenJournalFd.Num, err) + } else { + db.logf("journal@remove removed @%d", db.frozenJournalFd.Num) + } + db.frozenJournalFd = storage.FileDesc{} + db.frozenMem.decref() + db.frozenMem = nil + db.memMu.Unlock() +} + +// Clear mems ptr; used by DB.Close(). +func (db *DB) clearMems() { + db.memMu.Lock() + db.mem = nil + db.frozenMem = nil + db.memMu.Unlock() +} + +// Set closed flag; return true if not already closed. +func (db *DB) setClosed() bool { + return atomic.CompareAndSwapUint32(&db.closed, 0, 1) +} + +// Check whether DB was closed. +func (db *DB) isClosed() bool { + return atomic.LoadUint32(&db.closed) != 0 +} + +// Check read ok status. +func (db *DB) ok() error { + if db.isClosed() { + return ErrClosed + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go new file mode 100644 index 0000000..b8f7e7d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go @@ -0,0 +1,325 @@ +// Copyright (c) 2016, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "errors" + "sync" + "time" + + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +var errTransactionDone = errors.New("leveldb: transaction already closed") + +// Transaction is the transaction handle. +type Transaction struct { + db *DB + lk sync.RWMutex + seq uint64 + mem *memDB + tables tFiles + ikScratch []byte + rec sessionRecord + stats cStatStaging + closed bool +} + +// Get gets the value for the given key. It returns ErrNotFound if the +// DB does not contains the key. +// +// The returned slice is its own copy, it is safe to modify the contents +// of the returned slice. +// It is safe to modify the contents of the argument after Get returns. +func (tr *Transaction) Get(key []byte, ro *opt.ReadOptions) ([]byte, error) { + tr.lk.RLock() + defer tr.lk.RUnlock() + if tr.closed { + return nil, errTransactionDone + } + return tr.db.get(tr.mem.DB, tr.tables, key, tr.seq, ro) +} + +// Has returns true if the DB does contains the given key. +// +// It is safe to modify the contents of the argument after Has returns. +func (tr *Transaction) Has(key []byte, ro *opt.ReadOptions) (bool, error) { + tr.lk.RLock() + defer tr.lk.RUnlock() + if tr.closed { + return false, errTransactionDone + } + return tr.db.has(tr.mem.DB, tr.tables, key, tr.seq, ro) +} + +// NewIterator returns an iterator for the latest snapshot of the transaction. +// The returned iterator is not safe for concurrent use, but it is safe to use +// multiple iterators concurrently, with each in a dedicated goroutine. +// It is also safe to use an iterator concurrently while writes to the +// transaction. The resultant key/value pairs are guaranteed to be consistent. +// +// Slice allows slicing the iterator to only contains keys in the given +// range. A nil Range.Start is treated as a key before all keys in the +// DB. And a nil Range.Limit is treated as a key after all keys in +// the DB. +// +// The iterator must be released after use, by calling Release method. +// +// Also read Iterator documentation of the leveldb/iterator package. +func (tr *Transaction) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + tr.lk.RLock() + defer tr.lk.RUnlock() + if tr.closed { + return iterator.NewEmptyIterator(errTransactionDone) + } + tr.mem.incref() + return tr.db.newIterator(tr.mem, tr.tables, tr.seq, slice, ro) +} + +func (tr *Transaction) flush() error { + // Flush memdb. + if tr.mem.Len() != 0 { + tr.stats.startTimer() + iter := tr.mem.NewIterator(nil) + t, n, err := tr.db.s.tops.createFrom(iter) + iter.Release() + tr.stats.stopTimer() + if err != nil { + return err + } + if tr.mem.getref() == 1 { + tr.mem.Reset() + } else { + tr.mem.decref() + tr.mem = tr.db.mpoolGet(0) + tr.mem.incref() + } + tr.tables = append(tr.tables, t) + tr.rec.addTableFile(0, t) + tr.stats.write += t.size + tr.db.logf("transaction@flush created L0@%d N·%d S·%s %q:%q", t.fd.Num, n, shortenb(int(t.size)), t.imin, t.imax) + } + return nil +} + +func (tr *Transaction) put(kt keyType, key, value []byte) error { + tr.ikScratch = makeInternalKey(tr.ikScratch, key, tr.seq+1, kt) + if tr.mem.Free() < len(tr.ikScratch)+len(value) { + if err := tr.flush(); err != nil { + return err + } + } + if err := tr.mem.Put(tr.ikScratch, value); err != nil { + return err + } + tr.seq++ + return nil +} + +// Put sets the value for the given key. It overwrites any previous value +// for that key; a DB is not a multi-map. +// Please note that the transaction is not compacted until committed, so if you +// writes 10 same keys, then those 10 same keys are in the transaction. +// +// It is safe to modify the contents of the arguments after Put returns. +func (tr *Transaction) Put(key, value []byte, wo *opt.WriteOptions) error { + tr.lk.Lock() + defer tr.lk.Unlock() + if tr.closed { + return errTransactionDone + } + return tr.put(keyTypeVal, key, value) +} + +// Delete deletes the value for the given key. +// Please note that the transaction is not compacted until committed, so if you +// writes 10 same keys, then those 10 same keys are in the transaction. +// +// It is safe to modify the contents of the arguments after Delete returns. +func (tr *Transaction) Delete(key []byte, wo *opt.WriteOptions) error { + tr.lk.Lock() + defer tr.lk.Unlock() + if tr.closed { + return errTransactionDone + } + return tr.put(keyTypeDel, key, nil) +} + +// Write apply the given batch to the transaction. The batch will be applied +// sequentially. +// Please note that the transaction is not compacted until committed, so if you +// writes 10 same keys, then those 10 same keys are in the transaction. +// +// It is safe to modify the contents of the arguments after Write returns. +func (tr *Transaction) Write(b *Batch, wo *opt.WriteOptions) error { + if b == nil || b.Len() == 0 { + return nil + } + + tr.lk.Lock() + defer tr.lk.Unlock() + if tr.closed { + return errTransactionDone + } + return b.replayInternal(func(i int, kt keyType, k, v []byte) error { + return tr.put(kt, k, v) + }) +} + +func (tr *Transaction) setDone() { + tr.closed = true + tr.db.tr = nil + tr.mem.decref() + <-tr.db.writeLockC +} + +// Commit commits the transaction. If error is not nil, then the transaction is +// not committed, it can then either be retried or discarded. +// +// Other methods should not be called after transaction has been committed. +func (tr *Transaction) Commit() error { + if err := tr.db.ok(); err != nil { + return err + } + + tr.lk.Lock() + defer tr.lk.Unlock() + if tr.closed { + return errTransactionDone + } + if err := tr.flush(); err != nil { + // Return error, lets user decide either to retry or discard + // transaction. + return err + } + if len(tr.tables) != 0 { + // Committing transaction. + tr.rec.setSeqNum(tr.seq) + tr.db.compCommitLk.Lock() + tr.stats.startTimer() + var cerr error + for retry := 0; retry < 3; retry++ { + cerr = tr.db.s.commit(&tr.rec) + if cerr != nil { + tr.db.logf("transaction@commit error R·%d %q", retry, cerr) + select { + case <-time.After(time.Second): + case <-tr.db.closeC: + tr.db.logf("transaction@commit exiting") + tr.db.compCommitLk.Unlock() + return cerr + } + } else { + // Success. Set db.seq. + tr.db.setSeq(tr.seq) + break + } + } + tr.stats.stopTimer() + if cerr != nil { + // Return error, lets user decide either to retry or discard + // transaction. + return cerr + } + + // Update compaction stats. This is safe as long as we hold compCommitLk. + tr.db.compStats.addStat(0, &tr.stats) + + // Trigger table auto-compaction. + tr.db.compTrigger(tr.db.tcompCmdC) + tr.db.compCommitLk.Unlock() + + // Additionally, wait compaction when certain threshold reached. + // Ignore error, returns error only if transaction can't be committed. + tr.db.waitCompaction() + } + // Only mark as done if transaction committed successfully. + tr.setDone() + return nil +} + +func (tr *Transaction) discard() { + // Discard transaction. + for _, t := range tr.tables { + tr.db.logf("transaction@discard @%d", t.fd.Num) + if err1 := tr.db.s.stor.Remove(t.fd); err1 == nil { + tr.db.s.reuseFileNum(t.fd.Num) + } + } +} + +// Discard discards the transaction. +// +// Other methods should not be called after transaction has been discarded. +func (tr *Transaction) Discard() { + tr.lk.Lock() + if !tr.closed { + tr.discard() + tr.setDone() + } + tr.lk.Unlock() +} + +func (db *DB) waitCompaction() error { + if db.s.tLen(0) >= db.s.o.GetWriteL0PauseTrigger() { + return db.compTriggerWait(db.tcompCmdC) + } + return nil +} + +// OpenTransaction opens an atomic DB transaction. Only one transaction can be +// opened at a time. Subsequent call to Write and OpenTransaction will be blocked +// until in-flight transaction is committed or discarded. +// The returned transaction handle is safe for concurrent use. +// +// Transaction is expensive and can overwhelm compaction, especially if +// transaction size is small. Use with caution. +// +// The transaction must be closed once done, either by committing or discarding +// the transaction. +// Closing the DB will discard open transaction. +func (db *DB) OpenTransaction() (*Transaction, error) { + if err := db.ok(); err != nil { + return nil, err + } + + // The write happen synchronously. + select { + case db.writeLockC <- struct{}{}: + case err := <-db.compPerErrC: + return nil, err + case <-db.closeC: + return nil, ErrClosed + } + + if db.tr != nil { + panic("leveldb: has open transaction") + } + + // Flush current memdb. + if db.mem != nil && db.mem.Len() != 0 { + if _, err := db.rotateMem(0, true); err != nil { + return nil, err + } + } + + // Wait compaction when certain threshold reached. + if err := db.waitCompaction(); err != nil { + return nil, err + } + + tr := &Transaction{ + db: db, + seq: db.seq, + mem: db.mpoolGet(0), + } + tr.mem.incref() + db.tr = tr + return tr, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go new file mode 100644 index 0000000..7ecd960 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go @@ -0,0 +1,102 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// Reader is the interface that wraps basic Get and NewIterator methods. +// This interface implemented by both DB and Snapshot. +type Reader interface { + Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) + NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator +} + +// Sizes is list of size. +type Sizes []int64 + +// Sum returns sum of the sizes. +func (sizes Sizes) Sum() int64 { + var sum int64 + for _, size := range sizes { + sum += size + } + return sum +} + +// Logging. +func (db *DB) log(v ...interface{}) { db.s.log(v...) } +func (db *DB) logf(format string, v ...interface{}) { db.s.logf(format, v...) } + +// Check and clean files. +func (db *DB) checkAndCleanFiles() error { + v := db.s.version() + defer v.release() + + tmap := make(map[int64]bool) + for _, tables := range v.levels { + for _, t := range tables { + tmap[t.fd.Num] = false + } + } + + fds, err := db.s.stor.List(storage.TypeAll) + if err != nil { + return err + } + + var nt int + var rem []storage.FileDesc + for _, fd := range fds { + keep := true + switch fd.Type { + case storage.TypeManifest: + keep = fd.Num >= db.s.manifestFd.Num + case storage.TypeJournal: + if !db.frozenJournalFd.Zero() { + keep = fd.Num >= db.frozenJournalFd.Num + } else { + keep = fd.Num >= db.journalFd.Num + } + case storage.TypeTable: + _, keep = tmap[fd.Num] + if keep { + tmap[fd.Num] = true + nt++ + } + } + + if !keep { + rem = append(rem, fd) + } + } + + if nt != len(tmap) { + var mfds []storage.FileDesc + for num, present := range tmap { + if !present { + mfds = append(mfds, storage.FileDesc{storage.TypeTable, num}) + db.logf("db@janitor table missing @%d", num) + } + } + return errors.NewErrCorrupted(storage.FileDesc{}, &errors.ErrMissingFiles{Fds: mfds}) + } + + db.logf("db@janitor F·%d G·%d", len(fds), len(rem)) + for _, fd := range rem { + db.logf("db@janitor removing %s-%d", fd.Type, fd.Num) + if err := db.s.stor.Remove(fd); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_write.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_write.go new file mode 100644 index 0000000..cc428b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/db_write.go @@ -0,0 +1,443 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "time" + + "github.com/syndtr/goleveldb/leveldb/memdb" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +func (db *DB) writeJournal(batches []*Batch, seq uint64, sync bool) error { + wr, err := db.journal.Next() + if err != nil { + return err + } + if err := writeBatchesWithHeader(wr, batches, seq); err != nil { + return err + } + if err := db.journal.Flush(); err != nil { + return err + } + if sync { + return db.journalWriter.Sync() + } + return nil +} + +func (db *DB) rotateMem(n int, wait bool) (mem *memDB, err error) { + // Wait for pending memdb compaction. + err = db.compTriggerWait(db.mcompCmdC) + if err != nil { + return + } + + // Create new memdb and journal. + mem, err = db.newMem(n) + if err != nil { + return + } + + // Schedule memdb compaction. + if wait { + err = db.compTriggerWait(db.mcompCmdC) + } else { + db.compTrigger(db.mcompCmdC) + } + return +} + +func (db *DB) flush(n int) (mdb *memDB, mdbFree int, err error) { + delayed := false + slowdownTrigger := db.s.o.GetWriteL0SlowdownTrigger() + pauseTrigger := db.s.o.GetWriteL0PauseTrigger() + flush := func() (retry bool) { + mdb = db.getEffectiveMem() + if mdb == nil { + err = ErrClosed + return false + } + defer func() { + if retry { + mdb.decref() + mdb = nil + } + }() + tLen := db.s.tLen(0) + mdbFree = mdb.Free() + switch { + case tLen >= slowdownTrigger && !delayed: + delayed = true + time.Sleep(time.Millisecond) + case mdbFree >= n: + return false + case tLen >= pauseTrigger: + delayed = true + err = db.compTriggerWait(db.tcompCmdC) + if err != nil { + return false + } + default: + // Allow memdb to grow if it has no entry. + if mdb.Len() == 0 { + mdbFree = n + } else { + mdb.decref() + mdb, err = db.rotateMem(n, false) + if err == nil { + mdbFree = mdb.Free() + } else { + mdbFree = 0 + } + } + return false + } + return true + } + start := time.Now() + for flush() { + } + if delayed { + db.writeDelay += time.Since(start) + db.writeDelayN++ + } else if db.writeDelayN > 0 { + db.logf("db@write was delayed N·%d T·%v", db.writeDelayN, db.writeDelay) + db.writeDelay = 0 + db.writeDelayN = 0 + } + return +} + +type writeMerge struct { + sync bool + batch *Batch + keyType keyType + key, value []byte +} + +func (db *DB) unlockWrite(overflow bool, merged int, err error) { + for i := 0; i < merged; i++ { + db.writeAckC <- err + } + if overflow { + // Pass lock to the next write (that failed to merge). + db.writeMergedC <- false + } else { + // Release lock. + <-db.writeLockC + } +} + +// ourBatch if defined should equal with batch. +func (db *DB) writeLocked(batch, ourBatch *Batch, merge, sync bool) error { + // Try to flush memdb. This method would also trying to throttle writes + // if it is too fast and compaction cannot catch-up. + mdb, mdbFree, err := db.flush(batch.internalLen) + if err != nil { + db.unlockWrite(false, 0, err) + return err + } + defer mdb.decref() + + var ( + overflow bool + merged int + batches = []*Batch{batch} + ) + + if merge { + // Merge limit. + var mergeLimit int + if batch.internalLen > 128<<10 { + mergeLimit = (1 << 20) - batch.internalLen + } else { + mergeLimit = 128 << 10 + } + mergeCap := mdbFree - batch.internalLen + if mergeLimit > mergeCap { + mergeLimit = mergeCap + } + + merge: + for mergeLimit > 0 { + select { + case incoming := <-db.writeMergeC: + if incoming.batch != nil { + // Merge batch. + if incoming.batch.internalLen > mergeLimit { + overflow = true + break merge + } + batches = append(batches, incoming.batch) + mergeLimit -= incoming.batch.internalLen + } else { + // Merge put. + internalLen := len(incoming.key) + len(incoming.value) + 8 + if internalLen > mergeLimit { + overflow = true + break merge + } + if ourBatch == nil { + ourBatch = db.batchPool.Get().(*Batch) + ourBatch.Reset() + batches = append(batches, ourBatch) + } + // We can use same batch since concurrent write doesn't + // guarantee write order. + ourBatch.appendRec(incoming.keyType, incoming.key, incoming.value) + mergeLimit -= internalLen + } + sync = sync || incoming.sync + merged++ + db.writeMergedC <- true + + default: + break merge + } + } + } + + // Seq number. + seq := db.seq + 1 + + // Write journal. + if err := db.writeJournal(batches, seq, sync); err != nil { + db.unlockWrite(overflow, merged, err) + return err + } + + // Put batches. + for _, batch := range batches { + if err := batch.putMem(seq, mdb.DB); err != nil { + panic(err) + } + seq += uint64(batch.Len()) + } + + // Incr seq number. + db.addSeq(uint64(batchesLen(batches))) + + // Rotate memdb if it's reach the threshold. + if batch.internalLen >= mdbFree { + db.rotateMem(0, false) + } + + db.unlockWrite(overflow, merged, nil) + return nil +} + +// Write apply the given batch to the DB. The batch records will be applied +// sequentially. Write might be used concurrently, when used concurrently and +// batch is small enough, write will try to merge the batches. Set NoWriteMerge +// option to true to disable write merge. +// +// It is safe to modify the contents of the arguments after Write returns but +// not before. Write will not modify content of the batch. +func (db *DB) Write(batch *Batch, wo *opt.WriteOptions) error { + if err := db.ok(); err != nil || batch == nil || batch.Len() == 0 { + return err + } + + // If the batch size is larger than write buffer, it may justified to write + // using transaction instead. Using transaction the batch will be written + // into tables directly, skipping the journaling. + if batch.internalLen > db.s.o.GetWriteBuffer() && !db.s.o.GetDisableLargeBatchTransaction() { + tr, err := db.OpenTransaction() + if err != nil { + return err + } + if err := tr.Write(batch, wo); err != nil { + tr.Discard() + return err + } + return tr.Commit() + } + + merge := !wo.GetNoWriteMerge() && !db.s.o.GetNoWriteMerge() + sync := wo.GetSync() && !db.s.o.GetNoSync() + + // Acquire write lock. + if merge { + select { + case db.writeMergeC <- writeMerge{sync: sync, batch: batch}: + if <-db.writeMergedC { + // Write is merged. + return <-db.writeAckC + } + // Write is not merged, the write lock is handed to us. Continue. + case db.writeLockC <- struct{}{}: + // Write lock acquired. + case err := <-db.compPerErrC: + // Compaction error. + return err + case <-db.closeC: + // Closed + return ErrClosed + } + } else { + select { + case db.writeLockC <- struct{}{}: + // Write lock acquired. + case err := <-db.compPerErrC: + // Compaction error. + return err + case <-db.closeC: + // Closed + return ErrClosed + } + } + + return db.writeLocked(batch, nil, merge, sync) +} + +func (db *DB) putRec(kt keyType, key, value []byte, wo *opt.WriteOptions) error { + if err := db.ok(); err != nil { + return err + } + + merge := !wo.GetNoWriteMerge() && !db.s.o.GetNoWriteMerge() + sync := wo.GetSync() && !db.s.o.GetNoSync() + + // Acquire write lock. + if merge { + select { + case db.writeMergeC <- writeMerge{sync: sync, keyType: kt, key: key, value: value}: + if <-db.writeMergedC { + // Write is merged. + return <-db.writeAckC + } + // Write is not merged, the write lock is handed to us. Continue. + case db.writeLockC <- struct{}{}: + // Write lock acquired. + case err := <-db.compPerErrC: + // Compaction error. + return err + case <-db.closeC: + // Closed + return ErrClosed + } + } else { + select { + case db.writeLockC <- struct{}{}: + // Write lock acquired. + case err := <-db.compPerErrC: + // Compaction error. + return err + case <-db.closeC: + // Closed + return ErrClosed + } + } + + batch := db.batchPool.Get().(*Batch) + batch.Reset() + batch.appendRec(kt, key, value) + return db.writeLocked(batch, batch, merge, sync) +} + +// Put sets the value for the given key. It overwrites any previous value +// for that key; a DB is not a multi-map. Write merge also applies for Put, see +// Write. +// +// It is safe to modify the contents of the arguments after Put returns but not +// before. +func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error { + return db.putRec(keyTypeVal, key, value, wo) +} + +// Delete deletes the value for the given key. Delete will not returns error if +// key doesn't exist. Write merge also applies for Delete, see Write. +// +// It is safe to modify the contents of the arguments after Delete returns but +// not before. +func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error { + return db.putRec(keyTypeDel, key, nil, wo) +} + +func isMemOverlaps(icmp *iComparer, mem *memdb.DB, min, max []byte) bool { + iter := mem.NewIterator(nil) + defer iter.Release() + return (max == nil || (iter.First() && icmp.uCompare(max, internalKey(iter.Key()).ukey()) >= 0)) && + (min == nil || (iter.Last() && icmp.uCompare(min, internalKey(iter.Key()).ukey()) <= 0)) +} + +// CompactRange compacts the underlying DB for the given key range. +// In particular, deleted and overwritten versions are discarded, +// and the data is rearranged to reduce the cost of operations +// needed to access the data. This operation should typically only +// be invoked by users who understand the underlying implementation. +// +// A nil Range.Start is treated as a key before all keys in the DB. +// And a nil Range.Limit is treated as a key after all keys in the DB. +// Therefore if both is nil then it will compact entire DB. +func (db *DB) CompactRange(r util.Range) error { + if err := db.ok(); err != nil { + return err + } + + // Lock writer. + select { + case db.writeLockC <- struct{}{}: + case err := <-db.compPerErrC: + return err + case <-db.closeC: + return ErrClosed + } + + // Check for overlaps in memdb. + mdb := db.getEffectiveMem() + if mdb == nil { + return ErrClosed + } + defer mdb.decref() + if isMemOverlaps(db.s.icmp, mdb.DB, r.Start, r.Limit) { + // Memdb compaction. + if _, err := db.rotateMem(0, false); err != nil { + <-db.writeLockC + return err + } + <-db.writeLockC + if err := db.compTriggerWait(db.mcompCmdC); err != nil { + return err + } + } else { + <-db.writeLockC + } + + // Table compaction. + return db.compTriggerRange(db.tcompCmdC, -1, r.Start, r.Limit) +} + +// SetReadOnly makes DB read-only. It will stay read-only until reopened. +func (db *DB) SetReadOnly() error { + if err := db.ok(); err != nil { + return err + } + + // Lock writer. + select { + case db.writeLockC <- struct{}{}: + db.compWriteLocking = true + case err := <-db.compPerErrC: + return err + case <-db.closeC: + return ErrClosed + } + + // Set compaction read-only. + select { + case db.compErrSetC <- ErrReadOnly: + case perr := <-db.compPerErrC: + return perr + case <-db.closeC: + return ErrClosed + } + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/doc.go new file mode 100644 index 0000000..53f13bb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/doc.go @@ -0,0 +1,90 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package leveldb provides implementation of LevelDB key/value database. +// +// Create or open a database: +// +// db, err := leveldb.OpenFile("path/to/db", nil) +// ... +// defer db.Close() +// ... +// +// Read or modify the database content: +// +// // Remember that the contents of the returned slice should not be modified. +// data, err := db.Get([]byte("key"), nil) +// ... +// err = db.Put([]byte("key"), []byte("value"), nil) +// ... +// err = db.Delete([]byte("key"), nil) +// ... +// +// Iterate over database content: +// +// iter := db.NewIterator(nil, nil) +// for iter.Next() { +// // Remember that the contents of the returned slice should not be modified, and +// // only valid until the next call to Next. +// key := iter.Key() +// value := iter.Value() +// ... +// } +// iter.Release() +// err = iter.Error() +// ... +// +// Iterate over subset of database content with a particular prefix: +// iter := db.NewIterator(util.BytesPrefix([]byte("foo-")), nil) +// for iter.Next() { +// // Use key/value. +// ... +// } +// iter.Release() +// err = iter.Error() +// ... +// +// Seek-then-Iterate: +// +// iter := db.NewIterator(nil, nil) +// for ok := iter.Seek(key); ok; ok = iter.Next() { +// // Use key/value. +// ... +// } +// iter.Release() +// err = iter.Error() +// ... +// +// Iterate over subset of database content: +// +// iter := db.NewIterator(&util.Range{Start: []byte("foo"), Limit: []byte("xoo")}, nil) +// for iter.Next() { +// // Use key/value. +// ... +// } +// iter.Release() +// err = iter.Error() +// ... +// +// Batch writes: +// +// batch := new(leveldb.Batch) +// batch.Put([]byte("foo"), []byte("value")) +// batch.Put([]byte("bar"), []byte("another value")) +// batch.Delete([]byte("baz")) +// err = db.Write(batch, nil) +// ... +// +// Use bloom filter: +// +// o := &opt.Options{ +// Filter: filter.NewBloomFilter(10), +// } +// db, err := leveldb.OpenFile("path/to/db", o) +// ... +// defer db.Close() +// ... +package leveldb diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors.go new file mode 100644 index 0000000..de26498 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors.go @@ -0,0 +1,20 @@ +// Copyright (c) 2014, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "github.com/syndtr/goleveldb/leveldb/errors" +) + +// Common errors. +var ( + ErrNotFound = errors.ErrNotFound + ErrReadOnly = errors.New("leveldb: read-only mode") + ErrSnapshotReleased = errors.New("leveldb: snapshot released") + ErrIterReleased = errors.New("leveldb: iterator released") + ErrClosed = errors.New("leveldb: closed") +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors/errors.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors/errors.go new file mode 100644 index 0000000..8d6146b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/errors/errors.go @@ -0,0 +1,78 @@ +// Copyright (c) 2014, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package errors provides common error types used throughout leveldb. +package errors + +import ( + "errors" + "fmt" + + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// Common errors. +var ( + ErrNotFound = New("leveldb: not found") + ErrReleased = util.ErrReleased + ErrHasReleaser = util.ErrHasReleaser +) + +// New returns an error that formats as the given text. +func New(text string) error { + return errors.New(text) +} + +// ErrCorrupted is the type that wraps errors that indicate corruption in +// the database. +type ErrCorrupted struct { + Fd storage.FileDesc + Err error +} + +func (e *ErrCorrupted) Error() string { + if !e.Fd.Zero() { + return fmt.Sprintf("%v [file=%v]", e.Err, e.Fd) + } + return e.Err.Error() +} + +// NewErrCorrupted creates new ErrCorrupted error. +func NewErrCorrupted(fd storage.FileDesc, err error) error { + return &ErrCorrupted{fd, err} +} + +// IsCorrupted returns a boolean indicating whether the error is indicating +// a corruption. +func IsCorrupted(err error) bool { + switch err.(type) { + case *ErrCorrupted: + return true + case *storage.ErrCorrupted: + return true + } + return false +} + +// ErrMissingFiles is the type that indicating a corruption due to missing +// files. ErrMissingFiles always wrapped with ErrCorrupted. +type ErrMissingFiles struct { + Fds []storage.FileDesc +} + +func (e *ErrMissingFiles) Error() string { return "file missing" } + +// SetFd sets 'file info' of the given error with the given file. +// Currently only ErrCorrupted is supported, otherwise will do nothing. +func SetFd(err error, fd storage.FileDesc) error { + switch x := err.(type) { + case *ErrCorrupted: + x.Fd = fd + return x + } + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter.go new file mode 100644 index 0000000..e961e42 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter.go @@ -0,0 +1,31 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "github.com/syndtr/goleveldb/leveldb/filter" +) + +type iFilter struct { + filter.Filter +} + +func (f iFilter) Contains(filter, key []byte) bool { + return f.Filter.Contains(filter, internalKey(key).ukey()) +} + +func (f iFilter) NewGenerator() filter.FilterGenerator { + return iFilterGenerator{f.Filter.NewGenerator()} +} + +type iFilterGenerator struct { + filter.FilterGenerator +} + +func (g iFilterGenerator) Add(key []byte) { + g.FilterGenerator.Add(internalKey(key).ukey()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/bloom.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/bloom.go new file mode 100644 index 0000000..bab0e99 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/bloom.go @@ -0,0 +1,116 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package filter + +import ( + "github.com/syndtr/goleveldb/leveldb/util" +) + +func bloomHash(key []byte) uint32 { + return util.Hash(key, 0xbc9f1d34) +} + +type bloomFilter int + +// The bloom filter serializes its parameters and is backward compatible +// with respect to them. Therefor, its parameters are not added to its +// name. +func (bloomFilter) Name() string { + return "leveldb.BuiltinBloomFilter" +} + +func (f bloomFilter) Contains(filter, key []byte) bool { + nBytes := len(filter) - 1 + if nBytes < 1 { + return false + } + nBits := uint32(nBytes * 8) + + // Use the encoded k so that we can read filters generated by + // bloom filters created using different parameters. + k := filter[nBytes] + if k > 30 { + // Reserved for potentially new encodings for short bloom filters. + // Consider it a match. + return true + } + + kh := bloomHash(key) + delta := (kh >> 17) | (kh << 15) // Rotate right 17 bits + for j := uint8(0); j < k; j++ { + bitpos := kh % nBits + if (uint32(filter[bitpos/8]) & (1 << (bitpos % 8))) == 0 { + return false + } + kh += delta + } + return true +} + +func (f bloomFilter) NewGenerator() FilterGenerator { + // Round down to reduce probing cost a little bit. + k := uint8(f * 69 / 100) // 0.69 =~ ln(2) + if k < 1 { + k = 1 + } else if k > 30 { + k = 30 + } + return &bloomFilterGenerator{ + n: int(f), + k: k, + } +} + +type bloomFilterGenerator struct { + n int + k uint8 + + keyHashes []uint32 +} + +func (g *bloomFilterGenerator) Add(key []byte) { + // Use double-hashing to generate a sequence of hash values. + // See analysis in [Kirsch,Mitzenmacher 2006]. + g.keyHashes = append(g.keyHashes, bloomHash(key)) +} + +func (g *bloomFilterGenerator) Generate(b Buffer) { + // Compute bloom filter size (in both bits and bytes) + nBits := uint32(len(g.keyHashes) * g.n) + // For small n, we can see a very high false positive rate. Fix it + // by enforcing a minimum bloom filter length. + if nBits < 64 { + nBits = 64 + } + nBytes := (nBits + 7) / 8 + nBits = nBytes * 8 + + dest := b.Alloc(int(nBytes) + 1) + dest[nBytes] = g.k + for _, kh := range g.keyHashes { + delta := (kh >> 17) | (kh << 15) // Rotate right 17 bits + for j := uint8(0); j < g.k; j++ { + bitpos := kh % nBits + dest[bitpos/8] |= (1 << (bitpos % 8)) + kh += delta + } + } + + g.keyHashes = g.keyHashes[:0] +} + +// NewBloomFilter creates a new initialized bloom filter for given +// bitsPerKey. +// +// Since bitsPerKey is persisted individually for each bloom filter +// serialization, bloom filters are backwards compatible with respect to +// changing bitsPerKey. This means that no big performance penalty will +// be experienced when changing the parameter. See documentation for +// opt.Options.Filter for more information. +func NewBloomFilter(bitsPerKey int) Filter { + return bloomFilter(bitsPerKey) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/filter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/filter.go new file mode 100644 index 0000000..7a925c5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/filter/filter.go @@ -0,0 +1,60 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package filter provides interface and implementation of probabilistic +// data structure. +// +// The filter is resposible for creating small filter from a set of keys. +// These filter will then used to test whether a key is a member of the set. +// In many cases, a filter can cut down the number of disk seeks from a +// handful to a single disk seek per DB.Get call. +package filter + +// Buffer is the interface that wraps basic Alloc, Write and WriteByte methods. +type Buffer interface { + // Alloc allocs n bytes of slice from the buffer. This also advancing + // write offset. + Alloc(n int) []byte + + // Write appends the contents of p to the buffer. + Write(p []byte) (n int, err error) + + // WriteByte appends the byte c to the buffer. + WriteByte(c byte) error +} + +// Filter is the filter. +type Filter interface { + // Name returns the name of this policy. + // + // Note that if the filter encoding changes in an incompatible way, + // the name returned by this method must be changed. Otherwise, old + // incompatible filters may be passed to methods of this type. + Name() string + + // NewGenerator creates a new filter generator. + NewGenerator() FilterGenerator + + // Contains returns true if the filter contains the given key. + // + // The filter are filters generated by the filter generator. + Contains(filter, key []byte) bool +} + +// FilterGenerator is the filter generator. +type FilterGenerator interface { + // Add adds a key to the filter generator. + // + // The key may become invalid after call to this method end, therefor + // key must be copied if implementation require keeping key for later + // use. The key should not modified directly, doing so may cause + // undefined results. + Add(key []byte) + + // Generate generates filters based on keys passed so far. After call + // to Generate the filter generator maybe resetted, depends on implementation. + Generate(b Buffer) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/array_iter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/array_iter.go new file mode 100644 index 0000000..a23ab05 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/array_iter.go @@ -0,0 +1,184 @@ +// Copyright (c) 2014, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package iterator + +import ( + "github.com/syndtr/goleveldb/leveldb/util" +) + +// BasicArray is the interface that wraps basic Len and Search method. +type BasicArray interface { + // Len returns length of the array. + Len() int + + // Search finds smallest index that point to a key that is greater + // than or equal to the given key. + Search(key []byte) int +} + +// Array is the interface that wraps BasicArray and basic Index method. +type Array interface { + BasicArray + + // Index returns key/value pair with index of i. + Index(i int) (key, value []byte) +} + +// Array is the interface that wraps BasicArray and basic Get method. +type ArrayIndexer interface { + BasicArray + + // Get returns a new data iterator with index of i. + Get(i int) Iterator +} + +type basicArrayIterator struct { + util.BasicReleaser + array BasicArray + pos int + err error +} + +func (i *basicArrayIterator) Valid() bool { + return i.pos >= 0 && i.pos < i.array.Len() && !i.Released() +} + +func (i *basicArrayIterator) First() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + if i.array.Len() == 0 { + i.pos = -1 + return false + } + i.pos = 0 + return true +} + +func (i *basicArrayIterator) Last() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + n := i.array.Len() + if n == 0 { + i.pos = 0 + return false + } + i.pos = n - 1 + return true +} + +func (i *basicArrayIterator) Seek(key []byte) bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + n := i.array.Len() + if n == 0 { + i.pos = 0 + return false + } + i.pos = i.array.Search(key) + if i.pos >= n { + return false + } + return true +} + +func (i *basicArrayIterator) Next() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + i.pos++ + if n := i.array.Len(); i.pos >= n { + i.pos = n + return false + } + return true +} + +func (i *basicArrayIterator) Prev() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + i.pos-- + if i.pos < 0 { + i.pos = -1 + return false + } + return true +} + +func (i *basicArrayIterator) Error() error { return i.err } + +type arrayIterator struct { + basicArrayIterator + array Array + pos int + key, value []byte +} + +func (i *arrayIterator) updateKV() { + if i.pos == i.basicArrayIterator.pos { + return + } + i.pos = i.basicArrayIterator.pos + if i.Valid() { + i.key, i.value = i.array.Index(i.pos) + } else { + i.key = nil + i.value = nil + } +} + +func (i *arrayIterator) Key() []byte { + i.updateKV() + return i.key +} + +func (i *arrayIterator) Value() []byte { + i.updateKV() + return i.value +} + +type arrayIteratorIndexer struct { + basicArrayIterator + array ArrayIndexer +} + +func (i *arrayIteratorIndexer) Get() Iterator { + if i.Valid() { + return i.array.Get(i.basicArrayIterator.pos) + } + return nil +} + +// NewArrayIterator returns an iterator from the given array. +func NewArrayIterator(array Array) Iterator { + return &arrayIterator{ + basicArrayIterator: basicArrayIterator{array: array, pos: -1}, + array: array, + pos: -1, + } +} + +// NewArrayIndexer returns an index iterator from the given array. +func NewArrayIndexer(array ArrayIndexer) IteratorIndexer { + return &arrayIteratorIndexer{ + basicArrayIterator: basicArrayIterator{array: array, pos: -1}, + array: array, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/indexed_iter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/indexed_iter.go new file mode 100644 index 0000000..939adbb --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/indexed_iter.go @@ -0,0 +1,242 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package iterator + +import ( + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// IteratorIndexer is the interface that wraps CommonIterator and basic Get +// method. IteratorIndexer provides index for indexed iterator. +type IteratorIndexer interface { + CommonIterator + + // Get returns a new data iterator for the current position, or nil if + // done. + Get() Iterator +} + +type indexedIterator struct { + util.BasicReleaser + index IteratorIndexer + strict bool + + data Iterator + err error + errf func(err error) + closed bool +} + +func (i *indexedIterator) setData() { + if i.data != nil { + i.data.Release() + } + i.data = i.index.Get() +} + +func (i *indexedIterator) clearData() { + if i.data != nil { + i.data.Release() + } + i.data = nil +} + +func (i *indexedIterator) indexErr() { + if err := i.index.Error(); err != nil { + if i.errf != nil { + i.errf(err) + } + i.err = err + } +} + +func (i *indexedIterator) dataErr() bool { + if err := i.data.Error(); err != nil { + if i.errf != nil { + i.errf(err) + } + if i.strict || !errors.IsCorrupted(err) { + i.err = err + return true + } + } + return false +} + +func (i *indexedIterator) Valid() bool { + return i.data != nil && i.data.Valid() +} + +func (i *indexedIterator) First() bool { + if i.err != nil { + return false + } else if i.Released() { + i.err = ErrIterReleased + return false + } + + if !i.index.First() { + i.indexErr() + i.clearData() + return false + } + i.setData() + return i.Next() +} + +func (i *indexedIterator) Last() bool { + if i.err != nil { + return false + } else if i.Released() { + i.err = ErrIterReleased + return false + } + + if !i.index.Last() { + i.indexErr() + i.clearData() + return false + } + i.setData() + if !i.data.Last() { + if i.dataErr() { + return false + } + i.clearData() + return i.Prev() + } + return true +} + +func (i *indexedIterator) Seek(key []byte) bool { + if i.err != nil { + return false + } else if i.Released() { + i.err = ErrIterReleased + return false + } + + if !i.index.Seek(key) { + i.indexErr() + i.clearData() + return false + } + i.setData() + if !i.data.Seek(key) { + if i.dataErr() { + return false + } + i.clearData() + return i.Next() + } + return true +} + +func (i *indexedIterator) Next() bool { + if i.err != nil { + return false + } else if i.Released() { + i.err = ErrIterReleased + return false + } + + switch { + case i.data != nil && !i.data.Next(): + if i.dataErr() { + return false + } + i.clearData() + fallthrough + case i.data == nil: + if !i.index.Next() { + i.indexErr() + return false + } + i.setData() + return i.Next() + } + return true +} + +func (i *indexedIterator) Prev() bool { + if i.err != nil { + return false + } else if i.Released() { + i.err = ErrIterReleased + return false + } + + switch { + case i.data != nil && !i.data.Prev(): + if i.dataErr() { + return false + } + i.clearData() + fallthrough + case i.data == nil: + if !i.index.Prev() { + i.indexErr() + return false + } + i.setData() + if !i.data.Last() { + if i.dataErr() { + return false + } + i.clearData() + return i.Prev() + } + } + return true +} + +func (i *indexedIterator) Key() []byte { + if i.data == nil { + return nil + } + return i.data.Key() +} + +func (i *indexedIterator) Value() []byte { + if i.data == nil { + return nil + } + return i.data.Value() +} + +func (i *indexedIterator) Release() { + i.clearData() + i.index.Release() + i.BasicReleaser.Release() +} + +func (i *indexedIterator) Error() error { + if i.err != nil { + return i.err + } + if err := i.index.Error(); err != nil { + return err + } + return nil +} + +func (i *indexedIterator) SetErrorCallback(f func(err error)) { + i.errf = f +} + +// NewIndexedIterator returns an 'indexed iterator'. An index is iterator +// that returns another iterator, a 'data iterator'. A 'data iterator' is the +// iterator that contains actual key/value pairs. +// +// If strict is true the any 'corruption errors' (i.e errors.IsCorrupted(err) == true) +// won't be ignored and will halt 'indexed iterator', otherwise the iterator will +// continue to the next 'data iterator'. Corruption on 'index iterator' will not be +// ignored and will halt the iterator. +func NewIndexedIterator(index IteratorIndexer, strict bool) Iterator { + return &indexedIterator{index: index, strict: strict} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/iter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/iter.go new file mode 100644 index 0000000..3b55532 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/iter.go @@ -0,0 +1,132 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package iterator provides interface and implementation to traverse over +// contents of a database. +package iterator + +import ( + "errors" + + "github.com/syndtr/goleveldb/leveldb/util" +) + +var ( + ErrIterReleased = errors.New("leveldb/iterator: iterator released") +) + +// IteratorSeeker is the interface that wraps the 'seeks method'. +type IteratorSeeker interface { + // First moves the iterator to the first key/value pair. If the iterator + // only contains one key/value pair then First and Last would moves + // to the same key/value pair. + // It returns whether such pair exist. + First() bool + + // Last moves the iterator to the last key/value pair. If the iterator + // only contains one key/value pair then First and Last would moves + // to the same key/value pair. + // It returns whether such pair exist. + Last() bool + + // Seek moves the iterator to the first key/value pair whose key is greater + // than or equal to the given key. + // It returns whether such pair exist. + // + // It is safe to modify the contents of the argument after Seek returns. + Seek(key []byte) bool + + // Next moves the iterator to the next key/value pair. + // It returns whether the iterator is exhausted. + Next() bool + + // Prev moves the iterator to the previous key/value pair. + // It returns whether the iterator is exhausted. + Prev() bool +} + +// CommonIterator is the interface that wraps common iterator methods. +type CommonIterator interface { + IteratorSeeker + + // util.Releaser is the interface that wraps basic Release method. + // When called Release will releases any resources associated with the + // iterator. + util.Releaser + + // util.ReleaseSetter is the interface that wraps the basic SetReleaser + // method. + util.ReleaseSetter + + // TODO: Remove this when ready. + Valid() bool + + // Error returns any accumulated error. Exhausting all the key/value pairs + // is not considered to be an error. + Error() error +} + +// Iterator iterates over a DB's key/value pairs in key order. +// +// When encounter an error any 'seeks method' will return false and will +// yield no key/value pairs. The error can be queried by calling the Error +// method. Calling Release is still necessary. +// +// An iterator must be released after use, but it is not necessary to read +// an iterator until exhaustion. +// Also, an iterator is not necessarily safe for concurrent use, but it is +// safe to use multiple iterators concurrently, with each in a dedicated +// goroutine. +type Iterator interface { + CommonIterator + + // Key returns the key of the current key/value pair, or nil if done. + // The caller should not modify the contents of the returned slice, and + // its contents may change on the next call to any 'seeks method'. + Key() []byte + + // Value returns the key of the current key/value pair, or nil if done. + // The caller should not modify the contents of the returned slice, and + // its contents may change on the next call to any 'seeks method'. + Value() []byte +} + +// ErrorCallbackSetter is the interface that wraps basic SetErrorCallback +// method. +// +// ErrorCallbackSetter implemented by indexed and merged iterator. +type ErrorCallbackSetter interface { + // SetErrorCallback allows set an error callback of the corresponding + // iterator. Use nil to clear the callback. + SetErrorCallback(f func(err error)) +} + +type emptyIterator struct { + util.BasicReleaser + err error +} + +func (i *emptyIterator) rErr() { + if i.err == nil && i.Released() { + i.err = ErrIterReleased + } +} + +func (*emptyIterator) Valid() bool { return false } +func (i *emptyIterator) First() bool { i.rErr(); return false } +func (i *emptyIterator) Last() bool { i.rErr(); return false } +func (i *emptyIterator) Seek(key []byte) bool { i.rErr(); return false } +func (i *emptyIterator) Next() bool { i.rErr(); return false } +func (i *emptyIterator) Prev() bool { i.rErr(); return false } +func (*emptyIterator) Key() []byte { return nil } +func (*emptyIterator) Value() []byte { return nil } +func (i *emptyIterator) Error() error { return i.err } + +// NewEmptyIterator creates an empty iterator. The err parameter can be +// nil, but if not nil the given err will be returned by Error method. +func NewEmptyIterator(err error) Iterator { + return &emptyIterator{err: err} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/merged_iter.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/merged_iter.go new file mode 100644 index 0000000..1a7e29d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/iterator/merged_iter.go @@ -0,0 +1,304 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package iterator + +import ( + "github.com/syndtr/goleveldb/leveldb/comparer" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/util" +) + +type dir int + +const ( + dirReleased dir = iota - 1 + dirSOI + dirEOI + dirBackward + dirForward +) + +type mergedIterator struct { + cmp comparer.Comparer + iters []Iterator + strict bool + + keys [][]byte + index int + dir dir + err error + errf func(err error) + releaser util.Releaser +} + +func assertKey(key []byte) []byte { + if key == nil { + panic("leveldb/iterator: nil key") + } + return key +} + +func (i *mergedIterator) iterErr(iter Iterator) bool { + if err := iter.Error(); err != nil { + if i.errf != nil { + i.errf(err) + } + if i.strict || !errors.IsCorrupted(err) { + i.err = err + return true + } + } + return false +} + +func (i *mergedIterator) Valid() bool { + return i.err == nil && i.dir > dirEOI +} + +func (i *mergedIterator) First() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.First(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirSOI + return i.next() +} + +func (i *mergedIterator) Last() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.Last(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirEOI + return i.prev() +} + +func (i *mergedIterator) Seek(key []byte) bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.Seek(key): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirSOI + return i.next() +} + +func (i *mergedIterator) next() bool { + var key []byte + if i.dir == dirForward { + key = i.keys[i.index] + } + for x, tkey := range i.keys { + if tkey != nil && (key == nil || i.cmp.Compare(tkey, key) < 0) { + key = tkey + i.index = x + } + } + if key == nil { + i.dir = dirEOI + return false + } + i.dir = dirForward + return true +} + +func (i *mergedIterator) Next() bool { + if i.dir == dirEOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + switch i.dir { + case dirSOI: + return i.First() + case dirBackward: + key := append([]byte{}, i.keys[i.index]...) + if !i.Seek(key) { + return false + } + return i.Next() + } + + x := i.index + iter := i.iters[x] + switch { + case iter.Next(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + return i.next() +} + +func (i *mergedIterator) prev() bool { + var key []byte + if i.dir == dirBackward { + key = i.keys[i.index] + } + for x, tkey := range i.keys { + if tkey != nil && (key == nil || i.cmp.Compare(tkey, key) > 0) { + key = tkey + i.index = x + } + } + if key == nil { + i.dir = dirSOI + return false + } + i.dir = dirBackward + return true +} + +func (i *mergedIterator) Prev() bool { + if i.dir == dirSOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + switch i.dir { + case dirEOI: + return i.Last() + case dirForward: + key := append([]byte{}, i.keys[i.index]...) + for x, iter := range i.iters { + if x == i.index { + continue + } + seek := iter.Seek(key) + switch { + case seek && iter.Prev(), !seek && iter.Last(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + } + + x := i.index + iter := i.iters[x] + switch { + case iter.Prev(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + return i.prev() +} + +func (i *mergedIterator) Key() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.keys[i.index] +} + +func (i *mergedIterator) Value() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.iters[i.index].Value() +} + +func (i *mergedIterator) Release() { + if i.dir != dirReleased { + i.dir = dirReleased + for _, iter := range i.iters { + iter.Release() + } + i.iters = nil + i.keys = nil + if i.releaser != nil { + i.releaser.Release() + i.releaser = nil + } + } +} + +func (i *mergedIterator) SetReleaser(releaser util.Releaser) { + if i.dir == dirReleased { + panic(util.ErrReleased) + } + if i.releaser != nil && releaser != nil { + panic(util.ErrHasReleaser) + } + i.releaser = releaser +} + +func (i *mergedIterator) Error() error { + return i.err +} + +func (i *mergedIterator) SetErrorCallback(f func(err error)) { + i.errf = f +} + +// NewMergedIterator returns an iterator that merges its input. Walking the +// resultant iterator will return all key/value pairs of all input iterators +// in strictly increasing key order, as defined by cmp. +// The input's key ranges may overlap, but there are assumed to be no duplicate +// keys: if iters[i] contains a key k then iters[j] will not contain that key k. +// None of the iters may be nil. +// +// If strict is true the any 'corruption errors' (i.e errors.IsCorrupted(err) == true) +// won't be ignored and will halt 'merged iterator', otherwise the iterator will +// continue to the next 'input iterator'. +func NewMergedIterator(iters []Iterator, cmp comparer.Comparer, strict bool) Iterator { + return &mergedIterator{ + iters: iters, + cmp: cmp, + strict: strict, + keys: make([][]byte, len(iters)), + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/journal/journal.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/journal/journal.go new file mode 100644 index 0000000..d094c3d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/journal/journal.go @@ -0,0 +1,524 @@ +// Copyright 2011 The LevelDB-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Taken from: https://code.google.com/p/leveldb-go/source/browse/leveldb/record/record.go?r=1d5ccbe03246da926391ee12d1c6caae054ff4b0 +// License, authors and contributors informations can be found at bellow URLs respectively: +// https://code.google.com/p/leveldb-go/source/browse/LICENSE +// https://code.google.com/p/leveldb-go/source/browse/AUTHORS +// https://code.google.com/p/leveldb-go/source/browse/CONTRIBUTORS + +// Package journal reads and writes sequences of journals. Each journal is a stream +// of bytes that completes before the next journal starts. +// +// When reading, call Next to obtain an io.Reader for the next journal. Next will +// return io.EOF when there are no more journals. It is valid to call Next +// without reading the current journal to exhaustion. +// +// When writing, call Next to obtain an io.Writer for the next journal. Calling +// Next finishes the current journal. Call Close to finish the final journal. +// +// Optionally, call Flush to finish the current journal and flush the underlying +// writer without starting a new journal. To start a new journal after flushing, +// call Next. +// +// Neither Readers or Writers are safe to use concurrently. +// +// Example code: +// func read(r io.Reader) ([]string, error) { +// var ss []string +// journals := journal.NewReader(r, nil, true, true) +// for { +// j, err := journals.Next() +// if err == io.EOF { +// break +// } +// if err != nil { +// return nil, err +// } +// s, err := ioutil.ReadAll(j) +// if err != nil { +// return nil, err +// } +// ss = append(ss, string(s)) +// } +// return ss, nil +// } +// +// func write(w io.Writer, ss []string) error { +// journals := journal.NewWriter(w) +// for _, s := range ss { +// j, err := journals.Next() +// if err != nil { +// return err +// } +// if _, err := j.Write([]byte(s)), err != nil { +// return err +// } +// } +// return journals.Close() +// } +// +// The wire format is that the stream is divided into 32KiB blocks, and each +// block contains a number of tightly packed chunks. Chunks cannot cross block +// boundaries. The last block may be shorter than 32 KiB. Any unused bytes in a +// block must be zero. +// +// A journal maps to one or more chunks. Each chunk has a 7 byte header (a 4 +// byte checksum, a 2 byte little-endian uint16 length, and a 1 byte chunk type) +// followed by a payload. The checksum is over the chunk type and the payload. +// +// There are four chunk types: whether the chunk is the full journal, or the +// first, middle or last chunk of a multi-chunk journal. A multi-chunk journal +// has one first chunk, zero or more middle chunks, and one last chunk. +// +// The wire format allows for limited recovery in the face of data corruption: +// on a format error (such as a checksum mismatch), the reader moves to the +// next block and looks for the next full or first chunk. +package journal + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// These constants are part of the wire format and should not be changed. +const ( + fullChunkType = 1 + firstChunkType = 2 + middleChunkType = 3 + lastChunkType = 4 +) + +const ( + blockSize = 32 * 1024 + headerSize = 7 +) + +type flusher interface { + Flush() error +} + +// ErrCorrupted is the error type that generated by corrupted block or chunk. +type ErrCorrupted struct { + Size int + Reason string +} + +func (e *ErrCorrupted) Error() string { + return fmt.Sprintf("leveldb/journal: block/chunk corrupted: %s (%d bytes)", e.Reason, e.Size) +} + +// Dropper is the interface that wrap simple Drop method. The Drop +// method will be called when the journal reader dropping a block or chunk. +type Dropper interface { + Drop(err error) +} + +// Reader reads journals from an underlying io.Reader. +type Reader struct { + // r is the underlying reader. + r io.Reader + // the dropper. + dropper Dropper + // strict flag. + strict bool + // checksum flag. + checksum bool + // seq is the sequence number of the current journal. + seq int + // buf[i:j] is the unread portion of the current chunk's payload. + // The low bound, i, excludes the chunk header. + i, j int + // n is the number of bytes of buf that are valid. Once reading has started, + // only the final block can have n < blockSize. + n int + // last is whether the current chunk is the last chunk of the journal. + last bool + // err is any accumulated error. + err error + // buf is the buffer. + buf [blockSize]byte +} + +// NewReader returns a new reader. The dropper may be nil, and if +// strict is true then corrupted or invalid chunk will halt the journal +// reader entirely. +func NewReader(r io.Reader, dropper Dropper, strict, checksum bool) *Reader { + return &Reader{ + r: r, + dropper: dropper, + strict: strict, + checksum: checksum, + last: true, + } +} + +var errSkip = errors.New("leveldb/journal: skipped") + +func (r *Reader) corrupt(n int, reason string, skip bool) error { + if r.dropper != nil { + r.dropper.Drop(&ErrCorrupted{n, reason}) + } + if r.strict && !skip { + r.err = errors.NewErrCorrupted(storage.FileDesc{}, &ErrCorrupted{n, reason}) + return r.err + } + return errSkip +} + +// nextChunk sets r.buf[r.i:r.j] to hold the next chunk's payload, reading the +// next block into the buffer if necessary. +func (r *Reader) nextChunk(first bool) error { + for { + if r.j+headerSize <= r.n { + checksum := binary.LittleEndian.Uint32(r.buf[r.j+0 : r.j+4]) + length := binary.LittleEndian.Uint16(r.buf[r.j+4 : r.j+6]) + chunkType := r.buf[r.j+6] + unprocBlock := r.n - r.j + if checksum == 0 && length == 0 && chunkType == 0 { + // Drop entire block. + r.i = r.n + r.j = r.n + return r.corrupt(unprocBlock, "zero header", false) + } + if chunkType < fullChunkType || chunkType > lastChunkType { + // Drop entire block. + r.i = r.n + r.j = r.n + return r.corrupt(unprocBlock, fmt.Sprintf("invalid chunk type %#x", chunkType), false) + } + r.i = r.j + headerSize + r.j = r.j + headerSize + int(length) + if r.j > r.n { + // Drop entire block. + r.i = r.n + r.j = r.n + return r.corrupt(unprocBlock, "chunk length overflows block", false) + } else if r.checksum && checksum != util.NewCRC(r.buf[r.i-1:r.j]).Value() { + // Drop entire block. + r.i = r.n + r.j = r.n + return r.corrupt(unprocBlock, "checksum mismatch", false) + } + if first && chunkType != fullChunkType && chunkType != firstChunkType { + chunkLength := (r.j - r.i) + headerSize + r.i = r.j + // Report the error, but skip it. + return r.corrupt(chunkLength, "orphan chunk", true) + } + r.last = chunkType == fullChunkType || chunkType == lastChunkType + return nil + } + + // The last block. + if r.n < blockSize && r.n > 0 { + if !first { + return r.corrupt(0, "missing chunk part", false) + } + r.err = io.EOF + return r.err + } + + // Read block. + n, err := io.ReadFull(r.r, r.buf[:]) + if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { + return err + } + if n == 0 { + if !first { + return r.corrupt(0, "missing chunk part", false) + } + r.err = io.EOF + return r.err + } + r.i, r.j, r.n = 0, 0, n + } +} + +// Next returns a reader for the next journal. It returns io.EOF if there are no +// more journals. The reader returned becomes stale after the next Next call, +// and should no longer be used. If strict is false, the reader will returns +// io.ErrUnexpectedEOF error when found corrupted journal. +func (r *Reader) Next() (io.Reader, error) { + r.seq++ + if r.err != nil { + return nil, r.err + } + r.i = r.j + for { + if err := r.nextChunk(true); err == nil { + break + } else if err != errSkip { + return nil, err + } + } + return &singleReader{r, r.seq, nil}, nil +} + +// Reset resets the journal reader, allows reuse of the journal reader. Reset returns +// last accumulated error. +func (r *Reader) Reset(reader io.Reader, dropper Dropper, strict, checksum bool) error { + r.seq++ + err := r.err + r.r = reader + r.dropper = dropper + r.strict = strict + r.checksum = checksum + r.i = 0 + r.j = 0 + r.n = 0 + r.last = true + r.err = nil + return err +} + +type singleReader struct { + r *Reader + seq int + err error +} + +func (x *singleReader) Read(p []byte) (int, error) { + r := x.r + if r.seq != x.seq { + return 0, errors.New("leveldb/journal: stale reader") + } + if x.err != nil { + return 0, x.err + } + if r.err != nil { + return 0, r.err + } + for r.i == r.j { + if r.last { + return 0, io.EOF + } + x.err = r.nextChunk(false) + if x.err != nil { + if x.err == errSkip { + x.err = io.ErrUnexpectedEOF + } + return 0, x.err + } + } + n := copy(p, r.buf[r.i:r.j]) + r.i += n + return n, nil +} + +func (x *singleReader) ReadByte() (byte, error) { + r := x.r + if r.seq != x.seq { + return 0, errors.New("leveldb/journal: stale reader") + } + if x.err != nil { + return 0, x.err + } + if r.err != nil { + return 0, r.err + } + for r.i == r.j { + if r.last { + return 0, io.EOF + } + x.err = r.nextChunk(false) + if x.err != nil { + if x.err == errSkip { + x.err = io.ErrUnexpectedEOF + } + return 0, x.err + } + } + c := r.buf[r.i] + r.i++ + return c, nil +} + +// Writer writes journals to an underlying io.Writer. +type Writer struct { + // w is the underlying writer. + w io.Writer + // seq is the sequence number of the current journal. + seq int + // f is w as a flusher. + f flusher + // buf[i:j] is the bytes that will become the current chunk. + // The low bound, i, includes the chunk header. + i, j int + // buf[:written] has already been written to w. + // written is zero unless Flush has been called. + written int + // first is whether the current chunk is the first chunk of the journal. + first bool + // pending is whether a chunk is buffered but not yet written. + pending bool + // err is any accumulated error. + err error + // buf is the buffer. + buf [blockSize]byte +} + +// NewWriter returns a new Writer. +func NewWriter(w io.Writer) *Writer { + f, _ := w.(flusher) + return &Writer{ + w: w, + f: f, + } +} + +// fillHeader fills in the header for the pending chunk. +func (w *Writer) fillHeader(last bool) { + if w.i+headerSize > w.j || w.j > blockSize { + panic("leveldb/journal: bad writer state") + } + if last { + if w.first { + w.buf[w.i+6] = fullChunkType + } else { + w.buf[w.i+6] = lastChunkType + } + } else { + if w.first { + w.buf[w.i+6] = firstChunkType + } else { + w.buf[w.i+6] = middleChunkType + } + } + binary.LittleEndian.PutUint32(w.buf[w.i+0:w.i+4], util.NewCRC(w.buf[w.i+6:w.j]).Value()) + binary.LittleEndian.PutUint16(w.buf[w.i+4:w.i+6], uint16(w.j-w.i-headerSize)) +} + +// writeBlock writes the buffered block to the underlying writer, and reserves +// space for the next chunk's header. +func (w *Writer) writeBlock() { + _, w.err = w.w.Write(w.buf[w.written:]) + w.i = 0 + w.j = headerSize + w.written = 0 +} + +// writePending finishes the current journal and writes the buffer to the +// underlying writer. +func (w *Writer) writePending() { + if w.err != nil { + return + } + if w.pending { + w.fillHeader(true) + w.pending = false + } + _, w.err = w.w.Write(w.buf[w.written:w.j]) + w.written = w.j +} + +// Close finishes the current journal and closes the writer. +func (w *Writer) Close() error { + w.seq++ + w.writePending() + if w.err != nil { + return w.err + } + w.err = errors.New("leveldb/journal: closed Writer") + return nil +} + +// Flush finishes the current journal, writes to the underlying writer, and +// flushes it if that writer implements interface{ Flush() error }. +func (w *Writer) Flush() error { + w.seq++ + w.writePending() + if w.err != nil { + return w.err + } + if w.f != nil { + w.err = w.f.Flush() + return w.err + } + return nil +} + +// Reset resets the journal writer, allows reuse of the journal writer. Reset +// will also closes the journal writer if not already. +func (w *Writer) Reset(writer io.Writer) (err error) { + w.seq++ + if w.err == nil { + w.writePending() + err = w.err + } + w.w = writer + w.f, _ = writer.(flusher) + w.i = 0 + w.j = 0 + w.written = 0 + w.first = false + w.pending = false + w.err = nil + return +} + +// Next returns a writer for the next journal. The writer returned becomes stale +// after the next Close, Flush or Next call, and should no longer be used. +func (w *Writer) Next() (io.Writer, error) { + w.seq++ + if w.err != nil { + return nil, w.err + } + if w.pending { + w.fillHeader(true) + } + w.i = w.j + w.j = w.j + headerSize + // Check if there is room in the block for the header. + if w.j > blockSize { + // Fill in the rest of the block with zeroes. + for k := w.i; k < blockSize; k++ { + w.buf[k] = 0 + } + w.writeBlock() + if w.err != nil { + return nil, w.err + } + } + w.first = true + w.pending = true + return singleWriter{w, w.seq}, nil +} + +type singleWriter struct { + w *Writer + seq int +} + +func (x singleWriter) Write(p []byte) (int, error) { + w := x.w + if w.seq != x.seq { + return 0, errors.New("leveldb/journal: stale writer") + } + if w.err != nil { + return 0, w.err + } + n0 := len(p) + for len(p) > 0 { + // Write a block, if it is full. + if w.j == blockSize { + w.fillHeader(false) + w.writeBlock() + if w.err != nil { + return 0, w.err + } + w.first = false + } + // Copy bytes into the buffer. + n := copy(w.buf[w.j:], p) + w.j += n + p = p[n:] + } + return n0, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/key.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/key.go new file mode 100644 index 0000000..ad8f51e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/key.go @@ -0,0 +1,143 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "encoding/binary" + "fmt" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +// ErrInternalKeyCorrupted records internal key corruption. +type ErrInternalKeyCorrupted struct { + Ikey []byte + Reason string +} + +func (e *ErrInternalKeyCorrupted) Error() string { + return fmt.Sprintf("leveldb: internal key %q corrupted: %s", e.Ikey, e.Reason) +} + +func newErrInternalKeyCorrupted(ikey []byte, reason string) error { + return errors.NewErrCorrupted(storage.FileDesc{}, &ErrInternalKeyCorrupted{append([]byte{}, ikey...), reason}) +} + +type keyType uint + +func (kt keyType) String() string { + switch kt { + case keyTypeDel: + return "d" + case keyTypeVal: + return "v" + } + return fmt.Sprintf("", uint(kt)) +} + +// Value types encoded as the last component of internal keys. +// Don't modify; this value are saved to disk. +const ( + keyTypeDel = keyType(0) + keyTypeVal = keyType(1) +) + +// keyTypeSeek defines the keyType that should be passed when constructing an +// internal key for seeking to a particular sequence number (since we +// sort sequence numbers in decreasing order and the value type is +// embedded as the low 8 bits in the sequence number in internal keys, +// we need to use the highest-numbered ValueType, not the lowest). +const keyTypeSeek = keyTypeVal + +const ( + // Maximum value possible for sequence number; the 8-bits are + // used by value type, so its can packed together in single + // 64-bit integer. + keyMaxSeq = (uint64(1) << 56) - 1 + // Maximum value possible for packed sequence number and type. + keyMaxNum = (keyMaxSeq << 8) | uint64(keyTypeSeek) +) + +// Maximum number encoded in bytes. +var keyMaxNumBytes = make([]byte, 8) + +func init() { + binary.LittleEndian.PutUint64(keyMaxNumBytes, keyMaxNum) +} + +type internalKey []byte + +func makeInternalKey(dst, ukey []byte, seq uint64, kt keyType) internalKey { + if seq > keyMaxSeq { + panic("leveldb: invalid sequence number") + } else if kt > keyTypeVal { + panic("leveldb: invalid type") + } + + dst = ensureBuffer(dst, len(ukey)+8) + copy(dst, ukey) + binary.LittleEndian.PutUint64(dst[len(ukey):], (seq<<8)|uint64(kt)) + return internalKey(dst) +} + +func parseInternalKey(ik []byte) (ukey []byte, seq uint64, kt keyType, err error) { + if len(ik) < 8 { + return nil, 0, 0, newErrInternalKeyCorrupted(ik, "invalid length") + } + num := binary.LittleEndian.Uint64(ik[len(ik)-8:]) + seq, kt = uint64(num>>8), keyType(num&0xff) + if kt > keyTypeVal { + return nil, 0, 0, newErrInternalKeyCorrupted(ik, "invalid type") + } + ukey = ik[:len(ik)-8] + return +} + +func validInternalKey(ik []byte) bool { + _, _, _, err := parseInternalKey(ik) + return err == nil +} + +func (ik internalKey) assert() { + if ik == nil { + panic("leveldb: nil internalKey") + } + if len(ik) < 8 { + panic(fmt.Sprintf("leveldb: internal key %q, len=%d: invalid length", []byte(ik), len(ik))) + } +} + +func (ik internalKey) ukey() []byte { + ik.assert() + return ik[:len(ik)-8] +} + +func (ik internalKey) num() uint64 { + ik.assert() + return binary.LittleEndian.Uint64(ik[len(ik)-8:]) +} + +func (ik internalKey) parseNum() (seq uint64, kt keyType) { + num := ik.num() + seq, kt = uint64(num>>8), keyType(num&0xff) + if kt > keyTypeVal { + panic(fmt.Sprintf("leveldb: internal key %q, len=%d: invalid type %#x", []byte(ik), len(ik), kt)) + } + return +} + +func (ik internalKey) String() string { + if ik == nil { + return "" + } + + if ukey, seq, kt, err := parseInternalKey(ik); err == nil { + return fmt.Sprintf("%s,%s%d", shorten(string(ukey)), kt, seq) + } + return fmt.Sprintf("", []byte(ik)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/memdb/memdb.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/memdb/memdb.go new file mode 100644 index 0000000..18a19ed --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/memdb/memdb.go @@ -0,0 +1,475 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package memdb provides in-memory key/value database implementation. +package memdb + +import ( + "math/rand" + "sync" + + "github.com/syndtr/goleveldb/leveldb/comparer" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// Common errors. +var ( + ErrNotFound = errors.ErrNotFound + ErrIterReleased = errors.New("leveldb/memdb: iterator released") +) + +const tMaxHeight = 12 + +type dbIter struct { + util.BasicReleaser + p *DB + slice *util.Range + node int + forward bool + key, value []byte + err error +} + +func (i *dbIter) fill(checkStart, checkLimit bool) bool { + if i.node != 0 { + n := i.p.nodeData[i.node] + m := n + i.p.nodeData[i.node+nKey] + i.key = i.p.kvData[n:m] + if i.slice != nil { + switch { + case checkLimit && i.slice.Limit != nil && i.p.cmp.Compare(i.key, i.slice.Limit) >= 0: + fallthrough + case checkStart && i.slice.Start != nil && i.p.cmp.Compare(i.key, i.slice.Start) < 0: + i.node = 0 + goto bail + } + } + i.value = i.p.kvData[m : m+i.p.nodeData[i.node+nVal]] + return true + } +bail: + i.key = nil + i.value = nil + return false +} + +func (i *dbIter) Valid() bool { + return i.node != 0 +} + +func (i *dbIter) First() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + i.forward = true + i.p.mu.RLock() + defer i.p.mu.RUnlock() + if i.slice != nil && i.slice.Start != nil { + i.node, _ = i.p.findGE(i.slice.Start, false) + } else { + i.node = i.p.nodeData[nNext] + } + return i.fill(false, true) +} + +func (i *dbIter) Last() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + i.forward = false + i.p.mu.RLock() + defer i.p.mu.RUnlock() + if i.slice != nil && i.slice.Limit != nil { + i.node = i.p.findLT(i.slice.Limit) + } else { + i.node = i.p.findLast() + } + return i.fill(true, false) +} + +func (i *dbIter) Seek(key []byte) bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + i.forward = true + i.p.mu.RLock() + defer i.p.mu.RUnlock() + if i.slice != nil && i.slice.Start != nil && i.p.cmp.Compare(key, i.slice.Start) < 0 { + key = i.slice.Start + } + i.node, _ = i.p.findGE(key, false) + return i.fill(false, true) +} + +func (i *dbIter) Next() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + if i.node == 0 { + if !i.forward { + return i.First() + } + return false + } + i.forward = true + i.p.mu.RLock() + defer i.p.mu.RUnlock() + i.node = i.p.nodeData[i.node+nNext] + return i.fill(false, true) +} + +func (i *dbIter) Prev() bool { + if i.Released() { + i.err = ErrIterReleased + return false + } + + if i.node == 0 { + if i.forward { + return i.Last() + } + return false + } + i.forward = false + i.p.mu.RLock() + defer i.p.mu.RUnlock() + i.node = i.p.findLT(i.key) + return i.fill(true, false) +} + +func (i *dbIter) Key() []byte { + return i.key +} + +func (i *dbIter) Value() []byte { + return i.value +} + +func (i *dbIter) Error() error { return i.err } + +func (i *dbIter) Release() { + if !i.Released() { + i.p = nil + i.node = 0 + i.key = nil + i.value = nil + i.BasicReleaser.Release() + } +} + +const ( + nKV = iota + nKey + nVal + nHeight + nNext +) + +// DB is an in-memory key/value database. +type DB struct { + cmp comparer.BasicComparer + rnd *rand.Rand + + mu sync.RWMutex + kvData []byte + // Node data: + // [0] : KV offset + // [1] : Key length + // [2] : Value length + // [3] : Height + // [3..height] : Next nodes + nodeData []int + prevNode [tMaxHeight]int + maxHeight int + n int + kvSize int +} + +func (p *DB) randHeight() (h int) { + const branching = 4 + h = 1 + for h < tMaxHeight && p.rnd.Int()%branching == 0 { + h++ + } + return +} + +// Must hold RW-lock if prev == true, as it use shared prevNode slice. +func (p *DB) findGE(key []byte, prev bool) (int, bool) { + node := 0 + h := p.maxHeight - 1 + for { + next := p.nodeData[node+nNext+h] + cmp := 1 + if next != 0 { + o := p.nodeData[next] + cmp = p.cmp.Compare(p.kvData[o:o+p.nodeData[next+nKey]], key) + } + if cmp < 0 { + // Keep searching in this list + node = next + } else { + if prev { + p.prevNode[h] = node + } else if cmp == 0 { + return next, true + } + if h == 0 { + return next, cmp == 0 + } + h-- + } + } +} + +func (p *DB) findLT(key []byte) int { + node := 0 + h := p.maxHeight - 1 + for { + next := p.nodeData[node+nNext+h] + o := p.nodeData[next] + if next == 0 || p.cmp.Compare(p.kvData[o:o+p.nodeData[next+nKey]], key) >= 0 { + if h == 0 { + break + } + h-- + } else { + node = next + } + } + return node +} + +func (p *DB) findLast() int { + node := 0 + h := p.maxHeight - 1 + for { + next := p.nodeData[node+nNext+h] + if next == 0 { + if h == 0 { + break + } + h-- + } else { + node = next + } + } + return node +} + +// Put sets the value for the given key. It overwrites any previous value +// for that key; a DB is not a multi-map. +// +// It is safe to modify the contents of the arguments after Put returns. +func (p *DB) Put(key []byte, value []byte) error { + p.mu.Lock() + defer p.mu.Unlock() + + if node, exact := p.findGE(key, true); exact { + kvOffset := len(p.kvData) + p.kvData = append(p.kvData, key...) + p.kvData = append(p.kvData, value...) + p.nodeData[node] = kvOffset + m := p.nodeData[node+nVal] + p.nodeData[node+nVal] = len(value) + p.kvSize += len(value) - m + return nil + } + + h := p.randHeight() + if h > p.maxHeight { + for i := p.maxHeight; i < h; i++ { + p.prevNode[i] = 0 + } + p.maxHeight = h + } + + kvOffset := len(p.kvData) + p.kvData = append(p.kvData, key...) + p.kvData = append(p.kvData, value...) + // Node + node := len(p.nodeData) + p.nodeData = append(p.nodeData, kvOffset, len(key), len(value), h) + for i, n := range p.prevNode[:h] { + m := n + nNext + i + p.nodeData = append(p.nodeData, p.nodeData[m]) + p.nodeData[m] = node + } + + p.kvSize += len(key) + len(value) + p.n++ + return nil +} + +// Delete deletes the value for the given key. It returns ErrNotFound if +// the DB does not contain the key. +// +// It is safe to modify the contents of the arguments after Delete returns. +func (p *DB) Delete(key []byte) error { + p.mu.Lock() + defer p.mu.Unlock() + + node, exact := p.findGE(key, true) + if !exact { + return ErrNotFound + } + + h := p.nodeData[node+nHeight] + for i, n := range p.prevNode[:h] { + m := n + 4 + i + p.nodeData[m] = p.nodeData[p.nodeData[m]+nNext+i] + } + + p.kvSize -= p.nodeData[node+nKey] + p.nodeData[node+nVal] + p.n-- + return nil +} + +// Contains returns true if the given key are in the DB. +// +// It is safe to modify the contents of the arguments after Contains returns. +func (p *DB) Contains(key []byte) bool { + p.mu.RLock() + _, exact := p.findGE(key, false) + p.mu.RUnlock() + return exact +} + +// Get gets the value for the given key. It returns error.ErrNotFound if the +// DB does not contain the key. +// +// The caller should not modify the contents of the returned slice, but +// it is safe to modify the contents of the argument after Get returns. +func (p *DB) Get(key []byte) (value []byte, err error) { + p.mu.RLock() + if node, exact := p.findGE(key, false); exact { + o := p.nodeData[node] + p.nodeData[node+nKey] + value = p.kvData[o : o+p.nodeData[node+nVal]] + } else { + err = ErrNotFound + } + p.mu.RUnlock() + return +} + +// Find finds key/value pair whose key is greater than or equal to the +// given key. It returns ErrNotFound if the table doesn't contain +// such pair. +// +// The caller should not modify the contents of the returned slice, but +// it is safe to modify the contents of the argument after Find returns. +func (p *DB) Find(key []byte) (rkey, value []byte, err error) { + p.mu.RLock() + if node, _ := p.findGE(key, false); node != 0 { + n := p.nodeData[node] + m := n + p.nodeData[node+nKey] + rkey = p.kvData[n:m] + value = p.kvData[m : m+p.nodeData[node+nVal]] + } else { + err = ErrNotFound + } + p.mu.RUnlock() + return +} + +// NewIterator returns an iterator of the DB. +// The returned iterator is not safe for concurrent use, but it is safe to use +// multiple iterators concurrently, with each in a dedicated goroutine. +// It is also safe to use an iterator concurrently with modifying its +// underlying DB. However, the resultant key/value pairs are not guaranteed +// to be a consistent snapshot of the DB at a particular point in time. +// +// Slice allows slicing the iterator to only contains keys in the given +// range. A nil Range.Start is treated as a key before all keys in the +// DB. And a nil Range.Limit is treated as a key after all keys in +// the DB. +// +// The iterator must be released after use, by calling Release method. +// +// Also read Iterator documentation of the leveldb/iterator package. +func (p *DB) NewIterator(slice *util.Range) iterator.Iterator { + return &dbIter{p: p, slice: slice} +} + +// Capacity returns keys/values buffer capacity. +func (p *DB) Capacity() int { + p.mu.RLock() + defer p.mu.RUnlock() + return cap(p.kvData) +} + +// Size returns sum of keys and values length. Note that deleted +// key/value will not be accounted for, but it will still consume +// the buffer, since the buffer is append only. +func (p *DB) Size() int { + p.mu.RLock() + defer p.mu.RUnlock() + return p.kvSize +} + +// Free returns keys/values free buffer before need to grow. +func (p *DB) Free() int { + p.mu.RLock() + defer p.mu.RUnlock() + return cap(p.kvData) - len(p.kvData) +} + +// Len returns the number of entries in the DB. +func (p *DB) Len() int { + p.mu.RLock() + defer p.mu.RUnlock() + return p.n +} + +// Reset resets the DB to initial empty state. Allows reuse the buffer. +func (p *DB) Reset() { + p.mu.Lock() + p.rnd = rand.New(rand.NewSource(0xdeadbeef)) + p.maxHeight = 1 + p.n = 0 + p.kvSize = 0 + p.kvData = p.kvData[:0] + p.nodeData = p.nodeData[:nNext+tMaxHeight] + p.nodeData[nKV] = 0 + p.nodeData[nKey] = 0 + p.nodeData[nVal] = 0 + p.nodeData[nHeight] = tMaxHeight + for n := 0; n < tMaxHeight; n++ { + p.nodeData[nNext+n] = 0 + p.prevNode[n] = 0 + } + p.mu.Unlock() +} + +// New creates a new initialized in-memory key/value DB. The capacity +// is the initial key/value buffer capacity. The capacity is advisory, +// not enforced. +// +// This DB is append-only, deleting an entry would remove entry node but not +// reclaim KV buffer. +// +// The returned DB instance is safe for concurrent use. +func New(cmp comparer.BasicComparer, capacity int) *DB { + p := &DB{ + cmp: cmp, + rnd: rand.New(rand.NewSource(0xdeadbeef)), + maxHeight: 1, + kvData: make([]byte, 0, capacity), + nodeData: make([]int, 4+tMaxHeight), + } + p.nodeData[nHeight] = tMaxHeight + return p +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go new file mode 100644 index 0000000..44e7d9a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go @@ -0,0 +1,684 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package opt provides sets of options used by LevelDB. +package opt + +import ( + "math" + + "github.com/syndtr/goleveldb/leveldb/cache" + "github.com/syndtr/goleveldb/leveldb/comparer" + "github.com/syndtr/goleveldb/leveldb/filter" +) + +const ( + KiB = 1024 + MiB = KiB * 1024 + GiB = MiB * 1024 +) + +var ( + DefaultBlockCacher = LRUCacher + DefaultBlockCacheCapacity = 8 * MiB + DefaultBlockRestartInterval = 16 + DefaultBlockSize = 4 * KiB + DefaultCompactionExpandLimitFactor = 25 + DefaultCompactionGPOverlapsFactor = 10 + DefaultCompactionL0Trigger = 4 + DefaultCompactionSourceLimitFactor = 1 + DefaultCompactionTableSize = 2 * MiB + DefaultCompactionTableSizeMultiplier = 1.0 + DefaultCompactionTotalSize = 10 * MiB + DefaultCompactionTotalSizeMultiplier = 10.0 + DefaultCompressionType = SnappyCompression + DefaultIteratorSamplingRate = 1 * MiB + DefaultOpenFilesCacher = LRUCacher + DefaultOpenFilesCacheCapacity = 500 + DefaultWriteBuffer = 4 * MiB + DefaultWriteL0PauseTrigger = 12 + DefaultWriteL0SlowdownTrigger = 8 +) + +// Cacher is a caching algorithm. +type Cacher interface { + New(capacity int) cache.Cacher +} + +type CacherFunc struct { + NewFunc func(capacity int) cache.Cacher +} + +func (f *CacherFunc) New(capacity int) cache.Cacher { + if f.NewFunc != nil { + return f.NewFunc(capacity) + } + return nil +} + +func noCacher(int) cache.Cacher { return nil } + +var ( + // LRUCacher is the LRU-cache algorithm. + LRUCacher = &CacherFunc{cache.NewLRU} + + // NoCacher is the value to disable caching algorithm. + NoCacher = &CacherFunc{} +) + +// Compression is the 'sorted table' block compression algorithm to use. +type Compression uint + +func (c Compression) String() string { + switch c { + case DefaultCompression: + return "default" + case NoCompression: + return "none" + case SnappyCompression: + return "snappy" + } + return "invalid" +} + +const ( + DefaultCompression Compression = iota + NoCompression + SnappyCompression + nCompression +) + +// Strict is the DB 'strict level'. +type Strict uint + +const ( + // If present then a corrupted or invalid chunk or block in manifest + // journal will cause an error instead of being dropped. + // This will prevent database with corrupted manifest to be opened. + StrictManifest Strict = 1 << iota + + // If present then journal chunk checksum will be verified. + StrictJournalChecksum + + // If present then a corrupted or invalid chunk or block in journal + // will cause an error instead of being dropped. + // This will prevent database with corrupted journal to be opened. + StrictJournal + + // If present then 'sorted table' block checksum will be verified. + // This has effect on both 'read operation' and compaction. + StrictBlockChecksum + + // If present then a corrupted 'sorted table' will fails compaction. + // The database will enter read-only mode. + StrictCompaction + + // If present then a corrupted 'sorted table' will halts 'read operation'. + StrictReader + + // If present then leveldb.Recover will drop corrupted 'sorted table'. + StrictRecovery + + // This only applicable for ReadOptions, if present then this ReadOptions + // 'strict level' will override global ones. + StrictOverride + + // StrictAll enables all strict flags. + StrictAll = StrictManifest | StrictJournalChecksum | StrictJournal | StrictBlockChecksum | StrictCompaction | StrictReader | StrictRecovery + + // DefaultStrict is the default strict flags. Specify any strict flags + // will override default strict flags as whole (i.e. not OR'ed). + DefaultStrict = StrictJournalChecksum | StrictBlockChecksum | StrictCompaction | StrictReader + + // NoStrict disables all strict flags. Override default strict flags. + NoStrict = ^StrictAll +) + +// Options holds the optional parameters for the DB at large. +type Options struct { + // AltFilters defines one or more 'alternative filters'. + // 'alternative filters' will be used during reads if a filter block + // does not match with the 'effective filter'. + // + // The default value is nil + AltFilters []filter.Filter + + // BlockCacher provides cache algorithm for LevelDB 'sorted table' block caching. + // Specify NoCacher to disable caching algorithm. + // + // The default value is LRUCacher. + BlockCacher Cacher + + // BlockCacheCapacity defines the capacity of the 'sorted table' block caching. + // Use -1 for zero, this has same effect as specifying NoCacher to BlockCacher. + // + // The default value is 8MiB. + BlockCacheCapacity int + + // BlockRestartInterval is the number of keys between restart points for + // delta encoding of keys. + // + // The default value is 16. + BlockRestartInterval int + + // BlockSize is the minimum uncompressed size in bytes of each 'sorted table' + // block. + // + // The default value is 4KiB. + BlockSize int + + // CompactionExpandLimitFactor limits compaction size after expanded. + // This will be multiplied by table size limit at compaction target level. + // + // The default value is 25. + CompactionExpandLimitFactor int + + // CompactionGPOverlapsFactor limits overlaps in grandparent (Level + 2) that a + // single 'sorted table' generates. + // This will be multiplied by table size limit at grandparent level. + // + // The default value is 10. + CompactionGPOverlapsFactor int + + // CompactionL0Trigger defines number of 'sorted table' at level-0 that will + // trigger compaction. + // + // The default value is 4. + CompactionL0Trigger int + + // CompactionSourceLimitFactor limits compaction source size. This doesn't apply to + // level-0. + // This will be multiplied by table size limit at compaction target level. + // + // The default value is 1. + CompactionSourceLimitFactor int + + // CompactionTableSize limits size of 'sorted table' that compaction generates. + // The limits for each level will be calculated as: + // CompactionTableSize * (CompactionTableSizeMultiplier ^ Level) + // The multiplier for each level can also fine-tuned using CompactionTableSizeMultiplierPerLevel. + // + // The default value is 2MiB. + CompactionTableSize int + + // CompactionTableSizeMultiplier defines multiplier for CompactionTableSize. + // + // The default value is 1. + CompactionTableSizeMultiplier float64 + + // CompactionTableSizeMultiplierPerLevel defines per-level multiplier for + // CompactionTableSize. + // Use zero to skip a level. + // + // The default value is nil. + CompactionTableSizeMultiplierPerLevel []float64 + + // CompactionTotalSize limits total size of 'sorted table' for each level. + // The limits for each level will be calculated as: + // CompactionTotalSize * (CompactionTotalSizeMultiplier ^ Level) + // The multiplier for each level can also fine-tuned using + // CompactionTotalSizeMultiplierPerLevel. + // + // The default value is 10MiB. + CompactionTotalSize int + + // CompactionTotalSizeMultiplier defines multiplier for CompactionTotalSize. + // + // The default value is 10. + CompactionTotalSizeMultiplier float64 + + // CompactionTotalSizeMultiplierPerLevel defines per-level multiplier for + // CompactionTotalSize. + // Use zero to skip a level. + // + // The default value is nil. + CompactionTotalSizeMultiplierPerLevel []float64 + + // Comparer defines a total ordering over the space of []byte keys: a 'less + // than' relationship. The same comparison algorithm must be used for reads + // and writes over the lifetime of the DB. + // + // The default value uses the same ordering as bytes.Compare. + Comparer comparer.Comparer + + // Compression defines the 'sorted table' block compression to use. + // + // The default value (DefaultCompression) uses snappy compression. + Compression Compression + + // DisableBufferPool allows disable use of util.BufferPool functionality. + // + // The default value is false. + DisableBufferPool bool + + // DisableBlockCache allows disable use of cache.Cache functionality on + // 'sorted table' block. + // + // The default value is false. + DisableBlockCache bool + + // DisableCompactionBackoff allows disable compaction retry backoff. + // + // The default value is false. + DisableCompactionBackoff bool + + // DisableLargeBatchTransaction allows disabling switch-to-transaction mode + // on large batch write. If enable batch writes large than WriteBuffer will + // use transaction. + // + // The default is false. + DisableLargeBatchTransaction bool + + // ErrorIfExist defines whether an error should returned if the DB already + // exist. + // + // The default value is false. + ErrorIfExist bool + + // ErrorIfMissing defines whether an error should returned if the DB is + // missing. If false then the database will be created if missing, otherwise + // an error will be returned. + // + // The default value is false. + ErrorIfMissing bool + + // Filter defines an 'effective filter' to use. An 'effective filter' + // if defined will be used to generate per-table filter block. + // The filter name will be stored on disk. + // During reads LevelDB will try to find matching filter from + // 'effective filter' and 'alternative filters'. + // + // Filter can be changed after a DB has been created. It is recommended + // to put old filter to the 'alternative filters' to mitigate lack of + // filter during transition period. + // + // A filter is used to reduce disk reads when looking for a specific key. + // + // The default value is nil. + Filter filter.Filter + + // IteratorSamplingRate defines approximate gap (in bytes) between read + // sampling of an iterator. The samples will be used to determine when + // compaction should be triggered. + // + // The default is 1MiB. + IteratorSamplingRate int + + // NoSync allows completely disable fsync. + // + // The default is false. + NoSync bool + + // NoWriteMerge allows disabling write merge. + // + // The default is false. + NoWriteMerge bool + + // OpenFilesCacher provides cache algorithm for open files caching. + // Specify NoCacher to disable caching algorithm. + // + // The default value is LRUCacher. + OpenFilesCacher Cacher + + // OpenFilesCacheCapacity defines the capacity of the open files caching. + // Use -1 for zero, this has same effect as specifying NoCacher to OpenFilesCacher. + // + // The default value is 500. + OpenFilesCacheCapacity int + + // If true then opens DB in read-only mode. + // + // The default value is false. + ReadOnly bool + + // Strict defines the DB strict level. + Strict Strict + + // WriteBuffer defines maximum size of a 'memdb' before flushed to + // 'sorted table'. 'memdb' is an in-memory DB backed by an on-disk + // unsorted journal. + // + // LevelDB may held up to two 'memdb' at the same time. + // + // The default value is 4MiB. + WriteBuffer int + + // WriteL0StopTrigger defines number of 'sorted table' at level-0 that will + // pause write. + // + // The default value is 12. + WriteL0PauseTrigger int + + // WriteL0SlowdownTrigger defines number of 'sorted table' at level-0 that + // will trigger write slowdown. + // + // The default value is 8. + WriteL0SlowdownTrigger int +} + +func (o *Options) GetAltFilters() []filter.Filter { + if o == nil { + return nil + } + return o.AltFilters +} + +func (o *Options) GetBlockCacher() Cacher { + if o == nil || o.BlockCacher == nil { + return DefaultBlockCacher + } else if o.BlockCacher == NoCacher { + return nil + } + return o.BlockCacher +} + +func (o *Options) GetBlockCacheCapacity() int { + if o == nil || o.BlockCacheCapacity == 0 { + return DefaultBlockCacheCapacity + } else if o.BlockCacheCapacity < 0 { + return 0 + } + return o.BlockCacheCapacity +} + +func (o *Options) GetBlockRestartInterval() int { + if o == nil || o.BlockRestartInterval <= 0 { + return DefaultBlockRestartInterval + } + return o.BlockRestartInterval +} + +func (o *Options) GetBlockSize() int { + if o == nil || o.BlockSize <= 0 { + return DefaultBlockSize + } + return o.BlockSize +} + +func (o *Options) GetCompactionExpandLimit(level int) int { + factor := DefaultCompactionExpandLimitFactor + if o != nil && o.CompactionExpandLimitFactor > 0 { + factor = o.CompactionExpandLimitFactor + } + return o.GetCompactionTableSize(level+1) * factor +} + +func (o *Options) GetCompactionGPOverlaps(level int) int { + factor := DefaultCompactionGPOverlapsFactor + if o != nil && o.CompactionGPOverlapsFactor > 0 { + factor = o.CompactionGPOverlapsFactor + } + return o.GetCompactionTableSize(level+2) * factor +} + +func (o *Options) GetCompactionL0Trigger() int { + if o == nil || o.CompactionL0Trigger == 0 { + return DefaultCompactionL0Trigger + } + return o.CompactionL0Trigger +} + +func (o *Options) GetCompactionSourceLimit(level int) int { + factor := DefaultCompactionSourceLimitFactor + if o != nil && o.CompactionSourceLimitFactor > 0 { + factor = o.CompactionSourceLimitFactor + } + return o.GetCompactionTableSize(level+1) * factor +} + +func (o *Options) GetCompactionTableSize(level int) int { + var ( + base = DefaultCompactionTableSize + mult float64 + ) + if o != nil { + if o.CompactionTableSize > 0 { + base = o.CompactionTableSize + } + if level < len(o.CompactionTableSizeMultiplierPerLevel) && o.CompactionTableSizeMultiplierPerLevel[level] > 0 { + mult = o.CompactionTableSizeMultiplierPerLevel[level] + } else if o.CompactionTableSizeMultiplier > 0 { + mult = math.Pow(o.CompactionTableSizeMultiplier, float64(level)) + } + } + if mult == 0 { + mult = math.Pow(DefaultCompactionTableSizeMultiplier, float64(level)) + } + return int(float64(base) * mult) +} + +func (o *Options) GetCompactionTotalSize(level int) int64 { + var ( + base = DefaultCompactionTotalSize + mult float64 + ) + if o != nil { + if o.CompactionTotalSize > 0 { + base = o.CompactionTotalSize + } + if level < len(o.CompactionTotalSizeMultiplierPerLevel) && o.CompactionTotalSizeMultiplierPerLevel[level] > 0 { + mult = o.CompactionTotalSizeMultiplierPerLevel[level] + } else if o.CompactionTotalSizeMultiplier > 0 { + mult = math.Pow(o.CompactionTotalSizeMultiplier, float64(level)) + } + } + if mult == 0 { + mult = math.Pow(DefaultCompactionTotalSizeMultiplier, float64(level)) + } + return int64(float64(base) * mult) +} + +func (o *Options) GetComparer() comparer.Comparer { + if o == nil || o.Comparer == nil { + return comparer.DefaultComparer + } + return o.Comparer +} + +func (o *Options) GetCompression() Compression { + if o == nil || o.Compression <= DefaultCompression || o.Compression >= nCompression { + return DefaultCompressionType + } + return o.Compression +} + +func (o *Options) GetDisableBufferPool() bool { + if o == nil { + return false + } + return o.DisableBufferPool +} + +func (o *Options) GetDisableBlockCache() bool { + if o == nil { + return false + } + return o.DisableBlockCache +} + +func (o *Options) GetDisableCompactionBackoff() bool { + if o == nil { + return false + } + return o.DisableCompactionBackoff +} + +func (o *Options) GetDisableLargeBatchTransaction() bool { + if o == nil { + return false + } + return o.DisableLargeBatchTransaction +} + +func (o *Options) GetErrorIfExist() bool { + if o == nil { + return false + } + return o.ErrorIfExist +} + +func (o *Options) GetErrorIfMissing() bool { + if o == nil { + return false + } + return o.ErrorIfMissing +} + +func (o *Options) GetFilter() filter.Filter { + if o == nil { + return nil + } + return o.Filter +} + +func (o *Options) GetIteratorSamplingRate() int { + if o == nil || o.IteratorSamplingRate <= 0 { + return DefaultIteratorSamplingRate + } + return o.IteratorSamplingRate +} + +func (o *Options) GetNoSync() bool { + if o == nil { + return false + } + return o.NoSync +} + +func (o *Options) GetNoWriteMerge() bool { + if o == nil { + return false + } + return o.NoWriteMerge +} + +func (o *Options) GetOpenFilesCacher() Cacher { + if o == nil || o.OpenFilesCacher == nil { + return DefaultOpenFilesCacher + } + if o.OpenFilesCacher == NoCacher { + return nil + } + return o.OpenFilesCacher +} + +func (o *Options) GetOpenFilesCacheCapacity() int { + if o == nil || o.OpenFilesCacheCapacity == 0 { + return DefaultOpenFilesCacheCapacity + } else if o.OpenFilesCacheCapacity < 0 { + return 0 + } + return o.OpenFilesCacheCapacity +} + +func (o *Options) GetReadOnly() bool { + if o == nil { + return false + } + return o.ReadOnly +} + +func (o *Options) GetStrict(strict Strict) bool { + if o == nil || o.Strict == 0 { + return DefaultStrict&strict != 0 + } + return o.Strict&strict != 0 +} + +func (o *Options) GetWriteBuffer() int { + if o == nil || o.WriteBuffer <= 0 { + return DefaultWriteBuffer + } + return o.WriteBuffer +} + +func (o *Options) GetWriteL0PauseTrigger() int { + if o == nil || o.WriteL0PauseTrigger == 0 { + return DefaultWriteL0PauseTrigger + } + return o.WriteL0PauseTrigger +} + +func (o *Options) GetWriteL0SlowdownTrigger() int { + if o == nil || o.WriteL0SlowdownTrigger == 0 { + return DefaultWriteL0SlowdownTrigger + } + return o.WriteL0SlowdownTrigger +} + +// ReadOptions holds the optional parameters for 'read operation'. The +// 'read operation' includes Get, Find and NewIterator. +type ReadOptions struct { + // DontFillCache defines whether block reads for this 'read operation' + // should be cached. If false then the block will be cached. This does + // not affects already cached block. + // + // The default value is false. + DontFillCache bool + + // Strict will be OR'ed with global DB 'strict level' unless StrictOverride + // is present. Currently only StrictReader that has effect here. + Strict Strict +} + +func (ro *ReadOptions) GetDontFillCache() bool { + if ro == nil { + return false + } + return ro.DontFillCache +} + +func (ro *ReadOptions) GetStrict(strict Strict) bool { + if ro == nil { + return false + } + return ro.Strict&strict != 0 +} + +// WriteOptions holds the optional parameters for 'write operation'. The +// 'write operation' includes Write, Put and Delete. +type WriteOptions struct { + // NoWriteMerge allows disabling write merge. + // + // The default is false. + NoWriteMerge bool + + // Sync is whether to sync underlying writes from the OS buffer cache + // through to actual disk, if applicable. Setting Sync can result in + // slower writes. + // + // If false, and the machine crashes, then some recent writes may be lost. + // Note that if it is just the process that crashes (and the machine does + // not) then no writes will be lost. + // + // In other words, Sync being false has the same semantics as a write + // system call. Sync being true means write followed by fsync. + // + // The default value is false. + Sync bool +} + +func (wo *WriteOptions) GetNoWriteMerge() bool { + if wo == nil { + return false + } + return wo.NoWriteMerge +} + +func (wo *WriteOptions) GetSync() bool { + if wo == nil { + return false + } + return wo.Sync +} + +func GetStrict(o *Options, ro *ReadOptions, strict Strict) bool { + if ro.GetStrict(StrictOverride) { + return ro.GetStrict(strict) + } else { + return o.GetStrict(strict) || ro.GetStrict(strict) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/options.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/options.go new file mode 100644 index 0000000..b072b1a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/options.go @@ -0,0 +1,107 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "github.com/syndtr/goleveldb/leveldb/filter" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +func dupOptions(o *opt.Options) *opt.Options { + newo := &opt.Options{} + if o != nil { + *newo = *o + } + if newo.Strict == 0 { + newo.Strict = opt.DefaultStrict + } + return newo +} + +func (s *session) setOptions(o *opt.Options) { + no := dupOptions(o) + // Alternative filters. + if filters := o.GetAltFilters(); len(filters) > 0 { + no.AltFilters = make([]filter.Filter, len(filters)) + for i, filter := range filters { + no.AltFilters[i] = &iFilter{filter} + } + } + // Comparer. + s.icmp = &iComparer{o.GetComparer()} + no.Comparer = s.icmp + // Filter. + if filter := o.GetFilter(); filter != nil { + no.Filter = &iFilter{filter} + } + + s.o = &cachedOptions{Options: no} + s.o.cache() +} + +const optCachedLevel = 7 + +type cachedOptions struct { + *opt.Options + + compactionExpandLimit []int + compactionGPOverlaps []int + compactionSourceLimit []int + compactionTableSize []int + compactionTotalSize []int64 +} + +func (co *cachedOptions) cache() { + co.compactionExpandLimit = make([]int, optCachedLevel) + co.compactionGPOverlaps = make([]int, optCachedLevel) + co.compactionSourceLimit = make([]int, optCachedLevel) + co.compactionTableSize = make([]int, optCachedLevel) + co.compactionTotalSize = make([]int64, optCachedLevel) + + for level := 0; level < optCachedLevel; level++ { + co.compactionExpandLimit[level] = co.Options.GetCompactionExpandLimit(level) + co.compactionGPOverlaps[level] = co.Options.GetCompactionGPOverlaps(level) + co.compactionSourceLimit[level] = co.Options.GetCompactionSourceLimit(level) + co.compactionTableSize[level] = co.Options.GetCompactionTableSize(level) + co.compactionTotalSize[level] = co.Options.GetCompactionTotalSize(level) + } +} + +func (co *cachedOptions) GetCompactionExpandLimit(level int) int { + if level < optCachedLevel { + return co.compactionExpandLimit[level] + } + return co.Options.GetCompactionExpandLimit(level) +} + +func (co *cachedOptions) GetCompactionGPOverlaps(level int) int { + if level < optCachedLevel { + return co.compactionGPOverlaps[level] + } + return co.Options.GetCompactionGPOverlaps(level) +} + +func (co *cachedOptions) GetCompactionSourceLimit(level int) int { + if level < optCachedLevel { + return co.compactionSourceLimit[level] + } + return co.Options.GetCompactionSourceLimit(level) +} + +func (co *cachedOptions) GetCompactionTableSize(level int) int { + if level < optCachedLevel { + return co.compactionTableSize[level] + } + return co.Options.GetCompactionTableSize(level) +} + +func (co *cachedOptions) GetCompactionTotalSize(level int) int64 { + if level < optCachedLevel { + return co.compactionTotalSize[level] + } + return co.Options.GetCompactionTotalSize(level) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session.go new file mode 100644 index 0000000..ad68a87 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session.go @@ -0,0 +1,210 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "fmt" + "io" + "os" + "sync" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/journal" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +// ErrManifestCorrupted records manifest corruption. This error will be +// wrapped with errors.ErrCorrupted. +type ErrManifestCorrupted struct { + Field string + Reason string +} + +func (e *ErrManifestCorrupted) Error() string { + return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason) +} + +func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error { + return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason}) +} + +// session represent a persistent database session. +type session struct { + // Need 64-bit alignment. + stNextFileNum int64 // current unused file number + stJournalNum int64 // current journal file number; need external synchronization + stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb + stTempFileNum int64 + stSeqNum uint64 // last mem compacted seq; need external synchronization + + stor storage.Storage + storLock storage.Locker + o *cachedOptions + icmp *iComparer + tops *tOps + fileRef map[int64]int + + manifest *journal.Writer + manifestWriter storage.Writer + manifestFd storage.FileDesc + + stCompPtrs []internalKey // compaction pointers; need external synchronization + stVersion *version // current version + vmu sync.Mutex +} + +// Creates new initialized session instance. +func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) { + if stor == nil { + return nil, os.ErrInvalid + } + storLock, err := stor.Lock() + if err != nil { + return + } + s = &session{ + stor: stor, + storLock: storLock, + fileRef: make(map[int64]int), + } + s.setOptions(o) + s.tops = newTableOps(s) + s.setVersion(newVersion(s)) + s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed") + return +} + +// Close session. +func (s *session) close() { + s.tops.close() + if s.manifest != nil { + s.manifest.Close() + } + if s.manifestWriter != nil { + s.manifestWriter.Close() + } + s.manifest = nil + s.manifestWriter = nil + s.setVersion(&version{s: s, closing: true}) +} + +// Release session lock. +func (s *session) release() { + s.storLock.Unlock() +} + +// Create a new database session; need external synchronization. +func (s *session) create() error { + // create manifest + return s.newManifest(nil, nil) +} + +// Recover a database session; need external synchronization. +func (s *session) recover() (err error) { + defer func() { + if os.IsNotExist(err) { + // Don't return os.ErrNotExist if the underlying storage contains + // other files that belong to LevelDB. So the DB won't get trashed. + if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 { + err = &errors.ErrCorrupted{Fd: storage.FileDesc{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}} + } + } + }() + + fd, err := s.stor.GetMeta() + if err != nil { + return + } + + reader, err := s.stor.Open(fd) + if err != nil { + return + } + defer reader.Close() + + var ( + // Options. + strict = s.o.GetStrict(opt.StrictManifest) + + jr = journal.NewReader(reader, dropper{s, fd}, strict, true) + rec = &sessionRecord{} + staging = s.stVersion.newStaging() + ) + for { + var r io.Reader + r, err = jr.Next() + if err != nil { + if err == io.EOF { + err = nil + break + } + return errors.SetFd(err, fd) + } + + err = rec.decode(r) + if err == nil { + // save compact pointers + for _, r := range rec.compPtrs { + s.setCompPtr(r.level, internalKey(r.ikey)) + } + // commit record to version staging + staging.commit(rec) + } else { + err = errors.SetFd(err, fd) + if strict || !errors.IsCorrupted(err) { + return + } + s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd)) + } + rec.resetCompPtrs() + rec.resetAddedTables() + rec.resetDeletedTables() + } + + switch { + case !rec.has(recComparer): + return newErrManifestCorrupted(fd, "comparer", "missing") + case rec.comparer != s.icmp.uName(): + return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer)) + case !rec.has(recNextFileNum): + return newErrManifestCorrupted(fd, "next-file-num", "missing") + case !rec.has(recJournalNum): + return newErrManifestCorrupted(fd, "journal-file-num", "missing") + case !rec.has(recSeqNum): + return newErrManifestCorrupted(fd, "seq-num", "missing") + } + + s.manifestFd = fd + s.setVersion(staging.finish()) + s.setNextFileNum(rec.nextFileNum) + s.recordCommited(rec) + return nil +} + +// Commit session; need external synchronization. +func (s *session) commit(r *sessionRecord) (err error) { + v := s.version() + defer v.release() + + // spawn new version based on current version + nv := v.spawn(r) + + if s.manifest == nil { + // manifest journal writer not yet created, create one + err = s.newManifest(r, nv) + } else { + err = s.flushManifest(r) + } + + // finally, apply new version if no error rise + if err == nil { + s.setVersion(nv) + } + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go new file mode 100644 index 0000000..089cd00 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go @@ -0,0 +1,302 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "sync/atomic" + + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/memdb" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +func (s *session) pickMemdbLevel(umin, umax []byte, maxLevel int) int { + v := s.version() + defer v.release() + return v.pickMemdbLevel(umin, umax, maxLevel) +} + +func (s *session) flushMemdb(rec *sessionRecord, mdb *memdb.DB, maxLevel int) (int, error) { + // Create sorted table. + iter := mdb.NewIterator(nil) + defer iter.Release() + t, n, err := s.tops.createFrom(iter) + if err != nil { + return 0, err + } + + // Pick level other than zero can cause compaction issue with large + // bulk insert and delete on strictly incrementing key-space. The + // problem is that the small deletion markers trapped at lower level, + // while key/value entries keep growing at higher level. Since the + // key-space is strictly incrementing it will not overlaps with + // higher level, thus maximum possible level is always picked, while + // overlapping deletion marker pushed into lower level. + // See: https://github.com/syndtr/goleveldb/issues/127. + flushLevel := s.pickMemdbLevel(t.imin.ukey(), t.imax.ukey(), maxLevel) + rec.addTableFile(flushLevel, t) + + s.logf("memdb@flush created L%d@%d N·%d S·%s %q:%q", flushLevel, t.fd.Num, n, shortenb(int(t.size)), t.imin, t.imax) + return flushLevel, nil +} + +// Pick a compaction based on current state; need external synchronization. +func (s *session) pickCompaction() *compaction { + v := s.version() + + var sourceLevel int + var t0 tFiles + if v.cScore >= 1 { + sourceLevel = v.cLevel + cptr := s.getCompPtr(sourceLevel) + tables := v.levels[sourceLevel] + for _, t := range tables { + if cptr == nil || s.icmp.Compare(t.imax, cptr) > 0 { + t0 = append(t0, t) + break + } + } + if len(t0) == 0 { + t0 = append(t0, tables[0]) + } + } else { + if p := atomic.LoadPointer(&v.cSeek); p != nil { + ts := (*tSet)(p) + sourceLevel = ts.level + t0 = append(t0, ts.table) + } else { + v.release() + return nil + } + } + + return newCompaction(s, v, sourceLevel, t0) +} + +// Create compaction from given level and range; need external synchronization. +func (s *session) getCompactionRange(sourceLevel int, umin, umax []byte, noLimit bool) *compaction { + v := s.version() + + if sourceLevel >= len(v.levels) { + v.release() + return nil + } + + t0 := v.levels[sourceLevel].getOverlaps(nil, s.icmp, umin, umax, sourceLevel == 0) + if len(t0) == 0 { + v.release() + return nil + } + + // Avoid compacting too much in one shot in case the range is large. + // But we cannot do this for level-0 since level-0 files can overlap + // and we must not pick one file and drop another older file if the + // two files overlap. + if !noLimit && sourceLevel > 0 { + limit := int64(v.s.o.GetCompactionSourceLimit(sourceLevel)) + total := int64(0) + for i, t := range t0 { + total += t.size + if total >= limit { + s.logf("table@compaction limiting F·%d -> F·%d", len(t0), i+1) + t0 = t0[:i+1] + break + } + } + } + + return newCompaction(s, v, sourceLevel, t0) +} + +func newCompaction(s *session, v *version, sourceLevel int, t0 tFiles) *compaction { + c := &compaction{ + s: s, + v: v, + sourceLevel: sourceLevel, + levels: [2]tFiles{t0, nil}, + maxGPOverlaps: int64(s.o.GetCompactionGPOverlaps(sourceLevel)), + tPtrs: make([]int, len(v.levels)), + } + c.expand() + c.save() + return c +} + +// compaction represent a compaction state. +type compaction struct { + s *session + v *version + + sourceLevel int + levels [2]tFiles + maxGPOverlaps int64 + + gp tFiles + gpi int + seenKey bool + gpOverlappedBytes int64 + imin, imax internalKey + tPtrs []int + released bool + + snapGPI int + snapSeenKey bool + snapGPOverlappedBytes int64 + snapTPtrs []int +} + +func (c *compaction) save() { + c.snapGPI = c.gpi + c.snapSeenKey = c.seenKey + c.snapGPOverlappedBytes = c.gpOverlappedBytes + c.snapTPtrs = append(c.snapTPtrs[:0], c.tPtrs...) +} + +func (c *compaction) restore() { + c.gpi = c.snapGPI + c.seenKey = c.snapSeenKey + c.gpOverlappedBytes = c.snapGPOverlappedBytes + c.tPtrs = append(c.tPtrs[:0], c.snapTPtrs...) +} + +func (c *compaction) release() { + if !c.released { + c.released = true + c.v.release() + } +} + +// Expand compacted tables; need external synchronization. +func (c *compaction) expand() { + limit := int64(c.s.o.GetCompactionExpandLimit(c.sourceLevel)) + vt0 := c.v.levels[c.sourceLevel] + vt1 := tFiles{} + if level := c.sourceLevel + 1; level < len(c.v.levels) { + vt1 = c.v.levels[level] + } + + t0, t1 := c.levels[0], c.levels[1] + imin, imax := t0.getRange(c.s.icmp) + // We expand t0 here just incase ukey hop across tables. + t0 = vt0.getOverlaps(t0, c.s.icmp, imin.ukey(), imax.ukey(), c.sourceLevel == 0) + if len(t0) != len(c.levels[0]) { + imin, imax = t0.getRange(c.s.icmp) + } + t1 = vt1.getOverlaps(t1, c.s.icmp, imin.ukey(), imax.ukey(), false) + // Get entire range covered by compaction. + amin, amax := append(t0, t1...).getRange(c.s.icmp) + + // See if we can grow the number of inputs in "sourceLevel" without + // changing the number of "sourceLevel+1" files we pick up. + if len(t1) > 0 { + exp0 := vt0.getOverlaps(nil, c.s.icmp, amin.ukey(), amax.ukey(), c.sourceLevel == 0) + if len(exp0) > len(t0) && t1.size()+exp0.size() < limit { + xmin, xmax := exp0.getRange(c.s.icmp) + exp1 := vt1.getOverlaps(nil, c.s.icmp, xmin.ukey(), xmax.ukey(), false) + if len(exp1) == len(t1) { + c.s.logf("table@compaction expanding L%d+L%d (F·%d S·%s)+(F·%d S·%s) -> (F·%d S·%s)+(F·%d S·%s)", + c.sourceLevel, c.sourceLevel+1, len(t0), shortenb(int(t0.size())), len(t1), shortenb(int(t1.size())), + len(exp0), shortenb(int(exp0.size())), len(exp1), shortenb(int(exp1.size()))) + imin, imax = xmin, xmax + t0, t1 = exp0, exp1 + amin, amax = append(t0, t1...).getRange(c.s.icmp) + } + } + } + + // Compute the set of grandparent files that overlap this compaction + // (parent == sourceLevel+1; grandparent == sourceLevel+2) + if level := c.sourceLevel + 2; level < len(c.v.levels) { + c.gp = c.v.levels[level].getOverlaps(c.gp, c.s.icmp, amin.ukey(), amax.ukey(), false) + } + + c.levels[0], c.levels[1] = t0, t1 + c.imin, c.imax = imin, imax +} + +// Check whether compaction is trivial. +func (c *compaction) trivial() bool { + return len(c.levels[0]) == 1 && len(c.levels[1]) == 0 && c.gp.size() <= c.maxGPOverlaps +} + +func (c *compaction) baseLevelForKey(ukey []byte) bool { + for level := c.sourceLevel + 2; level < len(c.v.levels); level++ { + tables := c.v.levels[level] + for c.tPtrs[level] < len(tables) { + t := tables[c.tPtrs[level]] + if c.s.icmp.uCompare(ukey, t.imax.ukey()) <= 0 { + // We've advanced far enough. + if c.s.icmp.uCompare(ukey, t.imin.ukey()) >= 0 { + // Key falls in this file's range, so definitely not base level. + return false + } + break + } + c.tPtrs[level]++ + } + } + return true +} + +func (c *compaction) shouldStopBefore(ikey internalKey) bool { + for ; c.gpi < len(c.gp); c.gpi++ { + gp := c.gp[c.gpi] + if c.s.icmp.Compare(ikey, gp.imax) <= 0 { + break + } + if c.seenKey { + c.gpOverlappedBytes += gp.size + } + } + c.seenKey = true + + if c.gpOverlappedBytes > c.maxGPOverlaps { + // Too much overlap for current output; start new output. + c.gpOverlappedBytes = 0 + return true + } + return false +} + +// Creates an iterator. +func (c *compaction) newIterator() iterator.Iterator { + // Creates iterator slice. + icap := len(c.levels) + if c.sourceLevel == 0 { + // Special case for level-0. + icap = len(c.levels[0]) + 1 + } + its := make([]iterator.Iterator, 0, icap) + + // Options. + ro := &opt.ReadOptions{ + DontFillCache: true, + Strict: opt.StrictOverride, + } + strict := c.s.o.GetStrict(opt.StrictCompaction) + if strict { + ro.Strict |= opt.StrictReader + } + + for i, tables := range c.levels { + if len(tables) == 0 { + continue + } + + // Level-0 is not sorted and may overlaps each other. + if c.sourceLevel+i == 0 { + for _, t := range tables { + its = append(its, c.s.tops.newIterator(t, nil, ro)) + } + } else { + it := iterator.NewIndexedIterator(tables.newIndexIterator(c.s.tops, c.s.icmp, nil, ro), strict) + its = append(its, it) + } + } + + return iterator.NewMergedIterator(its, c.s.icmp, strict) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_record.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_record.go new file mode 100644 index 0000000..854e1aa --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/session_record.go @@ -0,0 +1,323 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "bufio" + "encoding/binary" + "io" + "strings" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +type byteReader interface { + io.Reader + io.ByteReader +} + +// These numbers are written to disk and should not be changed. +const ( + recComparer = 1 + recJournalNum = 2 + recNextFileNum = 3 + recSeqNum = 4 + recCompPtr = 5 + recDelTable = 6 + recAddTable = 7 + // 8 was used for large value refs + recPrevJournalNum = 9 +) + +type cpRecord struct { + level int + ikey internalKey +} + +type atRecord struct { + level int + num int64 + size int64 + imin internalKey + imax internalKey +} + +type dtRecord struct { + level int + num int64 +} + +type sessionRecord struct { + hasRec int + comparer string + journalNum int64 + prevJournalNum int64 + nextFileNum int64 + seqNum uint64 + compPtrs []cpRecord + addedTables []atRecord + deletedTables []dtRecord + + scratch [binary.MaxVarintLen64]byte + err error +} + +func (p *sessionRecord) has(rec int) bool { + return p.hasRec&(1< +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "fmt" + "sync/atomic" + + "github.com/syndtr/goleveldb/leveldb/journal" + "github.com/syndtr/goleveldb/leveldb/storage" +) + +// Logging. + +type dropper struct { + s *session + fd storage.FileDesc +} + +func (d dropper) Drop(err error) { + if e, ok := err.(*journal.ErrCorrupted); ok { + d.s.logf("journal@drop %s-%d S·%s %q", d.fd.Type, d.fd.Num, shortenb(e.Size), e.Reason) + } else { + d.s.logf("journal@drop %s-%d %q", d.fd.Type, d.fd.Num, err) + } +} + +func (s *session) log(v ...interface{}) { s.stor.Log(fmt.Sprint(v...)) } +func (s *session) logf(format string, v ...interface{}) { s.stor.Log(fmt.Sprintf(format, v...)) } + +// File utils. + +func (s *session) newTemp() storage.FileDesc { + num := atomic.AddInt64(&s.stTempFileNum, 1) - 1 + return storage.FileDesc{storage.TypeTemp, num} +} + +func (s *session) addFileRef(fd storage.FileDesc, ref int) int { + ref += s.fileRef[fd.Num] + if ref > 0 { + s.fileRef[fd.Num] = ref + } else if ref == 0 { + delete(s.fileRef, fd.Num) + } else { + panic(fmt.Sprintf("negative ref: %v", fd)) + } + return ref +} + +// Session state. + +// Get current version. This will incr version ref, must call +// version.release (exactly once) after use. +func (s *session) version() *version { + s.vmu.Lock() + defer s.vmu.Unlock() + s.stVersion.incref() + return s.stVersion +} + +func (s *session) tLen(level int) int { + s.vmu.Lock() + defer s.vmu.Unlock() + return s.stVersion.tLen(level) +} + +// Set current version to v. +func (s *session) setVersion(v *version) { + s.vmu.Lock() + defer s.vmu.Unlock() + // Hold by session. It is important to call this first before releasing + // current version, otherwise the still used files might get released. + v.incref() + if s.stVersion != nil { + // Release current version. + s.stVersion.releaseNB() + } + s.stVersion = v +} + +// Get current unused file number. +func (s *session) nextFileNum() int64 { + return atomic.LoadInt64(&s.stNextFileNum) +} + +// Set current unused file number to num. +func (s *session) setNextFileNum(num int64) { + atomic.StoreInt64(&s.stNextFileNum, num) +} + +// Mark file number as used. +func (s *session) markFileNum(num int64) { + nextFileNum := num + 1 + for { + old, x := s.stNextFileNum, nextFileNum + if old > x { + x = old + } + if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) { + break + } + } +} + +// Allocate a file number. +func (s *session) allocFileNum() int64 { + return atomic.AddInt64(&s.stNextFileNum, 1) - 1 +} + +// Reuse given file number. +func (s *session) reuseFileNum(num int64) { + for { + old, x := s.stNextFileNum, num + if old != x+1 { + x = old + } + if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) { + break + } + } +} + +// Set compaction ptr at given level; need external synchronization. +func (s *session) setCompPtr(level int, ik internalKey) { + if level >= len(s.stCompPtrs) { + newCompPtrs := make([]internalKey, level+1) + copy(newCompPtrs, s.stCompPtrs) + s.stCompPtrs = newCompPtrs + } + s.stCompPtrs[level] = append(internalKey{}, ik...) +} + +// Get compaction ptr at given level; need external synchronization. +func (s *session) getCompPtr(level int) internalKey { + if level >= len(s.stCompPtrs) { + return nil + } + return s.stCompPtrs[level] +} + +// Manifest related utils. + +// Fill given session record obj with current states; need external +// synchronization. +func (s *session) fillRecord(r *sessionRecord, snapshot bool) { + r.setNextFileNum(s.nextFileNum()) + + if snapshot { + if !r.has(recJournalNum) { + r.setJournalNum(s.stJournalNum) + } + + if !r.has(recSeqNum) { + r.setSeqNum(s.stSeqNum) + } + + for level, ik := range s.stCompPtrs { + if ik != nil { + r.addCompPtr(level, ik) + } + } + + r.setComparer(s.icmp.uName()) + } +} + +// Mark if record has been committed, this will update session state; +// need external synchronization. +func (s *session) recordCommited(rec *sessionRecord) { + if rec.has(recJournalNum) { + s.stJournalNum = rec.journalNum + } + + if rec.has(recPrevJournalNum) { + s.stPrevJournalNum = rec.prevJournalNum + } + + if rec.has(recSeqNum) { + s.stSeqNum = rec.seqNum + } + + for _, r := range rec.compPtrs { + s.setCompPtr(r.level, internalKey(r.ikey)) + } +} + +// Create a new manifest file; need external synchronization. +func (s *session) newManifest(rec *sessionRecord, v *version) (err error) { + fd := storage.FileDesc{storage.TypeManifest, s.allocFileNum()} + writer, err := s.stor.Create(fd) + if err != nil { + return + } + jw := journal.NewWriter(writer) + + if v == nil { + v = s.version() + defer v.release() + } + if rec == nil { + rec = &sessionRecord{} + } + s.fillRecord(rec, true) + v.fillRecord(rec) + + defer func() { + if err == nil { + s.recordCommited(rec) + if s.manifest != nil { + s.manifest.Close() + } + if s.manifestWriter != nil { + s.manifestWriter.Close() + } + if !s.manifestFd.Zero() { + s.stor.Remove(s.manifestFd) + } + s.manifestFd = fd + s.manifestWriter = writer + s.manifest = jw + } else { + writer.Close() + s.stor.Remove(fd) + s.reuseFileNum(fd.Num) + } + }() + + w, err := jw.Next() + if err != nil { + return + } + err = rec.encode(w) + if err != nil { + return + } + err = jw.Flush() + if err != nil { + return + } + err = s.stor.SetMeta(fd) + return +} + +// Flush record to disk. +func (s *session) flushManifest(rec *sessionRecord) (err error) { + s.fillRecord(rec, false) + w, err := s.manifest.Next() + if err != nil { + return + } + err = rec.encode(w) + if err != nil { + return + } + err = s.manifest.Flush() + if err != nil { + return + } + if !s.o.GetNoSync() { + err = s.manifestWriter.Sync() + if err != nil { + return + } + } + s.recordCommited(rec) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go new file mode 100644 index 0000000..e53434c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go @@ -0,0 +1,583 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reservefs. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package storage + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +var ( + errFileOpen = errors.New("leveldb/storage: file still open") + errReadOnly = errors.New("leveldb/storage: storage is read-only") +) + +type fileLock interface { + release() error +} + +type fileStorageLock struct { + fs *fileStorage +} + +func (lock *fileStorageLock) Unlock() { + if lock.fs != nil { + lock.fs.mu.Lock() + defer lock.fs.mu.Unlock() + if lock.fs.slock == lock { + lock.fs.slock = nil + } + } +} + +const logSizeThreshold = 1024 * 1024 // 1 MiB + +// fileStorage is a file-system backed storage. +type fileStorage struct { + path string + readOnly bool + + mu sync.Mutex + flock fileLock + slock *fileStorageLock + logw *os.File + logSize int64 + buf []byte + // Opened file counter; if open < 0 means closed. + open int + day int +} + +// OpenFile returns a new filesytem-backed storage implementation with the given +// path. This also acquire a file lock, so any subsequent attempt to open the +// same path will fail. +// +// The storage must be closed after use, by calling Close method. +func OpenFile(path string, readOnly bool) (Storage, error) { + if fi, err := os.Stat(path); err == nil { + if !fi.IsDir() { + return nil, fmt.Errorf("leveldb/storage: open %s: not a directory", path) + } + } else if os.IsNotExist(err) && !readOnly { + if err := os.MkdirAll(path, 0755); err != nil { + return nil, err + } + } else { + return nil, err + } + + flock, err := newFileLock(filepath.Join(path, "LOCK"), readOnly) + if err != nil { + return nil, err + } + + defer func() { + if err != nil { + flock.release() + } + }() + + var ( + logw *os.File + logSize int64 + ) + if !readOnly { + logw, err = os.OpenFile(filepath.Join(path, "LOG"), os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return nil, err + } + logSize, err = logw.Seek(0, os.SEEK_END) + if err != nil { + logw.Close() + return nil, err + } + } + + fs := &fileStorage{ + path: path, + readOnly: readOnly, + flock: flock, + logw: logw, + logSize: logSize, + } + runtime.SetFinalizer(fs, (*fileStorage).Close) + return fs, nil +} + +func (fs *fileStorage) Lock() (Locker, error) { + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return nil, ErrClosed + } + if fs.readOnly { + return &fileStorageLock{}, nil + } + if fs.slock != nil { + return nil, ErrLocked + } + fs.slock = &fileStorageLock{fs: fs} + return fs.slock, nil +} + +func itoa(buf []byte, i int, wid int) []byte { + u := uint(i) + if u == 0 && wid <= 1 { + return append(buf, '0') + } + + // Assemble decimal in reverse order. + var b [32]byte + bp := len(b) + for ; u > 0 || wid > 0; u /= 10 { + bp-- + wid-- + b[bp] = byte(u%10) + '0' + } + return append(buf, b[bp:]...) +} + +func (fs *fileStorage) printDay(t time.Time) { + if fs.day == t.Day() { + return + } + fs.day = t.Day() + fs.logw.Write([]byte("=============== " + t.Format("Jan 2, 2006 (MST)") + " ===============\n")) +} + +func (fs *fileStorage) doLog(t time.Time, str string) { + if fs.logSize > logSizeThreshold { + // Rotate log file. + fs.logw.Close() + fs.logw = nil + fs.logSize = 0 + rename(filepath.Join(fs.path, "LOG"), filepath.Join(fs.path, "LOG.old")) + } + if fs.logw == nil { + var err error + fs.logw, err = os.OpenFile(filepath.Join(fs.path, "LOG"), os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return + } + // Force printDay on new log file. + fs.day = 0 + } + fs.printDay(t) + hour, min, sec := t.Clock() + msec := t.Nanosecond() / 1e3 + // time + fs.buf = itoa(fs.buf[:0], hour, 2) + fs.buf = append(fs.buf, ':') + fs.buf = itoa(fs.buf, min, 2) + fs.buf = append(fs.buf, ':') + fs.buf = itoa(fs.buf, sec, 2) + fs.buf = append(fs.buf, '.') + fs.buf = itoa(fs.buf, msec, 6) + fs.buf = append(fs.buf, ' ') + // write + fs.buf = append(fs.buf, []byte(str)...) + fs.buf = append(fs.buf, '\n') + fs.logw.Write(fs.buf) +} + +func (fs *fileStorage) Log(str string) { + if !fs.readOnly { + t := time.Now() + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return + } + fs.doLog(t, str) + } +} + +func (fs *fileStorage) log(str string) { + if !fs.readOnly { + fs.doLog(time.Now(), str) + } +} + +func (fs *fileStorage) SetMeta(fd FileDesc) (err error) { + if !FileDescOk(fd) { + return ErrInvalidFile + } + if fs.readOnly { + return errReadOnly + } + + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return ErrClosed + } + defer func() { + if err != nil { + fs.log(fmt.Sprintf("CURRENT: %v", err)) + } + }() + path := fmt.Sprintf("%s.%d", filepath.Join(fs.path, "CURRENT"), fd.Num) + w, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return + } + _, err = fmt.Fprintln(w, fsGenName(fd)) + // Close the file first. + if cerr := w.Close(); cerr != nil { + fs.log(fmt.Sprintf("close CURRENT.%d: %v", fd.Num, cerr)) + } + if err != nil { + return + } + return rename(path, filepath.Join(fs.path, "CURRENT")) +} + +func (fs *fileStorage) GetMeta() (fd FileDesc, err error) { + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return FileDesc{}, ErrClosed + } + dir, err := os.Open(fs.path) + if err != nil { + return + } + names, err := dir.Readdirnames(0) + // Close the dir first before checking for Readdirnames error. + if ce := dir.Close(); ce != nil { + fs.log(fmt.Sprintf("close dir: %v", ce)) + } + if err != nil { + return + } + // Find latest CURRENT file. + var rem []string + var pend bool + var cerr error + for _, name := range names { + if strings.HasPrefix(name, "CURRENT") { + pend1 := len(name) > 7 + var pendNum int64 + // Make sure it is valid name for a CURRENT file, otherwise skip it. + if pend1 { + if name[7] != '.' || len(name) < 9 { + fs.log(fmt.Sprintf("skipping %s: invalid file name", name)) + continue + } + var e1 error + if pendNum, e1 = strconv.ParseInt(name[8:], 10, 0); e1 != nil { + fs.log(fmt.Sprintf("skipping %s: invalid file num: %v", name, e1)) + continue + } + } + path := filepath.Join(fs.path, name) + r, e1 := os.OpenFile(path, os.O_RDONLY, 0) + if e1 != nil { + return FileDesc{}, e1 + } + b, e1 := ioutil.ReadAll(r) + if e1 != nil { + r.Close() + return FileDesc{}, e1 + } + var fd1 FileDesc + if len(b) < 1 || b[len(b)-1] != '\n' || !fsParseNamePtr(string(b[:len(b)-1]), &fd1) { + fs.log(fmt.Sprintf("skipping %s: corrupted or incomplete", name)) + if pend1 { + rem = append(rem, name) + } + if !pend1 || cerr == nil { + metaFd, _ := fsParseName(name) + cerr = &ErrCorrupted{ + Fd: metaFd, + Err: errors.New("leveldb/storage: corrupted or incomplete meta file"), + } + } + } else if pend1 && pendNum != fd1.Num { + fs.log(fmt.Sprintf("skipping %s: inconsistent pending-file num: %d vs %d", name, pendNum, fd1.Num)) + rem = append(rem, name) + } else if fd1.Num < fd.Num { + fs.log(fmt.Sprintf("skipping %s: obsolete", name)) + if pend1 { + rem = append(rem, name) + } + } else { + fd = fd1 + pend = pend1 + } + if err := r.Close(); err != nil { + fs.log(fmt.Sprintf("close %s: %v", name, err)) + } + } + } + // Don't remove any files if there is no valid CURRENT file. + if fd.Zero() { + if cerr != nil { + err = cerr + } else { + err = os.ErrNotExist + } + return + } + if !fs.readOnly { + // Rename pending CURRENT file to an effective CURRENT. + if pend { + path := fmt.Sprintf("%s.%d", filepath.Join(fs.path, "CURRENT"), fd.Num) + if err := rename(path, filepath.Join(fs.path, "CURRENT")); err != nil { + fs.log(fmt.Sprintf("CURRENT.%d -> CURRENT: %v", fd.Num, err)) + } + } + // Remove obsolete or incomplete pending CURRENT files. + for _, name := range rem { + path := filepath.Join(fs.path, name) + if err := os.Remove(path); err != nil { + fs.log(fmt.Sprintf("remove %s: %v", name, err)) + } + } + } + return +} + +func (fs *fileStorage) List(ft FileType) (fds []FileDesc, err error) { + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return nil, ErrClosed + } + dir, err := os.Open(fs.path) + if err != nil { + return + } + names, err := dir.Readdirnames(0) + // Close the dir first before checking for Readdirnames error. + if cerr := dir.Close(); cerr != nil { + fs.log(fmt.Sprintf("close dir: %v", cerr)) + } + if err == nil { + for _, name := range names { + if fd, ok := fsParseName(name); ok && fd.Type&ft != 0 { + fds = append(fds, fd) + } + } + } + return +} + +func (fs *fileStorage) Open(fd FileDesc) (Reader, error) { + if !FileDescOk(fd) { + return nil, ErrInvalidFile + } + + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return nil, ErrClosed + } + of, err := os.OpenFile(filepath.Join(fs.path, fsGenName(fd)), os.O_RDONLY, 0) + if err != nil { + if fsHasOldName(fd) && os.IsNotExist(err) { + of, err = os.OpenFile(filepath.Join(fs.path, fsGenOldName(fd)), os.O_RDONLY, 0) + if err == nil { + goto ok + } + } + return nil, err + } +ok: + fs.open++ + return &fileWrap{File: of, fs: fs, fd: fd}, nil +} + +func (fs *fileStorage) Create(fd FileDesc) (Writer, error) { + if !FileDescOk(fd) { + return nil, ErrInvalidFile + } + if fs.readOnly { + return nil, errReadOnly + } + + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return nil, ErrClosed + } + of, err := os.OpenFile(filepath.Join(fs.path, fsGenName(fd)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return nil, err + } + fs.open++ + return &fileWrap{File: of, fs: fs, fd: fd}, nil +} + +func (fs *fileStorage) Remove(fd FileDesc) error { + if !FileDescOk(fd) { + return ErrInvalidFile + } + if fs.readOnly { + return errReadOnly + } + + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return ErrClosed + } + err := os.Remove(filepath.Join(fs.path, fsGenName(fd))) + if err != nil { + if fsHasOldName(fd) && os.IsNotExist(err) { + if e1 := os.Remove(filepath.Join(fs.path, fsGenOldName(fd))); !os.IsNotExist(e1) { + fs.log(fmt.Sprintf("remove %s: %v (old name)", fd, err)) + err = e1 + } + } else { + fs.log(fmt.Sprintf("remove %s: %v", fd, err)) + } + } + return err +} + +func (fs *fileStorage) Rename(oldfd, newfd FileDesc) error { + if !FileDescOk(oldfd) || !FileDescOk(newfd) { + return ErrInvalidFile + } + if oldfd == newfd { + return nil + } + if fs.readOnly { + return errReadOnly + } + + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return ErrClosed + } + return rename(filepath.Join(fs.path, fsGenName(oldfd)), filepath.Join(fs.path, fsGenName(newfd))) +} + +func (fs *fileStorage) Close() error { + fs.mu.Lock() + defer fs.mu.Unlock() + if fs.open < 0 { + return ErrClosed + } + // Clear the finalizer. + runtime.SetFinalizer(fs, nil) + + if fs.open > 0 { + fs.log(fmt.Sprintf("close: warning, %d files still open", fs.open)) + } + fs.open = -1 + if fs.logw != nil { + fs.logw.Close() + } + return fs.flock.release() +} + +type fileWrap struct { + *os.File + fs *fileStorage + fd FileDesc + closed bool +} + +func (fw *fileWrap) Sync() error { + if err := fw.File.Sync(); err != nil { + return err + } + if fw.fd.Type == TypeManifest { + // Also sync parent directory if file type is manifest. + // See: https://code.google.com/p/leveldb/issues/detail?id=190. + if err := syncDir(fw.fs.path); err != nil { + fw.fs.log(fmt.Sprintf("syncDir: %v", err)) + return err + } + } + return nil +} + +func (fw *fileWrap) Close() error { + fw.fs.mu.Lock() + defer fw.fs.mu.Unlock() + if fw.closed { + return ErrClosed + } + fw.closed = true + fw.fs.open-- + err := fw.File.Close() + if err != nil { + fw.fs.log(fmt.Sprintf("close %s: %v", fw.fd, err)) + } + return err +} + +func fsGenName(fd FileDesc) string { + switch fd.Type { + case TypeManifest: + return fmt.Sprintf("MANIFEST-%06d", fd.Num) + case TypeJournal: + return fmt.Sprintf("%06d.log", fd.Num) + case TypeTable: + return fmt.Sprintf("%06d.ldb", fd.Num) + case TypeTemp: + return fmt.Sprintf("%06d.tmp", fd.Num) + default: + panic("invalid file type") + } +} + +func fsHasOldName(fd FileDesc) bool { + return fd.Type == TypeTable +} + +func fsGenOldName(fd FileDesc) string { + switch fd.Type { + case TypeTable: + return fmt.Sprintf("%06d.sst", fd.Num) + } + return fsGenName(fd) +} + +func fsParseName(name string) (fd FileDesc, ok bool) { + var tail string + _, err := fmt.Sscanf(name, "%d.%s", &fd.Num, &tail) + if err == nil { + switch tail { + case "log": + fd.Type = TypeJournal + case "ldb", "sst": + fd.Type = TypeTable + case "tmp": + fd.Type = TypeTemp + default: + return + } + return fd, true + } + n, _ := fmt.Sscanf(name, "MANIFEST-%d%s", &fd.Num, &tail) + if n == 1 { + fd.Type = TypeManifest + return fd, true + } + return +} + +func fsParseNamePtr(name string, fd *FileDesc) bool { + _fd, ok := fsParseName(name) + if fd != nil { + *fd = _fd + } + return ok +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_nacl.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_nacl.go new file mode 100644 index 0000000..5545aee --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_nacl.go @@ -0,0 +1,34 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +build nacl + +package storage + +import ( + "os" + "syscall" +) + +func newFileLock(path string, readOnly bool) (fl fileLock, err error) { + return nil, syscall.ENOTSUP +} + +func setFileLock(f *os.File, readOnly, lock bool) error { + return syscall.ENOTSUP +} + +func rename(oldpath, newpath string) error { + return syscall.ENOTSUP +} + +func isErrInvalid(err error) bool { + return false +} + +func syncDir(name string) error { + return syscall.ENOTSUP +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_plan9.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_plan9.go new file mode 100644 index 0000000..bab62bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_plan9.go @@ -0,0 +1,65 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package storage + +import ( + "os" + "path/filepath" +) + +type plan9FileLock struct { + f *os.File +} + +func (fl *plan9FileLock) release() error { + return fl.f.Close() +} + +func newFileLock(path string, readOnly bool) (fl fileLock, err error) { + var ( + flag int + perm os.FileMode + ) + if readOnly { + flag = os.O_RDONLY + } else { + flag = os.O_RDWR + perm = os.ModeExclusive + } + f, err := os.OpenFile(path, flag, perm) + if os.IsNotExist(err) { + f, err = os.OpenFile(path, flag|os.O_CREATE, perm|0644) + } + if err != nil { + return + } + fl = &plan9FileLock{f: f} + return +} + +func rename(oldpath, newpath string) error { + if _, err := os.Stat(newpath); err == nil { + if err := os.Remove(newpath); err != nil { + return err + } + } + + _, fname := filepath.Split(newpath) + return os.Rename(oldpath, fname) +} + +func syncDir(name string) error { + f, err := os.Open(name) + if err != nil { + return err + } + defer f.Close() + if err := f.Sync(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_solaris.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_solaris.go new file mode 100644 index 0000000..79901ee --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_solaris.go @@ -0,0 +1,81 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +build solaris + +package storage + +import ( + "os" + "syscall" +) + +type unixFileLock struct { + f *os.File +} + +func (fl *unixFileLock) release() error { + if err := setFileLock(fl.f, false, false); err != nil { + return err + } + return fl.f.Close() +} + +func newFileLock(path string, readOnly bool) (fl fileLock, err error) { + var flag int + if readOnly { + flag = os.O_RDONLY + } else { + flag = os.O_RDWR + } + f, err := os.OpenFile(path, flag, 0) + if os.IsNotExist(err) { + f, err = os.OpenFile(path, flag|os.O_CREATE, 0644) + } + if err != nil { + return + } + err = setFileLock(f, readOnly, true) + if err != nil { + f.Close() + return + } + fl = &unixFileLock{f: f} + return +} + +func setFileLock(f *os.File, readOnly, lock bool) error { + flock := syscall.Flock_t{ + Type: syscall.F_UNLCK, + Start: 0, + Len: 0, + Whence: 1, + } + if lock { + if readOnly { + flock.Type = syscall.F_RDLCK + } else { + flock.Type = syscall.F_WRLCK + } + } + return syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &flock) +} + +func rename(oldpath, newpath string) error { + return os.Rename(oldpath, newpath) +} + +func syncDir(name string) error { + f, err := os.Open(name) + if err != nil { + return err + } + defer f.Close() + if err := f.Sync(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_unix.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_unix.go new file mode 100644 index 0000000..7e29915 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_unix.go @@ -0,0 +1,86 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd + +package storage + +import ( + "os" + "syscall" +) + +type unixFileLock struct { + f *os.File +} + +func (fl *unixFileLock) release() error { + if err := setFileLock(fl.f, false, false); err != nil { + return err + } + return fl.f.Close() +} + +func newFileLock(path string, readOnly bool) (fl fileLock, err error) { + var flag int + if readOnly { + flag = os.O_RDONLY + } else { + flag = os.O_RDWR + } + f, err := os.OpenFile(path, flag, 0) + if os.IsNotExist(err) { + f, err = os.OpenFile(path, flag|os.O_CREATE, 0644) + } + if err != nil { + return + } + err = setFileLock(f, readOnly, true) + if err != nil { + f.Close() + return + } + fl = &unixFileLock{f: f} + return +} + +func setFileLock(f *os.File, readOnly, lock bool) error { + how := syscall.LOCK_UN + if lock { + if readOnly { + how = syscall.LOCK_SH + } else { + how = syscall.LOCK_EX + } + } + return syscall.Flock(int(f.Fd()), how|syscall.LOCK_NB) +} + +func rename(oldpath, newpath string) error { + return os.Rename(oldpath, newpath) +} + +func isErrInvalid(err error) bool { + if err == os.ErrInvalid { + return true + } + if syserr, ok := err.(*os.SyscallError); ok && syserr.Err == syscall.EINVAL { + return true + } + return false +} + +func syncDir(name string) error { + f, err := os.Open(name) + if err != nil { + return err + } + defer f.Close() + if err := f.Sync(); err != nil && !isErrInvalid(err) { + return err + } + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_windows.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_windows.go new file mode 100644 index 0000000..899335f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/file_storage_windows.go @@ -0,0 +1,78 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package storage + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procMoveFileExW = modkernel32.NewProc("MoveFileExW") +) + +const ( + _MOVEFILE_REPLACE_EXISTING = 1 +) + +type windowsFileLock struct { + fd syscall.Handle +} + +func (fl *windowsFileLock) release() error { + return syscall.Close(fl.fd) +} + +func newFileLock(path string, readOnly bool) (fl fileLock, err error) { + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return + } + var access, shareMode uint32 + if readOnly { + access = syscall.GENERIC_READ + shareMode = syscall.FILE_SHARE_READ + } else { + access = syscall.GENERIC_READ | syscall.GENERIC_WRITE + } + fd, err := syscall.CreateFile(pathp, access, shareMode, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0) + if err == syscall.ERROR_FILE_NOT_FOUND { + fd, err = syscall.CreateFile(pathp, access, shareMode, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0) + } + if err != nil { + return + } + fl = &windowsFileLock{fd: fd} + return +} + +func moveFileEx(from *uint16, to *uint16, flags uint32) error { + r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) + if r1 == 0 { + if e1 != 0 { + return error(e1) + } + return syscall.EINVAL + } + return nil +} + +func rename(oldpath, newpath string) error { + from, err := syscall.UTF16PtrFromString(oldpath) + if err != nil { + return err + } + to, err := syscall.UTF16PtrFromString(newpath) + if err != nil { + return err + } + return moveFileEx(from, to, _MOVEFILE_REPLACE_EXISTING) +} + +func syncDir(name string) error { return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/mem_storage.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/mem_storage.go new file mode 100644 index 0000000..9b0421f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/mem_storage.go @@ -0,0 +1,218 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package storage + +import ( + "bytes" + "os" + "sync" +) + +const typeShift = 3 + +type memStorageLock struct { + ms *memStorage +} + +func (lock *memStorageLock) Unlock() { + ms := lock.ms + ms.mu.Lock() + defer ms.mu.Unlock() + if ms.slock == lock { + ms.slock = nil + } + return +} + +// memStorage is a memory-backed storage. +type memStorage struct { + mu sync.Mutex + slock *memStorageLock + files map[uint64]*memFile + meta FileDesc +} + +// NewMemStorage returns a new memory-backed storage implementation. +func NewMemStorage() Storage { + return &memStorage{ + files: make(map[uint64]*memFile), + } +} + +func (ms *memStorage) Lock() (Locker, error) { + ms.mu.Lock() + defer ms.mu.Unlock() + if ms.slock != nil { + return nil, ErrLocked + } + ms.slock = &memStorageLock{ms: ms} + return ms.slock, nil +} + +func (*memStorage) Log(str string) {} + +func (ms *memStorage) SetMeta(fd FileDesc) error { + if !FileDescOk(fd) { + return ErrInvalidFile + } + + ms.mu.Lock() + ms.meta = fd + ms.mu.Unlock() + return nil +} + +func (ms *memStorage) GetMeta() (FileDesc, error) { + ms.mu.Lock() + defer ms.mu.Unlock() + if ms.meta.Zero() { + return FileDesc{}, os.ErrNotExist + } + return ms.meta, nil +} + +func (ms *memStorage) List(ft FileType) ([]FileDesc, error) { + ms.mu.Lock() + var fds []FileDesc + for x := range ms.files { + fd := unpackFile(x) + if fd.Type&ft != 0 { + fds = append(fds, fd) + } + } + ms.mu.Unlock() + return fds, nil +} + +func (ms *memStorage) Open(fd FileDesc) (Reader, error) { + if !FileDescOk(fd) { + return nil, ErrInvalidFile + } + + ms.mu.Lock() + defer ms.mu.Unlock() + if m, exist := ms.files[packFile(fd)]; exist { + if m.open { + return nil, errFileOpen + } + m.open = true + return &memReader{Reader: bytes.NewReader(m.Bytes()), ms: ms, m: m}, nil + } + return nil, os.ErrNotExist +} + +func (ms *memStorage) Create(fd FileDesc) (Writer, error) { + if !FileDescOk(fd) { + return nil, ErrInvalidFile + } + + x := packFile(fd) + ms.mu.Lock() + defer ms.mu.Unlock() + m, exist := ms.files[x] + if exist { + if m.open { + return nil, errFileOpen + } + m.Reset() + } else { + m = &memFile{} + ms.files[x] = m + } + m.open = true + return &memWriter{memFile: m, ms: ms}, nil +} + +func (ms *memStorage) Remove(fd FileDesc) error { + if !FileDescOk(fd) { + return ErrInvalidFile + } + + x := packFile(fd) + ms.mu.Lock() + defer ms.mu.Unlock() + if _, exist := ms.files[x]; exist { + delete(ms.files, x) + return nil + } + return os.ErrNotExist +} + +func (ms *memStorage) Rename(oldfd, newfd FileDesc) error { + if FileDescOk(oldfd) || FileDescOk(newfd) { + return ErrInvalidFile + } + if oldfd == newfd { + return nil + } + + oldx := packFile(oldfd) + newx := packFile(newfd) + ms.mu.Lock() + defer ms.mu.Unlock() + oldm, exist := ms.files[oldx] + if !exist { + return os.ErrNotExist + } + newm, exist := ms.files[newx] + if (exist && newm.open) || oldm.open { + return errFileOpen + } + delete(ms.files, oldx) + ms.files[newx] = oldm + return nil +} + +func (*memStorage) Close() error { return nil } + +type memFile struct { + bytes.Buffer + open bool +} + +type memReader struct { + *bytes.Reader + ms *memStorage + m *memFile + closed bool +} + +func (mr *memReader) Close() error { + mr.ms.mu.Lock() + defer mr.ms.mu.Unlock() + if mr.closed { + return ErrClosed + } + mr.m.open = false + return nil +} + +type memWriter struct { + *memFile + ms *memStorage + closed bool +} + +func (*memWriter) Sync() error { return nil } + +func (mw *memWriter) Close() error { + mw.ms.mu.Lock() + defer mw.ms.mu.Unlock() + if mw.closed { + return ErrClosed + } + mw.memFile.open = false + return nil +} + +func packFile(fd FileDesc) uint64 { + return uint64(fd.Num)<> typeShift)} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/storage.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/storage.go new file mode 100644 index 0000000..c16bce6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/storage/storage.go @@ -0,0 +1,179 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package storage provides storage abstraction for LevelDB. +package storage + +import ( + "errors" + "fmt" + "io" +) + +// FileType represent a file type. +type FileType int + +// File types. +const ( + TypeManifest FileType = 1 << iota + TypeJournal + TypeTable + TypeTemp + + TypeAll = TypeManifest | TypeJournal | TypeTable | TypeTemp +) + +func (t FileType) String() string { + switch t { + case TypeManifest: + return "manifest" + case TypeJournal: + return "journal" + case TypeTable: + return "table" + case TypeTemp: + return "temp" + } + return fmt.Sprintf("", t) +} + +// Common error. +var ( + ErrInvalidFile = errors.New("leveldb/storage: invalid file for argument") + ErrLocked = errors.New("leveldb/storage: already locked") + ErrClosed = errors.New("leveldb/storage: closed") +) + +// ErrCorrupted is the type that wraps errors that indicate corruption of +// a file. Package storage has its own type instead of using +// errors.ErrCorrupted to prevent circular import. +type ErrCorrupted struct { + Fd FileDesc + Err error +} + +func (e *ErrCorrupted) Error() string { + if !e.Fd.Zero() { + return fmt.Sprintf("%v [file=%v]", e.Err, e.Fd) + } + return e.Err.Error() +} + +// Syncer is the interface that wraps basic Sync method. +type Syncer interface { + // Sync commits the current contents of the file to stable storage. + Sync() error +} + +// Reader is the interface that groups the basic Read, Seek, ReadAt and Close +// methods. +type Reader interface { + io.ReadSeeker + io.ReaderAt + io.Closer +} + +// Writer is the interface that groups the basic Write, Sync and Close +// methods. +type Writer interface { + io.WriteCloser + Syncer +} + +// Locker is the interface that wraps Unlock method. +type Locker interface { + Unlock() +} + +// FileDesc is a 'file descriptor'. +type FileDesc struct { + Type FileType + Num int64 +} + +func (fd FileDesc) String() string { + switch fd.Type { + case TypeManifest: + return fmt.Sprintf("MANIFEST-%06d", fd.Num) + case TypeJournal: + return fmt.Sprintf("%06d.log", fd.Num) + case TypeTable: + return fmt.Sprintf("%06d.ldb", fd.Num) + case TypeTemp: + return fmt.Sprintf("%06d.tmp", fd.Num) + default: + return fmt.Sprintf("%#x-%d", fd.Type, fd.Num) + } +} + +// Zero returns true if fd == (FileDesc{}). +func (fd FileDesc) Zero() bool { + return fd == (FileDesc{}) +} + +// FileDescOk returns true if fd is a valid 'file descriptor'. +func FileDescOk(fd FileDesc) bool { + switch fd.Type { + case TypeManifest: + case TypeJournal: + case TypeTable: + case TypeTemp: + default: + return false + } + return fd.Num >= 0 +} + +// Storage is the storage. A storage instance must be safe for concurrent use. +type Storage interface { + // Lock locks the storage. Any subsequent attempt to call Lock will fail + // until the last lock released. + // Caller should call Unlock method after use. + Lock() (Locker, error) + + // Log logs a string. This is used for logging. + // An implementation may write to a file, stdout or simply do nothing. + Log(str string) + + // SetMeta store 'file descriptor' that can later be acquired using GetMeta + // method. The 'file descriptor' should point to a valid file. + // SetMeta should be implemented in such way that changes should happen + // atomically. + SetMeta(fd FileDesc) error + + // GetMeta returns 'file descriptor' stored in meta. The 'file descriptor' + // can be updated using SetMeta method. + // Returns os.ErrNotExist if meta doesn't store any 'file descriptor', or + // 'file descriptor' point to nonexistent file. + GetMeta() (FileDesc, error) + + // List returns file descriptors that match the given file types. + // The file types may be OR'ed together. + List(ft FileType) ([]FileDesc, error) + + // Open opens file with the given 'file descriptor' read-only. + // Returns os.ErrNotExist error if the file does not exist. + // Returns ErrClosed if the underlying storage is closed. + Open(fd FileDesc) (Reader, error) + + // Create creates file with the given 'file descriptor', truncate if already + // exist and opens write-only. + // Returns ErrClosed if the underlying storage is closed. + Create(fd FileDesc) (Writer, error) + + // Remove removes file with the given 'file descriptor'. + // Returns ErrClosed if the underlying storage is closed. + Remove(fd FileDesc) error + + // Rename renames file from oldfd to newfd. + // Returns ErrClosed if the underlying storage is closed. + Rename(oldfd, newfd FileDesc) error + + // Close closes the storage. + // It is valid to call Close multiple times. Other methods should not be + // called after the storage has been closed. + Close() error +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table.go new file mode 100644 index 0000000..81d18a5 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table.go @@ -0,0 +1,529 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "fmt" + "sort" + "sync/atomic" + + "github.com/syndtr/goleveldb/leveldb/cache" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/table" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// tFile holds basic information about a table. +type tFile struct { + fd storage.FileDesc + seekLeft int32 + size int64 + imin, imax internalKey +} + +// Returns true if given key is after largest key of this table. +func (t *tFile) after(icmp *iComparer, ukey []byte) bool { + return ukey != nil && icmp.uCompare(ukey, t.imax.ukey()) > 0 +} + +// Returns true if given key is before smallest key of this table. +func (t *tFile) before(icmp *iComparer, ukey []byte) bool { + return ukey != nil && icmp.uCompare(ukey, t.imin.ukey()) < 0 +} + +// Returns true if given key range overlaps with this table key range. +func (t *tFile) overlaps(icmp *iComparer, umin, umax []byte) bool { + return !t.after(icmp, umin) && !t.before(icmp, umax) +} + +// Cosumes one seek and return current seeks left. +func (t *tFile) consumeSeek() int32 { + return atomic.AddInt32(&t.seekLeft, -1) +} + +// Creates new tFile. +func newTableFile(fd storage.FileDesc, size int64, imin, imax internalKey) *tFile { + f := &tFile{ + fd: fd, + size: size, + imin: imin, + imax: imax, + } + + // We arrange to automatically compact this file after + // a certain number of seeks. Let's assume: + // (1) One seek costs 10ms + // (2) Writing or reading 1MB costs 10ms (100MB/s) + // (3) A compaction of 1MB does 25MB of IO: + // 1MB read from this level + // 10-12MB read from next level (boundaries may be misaligned) + // 10-12MB written to next level + // This implies that 25 seeks cost the same as the compaction + // of 1MB of data. I.e., one seek costs approximately the + // same as the compaction of 40KB of data. We are a little + // conservative and allow approximately one seek for every 16KB + // of data before triggering a compaction. + f.seekLeft = int32(size / 16384) + if f.seekLeft < 100 { + f.seekLeft = 100 + } + + return f +} + +func tableFileFromRecord(r atRecord) *tFile { + return newTableFile(storage.FileDesc{storage.TypeTable, r.num}, r.size, r.imin, r.imax) +} + +// tFiles hold multiple tFile. +type tFiles []*tFile + +func (tf tFiles) Len() int { return len(tf) } +func (tf tFiles) Swap(i, j int) { tf[i], tf[j] = tf[j], tf[i] } + +func (tf tFiles) nums() string { + x := "[ " + for i, f := range tf { + if i != 0 { + x += ", " + } + x += fmt.Sprint(f.fd.Num) + } + x += " ]" + return x +} + +// Returns true if i smallest key is less than j. +// This used for sort by key in ascending order. +func (tf tFiles) lessByKey(icmp *iComparer, i, j int) bool { + a, b := tf[i], tf[j] + n := icmp.Compare(a.imin, b.imin) + if n == 0 { + return a.fd.Num < b.fd.Num + } + return n < 0 +} + +// Returns true if i file number is greater than j. +// This used for sort by file number in descending order. +func (tf tFiles) lessByNum(i, j int) bool { + return tf[i].fd.Num > tf[j].fd.Num +} + +// Sorts tables by key in ascending order. +func (tf tFiles) sortByKey(icmp *iComparer) { + sort.Sort(&tFilesSortByKey{tFiles: tf, icmp: icmp}) +} + +// Sorts tables by file number in descending order. +func (tf tFiles) sortByNum() { + sort.Sort(&tFilesSortByNum{tFiles: tf}) +} + +// Returns sum of all tables size. +func (tf tFiles) size() (sum int64) { + for _, t := range tf { + sum += t.size + } + return sum +} + +// Searches smallest index of tables whose its smallest +// key is after or equal with given key. +func (tf tFiles) searchMin(icmp *iComparer, ikey internalKey) int { + return sort.Search(len(tf), func(i int) bool { + return icmp.Compare(tf[i].imin, ikey) >= 0 + }) +} + +// Searches smallest index of tables whose its largest +// key is after or equal with given key. +func (tf tFiles) searchMax(icmp *iComparer, ikey internalKey) int { + return sort.Search(len(tf), func(i int) bool { + return icmp.Compare(tf[i].imax, ikey) >= 0 + }) +} + +// Returns true if given key range overlaps with one or more +// tables key range. If unsorted is true then binary search will not be used. +func (tf tFiles) overlaps(icmp *iComparer, umin, umax []byte, unsorted bool) bool { + if unsorted { + // Check against all files. + for _, t := range tf { + if t.overlaps(icmp, umin, umax) { + return true + } + } + return false + } + + i := 0 + if len(umin) > 0 { + // Find the earliest possible internal key for min. + i = tf.searchMax(icmp, makeInternalKey(nil, umin, keyMaxSeq, keyTypeSeek)) + } + if i >= len(tf) { + // Beginning of range is after all files, so no overlap. + return false + } + return !tf[i].before(icmp, umax) +} + +// Returns tables whose its key range overlaps with given key range. +// Range will be expanded if ukey found hop across tables. +// If overlapped is true then the search will be restarted if umax +// expanded. +// The dst content will be overwritten. +func (tf tFiles) getOverlaps(dst tFiles, icmp *iComparer, umin, umax []byte, overlapped bool) tFiles { + dst = dst[:0] + for i := 0; i < len(tf); { + t := tf[i] + if t.overlaps(icmp, umin, umax) { + if umin != nil && icmp.uCompare(t.imin.ukey(), umin) < 0 { + umin = t.imin.ukey() + dst = dst[:0] + i = 0 + continue + } else if umax != nil && icmp.uCompare(t.imax.ukey(), umax) > 0 { + umax = t.imax.ukey() + // Restart search if it is overlapped. + if overlapped { + dst = dst[:0] + i = 0 + continue + } + } + + dst = append(dst, t) + } + i++ + } + + return dst +} + +// Returns tables key range. +func (tf tFiles) getRange(icmp *iComparer) (imin, imax internalKey) { + for i, t := range tf { + if i == 0 { + imin, imax = t.imin, t.imax + continue + } + if icmp.Compare(t.imin, imin) < 0 { + imin = t.imin + } + if icmp.Compare(t.imax, imax) > 0 { + imax = t.imax + } + } + + return +} + +// Creates iterator index from tables. +func (tf tFiles) newIndexIterator(tops *tOps, icmp *iComparer, slice *util.Range, ro *opt.ReadOptions) iterator.IteratorIndexer { + if slice != nil { + var start, limit int + if slice.Start != nil { + start = tf.searchMax(icmp, internalKey(slice.Start)) + } + if slice.Limit != nil { + limit = tf.searchMin(icmp, internalKey(slice.Limit)) + } else { + limit = tf.Len() + } + tf = tf[start:limit] + } + return iterator.NewArrayIndexer(&tFilesArrayIndexer{ + tFiles: tf, + tops: tops, + icmp: icmp, + slice: slice, + ro: ro, + }) +} + +// Tables iterator index. +type tFilesArrayIndexer struct { + tFiles + tops *tOps + icmp *iComparer + slice *util.Range + ro *opt.ReadOptions +} + +func (a *tFilesArrayIndexer) Search(key []byte) int { + return a.searchMax(a.icmp, internalKey(key)) +} + +func (a *tFilesArrayIndexer) Get(i int) iterator.Iterator { + if i == 0 || i == a.Len()-1 { + return a.tops.newIterator(a.tFiles[i], a.slice, a.ro) + } + return a.tops.newIterator(a.tFiles[i], nil, a.ro) +} + +// Helper type for sortByKey. +type tFilesSortByKey struct { + tFiles + icmp *iComparer +} + +func (x *tFilesSortByKey) Less(i, j int) bool { + return x.lessByKey(x.icmp, i, j) +} + +// Helper type for sortByNum. +type tFilesSortByNum struct { + tFiles +} + +func (x *tFilesSortByNum) Less(i, j int) bool { + return x.lessByNum(i, j) +} + +// Table operations. +type tOps struct { + s *session + noSync bool + cache *cache.Cache + bcache *cache.Cache + bpool *util.BufferPool +} + +// Creates an empty table and returns table writer. +func (t *tOps) create() (*tWriter, error) { + fd := storage.FileDesc{storage.TypeTable, t.s.allocFileNum()} + fw, err := t.s.stor.Create(fd) + if err != nil { + return nil, err + } + return &tWriter{ + t: t, + fd: fd, + w: fw, + tw: table.NewWriter(fw, t.s.o.Options), + }, nil +} + +// Builds table from src iterator. +func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) { + w, err := t.create() + if err != nil { + return + } + + defer func() { + if err != nil { + w.drop() + } + }() + + for src.Next() { + err = w.append(src.Key(), src.Value()) + if err != nil { + return + } + } + err = src.Error() + if err != nil { + return + } + + n = w.tw.EntriesLen() + f, err = w.finish() + return +} + +// Opens table. It returns a cache handle, which should +// be released after use. +func (t *tOps) open(f *tFile) (ch *cache.Handle, err error) { + ch = t.cache.Get(0, uint64(f.fd.Num), func() (size int, value cache.Value) { + var r storage.Reader + r, err = t.s.stor.Open(f.fd) + if err != nil { + return 0, nil + } + + var bcache *cache.NamespaceGetter + if t.bcache != nil { + bcache = &cache.NamespaceGetter{Cache: t.bcache, NS: uint64(f.fd.Num)} + } + + var tr *table.Reader + tr, err = table.NewReader(r, f.size, f.fd, bcache, t.bpool, t.s.o.Options) + if err != nil { + r.Close() + return 0, nil + } + return 1, tr + + }) + if ch == nil && err == nil { + err = ErrClosed + } + return +} + +// Finds key/value pair whose key is greater than or equal to the +// given key. +func (t *tOps) find(f *tFile, key []byte, ro *opt.ReadOptions) (rkey, rvalue []byte, err error) { + ch, err := t.open(f) + if err != nil { + return nil, nil, err + } + defer ch.Release() + return ch.Value().(*table.Reader).Find(key, true, ro) +} + +// Finds key that is greater than or equal to the given key. +func (t *tOps) findKey(f *tFile, key []byte, ro *opt.ReadOptions) (rkey []byte, err error) { + ch, err := t.open(f) + if err != nil { + return nil, err + } + defer ch.Release() + return ch.Value().(*table.Reader).FindKey(key, true, ro) +} + +// Returns approximate offset of the given key. +func (t *tOps) offsetOf(f *tFile, key []byte) (offset int64, err error) { + ch, err := t.open(f) + if err != nil { + return + } + defer ch.Release() + return ch.Value().(*table.Reader).OffsetOf(key) +} + +// Creates an iterator from the given table. +func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + ch, err := t.open(f) + if err != nil { + return iterator.NewEmptyIterator(err) + } + iter := ch.Value().(*table.Reader).NewIterator(slice, ro) + iter.SetReleaser(ch) + return iter +} + +// Removes table from persistent storage. It waits until +// no one use the the table. +func (t *tOps) remove(f *tFile) { + t.cache.Delete(0, uint64(f.fd.Num), func() { + if err := t.s.stor.Remove(f.fd); err != nil { + t.s.logf("table@remove removing @%d %q", f.fd.Num, err) + } else { + t.s.logf("table@remove removed @%d", f.fd.Num) + } + if t.bcache != nil { + t.bcache.EvictNS(uint64(f.fd.Num)) + } + }) +} + +// Closes the table ops instance. It will close all tables, +// regadless still used or not. +func (t *tOps) close() { + t.bpool.Close() + t.cache.Close() + if t.bcache != nil { + t.bcache.CloseWeak() + } +} + +// Creates new initialized table ops instance. +func newTableOps(s *session) *tOps { + var ( + cacher cache.Cacher + bcache *cache.Cache + bpool *util.BufferPool + ) + if s.o.GetOpenFilesCacheCapacity() > 0 { + cacher = cache.NewLRU(s.o.GetOpenFilesCacheCapacity()) + } + if !s.o.GetDisableBlockCache() { + var bcacher cache.Cacher + if s.o.GetBlockCacheCapacity() > 0 { + bcacher = cache.NewLRU(s.o.GetBlockCacheCapacity()) + } + bcache = cache.NewCache(bcacher) + } + if !s.o.GetDisableBufferPool() { + bpool = util.NewBufferPool(s.o.GetBlockSize() + 5) + } + return &tOps{ + s: s, + noSync: s.o.GetNoSync(), + cache: cache.NewCache(cacher), + bcache: bcache, + bpool: bpool, + } +} + +// tWriter wraps the table writer. It keep track of file descriptor +// and added key range. +type tWriter struct { + t *tOps + + fd storage.FileDesc + w storage.Writer + tw *table.Writer + + first, last []byte +} + +// Append key/value pair to the table. +func (w *tWriter) append(key, value []byte) error { + if w.first == nil { + w.first = append([]byte{}, key...) + } + w.last = append(w.last[:0], key...) + return w.tw.Append(key, value) +} + +// Returns true if the table is empty. +func (w *tWriter) empty() bool { + return w.first == nil +} + +// Closes the storage.Writer. +func (w *tWriter) close() { + if w.w != nil { + w.w.Close() + w.w = nil + } +} + +// Finalizes the table and returns table file. +func (w *tWriter) finish() (f *tFile, err error) { + defer w.close() + err = w.tw.Close() + if err != nil { + return + } + if !w.t.noSync { + err = w.w.Sync() + if err != nil { + return + } + } + f = newTableFile(w.fd, int64(w.tw.BytesLen()), internalKey(w.first), internalKey(w.last)) + return +} + +// Drops the table. +func (w *tWriter) drop() { + w.close() + w.t.s.stor.Remove(w.fd) + w.t.s.reuseFileNum(w.fd.Num) + w.tw = nil + w.first = nil + w.last = nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/reader.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/reader.go new file mode 100644 index 0000000..c5be420 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/reader.go @@ -0,0 +1,1134 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package table + +import ( + "encoding/binary" + "fmt" + "io" + "sort" + "strings" + "sync" + + "github.com/golang/snappy" + + "github.com/syndtr/goleveldb/leveldb/cache" + "github.com/syndtr/goleveldb/leveldb/comparer" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/filter" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// Reader errors. +var ( + ErrNotFound = errors.ErrNotFound + ErrReaderReleased = errors.New("leveldb/table: reader released") + ErrIterReleased = errors.New("leveldb/table: iterator released") +) + +// ErrCorrupted describes error due to corruption. This error will be wrapped +// with errors.ErrCorrupted. +type ErrCorrupted struct { + Pos int64 + Size int64 + Kind string + Reason string +} + +func (e *ErrCorrupted) Error() string { + return fmt.Sprintf("leveldb/table: corruption on %s (pos=%d): %s", e.Kind, e.Pos, e.Reason) +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +type block struct { + bpool *util.BufferPool + bh blockHandle + data []byte + restartsLen int + restartsOffset int +} + +func (b *block) seek(cmp comparer.Comparer, rstart, rlimit int, key []byte) (index, offset int, err error) { + index = sort.Search(b.restartsLen-rstart-(b.restartsLen-rlimit), func(i int) bool { + offset := int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*(rstart+i):])) + offset++ // shared always zero, since this is a restart point + v1, n1 := binary.Uvarint(b.data[offset:]) // key length + _, n2 := binary.Uvarint(b.data[offset+n1:]) // value length + m := offset + n1 + n2 + return cmp.Compare(b.data[m:m+int(v1)], key) > 0 + }) + rstart - 1 + if index < rstart { + // The smallest key is greater-than key sought. + index = rstart + } + offset = int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*index:])) + return +} + +func (b *block) restartIndex(rstart, rlimit, offset int) int { + return sort.Search(b.restartsLen-rstart-(b.restartsLen-rlimit), func(i int) bool { + return int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*(rstart+i):])) > offset + }) + rstart - 1 +} + +func (b *block) restartOffset(index int) int { + return int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*index:])) +} + +func (b *block) entry(offset int) (key, value []byte, nShared, n int, err error) { + if offset >= b.restartsOffset { + if offset != b.restartsOffset { + err = &ErrCorrupted{Reason: "entries offset not aligned"} + } + return + } + v0, n0 := binary.Uvarint(b.data[offset:]) // Shared prefix length + v1, n1 := binary.Uvarint(b.data[offset+n0:]) // Key length + v2, n2 := binary.Uvarint(b.data[offset+n0+n1:]) // Value length + m := n0 + n1 + n2 + n = m + int(v1) + int(v2) + if n0 <= 0 || n1 <= 0 || n2 <= 0 || offset+n > b.restartsOffset { + err = &ErrCorrupted{Reason: "entries corrupted"} + return + } + key = b.data[offset+m : offset+m+int(v1)] + value = b.data[offset+m+int(v1) : offset+n] + nShared = int(v0) + return +} + +func (b *block) Release() { + b.bpool.Put(b.data) + b.bpool = nil + b.data = nil +} + +type dir int + +const ( + dirReleased dir = iota - 1 + dirSOI + dirEOI + dirBackward + dirForward +) + +type blockIter struct { + tr *Reader + block *block + blockReleaser util.Releaser + releaser util.Releaser + key, value []byte + offset int + // Previous offset, only filled by Next. + prevOffset int + prevNode []int + prevKeys []byte + restartIndex int + // Iterator direction. + dir dir + // Restart index slice range. + riStart int + riLimit int + // Offset slice range. + offsetStart int + offsetRealStart int + offsetLimit int + // Error. + err error +} + +func (i *blockIter) sErr(err error) { + i.err = err + i.key = nil + i.value = nil + i.prevNode = nil + i.prevKeys = nil +} + +func (i *blockIter) reset() { + if i.dir == dirBackward { + i.prevNode = i.prevNode[:0] + i.prevKeys = i.prevKeys[:0] + } + i.restartIndex = i.riStart + i.offset = i.offsetStart + i.dir = dirSOI + i.key = i.key[:0] + i.value = nil +} + +func (i *blockIter) isFirst() bool { + switch i.dir { + case dirForward: + return i.prevOffset == i.offsetRealStart + case dirBackward: + return len(i.prevNode) == 1 && i.restartIndex == i.riStart + } + return false +} + +func (i *blockIter) isLast() bool { + switch i.dir { + case dirForward, dirBackward: + return i.offset == i.offsetLimit + } + return false +} + +func (i *blockIter) First() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if i.dir == dirBackward { + i.prevNode = i.prevNode[:0] + i.prevKeys = i.prevKeys[:0] + } + i.dir = dirSOI + return i.Next() +} + +func (i *blockIter) Last() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if i.dir == dirBackward { + i.prevNode = i.prevNode[:0] + i.prevKeys = i.prevKeys[:0] + } + i.dir = dirEOI + return i.Prev() +} + +func (i *blockIter) Seek(key []byte) bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + ri, offset, err := i.block.seek(i.tr.cmp, i.riStart, i.riLimit, key) + if err != nil { + i.sErr(err) + return false + } + i.restartIndex = ri + i.offset = max(i.offsetStart, offset) + if i.dir == dirSOI || i.dir == dirEOI { + i.dir = dirForward + } + for i.Next() { + if i.tr.cmp.Compare(i.key, key) >= 0 { + return true + } + } + return false +} + +func (i *blockIter) Next() bool { + if i.dir == dirEOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + if i.dir == dirSOI { + i.restartIndex = i.riStart + i.offset = i.offsetStart + } else if i.dir == dirBackward { + i.prevNode = i.prevNode[:0] + i.prevKeys = i.prevKeys[:0] + } + for i.offset < i.offsetRealStart { + key, value, nShared, n, err := i.block.entry(i.offset) + if err != nil { + i.sErr(i.tr.fixErrCorruptedBH(i.block.bh, err)) + return false + } + if n == 0 { + i.dir = dirEOI + return false + } + i.key = append(i.key[:nShared], key...) + i.value = value + i.offset += n + } + if i.offset >= i.offsetLimit { + i.dir = dirEOI + if i.offset != i.offsetLimit { + i.sErr(i.tr.newErrCorruptedBH(i.block.bh, "entries offset not aligned")) + } + return false + } + key, value, nShared, n, err := i.block.entry(i.offset) + if err != nil { + i.sErr(i.tr.fixErrCorruptedBH(i.block.bh, err)) + return false + } + if n == 0 { + i.dir = dirEOI + return false + } + i.key = append(i.key[:nShared], key...) + i.value = value + i.prevOffset = i.offset + i.offset += n + i.dir = dirForward + return true +} + +func (i *blockIter) Prev() bool { + if i.dir == dirSOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + var ri int + if i.dir == dirForward { + // Change direction. + i.offset = i.prevOffset + if i.offset == i.offsetRealStart { + i.dir = dirSOI + return false + } + ri = i.block.restartIndex(i.restartIndex, i.riLimit, i.offset) + i.dir = dirBackward + } else if i.dir == dirEOI { + // At the end of iterator. + i.restartIndex = i.riLimit + i.offset = i.offsetLimit + if i.offset == i.offsetRealStart { + i.dir = dirSOI + return false + } + ri = i.riLimit - 1 + i.dir = dirBackward + } else if len(i.prevNode) == 1 { + // This is the end of a restart range. + i.offset = i.prevNode[0] + i.prevNode = i.prevNode[:0] + if i.restartIndex == i.riStart { + i.dir = dirSOI + return false + } + i.restartIndex-- + ri = i.restartIndex + } else { + // In the middle of restart range, get from cache. + n := len(i.prevNode) - 3 + node := i.prevNode[n:] + i.prevNode = i.prevNode[:n] + // Get the key. + ko := node[0] + i.key = append(i.key[:0], i.prevKeys[ko:]...) + i.prevKeys = i.prevKeys[:ko] + // Get the value. + vo := node[1] + vl := vo + node[2] + i.value = i.block.data[vo:vl] + i.offset = vl + return true + } + // Build entries cache. + i.key = i.key[:0] + i.value = nil + offset := i.block.restartOffset(ri) + if offset == i.offset { + ri-- + if ri < 0 { + i.dir = dirSOI + return false + } + offset = i.block.restartOffset(ri) + } + i.prevNode = append(i.prevNode, offset) + for { + key, value, nShared, n, err := i.block.entry(offset) + if err != nil { + i.sErr(i.tr.fixErrCorruptedBH(i.block.bh, err)) + return false + } + if offset >= i.offsetRealStart { + if i.value != nil { + // Appends 3 variables: + // 1. Previous keys offset + // 2. Value offset in the data block + // 3. Value length + i.prevNode = append(i.prevNode, len(i.prevKeys), offset-len(i.value), len(i.value)) + i.prevKeys = append(i.prevKeys, i.key...) + } + i.value = value + } + i.key = append(i.key[:nShared], key...) + offset += n + // Stop if target offset reached. + if offset >= i.offset { + if offset != i.offset { + i.sErr(i.tr.newErrCorruptedBH(i.block.bh, "entries offset not aligned")) + return false + } + + break + } + } + i.restartIndex = ri + i.offset = offset + return true +} + +func (i *blockIter) Key() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.key +} + +func (i *blockIter) Value() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.value +} + +func (i *blockIter) Release() { + if i.dir != dirReleased { + i.tr = nil + i.block = nil + i.prevNode = nil + i.prevKeys = nil + i.key = nil + i.value = nil + i.dir = dirReleased + if i.blockReleaser != nil { + i.blockReleaser.Release() + i.blockReleaser = nil + } + if i.releaser != nil { + i.releaser.Release() + i.releaser = nil + } + } +} + +func (i *blockIter) SetReleaser(releaser util.Releaser) { + if i.dir == dirReleased { + panic(util.ErrReleased) + } + if i.releaser != nil && releaser != nil { + panic(util.ErrHasReleaser) + } + i.releaser = releaser +} + +func (i *blockIter) Valid() bool { + return i.err == nil && (i.dir == dirBackward || i.dir == dirForward) +} + +func (i *blockIter) Error() error { + return i.err +} + +type filterBlock struct { + bpool *util.BufferPool + data []byte + oOffset int + baseLg uint + filtersNum int +} + +func (b *filterBlock) contains(filter filter.Filter, offset uint64, key []byte) bool { + i := int(offset >> b.baseLg) + if i < b.filtersNum { + o := b.data[b.oOffset+i*4:] + n := int(binary.LittleEndian.Uint32(o)) + m := int(binary.LittleEndian.Uint32(o[4:])) + if n < m && m <= b.oOffset { + return filter.Contains(b.data[n:m], key) + } else if n == m { + return false + } + } + return true +} + +func (b *filterBlock) Release() { + b.bpool.Put(b.data) + b.bpool = nil + b.data = nil +} + +type indexIter struct { + *blockIter + tr *Reader + slice *util.Range + // Options + fillCache bool +} + +func (i *indexIter) Get() iterator.Iterator { + value := i.Value() + if value == nil { + return nil + } + dataBH, n := decodeBlockHandle(value) + if n == 0 { + return iterator.NewEmptyIterator(i.tr.newErrCorruptedBH(i.tr.indexBH, "bad data block handle")) + } + + var slice *util.Range + if i.slice != nil && (i.blockIter.isFirst() || i.blockIter.isLast()) { + slice = i.slice + } + return i.tr.getDataIterErr(dataBH, slice, i.tr.verifyChecksum, i.fillCache) +} + +// Reader is a table reader. +type Reader struct { + mu sync.RWMutex + fd storage.FileDesc + reader io.ReaderAt + cache *cache.NamespaceGetter + err error + bpool *util.BufferPool + // Options + o *opt.Options + cmp comparer.Comparer + filter filter.Filter + verifyChecksum bool + + dataEnd int64 + metaBH, indexBH, filterBH blockHandle + indexBlock *block + filterBlock *filterBlock +} + +func (r *Reader) blockKind(bh blockHandle) string { + switch bh.offset { + case r.metaBH.offset: + return "meta-block" + case r.indexBH.offset: + return "index-block" + case r.filterBH.offset: + if r.filterBH.length > 0 { + return "filter-block" + } + } + return "data-block" +} + +func (r *Reader) newErrCorrupted(pos, size int64, kind, reason string) error { + return &errors.ErrCorrupted{Fd: r.fd, Err: &ErrCorrupted{Pos: pos, Size: size, Kind: kind, Reason: reason}} +} + +func (r *Reader) newErrCorruptedBH(bh blockHandle, reason string) error { + return r.newErrCorrupted(int64(bh.offset), int64(bh.length), r.blockKind(bh), reason) +} + +func (r *Reader) fixErrCorruptedBH(bh blockHandle, err error) error { + if cerr, ok := err.(*ErrCorrupted); ok { + cerr.Pos = int64(bh.offset) + cerr.Size = int64(bh.length) + cerr.Kind = r.blockKind(bh) + return &errors.ErrCorrupted{Fd: r.fd, Err: cerr} + } + return err +} + +func (r *Reader) readRawBlock(bh blockHandle, verifyChecksum bool) ([]byte, error) { + data := r.bpool.Get(int(bh.length + blockTrailerLen)) + if _, err := r.reader.ReadAt(data, int64(bh.offset)); err != nil && err != io.EOF { + return nil, err + } + + if verifyChecksum { + n := bh.length + 1 + checksum0 := binary.LittleEndian.Uint32(data[n:]) + checksum1 := util.NewCRC(data[:n]).Value() + if checksum0 != checksum1 { + r.bpool.Put(data) + return nil, r.newErrCorruptedBH(bh, fmt.Sprintf("checksum mismatch, want=%#x got=%#x", checksum0, checksum1)) + } + } + + switch data[bh.length] { + case blockTypeNoCompression: + data = data[:bh.length] + case blockTypeSnappyCompression: + decLen, err := snappy.DecodedLen(data[:bh.length]) + if err != nil { + return nil, r.newErrCorruptedBH(bh, err.Error()) + } + decData := r.bpool.Get(decLen) + decData, err = snappy.Decode(decData, data[:bh.length]) + r.bpool.Put(data) + if err != nil { + r.bpool.Put(decData) + return nil, r.newErrCorruptedBH(bh, err.Error()) + } + data = decData + default: + r.bpool.Put(data) + return nil, r.newErrCorruptedBH(bh, fmt.Sprintf("unknown compression type %#x", data[bh.length])) + } + return data, nil +} + +func (r *Reader) readBlock(bh blockHandle, verifyChecksum bool) (*block, error) { + data, err := r.readRawBlock(bh, verifyChecksum) + if err != nil { + return nil, err + } + restartsLen := int(binary.LittleEndian.Uint32(data[len(data)-4:])) + b := &block{ + bpool: r.bpool, + bh: bh, + data: data, + restartsLen: restartsLen, + restartsOffset: len(data) - (restartsLen+1)*4, + } + return b, nil +} + +func (r *Reader) readBlockCached(bh blockHandle, verifyChecksum, fillCache bool) (*block, util.Releaser, error) { + if r.cache != nil { + var ( + err error + ch *cache.Handle + ) + if fillCache { + ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) { + var b *block + b, err = r.readBlock(bh, verifyChecksum) + if err != nil { + return 0, nil + } + return cap(b.data), b + }) + } else { + ch = r.cache.Get(bh.offset, nil) + } + if ch != nil { + b, ok := ch.Value().(*block) + if !ok { + ch.Release() + return nil, nil, errors.New("leveldb/table: inconsistent block type") + } + return b, ch, err + } else if err != nil { + return nil, nil, err + } + } + + b, err := r.readBlock(bh, verifyChecksum) + return b, b, err +} + +func (r *Reader) readFilterBlock(bh blockHandle) (*filterBlock, error) { + data, err := r.readRawBlock(bh, true) + if err != nil { + return nil, err + } + n := len(data) + if n < 5 { + return nil, r.newErrCorruptedBH(bh, "too short") + } + m := n - 5 + oOffset := int(binary.LittleEndian.Uint32(data[m:])) + if oOffset > m { + return nil, r.newErrCorruptedBH(bh, "invalid data-offsets offset") + } + b := &filterBlock{ + bpool: r.bpool, + data: data, + oOffset: oOffset, + baseLg: uint(data[n-1]), + filtersNum: (m - oOffset) / 4, + } + return b, nil +} + +func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) { + if r.cache != nil { + var ( + err error + ch *cache.Handle + ) + if fillCache { + ch = r.cache.Get(bh.offset, func() (size int, value cache.Value) { + var b *filterBlock + b, err = r.readFilterBlock(bh) + if err != nil { + return 0, nil + } + return cap(b.data), b + }) + } else { + ch = r.cache.Get(bh.offset, nil) + } + if ch != nil { + b, ok := ch.Value().(*filterBlock) + if !ok { + ch.Release() + return nil, nil, errors.New("leveldb/table: inconsistent block type") + } + return b, ch, err + } else if err != nil { + return nil, nil, err + } + } + + b, err := r.readFilterBlock(bh) + return b, b, err +} + +func (r *Reader) getIndexBlock(fillCache bool) (b *block, rel util.Releaser, err error) { + if r.indexBlock == nil { + return r.readBlockCached(r.indexBH, true, fillCache) + } + return r.indexBlock, util.NoopReleaser{}, nil +} + +func (r *Reader) getFilterBlock(fillCache bool) (*filterBlock, util.Releaser, error) { + if r.filterBlock == nil { + return r.readFilterBlockCached(r.filterBH, fillCache) + } + return r.filterBlock, util.NoopReleaser{}, nil +} + +func (r *Reader) newBlockIter(b *block, bReleaser util.Releaser, slice *util.Range, inclLimit bool) *blockIter { + bi := &blockIter{ + tr: r, + block: b, + blockReleaser: bReleaser, + // Valid key should never be nil. + key: make([]byte, 0), + dir: dirSOI, + riStart: 0, + riLimit: b.restartsLen, + offsetStart: 0, + offsetRealStart: 0, + offsetLimit: b.restartsOffset, + } + if slice != nil { + if slice.Start != nil { + if bi.Seek(slice.Start) { + bi.riStart = b.restartIndex(bi.restartIndex, b.restartsLen, bi.prevOffset) + bi.offsetStart = b.restartOffset(bi.riStart) + bi.offsetRealStart = bi.prevOffset + } else { + bi.riStart = b.restartsLen + bi.offsetStart = b.restartsOffset + bi.offsetRealStart = b.restartsOffset + } + } + if slice.Limit != nil { + if bi.Seek(slice.Limit) && (!inclLimit || bi.Next()) { + bi.offsetLimit = bi.prevOffset + bi.riLimit = bi.restartIndex + 1 + } + } + bi.reset() + if bi.offsetStart > bi.offsetLimit { + bi.sErr(errors.New("leveldb/table: invalid slice range")) + } + } + return bi +} + +func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, verifyChecksum, fillCache bool) iterator.Iterator { + b, rel, err := r.readBlockCached(dataBH, verifyChecksum, fillCache) + if err != nil { + return iterator.NewEmptyIterator(err) + } + return r.newBlockIter(b, rel, slice, false) +} + +func (r *Reader) getDataIterErr(dataBH blockHandle, slice *util.Range, verifyChecksum, fillCache bool) iterator.Iterator { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.err != nil { + return iterator.NewEmptyIterator(r.err) + } + + return r.getDataIter(dataBH, slice, verifyChecksum, fillCache) +} + +// NewIterator creates an iterator from the table. +// +// Slice allows slicing the iterator to only contains keys in the given +// range. A nil Range.Start is treated as a key before all keys in the +// table. And a nil Range.Limit is treated as a key after all keys in +// the table. +// +// The returned iterator is not safe for concurrent use and should be released +// after use. +// +// Also read Iterator documentation of the leveldb/iterator package. +func (r *Reader) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.err != nil { + return iterator.NewEmptyIterator(r.err) + } + + fillCache := !ro.GetDontFillCache() + indexBlock, rel, err := r.getIndexBlock(fillCache) + if err != nil { + return iterator.NewEmptyIterator(err) + } + index := &indexIter{ + blockIter: r.newBlockIter(indexBlock, rel, slice, true), + tr: r, + slice: slice, + fillCache: !ro.GetDontFillCache(), + } + return iterator.NewIndexedIterator(index, opt.GetStrict(r.o, ro, opt.StrictReader)) +} + +func (r *Reader) find(key []byte, filtered bool, ro *opt.ReadOptions, noValue bool) (rkey, value []byte, err error) { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.err != nil { + err = r.err + return + } + + indexBlock, rel, err := r.getIndexBlock(true) + if err != nil { + return + } + defer rel.Release() + + index := r.newBlockIter(indexBlock, nil, nil, true) + defer index.Release() + + if !index.Seek(key) { + if err = index.Error(); err == nil { + err = ErrNotFound + } + return + } + + dataBH, n := decodeBlockHandle(index.Value()) + if n == 0 { + r.err = r.newErrCorruptedBH(r.indexBH, "bad data block handle") + return nil, nil, r.err + } + + // The filter should only used for exact match. + if filtered && r.filter != nil { + filterBlock, frel, ferr := r.getFilterBlock(true) + if ferr == nil { + if !filterBlock.contains(r.filter, dataBH.offset, key) { + frel.Release() + return nil, nil, ErrNotFound + } + frel.Release() + } else if !errors.IsCorrupted(ferr) { + return nil, nil, ferr + } + } + + data := r.getDataIter(dataBH, nil, r.verifyChecksum, !ro.GetDontFillCache()) + if !data.Seek(key) { + data.Release() + if err = data.Error(); err != nil { + return + } + + // The nearest greater-than key is the first key of the next block. + if !index.Next() { + if err = index.Error(); err == nil { + err = ErrNotFound + } + return + } + + dataBH, n = decodeBlockHandle(index.Value()) + if n == 0 { + r.err = r.newErrCorruptedBH(r.indexBH, "bad data block handle") + return nil, nil, r.err + } + + data = r.getDataIter(dataBH, nil, r.verifyChecksum, !ro.GetDontFillCache()) + if !data.Next() { + data.Release() + if err = data.Error(); err == nil { + err = ErrNotFound + } + return + } + } + + // Key doesn't use block buffer, no need to copy the buffer. + rkey = data.Key() + if !noValue { + if r.bpool == nil { + value = data.Value() + } else { + // Value does use block buffer, and since the buffer will be + // recycled, it need to be copied. + value = append([]byte{}, data.Value()...) + } + } + data.Release() + return +} + +// Find finds key/value pair whose key is greater than or equal to the +// given key. It returns ErrNotFound if the table doesn't contain +// such pair. +// If filtered is true then the nearest 'block' will be checked against +// 'filter data' (if present) and will immediately return ErrNotFound if +// 'filter data' indicates that such pair doesn't exist. +// +// The caller may modify the contents of the returned slice as it is its +// own copy. +// It is safe to modify the contents of the argument after Find returns. +func (r *Reader) Find(key []byte, filtered bool, ro *opt.ReadOptions) (rkey, value []byte, err error) { + return r.find(key, filtered, ro, false) +} + +// FindKey finds key that is greater than or equal to the given key. +// It returns ErrNotFound if the table doesn't contain such key. +// If filtered is true then the nearest 'block' will be checked against +// 'filter data' (if present) and will immediately return ErrNotFound if +// 'filter data' indicates that such key doesn't exist. +// +// The caller may modify the contents of the returned slice as it is its +// own copy. +// It is safe to modify the contents of the argument after Find returns. +func (r *Reader) FindKey(key []byte, filtered bool, ro *opt.ReadOptions) (rkey []byte, err error) { + rkey, _, err = r.find(key, filtered, ro, true) + return +} + +// Get gets the value for the given key. It returns errors.ErrNotFound +// if the table does not contain the key. +// +// The caller may modify the contents of the returned slice as it is its +// own copy. +// It is safe to modify the contents of the argument after Find returns. +func (r *Reader) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.err != nil { + err = r.err + return + } + + rkey, value, err := r.find(key, false, ro, false) + if err == nil && r.cmp.Compare(rkey, key) != 0 { + value = nil + err = ErrNotFound + } + return +} + +// OffsetOf returns approximate offset for the given key. +// +// It is safe to modify the contents of the argument after Get returns. +func (r *Reader) OffsetOf(key []byte) (offset int64, err error) { + r.mu.RLock() + defer r.mu.RUnlock() + + if r.err != nil { + err = r.err + return + } + + indexBlock, rel, err := r.readBlockCached(r.indexBH, true, true) + if err != nil { + return + } + defer rel.Release() + + index := r.newBlockIter(indexBlock, nil, nil, true) + defer index.Release() + if index.Seek(key) { + dataBH, n := decodeBlockHandle(index.Value()) + if n == 0 { + r.err = r.newErrCorruptedBH(r.indexBH, "bad data block handle") + return + } + offset = int64(dataBH.offset) + return + } + err = index.Error() + if err == nil { + offset = r.dataEnd + } + return +} + +// Release implements util.Releaser. +// It also close the file if it is an io.Closer. +func (r *Reader) Release() { + r.mu.Lock() + defer r.mu.Unlock() + + if closer, ok := r.reader.(io.Closer); ok { + closer.Close() + } + if r.indexBlock != nil { + r.indexBlock.Release() + r.indexBlock = nil + } + if r.filterBlock != nil { + r.filterBlock.Release() + r.filterBlock = nil + } + r.reader = nil + r.cache = nil + r.bpool = nil + r.err = ErrReaderReleased +} + +// NewReader creates a new initialized table reader for the file. +// The fi, cache and bpool is optional and can be nil. +// +// The returned table reader instance is safe for concurrent use. +func NewReader(f io.ReaderAt, size int64, fd storage.FileDesc, cache *cache.NamespaceGetter, bpool *util.BufferPool, o *opt.Options) (*Reader, error) { + if f == nil { + return nil, errors.New("leveldb/table: nil file") + } + + r := &Reader{ + fd: fd, + reader: f, + cache: cache, + bpool: bpool, + o: o, + cmp: o.GetComparer(), + verifyChecksum: o.GetStrict(opt.StrictBlockChecksum), + } + + if size < footerLen { + r.err = r.newErrCorrupted(0, size, "table", "too small") + return r, nil + } + + footerPos := size - footerLen + var footer [footerLen]byte + if _, err := r.reader.ReadAt(footer[:], footerPos); err != nil && err != io.EOF { + return nil, err + } + if string(footer[footerLen-len(magic):footerLen]) != magic { + r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad magic number") + return r, nil + } + + var n int + // Decode the metaindex block handle. + r.metaBH, n = decodeBlockHandle(footer[:]) + if n == 0 { + r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad metaindex block handle") + return r, nil + } + + // Decode the index block handle. + r.indexBH, n = decodeBlockHandle(footer[n:]) + if n == 0 { + r.err = r.newErrCorrupted(footerPos, footerLen, "table-footer", "bad index block handle") + return r, nil + } + + // Read metaindex block. + metaBlock, err := r.readBlock(r.metaBH, true) + if err != nil { + if errors.IsCorrupted(err) { + r.err = err + return r, nil + } + return nil, err + } + + // Set data end. + r.dataEnd = int64(r.metaBH.offset) + + // Read metaindex. + metaIter := r.newBlockIter(metaBlock, nil, nil, true) + for metaIter.Next() { + key := string(metaIter.Key()) + if !strings.HasPrefix(key, "filter.") { + continue + } + fn := key[7:] + if f0 := o.GetFilter(); f0 != nil && f0.Name() == fn { + r.filter = f0 + } else { + for _, f0 := range o.GetAltFilters() { + if f0.Name() == fn { + r.filter = f0 + break + } + } + } + if r.filter != nil { + filterBH, n := decodeBlockHandle(metaIter.Value()) + if n == 0 { + continue + } + r.filterBH = filterBH + // Update data end. + r.dataEnd = int64(filterBH.offset) + break + } + } + metaIter.Release() + metaBlock.Release() + + // Cache index and filter block locally, since we don't have global cache. + if cache == nil { + r.indexBlock, err = r.readBlock(r.indexBH, true) + if err != nil { + if errors.IsCorrupted(err) { + r.err = err + return r, nil + } + return nil, err + } + if r.filter != nil { + r.filterBlock, err = r.readFilterBlock(r.filterBH) + if err != nil { + if !errors.IsCorrupted(err) { + return nil, err + } + + // Don't use filter then. + r.filter = nil + } + } + } + + return r, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/table.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/table.go new file mode 100644 index 0000000..beacdc1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/table.go @@ -0,0 +1,177 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package table allows read and write sorted key/value. +package table + +import ( + "encoding/binary" +) + +/* +Table: + +Table is consist of one or more data blocks, an optional filter block +a metaindex block, an index block and a table footer. Metaindex block +is a special block used to keep parameters of the table, such as filter +block name and its block handle. Index block is a special block used to +keep record of data blocks offset and length, index block use one as +restart interval. The key used by index block are the last key of preceding +block, shorter separator of adjacent blocks or shorter successor of the +last key of the last block. Filter block is an optional block contains +sequence of filter data generated by a filter generator. + +Table data structure: + + optional + / + +--------------+--------------+--------------+------+-------+-----------------+-------------+--------+ + | data block 1 | ... | data block n | filter block | metaindex block | index block | footer | + +--------------+--------------+--------------+--------------+-----------------+-------------+--------+ + + Each block followed by a 5-bytes trailer contains compression type and checksum. + +Table block trailer: + + +---------------------------+-------------------+ + | compression type (1-byte) | checksum (4-byte) | + +---------------------------+-------------------+ + + The checksum is a CRC-32 computed using Castagnoli's polynomial. Compression + type also included in the checksum. + +Table footer: + + +------------------- 40-bytes -------------------+ + / \ + +------------------------+--------------------+------+-----------------+ + | metaindex block handle / index block handle / ---- | magic (8-bytes) | + +------------------------+--------------------+------+-----------------+ + + The magic are first 64-bit of SHA-1 sum of "http://code.google.com/p/leveldb/". + +NOTE: All fixed-length integer are little-endian. +*/ + +/* +Block: + +Block is consist of one or more key/value entries and a block trailer. +Block entry shares key prefix with its preceding key until a restart +point reached. A block should contains at least one restart point. +First restart point are always zero. + +Block data structure: + + + restart point + restart point (depends on restart interval) + / / + +---------------+---------------+---------------+---------------+---------+ + | block entry 1 | block entry 2 | ... | block entry n | trailer | + +---------------+---------------+---------------+---------------+---------+ + +Key/value entry: + + +---- key len ----+ + / \ + +-------+---------+-----------+---------+--------------------+--------------+----------------+ + | shared (varint) | not shared (varint) | value len (varint) | key (varlen) | value (varlen) | + +-----------------+---------------------+--------------------+--------------+----------------+ + + Block entry shares key prefix with its preceding key: + Conditions: + restart_interval=2 + entry one : key=deck,value=v1 + entry two : key=dock,value=v2 + entry three: key=duck,value=v3 + The entries will be encoded as follow: + + + restart point (offset=0) + restart point (offset=16) + / / + +-----+-----+-----+----------+--------+-----+-----+-----+---------+--------+-----+-----+-----+----------+--------+ + | 0 | 4 | 2 | "deck" | "v1" | 1 | 3 | 2 | "ock" | "v2" | 0 | 4 | 2 | "duck" | "v3" | + +-----+-----+-----+----------+--------+-----+-----+-----+---------+--------+-----+-----+-----+----------+--------+ + \ / \ / \ / + +----------- entry one -----------+ +----------- entry two ----------+ +---------- entry three ----------+ + + The block trailer will contains two restart points: + + +------------+-----------+--------+ + | 0 | 16 | 2 | + +------------+-----------+---+----+ + \ / \ + +-- restart points --+ + restart points length + +Block trailer: + + +-- 4-bytes --+ + / \ + +-----------------+-----------------+-----------------+------------------------------+ + | restart point 1 | .... | restart point n | restart points len (4-bytes) | + +-----------------+-----------------+-----------------+------------------------------+ + + +NOTE: All fixed-length integer are little-endian. +*/ + +/* +Filter block: + +Filter block consist of one or more filter data and a filter block trailer. +The trailer contains filter data offsets, a trailer offset and a 1-byte base Lg. + +Filter block data structure: + + + offset 1 + offset 2 + offset n + trailer offset + / / / / + +---------------+---------------+---------------+---------+ + | filter data 1 | ... | filter data n | trailer | + +---------------+---------------+---------------+---------+ + +Filter block trailer: + + +- 4-bytes -+ + / \ + +---------------+---------------+---------------+-------------------------------+------------------+ + | data 1 offset | .... | data n offset | data-offsets offset (4-bytes) | base Lg (1-byte) | + +-------------- +---------------+---------------+-------------------------------+------------------+ + + +NOTE: All fixed-length integer are little-endian. +*/ + +const ( + blockTrailerLen = 5 + footerLen = 48 + + magic = "\x57\xfb\x80\x8b\x24\x75\x47\xdb" + + // The block type gives the per-block compression format. + // These constants are part of the file format and should not be changed. + blockTypeNoCompression = 0 + blockTypeSnappyCompression = 1 + + // Generate new filter every 2KB of data + filterBaseLg = 11 + filterBase = 1 << filterBaseLg +) + +type blockHandle struct { + offset, length uint64 +} + +func decodeBlockHandle(src []byte) (blockHandle, int) { + offset, n := binary.Uvarint(src) + length, m := binary.Uvarint(src[n:]) + if n == 0 || m == 0 { + return blockHandle{}, 0 + } + return blockHandle{offset, length}, n + m +} + +func encodeBlockHandle(dst []byte, b blockHandle) int { + n := binary.PutUvarint(dst, b.offset) + m := binary.PutUvarint(dst[n:], b.length) + return n + m +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/writer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/writer.go new file mode 100644 index 0000000..b96b271 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/table/writer.go @@ -0,0 +1,375 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package table + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/golang/snappy" + + "github.com/syndtr/goleveldb/leveldb/comparer" + "github.com/syndtr/goleveldb/leveldb/filter" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +func sharedPrefixLen(a, b []byte) int { + i, n := 0, len(a) + if n > len(b) { + n = len(b) + } + for i < n && a[i] == b[i] { + i++ + } + return i +} + +type blockWriter struct { + restartInterval int + buf util.Buffer + nEntries int + prevKey []byte + restarts []uint32 + scratch []byte +} + +func (w *blockWriter) append(key, value []byte) { + nShared := 0 + if w.nEntries%w.restartInterval == 0 { + w.restarts = append(w.restarts, uint32(w.buf.Len())) + } else { + nShared = sharedPrefixLen(w.prevKey, key) + } + n := binary.PutUvarint(w.scratch[0:], uint64(nShared)) + n += binary.PutUvarint(w.scratch[n:], uint64(len(key)-nShared)) + n += binary.PutUvarint(w.scratch[n:], uint64(len(value))) + w.buf.Write(w.scratch[:n]) + w.buf.Write(key[nShared:]) + w.buf.Write(value) + w.prevKey = append(w.prevKey[:0], key...) + w.nEntries++ +} + +func (w *blockWriter) finish() { + // Write restarts entry. + if w.nEntries == 0 { + // Must have at least one restart entry. + w.restarts = append(w.restarts, 0) + } + w.restarts = append(w.restarts, uint32(len(w.restarts))) + for _, x := range w.restarts { + buf4 := w.buf.Alloc(4) + binary.LittleEndian.PutUint32(buf4, x) + } +} + +func (w *blockWriter) reset() { + w.buf.Reset() + w.nEntries = 0 + w.restarts = w.restarts[:0] +} + +func (w *blockWriter) bytesLen() int { + restartsLen := len(w.restarts) + if restartsLen == 0 { + restartsLen = 1 + } + return w.buf.Len() + 4*restartsLen + 4 +} + +type filterWriter struct { + generator filter.FilterGenerator + buf util.Buffer + nKeys int + offsets []uint32 +} + +func (w *filterWriter) add(key []byte) { + if w.generator == nil { + return + } + w.generator.Add(key) + w.nKeys++ +} + +func (w *filterWriter) flush(offset uint64) { + if w.generator == nil { + return + } + for x := int(offset / filterBase); x > len(w.offsets); { + w.generate() + } +} + +func (w *filterWriter) finish() { + if w.generator == nil { + return + } + // Generate last keys. + + if w.nKeys > 0 { + w.generate() + } + w.offsets = append(w.offsets, uint32(w.buf.Len())) + for _, x := range w.offsets { + buf4 := w.buf.Alloc(4) + binary.LittleEndian.PutUint32(buf4, x) + } + w.buf.WriteByte(filterBaseLg) +} + +func (w *filterWriter) generate() { + // Record offset. + w.offsets = append(w.offsets, uint32(w.buf.Len())) + // Generate filters. + if w.nKeys > 0 { + w.generator.Generate(&w.buf) + w.nKeys = 0 + } +} + +// Writer is a table writer. +type Writer struct { + writer io.Writer + err error + // Options + cmp comparer.Comparer + filter filter.Filter + compression opt.Compression + blockSize int + + dataBlock blockWriter + indexBlock blockWriter + filterBlock filterWriter + pendingBH blockHandle + offset uint64 + nEntries int + // Scratch allocated enough for 5 uvarint. Block writer should not use + // first 20-bytes since it will be used to encode block handle, which + // then passed to the block writer itself. + scratch [50]byte + comparerScratch []byte + compressionScratch []byte +} + +func (w *Writer) writeBlock(buf *util.Buffer, compression opt.Compression) (bh blockHandle, err error) { + // Compress the buffer if necessary. + var b []byte + if compression == opt.SnappyCompression { + // Allocate scratch enough for compression and block trailer. + if n := snappy.MaxEncodedLen(buf.Len()) + blockTrailerLen; len(w.compressionScratch) < n { + w.compressionScratch = make([]byte, n) + } + compressed := snappy.Encode(w.compressionScratch, buf.Bytes()) + n := len(compressed) + b = compressed[:n+blockTrailerLen] + b[n] = blockTypeSnappyCompression + } else { + tmp := buf.Alloc(blockTrailerLen) + tmp[0] = blockTypeNoCompression + b = buf.Bytes() + } + + // Calculate the checksum. + n := len(b) - 4 + checksum := util.NewCRC(b[:n]).Value() + binary.LittleEndian.PutUint32(b[n:], checksum) + + // Write the buffer to the file. + _, err = w.writer.Write(b) + if err != nil { + return + } + bh = blockHandle{w.offset, uint64(len(b) - blockTrailerLen)} + w.offset += uint64(len(b)) + return +} + +func (w *Writer) flushPendingBH(key []byte) { + if w.pendingBH.length == 0 { + return + } + var separator []byte + if len(key) == 0 { + separator = w.cmp.Successor(w.comparerScratch[:0], w.dataBlock.prevKey) + } else { + separator = w.cmp.Separator(w.comparerScratch[:0], w.dataBlock.prevKey, key) + } + if separator == nil { + separator = w.dataBlock.prevKey + } else { + w.comparerScratch = separator + } + n := encodeBlockHandle(w.scratch[:20], w.pendingBH) + // Append the block handle to the index block. + w.indexBlock.append(separator, w.scratch[:n]) + // Reset prev key of the data block. + w.dataBlock.prevKey = w.dataBlock.prevKey[:0] + // Clear pending block handle. + w.pendingBH = blockHandle{} +} + +func (w *Writer) finishBlock() error { + w.dataBlock.finish() + bh, err := w.writeBlock(&w.dataBlock.buf, w.compression) + if err != nil { + return err + } + w.pendingBH = bh + // Reset the data block. + w.dataBlock.reset() + // Flush the filter block. + w.filterBlock.flush(w.offset) + return nil +} + +// Append appends key/value pair to the table. The keys passed must +// be in increasing order. +// +// It is safe to modify the contents of the arguments after Append returns. +func (w *Writer) Append(key, value []byte) error { + if w.err != nil { + return w.err + } + if w.nEntries > 0 && w.cmp.Compare(w.dataBlock.prevKey, key) >= 0 { + w.err = fmt.Errorf("leveldb/table: Writer: keys are not in increasing order: %q, %q", w.dataBlock.prevKey, key) + return w.err + } + + w.flushPendingBH(key) + // Append key/value pair to the data block. + w.dataBlock.append(key, value) + // Add key to the filter block. + w.filterBlock.add(key) + + // Finish the data block if block size target reached. + if w.dataBlock.bytesLen() >= w.blockSize { + if err := w.finishBlock(); err != nil { + w.err = err + return w.err + } + } + w.nEntries++ + return nil +} + +// BlocksLen returns number of blocks written so far. +func (w *Writer) BlocksLen() int { + n := w.indexBlock.nEntries + if w.pendingBH.length > 0 { + // Includes the pending block. + n++ + } + return n +} + +// EntriesLen returns number of entries added so far. +func (w *Writer) EntriesLen() int { + return w.nEntries +} + +// BytesLen returns number of bytes written so far. +func (w *Writer) BytesLen() int { + return int(w.offset) +} + +// Close will finalize the table. Calling Append is not possible +// after Close, but calling BlocksLen, EntriesLen and BytesLen +// is still possible. +func (w *Writer) Close() error { + if w.err != nil { + return w.err + } + + // Write the last data block. Or empty data block if there + // aren't any data blocks at all. + if w.dataBlock.nEntries > 0 || w.nEntries == 0 { + if err := w.finishBlock(); err != nil { + w.err = err + return w.err + } + } + w.flushPendingBH(nil) + + // Write the filter block. + var filterBH blockHandle + w.filterBlock.finish() + if buf := &w.filterBlock.buf; buf.Len() > 0 { + filterBH, w.err = w.writeBlock(buf, opt.NoCompression) + if w.err != nil { + return w.err + } + } + + // Write the metaindex block. + if filterBH.length > 0 { + key := []byte("filter." + w.filter.Name()) + n := encodeBlockHandle(w.scratch[:20], filterBH) + w.dataBlock.append(key, w.scratch[:n]) + } + w.dataBlock.finish() + metaindexBH, err := w.writeBlock(&w.dataBlock.buf, w.compression) + if err != nil { + w.err = err + return w.err + } + + // Write the index block. + w.indexBlock.finish() + indexBH, err := w.writeBlock(&w.indexBlock.buf, w.compression) + if err != nil { + w.err = err + return w.err + } + + // Write the table footer. + footer := w.scratch[:footerLen] + for i := range footer { + footer[i] = 0 + } + n := encodeBlockHandle(footer, metaindexBH) + encodeBlockHandle(footer[n:], indexBH) + copy(footer[footerLen-len(magic):], magic) + if _, err := w.writer.Write(footer); err != nil { + w.err = err + return w.err + } + w.offset += footerLen + + w.err = errors.New("leveldb/table: writer is closed") + return nil +} + +// NewWriter creates a new initialized table writer for the file. +// +// Table writer is not safe for concurrent use. +func NewWriter(f io.Writer, o *opt.Options) *Writer { + w := &Writer{ + writer: f, + cmp: o.GetComparer(), + filter: o.GetFilter(), + compression: o.GetCompression(), + blockSize: o.GetBlockSize(), + comparerScratch: make([]byte, 0), + } + // data block + w.dataBlock.restartInterval = o.GetBlockRestartInterval() + // The first 20-bytes are used for encoding block handle. + w.dataBlock.scratch = w.scratch[20:] + // index block + w.indexBlock.restartInterval = 1 + w.indexBlock.scratch = w.scratch[20:] + // filter block + if w.filter != nil { + w.filterBlock.generator = w.filter.NewGenerator() + w.filterBlock.flush(0) + } + return w +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go new file mode 100644 index 0000000..e572a32 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util.go @@ -0,0 +1,98 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "fmt" + "sort" + + "github.com/syndtr/goleveldb/leveldb/storage" +) + +func shorten(str string) string { + if len(str) <= 8 { + return str + } + return str[:3] + ".." + str[len(str)-3:] +} + +var bunits = [...]string{"", "Ki", "Mi", "Gi"} + +func shortenb(bytes int) string { + i := 0 + for ; bytes > 1024 && i < 4; i++ { + bytes /= 1024 + } + return fmt.Sprintf("%d%sB", bytes, bunits[i]) +} + +func sshortenb(bytes int) string { + if bytes == 0 { + return "~" + } + sign := "+" + if bytes < 0 { + sign = "-" + bytes *= -1 + } + i := 0 + for ; bytes > 1024 && i < 4; i++ { + bytes /= 1024 + } + return fmt.Sprintf("%s%d%sB", sign, bytes, bunits[i]) +} + +func sint(x int) string { + if x == 0 { + return "~" + } + sign := "+" + if x < 0 { + sign = "-" + x *= -1 + } + return fmt.Sprintf("%s%d", sign, x) +} + +func minInt(a, b int) int { + if a < b { + return a + } + return b +} + +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} + +type fdSorter []storage.FileDesc + +func (p fdSorter) Len() int { + return len(p) +} + +func (p fdSorter) Less(i, j int) bool { + return p[i].Num < p[j].Num +} + +func (p fdSorter) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func sortFds(fds []storage.FileDesc) { + sort.Sort(fdSorter(fds)) +} + +func ensureBuffer(b []byte, n int) []byte { + if cap(b) < n { + return make([]byte, n) + } + return b[:n] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer.go new file mode 100644 index 0000000..21de242 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer.go @@ -0,0 +1,293 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package util + +// This a copy of Go std bytes.Buffer with some modification +// and some features stripped. + +import ( + "bytes" + "io" +) + +// A Buffer is a variable-sized buffer of bytes with Read and Write methods. +// The zero value for Buffer is an empty buffer ready to use. +type Buffer struct { + buf []byte // contents are the bytes buf[off : len(buf)] + off int // read at &buf[off], write at &buf[len(buf)] + bootstrap [64]byte // memory to hold first slice; helps small buffers (Printf) avoid allocation. +} + +// Bytes returns a slice of the contents of the unread portion of the buffer; +// len(b.Bytes()) == b.Len(). If the caller changes the contents of the +// returned slice, the contents of the buffer will change provided there +// are no intervening method calls on the Buffer. +func (b *Buffer) Bytes() []byte { return b.buf[b.off:] } + +// String returns the contents of the unread portion of the buffer +// as a string. If the Buffer is a nil pointer, it returns "". +func (b *Buffer) String() string { + if b == nil { + // Special case, useful in debugging. + return "" + } + return string(b.buf[b.off:]) +} + +// Len returns the number of bytes of the unread portion of the buffer; +// b.Len() == len(b.Bytes()). +func (b *Buffer) Len() int { return len(b.buf) - b.off } + +// Truncate discards all but the first n unread bytes from the buffer. +// It panics if n is negative or greater than the length of the buffer. +func (b *Buffer) Truncate(n int) { + switch { + case n < 0 || n > b.Len(): + panic("leveldb/util.Buffer: truncation out of range") + case n == 0: + // Reuse buffer space. + b.off = 0 + } + b.buf = b.buf[0 : b.off+n] +} + +// Reset resets the buffer so it has no content. +// b.Reset() is the same as b.Truncate(0). +func (b *Buffer) Reset() { b.Truncate(0) } + +// grow grows the buffer to guarantee space for n more bytes. +// It returns the index where bytes should be written. +// If the buffer can't grow it will panic with bytes.ErrTooLarge. +func (b *Buffer) grow(n int) int { + m := b.Len() + // If buffer is empty, reset to recover space. + if m == 0 && b.off != 0 { + b.Truncate(0) + } + if len(b.buf)+n > cap(b.buf) { + var buf []byte + if b.buf == nil && n <= len(b.bootstrap) { + buf = b.bootstrap[0:] + } else if m+n <= cap(b.buf)/2 { + // We can slide things down instead of allocating a new + // slice. We only need m+n <= cap(b.buf) to slide, but + // we instead let capacity get twice as large so we + // don't spend all our time copying. + copy(b.buf[:], b.buf[b.off:]) + buf = b.buf[:m] + } else { + // not enough space anywhere + buf = makeSlice(2*cap(b.buf) + n) + copy(buf, b.buf[b.off:]) + } + b.buf = buf + b.off = 0 + } + b.buf = b.buf[0 : b.off+m+n] + return b.off + m +} + +// Alloc allocs n bytes of slice from the buffer, growing the buffer as +// needed. If n is negative, Alloc will panic. +// If the buffer can't grow it will panic with bytes.ErrTooLarge. +func (b *Buffer) Alloc(n int) []byte { + if n < 0 { + panic("leveldb/util.Buffer.Alloc: negative count") + } + m := b.grow(n) + return b.buf[m:] +} + +// Grow grows the buffer's capacity, if necessary, to guarantee space for +// another n bytes. After Grow(n), at least n bytes can be written to the +// buffer without another allocation. +// If n is negative, Grow will panic. +// If the buffer can't grow it will panic with bytes.ErrTooLarge. +func (b *Buffer) Grow(n int) { + if n < 0 { + panic("leveldb/util.Buffer.Grow: negative count") + } + m := b.grow(n) + b.buf = b.buf[0:m] +} + +// Write appends the contents of p to the buffer, growing the buffer as +// needed. The return value n is the length of p; err is always nil. If the +// buffer becomes too large, Write will panic with bytes.ErrTooLarge. +func (b *Buffer) Write(p []byte) (n int, err error) { + m := b.grow(len(p)) + return copy(b.buf[m:], p), nil +} + +// MinRead is the minimum slice size passed to a Read call by +// Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond +// what is required to hold the contents of r, ReadFrom will not grow the +// underlying buffer. +const MinRead = 512 + +// ReadFrom reads data from r until EOF and appends it to the buffer, growing +// the buffer as needed. The return value n is the number of bytes read. Any +// error except io.EOF encountered during the read is also returned. If the +// buffer becomes too large, ReadFrom will panic with bytes.ErrTooLarge. +func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { + // If buffer is empty, reset to recover space. + if b.off >= len(b.buf) { + b.Truncate(0) + } + for { + if free := cap(b.buf) - len(b.buf); free < MinRead { + // not enough space at end + newBuf := b.buf + if b.off+free < MinRead { + // not enough space using beginning of buffer; + // double buffer capacity + newBuf = makeSlice(2*cap(b.buf) + MinRead) + } + copy(newBuf, b.buf[b.off:]) + b.buf = newBuf[:len(b.buf)-b.off] + b.off = 0 + } + m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]) + b.buf = b.buf[0 : len(b.buf)+m] + n += int64(m) + if e == io.EOF { + break + } + if e != nil { + return n, e + } + } + return n, nil // err is EOF, so return nil explicitly +} + +// makeSlice allocates a slice of size n. If the allocation fails, it panics +// with bytes.ErrTooLarge. +func makeSlice(n int) []byte { + // If the make fails, give a known error. + defer func() { + if recover() != nil { + panic(bytes.ErrTooLarge) + } + }() + return make([]byte, n) +} + +// WriteTo writes data to w until the buffer is drained or an error occurs. +// The return value n is the number of bytes written; it always fits into an +// int, but it is int64 to match the io.WriterTo interface. Any error +// encountered during the write is also returned. +func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) { + if b.off < len(b.buf) { + nBytes := b.Len() + m, e := w.Write(b.buf[b.off:]) + if m > nBytes { + panic("leveldb/util.Buffer.WriteTo: invalid Write count") + } + b.off += m + n = int64(m) + if e != nil { + return n, e + } + // all bytes should have been written, by definition of + // Write method in io.Writer + if m != nBytes { + return n, io.ErrShortWrite + } + } + // Buffer is now empty; reset. + b.Truncate(0) + return +} + +// WriteByte appends the byte c to the buffer, growing the buffer as needed. +// The returned error is always nil, but is included to match bufio.Writer's +// WriteByte. If the buffer becomes too large, WriteByte will panic with +// bytes.ErrTooLarge. +func (b *Buffer) WriteByte(c byte) error { + m := b.grow(1) + b.buf[m] = c + return nil +} + +// Read reads the next len(p) bytes from the buffer or until the buffer +// is drained. The return value n is the number of bytes read. If the +// buffer has no data to return, err is io.EOF (unless len(p) is zero); +// otherwise it is nil. +func (b *Buffer) Read(p []byte) (n int, err error) { + if b.off >= len(b.buf) { + // Buffer is empty, reset to recover space. + b.Truncate(0) + if len(p) == 0 { + return + } + return 0, io.EOF + } + n = copy(p, b.buf[b.off:]) + b.off += n + return +} + +// Next returns a slice containing the next n bytes from the buffer, +// advancing the buffer as if the bytes had been returned by Read. +// If there are fewer than n bytes in the buffer, Next returns the entire buffer. +// The slice is only valid until the next call to a read or write method. +func (b *Buffer) Next(n int) []byte { + m := b.Len() + if n > m { + n = m + } + data := b.buf[b.off : b.off+n] + b.off += n + return data +} + +// ReadByte reads and returns the next byte from the buffer. +// If no byte is available, it returns error io.EOF. +func (b *Buffer) ReadByte() (c byte, err error) { + if b.off >= len(b.buf) { + // Buffer is empty, reset to recover space. + b.Truncate(0) + return 0, io.EOF + } + c = b.buf[b.off] + b.off++ + return c, nil +} + +// ReadBytes reads until the first occurrence of delim in the input, +// returning a slice containing the data up to and including the delimiter. +// If ReadBytes encounters an error before finding a delimiter, +// it returns the data read before the error and the error itself (often io.EOF). +// ReadBytes returns err != nil if and only if the returned data does not end in +// delim. +func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { + slice, err := b.readSlice(delim) + // return a copy of slice. The buffer's backing array may + // be overwritten by later calls. + line = append(line, slice...) + return +} + +// readSlice is like ReadBytes but returns a reference to internal buffer data. +func (b *Buffer) readSlice(delim byte) (line []byte, err error) { + i := bytes.IndexByte(b.buf[b.off:], delim) + end := b.off + i + 1 + if i < 0 { + end = len(b.buf) + err = io.EOF + } + line = b.buf[b.off:end] + b.off = end + return line, err +} + +// NewBuffer creates and initializes a new Buffer using buf as its initial +// contents. It is intended to prepare a Buffer to read existing data. It +// can also be used to size the internal buffer for writing. To do that, +// buf should have the desired capacity but a length of zero. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. +func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer_pool.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer_pool.go new file mode 100644 index 0000000..2f3db97 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/buffer_pool.go @@ -0,0 +1,239 @@ +// Copyright (c) 2014, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package util + +import ( + "fmt" + "sync" + "sync/atomic" + "time" +) + +type buffer struct { + b []byte + miss int +} + +// BufferPool is a 'buffer pool'. +type BufferPool struct { + pool [6]chan []byte + size [5]uint32 + sizeMiss [5]uint32 + sizeHalf [5]uint32 + baseline [4]int + baseline0 int + + mu sync.RWMutex + closed bool + closeC chan struct{} + + get uint32 + put uint32 + half uint32 + less uint32 + equal uint32 + greater uint32 + miss uint32 +} + +func (p *BufferPool) poolNum(n int) int { + if n <= p.baseline0 && n > p.baseline0/2 { + return 0 + } + for i, x := range p.baseline { + if n <= x { + return i + 1 + } + } + return len(p.baseline) + 1 +} + +// Get returns buffer with length of n. +func (p *BufferPool) Get(n int) []byte { + if p == nil { + return make([]byte, n) + } + + p.mu.RLock() + defer p.mu.RUnlock() + + if p.closed { + return make([]byte, n) + } + + atomic.AddUint32(&p.get, 1) + + poolNum := p.poolNum(n) + pool := p.pool[poolNum] + if poolNum == 0 { + // Fast path. + select { + case b := <-pool: + switch { + case cap(b) > n: + if cap(b)-n >= n { + atomic.AddUint32(&p.half, 1) + select { + case pool <- b: + default: + } + return make([]byte, n) + } else { + atomic.AddUint32(&p.less, 1) + return b[:n] + } + case cap(b) == n: + atomic.AddUint32(&p.equal, 1) + return b[:n] + default: + atomic.AddUint32(&p.greater, 1) + } + default: + atomic.AddUint32(&p.miss, 1) + } + + return make([]byte, n, p.baseline0) + } else { + sizePtr := &p.size[poolNum-1] + + select { + case b := <-pool: + switch { + case cap(b) > n: + if cap(b)-n >= n { + atomic.AddUint32(&p.half, 1) + sizeHalfPtr := &p.sizeHalf[poolNum-1] + if atomic.AddUint32(sizeHalfPtr, 1) == 20 { + atomic.StoreUint32(sizePtr, uint32(cap(b)/2)) + atomic.StoreUint32(sizeHalfPtr, 0) + } else { + select { + case pool <- b: + default: + } + } + return make([]byte, n) + } else { + atomic.AddUint32(&p.less, 1) + return b[:n] + } + case cap(b) == n: + atomic.AddUint32(&p.equal, 1) + return b[:n] + default: + atomic.AddUint32(&p.greater, 1) + if uint32(cap(b)) >= atomic.LoadUint32(sizePtr) { + select { + case pool <- b: + default: + } + } + } + default: + atomic.AddUint32(&p.miss, 1) + } + + if size := atomic.LoadUint32(sizePtr); uint32(n) > size { + if size == 0 { + atomic.CompareAndSwapUint32(sizePtr, 0, uint32(n)) + } else { + sizeMissPtr := &p.sizeMiss[poolNum-1] + if atomic.AddUint32(sizeMissPtr, 1) == 20 { + atomic.StoreUint32(sizePtr, uint32(n)) + atomic.StoreUint32(sizeMissPtr, 0) + } + } + return make([]byte, n) + } else { + return make([]byte, n, size) + } + } +} + +// Put adds given buffer to the pool. +func (p *BufferPool) Put(b []byte) { + if p == nil { + return + } + + p.mu.RLock() + defer p.mu.RUnlock() + + if p.closed { + return + } + + atomic.AddUint32(&p.put, 1) + + pool := p.pool[p.poolNum(cap(b))] + select { + case pool <- b: + default: + } + +} + +func (p *BufferPool) Close() { + if p == nil { + return + } + + p.mu.Lock() + if !p.closed { + p.closed = true + p.closeC <- struct{}{} + } + p.mu.Unlock() +} + +func (p *BufferPool) String() string { + if p == nil { + return "" + } + + return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v Zh·%v G·%d P·%d H·%d <·%d =·%d >·%d M·%d}", + p.baseline0, p.size, p.sizeMiss, p.sizeHalf, p.get, p.put, p.half, p.less, p.equal, p.greater, p.miss) +} + +func (p *BufferPool) drain() { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + for _, ch := range p.pool { + select { + case <-ch: + default: + } + } + case <-p.closeC: + close(p.closeC) + for _, ch := range p.pool { + close(ch) + } + return + } + } +} + +// NewBufferPool creates a new initialized 'buffer pool'. +func NewBufferPool(baseline int) *BufferPool { + if baseline <= 0 { + panic("baseline can't be <= 0") + } + p := &BufferPool{ + baseline0: baseline, + baseline: [...]int{baseline / 4, baseline / 2, baseline * 2, baseline * 4}, + closeC: make(chan struct{}, 1), + } + for i, cap := range []int{2, 2, 4, 4, 2, 1} { + p.pool[i] = make(chan []byte, cap) + } + go p.drain() + return p +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/crc32.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/crc32.go new file mode 100644 index 0000000..631c9d6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/crc32.go @@ -0,0 +1,30 @@ +// Copyright 2011 The LevelDB-Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package util + +import ( + "hash/crc32" +) + +var table = crc32.MakeTable(crc32.Castagnoli) + +// CRC is a CRC-32 checksum computed using Castagnoli's polynomial. +type CRC uint32 + +// NewCRC creates a new crc based on the given bytes. +func NewCRC(b []byte) CRC { + return CRC(0).Update(b) +} + +// Update updates the crc with the given bytes. +func (c CRC) Update(b []byte) CRC { + return CRC(crc32.Update(uint32(c), table, b)) +} + +// Value returns a masked crc. +func (c CRC) Value() uint32 { + return uint32(c>>15|c<<17) + 0xa282ead8 +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/hash.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/hash.go new file mode 100644 index 0000000..7f3fa4e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/hash.go @@ -0,0 +1,48 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package util + +import ( + "encoding/binary" +) + +// Hash return hash of the given data. +func Hash(data []byte, seed uint32) uint32 { + // Similar to murmur hash + const ( + m = uint32(0xc6a4a793) + r = uint32(24) + ) + var ( + h = seed ^ (uint32(len(data)) * m) + i int + ) + + for n := len(data) - len(data)%4; i < n; i += 4 { + h += binary.LittleEndian.Uint32(data[i:]) + h *= m + h ^= (h >> 16) + } + + switch len(data) - i { + default: + panic("not reached") + case 3: + h += uint32(data[i+2]) << 16 + fallthrough + case 2: + h += uint32(data[i+1]) << 8 + fallthrough + case 1: + h += uint32(data[i]) + h *= m + h ^= (h >> r) + case 0: + } + + return h +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/range.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/range.go new file mode 100644 index 0000000..8515958 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/range.go @@ -0,0 +1,32 @@ +// Copyright (c) 2014, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package util + +// Range is a key range. +type Range struct { + // Start of the key range, include in the range. + Start []byte + + // Limit of the key range, not include in the range. + Limit []byte +} + +// BytesPrefix returns key range that satisfy the given prefix. +// This only applicable for the standard 'bytes comparer'. +func BytesPrefix(prefix []byte) *Range { + var limit []byte + for i := len(prefix) - 1; i >= 0; i-- { + c := prefix[i] + if c < 0xff { + limit = make([]byte, i+1) + copy(limit, prefix) + limit[i] = c + 1 + break + } + } + return &Range{prefix, limit} +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/util.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/util.go new file mode 100644 index 0000000..f359768 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/util/util.go @@ -0,0 +1,73 @@ +// Copyright (c) 2013, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Package util provides utilities used throughout leveldb. +package util + +import ( + "errors" +) + +var ( + ErrReleased = errors.New("leveldb: resource already relesed") + ErrHasReleaser = errors.New("leveldb: releaser already defined") +) + +// Releaser is the interface that wraps the basic Release method. +type Releaser interface { + // Release releases associated resources. Release should always success + // and can be called multipe times without causing error. + Release() +} + +// ReleaseSetter is the interface that wraps the basic SetReleaser method. +type ReleaseSetter interface { + // SetReleaser associates the given releaser to the resources. The + // releaser will be called once coresponding resources released. + // Calling SetReleaser with nil will clear the releaser. + // + // This will panic if a releaser already present or coresponding + // resource is already released. Releaser should be cleared first + // before assigned a new one. + SetReleaser(releaser Releaser) +} + +// BasicReleaser provides basic implementation of Releaser and ReleaseSetter. +type BasicReleaser struct { + releaser Releaser + released bool +} + +// Released returns whether Release method already called. +func (r *BasicReleaser) Released() bool { + return r.released +} + +// Release implements Releaser.Release. +func (r *BasicReleaser) Release() { + if !r.released { + if r.releaser != nil { + r.releaser.Release() + r.releaser = nil + } + r.released = true + } +} + +// SetReleaser implements ReleaseSetter.SetReleaser. +func (r *BasicReleaser) SetReleaser(releaser Releaser) { + if r.released { + panic(ErrReleased) + } + if r.releaser != nil && releaser != nil { + panic(ErrHasReleaser) + } + r.releaser = releaser +} + +type NoopReleaser struct{} + +func (NoopReleaser) Release() {} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go new file mode 100644 index 0000000..73f272a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/github.com/syndtr/goleveldb/leveldb/version.go @@ -0,0 +1,528 @@ +// Copyright (c) 2012, Suryandaru Triandana +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package leveldb + +import ( + "fmt" + "sync/atomic" + "unsafe" + + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" +) + +type tSet struct { + level int + table *tFile +} + +type version struct { + s *session + + levels []tFiles + + // Level that should be compacted next and its compaction score. + // Score < 1 means compaction is not strictly needed. These fields + // are initialized by computeCompaction() + cLevel int + cScore float64 + + cSeek unsafe.Pointer + + closing bool + ref int + released bool +} + +func newVersion(s *session) *version { + return &version{s: s} +} + +func (v *version) incref() { + if v.released { + panic("already released") + } + + v.ref++ + if v.ref == 1 { + // Incr file ref. + for _, tt := range v.levels { + for _, t := range tt { + v.s.addFileRef(t.fd, 1) + } + } + } +} + +func (v *version) releaseNB() { + v.ref-- + if v.ref > 0 { + return + } else if v.ref < 0 { + panic("negative version ref") + } + + for _, tt := range v.levels { + for _, t := range tt { + if v.s.addFileRef(t.fd, -1) == 0 { + v.s.tops.remove(t) + } + } + } + + v.released = true +} + +func (v *version) release() { + v.s.vmu.Lock() + v.releaseNB() + v.s.vmu.Unlock() +} + +func (v *version) walkOverlapping(aux tFiles, ikey internalKey, f func(level int, t *tFile) bool, lf func(level int) bool) { + ukey := ikey.ukey() + + // Aux level. + if aux != nil { + for _, t := range aux { + if t.overlaps(v.s.icmp, ukey, ukey) { + if !f(-1, t) { + return + } + } + } + + if lf != nil && !lf(-1) { + return + } + } + + // Walk tables level-by-level. + for level, tables := range v.levels { + if len(tables) == 0 { + continue + } + + if level == 0 { + // Level-0 files may overlap each other. Find all files that + // overlap ukey. + for _, t := range tables { + if t.overlaps(v.s.icmp, ukey, ukey) { + if !f(level, t) { + return + } + } + } + } else { + if i := tables.searchMax(v.s.icmp, ikey); i < len(tables) { + t := tables[i] + if v.s.icmp.uCompare(ukey, t.imin.ukey()) >= 0 { + if !f(level, t) { + return + } + } + } + } + + if lf != nil && !lf(level) { + return + } + } +} + +func (v *version) get(aux tFiles, ikey internalKey, ro *opt.ReadOptions, noValue bool) (value []byte, tcomp bool, err error) { + if v.closing { + return nil, false, ErrClosed + } + + ukey := ikey.ukey() + + var ( + tset *tSet + tseek bool + + // Level-0. + zfound bool + zseq uint64 + zkt keyType + zval []byte + ) + + err = ErrNotFound + + // Since entries never hop across level, finding key/value + // in smaller level make later levels irrelevant. + v.walkOverlapping(aux, ikey, func(level int, t *tFile) bool { + if level >= 0 && !tseek { + if tset == nil { + tset = &tSet{level, t} + } else { + tseek = true + } + } + + var ( + fikey, fval []byte + ferr error + ) + if noValue { + fikey, ferr = v.s.tops.findKey(t, ikey, ro) + } else { + fikey, fval, ferr = v.s.tops.find(t, ikey, ro) + } + + switch ferr { + case nil: + case ErrNotFound: + return true + default: + err = ferr + return false + } + + if fukey, fseq, fkt, fkerr := parseInternalKey(fikey); fkerr == nil { + if v.s.icmp.uCompare(ukey, fukey) == 0 { + // Level <= 0 may overlaps each-other. + if level <= 0 { + if fseq >= zseq { + zfound = true + zseq = fseq + zkt = fkt + zval = fval + } + } else { + switch fkt { + case keyTypeVal: + value = fval + err = nil + case keyTypeDel: + default: + panic("leveldb: invalid internalKey type") + } + return false + } + } + } else { + err = fkerr + return false + } + + return true + }, func(level int) bool { + if zfound { + switch zkt { + case keyTypeVal: + value = zval + err = nil + case keyTypeDel: + default: + panic("leveldb: invalid internalKey type") + } + return false + } + + return true + }) + + if tseek && tset.table.consumeSeek() <= 0 { + tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset)) + } + + return +} + +func (v *version) sampleSeek(ikey internalKey) (tcomp bool) { + var tset *tSet + + v.walkOverlapping(nil, ikey, func(level int, t *tFile) bool { + if tset == nil { + tset = &tSet{level, t} + return true + } + if tset.table.consumeSeek() <= 0 { + tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset)) + } + return false + }, nil) + + return +} + +func (v *version) getIterators(slice *util.Range, ro *opt.ReadOptions) (its []iterator.Iterator) { + strict := opt.GetStrict(v.s.o.Options, ro, opt.StrictReader) + for level, tables := range v.levels { + if level == 0 { + // Merge all level zero files together since they may overlap. + for _, t := range tables { + its = append(its, v.s.tops.newIterator(t, slice, ro)) + } + } else if len(tables) != 0 { + its = append(its, iterator.NewIndexedIterator(tables.newIndexIterator(v.s.tops, v.s.icmp, slice, ro), strict)) + } + } + return +} + +func (v *version) newStaging() *versionStaging { + return &versionStaging{base: v} +} + +// Spawn a new version based on this version. +func (v *version) spawn(r *sessionRecord) *version { + staging := v.newStaging() + staging.commit(r) + return staging.finish() +} + +func (v *version) fillRecord(r *sessionRecord) { + for level, tables := range v.levels { + for _, t := range tables { + r.addTableFile(level, t) + } + } +} + +func (v *version) tLen(level int) int { + if level < len(v.levels) { + return len(v.levels[level]) + } + return 0 +} + +func (v *version) offsetOf(ikey internalKey) (n int64, err error) { + for level, tables := range v.levels { + for _, t := range tables { + if v.s.icmp.Compare(t.imax, ikey) <= 0 { + // Entire file is before "ikey", so just add the file size + n += t.size + } else if v.s.icmp.Compare(t.imin, ikey) > 0 { + // Entire file is after "ikey", so ignore + if level > 0 { + // Files other than level 0 are sorted by meta->min, so + // no further files in this level will contain data for + // "ikey". + break + } + } else { + // "ikey" falls in the range for this table. Add the + // approximate offset of "ikey" within the table. + if m, err := v.s.tops.offsetOf(t, ikey); err == nil { + n += m + } else { + return 0, err + } + } + } + } + + return +} + +func (v *version) pickMemdbLevel(umin, umax []byte, maxLevel int) (level int) { + if maxLevel > 0 { + if len(v.levels) == 0 { + return maxLevel + } + if !v.levels[0].overlaps(v.s.icmp, umin, umax, true) { + var overlaps tFiles + for ; level < maxLevel; level++ { + if pLevel := level + 1; pLevel >= len(v.levels) { + return maxLevel + } else if v.levels[pLevel].overlaps(v.s.icmp, umin, umax, false) { + break + } + if gpLevel := level + 2; gpLevel < len(v.levels) { + overlaps = v.levels[gpLevel].getOverlaps(overlaps, v.s.icmp, umin, umax, false) + if overlaps.size() > int64(v.s.o.GetCompactionGPOverlaps(level)) { + break + } + } + } + } + } + return +} + +func (v *version) computeCompaction() { + // Precomputed best level for next compaction + bestLevel := int(-1) + bestScore := float64(-1) + + statFiles := make([]int, len(v.levels)) + statSizes := make([]string, len(v.levels)) + statScore := make([]string, len(v.levels)) + statTotSize := int64(0) + + for level, tables := range v.levels { + var score float64 + size := tables.size() + if level == 0 { + // We treat level-0 specially by bounding the number of files + // instead of number of bytes for two reasons: + // + // (1) With larger write-buffer sizes, it is nice not to do too + // many level-0 compaction. + // + // (2) The files in level-0 are merged on every read and + // therefore we wish to avoid too many files when the individual + // file size is small (perhaps because of a small write-buffer + // setting, or very high compression ratios, or lots of + // overwrites/deletions). + score = float64(len(tables)) / float64(v.s.o.GetCompactionL0Trigger()) + } else { + score = float64(size) / float64(v.s.o.GetCompactionTotalSize(level)) + } + + if score > bestScore { + bestLevel = level + bestScore = score + } + + statFiles[level] = len(tables) + statSizes[level] = shortenb(int(size)) + statScore[level] = fmt.Sprintf("%.2f", score) + statTotSize += size + } + + v.cLevel = bestLevel + v.cScore = bestScore + + v.s.logf("version@stat F·%v S·%s%v Sc·%v", statFiles, shortenb(int(statTotSize)), statSizes, statScore) +} + +func (v *version) needCompaction() bool { + return v.cScore >= 1 || atomic.LoadPointer(&v.cSeek) != nil +} + +type tablesScratch struct { + added map[int64]atRecord + deleted map[int64]struct{} +} + +type versionStaging struct { + base *version + levels []tablesScratch +} + +func (p *versionStaging) getScratch(level int) *tablesScratch { + if level >= len(p.levels) { + newLevels := make([]tablesScratch, level+1) + copy(newLevels, p.levels) + p.levels = newLevels + } + return &(p.levels[level]) +} + +func (p *versionStaging) commit(r *sessionRecord) { + // Deleted tables. + for _, r := range r.deletedTables { + scratch := p.getScratch(r.level) + if r.level < len(p.base.levels) && len(p.base.levels[r.level]) > 0 { + if scratch.deleted == nil { + scratch.deleted = make(map[int64]struct{}) + } + scratch.deleted[r.num] = struct{}{} + } + if scratch.added != nil { + delete(scratch.added, r.num) + } + } + + // New tables. + for _, r := range r.addedTables { + scratch := p.getScratch(r.level) + if scratch.added == nil { + scratch.added = make(map[int64]atRecord) + } + scratch.added[r.num] = r + if scratch.deleted != nil { + delete(scratch.deleted, r.num) + } + } +} + +func (p *versionStaging) finish() *version { + // Build new version. + nv := newVersion(p.base.s) + numLevel := len(p.levels) + if len(p.base.levels) > numLevel { + numLevel = len(p.base.levels) + } + nv.levels = make([]tFiles, numLevel) + for level := 0; level < numLevel; level++ { + var baseTabels tFiles + if level < len(p.base.levels) { + baseTabels = p.base.levels[level] + } + + if level < len(p.levels) { + scratch := p.levels[level] + + var nt tFiles + // Prealloc list if possible. + if n := len(baseTabels) + len(scratch.added) - len(scratch.deleted); n > 0 { + nt = make(tFiles, 0, n) + } + + // Base tables. + for _, t := range baseTabels { + if _, ok := scratch.deleted[t.fd.Num]; ok { + continue + } + if _, ok := scratch.added[t.fd.Num]; ok { + continue + } + nt = append(nt, t) + } + + // New tables. + for _, r := range scratch.added { + nt = append(nt, tableFileFromRecord(r)) + } + + if len(nt) != 0 { + // Sort tables. + if level == 0 { + nt.sortByNum() + } else { + nt.sortByKey(p.base.s.icmp) + } + + nv.levels[level] = nt + } + } else { + nv.levels[level] = baseTabels + } + } + + // Trim levels. + n := len(nv.levels) + for ; n > 0 && nv.levels[n-1] == nil; n-- { + } + nv.levels = nv.levels[:n] + + // Compute compaction score for new version. + nv.computeCompaction() + + return nv +} + +type versionReleaser struct { + v *version + once bool +} + +func (vr *versionReleaser) Release() { + v := vr.v + v.s.vmu.Lock() + if !vr.once { + v.releaseNB() + vr.once = true + } + v.s.vmu.Unlock() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitattributes b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitattributes new file mode 100644 index 0000000..d2f212e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitattributes @@ -0,0 +1,10 @@ +# Treat all files in this repo as binary, with no git magic updating +# line endings. Windows users contributing to Go will need to use a +# modern version of git and editors capable of LF line endings. +# +# We'll prevent accidental CRLF line endings from entering the repo +# via the git-review gofmt checks. +# +# See golang.org/issue/9281 + +* -text diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitignore b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitignore new file mode 100644 index 0000000..8339fd6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/.gitignore @@ -0,0 +1,2 @@ +# Add no patterns to .hgignore except for files generated by the build. +last-change diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTING.md b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTING.md new file mode 100644 index 0000000..88dff59 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + + +## Filing issues + +When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +**We do not accept GitHub pull requests** +(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/PATENTS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/README b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/README new file mode 100644 index 0000000..f1e0cbf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/README @@ -0,0 +1,3 @@ +This repository holds supplementary Go cryptography libraries. + +To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/cast5/cast5.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/cast5/cast5.go new file mode 100644 index 0000000..0b4af37 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/cast5/cast5.go @@ -0,0 +1,526 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common +// OpenPGP cipher. +package cast5 // import "golang.org/x/crypto/cast5" + +import "errors" + +const BlockSize = 8 +const KeySize = 16 + +type Cipher struct { + masking [16]uint32 + rotate [16]uint8 +} + +func NewCipher(key []byte) (c *Cipher, err error) { + if len(key) != KeySize { + return nil, errors.New("CAST5: keys must be 16 bytes") + } + + c = new(Cipher) + c.keySchedule(key) + return +} + +func (c *Cipher) BlockSize() int { + return BlockSize +} + +func (c *Cipher) Encrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + + l, r = r, l^f1(r, c.masking[0], c.rotate[0]) + l, r = r, l^f2(r, c.masking[1], c.rotate[1]) + l, r = r, l^f3(r, c.masking[2], c.rotate[2]) + l, r = r, l^f1(r, c.masking[3], c.rotate[3]) + + l, r = r, l^f2(r, c.masking[4], c.rotate[4]) + l, r = r, l^f3(r, c.masking[5], c.rotate[5]) + l, r = r, l^f1(r, c.masking[6], c.rotate[6]) + l, r = r, l^f2(r, c.masking[7], c.rotate[7]) + + l, r = r, l^f3(r, c.masking[8], c.rotate[8]) + l, r = r, l^f1(r, c.masking[9], c.rotate[9]) + l, r = r, l^f2(r, c.masking[10], c.rotate[10]) + l, r = r, l^f3(r, c.masking[11], c.rotate[11]) + + l, r = r, l^f1(r, c.masking[12], c.rotate[12]) + l, r = r, l^f2(r, c.masking[13], c.rotate[13]) + l, r = r, l^f3(r, c.masking[14], c.rotate[14]) + l, r = r, l^f1(r, c.masking[15], c.rotate[15]) + + dst[0] = uint8(r >> 24) + dst[1] = uint8(r >> 16) + dst[2] = uint8(r >> 8) + dst[3] = uint8(r) + dst[4] = uint8(l >> 24) + dst[5] = uint8(l >> 16) + dst[6] = uint8(l >> 8) + dst[7] = uint8(l) +} + +func (c *Cipher) Decrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + + l, r = r, l^f1(r, c.masking[15], c.rotate[15]) + l, r = r, l^f3(r, c.masking[14], c.rotate[14]) + l, r = r, l^f2(r, c.masking[13], c.rotate[13]) + l, r = r, l^f1(r, c.masking[12], c.rotate[12]) + + l, r = r, l^f3(r, c.masking[11], c.rotate[11]) + l, r = r, l^f2(r, c.masking[10], c.rotate[10]) + l, r = r, l^f1(r, c.masking[9], c.rotate[9]) + l, r = r, l^f3(r, c.masking[8], c.rotate[8]) + + l, r = r, l^f2(r, c.masking[7], c.rotate[7]) + l, r = r, l^f1(r, c.masking[6], c.rotate[6]) + l, r = r, l^f3(r, c.masking[5], c.rotate[5]) + l, r = r, l^f2(r, c.masking[4], c.rotate[4]) + + l, r = r, l^f1(r, c.masking[3], c.rotate[3]) + l, r = r, l^f3(r, c.masking[2], c.rotate[2]) + l, r = r, l^f2(r, c.masking[1], c.rotate[1]) + l, r = r, l^f1(r, c.masking[0], c.rotate[0]) + + dst[0] = uint8(r >> 24) + dst[1] = uint8(r >> 16) + dst[2] = uint8(r >> 8) + dst[3] = uint8(r) + dst[4] = uint8(l >> 24) + dst[5] = uint8(l >> 16) + dst[6] = uint8(l >> 8) + dst[7] = uint8(l) +} + +type keyScheduleA [4][7]uint8 +type keyScheduleB [4][5]uint8 + +// keyScheduleRound contains the magic values for a round of the key schedule. +// The keyScheduleA deals with the lines like: +// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8] +// Conceptually, both x and z are in the same array, x first. The first +// element describes which word of this array gets written to and the +// second, which word gets read. So, for the line above, it's "4, 0", because +// it's writing to the first word of z, which, being after x, is word 4, and +// reading from the first word of x: word 0. +// +// Next are the indexes into the S-boxes. Now the array is treated as bytes. So +// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear +// that it's z that we're indexing. +// +// keyScheduleB deals with lines like: +// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2] +// "K1" is ignored because key words are always written in order. So the five +// elements are the S-box indexes. They use the same form as in keyScheduleA, +// above. + +type keyScheduleRound struct{} +type keySchedule []keyScheduleRound + +var schedule = []struct { + a keyScheduleA + b keyScheduleB +}{ + { + keyScheduleA{ + {4, 0, 0xd, 0xf, 0xc, 0xe, 0x8}, + {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, + {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, + {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, + }, + keyScheduleB{ + {16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2}, + {16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6}, + {16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9}, + {16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc}, + }, + }, + { + keyScheduleA{ + {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, + {1, 4, 0, 2, 1, 3, 16 + 2}, + {2, 5, 7, 6, 5, 4, 16 + 1}, + {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, + }, + keyScheduleB{ + {3, 2, 0xc, 0xd, 8}, + {1, 0, 0xe, 0xf, 0xd}, + {7, 6, 8, 9, 3}, + {5, 4, 0xa, 0xb, 7}, + }, + }, + { + keyScheduleA{ + {4, 0, 0xd, 0xf, 0xc, 0xe, 8}, + {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, + {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, + {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, + }, + keyScheduleB{ + {16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9}, + {16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc}, + {16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2}, + {16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6}, + }, + }, + { + keyScheduleA{ + {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, + {1, 4, 0, 2, 1, 3, 16 + 2}, + {2, 5, 7, 6, 5, 4, 16 + 1}, + {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, + }, + keyScheduleB{ + {8, 9, 7, 6, 3}, + {0xa, 0xb, 5, 4, 7}, + {0xc, 0xd, 3, 2, 8}, + {0xe, 0xf, 1, 0, 0xd}, + }, + }, +} + +func (c *Cipher) keySchedule(in []byte) { + var t [8]uint32 + var k [32]uint32 + + for i := 0; i < 4; i++ { + j := i * 4 + t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3]) + } + + x := []byte{6, 7, 4, 5} + ki := 0 + + for half := 0; half < 2; half++ { + for _, round := range schedule { + for j := 0; j < 4; j++ { + var a [7]uint8 + copy(a[:], round.a[j][:]) + w := t[a[1]] + w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff] + w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff] + w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff] + w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff] + w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff] + t[a[0]] = w + } + + for j := 0; j < 4; j++ { + var b [5]uint8 + copy(b[:], round.b[j][:]) + w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff] + w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff] + w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff] + w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff] + w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff] + k[ki] = w + ki++ + } + } + } + + for i := 0; i < 16; i++ { + c.masking[i] = k[i] + c.rotate[i] = uint8(k[16+i] & 0x1f) + } +} + +// These are the three 'f' functions. See RFC 2144, section 2.2. +func f1(d, m uint32, r uint8) uint32 { + t := m + d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] +} + +func f2(d, m uint32, r uint8) uint32 { + t := m ^ d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] +} + +func f3(d, m uint32, r uint8) uint32 { + t := m - d + I := (t << r) | (t >> (32 - r)) + return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] +} + +var sBox = [8][256]uint32{ + { + 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, + 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, + 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, + 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, + 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, + 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, + 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, + 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, + 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, + 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, + 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, + 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, + 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, + 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, + 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, + 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, + 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, + 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, + 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, + 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, + 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, + 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, + 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, + 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, + 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, + 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, + 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, + 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, + 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, + 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, + 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, + 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, + }, + { + 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, + 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, + 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, + 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, + 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, + 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, + 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, + 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, + 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, + 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, + 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, + 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, + 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, + 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, + 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, + 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, + 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, + 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, + 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, + 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, + 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, + 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, + 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, + 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, + 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, + 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, + 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, + 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, + 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, + 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, + 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, + 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, + }, + { + 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, + 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, + 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, + 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, + 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, + 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, + 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, + 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, + 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, + 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, + 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, + 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, + 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, + 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, + 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, + 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, + 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, + 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, + 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, + 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, + 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, + 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, + 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, + 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, + 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, + 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, + 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, + 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, + 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, + 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, + 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, + 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, + }, + { + 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, + 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, + 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, + 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, + 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, + 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, + 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, + 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, + 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, + 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, + 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, + 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, + 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, + 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, + 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, + 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, + 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, + 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, + 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, + 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, + 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, + 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, + 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, + 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, + 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, + 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, + 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, + 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, + 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, + 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, + 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, + 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, + }, + { + 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, + 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, + 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, + 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, + 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, + 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, + 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, + 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, + 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, + 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, + 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, + 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, + 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, + 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, + 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, + 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, + 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, + 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, + 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, + 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, + 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, + 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, + 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, + 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, + 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, + 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, + 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, + 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, + 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, + 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, + 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, + 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, + }, + { + 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, + 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, + 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, + 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, + 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, + 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, + 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, + 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, + 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, + 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, + 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, + 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, + 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, + 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, + 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, + 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, + 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, + 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, + 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, + 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, + 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, + 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, + 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, + 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, + 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, + 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, + 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, + 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, + 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, + 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, + 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, + 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, + }, + { + 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, + 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, + 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, + 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, + 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, + 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, + 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, + 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, + 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, + 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, + 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, + 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, + 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, + 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, + 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, + 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, + 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, + 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, + 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, + 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, + 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, + 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, + 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, + 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, + 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, + 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, + 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, + 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, + 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, + 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, + 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, + 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, + }, + { + 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, + 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, + 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, + 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, + 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, + 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, + 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, + 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, + 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, + 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, + 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, + 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, + 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, + 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, + 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, + 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, + 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, + 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, + 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, + 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, + 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, + 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, + 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, + 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, + 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, + 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, + 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, + 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, + 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, + 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, + 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, + 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, + }, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/codereview.cfg b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/codereview.cfg new file mode 100644 index 0000000..3f8b14b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/armor.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/armor.go new file mode 100644 index 0000000..592d186 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/armor.go @@ -0,0 +1,219 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is +// very similar to PEM except that it has an additional CRC checksum. +package armor // import "golang.org/x/crypto/openpgp/armor" + +import ( + "bufio" + "bytes" + "encoding/base64" + "golang.org/x/crypto/openpgp/errors" + "io" +) + +// A Block represents an OpenPGP armored structure. +// +// The encoded form is: +// -----BEGIN Type----- +// Headers +// +// base64-encoded Bytes +// '=' base64 encoded checksum +// -----END Type----- +// where Headers is a possibly empty sequence of Key: Value lines. +// +// Since the armored data can be very large, this package presents a streaming +// interface. +type Block struct { + Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). + Header map[string]string // Optional headers. + Body io.Reader // A Reader from which the contents can be read + lReader lineReader + oReader openpgpReader +} + +var ArmorCorrupt error = errors.StructuralError("armor invalid") + +const crc24Init = 0xb704ce +const crc24Poly = 0x1864cfb +const crc24Mask = 0xffffff + +// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 +func crc24(crc uint32, d []byte) uint32 { + for _, b := range d { + crc ^= uint32(b) << 16 + for i := 0; i < 8; i++ { + crc <<= 1 + if crc&0x1000000 != 0 { + crc ^= crc24Poly + } + } + } + return crc +} + +var armorStart = []byte("-----BEGIN ") +var armorEnd = []byte("-----END ") +var armorEndOfLine = []byte("-----") + +// lineReader wraps a line based reader. It watches for the end of an armor +// block and records the expected CRC value. +type lineReader struct { + in *bufio.Reader + buf []byte + eof bool + crc uint32 +} + +func (l *lineReader) Read(p []byte) (n int, err error) { + if l.eof { + return 0, io.EOF + } + + if len(l.buf) > 0 { + n = copy(p, l.buf) + l.buf = l.buf[n:] + return + } + + line, isPrefix, err := l.in.ReadLine() + if err != nil { + return + } + if isPrefix { + return 0, ArmorCorrupt + } + + if len(line) == 5 && line[0] == '=' { + // This is the checksum line + var expectedBytes [3]byte + var m int + m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) + if m != 3 || err != nil { + return + } + l.crc = uint32(expectedBytes[0])<<16 | + uint32(expectedBytes[1])<<8 | + uint32(expectedBytes[2]) + + line, _, err = l.in.ReadLine() + if err != nil && err != io.EOF { + return + } + if !bytes.HasPrefix(line, armorEnd) { + return 0, ArmorCorrupt + } + + l.eof = true + return 0, io.EOF + } + + if len(line) > 96 { + return 0, ArmorCorrupt + } + + n = copy(p, line) + bytesToSave := len(line) - n + if bytesToSave > 0 { + if cap(l.buf) < bytesToSave { + l.buf = make([]byte, 0, bytesToSave) + } + l.buf = l.buf[0:bytesToSave] + copy(l.buf, line[n:]) + } + + return +} + +// openpgpReader passes Read calls to the underlying base64 decoder, but keeps +// a running CRC of the resulting data and checks the CRC against the value +// found by the lineReader at EOF. +type openpgpReader struct { + lReader *lineReader + b64Reader io.Reader + currentCRC uint32 +} + +func (r *openpgpReader) Read(p []byte) (n int, err error) { + n, err = r.b64Reader.Read(p) + r.currentCRC = crc24(r.currentCRC, p[:n]) + + if err == io.EOF { + if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { + return 0, ArmorCorrupt + } + } + + return +} + +// Decode reads a PGP armored block from the given Reader. It will ignore +// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The +// given Reader is not usable after calling this function: an arbitrary amount +// of data may have been read past the end of the block. +func Decode(in io.Reader) (p *Block, err error) { + r := bufio.NewReaderSize(in, 100) + var line []byte + ignoreNext := false + +TryNextBlock: + p = nil + + // Skip leading garbage + for { + ignoreThis := ignoreNext + line, ignoreNext, err = r.ReadLine() + if err != nil { + return + } + if ignoreNext || ignoreThis { + continue + } + line = bytes.TrimSpace(line) + if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { + break + } + } + + p = new(Block) + p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) + p.Header = make(map[string]string) + nextIsContinuation := false + var lastKey string + + // Read headers + for { + isContinuation := nextIsContinuation + line, nextIsContinuation, err = r.ReadLine() + if err != nil { + p = nil + return + } + if isContinuation { + p.Header[lastKey] += string(line) + continue + } + line = bytes.TrimSpace(line) + if len(line) == 0 { + break + } + + i := bytes.Index(line, []byte(": ")) + if i == -1 { + goto TryNextBlock + } + lastKey = string(line[:i]) + p.Header[lastKey] = string(line[i+2:]) + } + + p.lReader.in = r + p.oReader.currentCRC = crc24Init + p.oReader.lReader = &p.lReader + p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) + p.Body = &p.oReader + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/encode.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/encode.go new file mode 100644 index 0000000..6f07582 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/armor/encode.go @@ -0,0 +1,160 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package armor + +import ( + "encoding/base64" + "io" +) + +var armorHeaderSep = []byte(": ") +var blockEnd = []byte("\n=") +var newline = []byte("\n") +var armorEndOfLineOut = []byte("-----\n") + +// writeSlices writes its arguments to the given Writer. +func writeSlices(out io.Writer, slices ...[]byte) (err error) { + for _, s := range slices { + _, err = out.Write(s) + if err != nil { + return err + } + } + return +} + +// lineBreaker breaks data across several lines, all of the same byte length +// (except possibly the last). Lines are broken with a single '\n'. +type lineBreaker struct { + lineLength int + line []byte + used int + out io.Writer + haveWritten bool +} + +func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { + return &lineBreaker{ + lineLength: lineLength, + line: make([]byte, lineLength), + used: 0, + out: out, + } +} + +func (l *lineBreaker) Write(b []byte) (n int, err error) { + n = len(b) + + if n == 0 { + return + } + + if l.used == 0 && l.haveWritten { + _, err = l.out.Write([]byte{'\n'}) + if err != nil { + return + } + } + + if l.used+len(b) < l.lineLength { + l.used += copy(l.line[l.used:], b) + return + } + + l.haveWritten = true + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + excess := l.lineLength - l.used + l.used = 0 + + _, err = l.out.Write(b[0:excess]) + if err != nil { + return + } + + _, err = l.Write(b[excess:]) + return +} + +func (l *lineBreaker) Close() (err error) { + if l.used > 0 { + _, err = l.out.Write(l.line[0:l.used]) + if err != nil { + return + } + } + + return +} + +// encoding keeps track of a running CRC24 over the data which has been written +// to it and outputs a OpenPGP checksum when closed, followed by an armor +// trailer. +// +// It's built into a stack of io.Writers: +// encoding -> base64 encoder -> lineBreaker -> out +type encoding struct { + out io.Writer + breaker *lineBreaker + b64 io.WriteCloser + crc uint32 + blockType []byte +} + +func (e *encoding) Write(data []byte) (n int, err error) { + e.crc = crc24(e.crc, data) + return e.b64.Write(data) +} + +func (e *encoding) Close() (err error) { + err = e.b64.Close() + if err != nil { + return + } + e.breaker.Close() + + var checksumBytes [3]byte + checksumBytes[0] = byte(e.crc >> 16) + checksumBytes[1] = byte(e.crc >> 8) + checksumBytes[2] = byte(e.crc) + + var b64ChecksumBytes [4]byte + base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) + + return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) +} + +// Encode returns a WriteCloser which will encode the data written to it in +// OpenPGP armor. +func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { + bType := []byte(blockType) + err = writeSlices(out, armorStart, bType, armorEndOfLineOut) + if err != nil { + return + } + + for k, v := range headers { + err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) + if err != nil { + return + } + } + + _, err = out.Write(newline) + if err != nil { + return + } + + e := &encoding{ + out: out, + breaker: newLineBreaker(out, 64), + crc: crc24Init, + blockType: bType, + } + e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) + return e, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/canonical_text.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/canonical_text.go new file mode 100644 index 0000000..e601e38 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/canonical_text.go @@ -0,0 +1,59 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import "hash" + +// NewCanonicalTextHash reformats text written to it into the canonical +// form and then applies the hash h. See RFC 4880, section 5.2.1. +func NewCanonicalTextHash(h hash.Hash) hash.Hash { + return &canonicalTextHash{h, 0} +} + +type canonicalTextHash struct { + h hash.Hash + s int +} + +var newline = []byte{'\r', '\n'} + +func (cth *canonicalTextHash) Write(buf []byte) (int, error) { + start := 0 + + for i, c := range buf { + switch cth.s { + case 0: + if c == '\r' { + cth.s = 1 + } else if c == '\n' { + cth.h.Write(buf[start:i]) + cth.h.Write(newline) + start = i + 1 + } + case 1: + cth.s = 0 + } + } + + cth.h.Write(buf[start:]) + return len(buf), nil +} + +func (cth *canonicalTextHash) Sum(in []byte) []byte { + return cth.h.Sum(in) +} + +func (cth *canonicalTextHash) Reset() { + cth.h.Reset() + cth.s = 0 +} + +func (cth *canonicalTextHash) Size() int { + return cth.h.Size() +} + +func (cth *canonicalTextHash) BlockSize() int { + return cth.h.BlockSize() +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go new file mode 100644 index 0000000..73f4fe3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go @@ -0,0 +1,122 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package elgamal implements ElGamal encryption, suitable for OpenPGP, +// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on +// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, +// n. 4, 1985, pp. 469-472. +// +// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it +// unsuitable for other protocols. RSA should be used in preference in any +// case. +package elgamal // import "golang.org/x/crypto/openpgp/elgamal" + +import ( + "crypto/rand" + "crypto/subtle" + "errors" + "io" + "math/big" +) + +// PublicKey represents an ElGamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents an ElGamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// Encrypt encrypts the given message to the given public key. The result is a +// pair of integers. Errors can result from reading random, or because msg is +// too large to be encrypted to the public key. +func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) { + pLen := (pub.P.BitLen() + 7) / 8 + if len(msg) > pLen-11 { + err = errors.New("elgamal: message too long") + return + } + + // EM = 0x02 || PS || 0x00 || M + em := make([]byte, pLen-1) + em[0] = 2 + ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + err = nonZeroRandomBytes(ps, random) + if err != nil { + return + } + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + + m := new(big.Int).SetBytes(em) + + k, err := rand.Int(random, pub.P) + if err != nil { + return + } + + c1 = new(big.Int).Exp(pub.G, k, pub.P) + s := new(big.Int).Exp(pub.Y, k, pub.P) + c2 = s.Mul(s, m) + c2.Mod(c2, pub.P) + + return +} + +// Decrypt takes two integers, resulting from an ElGamal encryption, and +// returns the plaintext of the message. An error can result only if the +// ciphertext is invalid. Users should keep in mind that this is a padding +// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can +// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks +// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel +// Bleichenbacher, Advances in Cryptology (Crypto '98), +func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { + s := new(big.Int).Exp(c1, priv.X, priv.P) + s.ModInverse(s, priv.P) + s.Mul(s, c2) + s.Mod(s, priv.P) + em := s.Bytes() + + firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) + + // The remainder of the plaintext must be a string of non-zero random + // octets, followed by a 0, followed by the message. + // lookingForIndex: 1 iff we are still looking for the zero. + // index: the offset of the first zero byte. + var lookingForIndex, index int + lookingForIndex = 1 + + for i := 1; i < len(em); i++ { + equals0 := subtle.ConstantTimeByteEq(em[i], 0) + index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) + } + + if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { + return nil, errors.New("elgamal: decryption error") + } + return em[index+1:], nil +} + +// nonZeroRandomBytes fills the given slice with non-zero random octets. +func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { + _, err = io.ReadFull(rand, s) + if err != nil { + return + } + + for i := 0; i < len(s); i++ { + for s[i] == 0 { + _, err = io.ReadFull(rand, s[i:i+1]) + if err != nil { + return + } + } + } + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/errors/errors.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/errors/errors.go new file mode 100644 index 0000000..eb0550b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/errors/errors.go @@ -0,0 +1,72 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errors contains common error types for the OpenPGP packages. +package errors // import "golang.org/x/crypto/openpgp/errors" + +import ( + "strconv" +) + +// A StructuralError is returned when OpenPGP data is found to be syntactically +// invalid. +type StructuralError string + +func (s StructuralError) Error() string { + return "openpgp: invalid data: " + string(s) +} + +// UnsupportedError indicates that, although the OpenPGP data is valid, it +// makes use of currently unimplemented features. +type UnsupportedError string + +func (s UnsupportedError) Error() string { + return "openpgp: unsupported feature: " + string(s) +} + +// InvalidArgumentError indicates that the caller is in error and passed an +// incorrect value. +type InvalidArgumentError string + +func (i InvalidArgumentError) Error() string { + return "openpgp: invalid argument: " + string(i) +} + +// SignatureError indicates that a syntactically valid signature failed to +// validate. +type SignatureError string + +func (b SignatureError) Error() string { + return "openpgp: invalid signature: " + string(b) +} + +type keyIncorrectError int + +func (ki keyIncorrectError) Error() string { + return "openpgp: incorrect key" +} + +var ErrKeyIncorrect error = keyIncorrectError(0) + +type unknownIssuerError int + +func (unknownIssuerError) Error() string { + return "openpgp: signature made by unknown entity" +} + +var ErrUnknownIssuer error = unknownIssuerError(0) + +type keyRevokedError int + +func (keyRevokedError) Error() string { + return "openpgp: signature made by revoked key" +} + +var ErrKeyRevoked error = keyRevokedError(0) + +type UnknownPacketTypeError uint8 + +func (upte UnknownPacketTypeError) Error() string { + return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/keys.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/keys.go new file mode 100644 index 0000000..68b14c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/keys.go @@ -0,0 +1,637 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "crypto/rsa" + "io" + "time" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" +) + +// PublicKeyType is the armor type for a PGP public key. +var PublicKeyType = "PGP PUBLIC KEY BLOCK" + +// PrivateKeyType is the armor type for a PGP private key. +var PrivateKeyType = "PGP PRIVATE KEY BLOCK" + +// An Entity represents the components of an OpenPGP key: a primary public key +// (which must be a signing key), one or more identities claimed by that key, +// and zero or more subkeys, which may be encryption keys. +type Entity struct { + PrimaryKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Identities map[string]*Identity // indexed by Identity.Name + Revocations []*packet.Signature + Subkeys []Subkey +} + +// An Identity represents an identity claimed by an Entity and zero or more +// assertions by other entities about that claim. +type Identity struct { + Name string // by convention, has the form "Full Name (comment) " + UserId *packet.UserId + SelfSignature *packet.Signature + Signatures []*packet.Signature +} + +// A Subkey is an additional public key in an Entity. Subkeys can be used for +// encryption. +type Subkey struct { + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Sig *packet.Signature +} + +// A Key identifies a specific public key in an Entity. This is either the +// Entity's primary key or a subkey. +type Key struct { + Entity *Entity + PublicKey *packet.PublicKey + PrivateKey *packet.PrivateKey + SelfSignature *packet.Signature +} + +// A KeyRing provides access to public and private keys. +type KeyRing interface { + // KeysById returns the set of keys that have the given key id. + KeysById(id uint64) []Key + // KeysByIdAndUsage returns the set of keys with the given id + // that also meet the key usage given by requiredUsage. + // The requiredUsage is expressed as the bitwise-OR of + // packet.KeyFlag* values. + KeysByIdUsage(id uint64, requiredUsage byte) []Key + // DecryptionKeys returns all private keys that are valid for + // decryption. + DecryptionKeys() []Key +} + +// primaryIdentity returns the Identity marked as primary or the first identity +// if none are so marked. +func (e *Entity) primaryIdentity() *Identity { + var firstIdentity *Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + return ident + } + } + return firstIdentity +} + +// encryptionKey returns the best candidate Key for encrypting a message to the +// given Entity. +func (e *Entity) encryptionKey(now time.Time) (Key, bool) { + candidateSubkey := -1 + + // Iterate the keys to find the newest key + var maxTime time.Time + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagEncryptCommunications && + subkey.PublicKey.PubKeyAlgo.CanEncrypt() && + !subkey.Sig.KeyExpired(now) && + (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { + candidateSubkey = i + maxTime = subkey.Sig.CreationTime + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && + e.PrimaryKey.PubKeyAlgo.CanEncrypt() && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + // This Entity appears to be signing only. + return Key{}, false +} + +// signingKey return the best candidate Key for signing a message with this +// Entity. +func (e *Entity) signingKey(now time.Time) (Key, bool) { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && + subkey.Sig.FlagSign && + subkey.PublicKey.PubKeyAlgo.CanSign() && + !subkey.Sig.KeyExpired(now) { + candidateSubkey = i + break + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true + } + + // If we have no candidate subkey then we assume that it's ok to sign + // with the primary key. + i := e.primaryIdentity() + if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && + !i.SelfSignature.KeyExpired(now) { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true + } + + return Key{}, false +} + +// An EntityList contains one or more Entities. +type EntityList []*Entity + +// KeysById returns the set of keys that have the given key id. +func (el EntityList) KeysById(id uint64) (keys []Key) { + for _, e := range el { + if e.PrimaryKey.KeyId == id { + var selfSig *packet.Signature + for _, ident := range e.Identities { + if selfSig == nil { + selfSig = ident.SelfSignature + } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + selfSig = ident.SelfSignature + break + } + } + keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) + } + + for _, subKey := range e.Subkeys { + if subKey.PublicKey.KeyId == id { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// KeysByIdAndUsage returns the set of keys with the given id that also meet +// the key usage given by requiredUsage. The requiredUsage is expressed as +// the bitwise-OR of packet.KeyFlag* values. +func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) { + for _, key := range el.KeysById(id) { + if len(key.Entity.Revocations) > 0 { + continue + } + + if key.SelfSignature.RevocationReason != nil { + continue + } + + if key.SelfSignature.FlagsValid && requiredUsage != 0 { + var usage byte + if key.SelfSignature.FlagCertify { + usage |= packet.KeyFlagCertify + } + if key.SelfSignature.FlagSign { + usage |= packet.KeyFlagSign + } + if key.SelfSignature.FlagEncryptCommunications { + usage |= packet.KeyFlagEncryptCommunications + } + if key.SelfSignature.FlagEncryptStorage { + usage |= packet.KeyFlagEncryptStorage + } + if usage&requiredUsage != requiredUsage { + continue + } + } + + keys = append(keys, key) + } + return +} + +// DecryptionKeys returns all private keys that are valid for decryption. +func (el EntityList) DecryptionKeys() (keys []Key) { + for _, e := range el { + for _, subKey := range e.Subkeys { + if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { + keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) + } + } + } + return +} + +// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. +func ReadArmoredKeyRing(r io.Reader) (EntityList, error) { + block, err := armor.Decode(r) + if err == io.EOF { + return nil, errors.InvalidArgumentError("no armored data found") + } + if err != nil { + return nil, err + } + if block.Type != PublicKeyType && block.Type != PrivateKeyType { + return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type) + } + + return ReadKeyRing(block.Body) +} + +// ReadKeyRing reads one or more public/private keys. Unsupported keys are +// ignored as long as at least a single valid key is found. +func ReadKeyRing(r io.Reader) (el EntityList, err error) { + packets := packet.NewReader(r) + var lastUnsupportedError error + + for { + var e *Entity + e, err = ReadEntity(packets) + if err != nil { + // TODO: warn about skipped unsupported/unreadable keys + if _, ok := err.(errors.UnsupportedError); ok { + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } else if _, ok := err.(errors.StructuralError); ok { + // Skip unreadable, badly-formatted keys + lastUnsupportedError = err + err = readToNextPublicKey(packets) + } + if err == io.EOF { + err = nil + break + } + if err != nil { + el = nil + break + } + } else { + el = append(el, e) + } + } + + if len(el) == 0 && err == nil { + err = lastUnsupportedError + } + return +} + +// readToNextPublicKey reads packets until the start of the entity and leaves +// the first packet of the new entity in the Reader. +func readToNextPublicKey(packets *packet.Reader) (err error) { + var p packet.Packet + for { + p, err = packets.Next() + if err == io.EOF { + return + } else if err != nil { + if _, ok := err.(errors.UnsupportedError); ok { + err = nil + continue + } + return + } + + if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { + packets.Unread(p) + return + } + } +} + +// ReadEntity reads an entity (public key, identities, subkeys etc) from the +// given Reader. +func ReadEntity(packets *packet.Reader) (*Entity, error) { + e := new(Entity) + e.Identities = make(map[string]*Identity) + + p, err := packets.Next() + if err != nil { + return nil, err + } + + var ok bool + if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { + if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { + packets.Unread(p) + return nil, errors.StructuralError("first packet was not a public/private key") + } else { + e.PrimaryKey = &e.PrivateKey.PublicKey + } + } + + if !e.PrimaryKey.PubKeyAlgo.CanSign() { + return nil, errors.StructuralError("primary key cannot be used for signatures") + } + + var current *Identity + var revocations []*packet.Signature +EachPacket: + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + switch pkt := p.(type) { + case *packet.UserId: + current = new(Identity) + current.Name = pkt.Id + current.UserId = pkt + e.Identities[pkt.Id] = current + + for { + p, err = packets.Next() + if err == io.EOF { + return nil, io.ErrUnexpectedEOF + } else if err != nil { + return nil, err + } + + sig, ok := p.(*packet.Signature) + if !ok { + return nil, errors.StructuralError("user ID packet not followed by self-signature") + } + + if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { + if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { + return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error()) + } + current.SelfSignature = sig + break + } + current.Signatures = append(current.Signatures, sig) + } + case *packet.Signature: + if pkt.SigType == packet.SigTypeKeyRevocation { + revocations = append(revocations, pkt) + } else if pkt.SigType == packet.SigTypeDirectSignature { + // TODO: RFC4880 5.2.1 permits signatures + // directly on keys (eg. to bind additional + // revocation keys). + } else if current == nil { + return nil, errors.StructuralError("signature packet found before user id packet") + } else { + current.Signatures = append(current.Signatures, pkt) + } + case *packet.PrivateKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, &pkt.PublicKey, pkt) + if err != nil { + return nil, err + } + case *packet.PublicKey: + if pkt.IsSubkey == false { + packets.Unread(p) + break EachPacket + } + err = addSubkey(e, packets, pkt, nil) + if err != nil { + return nil, err + } + default: + // we ignore unknown packets + } + } + + if len(e.Identities) == 0 { + return nil, errors.StructuralError("entity without any identities") + } + + for _, revocation := range revocations { + err = e.PrimaryKey.VerifyRevocationSignature(revocation) + if err == nil { + e.Revocations = append(e.Revocations, revocation) + } else { + // TODO: RFC 4880 5.2.3.15 defines revocation keys. + return nil, errors.StructuralError("revocation signature signed by alternate key") + } + } + + return e, nil +} + +func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { + var subKey Subkey + subKey.PublicKey = pub + subKey.PrivateKey = priv + p, err := packets.Next() + if err == io.EOF { + return io.ErrUnexpectedEOF + } + if err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + var ok bool + subKey.Sig, ok = p.(*packet.Signature) + if !ok { + return errors.StructuralError("subkey packet not followed by signature") + } + if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation { + return errors.StructuralError("subkey signature with wrong type") + } + err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig) + if err != nil { + return errors.StructuralError("subkey signature invalid: " + err.Error()) + } + e.Subkeys = append(e.Subkeys, subKey) + return nil +} + +const defaultRSAKeyBits = 2048 + +// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a +// single identity composed of the given full name, comment and email, any of +// which may be empty but must not contain any of "()<>\x00". +// If config is nil, sensible defaults will be used. +func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { + currentTime := config.Now() + + bits := defaultRSAKeyBits + if config != nil && config.RSABits != 0 { + bits = config.RSABits + } + + uid := packet.NewUserId(name, comment, email) + if uid == nil { + return nil, errors.InvalidArgumentError("user id field contained invalid characters") + } + signingPriv, err := rsa.GenerateKey(config.Random(), bits) + if err != nil { + return nil, err + } + encryptingPriv, err := rsa.GenerateKey(config.Random(), bits) + if err != nil { + return nil, err + } + + e := &Entity{ + PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey), + PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv), + Identities: make(map[string]*Identity), + } + isPrimaryId := true + e.Identities[uid.Id] = &Identity{ + Name: uid.Name, + UserId: uid, + SelfSignature: &packet.Signature{ + CreationTime: currentTime, + SigType: packet.SigTypePositiveCert, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + IsPrimaryId: &isPrimaryId, + FlagsValid: true, + FlagSign: true, + FlagCertify: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + } + + // If the user passes in a DefaultHash via packet.Config, + // set the PreferredHash for the SelfSignature. + if config != nil && config.DefaultHash != 0 { + e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)} + } + + e.Subkeys = make([]Subkey, 1) + e.Subkeys[0] = Subkey{ + PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey), + PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv), + Sig: &packet.Signature{ + CreationTime: currentTime, + SigType: packet.SigTypeSubkeyBinding, + PubKeyAlgo: packet.PubKeyAlgoRSA, + Hash: config.Hash(), + FlagsValid: true, + FlagEncryptStorage: true, + FlagEncryptCommunications: true, + IssuerKeyId: &e.PrimaryKey.KeyId, + }, + } + e.Subkeys[0].PublicKey.IsSubkey = true + e.Subkeys[0].PrivateKey.IsSubkey = true + + return e, nil +} + +// SerializePrivate serializes an Entity, including private key material, to +// the given Writer. For now, it must only be used on an Entity returned from +// NewEntity. +// If config is nil, sensible defaults will be used. +func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { + err = e.PrivateKey.Serialize(w) + if err != nil { + return + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return + } + err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) + if err != nil { + return + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return + } + } + for _, subkey := range e.Subkeys { + err = subkey.PrivateKey.Serialize(w) + if err != nil { + return + } + err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) + if err != nil { + return + } + err = subkey.Sig.Serialize(w) + if err != nil { + return + } + } + return nil +} + +// Serialize writes the public part of the given Entity to w. (No private +// key material will be output). +func (e *Entity) Serialize(w io.Writer) error { + err := e.PrimaryKey.Serialize(w) + if err != nil { + return err + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return err + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return err + } + for _, sig := range ident.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { + err = subkey.PublicKey.Serialize(w) + if err != nil { + return err + } + err = subkey.Sig.Serialize(w) + if err != nil { + return err + } + } + return nil +} + +// SignIdentity adds a signature to e, from signer, attesting that identity is +// associated with e. The provided identity must already be an element of +// e.Identities and the private key of signer must have been decrypted if +// necessary. +// If config is nil, sensible defaults will be used. +func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { + if signer.PrivateKey == nil { + return errors.InvalidArgumentError("signing Entity must have a private key") + } + if signer.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing Entity's private key must be decrypted") + } + ident, ok := e.Identities[identity] + if !ok { + return errors.InvalidArgumentError("given identity string not found in Entity") + } + + sig := &packet.Signature{ + SigType: packet.SigTypeGenericCert, + PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, + Hash: config.Hash(), + CreationTime: config.Now(), + IssuerKeyId: &signer.PrivateKey.KeyId, + } + if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil { + return err + } + ident.Signatures = append(ident.Signatures, sig) + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/compressed.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/compressed.go new file mode 100644 index 0000000..e8f0b5c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/compressed.go @@ -0,0 +1,123 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "compress/bzip2" + "compress/flate" + "compress/zlib" + "golang.org/x/crypto/openpgp/errors" + "io" + "strconv" +) + +// Compressed represents a compressed OpenPGP packet. The decompressed contents +// will contain more OpenPGP packets. See RFC 4880, section 5.6. +type Compressed struct { + Body io.Reader +} + +const ( + NoCompression = flate.NoCompression + BestSpeed = flate.BestSpeed + BestCompression = flate.BestCompression + DefaultCompression = flate.DefaultCompression +) + +// CompressionConfig contains compressor configuration settings. +type CompressionConfig struct { + // Level is the compression level to use. It must be set to + // between -1 and 9, with -1 causing the compressor to use the + // default compression level, 0 causing the compressor to use + // no compression and 1 to 9 representing increasing (better, + // slower) compression levels. If Level is less than -1 or + // more then 9, a non-nil error will be returned during + // encryption. See the constants above for convenient common + // settings for Level. + Level int +} + +func (c *Compressed) parse(r io.Reader) error { + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + + switch buf[0] { + case 1: + c.Body = flate.NewReader(r) + case 2: + c.Body, err = zlib.NewReader(r) + case 3: + c.Body = bzip2.NewReader(r) + default: + err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) + } + + return err +} + +// compressedWriterCloser represents the serialized compression stream +// header and the compressor. Its Close() method ensures that both the +// compressor and serialized stream header are closed. Its Write() +// method writes to the compressor. +type compressedWriteCloser struct { + sh io.Closer // Stream Header + c io.WriteCloser // Compressor +} + +func (cwc compressedWriteCloser) Write(p []byte) (int, error) { + return cwc.c.Write(p) +} + +func (cwc compressedWriteCloser) Close() (err error) { + err = cwc.c.Close() + if err != nil { + return err + } + + return cwc.sh.Close() +} + +// SerializeCompressed serializes a compressed data packet to w and +// returns a WriteCloser to which the literal data packets themselves +// can be written and which MUST be closed on completion. If cc is +// nil, sensible defaults will be used to configure the compression +// algorithm. +func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { + compressed, err := serializeStreamHeader(w, packetTypeCompressed) + if err != nil { + return + } + + _, err = compressed.Write([]byte{uint8(algo)}) + if err != nil { + return + } + + level := DefaultCompression + if cc != nil { + level = cc.Level + } + + var compressor io.WriteCloser + switch algo { + case CompressionZIP: + compressor, err = flate.NewWriter(compressed, level) + case CompressionZLIB: + compressor, err = zlib.NewWriterLevel(compressed, level) + default: + s := strconv.Itoa(int(algo)) + err = errors.UnsupportedError("Unsupported compression algorithm: " + s) + } + if err != nil { + return + } + + literaldata = compressedWriteCloser{compressed, compressor} + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/config.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/config.go new file mode 100644 index 0000000..c76eecc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/config.go @@ -0,0 +1,91 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/rand" + "io" + "time" +) + +// Config collects a number of parameters along with sensible defaults. +// A nil *Config is valid and results in all default values. +type Config struct { + // Rand provides the source of entropy. + // If nil, the crypto/rand Reader is used. + Rand io.Reader + // DefaultHash is the default hash function to be used. + // If zero, SHA-256 is used. + DefaultHash crypto.Hash + // DefaultCipher is the cipher to be used. + // If zero, AES-128 is used. + DefaultCipher CipherFunction + // Time returns the current time as the number of seconds since the + // epoch. If Time is nil, time.Now is used. + Time func() time.Time + // DefaultCompressionAlgo is the compression algorithm to be + // applied to the plaintext before encryption. If zero, no + // compression is done. + DefaultCompressionAlgo CompressionAlgo + // CompressionConfig configures the compression settings. + CompressionConfig *CompressionConfig + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 1024 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 65536 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. When set, it is strongly encrouraged to + // use a value that is at least 65536. See RFC 4880 Section + // 3.7.1.3. + S2KCount int + // RSABits is the number of bits in new RSA keys made with NewEntity. + // If zero, then 2048 bit keys are created. + RSABits int +} + +func (c *Config) Random() io.Reader { + if c == nil || c.Rand == nil { + return rand.Reader + } + return c.Rand +} + +func (c *Config) Hash() crypto.Hash { + if c == nil || uint(c.DefaultHash) == 0 { + return crypto.SHA256 + } + return c.DefaultHash +} + +func (c *Config) Cipher() CipherFunction { + if c == nil || uint8(c.DefaultCipher) == 0 { + return CipherAES128 + } + return c.DefaultCipher +} + +func (c *Config) Now() time.Time { + if c == nil || c.Time == nil { + return time.Now() + } + return c.Time() +} + +func (c *Config) Compression() CompressionAlgo { + if c == nil { + return CompressionNone + } + return c.DefaultCompressionAlgo +} + +func (c *Config) PasswordHashIterations() int { + if c == nil || c.S2KCount == 0 { + return 0 + } + return c.S2KCount +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go new file mode 100644 index 0000000..266840d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go @@ -0,0 +1,199 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto/rsa" + "encoding/binary" + "io" + "math/big" + "strconv" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" +) + +const encryptedKeyVersion = 3 + +// EncryptedKey represents a public-key encrypted session key. See RFC 4880, +// section 5.1. +type EncryptedKey struct { + KeyId uint64 + Algo PublicKeyAlgorithm + CipherFunc CipherFunction // only valid after a successful Decrypt + Key []byte // only valid after a successful Decrypt + + encryptedMPI1, encryptedMPI2 parsedMPI +} + +func (e *EncryptedKey) parse(r io.Reader) (err error) { + var buf [10]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != encryptedKeyVersion { + return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) + } + e.KeyId = binary.BigEndian.Uint64(buf[1:9]) + e.Algo = PublicKeyAlgorithm(buf[9]) + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) + case PubKeyAlgoElGamal: + e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) + if err != nil { + return + } + e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r) + } + _, err = consumeAll(r) + return +} + +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) + } + return checksum +} + +// Decrypt decrypts an encrypted session key with the given private key. The +// private key must have been decrypted first. +// If config is nil, sensible defaults will be used. +func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { + var err error + var b []byte + + // TODO(agl): use session key decryption routines here to avoid + // padding oracle attacks. + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes) + case PubKeyAlgoElGamal: + c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) + c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) + b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) + default: + err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + + if err != nil { + return err + } + + e.CipherFunc = CipherFunction(b[0]) + e.Key = b[1 : len(b)-2] + expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) + checksum := checksumKeyMaterial(e.Key) + if checksum != expectedChecksum { + return errors.StructuralError("EncryptedKey checksum incorrect") + } + + return nil +} + +// Serialize writes the encrypted key packet, e, to w. +func (e *EncryptedKey) Serialize(w io.Writer) error { + var mpiLen int + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + mpiLen = 2 + len(e.encryptedMPI1.bytes) + case PubKeyAlgoElGamal: + mpiLen = 2 + len(e.encryptedMPI1.bytes) + 2 + len(e.encryptedMPI2.bytes) + default: + return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) + } + + serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) + + w.Write([]byte{encryptedKeyVersion}) + binary.Write(w, binary.BigEndian, e.KeyId) + w.Write([]byte{byte(e.Algo)}) + + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + writeMPIs(w, e.encryptedMPI1) + case PubKeyAlgoElGamal: + writeMPIs(w, e.encryptedMPI1, e.encryptedMPI2) + default: + panic("internal error") + } + + return nil +} + +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { + var buf [10]byte + buf[0] = encryptedKeyVersion + binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) + buf[9] = byte(pub.PubKeyAlgo) + + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) + keyBlock[0] = byte(cipherFunc) + copy(keyBlock[1:], key) + checksum := checksumKeyMaterial(key) + keyBlock[1+len(key)] = byte(checksum >> 8) + keyBlock[1+len(key)+1] = byte(checksum) + + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + case PubKeyAlgoElGamal: + return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: + return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) + } + + return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error { + cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) + } + + packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + return writeMPI(w, 8*uint16(len(cipherText)), cipherText) +} + +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { + c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error()) + } + + packetLen := 10 /* header length */ + packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 + packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + err = writeBig(w, c1) + if err != nil { + return err + } + return writeBig(w, c2) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/literal.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/literal.go new file mode 100644 index 0000000..1a9ec6e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/literal.go @@ -0,0 +1,89 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "encoding/binary" + "io" +) + +// LiteralData represents an encrypted file. See RFC 4880, section 5.9. +type LiteralData struct { + IsBinary bool + FileName string + Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. + Body io.Reader +} + +// ForEyesOnly returns whether the contents of the LiteralData have been marked +// as especially sensitive. +func (l *LiteralData) ForEyesOnly() bool { + return l.FileName == "_CONSOLE" +} + +func (l *LiteralData) parse(r io.Reader) (err error) { + var buf [256]byte + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + + l.IsBinary = buf[0] == 'b' + fileNameLen := int(buf[1]) + + _, err = readFull(r, buf[:fileNameLen]) + if err != nil { + return + } + + l.FileName = string(buf[:fileNameLen]) + + _, err = readFull(r, buf[:4]) + if err != nil { + return + } + + l.Time = binary.BigEndian.Uint32(buf[:4]) + l.Body = r + return +} + +// SerializeLiteral serializes a literal data packet to w and returns a +// WriteCloser to which the data itself can be written and which MUST be closed +// on completion. The fileName is truncated to 255 bytes. +func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { + var buf [4]byte + buf[0] = 't' + if isBinary { + buf[0] = 'b' + } + if len(fileName) > 255 { + fileName = fileName[:255] + } + buf[1] = byte(len(fileName)) + + inner, err := serializeStreamHeader(w, packetTypeLiteralData) + if err != nil { + return + } + + _, err = inner.Write(buf[:2]) + if err != nil { + return + } + _, err = inner.Write([]byte(fileName)) + if err != nil { + return + } + binary.BigEndian.PutUint32(buf[:], time) + _, err = inner.Write(buf[:]) + if err != nil { + return + } + + plaintext = inner + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go new file mode 100644 index 0000000..ce2a33a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/ocfb.go @@ -0,0 +1,143 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 + +package packet + +import ( + "crypto/cipher" +) + +type ocfbEncrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// An OCFBResyncOption determines if the "resynchronization step" of OCFB is +// performed. +type OCFBResyncOption bool + +const ( + OCFBResync OCFBResyncOption = true + OCFBNoResync OCFBResyncOption = false +) + +// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block, and an initial amount of +// ciphertext. randData must be random bytes and be the same length as the +// cipher.Block's block size. Resync determines if the "resynchronization step" +// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on +// this point. +func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) { + blockSize := block.BlockSize() + if len(randData) != blockSize { + return nil, nil + } + + x := &ocfbEncrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefix := make([]byte, blockSize+2) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefix[i] = randData[i] ^ x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] + prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + return x, prefix +} + +func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + x.fre[x.outUsed] ^= src[i] + dst[i] = x.fre[x.outUsed] + x.outUsed++ + } +} + +type ocfbDecrypter struct { + b cipher.Block + fre []byte + outUsed int +} + +// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's +// cipher feedback mode using the given cipher.Block. Prefix must be the first +// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's +// block size. If an incorrect key is detected then nil is returned. On +// successful exit, blockSize+2 bytes of decrypted data are written into +// prefix. Resync determines if the "resynchronization step" from RFC 4880, +// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. +func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { + blockSize := block.BlockSize() + if len(prefix) != blockSize+2 { + return nil + } + + x := &ocfbDecrypter{ + b: block, + fre: make([]byte, blockSize), + outUsed: 0, + } + prefixCopy := make([]byte, len(prefix)) + copy(prefixCopy, prefix) + + block.Encrypt(x.fre, x.fre) + for i := 0; i < blockSize; i++ { + prefixCopy[i] ^= x.fre[i] + } + + block.Encrypt(x.fre, prefix[:blockSize]) + prefixCopy[blockSize] ^= x.fre[0] + prefixCopy[blockSize+1] ^= x.fre[1] + + if prefixCopy[blockSize-2] != prefixCopy[blockSize] || + prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { + return nil + } + + if resync { + block.Encrypt(x.fre, prefix[2:]) + } else { + x.fre[0] = prefix[blockSize] + x.fre[1] = prefix[blockSize+1] + x.outUsed = 2 + } + copy(prefix, prefixCopy) + return x +} + +func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { + for i := 0; i < len(src); i++ { + if x.outUsed == len(x.fre) { + x.b.Encrypt(x.fre, x.fre) + x.outUsed = 0 + } + + c := src[i] + dst[i] = x.fre[x.outUsed] ^ src[i] + x.fre[x.outUsed] = c + x.outUsed++ + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go new file mode 100644 index 0000000..1713503 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "encoding/binary" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" + "io" + "strconv" +) + +// OnePassSignature represents a one-pass signature packet. See RFC 4880, +// section 5.4. +type OnePassSignature struct { + SigType SignatureType + Hash crypto.Hash + PubKeyAlgo PublicKeyAlgorithm + KeyId uint64 + IsLast bool +} + +const onePassSignatureVersion = 3 + +func (ops *OnePassSignature) parse(r io.Reader) (err error) { + var buf [13]byte + + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != onePassSignatureVersion { + err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) + } + + var ok bool + ops.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) + } + + ops.SigType = SignatureType(buf[1]) + ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) + ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) + ops.IsLast = buf[12] != 0 + return +} + +// Serialize marshals the given OnePassSignature to w. +func (ops *OnePassSignature) Serialize(w io.Writer) error { + var buf [13]byte + buf[0] = onePassSignatureVersion + buf[1] = uint8(ops.SigType) + var ok bool + buf[2], ok = s2k.HashToHashId(ops.Hash) + if !ok { + return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) + } + buf[3] = uint8(ops.PubKeyAlgo) + binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) + if ops.IsLast { + buf[12] = 1 + } + + if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + return err + } + _, err := w.Write(buf[:]) + return err +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/opaque.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/opaque.go new file mode 100644 index 0000000..456d807 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/opaque.go @@ -0,0 +1,162 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "io" + "io/ioutil" + + "golang.org/x/crypto/openpgp/errors" +) + +// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is +// useful for splitting and storing the original packet contents separately, +// handling unsupported packet types or accessing parts of the packet not yet +// implemented by this package. +type OpaquePacket struct { + // Packet type + Tag uint8 + // Reason why the packet was parsed opaquely + Reason error + // Binary contents of the packet data + Contents []byte +} + +func (op *OpaquePacket) parse(r io.Reader) (err error) { + op.Contents, err = ioutil.ReadAll(r) + return +} + +// Serialize marshals the packet to a writer in its original form, including +// the packet header. +func (op *OpaquePacket) Serialize(w io.Writer) (err error) { + err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) + if err == nil { + _, err = w.Write(op.Contents) + } + return +} + +// Parse attempts to parse the opaque contents into a structure supported by +// this package. If the packet is not known then the result will be another +// OpaquePacket. +func (op *OpaquePacket) Parse() (p Packet, err error) { + hdr := bytes.NewBuffer(nil) + err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) + if err != nil { + op.Reason = err + return op, err + } + p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) + if err != nil { + op.Reason = err + p = op + } + return +} + +// OpaqueReader reads OpaquePackets from an io.Reader. +type OpaqueReader struct { + r io.Reader +} + +func NewOpaqueReader(r io.Reader) *OpaqueReader { + return &OpaqueReader{r: r} +} + +// Read the next OpaquePacket. +func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { + tag, _, contents, err := readHeader(or.r) + if err != nil { + return + } + op = &OpaquePacket{Tag: uint8(tag), Reason: err} + err = op.parse(contents) + if err != nil { + consumeAll(contents) + } + return +} + +// OpaqueSubpacket represents an unparsed OpenPGP subpacket, +// as found in signature and user attribute packets. +type OpaqueSubpacket struct { + SubType uint8 + Contents []byte +} + +// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from +// their byte representation. +func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { + var ( + subHeaderLen int + subPacket *OpaqueSubpacket + ) + for len(contents) > 0 { + subHeaderLen, subPacket, err = nextSubpacket(contents) + if err != nil { + break + } + result = append(result, subPacket) + contents = contents[subHeaderLen+len(subPacket.Contents):] + } + return +} + +func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { + // RFC 4880, section 5.2.3.1 + var subLen uint32 + if len(contents) < 1 { + goto Truncated + } + subPacket = &OpaqueSubpacket{} + switch { + case contents[0] < 192: + subHeaderLen = 2 // 1 length byte, 1 subtype byte + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]) + contents = contents[1:] + case contents[0] < 255: + subHeaderLen = 3 // 2 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 + contents = contents[2:] + default: + subHeaderLen = 6 // 5 length bytes, 1 subtype + if len(contents) < subHeaderLen { + goto Truncated + } + subLen = uint32(contents[1])<<24 | + uint32(contents[2])<<16 | + uint32(contents[3])<<8 | + uint32(contents[4]) + contents = contents[5:] + } + if subLen > uint32(len(contents)) || subLen == 0 { + goto Truncated + } + subPacket.SubType = contents[0] + subPacket.Contents = contents[1:subLen] + return +Truncated: + err = errors.StructuralError("subpacket truncated") + return +} + +func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { + buf := make([]byte, 6) + n := serializeSubpacketLength(buf, len(osp.Contents)+1) + buf[n] = osp.SubType + if _, err = w.Write(buf[:n+1]); err != nil { + return + } + _, err = w.Write(osp.Contents) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/packet.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/packet.go new file mode 100644 index 0000000..3eded93 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/packet.go @@ -0,0 +1,537 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package packet implements parsing and serialization of OpenPGP packets, as +// specified in RFC 4880. +package packet // import "golang.org/x/crypto/openpgp/packet" + +import ( + "bufio" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "golang.org/x/crypto/cast5" + "golang.org/x/crypto/openpgp/errors" + "io" + "math/big" +) + +// readFull is the same as io.ReadFull except that reading zero bytes returns +// ErrUnexpectedEOF rather than EOF. +func readFull(r io.Reader, buf []byte) (n int, err error) { + n, err = io.ReadFull(r, buf) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. +func readLength(r io.Reader) (length int64, isPartial bool, err error) { + var buf [4]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + switch { + case buf[0] < 192: + length = int64(buf[0]) + case buf[0] < 224: + length = int64(buf[0]-192) << 8 + _, err = readFull(r, buf[0:1]) + if err != nil { + return + } + length += int64(buf[0]) + 192 + case buf[0] < 255: + length = int64(1) << (buf[0] & 0x1f) + isPartial = true + default: + _, err = readFull(r, buf[0:4]) + if err != nil { + return + } + length = int64(buf[0])<<24 | + int64(buf[1])<<16 | + int64(buf[2])<<8 | + int64(buf[3]) + } + return +} + +// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. +// The continuation lengths are parsed and removed from the stream and EOF is +// returned at the end of the packet. See RFC 4880, section 4.2.2.4. +type partialLengthReader struct { + r io.Reader + remaining int64 + isPartial bool +} + +func (r *partialLengthReader) Read(p []byte) (n int, err error) { + for r.remaining == 0 { + if !r.isPartial { + return 0, io.EOF + } + r.remaining, r.isPartial, err = readLength(r.r) + if err != nil { + return 0, err + } + } + + toRead := int64(len(p)) + if toRead > r.remaining { + toRead = r.remaining + } + + n, err = r.r.Read(p[:int(toRead)]) + r.remaining -= int64(n) + if n < int(toRead) && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// partialLengthWriter writes a stream of data using OpenPGP partial lengths. +// See RFC 4880, section 4.2.2.4. +type partialLengthWriter struct { + w io.WriteCloser + lengthByte [1]byte +} + +func (w *partialLengthWriter) Write(p []byte) (n int, err error) { + for len(p) > 0 { + for power := uint(14); power < 32; power-- { + l := 1 << power + if len(p) >= l { + w.lengthByte[0] = 224 + uint8(power) + _, err = w.w.Write(w.lengthByte[:]) + if err != nil { + return + } + var m int + m, err = w.w.Write(p[:l]) + n += m + if err != nil { + return + } + p = p[l:] + break + } + } + } + return +} + +func (w *partialLengthWriter) Close() error { + w.lengthByte[0] = 0 + _, err := w.w.Write(w.lengthByte[:]) + if err != nil { + return err + } + return w.w.Close() +} + +// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the +// underlying Reader returns EOF before the limit has been reached. +type spanReader struct { + r io.Reader + n int64 +} + +func (l *spanReader) Read(p []byte) (n int, err error) { + if l.n <= 0 { + return 0, io.EOF + } + if int64(len(p)) > l.n { + p = p[0:l.n] + } + n, err = l.r.Read(p) + l.n -= int64(n) + if l.n > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return +} + +// readHeader parses a packet header and returns an io.Reader which will return +// the contents of the packet. See RFC 4880, section 4.2. +func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) { + var buf [4]byte + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + if buf[0]&0x80 == 0 { + err = errors.StructuralError("tag byte does not have MSB set") + return + } + if buf[0]&0x40 == 0 { + // Old format packet + tag = packetType((buf[0] & 0x3f) >> 2) + lengthType := buf[0] & 3 + if lengthType == 3 { + length = -1 + contents = r + return + } + lengthBytes := 1 << lengthType + _, err = readFull(r, buf[0:lengthBytes]) + if err != nil { + return + } + for i := 0; i < lengthBytes; i++ { + length <<= 8 + length |= int64(buf[i]) + } + contents = &spanReader{r, length} + return + } + + // New format packet + tag = packetType(buf[0] & 0x3f) + length, isPartial, err := readLength(r) + if err != nil { + return + } + if isPartial { + contents = &partialLengthReader{ + remaining: length, + isPartial: true, + r: r, + } + length = -1 + } else { + contents = &spanReader{r, length} + } + return +} + +// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section +// 4.2. +func serializeHeader(w io.Writer, ptype packetType, length int) (err error) { + var buf [6]byte + var n int + + buf[0] = 0x80 | 0x40 | byte(ptype) + if length < 192 { + buf[1] = byte(length) + n = 2 + } else if length < 8384 { + length -= 192 + buf[1] = 192 + byte(length>>8) + buf[2] = byte(length) + n = 3 + } else { + buf[1] = 255 + buf[2] = byte(length >> 24) + buf[3] = byte(length >> 16) + buf[4] = byte(length >> 8) + buf[5] = byte(length) + n = 6 + } + + _, err = w.Write(buf[:n]) + return +} + +// serializeStreamHeader writes an OpenPGP packet header to w where the +// length of the packet is unknown. It returns a io.WriteCloser which can be +// used to write the contents of the packet. See RFC 4880, section 4.2. +func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) { + var buf [1]byte + buf[0] = 0x80 | 0x40 | byte(ptype) + _, err = w.Write(buf[:]) + if err != nil { + return + } + out = &partialLengthWriter{w: w} + return +} + +// Packet represents an OpenPGP packet. Users are expected to try casting +// instances of this interface to specific packet types. +type Packet interface { + parse(io.Reader) error +} + +// consumeAll reads from the given Reader until error, returning the number of +// bytes read. +func consumeAll(r io.Reader) (n int64, err error) { + var m int + var buf [1024]byte + + for { + m, err = r.Read(buf[:]) + n += int64(m) + if err == io.EOF { + err = nil + return + } + if err != nil { + return + } + } +} + +// packetType represents the numeric ids of the different OpenPGP packet types. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 +type packetType uint8 + +const ( + packetTypeEncryptedKey packetType = 1 + packetTypeSignature packetType = 2 + packetTypeSymmetricKeyEncrypted packetType = 3 + packetTypeOnePassSignature packetType = 4 + packetTypePrivateKey packetType = 5 + packetTypePublicKey packetType = 6 + packetTypePrivateSubkey packetType = 7 + packetTypeCompressed packetType = 8 + packetTypeSymmetricallyEncrypted packetType = 9 + packetTypeLiteralData packetType = 11 + packetTypeUserId packetType = 13 + packetTypePublicSubkey packetType = 14 + packetTypeUserAttribute packetType = 17 + packetTypeSymmetricallyEncryptedMDC packetType = 18 +) + +// peekVersion detects the version of a public key packet about to +// be read. A bufio.Reader at the original position of the io.Reader +// is returned. +func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) { + bufr = bufio.NewReader(r) + var verBuf []byte + if verBuf, err = bufr.Peek(1); err != nil { + return + } + ver = verBuf[0] + return +} + +// Read reads a single OpenPGP packet from the given io.Reader. If there is an +// error parsing a packet, the whole packet is consumed from the input. +func Read(r io.Reader) (p Packet, err error) { + tag, _, contents, err := readHeader(r) + if err != nil { + return + } + + switch tag { + case packetTypeEncryptedKey: + p = new(EncryptedKey) + case packetTypeSignature: + var version byte + // Detect signature version + if contents, version, err = peekVersion(contents); err != nil { + return + } + if version < 4 { + p = new(SignatureV3) + } else { + p = new(Signature) + } + case packetTypeSymmetricKeyEncrypted: + p = new(SymmetricKeyEncrypted) + case packetTypeOnePassSignature: + p = new(OnePassSignature) + case packetTypePrivateKey, packetTypePrivateSubkey: + pk := new(PrivateKey) + if tag == packetTypePrivateSubkey { + pk.IsSubkey = true + } + p = pk + case packetTypePublicKey, packetTypePublicSubkey: + var version byte + if contents, version, err = peekVersion(contents); err != nil { + return + } + isSubkey := tag == packetTypePublicSubkey + if version < 4 { + p = &PublicKeyV3{IsSubkey: isSubkey} + } else { + p = &PublicKey{IsSubkey: isSubkey} + } + case packetTypeCompressed: + p = new(Compressed) + case packetTypeSymmetricallyEncrypted: + p = new(SymmetricallyEncrypted) + case packetTypeLiteralData: + p = new(LiteralData) + case packetTypeUserId: + p = new(UserId) + case packetTypeUserAttribute: + p = new(UserAttribute) + case packetTypeSymmetricallyEncryptedMDC: + se := new(SymmetricallyEncrypted) + se.MDC = true + p = se + default: + err = errors.UnknownPacketTypeError(tag) + } + if p != nil { + err = p.parse(contents) + } + if err != nil { + consumeAll(contents) + } + return +} + +// SignatureType represents the different semantic meanings of an OpenPGP +// signature. See RFC 4880, section 5.2.1. +type SignatureType uint8 + +const ( + SigTypeBinary SignatureType = 0 + SigTypeText = 1 + SigTypeGenericCert = 0x10 + SigTypePersonaCert = 0x11 + SigTypeCasualCert = 0x12 + SigTypePositiveCert = 0x13 + SigTypeSubkeyBinding = 0x18 + SigTypePrimaryKeyBinding = 0x19 + SigTypeDirectSignature = 0x1F + SigTypeKeyRevocation = 0x20 + SigTypeSubkeyRevocation = 0x28 +) + +// PublicKeyAlgorithm represents the different public key system specified for +// OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 +type PublicKeyAlgorithm uint8 + +const ( + PubKeyAlgoRSA PublicKeyAlgorithm = 1 + PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 + PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 + PubKeyAlgoElGamal PublicKeyAlgorithm = 16 + PubKeyAlgoDSA PublicKeyAlgorithm = 17 + // RFC 6637, Section 5. + PubKeyAlgoECDH PublicKeyAlgorithm = 18 + PubKeyAlgoECDSA PublicKeyAlgorithm = 19 +) + +// CanEncrypt returns true if it's possible to encrypt a message to a public +// key of the given type. +func (pka PublicKeyAlgorithm) CanEncrypt() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: + return true + } + return false +} + +// CanSign returns true if it's possible for a public key of the given type to +// sign a message. +func (pka PublicKeyAlgorithm) CanSign() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: + return true + } + return false +} + +// CipherFunction represents the different block ciphers specified for OpenPGP. See +// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 +type CipherFunction uint8 + +const ( + Cipher3DES CipherFunction = 2 + CipherCAST5 CipherFunction = 3 + CipherAES128 CipherFunction = 7 + CipherAES192 CipherFunction = 8 + CipherAES256 CipherFunction = 9 +) + +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { + switch cipher { + case Cipher3DES: + return 24 + case CipherCAST5: + return cast5.KeySize + case CipherAES128: + return 16 + case CipherAES192: + return 24 + case CipherAES256: + return 32 + } + return 0 +} + +// blockSize returns the block size, in bytes, of cipher. +func (cipher CipherFunction) blockSize() int { + switch cipher { + case Cipher3DES: + return des.BlockSize + case CipherCAST5: + return 8 + case CipherAES128, CipherAES192, CipherAES256: + return 16 + } + return 0 +} + +// new returns a fresh instance of the given cipher. +func (cipher CipherFunction) new(key []byte) (block cipher.Block) { + switch cipher { + case Cipher3DES: + block, _ = des.NewTripleDESCipher(key) + case CipherCAST5: + block, _ = cast5.NewCipher(key) + case CipherAES128, CipherAES192, CipherAES256: + block, _ = aes.NewCipher(key) + } + return +} + +// readMPI reads a big integer from r. The bit length returned is the bit +// length that was specified in r. This is preserved so that the integer can be +// reserialized exactly. +func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) { + var buf [2]byte + _, err = readFull(r, buf[0:]) + if err != nil { + return + } + bitLength = uint16(buf[0])<<8 | uint16(buf[1]) + numBytes := (int(bitLength) + 7) / 8 + mpi = make([]byte, numBytes) + _, err = readFull(r, mpi) + return +} + +// mpiLength returns the length of the given *big.Int when serialized as an +// MPI. +func mpiLength(n *big.Int) (mpiLengthInBytes int) { + mpiLengthInBytes = 2 /* MPI length */ + mpiLengthInBytes += (n.BitLen() + 7) / 8 + return +} + +// writeMPI serializes a big integer to w. +func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { + _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) + if err == nil { + _, err = w.Write(mpiBytes) + } + return +} + +// writeBig serializes a *big.Int to w. +func writeBig(w io.Writer, i *big.Int) error { + return writeMPI(w, uint16(i.BitLen()), i.Bytes()) +} + +// CompressionAlgo Represents the different compression algorithms +// supported by OpenPGP (except for BZIP2, which is not currently +// supported). See Section 9.3 of RFC 4880. +type CompressionAlgo uint8 + +const ( + CompressionNone CompressionAlgo = 0 + CompressionZIP CompressionAlgo = 1 + CompressionZLIB CompressionAlgo = 2 +) diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/private_key.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/private_key.go new file mode 100644 index 0000000..34734cc --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/private_key.go @@ -0,0 +1,380 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/cipher" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "io" + "io/ioutil" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// PrivateKey represents a possibly encrypted private key. See RFC 4880, +// section 5.5.3. +type PrivateKey struct { + PublicKey + Encrypted bool // if true then the private key is unavailable until Decrypt has been called. + encryptedData []byte + cipher CipherFunction + s2k func(out, in []byte) + PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or a crypto.Signer. + sha1Checksum bool + iv []byte +} + +func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewDSAPrivateKey(currentTime time.Time, priv *dsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewDSAPublicKey(currentTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewElGamalPrivateKey(currentTime time.Time, priv *elgamal.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewElGamalPublicKey(currentTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewECDSAPublicKey(currentTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +// NewSignerPrivateKey creates a sign-only PrivateKey from a crypto.Signer that +// implements RSA or ECDSA. +func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey { + pk := new(PrivateKey) + switch pubkey := signer.Public().(type) { + case rsa.PublicKey: + pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey) + pk.PubKeyAlgo = PubKeyAlgoRSASignOnly + case ecdsa.PublicKey: + pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey) + default: + panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey") + } + pk.PrivateKey = signer + return pk +} + +func (pk *PrivateKey) parse(r io.Reader) (err error) { + err = (&pk.PublicKey).parse(r) + if err != nil { + return + } + var buf [1]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + + s2kType := buf[0] + + switch s2kType { + case 0: + pk.s2k = nil + pk.Encrypted = false + case 254, 255: + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.cipher = CipherFunction(buf[0]) + pk.Encrypted = true + pk.s2k, err = s2k.Parse(r) + if err != nil { + return + } + if s2kType == 254 { + pk.sha1Checksum = true + } + default: + return errors.UnsupportedError("deprecated s2k function in private key") + } + + if pk.Encrypted { + blockSize := pk.cipher.blockSize() + if blockSize == 0 { + return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) + } + pk.iv = make([]byte, blockSize) + _, err = readFull(r, pk.iv) + if err != nil { + return + } + } + + pk.encryptedData, err = ioutil.ReadAll(r) + if err != nil { + return + } + + if !pk.Encrypted { + return pk.parsePrivateKey(pk.encryptedData) + } + + return +} + +func mod64kHash(d []byte) uint16 { + var h uint16 + for _, b := range d { + h += uint16(b) + } + return h +} + +func (pk *PrivateKey) Serialize(w io.Writer) (err error) { + // TODO(agl): support encrypted private keys + buf := bytes.NewBuffer(nil) + err = pk.PublicKey.serializeWithoutHeaders(buf) + if err != nil { + return + } + buf.WriteByte(0 /* no encryption */) + + privateKeyBuf := bytes.NewBuffer(nil) + + switch priv := pk.PrivateKey.(type) { + case *rsa.PrivateKey: + err = serializeRSAPrivateKey(privateKeyBuf, priv) + case *dsa.PrivateKey: + err = serializeDSAPrivateKey(privateKeyBuf, priv) + case *elgamal.PrivateKey: + err = serializeElGamalPrivateKey(privateKeyBuf, priv) + case *ecdsa.PrivateKey: + err = serializeECDSAPrivateKey(privateKeyBuf, priv) + default: + err = errors.InvalidArgumentError("unknown private key type") + } + if err != nil { + return + } + + ptype := packetTypePrivateKey + contents := buf.Bytes() + privateKeyBytes := privateKeyBuf.Bytes() + if pk.IsSubkey { + ptype = packetTypePrivateSubkey + } + err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) + if err != nil { + return + } + _, err = w.Write(contents) + if err != nil { + return + } + _, err = w.Write(privateKeyBytes) + if err != nil { + return + } + + checksum := mod64kHash(privateKeyBytes) + var checksumBytes [2]byte + checksumBytes[0] = byte(checksum >> 8) + checksumBytes[1] = byte(checksum) + _, err = w.Write(checksumBytes[:]) + + return +} + +func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error { + err := writeBig(w, priv.D) + if err != nil { + return err + } + err = writeBig(w, priv.Primes[1]) + if err != nil { + return err + } + err = writeBig(w, priv.Primes[0]) + if err != nil { + return err + } + return writeBig(w, priv.Precomputed.Qinv) +} + +func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error { + return writeBig(w, priv.X) +} + +func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error { + return writeBig(w, priv.X) +} + +func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error { + return writeBig(w, priv.D) +} + +// Decrypt decrypts an encrypted private key using a passphrase. +func (pk *PrivateKey) Decrypt(passphrase []byte) error { + if !pk.Encrypted { + return nil + } + + key := make([]byte, pk.cipher.KeySize()) + pk.s2k(key, passphrase) + block := pk.cipher.new(key) + cfb := cipher.NewCFBDecrypter(block, pk.iv) + + data := make([]byte, len(pk.encryptedData)) + cfb.XORKeyStream(data, pk.encryptedData) + + if pk.sha1Checksum { + if len(data) < sha1.Size { + return errors.StructuralError("truncated private key data") + } + h := sha1.New() + h.Write(data[:len(data)-sha1.Size]) + sum := h.Sum(nil) + if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-sha1.Size] + } else { + if len(data) < 2 { + return errors.StructuralError("truncated private key data") + } + var sum uint16 + for i := 0; i < len(data)-2; i++ { + sum += uint16(data[i]) + } + if data[len(data)-2] != uint8(sum>>8) || + data[len(data)-1] != uint8(sum) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-2] + } + + return pk.parsePrivateKey(data) +} + +func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { + switch pk.PublicKey.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: + return pk.parseRSAPrivateKey(data) + case PubKeyAlgoDSA: + return pk.parseDSAPrivateKey(data) + case PubKeyAlgoElGamal: + return pk.parseElGamalPrivateKey(data) + case PubKeyAlgoECDSA: + return pk.parseECDSAPrivateKey(data) + } + panic("impossible") +} + +func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { + rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) + rsaPriv := new(rsa.PrivateKey) + rsaPriv.PublicKey = *rsaPub + + buf := bytes.NewBuffer(data) + d, _, err := readMPI(buf) + if err != nil { + return + } + p, _, err := readMPI(buf) + if err != nil { + return + } + q, _, err := readMPI(buf) + if err != nil { + return + } + + rsaPriv.D = new(big.Int).SetBytes(d) + rsaPriv.Primes = make([]*big.Int, 2) + rsaPriv.Primes[0] = new(big.Int).SetBytes(p) + rsaPriv.Primes[1] = new(big.Int).SetBytes(q) + if err := rsaPriv.Validate(); err != nil { + return err + } + rsaPriv.Precompute() + pk.PrivateKey = rsaPriv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) { + dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) + dsaPriv := new(dsa.PrivateKey) + dsaPriv.PublicKey = *dsaPub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + dsaPriv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = dsaPriv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) { + pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) + priv := new(elgamal.PrivateKey) + priv.PublicKey = *pub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + priv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = priv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} + +func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) { + ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey) + + buf := bytes.NewBuffer(data) + d, _, err := readMPI(buf) + if err != nil { + return + } + + pk.PrivateKey = &ecdsa.PrivateKey{ + PublicKey: *ecdsaPub, + D: new(big.Int).SetBytes(d), + } + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key.go new file mode 100644 index 0000000..ead2623 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key.go @@ -0,0 +1,748 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/elgamal" + "golang.org/x/crypto/openpgp/errors" +) + +var ( + // NIST curve P-256 + oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} + // NIST curve P-384 + oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22} + // NIST curve P-521 + oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23} +) + +const maxOIDLength = 8 + +// ecdsaKey stores the algorithm-specific fields for ECDSA keys. +// as defined in RFC 6637, Section 9. +type ecdsaKey struct { + // oid contains the OID byte sequence identifying the elliptic curve used + oid []byte + // p contains the elliptic curve point that represents the public key + p parsedMPI +} + +// parseOID reads the OID for the curve as defined in RFC 6637, Section 9. +func parseOID(r io.Reader) (oid []byte, err error) { + buf := make([]byte, maxOIDLength) + if _, err = readFull(r, buf[:1]); err != nil { + return + } + oidLen := buf[0] + if int(oidLen) > len(buf) { + err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen))) + return + } + oid = buf[:oidLen] + _, err = readFull(r, oid) + return +} + +func (f *ecdsaKey) parse(r io.Reader) (err error) { + if f.oid, err = parseOID(r); err != nil { + return err + } + f.p.bytes, f.p.bitLength, err = readMPI(r) + return +} + +func (f *ecdsaKey) serialize(w io.Writer) (err error) { + buf := make([]byte, maxOIDLength+1) + buf[0] = byte(len(f.oid)) + copy(buf[1:], f.oid) + if _, err = w.Write(buf[:len(f.oid)+1]); err != nil { + return + } + return writeMPIs(w, f.p) +} + +func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) { + var c elliptic.Curve + if bytes.Equal(f.oid, oidCurveP256) { + c = elliptic.P256() + } else if bytes.Equal(f.oid, oidCurveP384) { + c = elliptic.P384() + } else if bytes.Equal(f.oid, oidCurveP521) { + c = elliptic.P521() + } else { + return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid)) + } + x, y := elliptic.Unmarshal(c, f.p.bytes) + if x == nil { + return nil, errors.UnsupportedError("failed to parse EC point") + } + return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil +} + +func (f *ecdsaKey) byteLen() int { + return 1 + len(f.oid) + 2 + len(f.p.bytes) +} + +type kdfHashFunction byte +type kdfAlgorithm byte + +// ecdhKdf stores key derivation function parameters +// used for ECDH encryption. See RFC 6637, Section 9. +type ecdhKdf struct { + KdfHash kdfHashFunction + KdfAlgo kdfAlgorithm +} + +func (f *ecdhKdf) parse(r io.Reader) (err error) { + buf := make([]byte, 1) + if _, err = readFull(r, buf); err != nil { + return + } + kdfLen := int(buf[0]) + if kdfLen < 3 { + return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) + } + buf = make([]byte, kdfLen) + if _, err = readFull(r, buf); err != nil { + return + } + reserved := int(buf[0]) + f.KdfHash = kdfHashFunction(buf[1]) + f.KdfAlgo = kdfAlgorithm(buf[2]) + if reserved != 0x01 { + return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved)) + } + return +} + +func (f *ecdhKdf) serialize(w io.Writer) (err error) { + buf := make([]byte, 4) + // See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys. + buf[0] = byte(0x03) // Length of the following fields + buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now + buf[2] = byte(f.KdfHash) + buf[3] = byte(f.KdfAlgo) + _, err = w.Write(buf[:]) + return +} + +func (f *ecdhKdf) byteLen() int { + return 4 +} + +// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. +type PublicKey struct { + CreationTime time.Time + PubKeyAlgo PublicKeyAlgorithm + PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey + Fingerprint [20]byte + KeyId uint64 + IsSubkey bool + + n, e, p, q, g, y parsedMPI + + // RFC 6637 fields + ec *ecdsaKey + ecdh *ecdhKdf +} + +// signingKey provides a convenient abstraction over signature verification +// for v3 and v4 public keys. +type signingKey interface { + SerializeSignaturePrefix(io.Writer) + serializeWithoutHeaders(io.Writer) error +} + +func fromBig(n *big.Int) parsedMPI { + return parsedMPI{ + bytes: n.Bytes(), + bitLength: uint16(n.BitLen()), + } +} + +// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. +func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoRSA, + PublicKey: pub, + n: fromBig(pub.N), + e: fromBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey. +func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoDSA, + PublicKey: pub, + p: fromBig(pub.P), + q: fromBig(pub.Q), + g: fromBig(pub.G), + y: fromBig(pub.Y), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey. +func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoElGamal, + PublicKey: pub, + p: fromBig(pub.P), + g: fromBig(pub.G), + y: fromBig(pub.Y), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey { + pk := &PublicKey{ + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoECDSA, + PublicKey: pub, + ec: new(ecdsaKey), + } + + switch pub.Curve { + case elliptic.P256(): + pk.ec.oid = oidCurveP256 + case elliptic.P384(): + pk.ec.oid = oidCurveP384 + case elliptic.P521(): + pk.ec.oid = oidCurveP521 + default: + panic("unknown elliptic curve") + } + + pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) + pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes)) + + pk.setFingerPrintAndKeyId() + return pk +} + +func (pk *PublicKey) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [6]byte + _, err = readFull(r, buf[:]) + if err != nil { + return + } + if buf[0] != 4 { + return errors.UnsupportedError("public key version") + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + case PubKeyAlgoDSA: + err = pk.parseDSA(r) + case PubKeyAlgoElGamal: + err = pk.parseElGamal(r) + case PubKeyAlgoECDSA: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return err + } + pk.PublicKey, err = pk.ec.newECDSA() + case PubKeyAlgoECDH: + pk.ec = new(ecdsaKey) + if err = pk.ec.parse(r); err != nil { + return + } + pk.ecdh = new(ecdhKdf) + if err = pk.ecdh.parse(r); err != nil { + return + } + // The ECDH key is stored in an ecdsa.PublicKey for convenience. + pk.PublicKey, err = pk.ec.newECDSA() + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerPrintAndKeyId() + return +} + +func (pk *PublicKey) setFingerPrintAndKeyId() { + // RFC 4880, section 12.2 + fingerPrint := sha1.New() + pk.SerializeSignaturePrefix(fingerPrint) + pk.serializeWithoutHeaders(fingerPrint) + copy(pk.Fingerprint[:], fingerPrint.Sum(nil)) + pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseRSA(r io.Reader) (err error) { + pk.n.bytes, pk.n.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.e.bytes, pk.e.bitLength, err = readMPI(r) + if err != nil { + return + } + + if len(pk.e.bytes) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{ + N: new(big.Int).SetBytes(pk.n.bytes), + E: 0, + } + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// parseDSA parses DSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKey) parseDSA(r io.Reader) (err error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.q.bytes, pk.q.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + dsa := new(dsa.PublicKey) + dsa.P = new(big.Int).SetBytes(pk.p.bytes) + dsa.Q = new(big.Int).SetBytes(pk.q.bytes) + dsa.G = new(big.Int).SetBytes(pk.g.bytes) + dsa.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = dsa + return +} + +// parseElGamal parses ElGamal public key material from the given Reader. See +// RFC 4880, section 5.5.2. +func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + elgamal := new(elgamal.PublicKey) + elgamal.P = new(big.Int).SetBytes(pk.p.bytes) + elgamal.G = new(big.Int).SetBytes(pk.g.bytes) + elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = elgamal + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + case PubKeyAlgoDSA: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.q.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoElGamal: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoECDSA: + pLength += uint16(pk.ec.byteLen()) + case PubKeyAlgoECDH: + pLength += uint16(pk.ec.byteLen()) + pLength += uint16(pk.ecdh.byteLen()) + default: + panic("unknown public key algorithm") + } + pLength += 6 + h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +func (pk *PublicKey) Serialize(w io.Writer) (err error) { + length := 6 // 6 byte header + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += 2 + len(pk.n.bytes) + length += 2 + len(pk.e.bytes) + case PubKeyAlgoDSA: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.q.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + case PubKeyAlgoElGamal: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + case PubKeyAlgoECDSA: + length += pk.ec.byteLen() + case PubKeyAlgoECDH: + length += pk.ec.byteLen() + length += pk.ecdh.byteLen() + default: + panic("unknown public key algorithm") + } + + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + err = serializeHeader(w, packetType, length) + if err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { + var buf [6]byte + buf[0] = 4 + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) + buf[5] = byte(pk.PubKeyAlgo) + + _, err = w.Write(buf[:]) + if err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + case PubKeyAlgoDSA: + return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) + case PubKeyAlgoElGamal: + return writeMPIs(w, pk.p, pk.g, pk.y) + case PubKeyAlgoECDSA: + return pk.ec.serialize(w) + case PubKeyAlgoECDH: + if err = pk.ec.serialize(w); err != nil { + return + } + return pk.ecdh.serialize(w) + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKey) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal +} + +// VerifySignature returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + signed.Write(sig.HashSuffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) + if err != nil { + return errors.SignatureError("RSA verification failure") + } + return nil + case PubKeyAlgoDSA: + dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { + return errors.SignatureError("DSA verification failure") + } + return nil + case PubKeyAlgoECDSA: + ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) + if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) { + return errors.SignatureError("ECDSA verification failure") + } + return nil + default: + return errors.SignatureError("Unsupported public key algorithm used in signature") + } +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) + if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + case PubKeyAlgoDSA: + dsaPublicKey := pk.PublicKey.(*dsa.PublicKey) + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 + if len(hashBytes) > subgroupSize { + hashBytes = hashBytes[:subgroupSize] + } + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { + return errors.SignatureError("DSA verification failure") + } + return nil + default: + panic("shouldn't happen") + } +} + +// keySignatureHash returns a Hash of the message that needs to be signed for +// pk to assert a subkey relationship to signed. +func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + signed.SerializeSignaturePrefix(h) + signed.serializeWithoutHeaders(h) + return +} + +// VerifyKeySignature returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + if err = pk.VerifySignature(h, sig); err != nil { + return err + } + + if sig.FlagSign { + // Signing subkeys must be cross-signed. See + // https://www.gnupg.org/faq/subkey-cross-certify.html. + if sig.EmbeddedSignature == nil { + return errors.StructuralError("signing subkey is missing cross-signature") + } + // Verify the cross-signature. This is calculated over the same + // data as the main signature, so we cannot just recursively + // call signed.VerifyKeySignature(...) + if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil { + return errors.StructuralError("error while hashing for cross-signature: " + err.Error()) + } + if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil { + return errors.StructuralError("error while verifying cross-signature: " + err.Error()) + } + } + + return nil +} + +func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + return +} + +// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this +// public key. +func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) { + h, err := keyRevocationHash(pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// userIdSignatureHash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + var buf [5]byte + buf[0] = 0xb4 + buf[1] = byte(len(id) >> 24) + buf[2] = byte(len(id) >> 16) + buf[3] = byte(len(id) >> 8) + buf[4] = byte(len(id)) + h.Write(buf[:]) + h.Write([]byte(id)) + + return +} + +// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserIdSignatureV3(id string, pub *PublicKey, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKey) KeyIdString() string { + return fmt.Sprintf("%X", pk.Fingerprint[12:20]) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKey) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.Fingerprint[16:20]) +} + +// A parsedMPI is used to store the contents of a big integer, along with the +// bit length that was specified in the original input. This allows the MPI to +// be reserialized exactly. +type parsedMPI struct { + bytes []byte + bitLength uint16 +} + +// writeMPIs is a utility function for serializing several big integers to the +// given Writer. +func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) { + for _, mpi := range mpis { + err = writeMPI(w, mpi.bitLength, mpi.bytes) + if err != nil { + return + } + } + return +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKey) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + case PubKeyAlgoDSA: + bitLength = pk.p.bitLength + case PubKeyAlgoElGamal: + bitLength = pk.p.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go new file mode 100644 index 0000000..5daf7b6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go @@ -0,0 +1,279 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "crypto/md5" + "crypto/rsa" + "encoding/binary" + "fmt" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" +) + +// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and +// should not be used for signing or encrypting. They are supported here only for +// parsing version 3 key material and validating signatures. +// See RFC 4880, section 5.5.2. +type PublicKeyV3 struct { + CreationTime time.Time + DaysToExpire uint16 + PubKeyAlgo PublicKeyAlgorithm + PublicKey *rsa.PublicKey + Fingerprint [16]byte + KeyId uint64 + IsSubkey bool + + n, e parsedMPI +} + +// newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey. +// Included here for testing purposes only. RFC 4880, section 5.5.2: +// "an implementation MUST NOT generate a V3 key, but MAY accept it." +func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3 { + pk := &PublicKeyV3{ + CreationTime: creationTime, + PublicKey: pub, + n: fromBig(pub.N), + e: fromBig(big.NewInt(int64(pub.E))), + } + + pk.setFingerPrintAndKeyId() + return pk +} + +func (pk *PublicKeyV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.5.2 + var buf [8]byte + if _, err = readFull(r, buf[:]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + return errors.UnsupportedError("public key version") + } + pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) + pk.DaysToExpire = binary.BigEndian.Uint16(buf[5:7]) + pk.PubKeyAlgo = PublicKeyAlgorithm(buf[7]) + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + err = pk.parseRSA(r) + default: + err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) + } + if err != nil { + return + } + + pk.setFingerPrintAndKeyId() + return +} + +func (pk *PublicKeyV3) setFingerPrintAndKeyId() { + // RFC 4880, section 12.2 + fingerPrint := md5.New() + fingerPrint.Write(pk.n.bytes) + fingerPrint.Write(pk.e.bytes) + fingerPrint.Sum(pk.Fingerprint[:0]) + pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:]) +} + +// parseRSA parses RSA public key material from the given Reader. See RFC 4880, +// section 5.5.2. +func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) { + if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil { + return + } + if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil { + return + } + + // RFC 4880 Section 12.2 requires the low 8 bytes of the + // modulus to form the key id. + if len(pk.n.bytes) < 8 { + return errors.StructuralError("v3 public key modulus is too short") + } + if len(pk.e.bytes) > 3 { + err = errors.UnsupportedError("large public exponent") + return + } + rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)} + for i := 0; i < len(pk.e.bytes); i++ { + rsa.E <<= 8 + rsa.E |= int(pk.e.bytes[i]) + } + pk.PublicKey = rsa + return +} + +// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. +// The prefix is used when calculating a signature over this public key. See +// RFC 4880, section 5.2.4. +func (pk *PublicKeyV3) SerializeSignaturePrefix(w io.Writer) { + var pLength uint16 + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + pLength += 2 + uint16(len(pk.n.bytes)) + pLength += 2 + uint16(len(pk.e.bytes)) + default: + panic("unknown public key algorithm") + } + pLength += 6 + w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + return +} + +func (pk *PublicKeyV3) Serialize(w io.Writer) (err error) { + length := 8 // 8 byte header + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + length += 2 + len(pk.n.bytes) + length += 2 + len(pk.e.bytes) + default: + panic("unknown public key algorithm") + } + + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + if err = serializeHeader(w, packetType, length); err != nil { + return + } + return pk.serializeWithoutHeaders(w) +} + +// serializeWithoutHeaders marshals the PublicKey to w in the form of an +// OpenPGP public key packet, not including the packet header. +func (pk *PublicKeyV3) serializeWithoutHeaders(w io.Writer) (err error) { + var buf [8]byte + // Version 3 + buf[0] = 3 + // Creation time + t := uint32(pk.CreationTime.Unix()) + buf[1] = byte(t >> 24) + buf[2] = byte(t >> 16) + buf[3] = byte(t >> 8) + buf[4] = byte(t) + // Days to expire + buf[5] = byte(pk.DaysToExpire >> 8) + buf[6] = byte(pk.DaysToExpire) + // Public key algorithm + buf[7] = byte(pk.PubKeyAlgo) + + if _, err = w.Write(buf[:]); err != nil { + return + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + return writeMPIs(w, pk.n, pk.e) + } + return errors.InvalidArgumentError("bad public-key algorithm") +} + +// CanSign returns true iff this public key can generate signatures +func (pk *PublicKeyV3) CanSign() bool { + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly +} + +// VerifySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of the data hashed into signed. signed is mutated by this call. +func (pk *PublicKeyV3) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { + if !pk.CanSign() { + return errors.InvalidArgumentError("public key cannot generate signatures") + } + + suffix := make([]byte, 5) + suffix[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) + signed.Write(suffix) + hashBytes := signed.Sum(nil) + + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + + if pk.PubKeyAlgo != sig.PubKeyAlgo { + return errors.InvalidArgumentError("public key and signature use different algorithms") + } + + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { + return errors.SignatureError("RSA verification failure") + } + return + default: + // V3 public keys only support RSA. + panic("shouldn't happen") + } +} + +// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKeyV3) VerifyUserIdSignatureV3(id string, pub *PublicKeyV3, sig *SignatureV3) (err error) { + h, err := userIdSignatureV3Hash(id, pk, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// VerifyKeySignatureV3 returns nil iff sig is a valid signature, made by this +// public key, of signed. +func (pk *PublicKeyV3) VerifyKeySignatureV3(signed *PublicKeyV3, sig *SignatureV3) (err error) { + h, err := keySignatureHash(pk, signed, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignatureV3(h, sig) +} + +// userIdSignatureV3Hash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userIdSignatureV3Hash(id string, pk signingKey, hfn crypto.Hash) (h hash.Hash, err error) { + if !hfn.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hfn.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + h.Write([]byte(id)) + + return +} + +// KeyIdString returns the public key's fingerprint in capital hex +// (e.g. "6C7EE1B8621CC013"). +func (pk *PublicKeyV3) KeyIdString() string { + return fmt.Sprintf("%X", pk.KeyId) +} + +// KeyIdShortString returns the short form of public key's fingerprint +// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). +func (pk *PublicKeyV3) KeyIdShortString() string { + return fmt.Sprintf("%X", pk.KeyId&0xFFFFFFFF) +} + +// BitLength returns the bit length for the given public key. +func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: + bitLength = pk.n.bitLength + default: + err = errors.InvalidArgumentError("bad public-key algorithm") + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/reader.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/reader.go new file mode 100644 index 0000000..34bc7c6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/reader.go @@ -0,0 +1,76 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "golang.org/x/crypto/openpgp/errors" + "io" +) + +// Reader reads packets from an io.Reader and allows packets to be 'unread' so +// that they result from the next call to Next. +type Reader struct { + q []Packet + readers []io.Reader +} + +// New io.Readers are pushed when a compressed or encrypted packet is processed +// and recursively treated as a new source of packets. However, a carefully +// crafted packet can trigger an infinite recursive sequence of packets. See +// http://mumble.net/~campbell/misc/pgp-quine +// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402 +// This constant limits the number of recursive packets that may be pushed. +const maxReaders = 32 + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown packet types are skipped. +func (r *Reader) Next() (p Packet, err error) { + if len(r.q) > 0 { + p = r.q[len(r.q)-1] + r.q = r.q[:len(r.q)-1] + return + } + + for len(r.readers) > 0 { + p, err = Read(r.readers[len(r.readers)-1]) + if err == nil { + return + } + if err == io.EOF { + r.readers = r.readers[:len(r.readers)-1] + continue + } + if _, ok := err.(errors.UnknownPacketTypeError); !ok { + return nil, err + } + } + + return nil, io.EOF +} + +// Push causes the Reader to start reading from a new io.Reader. When an EOF +// error is seen from the new io.Reader, it is popped and the Reader continues +// to read from the next most recent io.Reader. Push returns a StructuralError +// if pushing the reader would exceed the maximum recursion level, otherwise it +// returns nil. +func (r *Reader) Push(reader io.Reader) (err error) { + if len(r.readers) >= maxReaders { + return errors.StructuralError("too many layers of packets") + } + r.readers = append(r.readers, reader) + return nil +} + +// Unread causes the given Packet to be returned from the next call to Next. +func (r *Reader) Unread(p Packet) { + r.q = append(r.q, p) +} + +func NewReader(r io.Reader) *Reader { + return &Reader{ + q: nil, + readers: []io.Reader{r}, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature.go new file mode 100644 index 0000000..6ce0cbe --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature.go @@ -0,0 +1,731 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "encoding/asn1" + "encoding/binary" + "hash" + "io" + "math/big" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +const ( + // See RFC 4880, section 5.2.3.21 for details. + KeyFlagCertify = 1 << iota + KeyFlagSign + KeyFlagEncryptCommunications + KeyFlagEncryptStorage +) + +// Signature represents a signature. See RFC 4880, section 5.2. +type Signature struct { + SigType SignatureType + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + + // HashSuffix is extra data that is hashed in after the signed data. + HashSuffix []byte + // HashTag contains the first two bytes of the hash for fast rejection + // of bad signed data. + HashTag [2]byte + CreationTime time.Time + + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI + ECDSASigR, ECDSASigS parsedMPI + + // rawSubpackets contains the unparsed subpackets, in order. + rawSubpackets []outputSubpacket + + // The following are optional so are nil when not included in the + // signature. + + SigLifetimeSecs, KeyLifetimeSecs *uint32 + PreferredSymmetric, PreferredHash, PreferredCompression []uint8 + IssuerKeyId *uint64 + IsPrimaryId *bool + + // FlagsValid is set if any flags were given. See RFC 4880, section + // 5.2.3.21 for details. + FlagsValid bool + FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool + + // RevocationReason is set if this signature has been revoked. + // See RFC 4880, section 5.2.3.23 for details. + RevocationReason *uint8 + RevocationReasonText string + + // MDC is set if this signature has a feature packet that indicates + // support for MDC subpackets. + MDC bool + + // EmbeddedSignature, if non-nil, is a signature of the parent key, by + // this key. This prevents an attacker from claiming another's signing + // subkey as their own. + EmbeddedSignature *Signature + + outSubpackets []outputSubpacket +} + +func (sig *Signature) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.3 + var buf [5]byte + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + if buf[0] != 4 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + + _, err = readFull(r, buf[:5]) + if err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + + var ok bool + sig.Hash, ok = s2k.HashIdToHash(buf[2]) + if !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) + l := 6 + hashedSubpacketsLength + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + copy(sig.HashSuffix[1:], buf[:5]) + hashedSubpackets := sig.HashSuffix[6:l] + _, err = readFull(r, hashedSubpackets) + if err != nil { + return + } + // See RFC 4880, section 5.2.4 + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = uint8(l >> 24) + trailer[3] = uint8(l >> 16) + trailer[4] = uint8(l >> 8) + trailer[5] = uint8(l) + + err = parseSignatureSubpackets(sig, hashedSubpackets, true) + if err != nil { + return + } + + _, err = readFull(r, buf[:2]) + if err != nil { + return + } + unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) + unhashedSubpackets := make([]byte, unhashedSubpacketsLength) + _, err = readFull(r, unhashedSubpackets) + if err != nil { + return + } + err = parseSignatureSubpackets(sig, unhashedSubpackets, false) + if err != nil { + return + } + + _, err = readFull(r, sig.HashTag[:2]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) + case PubKeyAlgoDSA: + sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) + if err == nil { + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) + } + case PubKeyAlgoECDSA: + sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) + if err == nil { + sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) + } + default: + panic("unreachable") + } + return +} + +// parseSignatureSubpackets parses subpackets of the main signature packet. See +// RFC 4880, section 5.2.3.1. +func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { + for len(subpackets) > 0 { + subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) + if err != nil { + return + } + } + + if sig.CreationTime.IsZero() { + err = errors.StructuralError("no creation time in signature") + } + + return +} + +type signatureSubpacketType uint8 + +const ( + creationTimeSubpacket signatureSubpacketType = 2 + signatureExpirationSubpacket signatureSubpacketType = 3 + keyExpirationSubpacket signatureSubpacketType = 9 + prefSymmetricAlgosSubpacket signatureSubpacketType = 11 + issuerSubpacket signatureSubpacketType = 16 + prefHashAlgosSubpacket signatureSubpacketType = 21 + prefCompressionSubpacket signatureSubpacketType = 22 + primaryUserIdSubpacket signatureSubpacketType = 25 + keyFlagsSubpacket signatureSubpacketType = 27 + reasonForRevocationSubpacket signatureSubpacketType = 29 + featuresSubpacket signatureSubpacketType = 30 + embeddedSignatureSubpacket signatureSubpacketType = 32 +) + +// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. +func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { + // RFC 4880, section 5.2.3.1 + var ( + length uint32 + packetType signatureSubpacketType + isCritical bool + ) + switch { + case subpacket[0] < 192: + length = uint32(subpacket[0]) + subpacket = subpacket[1:] + case subpacket[0] < 255: + if len(subpacket) < 2 { + goto Truncated + } + length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 + subpacket = subpacket[2:] + default: + if len(subpacket) < 5 { + goto Truncated + } + length = uint32(subpacket[1])<<24 | + uint32(subpacket[2])<<16 | + uint32(subpacket[3])<<8 | + uint32(subpacket[4]) + subpacket = subpacket[5:] + } + if length > uint32(len(subpacket)) { + goto Truncated + } + rest = subpacket[length:] + subpacket = subpacket[:length] + if len(subpacket) == 0 { + err = errors.StructuralError("zero length signature subpacket") + return + } + packetType = signatureSubpacketType(subpacket[0] & 0x7f) + isCritical = subpacket[0]&0x80 == 0x80 + subpacket = subpacket[1:] + sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) + switch packetType { + case creationTimeSubpacket: + if !isHashed { + err = errors.StructuralError("signature creation time in non-hashed area") + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("signature creation time not four bytes") + return + } + t := binary.BigEndian.Uint32(subpacket) + sig.CreationTime = time.Unix(int64(t), 0) + case signatureExpirationSubpacket: + // Signature expiration time, section 5.2.3.10 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("expiration subpacket with bad length") + return + } + sig.SigLifetimeSecs = new(uint32) + *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case keyExpirationSubpacket: + // Key expiration time, section 5.2.3.6 + if !isHashed { + return + } + if len(subpacket) != 4 { + err = errors.StructuralError("key expiration subpacket with bad length") + return + } + sig.KeyLifetimeSecs = new(uint32) + *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case prefSymmetricAlgosSubpacket: + // Preferred symmetric algorithms, section 5.2.3.7 + if !isHashed { + return + } + sig.PreferredSymmetric = make([]byte, len(subpacket)) + copy(sig.PreferredSymmetric, subpacket) + case issuerSubpacket: + // Issuer, section 5.2.3.5 + if len(subpacket) != 8 { + err = errors.StructuralError("issuer subpacket with bad length") + return + } + sig.IssuerKeyId = new(uint64) + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + case prefHashAlgosSubpacket: + // Preferred hash algorithms, section 5.2.3.8 + if !isHashed { + return + } + sig.PreferredHash = make([]byte, len(subpacket)) + copy(sig.PreferredHash, subpacket) + case prefCompressionSubpacket: + // Preferred compression algorithms, section 5.2.3.9 + if !isHashed { + return + } + sig.PreferredCompression = make([]byte, len(subpacket)) + copy(sig.PreferredCompression, subpacket) + case primaryUserIdSubpacket: + // Primary User ID, section 5.2.3.19 + if !isHashed { + return + } + if len(subpacket) != 1 { + err = errors.StructuralError("primary user id subpacket with bad length") + return + } + sig.IsPrimaryId = new(bool) + if subpacket[0] > 0 { + *sig.IsPrimaryId = true + } + case keyFlagsSubpacket: + // Key flags, section 5.2.3.21 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty key flags subpacket") + return + } + sig.FlagsValid = true + if subpacket[0]&KeyFlagCertify != 0 { + sig.FlagCertify = true + } + if subpacket[0]&KeyFlagSign != 0 { + sig.FlagSign = true + } + if subpacket[0]&KeyFlagEncryptCommunications != 0 { + sig.FlagEncryptCommunications = true + } + if subpacket[0]&KeyFlagEncryptStorage != 0 { + sig.FlagEncryptStorage = true + } + case reasonForRevocationSubpacket: + // Reason For Revocation, section 5.2.3.23 + if !isHashed { + return + } + if len(subpacket) == 0 { + err = errors.StructuralError("empty revocation reason subpacket") + return + } + sig.RevocationReason = new(uint8) + *sig.RevocationReason = subpacket[0] + sig.RevocationReasonText = string(subpacket[1:]) + case featuresSubpacket: + // Features subpacket, section 5.2.3.24 specifies a very general + // mechanism for OpenPGP implementations to signal support for new + // features. In practice, the subpacket is used exclusively to + // indicate support for MDC-protected encryption. + sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1 + case embeddedSignatureSubpacket: + // Only usage is in signatures that cross-certify + // signing subkeys. section 5.2.3.26 describes the + // format, with its usage described in section 11.1 + if sig.EmbeddedSignature != nil { + err = errors.StructuralError("Cannot have multiple embedded signatures") + return + } + sig.EmbeddedSignature = new(Signature) + // Embedded signatures are required to be v4 signatures see + // section 12.1. However, we only parse v4 signatures in this + // file anyway. + if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { + return nil, err + } + if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { + return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) + } + default: + if isCritical { + err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) + return + } + } + return + +Truncated: + err = errors.StructuralError("signature subpacket truncated") + return +} + +// subpacketLengthLength returns the length, in bytes, of an encoded length value. +func subpacketLengthLength(length int) int { + if length < 192 { + return 1 + } + if length < 16320 { + return 2 + } + return 5 +} + +// serializeSubpacketLength marshals the given length into to. +func serializeSubpacketLength(to []byte, length int) int { + // RFC 4880, Section 4.2.2. + if length < 192 { + to[0] = byte(length) + return 1 + } + if length < 16320 { + length -= 192 + to[0] = byte((length >> 8) + 192) + to[1] = byte(length) + return 2 + } + to[0] = 255 + to[1] = byte(length >> 24) + to[2] = byte(length >> 16) + to[3] = byte(length >> 8) + to[4] = byte(length) + return 5 +} + +// subpacketsLength returns the serialized length, in bytes, of the given +// subpackets. +func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + length += subpacketLengthLength(len(subpacket.contents) + 1) + length += 1 // type byte + length += len(subpacket.contents) + } + } + return +} + +// serializeSubpackets marshals the given subpackets into to. +func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { + for _, subpacket := range subpackets { + if subpacket.hashed == hashed { + n := serializeSubpacketLength(to, len(subpacket.contents)+1) + to[n] = byte(subpacket.subpacketType) + to = to[1+n:] + n = copy(to, subpacket.contents) + to = to[n:] + } + } + return +} + +// KeyExpired returns whether sig is a self-signature of a key that has +// expired. +func (sig *Signature) KeyExpired(currentTime time.Time) bool { + if sig.KeyLifetimeSecs == nil { + return false + } + expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) + return currentTime.After(expiry) +} + +// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. +func (sig *Signature) buildHashSuffix() (err error) { + hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) + + var ok bool + l := 6 + hashedSubpacketsLen + sig.HashSuffix = make([]byte, l+6) + sig.HashSuffix[0] = 4 + sig.HashSuffix[1] = uint8(sig.SigType) + sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) + sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) + if !ok { + sig.HashSuffix = nil + return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) + } + sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) + sig.HashSuffix[5] = byte(hashedSubpacketsLen) + serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) + trailer := sig.HashSuffix[l:] + trailer[0] = 4 + trailer[1] = 0xff + trailer[2] = byte(l >> 24) + trailer[3] = byte(l >> 16) + trailer[4] = byte(l >> 8) + trailer[5] = byte(l) + return +} + +func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { + err = sig.buildHashSuffix() + if err != nil { + return + } + + h.Write(sig.HashSuffix) + digest = h.Sum(nil) + copy(sig.HashTag[:], digest) + return +} + +// Sign signs a message with a private key. The hash, h, must contain +// the hash of the message to be signed and will be mutated by this function. +// On success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { + sig.outSubpackets = sig.buildSubpackets() + digest, err := sig.signPrepareHash(h) + if err != nil { + return + } + + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + // supports both *rsa.PrivateKey and crypto.Signer + sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) + case PubKeyAlgoDSA: + dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) + + // Need to truncate hashBytes to match FIPS 186-3 section 4.6. + subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 + if len(digest) > subgroupSize { + digest = digest[:subgroupSize] + } + r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) + if err == nil { + sig.DSASigR.bytes = r.Bytes() + sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) + sig.DSASigS.bytes = s.Bytes() + sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) + } + case PubKeyAlgoECDSA: + var r, s *big.Int + if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { + // direct support, avoid asn1 wrapping/unwrapping + r, s, err = ecdsa.Sign(config.Random(), pk, digest) + } else { + var b []byte + b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil) + if err == nil { + r, s, err = unwrapECDSASig(b) + } + } + if err == nil { + sig.ECDSASigR = fromBig(r) + sig.ECDSASigS = fromBig(s) + } + default: + err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) + } + + return +} + +// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA +// signature. +func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { + var ecsdaSig struct { + R, S *big.Int + } + _, err = asn1.Unmarshal(b, &ecsdaSig) + if err != nil { + return + } + return ecsdaSig.R, ecsdaSig.S, nil +} + +// SignUserId computes a signature from priv, asserting that pub is a valid +// key for the identity id. On success, the signature is stored in sig. Call +// Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := userIdSignatureHash(id, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// SignKey computes a signature from priv, asserting that pub is a subkey. On +// success, the signature is stored in sig. Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { + h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *Signature) Serialize(w io.Writer) (err error) { + if len(sig.outSubpackets) == 0 { + sig.outSubpackets = sig.rawSubpackets + } + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + sigLength := 0 + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sigLength = 2 + len(sig.RSASignature.bytes) + case PubKeyAlgoDSA: + sigLength = 2 + len(sig.DSASigR.bytes) + sigLength += 2 + len(sig.DSASigS.bytes) + case PubKeyAlgoECDSA: + sigLength = 2 + len(sig.ECDSASigR.bytes) + sigLength += 2 + len(sig.ECDSASigS.bytes) + default: + panic("impossible") + } + + unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) + length := len(sig.HashSuffix) - 6 /* trailer not included */ + + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + + 2 /* hash tag */ + sigLength + err = serializeHeader(w, packetTypeSignature, length) + if err != nil { + return + } + + _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) + if err != nil { + return + } + + unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen) + serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + + _, err = w.Write(unhashedSubpackets) + if err != nil { + return + } + _, err = w.Write(sig.HashTag[:]) + if err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + err = writeMPIs(w, sig.RSASignature) + case PubKeyAlgoDSA: + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + case PubKeyAlgoECDSA: + err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) + default: + panic("impossible") + } + return +} + +// outputSubpacket represents a subpacket to be marshaled. +type outputSubpacket struct { + hashed bool // true if this subpacket is in the hashed area. + subpacketType signatureSubpacketType + isCritical bool + contents []byte +} + +func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { + creationTime := make([]byte, 4) + binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) + + if sig.IssuerKeyId != nil { + keyId := make([]byte, 8) + binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) + } + + if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { + sigLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + } + + // Key flags may only appear in self-signatures or certification signatures. + + if sig.FlagsValid { + var flags byte + if sig.FlagCertify { + flags |= KeyFlagCertify + } + if sig.FlagSign { + flags |= KeyFlagSign + } + if sig.FlagEncryptCommunications { + flags |= KeyFlagEncryptCommunications + } + if sig.FlagEncryptStorage { + flags |= KeyFlagEncryptStorage + } + subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) + } + + // The following subpackets may only appear in self-signatures + + if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { + keyLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) + } + + if sig.IsPrimaryId != nil && *sig.IsPrimaryId { + subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) + } + + if len(sig.PreferredSymmetric) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) + } + + if len(sig.PreferredHash) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) + } + + if len(sig.PreferredCompression) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) + } + + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go new file mode 100644 index 0000000..6edff88 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go @@ -0,0 +1,146 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto" + "encoding/binary" + "fmt" + "io" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// SignatureV3 represents older version 3 signatures. These signatures are less secure +// than version 4 and should not be used to create new signatures. They are included +// here for backwards compatibility to read and validate with older key material. +// See RFC 4880, section 5.2.2. +type SignatureV3 struct { + SigType SignatureType + CreationTime time.Time + IssuerKeyId uint64 + PubKeyAlgo PublicKeyAlgorithm + Hash crypto.Hash + HashTag [2]byte + + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI +} + +func (sig *SignatureV3) parse(r io.Reader) (err error) { + // RFC 4880, section 5.2.2 + var buf [8]byte + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] < 2 || buf[0] > 3 { + err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) + return + } + if _, err = readFull(r, buf[:1]); err != nil { + return + } + if buf[0] != 5 { + err = errors.UnsupportedError( + "invalid hashed material length " + strconv.Itoa(int(buf[0]))) + return + } + + // Read hashed material: signature type + creation time + if _, err = readFull(r, buf[:5]); err != nil { + return + } + sig.SigType = SignatureType(buf[0]) + t := binary.BigEndian.Uint32(buf[1:5]) + sig.CreationTime = time.Unix(int64(t), 0) + + // Eight-octet Key ID of signer. + if _, err = readFull(r, buf[:8]); err != nil { + return + } + sig.IssuerKeyId = binary.BigEndian.Uint64(buf[:]) + + // Public-key and hash algorithm + if _, err = readFull(r, buf[:2]); err != nil { + return + } + sig.PubKeyAlgo = PublicKeyAlgorithm(buf[0]) + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + default: + err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) + return + } + var ok bool + if sig.Hash, ok = s2k.HashIdToHash(buf[1]); !ok { + return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) + } + + // Two-octet field holding left 16 bits of signed hash value. + if _, err = readFull(r, sig.HashTag[:2]); err != nil { + return + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) + case PubKeyAlgoDSA: + if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil { + return + } + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) + default: + panic("unreachable") + } + return +} + +// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been +// called first. +func (sig *SignatureV3) Serialize(w io.Writer) (err error) { + buf := make([]byte, 8) + + // Write the sig type and creation time + buf[0] = byte(sig.SigType) + binary.BigEndian.PutUint32(buf[1:5], uint32(sig.CreationTime.Unix())) + if _, err = w.Write(buf[:5]); err != nil { + return + } + + // Write the issuer long key ID + binary.BigEndian.PutUint64(buf[:8], sig.IssuerKeyId) + if _, err = w.Write(buf[:8]); err != nil { + return + } + + // Write public key algorithm, hash ID, and hash value + buf[0] = byte(sig.PubKeyAlgo) + hashId, ok := s2k.HashToHashId(sig.Hash) + if !ok { + return errors.UnsupportedError(fmt.Sprintf("hash function %v", sig.Hash)) + } + buf[1] = hashId + copy(buf[2:4], sig.HashTag[:]) + if _, err = w.Write(buf[:4]); err != nil { + return + } + + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { + return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") + } + + switch sig.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: + err = writeMPIs(w, sig.RSASignature) + case PubKeyAlgoDSA: + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) + default: + panic("impossible") + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go new file mode 100644 index 0000000..4b1105b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -0,0 +1,155 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "crypto/cipher" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/s2k" +) + +// This is the largest session key that we'll support. Since no 512-bit cipher +// has even been seriously used, this is comfortably large. +const maxSessionKeySizeInBytes = 64 + +// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC +// 4880, section 5.3. +type SymmetricKeyEncrypted struct { + CipherFunc CipherFunction + s2k func(out, in []byte) + encryptedKey []byte +} + +const symmetricKeyEncryptedVersion = 4 + +func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { + // RFC 4880, section 5.3. + var buf [2]byte + if _, err := readFull(r, buf[:]); err != nil { + return err + } + if buf[0] != symmetricKeyEncryptedVersion { + return errors.UnsupportedError("SymmetricKeyEncrypted version") + } + ske.CipherFunc = CipherFunction(buf[1]) + + if ske.CipherFunc.KeySize() == 0 { + return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) + } + + var err error + ske.s2k, err = s2k.Parse(r) + if err != nil { + return err + } + + encryptedKey := make([]byte, maxSessionKeySizeInBytes) + // The session key may follow. We just have to try and read to find + // out. If it exists then we limit it to maxSessionKeySizeInBytes. + n, err := readFull(r, encryptedKey) + if err != nil && err != io.ErrUnexpectedEOF { + return err + } + + if n != 0 { + if n == maxSessionKeySizeInBytes { + return errors.UnsupportedError("oversized encrypted session key") + } + ske.encryptedKey = encryptedKey[:n] + } + + return nil +} + +// Decrypt attempts to decrypt an encrypted session key and returns the key and +// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data +// packet. +func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) { + key := make([]byte, ske.CipherFunc.KeySize()) + ske.s2k(key, passphrase) + + if len(ske.encryptedKey) == 0 { + return key, ske.CipherFunc, nil + } + + // the IV is all zeros + iv := make([]byte, ske.CipherFunc.blockSize()) + c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) + plaintextKey := make([]byte, len(ske.encryptedKey)) + c.XORKeyStream(plaintextKey, ske.encryptedKey) + cipherFunc := CipherFunction(plaintextKey[0]) + if cipherFunc.blockSize() == 0 { + return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + plaintextKey = plaintextKey[1:] + if l := len(plaintextKey); l == 0 || l%cipherFunc.blockSize() != 0 { + return nil, cipherFunc, errors.StructuralError("length of decrypted key not a multiple of block size") + } + + return plaintextKey, cipherFunc, nil +} + +// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The +// packet contains a random session key, encrypted by a key derived from the +// given passphrase. The session key is returned and must be passed to +// SerializeSymmetricallyEncrypted. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { + cipherFunc := config.Cipher() + keySize := cipherFunc.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) + } + + s2kBuf := new(bytes.Buffer) + keyEncryptingKey := make([]byte, keySize) + // s2k.Serialize salts and stretches the passphrase, and writes the + // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. + err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()}) + if err != nil { + return + } + s2kBytes := s2kBuf.Bytes() + + packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize + err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) + if err != nil { + return + } + + var buf [2]byte + buf[0] = symmetricKeyEncryptedVersion + buf[1] = byte(cipherFunc) + _, err = w.Write(buf[:]) + if err != nil { + return + } + _, err = w.Write(s2kBytes) + if err != nil { + return + } + + sessionKey := make([]byte, keySize) + _, err = io.ReadFull(config.Random(), sessionKey) + if err != nil { + return + } + iv := make([]byte, cipherFunc.blockSize()) + c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) + encryptedCipherAndKey := make([]byte, keySize+1) + c.XORKeyStream(encryptedCipherAndKey, buf[1:]) + c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) + _, err = w.Write(encryptedCipherAndKey) + if err != nil { + return + } + + key = sessionKey + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go new file mode 100644 index 0000000..6126030 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go @@ -0,0 +1,290 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "crypto/cipher" + "crypto/sha1" + "crypto/subtle" + "golang.org/x/crypto/openpgp/errors" + "hash" + "io" + "strconv" +) + +// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The +// encrypted contents will consist of more OpenPGP packets. See RFC 4880, +// sections 5.7 and 5.13. +type SymmetricallyEncrypted struct { + MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. + contents io.Reader + prefix []byte +} + +const symmetricallyEncryptedVersion = 1 + +func (se *SymmetricallyEncrypted) parse(r io.Reader) error { + if se.MDC { + // See RFC 4880, section 5.13. + var buf [1]byte + _, err := readFull(r, buf[:]) + if err != nil { + return err + } + if buf[0] != symmetricallyEncryptedVersion { + return errors.UnsupportedError("unknown SymmetricallyEncrypted version") + } + } + se.contents = r + return nil +} + +// Decrypt returns a ReadCloser, from which the decrypted contents of the +// packet can be read. An incorrect key can, with high probability, be detected +// immediately and this will result in a KeyIncorrect error being returned. +func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { + keySize := c.KeySize() + if keySize == 0 { + return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) + } + if len(key) != keySize { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") + } + + if se.prefix == nil { + se.prefix = make([]byte, c.blockSize()+2) + _, err := readFull(se.contents, se.prefix) + if err != nil { + return nil, err + } + } else if len(se.prefix) != c.blockSize()+2 { + return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") + } + + ocfbResync := OCFBResync + if se.MDC { + // MDC packets use a different form of OCFB mode. + ocfbResync = OCFBNoResync + } + + s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) + if s == nil { + return nil, errors.ErrKeyIncorrect + } + + plaintext := cipher.StreamReader{S: s, R: se.contents} + + if se.MDC { + // MDC packets have an embedded hash that we need to check. + h := sha1.New() + h.Write(se.prefix) + return &seMDCReader{in: plaintext, h: h}, nil + } + + // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. + return seReader{plaintext}, nil +} + +// seReader wraps an io.Reader with a no-op Close method. +type seReader struct { + in io.Reader +} + +func (ser seReader) Read(buf []byte) (int, error) { + return ser.in.Read(buf) +} + +func (ser seReader) Close() error { + return nil +} + +const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size + +// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold +// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an +// MDC packet containing a hash of the previous contents which is checked +// against the running hash. See RFC 4880, section 5.13. +type seMDCReader struct { + in io.Reader + h hash.Hash + trailer [mdcTrailerSize]byte + scratch [mdcTrailerSize]byte + trailerUsed int + error bool + eof bool +} + +func (ser *seMDCReader) Read(buf []byte) (n int, err error) { + if ser.error { + err = io.ErrUnexpectedEOF + return + } + if ser.eof { + err = io.EOF + return + } + + // If we haven't yet filled the trailer buffer then we must do that + // first. + for ser.trailerUsed < mdcTrailerSize { + n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) + ser.trailerUsed += n + if err == io.EOF { + if ser.trailerUsed != mdcTrailerSize { + n = 0 + err = io.ErrUnexpectedEOF + ser.error = true + return + } + ser.eof = true + n = 0 + return + } + + if err != nil { + n = 0 + return + } + } + + // If it's a short read then we read into a temporary buffer and shift + // the data into the caller's buffer. + if len(buf) <= mdcTrailerSize { + n, err = readFull(ser.in, ser.scratch[:len(buf)]) + copy(buf, ser.trailer[:n]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], ser.trailer[n:]) + copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) + if n < len(buf) { + ser.eof = true + err = io.EOF + } + return + } + + n, err = ser.in.Read(buf[mdcTrailerSize:]) + copy(buf, ser.trailer[:]) + ser.h.Write(buf[:n]) + copy(ser.trailer[:], buf[n:]) + + if err == io.EOF { + ser.eof = true + } + return +} + +// This is a new-format packet tag byte for a type 19 (MDC) packet. +const mdcPacketTagByte = byte(0x80) | 0x40 | 19 + +func (ser *seMDCReader) Close() error { + if ser.error { + return errors.SignatureError("error during reading") + } + + for !ser.eof { + // We haven't seen EOF so we need to read to the end + var buf [1024]byte + _, err := ser.Read(buf[:]) + if err == io.EOF { + break + } + if err != nil { + return errors.SignatureError("error during reading") + } + } + + if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { + return errors.SignatureError("MDC packet not found") + } + ser.h.Write(ser.trailer[:2]) + + final := ser.h.Sum(nil) + if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { + return errors.SignatureError("hash mismatch") + } + return nil +} + +// An seMDCWriter writes through to an io.WriteCloser while maintains a running +// hash of the data written. On close, it emits an MDC packet containing the +// running hash. +type seMDCWriter struct { + w io.WriteCloser + h hash.Hash +} + +func (w *seMDCWriter) Write(buf []byte) (n int, err error) { + w.h.Write(buf) + return w.w.Write(buf) +} + +func (w *seMDCWriter) Close() (err error) { + var buf [mdcTrailerSize]byte + + buf[0] = mdcPacketTagByte + buf[1] = sha1.Size + w.h.Write(buf[:2]) + digest := w.h.Sum(nil) + copy(buf[2:], digest) + + _, err = w.w.Write(buf[:]) + if err != nil { + return + } + return w.w.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} + +// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet +// to w and returns a WriteCloser to which the to-be-encrypted packets can be +// written. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { + if c.KeySize() != len(key) { + return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") + } + writeCloser := noOpCloser{w} + ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) + if err != nil { + return + } + + _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) + if err != nil { + return + } + + block := c.new(key) + blockSize := block.BlockSize() + iv := make([]byte, blockSize) + _, err = config.Random().Read(iv) + if err != nil { + return + } + s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) + _, err = ciphertext.Write(prefix) + if err != nil { + return + } + plaintext := cipher.StreamWriter{S: s, W: ciphertext} + + h := sha1.New() + h.Write(iv) + h.Write(iv[blockSize-2:]) + contents = &seMDCWriter{w: plaintext, h: h} + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go new file mode 100644 index 0000000..96a2b38 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userattribute.go @@ -0,0 +1,91 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "bytes" + "image" + "image/jpeg" + "io" + "io/ioutil" +) + +const UserAttrImageSubpacket = 1 + +// UserAttribute is capable of storing other types of data about a user +// beyond name, email and a text comment. In practice, user attributes are typically used +// to store a signed thumbnail photo JPEG image of the user. +// See RFC 4880, section 5.12. +type UserAttribute struct { + Contents []*OpaqueSubpacket +} + +// NewUserAttributePhoto creates a user attribute packet +// containing the given images. +func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { + uat = new(UserAttribute) + for _, photo := range photos { + var buf bytes.Buffer + // RFC 4880, Section 5.12.1. + data := []byte{ + 0x10, 0x00, // Little-endian image header length (16 bytes) + 0x01, // Image header version 1 + 0x01, // JPEG + 0, 0, 0, 0, // 12 reserved octets, must be all zero. + 0, 0, 0, 0, + 0, 0, 0, 0} + if _, err = buf.Write(data); err != nil { + return + } + if err = jpeg.Encode(&buf, photo, nil); err != nil { + return + } + uat.Contents = append(uat.Contents, &OpaqueSubpacket{ + SubType: UserAttrImageSubpacket, + Contents: buf.Bytes()}) + } + return +} + +// NewUserAttribute creates a new user attribute packet containing the given subpackets. +func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { + return &UserAttribute{Contents: contents} +} + +func (uat *UserAttribute) parse(r io.Reader) (err error) { + // RFC 4880, section 5.13 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uat.Contents, err = OpaqueSubpackets(b) + return +} + +// Serialize marshals the user attribute to w in the form of an OpenPGP packet, including +// header. +func (uat *UserAttribute) Serialize(w io.Writer) (err error) { + var buf bytes.Buffer + for _, sp := range uat.Contents { + sp.Serialize(&buf) + } + if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { + return err + } + _, err = w.Write(buf.Bytes()) + return +} + +// ImageData returns zero or more byte slices, each containing +// JPEG File Interchange Format (JFIF), for each photo in the +// the user attribute packet. +func (uat *UserAttribute) ImageData() (imageData [][]byte) { + for _, sp := range uat.Contents { + if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { + imageData = append(imageData, sp.Contents[16:]) + } + } + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userid.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userid.go new file mode 100644 index 0000000..d6bea7d --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/packet/userid.go @@ -0,0 +1,160 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "io" + "io/ioutil" + "strings" +) + +// UserId contains text that is intended to represent the name and email +// address of the key holder. See RFC 4880, section 5.11. By convention, this +// takes the form "Full Name (Comment) " +type UserId struct { + Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. + + Name, Comment, Email string +} + +func hasInvalidCharacters(s string) bool { + for _, c := range s { + switch c { + case '(', ')', '<', '>', 0: + return true + } + } + return false +} + +// NewUserId returns a UserId or nil if any of the arguments contain invalid +// characters. The invalid characters are '\x00', '(', ')', '<' and '>' +func NewUserId(name, comment, email string) *UserId { + // RFC 4880 doesn't deal with the structure of userid strings; the + // name, comment and email form is just a convention. However, there's + // no convention about escaping the metacharacters and GPG just refuses + // to create user ids where, say, the name contains a '('. We mirror + // this behaviour. + + if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { + return nil + } + + uid := new(UserId) + uid.Name, uid.Comment, uid.Email = name, comment, email + uid.Id = name + if len(comment) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "(" + uid.Id += comment + uid.Id += ")" + } + if len(email) > 0 { + if len(uid.Id) > 0 { + uid.Id += " " + } + uid.Id += "<" + uid.Id += email + uid.Id += ">" + } + return uid +} + +func (uid *UserId) parse(r io.Reader) (err error) { + // RFC 4880, section 5.11 + b, err := ioutil.ReadAll(r) + if err != nil { + return + } + uid.Id = string(b) + uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) + return +} + +// Serialize marshals uid to w in the form of an OpenPGP packet, including +// header. +func (uid *UserId) Serialize(w io.Writer) error { + err := serializeHeader(w, packetTypeUserId, len(uid.Id)) + if err != nil { + return err + } + _, err = w.Write([]byte(uid.Id)) + return err +} + +// parseUserId extracts the name, comment and email from a user id string that +// is formatted as "Full Name (Comment) ". +func parseUserId(id string) (name, comment, email string) { + var n, c, e struct { + start, end int + } + var state int + + for offset, rune := range id { + switch state { + case 0: + // Entering name + n.start = offset + state = 1 + fallthrough + case 1: + // In name + if rune == '(' { + state = 2 + n.end = offset + } else if rune == '<' { + state = 5 + n.end = offset + } + case 2: + // Entering comment + c.start = offset + state = 3 + fallthrough + case 3: + // In comment + if rune == ')' { + state = 4 + c.end = offset + } + case 4: + // Between comment and email + if rune == '<' { + state = 5 + } + case 5: + // Entering email + e.start = offset + state = 6 + fallthrough + case 6: + // In email + if rune == '>' { + state = 7 + e.end = offset + } + default: + // After email + } + } + switch state { + case 1: + // ended in the name + n.end = len(id) + case 3: + // ended in comment + c.end = len(id) + case 6: + // ended in email + e.end = len(id) + } + + name = strings.TrimSpace(id[n.start:n.end]) + comment = strings.TrimSpace(id[c.start:c.end]) + email = strings.TrimSpace(id[e.start:e.end]) + return +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/read.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/read.go new file mode 100644 index 0000000..6ec664f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/read.go @@ -0,0 +1,442 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package openpgp implements high level operations on OpenPGP messages. +package openpgp // import "golang.org/x/crypto/openpgp" + +import ( + "crypto" + _ "crypto/sha256" + "hash" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" +) + +// SignatureType is the armor type for a PGP signature. +var SignatureType = "PGP SIGNATURE" + +// readArmored reads an armored block with the given type. +func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) { + block, err := armor.Decode(r) + if err != nil { + return + } + + if block.Type != expectedType { + return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) + } + + return block.Body, nil +} + +// MessageDetails contains the result of parsing an OpenPGP encrypted and/or +// signed message. +type MessageDetails struct { + IsEncrypted bool // true if the message was encrypted. + EncryptedToKeyIds []uint64 // the list of recipient key ids. + IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. + DecryptedWith Key // the private key used to decrypt the message, if any. + IsSigned bool // true if the message is signed. + SignedByKeyId uint64 // the key id of the signer, if any. + SignedBy *Key // the key of the signer, if available. + LiteralData *packet.LiteralData // the metadata of the contents + UnverifiedBody io.Reader // the contents of the message. + + // If IsSigned is true and SignedBy is non-zero then the signature will + // be verified as UnverifiedBody is read. The signature cannot be + // checked until the whole of UnverifiedBody is read so UnverifiedBody + // must be consumed until EOF before the data can be trusted. Even if a + // message isn't signed (or the signer is unknown) the data may contain + // an authentication code that is only checked once UnverifiedBody has + // been consumed. Once EOF has been seen, the following fields are + // valid. (An authentication code failure is reported as a + // SignatureError error when reading from UnverifiedBody.) + SignatureError error // nil if the signature is good. + Signature *packet.Signature // the signature packet itself, if v4 (default) + SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature + + decrypted io.ReadCloser +} + +// A PromptFunction is used as a callback by functions that may need to decrypt +// a private key, or prompt for a passphrase. It is called with a list of +// acceptable, encrypted private keys and a boolean that indicates whether a +// passphrase is usable. It should either decrypt a private key or return a +// passphrase to try. If the decrypted private key or given passphrase isn't +// correct, the function will be called again, forever. Any error returned will +// be passed up. +type PromptFunction func(keys []Key, symmetric bool) ([]byte, error) + +// A keyEnvelopePair is used to store a private key with the envelope that +// contains a symmetric key, encrypted with that key. +type keyEnvelopePair struct { + key Key + encryptedKey *packet.EncryptedKey +} + +// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. +// The given KeyRing should contain both public keys (for signature +// verification) and, possibly encrypted, private keys for decrypting. +// If config is nil, sensible defaults will be used. +func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { + var p packet.Packet + + var symKeys []*packet.SymmetricKeyEncrypted + var pubKeys []keyEnvelopePair + var se *packet.SymmetricallyEncrypted + + packets := packet.NewReader(r) + md = new(MessageDetails) + md.IsEncrypted = true + + // The message, if encrypted, starts with a number of packets + // containing an encrypted decryption key. The decryption key is either + // encrypted to a public key, or with a passphrase. This loop + // collects these packets. +ParsePackets: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.SymmetricKeyEncrypted: + // This packet contains the decryption key encrypted with a passphrase. + md.IsSymmetricallyEncrypted = true + symKeys = append(symKeys, p) + case *packet.EncryptedKey: + // This packet contains the decryption key encrypted to a public key. + md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) + switch p.Algo { + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: + break + default: + continue + } + var keys []Key + if p.KeyId == 0 { + keys = keyring.DecryptionKeys() + } else { + keys = keyring.KeysById(p.KeyId) + } + for _, k := range keys { + pubKeys = append(pubKeys, keyEnvelopePair{k, p}) + } + case *packet.SymmetricallyEncrypted: + se = p + break ParsePackets + case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: + // This message isn't encrypted. + if len(symKeys) != 0 || len(pubKeys) != 0 { + return nil, errors.StructuralError("key material not followed by encrypted message") + } + packets.Unread(p) + return readSignedMessage(packets, nil, keyring) + } + } + + var candidates []Key + var decrypted io.ReadCloser + + // Now that we have the list of encrypted keys we need to decrypt at + // least one of them or, if we cannot, we need to call the prompt + // function so that it can decrypt a key or give us a passphrase. +FindKey: + for { + // See if any of the keys already have a private key available + candidates = candidates[:0] + candidateFingerprints := make(map[string]bool) + + for _, pk := range pubKeys { + if pk.key.PrivateKey == nil { + continue + } + if !pk.key.PrivateKey.Encrypted { + if len(pk.encryptedKey.Key) == 0 { + pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) + } + if len(pk.encryptedKey.Key) == 0 { + continue + } + decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + md.DecryptedWith = pk.key + break FindKey + } + } else { + fpr := string(pk.key.PublicKey.Fingerprint[:]) + if v := candidateFingerprints[fpr]; v { + continue + } + candidates = append(candidates, pk.key) + candidateFingerprints[fpr] = true + } + } + + if len(candidates) == 0 && len(symKeys) == 0 { + return nil, errors.ErrKeyIncorrect + } + + if prompt == nil { + return nil, errors.ErrKeyIncorrect + } + + passphrase, err := prompt(candidates, len(symKeys) != 0) + if err != nil { + return nil, err + } + + // Try the symmetric passphrase first + if len(symKeys) != 0 && passphrase != nil { + for _, s := range symKeys { + key, cipherFunc, err := s.Decrypt(passphrase) + if err == nil { + decrypted, err = se.Decrypt(cipherFunc, key) + if err != nil && err != errors.ErrKeyIncorrect { + return nil, err + } + if decrypted != nil { + break FindKey + } + } + + } + } + } + + md.decrypted = decrypted + if err := packets.Push(decrypted); err != nil { + return nil, err + } + return readSignedMessage(packets, md, keyring) +} + +// readSignedMessage reads a possibly signed message if mdin is non-zero then +// that structure is updated and returned. Otherwise a fresh MessageDetails is +// used. +func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) { + if mdin == nil { + mdin = new(MessageDetails) + } + md = mdin + + var p packet.Packet + var h hash.Hash + var wrappedHash hash.Hash +FindLiteralData: + for { + p, err = packets.Next() + if err != nil { + return nil, err + } + switch p := p.(type) { + case *packet.Compressed: + if err := packets.Push(p.Body); err != nil { + return nil, err + } + case *packet.OnePassSignature: + if !p.IsLast { + return nil, errors.UnsupportedError("nested signatures") + } + + h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) + if err != nil { + md = nil + return + } + + md.IsSigned = true + md.SignedByKeyId = p.KeyId + keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign) + if len(keys) > 0 { + md.SignedBy = &keys[0] + } + case *packet.LiteralData: + md.LiteralData = p + break FindLiteralData + } + } + + if md.SignedBy != nil { + md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} + } else if md.decrypted != nil { + md.UnverifiedBody = checkReader{md} + } else { + md.UnverifiedBody = md.LiteralData.Body + } + + return md, nil +} + +// hashForSignature returns a pair of hashes that can be used to verify a +// signature. The signature may specify that the contents of the signed message +// should be preprocessed (i.e. to normalize line endings). Thus this function +// returns two hashes. The second should be used to hash the message itself and +// performs any needed preprocessing. +func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { + if !hashId.Available() { + return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) + } + h := hashId.New() + + switch sigType { + case packet.SigTypeBinary: + return h, h, nil + case packet.SigTypeText: + return h, NewCanonicalTextHash(h), nil + } + + return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) +} + +// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF +// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger +// MDC checks. +type checkReader struct { + md *MessageDetails +} + +func (cr checkReader) Read(buf []byte) (n int, err error) { + n, err = cr.md.LiteralData.Body.Read(buf) + if err == io.EOF { + mdcErr := cr.md.decrypted.Close() + if mdcErr != nil { + err = mdcErr + } + } + return +} + +// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes +// the data as it is read. When it sees an EOF from the underlying io.Reader +// it parses and checks a trailing Signature packet and triggers any MDC checks. +type signatureCheckReader struct { + packets *packet.Reader + h, wrappedHash hash.Hash + md *MessageDetails +} + +func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) { + n, err = scr.md.LiteralData.Body.Read(buf) + scr.wrappedHash.Write(buf[:n]) + if err == io.EOF { + var p packet.Packet + p, scr.md.SignatureError = scr.packets.Next() + if scr.md.SignatureError != nil { + return + } + + var ok bool + if scr.md.Signature, ok = p.(*packet.Signature); ok { + scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) + } else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok { + scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3) + } else { + scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature") + return + } + + // The SymmetricallyEncrypted packet, if any, might have an + // unsigned hash of its own. In order to check this we need to + // close that Reader. + if scr.md.decrypted != nil { + mdcErr := scr.md.decrypted.Close() + if mdcErr != nil { + err = mdcErr + } + } + } + return +} + +// CheckDetachedSignature takes a signed file and a detached signature and +// returns the signer if the signature is valid. If the signer isn't known, +// ErrUnknownIssuer is returned. +func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { + var issuerKeyId uint64 + var hashFunc crypto.Hash + var sigType packet.SignatureType + var keys []Key + var p packet.Packet + + packets := packet.NewReader(signature) + for { + p, err = packets.Next() + if err == io.EOF { + return nil, errors.ErrUnknownIssuer + } + if err != nil { + return nil, err + } + + switch sig := p.(type) { + case *packet.Signature: + if sig.IssuerKeyId == nil { + return nil, errors.StructuralError("signature doesn't have an issuer") + } + issuerKeyId = *sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + case *packet.SignatureV3: + issuerKeyId = sig.IssuerKeyId + hashFunc = sig.Hash + sigType = sig.SigType + default: + return nil, errors.StructuralError("non signature packet found") + } + + keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) + if len(keys) > 0 { + break + } + } + + if len(keys) == 0 { + panic("unreachable") + } + + h, wrappedHash, err := hashForSignature(hashFunc, sigType) + if err != nil { + return nil, err + } + + if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { + return nil, err + } + + for _, key := range keys { + switch sig := p.(type) { + case *packet.Signature: + err = key.PublicKey.VerifySignature(h, sig) + case *packet.SignatureV3: + err = key.PublicKey.VerifySignatureV3(h, sig) + default: + panic("unreachable") + } + + if err == nil { + return key.Entity, nil + } + } + + return nil, err +} + +// CheckArmoredDetachedSignature performs the same actions as +// CheckDetachedSignature but expects the signature to be armored. +func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { + body, err := readArmored(signature, SignatureType) + if err != nil { + return + } + + return CheckDetachedSignature(keyring, signed, body) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go new file mode 100644 index 0000000..4b9a44c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go @@ -0,0 +1,273 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package s2k implements the various OpenPGP string-to-key transforms as +// specified in RFC 4800 section 3.7.1. +package s2k // import "golang.org/x/crypto/openpgp/s2k" + +import ( + "crypto" + "hash" + "io" + "strconv" + + "golang.org/x/crypto/openpgp/errors" +) + +// Config collects configuration parameters for s2k key-stretching +// transformatioms. A nil *Config is valid and results in all default +// values. Currently, Config is used only by the Serialize function in +// this package. +type Config struct { + // Hash is the default hash function to be used. If + // nil, SHA1 is used. + Hash crypto.Hash + // S2KCount is only used for symmetric encryption. It + // determines the strength of the passphrase stretching when + // the said passphrase is hashed to produce a key. S2KCount + // should be between 1024 and 65011712, inclusive. If Config + // is nil or S2KCount is 0, the value 65536 used. Not all + // values in the above range can be represented. S2KCount will + // be rounded up to the next representable value if it cannot + // be encoded exactly. When set, it is strongly encrouraged to + // use a value that is at least 65536. See RFC 4880 Section + // 3.7.1.3. + S2KCount int +} + +func (c *Config) hash() crypto.Hash { + if c == nil || uint(c.Hash) == 0 { + // SHA1 is the historical default in this package. + return crypto.SHA1 + } + + return c.Hash +} + +func (c *Config) encodedCount() uint8 { + if c == nil || c.S2KCount == 0 { + return 96 // The common case. Correspoding to 65536 + } + + i := c.S2KCount + switch { + // Behave like GPG. Should we make 65536 the lowest value used? + case i < 1024: + i = 1024 + case i > 65011712: + i = 65011712 + } + + return encodeCount(i) +} + +// encodeCount converts an iterative "count" in the range 1024 to +// 65011712, inclusive, to an encoded count. The return value is the +// octet that is actually stored in the GPG file. encodeCount panics +// if i is not in the above range (encodedCount above takes care to +// pass i in the correct range). See RFC 4880 Section 3.7.7.1. +func encodeCount(i int) uint8 { + if i < 1024 || i > 65011712 { + panic("count arg i outside the required range") + } + + for encoded := 0; encoded < 256; encoded++ { + count := decodeCount(uint8(encoded)) + if count >= i { + return uint8(encoded) + } + } + + return 255 +} + +// decodeCount returns the s2k mode 3 iterative "count" corresponding to +// the encoded octet c. +func decodeCount(c uint8) int { + return (16 + int(c&15)) << (uint32(c>>4) + 6) +} + +// Simple writes to out the result of computing the Simple S2K function (RFC +// 4880, section 3.7.1.1) using the given hash and input passphrase. +func Simple(out []byte, h hash.Hash, in []byte) { + Salted(out, h, in, nil) +} + +var zero [1]byte + +// Salted writes to out the result of computing the Salted S2K function (RFC +// 4880, section 3.7.1.2) using the given hash, input passphrase and salt. +func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { + done := 0 + var digest []byte + + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + h.Write(salt) + h.Write(in) + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Iterated writes to out the result of computing the Iterated and Salted S2K +// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, +// salt and iteration count. +func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { + combined := make([]byte, len(in)+len(salt)) + copy(combined, salt) + copy(combined[len(salt):], in) + + if count < len(combined) { + count = len(combined) + } + + done := 0 + var digest []byte + for i := 0; done < len(out); i++ { + h.Reset() + for j := 0; j < i; j++ { + h.Write(zero[:]) + } + written := 0 + for written < count { + if written+len(combined) > count { + todo := count - written + h.Write(combined[:todo]) + written = count + } else { + h.Write(combined) + written += len(combined) + } + } + digest = h.Sum(digest[:0]) + n := copy(out[done:], digest) + done += n + } +} + +// Parse reads a binary specification for a string-to-key transformation from r +// and returns a function which performs that transform. +func Parse(r io.Reader) (f func(out, in []byte), err error) { + var buf [9]byte + + _, err = io.ReadFull(r, buf[:2]) + if err != nil { + return + } + + hash, ok := HashIdToHash(buf[1]) + if !ok { + return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) + } + if !hash.Available() { + return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) + } + h := hash.New() + + switch buf[0] { + case 0: + f := func(out, in []byte) { + Simple(out, h, in) + } + return f, nil + case 1: + _, err = io.ReadFull(r, buf[:8]) + if err != nil { + return + } + f := func(out, in []byte) { + Salted(out, h, in, buf[:8]) + } + return f, nil + case 3: + _, err = io.ReadFull(r, buf[:9]) + if err != nil { + return + } + count := decodeCount(buf[8]) + f := func(out, in []byte) { + Iterated(out, h, in, buf[:8], count) + } + return f, nil + } + + return nil, errors.UnsupportedError("S2K function") +} + +// Serialize salts and stretches the given passphrase and writes the +// resulting key into key. It also serializes an S2K descriptor to +// w. The key stretching can be configured with c, which may be +// nil. In that case, sensible defaults will be used. +func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error { + var buf [11]byte + buf[0] = 3 /* iterated and salted */ + buf[1], _ = HashToHashId(c.hash()) + salt := buf[2:10] + if _, err := io.ReadFull(rand, salt); err != nil { + return err + } + encodedCount := c.encodedCount() + count := decodeCount(encodedCount) + buf[10] = encodedCount + if _, err := w.Write(buf[:]); err != nil { + return err + } + + Iterated(key, c.hash().New(), passphrase, salt, count) + return nil +} + +// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with +// Go's crypto.Hash type. See RFC 4880, section 9.4. +var hashToHashIdMapping = []struct { + id byte + hash crypto.Hash + name string +}{ + {1, crypto.MD5, "MD5"}, + {2, crypto.SHA1, "SHA1"}, + {3, crypto.RIPEMD160, "RIPEMD160"}, + {8, crypto.SHA256, "SHA256"}, + {9, crypto.SHA384, "SHA384"}, + {10, crypto.SHA512, "SHA512"}, + {11, crypto.SHA224, "SHA224"}, +} + +// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP +// hash id. +func HashIdToHash(id byte) (h crypto.Hash, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.hash, true + } + } + return 0, false +} + +// HashIdToString returns the name of the hash function corresponding to the +// given OpenPGP hash id. +func HashIdToString(id byte) (name string, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.name, true + } + } + + return "", false +} + +// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. +func HashToHashId(h crypto.Hash) (id byte, ok bool) { + for _, m := range hashToHashIdMapping { + if m.hash == h { + return m.id, true + } + } + return 0, false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/write.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/write.go new file mode 100644 index 0000000..65a304c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/openpgp/write.go @@ -0,0 +1,378 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "crypto" + "hash" + "io" + "strconv" + "time" + + "golang.org/x/crypto/openpgp/armor" + "golang.org/x/crypto/openpgp/errors" + "golang.org/x/crypto/openpgp/packet" + "golang.org/x/crypto/openpgp/s2k" +) + +// DetachSign signs message with the private key from signer (which must +// already have been decrypted) and writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// ArmoredDetachSign signs message with the private key from signer (which +// must already have been decrypted) and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { + return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) +} + +// DetachSignText signs message (after canonicalising the line endings) with +// the private key from signer (which must already have been decrypted) and +// writes the signature to w. +// If config is nil, sensible defaults will be used. +func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return detachSign(w, signer, message, packet.SigTypeText, config) +} + +// ArmoredDetachSignText signs message (after canonicalising the line endings) +// with the private key from signer (which must already have been decrypted) +// and writes an armored signature to w. +// If config is nil, sensible defaults will be used. +func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { + return armoredDetachSign(w, signer, message, packet.SigTypeText, config) +} + +func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + out, err := armor.Encode(w, SignatureType, nil) + if err != nil { + return + } + err = detachSign(out, signer, message, sigType, config) + if err != nil { + return + } + return out.Close() +} + +func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { + if signer.PrivateKey == nil { + return errors.InvalidArgumentError("signing key doesn't have a private key") + } + if signer.PrivateKey.Encrypted { + return errors.InvalidArgumentError("signing key is encrypted") + } + + sig := new(packet.Signature) + sig.SigType = sigType + sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo + sig.Hash = config.Hash() + sig.CreationTime = config.Now() + sig.IssuerKeyId = &signer.PrivateKey.KeyId + + h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) + if err != nil { + return + } + io.Copy(wrappedHash, message) + + err = sig.Sign(h, signer.PrivateKey, config) + if err != nil { + return + } + + return sig.Serialize(w) +} + +// FileHints contains metadata about encrypted files. This metadata is, itself, +// encrypted. +type FileHints struct { + // IsBinary can be set to hint that the contents are binary data. + IsBinary bool + // FileName hints at the name of the file that should be written. It's + // truncated to 255 bytes if longer. It may be empty to suggest that the + // file should not be written to disk. It may be equal to "_CONSOLE" to + // suggest the data should not be written to disk. + FileName string + // ModTime contains the modification time of the file, or the zero time if not applicable. + ModTime time.Time +} + +// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. +// The resulting WriteCloser must be closed after the contents of the file have +// been written. +// If config is nil, sensible defaults will be used. +func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + if hints == nil { + hints = &FileHints{} + } + + key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) + if err != nil { + return + } + w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) + if err != nil { + return + } + + literaldata := w + if algo := config.Compression(); algo != packet.CompressionNone { + var compConfig *packet.CompressionConfig + if config != nil { + compConfig = config.CompressionConfig + } + literaldata, err = packet.SerializeCompressed(w, algo, compConfig) + if err != nil { + return + } + } + + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) +} + +// intersectPreferences mutates and returns a prefix of a that contains only +// the values in the intersection of a and b. The order of a is preserved. +func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { + var j int + for _, v := range a { + for _, v2 := range b { + if v == v2 { + a[j] = v + j++ + break + } + } + } + + return a[:j] +} + +func hashToHashId(h crypto.Hash) uint8 { + v, ok := s2k.HashToHashId(h) + if !ok { + panic("tried to convert unknown hash") + } + return v +} + +// Encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +// If config is nil, sensible defaults will be used. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { + var signer *packet.PrivateKey + if signed != nil { + signKey, ok := signed.signingKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("no valid signing keys") + } + signer = signKey.PrivateKey + if signer == nil { + return nil, errors.InvalidArgumentError("no private key in signing key") + } + if signer.Encrypted { + return nil, errors.InvalidArgumentError("signing key must be decrypted") + } + } + + // These are the possible ciphers that we'll use for the message. + candidateCiphers := []uint8{ + uint8(packet.CipherAES128), + uint8(packet.CipherAES256), + uint8(packet.CipherCAST5), + } + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + // In the event that a recipient doesn't specify any supported ciphers + // or hash functions, these are the ones that we assume that every + // implementation supports. + defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] + defaultHashes := candidateHashes[len(candidateHashes)-1:] + + encryptKeys := make([]Key, len(to)) + for i := range to { + var ok bool + encryptKeys[i], ok = to[i].encryptionKey(config.Now()) + if !ok { + return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") + } + + sig := to[i].primaryIdentity().SelfSignature + + preferredSymmetric := sig.PreferredSymmetric + if len(preferredSymmetric) == 0 { + preferredSymmetric = defaultCiphers + } + preferredHashes := sig.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + } + + if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { + return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") + } + + cipher := packet.CipherFunction(candidateCiphers[0]) + // If the cipher specified by config is a candidate, we'll use that. + configuredCipher := config.Cipher() + for _, c := range candidateCiphers { + cipherFunc := packet.CipherFunction(c) + if cipherFunc == configuredCipher { + cipher = cipherFunc + break + } + } + + var hash crypto.Hash + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { + hash = h + break + } + } + + // If the hash specified by config is a candidate, we'll use that. + if configuredHash := config.Hash(); configuredHash.Available() { + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { + hash = h + break + } + } + } + + if hash == 0 { + hashId := candidateHashes[0] + name, ok := s2k.HashIdToString(hashId) + if !ok { + name = "#" + strconv.Itoa(int(hashId)) + } + return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") + } + + symKey := make([]byte, cipher.KeySize()) + if _, err := io.ReadFull(config.Random(), symKey); err != nil { + return nil, err + } + + for _, key := range encryptKeys { + if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { + return nil, err + } + } + + encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) + if err != nil { + return + } + + if signer != nil { + ops := &packet.OnePassSignature{ + SigType: packet.SigTypeBinary, + Hash: hash, + PubKeyAlgo: signer.PubKeyAlgo, + KeyId: signer.KeyId, + IsLast: true, + } + if err := ops.Serialize(encryptedData); err != nil { + return nil, err + } + } + + if hints == nil { + hints = &FileHints{} + } + + w := encryptedData + if signer != nil { + // If we need to write a signature packet after the literal + // data then we need to stop literalData from closing + // encryptedData. + w = noOpCloser{encryptedData} + + } + var epochSeconds uint32 + if !hints.ModTime.IsZero() { + epochSeconds = uint32(hints.ModTime.Unix()) + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) + if err != nil { + return nil, err + } + + if signer != nil { + return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil + } + return literalData, nil +} + +// signatureWriter hashes the contents of a message while passing it along to +// literalData. When closed, it closes literalData, writes a signature packet +// to encryptedData and then also closes encryptedData. +type signatureWriter struct { + encryptedData io.WriteCloser + literalData io.WriteCloser + hashType crypto.Hash + h hash.Hash + signer *packet.PrivateKey + config *packet.Config +} + +func (s signatureWriter) Write(data []byte) (int, error) { + s.h.Write(data) + return s.literalData.Write(data) +} + +func (s signatureWriter) Close() error { + sig := &packet.Signature{ + SigType: packet.SigTypeBinary, + PubKeyAlgo: s.signer.PubKeyAlgo, + Hash: s.hashType, + CreationTime: s.config.Now(), + IssuerKeyId: &s.signer.KeyId, + } + + if err := sig.Sign(s.h, s.signer, s.config); err != nil { + return err + } + if err := s.literalData.Close(); err != nil { + return err + } + if err := sig.Serialize(s.encryptedData); err != nil { + return err + } + return s.encryptedData.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +// TODO: we have two of these in OpenPGP packages alone. This probably needs +// to be promoted somewhere more common. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() error { + return nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go new file mode 100644 index 0000000..593f653 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go @@ -0,0 +1,77 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC +2898 / PKCS #5 v2.0. + +A key derivation function is useful when encrypting data based on a password +or any other not-fully-random data. It uses a pseudorandom function to derive +a secure encryption key based on the password. + +While v2.0 of the standard defines only one pseudorandom function to use, +HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved +Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To +choose, you can pass the `New` functions from the different SHA packages to +pbkdf2.Key. +*/ +package pbkdf2 // import "golang.org/x/crypto/pbkdf2" + +import ( + "crypto/hmac" + "hash" +) + +// Key derives a key from the password, salt and iteration count, returning a +// []byte of length keylen that can be used as cryptographic key. The key is +// derived based on the method described as PBKDF2 with the HMAC variant using +// the supplied hash function. +// +// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you +// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by +// doing: +// +// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) +// +// Remember to get a good random salt. At least 8 bytes is recommended by the +// RFC. +// +// Using a higher iteration count will increase the cost of an exhaustive +// search but will also make derivation proportionally slower. +func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { + prf := hmac.New(h, password) + hashLen := prf.Size() + numBlocks := (keyLen + hashLen - 1) / hashLen + + var buf [4]byte + dk := make([]byte, 0, numBlocks*hashLen) + U := make([]byte, hashLen) + for block := 1; block <= numBlocks; block++ { + // N.B.: || means concatenation, ^ means XOR + // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter + // U_1 = PRF(password, salt || uint(i)) + prf.Reset() + prf.Write(salt) + buf[0] = byte(block >> 24) + buf[1] = byte(block >> 16) + buf[2] = byte(block >> 8) + buf[3] = byte(block) + prf.Write(buf[:4]) + dk = prf.Sum(dk) + T := dk[len(dk)-hashLen:] + copy(U, T) + + // U_n = PRF(password, U_(n-1)) + for n := 2; n <= iter; n++ { + prf.Reset() + prf.Write(U) + U = U[:0] + U = prf.Sum(U) + for x := range U { + T[x] ^= U[x] + } + } + } + return dk[:keyLen] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160.go new file mode 100644 index 0000000..6c6e842 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160.go @@ -0,0 +1,120 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ripemd160 implements the RIPEMD-160 hash algorithm. +package ripemd160 // import "golang.org/x/crypto/ripemd160" + +// RIPEMD-160 is designed by by Hans Dobbertin, Antoon Bosselaers, and Bart +// Preneel with specifications available at: +// http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf. + +import ( + "crypto" + "hash" +) + +func init() { + crypto.RegisterHash(crypto.RIPEMD160, New) +} + +// The size of the checksum in bytes. +const Size = 20 + +// The block size of the hash algorithm in bytes. +const BlockSize = 64 + +const ( + _s0 = 0x67452301 + _s1 = 0xefcdab89 + _s2 = 0x98badcfe + _s3 = 0x10325476 + _s4 = 0xc3d2e1f0 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + s [5]uint32 // running context + x [BlockSize]byte // temporary buffer + nx int // index into x + tc uint64 // total count of bytes processed +} + +func (d *digest) Reset() { + d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4 + d.nx = 0 + d.tc = 0 +} + +// New returns a new hash.Hash computing the checksum. +func New() hash.Hash { + result := new(digest) + result.Reset() + return result +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.tc += uint64(nn) + if d.nx > 0 { + n := len(p) + if n > BlockSize-d.nx { + n = BlockSize - d.nx + } + for i := 0; i < n; i++ { + d.x[d.nx+i] = p[i] + } + d.nx += n + if d.nx == BlockSize { + _Block(d, d.x[0:]) + d.nx = 0 + } + p = p[n:] + } + n := _Block(d, p) + p = p[n:] + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +func (d0 *digest) Sum(in []byte) []byte { + // Make a copy of d0 so that caller can keep writing and summing. + d := *d0 + + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + tc := d.tc + var tmp [64]byte + tmp[0] = 0x80 + if tc%64 < 56 { + d.Write(tmp[0 : 56-tc%64]) + } else { + d.Write(tmp[0 : 64+56-tc%64]) + } + + // Length in bits. + tc <<= 3 + for i := uint(0); i < 8; i++ { + tmp[i] = byte(tc >> (8 * i)) + } + d.Write(tmp[0:8]) + + if d.nx != 0 { + panic("d.nx != 0") + } + + var digest [Size]byte + for i, s := range d.s { + digest[i*4] = byte(s) + digest[i*4+1] = byte(s >> 8) + digest[i*4+2] = byte(s >> 16) + digest[i*4+3] = byte(s >> 24) + } + + return append(in, digest[:]...) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160block.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160block.go new file mode 100644 index 0000000..7bc8e6c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/ripemd160/ripemd160block.go @@ -0,0 +1,161 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// RIPEMD-160 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package ripemd160 + +// work buffer indices and roll amounts for one line +var _n = [80]uint{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, +} + +var _r = [80]uint{ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, +} + +// same for the other parallel one +var n_ = [80]uint{ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, +} + +var r_ = [80]uint{ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, +} + +func _Block(md *digest, p []byte) int { + n := 0 + var x [16]uint32 + var alpha, beta uint32 + for len(p) >= BlockSize { + a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4] + aa, bb, cc, dd, ee := a, b, c, d, e + j := 0 + for i := 0; i < 16; i++ { + x[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 + j += 4 + } + + // round 1 + i := 0 + for i < 16 { + alpha = a + (b ^ c ^ d) + x[_n[i]] + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 2 + for i < 32 { + alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999 + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 3 + for i < 48 { + alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1 + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 4 + for i < 64 { + alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9 + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // round 5 + for i < 80 { + alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e + s := _r[i] + alpha = (alpha<>(32-s)) + e + beta = c<<10 | c>>22 + a, b, c, d, e = e, alpha, b, beta, d + + // parallel line + alpha = aa + (bb ^ cc ^ dd) + x[n_[i]] + s = r_[i] + alpha = (alpha<>(32-s)) + ee + beta = cc<<10 | cc>>22 + aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd + + i++ + } + + // combine results + dd += c + md.s[1] + md.s[1] = md.s[2] + d + ee + md.s[2] = md.s[3] + e + aa + md.s[3] = md.s[4] + a + bb + md.s[4] = md.s[0] + b + cc + md.s[0] = dd + + p = p[BlockSize:] + n += BlockSize + } + return n +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt/scrypt.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt/scrypt.go new file mode 100644 index 0000000..7455395 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt/scrypt.go @@ -0,0 +1,243 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package scrypt implements the scrypt key derivation function as defined in +// Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard +// Functions" (http://www.tarsnap.com/scrypt/scrypt.pdf). +package scrypt // import "golang.org/x/crypto/scrypt" + +import ( + "crypto/sha256" + "errors" + + "golang.org/x/crypto/pbkdf2" +) + +const maxInt = int(^uint(0) >> 1) + +// blockCopy copies n numbers from src into dst. +func blockCopy(dst, src []uint32, n int) { + copy(dst, src[:n]) +} + +// blockXOR XORs numbers from dst with n numbers from src. +func blockXOR(dst, src []uint32, n int) { + for i, v := range src[:n] { + dst[i] ^= v + } +} + +// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, +// and puts the result into both both tmp and out. +func salsaXOR(tmp *[16]uint32, in, out []uint32) { + w0 := tmp[0] ^ in[0] + w1 := tmp[1] ^ in[1] + w2 := tmp[2] ^ in[2] + w3 := tmp[3] ^ in[3] + w4 := tmp[4] ^ in[4] + w5 := tmp[5] ^ in[5] + w6 := tmp[6] ^ in[6] + w7 := tmp[7] ^ in[7] + w8 := tmp[8] ^ in[8] + w9 := tmp[9] ^ in[9] + w10 := tmp[10] ^ in[10] + w11 := tmp[11] ^ in[11] + w12 := tmp[12] ^ in[12] + w13 := tmp[13] ^ in[13] + w14 := tmp[14] ^ in[14] + w15 := tmp[15] ^ in[15] + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8 + x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15 + + for i := 0; i < 8; i += 2 { + u := x0 + x12 + x4 ^= u<<7 | u>>(32-7) + u = x4 + x0 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x4 + x12 ^= u<<13 | u>>(32-13) + u = x12 + x8 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x1 + x9 ^= u<<7 | u>>(32-7) + u = x9 + x5 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x9 + x1 ^= u<<13 | u>>(32-13) + u = x1 + x13 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x6 + x14 ^= u<<7 | u>>(32-7) + u = x14 + x10 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x14 + x6 ^= u<<13 | u>>(32-13) + u = x6 + x2 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x11 + x3 ^= u<<7 | u>>(32-7) + u = x3 + x15 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x3 + x11 ^= u<<13 | u>>(32-13) + u = x11 + x7 + x15 ^= u<<18 | u>>(32-18) + + u = x0 + x3 + x1 ^= u<<7 | u>>(32-7) + u = x1 + x0 + x2 ^= u<<9 | u>>(32-9) + u = x2 + x1 + x3 ^= u<<13 | u>>(32-13) + u = x3 + x2 + x0 ^= u<<18 | u>>(32-18) + + u = x5 + x4 + x6 ^= u<<7 | u>>(32-7) + u = x6 + x5 + x7 ^= u<<9 | u>>(32-9) + u = x7 + x6 + x4 ^= u<<13 | u>>(32-13) + u = x4 + x7 + x5 ^= u<<18 | u>>(32-18) + + u = x10 + x9 + x11 ^= u<<7 | u>>(32-7) + u = x11 + x10 + x8 ^= u<<9 | u>>(32-9) + u = x8 + x11 + x9 ^= u<<13 | u>>(32-13) + u = x9 + x8 + x10 ^= u<<18 | u>>(32-18) + + u = x15 + x14 + x12 ^= u<<7 | u>>(32-7) + u = x12 + x15 + x13 ^= u<<9 | u>>(32-9) + u = x13 + x12 + x14 ^= u<<13 | u>>(32-13) + u = x14 + x13 + x15 ^= u<<18 | u>>(32-18) + } + x0 += w0 + x1 += w1 + x2 += w2 + x3 += w3 + x4 += w4 + x5 += w5 + x6 += w6 + x7 += w7 + x8 += w8 + x9 += w9 + x10 += w10 + x11 += w11 + x12 += w12 + x13 += w13 + x14 += w14 + x15 += w15 + + out[0], tmp[0] = x0, x0 + out[1], tmp[1] = x1, x1 + out[2], tmp[2] = x2, x2 + out[3], tmp[3] = x3, x3 + out[4], tmp[4] = x4, x4 + out[5], tmp[5] = x5, x5 + out[6], tmp[6] = x6, x6 + out[7], tmp[7] = x7, x7 + out[8], tmp[8] = x8, x8 + out[9], tmp[9] = x9, x9 + out[10], tmp[10] = x10, x10 + out[11], tmp[11] = x11, x11 + out[12], tmp[12] = x12, x12 + out[13], tmp[13] = x13, x13 + out[14], tmp[14] = x14, x14 + out[15], tmp[15] = x15, x15 +} + +func blockMix(tmp *[16]uint32, in, out []uint32, r int) { + blockCopy(tmp[:], in[(2*r-1)*16:], 16) + for i := 0; i < 2*r; i += 2 { + salsaXOR(tmp, in[i*16:], out[i*8:]) + salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:]) + } +} + +func integer(b []uint32, r int) uint64 { + j := (2*r - 1) * 16 + return uint64(b[j]) | uint64(b[j+1])<<32 +} + +func smix(b []byte, r, N int, v, xy []uint32) { + var tmp [16]uint32 + x := xy + y := xy[32*r:] + + j := 0 + for i := 0; i < 32*r; i++ { + x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 + j += 4 + } + for i := 0; i < N; i += 2 { + blockCopy(v[i*(32*r):], x, 32*r) + blockMix(&tmp, x, y, r) + + blockCopy(v[(i+1)*(32*r):], y, 32*r) + blockMix(&tmp, y, x, r) + } + for i := 0; i < N; i += 2 { + j := int(integer(x, r) & uint64(N-1)) + blockXOR(x, v[j*(32*r):], 32*r) + blockMix(&tmp, x, y, r) + + j = int(integer(y, r) & uint64(N-1)) + blockXOR(y, v[j*(32*r):], 32*r) + blockMix(&tmp, y, x, r) + } + j = 0 + for _, v := range x[:32*r] { + b[j+0] = byte(v >> 0) + b[j+1] = byte(v >> 8) + b[j+2] = byte(v >> 16) + b[j+3] = byte(v >> 24) + j += 4 + } +} + +// Key derives a key from the password, salt, and cost parameters, returning +// a byte slice of length keyLen that can be used as cryptographic key. +// +// N is a CPU/memory cost parameter, which must be a power of two greater than 1. +// r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the +// limits, the function returns a nil byte slice and an error. +// +// For example, you can get a derived key for e.g. AES-256 (which needs a +// 32-byte key) by doing: +// +// dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) +// +// The recommended parameters for interactive logins as of 2009 are N=16384, +// r=8, p=1. They should be increased as memory latency and CPU parallelism +// increases. Remember to get a good random salt. +func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { + if N <= 1 || N&(N-1) != 0 { + return nil, errors.New("scrypt: N must be > 1 and a power of 2") + } + if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { + return nil, errors.New("scrypt: parameters are too large") + } + + xy := make([]uint32, 64*r) + v := make([]uint32, 32*N*r) + b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) + + for i := 0; i < p; i++ { + smix(b[i*128*r:], r, N, v, xy) + } + + return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitattributes b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitattributes new file mode 100644 index 0000000..d2f212e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitattributes @@ -0,0 +1,10 @@ +# Treat all files in this repo as binary, with no git magic updating +# line endings. Windows users contributing to Go will need to use a +# modern version of git and editors capable of LF line endings. +# +# We'll prevent accidental CRLF line endings from entering the repo +# via the git-review gofmt checks. +# +# See golang.org/issue/9281 + +* -text diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitignore b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitignore new file mode 100644 index 0000000..8339fd6 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/.gitignore @@ -0,0 +1,2 @@ +# Add no patterns to .hgignore except for files generated by the build. +last-change diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/AUTHORS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTING.md b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTING.md new file mode 100644 index 0000000..88dff59 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + + +## Filing issues + +When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +**We do not accept GitHub pull requests** +(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. + diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/LICENSE b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. 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 THE COPYRIGHT +OWNER 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. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/PATENTS b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/README b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/README new file mode 100644 index 0000000..6b13d8e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/README @@ -0,0 +1,3 @@ +This repository holds supplementary Go networking libraries. + +To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/codereview.cfg b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/codereview.cfg new file mode 100644 index 0000000..3f8b14b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/atom.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/atom.go new file mode 100644 index 0000000..cd0a8ac --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/atom.go @@ -0,0 +1,78 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package atom provides integer codes (also known as atoms) for a fixed set of +// frequently occurring HTML strings: tag names and attribute keys such as "p" +// and "id". +// +// Sharing an atom's name between all elements with the same tag can result in +// fewer string allocations when tokenizing and parsing HTML. Integer +// comparisons are also generally faster than string comparisons. +// +// The value of an atom's particular code is not guaranteed to stay the same +// between versions of this package. Neither is any ordering guaranteed: +// whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to +// be dense. The only guarantees are that e.g. looking up "div" will yield +// atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. +package atom // import "golang.org/x/net/html/atom" + +// Atom is an integer code for a string. The zero value maps to "". +type Atom uint32 + +// String returns the atom's name. +func (a Atom) String() string { + start := uint32(a >> 8) + n := uint32(a & 0xff) + if start+n > uint32(len(atomText)) { + return "" + } + return atomText[start : start+n] +} + +func (a Atom) string() string { + return atomText[a>>8 : a>>8+a&0xff] +} + +// fnv computes the FNV hash with an arbitrary starting value h. +func fnv(h uint32, s []byte) uint32 { + for i := range s { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +func match(s string, t []byte) bool { + for i, c := range t { + if s[i] != c { + return false + } + } + return true +} + +// Lookup returns the atom whose name is s. It returns zero if there is no +// such atom. The lookup is case sensitive. +func Lookup(s []byte) Atom { + if len(s) == 0 || len(s) > maxAtomLen { + return 0 + } + h := fnv(hash0, s) + if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { + return a + } + if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { + return a + } + return 0 +} + +// String returns a string whose contents are equal to s. In that sense, it is +// equivalent to string(s) but may be more efficient. +func String(s []byte) string { + if a := Lookup(s); a != 0 { + return a.String() + } + return string(s) +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/gen.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/gen.go new file mode 100644 index 0000000..6bfa866 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/gen.go @@ -0,0 +1,648 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// This program generates table.go and table_test.go. +// Invoke as +// +// go run gen.go |gofmt >table.go +// go run gen.go -test |gofmt >table_test.go + +import ( + "flag" + "fmt" + "math/rand" + "os" + "sort" + "strings" +) + +// identifier converts s to a Go exported identifier. +// It converts "div" to "Div" and "accept-charset" to "AcceptCharset". +func identifier(s string) string { + b := make([]byte, 0, len(s)) + cap := true + for _, c := range s { + if c == '-' { + cap = true + continue + } + if cap && 'a' <= c && c <= 'z' { + c -= 'a' - 'A' + } + cap = false + b = append(b, byte(c)) + } + return string(b) +} + +var test = flag.Bool("test", false, "generate table_test.go") + +func main() { + flag.Parse() + + var all []string + all = append(all, elements...) + all = append(all, attributes...) + all = append(all, eventHandlers...) + all = append(all, extra...) + sort.Strings(all) + + if *test { + fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n") + fmt.Printf("package atom\n\n") + fmt.Printf("var testAtomList = []string{\n") + for _, s := range all { + fmt.Printf("\t%q,\n", s) + } + fmt.Printf("}\n") + return + } + + // uniq - lists have dups + // compute max len too + maxLen := 0 + w := 0 + for _, s := range all { + if w == 0 || all[w-1] != s { + if maxLen < len(s) { + maxLen = len(s) + } + all[w] = s + w++ + } + } + all = all[:w] + + // Find hash that minimizes table size. + var best *table + for i := 0; i < 1000000; i++ { + if best != nil && 1<<(best.k-1) < len(all) { + break + } + h := rand.Uint32() + for k := uint(0); k <= 16; k++ { + if best != nil && k >= best.k { + break + } + var t table + if t.init(h, k, all) { + best = &t + break + } + } + } + if best == nil { + fmt.Fprintf(os.Stderr, "failed to construct string table\n") + os.Exit(1) + } + + // Lay out strings, using overlaps when possible. + layout := append([]string{}, all...) + + // Remove strings that are substrings of other strings + for changed := true; changed; { + changed = false + for i, s := range layout { + if s == "" { + continue + } + for j, t := range layout { + if i != j && t != "" && strings.Contains(s, t) { + changed = true + layout[j] = "" + } + } + } + } + + // Join strings where one suffix matches another prefix. + for { + // Find best i, j, k such that layout[i][len-k:] == layout[j][:k], + // maximizing overlap length k. + besti := -1 + bestj := -1 + bestk := 0 + for i, s := range layout { + if s == "" { + continue + } + for j, t := range layout { + if i == j { + continue + } + for k := bestk + 1; k <= len(s) && k <= len(t); k++ { + if s[len(s)-k:] == t[:k] { + besti = i + bestj = j + bestk = k + } + } + } + } + if bestk > 0 { + layout[besti] += layout[bestj][bestk:] + layout[bestj] = "" + continue + } + break + } + + text := strings.Join(layout, "") + + atom := map[string]uint32{} + for _, s := range all { + off := strings.Index(text, s) + if off < 0 { + panic("lost string " + s) + } + atom[s] = uint32(off<<8 | len(s)) + } + + // Generate the Go code. + fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n") + fmt.Printf("package atom\n\nconst (\n") + for _, s := range all { + fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s]) + } + fmt.Printf(")\n\n") + + fmt.Printf("const hash0 = %#x\n\n", best.h0) + fmt.Printf("const maxAtomLen = %d\n\n", maxLen) + + fmt.Printf("var table = [1<<%d]Atom{\n", best.k) + for i, s := range best.tab { + if s == "" { + continue + } + fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s) + } + fmt.Printf("}\n") + datasize := (1 << best.k) * 4 + + fmt.Printf("const atomText =\n") + textsize := len(text) + for len(text) > 60 { + fmt.Printf("\t%q +\n", text[:60]) + text = text[60:] + } + fmt.Printf("\t%q\n\n", text) + + fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize) +} + +type byLen []string + +func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) } +func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byLen) Len() int { return len(x) } + +// fnv computes the FNV hash with an arbitrary starting value h. +func fnv(h uint32, s string) uint32 { + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +// A table represents an attempt at constructing the lookup table. +// The lookup table uses cuckoo hashing, meaning that each string +// can be found in one of two positions. +type table struct { + h0 uint32 + k uint + mask uint32 + tab []string +} + +// hash returns the two hashes for s. +func (t *table) hash(s string) (h1, h2 uint32) { + h := fnv(t.h0, s) + h1 = h & t.mask + h2 = (h >> 16) & t.mask + return +} + +// init initializes the table with the given parameters. +// h0 is the initial hash value, +// k is the number of bits of hash value to use, and +// x is the list of strings to store in the table. +// init returns false if the table cannot be constructed. +func (t *table) init(h0 uint32, k uint, x []string) bool { + t.h0 = h0 + t.k = k + t.tab = make([]string, 1< len(t.tab) { + return false + } + s := t.tab[i] + h1, h2 := t.hash(s) + j := h1 + h2 - i + if t.tab[j] != "" && !t.push(j, depth+1) { + return false + } + t.tab[j] = s + return true +} + +// The lists of element names and attribute keys were taken from +// https://html.spec.whatwg.org/multipage/indices.html#index +// as of the "HTML Living Standard - Last Updated 21 February 2015" version. + +var elements = []string{ + "a", + "abbr", + "address", + "area", + "article", + "aside", + "audio", + "b", + "base", + "bdi", + "bdo", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "cite", + "code", + "col", + "colgroup", + "command", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dialog", + "div", + "dl", + "dt", + "em", + "embed", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "i", + "iframe", + "img", + "input", + "ins", + "kbd", + "keygen", + "label", + "legend", + "li", + "link", + "map", + "mark", + "menu", + "menuitem", + "meta", + "meter", + "nav", + "noscript", + "object", + "ol", + "optgroup", + "option", + "output", + "p", + "param", + "pre", + "progress", + "q", + "rp", + "rt", + "ruby", + "s", + "samp", + "script", + "section", + "select", + "small", + "source", + "span", + "strong", + "style", + "sub", + "summary", + "sup", + "table", + "tbody", + "td", + "template", + "textarea", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "u", + "ul", + "var", + "video", + "wbr", +} + +// https://html.spec.whatwg.org/multipage/indices.html#attributes-3 + +var attributes = []string{ + "abbr", + "accept", + "accept-charset", + "accesskey", + "action", + "alt", + "async", + "autocomplete", + "autofocus", + "autoplay", + "challenge", + "charset", + "checked", + "cite", + "class", + "cols", + "colspan", + "command", + "content", + "contenteditable", + "contextmenu", + "controls", + "coords", + "crossorigin", + "data", + "datetime", + "default", + "defer", + "dir", + "dirname", + "disabled", + "download", + "draggable", + "dropzone", + "enctype", + "for", + "form", + "formaction", + "formenctype", + "formmethod", + "formnovalidate", + "formtarget", + "headers", + "height", + "hidden", + "high", + "href", + "hreflang", + "http-equiv", + "icon", + "id", + "inputmode", + "ismap", + "itemid", + "itemprop", + "itemref", + "itemscope", + "itemtype", + "keytype", + "kind", + "label", + "lang", + "list", + "loop", + "low", + "manifest", + "max", + "maxlength", + "media", + "mediagroup", + "method", + "min", + "minlength", + "multiple", + "muted", + "name", + "novalidate", + "open", + "optimum", + "pattern", + "ping", + "placeholder", + "poster", + "preload", + "radiogroup", + "readonly", + "rel", + "required", + "reversed", + "rows", + "rowspan", + "sandbox", + "spellcheck", + "scope", + "scoped", + "seamless", + "selected", + "shape", + "size", + "sizes", + "sortable", + "sorted", + "span", + "src", + "srcdoc", + "srclang", + "start", + "step", + "style", + "tabindex", + "target", + "title", + "translate", + "type", + "typemustmatch", + "usemap", + "value", + "width", + "wrap", +} + +var eventHandlers = []string{ + "onabort", + "onautocomplete", + "onautocompleteerror", + "onafterprint", + "onbeforeprint", + "onbeforeunload", + "onblur", + "oncancel", + "oncanplay", + "oncanplaythrough", + "onchange", + "onclick", + "onclose", + "oncontextmenu", + "oncuechange", + "ondblclick", + "ondrag", + "ondragend", + "ondragenter", + "ondragleave", + "ondragover", + "ondragstart", + "ondrop", + "ondurationchange", + "onemptied", + "onended", + "onerror", + "onfocus", + "onhashchange", + "oninput", + "oninvalid", + "onkeydown", + "onkeypress", + "onkeyup", + "onlanguagechange", + "onload", + "onloadeddata", + "onloadedmetadata", + "onloadstart", + "onmessage", + "onmousedown", + "onmousemove", + "onmouseout", + "onmouseover", + "onmouseup", + "onmousewheel", + "onoffline", + "ononline", + "onpagehide", + "onpageshow", + "onpause", + "onplay", + "onplaying", + "onpopstate", + "onprogress", + "onratechange", + "onreset", + "onresize", + "onscroll", + "onseeked", + "onseeking", + "onselect", + "onshow", + "onsort", + "onstalled", + "onstorage", + "onsubmit", + "onsuspend", + "ontimeupdate", + "ontoggle", + "onunload", + "onvolumechange", + "onwaiting", +} + +// extra are ad-hoc values not covered by any of the lists above. +var extra = []string{ + "align", + "annotation", + "annotation-xml", + "applet", + "basefont", + "bgsound", + "big", + "blink", + "center", + "color", + "desc", + "face", + "font", + "foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive. + "foreignobject", + "frame", + "frameset", + "image", + "isindex", + "listing", + "malignmark", + "marquee", + "math", + "mglyph", + "mi", + "mn", + "mo", + "ms", + "mtext", + "nobr", + "noembed", + "noframes", + "plaintext", + "prompt", + "public", + "spacer", + "strike", + "svg", + "system", + "tt", + "xmp", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/table.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/table.go new file mode 100644 index 0000000..2605ba3 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/atom/table.go @@ -0,0 +1,713 @@ +// generated by go run gen.go; DO NOT EDIT + +package atom + +const ( + A Atom = 0x1 + Abbr Atom = 0x4 + Accept Atom = 0x2106 + AcceptCharset Atom = 0x210e + Accesskey Atom = 0x3309 + Action Atom = 0x1f606 + Address Atom = 0x4f307 + Align Atom = 0x1105 + Alt Atom = 0x4503 + Annotation Atom = 0x1670a + AnnotationXml Atom = 0x1670e + Applet Atom = 0x2b306 + Area Atom = 0x2fa04 + Article Atom = 0x38807 + Aside Atom = 0x8305 + Async Atom = 0x7b05 + Audio Atom = 0xa605 + Autocomplete Atom = 0x1fc0c + Autofocus Atom = 0xb309 + Autoplay Atom = 0xce08 + B Atom = 0x101 + Base Atom = 0xd604 + Basefont Atom = 0xd608 + Bdi Atom = 0x1a03 + Bdo Atom = 0xe703 + Bgsound Atom = 0x11807 + Big Atom = 0x12403 + Blink Atom = 0x12705 + Blockquote Atom = 0x12c0a + Body Atom = 0x2f04 + Br Atom = 0x202 + Button Atom = 0x13606 + Canvas Atom = 0x7f06 + Caption Atom = 0x1bb07 + Center Atom = 0x5b506 + Challenge Atom = 0x21f09 + Charset Atom = 0x2807 + Checked Atom = 0x32807 + Cite Atom = 0x3c804 + Class Atom = 0x4de05 + Code Atom = 0x14904 + Col Atom = 0x15003 + Colgroup Atom = 0x15008 + Color Atom = 0x15d05 + Cols Atom = 0x16204 + Colspan Atom = 0x16207 + Command Atom = 0x17507 + Content Atom = 0x42307 + Contenteditable Atom = 0x4230f + Contextmenu Atom = 0x3310b + Controls Atom = 0x18808 + Coords Atom = 0x19406 + Crossorigin Atom = 0x19f0b + Data Atom = 0x44a04 + Datalist Atom = 0x44a08 + Datetime Atom = 0x23c08 + Dd Atom = 0x26702 + Default Atom = 0x8607 + Defer Atom = 0x14b05 + Del Atom = 0x3ef03 + Desc Atom = 0x4db04 + Details Atom = 0x4807 + Dfn Atom = 0x6103 + Dialog Atom = 0x1b06 + Dir Atom = 0x6903 + Dirname Atom = 0x6907 + Disabled Atom = 0x10c08 + Div Atom = 0x11303 + Dl Atom = 0x11e02 + Download Atom = 0x40008 + Draggable Atom = 0x17b09 + Dropzone Atom = 0x39108 + Dt Atom = 0x50902 + Em Atom = 0x6502 + Embed Atom = 0x6505 + Enctype Atom = 0x21107 + Face Atom = 0x5b304 + Fieldset Atom = 0x1b008 + Figcaption Atom = 0x1b80a + Figure Atom = 0x1cc06 + Font Atom = 0xda04 + Footer Atom = 0x8d06 + For Atom = 0x1d803 + ForeignObject Atom = 0x1d80d + Foreignobject Atom = 0x1e50d + Form Atom = 0x1f204 + Formaction Atom = 0x1f20a + Formenctype Atom = 0x20d0b + Formmethod Atom = 0x2280a + Formnovalidate Atom = 0x2320e + Formtarget Atom = 0x2470a + Frame Atom = 0x9a05 + Frameset Atom = 0x9a08 + H1 Atom = 0x26e02 + H2 Atom = 0x29402 + H3 Atom = 0x2a702 + H4 Atom = 0x2e902 + H5 Atom = 0x2f302 + H6 Atom = 0x50b02 + Head Atom = 0x2d504 + Header Atom = 0x2d506 + Headers Atom = 0x2d507 + Height Atom = 0x25106 + Hgroup Atom = 0x25906 + Hidden Atom = 0x26506 + High Atom = 0x26b04 + Hr Atom = 0x27002 + Href Atom = 0x27004 + Hreflang Atom = 0x27008 + Html Atom = 0x25504 + HttpEquiv Atom = 0x2780a + I Atom = 0x601 + Icon Atom = 0x42204 + Id Atom = 0x8502 + Iframe Atom = 0x29606 + Image Atom = 0x29c05 + Img Atom = 0x2a103 + Input Atom = 0x3e805 + Inputmode Atom = 0x3e809 + Ins Atom = 0x1a803 + Isindex Atom = 0x2a907 + Ismap Atom = 0x2b005 + Itemid Atom = 0x33c06 + Itemprop Atom = 0x3c908 + Itemref Atom = 0x5ad07 + Itemscope Atom = 0x2b909 + Itemtype Atom = 0x2c308 + Kbd Atom = 0x1903 + Keygen Atom = 0x3906 + Keytype Atom = 0x53707 + Kind Atom = 0x10904 + Label Atom = 0xf005 + Lang Atom = 0x27404 + Legend Atom = 0x18206 + Li Atom = 0x1202 + Link Atom = 0x12804 + List Atom = 0x44e04 + Listing Atom = 0x44e07 + Loop Atom = 0xf404 + Low Atom = 0x11f03 + Malignmark Atom = 0x100a + Manifest Atom = 0x5f108 + Map Atom = 0x2b203 + Mark Atom = 0x1604 + Marquee Atom = 0x2cb07 + Math Atom = 0x2d204 + Max Atom = 0x2e103 + Maxlength Atom = 0x2e109 + Media Atom = 0x6e05 + Mediagroup Atom = 0x6e0a + Menu Atom = 0x33804 + Menuitem Atom = 0x33808 + Meta Atom = 0x45d04 + Meter Atom = 0x24205 + Method Atom = 0x22c06 + Mglyph Atom = 0x2a206 + Mi Atom = 0x2eb02 + Min Atom = 0x2eb03 + Minlength Atom = 0x2eb09 + Mn Atom = 0x23502 + Mo Atom = 0x3ed02 + Ms Atom = 0x2bc02 + Mtext Atom = 0x2f505 + Multiple Atom = 0x30308 + Muted Atom = 0x30b05 + Name Atom = 0x6c04 + Nav Atom = 0x3e03 + Nobr Atom = 0x5704 + Noembed Atom = 0x6307 + Noframes Atom = 0x9808 + Noscript Atom = 0x3d208 + Novalidate Atom = 0x2360a + Object Atom = 0x1ec06 + Ol Atom = 0xc902 + Onabort Atom = 0x13a07 + Onafterprint Atom = 0x1c00c + Onautocomplete Atom = 0x1fa0e + Onautocompleteerror Atom = 0x1fa13 + Onbeforeprint Atom = 0x6040d + Onbeforeunload Atom = 0x4e70e + Onblur Atom = 0xaa06 + Oncancel Atom = 0xe908 + Oncanplay Atom = 0x28509 + Oncanplaythrough Atom = 0x28510 + Onchange Atom = 0x3a708 + Onclick Atom = 0x31007 + Onclose Atom = 0x31707 + Oncontextmenu Atom = 0x32f0d + Oncuechange Atom = 0x3420b + Ondblclick Atom = 0x34d0a + Ondrag Atom = 0x35706 + Ondragend Atom = 0x35709 + Ondragenter Atom = 0x3600b + Ondragleave Atom = 0x36b0b + Ondragover Atom = 0x3760a + Ondragstart Atom = 0x3800b + Ondrop Atom = 0x38f06 + Ondurationchange Atom = 0x39f10 + Onemptied Atom = 0x39609 + Onended Atom = 0x3af07 + Onerror Atom = 0x3b607 + Onfocus Atom = 0x3bd07 + Onhashchange Atom = 0x3da0c + Oninput Atom = 0x3e607 + Oninvalid Atom = 0x3f209 + Onkeydown Atom = 0x3fb09 + Onkeypress Atom = 0x4080a + Onkeyup Atom = 0x41807 + Onlanguagechange Atom = 0x43210 + Onload Atom = 0x44206 + Onloadeddata Atom = 0x4420c + Onloadedmetadata Atom = 0x45510 + Onloadstart Atom = 0x46b0b + Onmessage Atom = 0x47609 + Onmousedown Atom = 0x47f0b + Onmousemove Atom = 0x48a0b + Onmouseout Atom = 0x4950a + Onmouseover Atom = 0x4a20b + Onmouseup Atom = 0x4ad09 + Onmousewheel Atom = 0x4b60c + Onoffline Atom = 0x4c209 + Ononline Atom = 0x4cb08 + Onpagehide Atom = 0x4d30a + Onpageshow Atom = 0x4fe0a + Onpause Atom = 0x50d07 + Onplay Atom = 0x51706 + Onplaying Atom = 0x51709 + Onpopstate Atom = 0x5200a + Onprogress Atom = 0x52a0a + Onratechange Atom = 0x53e0c + Onreset Atom = 0x54a07 + Onresize Atom = 0x55108 + Onscroll Atom = 0x55f08 + Onseeked Atom = 0x56708 + Onseeking Atom = 0x56f09 + Onselect Atom = 0x57808 + Onshow Atom = 0x58206 + Onsort Atom = 0x58b06 + Onstalled Atom = 0x59509 + Onstorage Atom = 0x59e09 + Onsubmit Atom = 0x5a708 + Onsuspend Atom = 0x5bb09 + Ontimeupdate Atom = 0xdb0c + Ontoggle Atom = 0x5c408 + Onunload Atom = 0x5cc08 + Onvolumechange Atom = 0x5d40e + Onwaiting Atom = 0x5e209 + Open Atom = 0x3cf04 + Optgroup Atom = 0xf608 + Optimum Atom = 0x5eb07 + Option Atom = 0x60006 + Output Atom = 0x49c06 + P Atom = 0xc01 + Param Atom = 0xc05 + Pattern Atom = 0x5107 + Ping Atom = 0x7704 + Placeholder Atom = 0xc30b + Plaintext Atom = 0xfd09 + Poster Atom = 0x15706 + Pre Atom = 0x25e03 + Preload Atom = 0x25e07 + Progress Atom = 0x52c08 + Prompt Atom = 0x5fa06 + Public Atom = 0x41e06 + Q Atom = 0x13101 + Radiogroup Atom = 0x30a + Readonly Atom = 0x2fb08 + Rel Atom = 0x25f03 + Required Atom = 0x1d008 + Reversed Atom = 0x5a08 + Rows Atom = 0x9204 + Rowspan Atom = 0x9207 + Rp Atom = 0x1c602 + Rt Atom = 0x13f02 + Ruby Atom = 0xaf04 + S Atom = 0x2c01 + Samp Atom = 0x4e04 + Sandbox Atom = 0xbb07 + Scope Atom = 0x2bd05 + Scoped Atom = 0x2bd06 + Script Atom = 0x3d406 + Seamless Atom = 0x31c08 + Section Atom = 0x4e207 + Select Atom = 0x57a06 + Selected Atom = 0x57a08 + Shape Atom = 0x4f905 + Size Atom = 0x55504 + Sizes Atom = 0x55505 + Small Atom = 0x18f05 + Sortable Atom = 0x58d08 + Sorted Atom = 0x19906 + Source Atom = 0x1aa06 + Spacer Atom = 0x2db06 + Span Atom = 0x9504 + Spellcheck Atom = 0x3230a + Src Atom = 0x3c303 + Srcdoc Atom = 0x3c306 + Srclang Atom = 0x41107 + Start Atom = 0x38605 + Step Atom = 0x5f704 + Strike Atom = 0x53306 + Strong Atom = 0x55906 + Style Atom = 0x61105 + Sub Atom = 0x5a903 + Summary Atom = 0x61607 + Sup Atom = 0x61d03 + Svg Atom = 0x62003 + System Atom = 0x62306 + Tabindex Atom = 0x46308 + Table Atom = 0x42d05 + Target Atom = 0x24b06 + Tbody Atom = 0x2e05 + Td Atom = 0x4702 + Template Atom = 0x62608 + Textarea Atom = 0x2f608 + Tfoot Atom = 0x8c05 + Th Atom = 0x22e02 + Thead Atom = 0x2d405 + Time Atom = 0xdd04 + Title Atom = 0xa105 + Tr Atom = 0x10502 + Track Atom = 0x10505 + Translate Atom = 0x14009 + Tt Atom = 0x5302 + Type Atom = 0x21404 + Typemustmatch Atom = 0x2140d + U Atom = 0xb01 + Ul Atom = 0x8a02 + Usemap Atom = 0x51106 + Value Atom = 0x4005 + Var Atom = 0x11503 + Video Atom = 0x28105 + Wbr Atom = 0x12103 + Width Atom = 0x50705 + Wrap Atom = 0x58704 + Xmp Atom = 0xc103 +) + +const hash0 = 0xc17da63e + +const maxAtomLen = 19 + +var table = [1 << 9]Atom{ + 0x1: 0x48a0b, // onmousemove + 0x2: 0x5e209, // onwaiting + 0x3: 0x1fa13, // onautocompleteerror + 0x4: 0x5fa06, // prompt + 0x7: 0x5eb07, // optimum + 0x8: 0x1604, // mark + 0xa: 0x5ad07, // itemref + 0xb: 0x4fe0a, // onpageshow + 0xc: 0x57a06, // select + 0xd: 0x17b09, // draggable + 0xe: 0x3e03, // nav + 0xf: 0x17507, // command + 0x11: 0xb01, // u + 0x14: 0x2d507, // headers + 0x15: 0x44a08, // datalist + 0x17: 0x4e04, // samp + 0x1a: 0x3fb09, // onkeydown + 0x1b: 0x55f08, // onscroll + 0x1c: 0x15003, // col + 0x20: 0x3c908, // itemprop + 0x21: 0x2780a, // http-equiv + 0x22: 0x61d03, // sup + 0x24: 0x1d008, // required + 0x2b: 0x25e07, // preload + 0x2c: 0x6040d, // onbeforeprint + 0x2d: 0x3600b, // ondragenter + 0x2e: 0x50902, // dt + 0x2f: 0x5a708, // onsubmit + 0x30: 0x27002, // hr + 0x31: 0x32f0d, // oncontextmenu + 0x33: 0x29c05, // image + 0x34: 0x50d07, // onpause + 0x35: 0x25906, // hgroup + 0x36: 0x7704, // ping + 0x37: 0x57808, // onselect + 0x3a: 0x11303, // div + 0x3b: 0x1fa0e, // onautocomplete + 0x40: 0x2eb02, // mi + 0x41: 0x31c08, // seamless + 0x42: 0x2807, // charset + 0x43: 0x8502, // id + 0x44: 0x5200a, // onpopstate + 0x45: 0x3ef03, // del + 0x46: 0x2cb07, // marquee + 0x47: 0x3309, // accesskey + 0x49: 0x8d06, // footer + 0x4a: 0x44e04, // list + 0x4b: 0x2b005, // ismap + 0x51: 0x33804, // menu + 0x52: 0x2f04, // body + 0x55: 0x9a08, // frameset + 0x56: 0x54a07, // onreset + 0x57: 0x12705, // blink + 0x58: 0xa105, // title + 0x59: 0x38807, // article + 0x5b: 0x22e02, // th + 0x5d: 0x13101, // q + 0x5e: 0x3cf04, // open + 0x5f: 0x2fa04, // area + 0x61: 0x44206, // onload + 0x62: 0xda04, // font + 0x63: 0xd604, // base + 0x64: 0x16207, // colspan + 0x65: 0x53707, // keytype + 0x66: 0x11e02, // dl + 0x68: 0x1b008, // fieldset + 0x6a: 0x2eb03, // min + 0x6b: 0x11503, // var + 0x6f: 0x2d506, // header + 0x70: 0x13f02, // rt + 0x71: 0x15008, // colgroup + 0x72: 0x23502, // mn + 0x74: 0x13a07, // onabort + 0x75: 0x3906, // keygen + 0x76: 0x4c209, // onoffline + 0x77: 0x21f09, // challenge + 0x78: 0x2b203, // map + 0x7a: 0x2e902, // h4 + 0x7b: 0x3b607, // onerror + 0x7c: 0x2e109, // maxlength + 0x7d: 0x2f505, // mtext + 0x7e: 0xbb07, // sandbox + 0x7f: 0x58b06, // onsort + 0x80: 0x100a, // malignmark + 0x81: 0x45d04, // meta + 0x82: 0x7b05, // async + 0x83: 0x2a702, // h3 + 0x84: 0x26702, // dd + 0x85: 0x27004, // href + 0x86: 0x6e0a, // mediagroup + 0x87: 0x19406, // coords + 0x88: 0x41107, // srclang + 0x89: 0x34d0a, // ondblclick + 0x8a: 0x4005, // value + 0x8c: 0xe908, // oncancel + 0x8e: 0x3230a, // spellcheck + 0x8f: 0x9a05, // frame + 0x91: 0x12403, // big + 0x94: 0x1f606, // action + 0x95: 0x6903, // dir + 0x97: 0x2fb08, // readonly + 0x99: 0x42d05, // table + 0x9a: 0x61607, // summary + 0x9b: 0x12103, // wbr + 0x9c: 0x30a, // radiogroup + 0x9d: 0x6c04, // name + 0x9f: 0x62306, // system + 0xa1: 0x15d05, // color + 0xa2: 0x7f06, // canvas + 0xa3: 0x25504, // html + 0xa5: 0x56f09, // onseeking + 0xac: 0x4f905, // shape + 0xad: 0x25f03, // rel + 0xae: 0x28510, // oncanplaythrough + 0xaf: 0x3760a, // ondragover + 0xb0: 0x62608, // template + 0xb1: 0x1d80d, // foreignObject + 0xb3: 0x9204, // rows + 0xb6: 0x44e07, // listing + 0xb7: 0x49c06, // output + 0xb9: 0x3310b, // contextmenu + 0xbb: 0x11f03, // low + 0xbc: 0x1c602, // rp + 0xbd: 0x5bb09, // onsuspend + 0xbe: 0x13606, // button + 0xbf: 0x4db04, // desc + 0xc1: 0x4e207, // section + 0xc2: 0x52a0a, // onprogress + 0xc3: 0x59e09, // onstorage + 0xc4: 0x2d204, // math + 0xc5: 0x4503, // alt + 0xc7: 0x8a02, // ul + 0xc8: 0x5107, // pattern + 0xc9: 0x4b60c, // onmousewheel + 0xca: 0x35709, // ondragend + 0xcb: 0xaf04, // ruby + 0xcc: 0xc01, // p + 0xcd: 0x31707, // onclose + 0xce: 0x24205, // meter + 0xcf: 0x11807, // bgsound + 0xd2: 0x25106, // height + 0xd4: 0x101, // b + 0xd5: 0x2c308, // itemtype + 0xd8: 0x1bb07, // caption + 0xd9: 0x10c08, // disabled + 0xdb: 0x33808, // menuitem + 0xdc: 0x62003, // svg + 0xdd: 0x18f05, // small + 0xde: 0x44a04, // data + 0xe0: 0x4cb08, // ononline + 0xe1: 0x2a206, // mglyph + 0xe3: 0x6505, // embed + 0xe4: 0x10502, // tr + 0xe5: 0x46b0b, // onloadstart + 0xe7: 0x3c306, // srcdoc + 0xeb: 0x5c408, // ontoggle + 0xed: 0xe703, // bdo + 0xee: 0x4702, // td + 0xef: 0x8305, // aside + 0xf0: 0x29402, // h2 + 0xf1: 0x52c08, // progress + 0xf2: 0x12c0a, // blockquote + 0xf4: 0xf005, // label + 0xf5: 0x601, // i + 0xf7: 0x9207, // rowspan + 0xfb: 0x51709, // onplaying + 0xfd: 0x2a103, // img + 0xfe: 0xf608, // optgroup + 0xff: 0x42307, // content + 0x101: 0x53e0c, // onratechange + 0x103: 0x3da0c, // onhashchange + 0x104: 0x4807, // details + 0x106: 0x40008, // download + 0x109: 0x14009, // translate + 0x10b: 0x4230f, // contenteditable + 0x10d: 0x36b0b, // ondragleave + 0x10e: 0x2106, // accept + 0x10f: 0x57a08, // selected + 0x112: 0x1f20a, // formaction + 0x113: 0x5b506, // center + 0x115: 0x45510, // onloadedmetadata + 0x116: 0x12804, // link + 0x117: 0xdd04, // time + 0x118: 0x19f0b, // crossorigin + 0x119: 0x3bd07, // onfocus + 0x11a: 0x58704, // wrap + 0x11b: 0x42204, // icon + 0x11d: 0x28105, // video + 0x11e: 0x4de05, // class + 0x121: 0x5d40e, // onvolumechange + 0x122: 0xaa06, // onblur + 0x123: 0x2b909, // itemscope + 0x124: 0x61105, // style + 0x127: 0x41e06, // public + 0x129: 0x2320e, // formnovalidate + 0x12a: 0x58206, // onshow + 0x12c: 0x51706, // onplay + 0x12d: 0x3c804, // cite + 0x12e: 0x2bc02, // ms + 0x12f: 0xdb0c, // ontimeupdate + 0x130: 0x10904, // kind + 0x131: 0x2470a, // formtarget + 0x135: 0x3af07, // onended + 0x136: 0x26506, // hidden + 0x137: 0x2c01, // s + 0x139: 0x2280a, // formmethod + 0x13a: 0x3e805, // input + 0x13c: 0x50b02, // h6 + 0x13d: 0xc902, // ol + 0x13e: 0x3420b, // oncuechange + 0x13f: 0x1e50d, // foreignobject + 0x143: 0x4e70e, // onbeforeunload + 0x144: 0x2bd05, // scope + 0x145: 0x39609, // onemptied + 0x146: 0x14b05, // defer + 0x147: 0xc103, // xmp + 0x148: 0x39f10, // ondurationchange + 0x149: 0x1903, // kbd + 0x14c: 0x47609, // onmessage + 0x14d: 0x60006, // option + 0x14e: 0x2eb09, // minlength + 0x14f: 0x32807, // checked + 0x150: 0xce08, // autoplay + 0x152: 0x202, // br + 0x153: 0x2360a, // novalidate + 0x156: 0x6307, // noembed + 0x159: 0x31007, // onclick + 0x15a: 0x47f0b, // onmousedown + 0x15b: 0x3a708, // onchange + 0x15e: 0x3f209, // oninvalid + 0x15f: 0x2bd06, // scoped + 0x160: 0x18808, // controls + 0x161: 0x30b05, // muted + 0x162: 0x58d08, // sortable + 0x163: 0x51106, // usemap + 0x164: 0x1b80a, // figcaption + 0x165: 0x35706, // ondrag + 0x166: 0x26b04, // high + 0x168: 0x3c303, // src + 0x169: 0x15706, // poster + 0x16b: 0x1670e, // annotation-xml + 0x16c: 0x5f704, // step + 0x16d: 0x4, // abbr + 0x16e: 0x1b06, // dialog + 0x170: 0x1202, // li + 0x172: 0x3ed02, // mo + 0x175: 0x1d803, // for + 0x176: 0x1a803, // ins + 0x178: 0x55504, // size + 0x179: 0x43210, // onlanguagechange + 0x17a: 0x8607, // default + 0x17b: 0x1a03, // bdi + 0x17c: 0x4d30a, // onpagehide + 0x17d: 0x6907, // dirname + 0x17e: 0x21404, // type + 0x17f: 0x1f204, // form + 0x181: 0x28509, // oncanplay + 0x182: 0x6103, // dfn + 0x183: 0x46308, // tabindex + 0x186: 0x6502, // em + 0x187: 0x27404, // lang + 0x189: 0x39108, // dropzone + 0x18a: 0x4080a, // onkeypress + 0x18b: 0x23c08, // datetime + 0x18c: 0x16204, // cols + 0x18d: 0x1, // a + 0x18e: 0x4420c, // onloadeddata + 0x190: 0xa605, // audio + 0x192: 0x2e05, // tbody + 0x193: 0x22c06, // method + 0x195: 0xf404, // loop + 0x196: 0x29606, // iframe + 0x198: 0x2d504, // head + 0x19e: 0x5f108, // manifest + 0x19f: 0xb309, // autofocus + 0x1a0: 0x14904, // code + 0x1a1: 0x55906, // strong + 0x1a2: 0x30308, // multiple + 0x1a3: 0xc05, // param + 0x1a6: 0x21107, // enctype + 0x1a7: 0x5b304, // face + 0x1a8: 0xfd09, // plaintext + 0x1a9: 0x26e02, // h1 + 0x1aa: 0x59509, // onstalled + 0x1ad: 0x3d406, // script + 0x1ae: 0x2db06, // spacer + 0x1af: 0x55108, // onresize + 0x1b0: 0x4a20b, // onmouseover + 0x1b1: 0x5cc08, // onunload + 0x1b2: 0x56708, // onseeked + 0x1b4: 0x2140d, // typemustmatch + 0x1b5: 0x1cc06, // figure + 0x1b6: 0x4950a, // onmouseout + 0x1b7: 0x25e03, // pre + 0x1b8: 0x50705, // width + 0x1b9: 0x19906, // sorted + 0x1bb: 0x5704, // nobr + 0x1be: 0x5302, // tt + 0x1bf: 0x1105, // align + 0x1c0: 0x3e607, // oninput + 0x1c3: 0x41807, // onkeyup + 0x1c6: 0x1c00c, // onafterprint + 0x1c7: 0x210e, // accept-charset + 0x1c8: 0x33c06, // itemid + 0x1c9: 0x3e809, // inputmode + 0x1cb: 0x53306, // strike + 0x1cc: 0x5a903, // sub + 0x1cd: 0x10505, // track + 0x1ce: 0x38605, // start + 0x1d0: 0xd608, // basefont + 0x1d6: 0x1aa06, // source + 0x1d7: 0x18206, // legend + 0x1d8: 0x2d405, // thead + 0x1da: 0x8c05, // tfoot + 0x1dd: 0x1ec06, // object + 0x1de: 0x6e05, // media + 0x1df: 0x1670a, // annotation + 0x1e0: 0x20d0b, // formenctype + 0x1e2: 0x3d208, // noscript + 0x1e4: 0x55505, // sizes + 0x1e5: 0x1fc0c, // autocomplete + 0x1e6: 0x9504, // span + 0x1e7: 0x9808, // noframes + 0x1e8: 0x24b06, // target + 0x1e9: 0x38f06, // ondrop + 0x1ea: 0x2b306, // applet + 0x1ec: 0x5a08, // reversed + 0x1f0: 0x2a907, // isindex + 0x1f3: 0x27008, // hreflang + 0x1f5: 0x2f302, // h5 + 0x1f6: 0x4f307, // address + 0x1fa: 0x2e103, // max + 0x1fb: 0xc30b, // placeholder + 0x1fc: 0x2f608, // textarea + 0x1fe: 0x4ad09, // onmouseup + 0x1ff: 0x3800b, // ondragstart +} + +const atomText = "abbradiogrouparamalignmarkbdialogaccept-charsetbodyaccesskey" + + "genavaluealtdetailsampatternobreversedfnoembedirnamediagroup" + + "ingasyncanvasidefaultfooterowspanoframesetitleaudionblurubya" + + "utofocusandboxmplaceholderautoplaybasefontimeupdatebdoncance" + + "labelooptgrouplaintextrackindisabledivarbgsoundlowbrbigblink" + + "blockquotebuttonabortranslatecodefercolgroupostercolorcolspa" + + "nnotation-xmlcommandraggablegendcontrolsmallcoordsortedcross" + + "originsourcefieldsetfigcaptionafterprintfigurequiredforeignO" + + "bjectforeignobjectformactionautocompleteerrorformenctypemust" + + "matchallengeformmethodformnovalidatetimeterformtargetheightm" + + "lhgroupreloadhiddenhigh1hreflanghttp-equivideoncanplaythroug" + + "h2iframeimageimglyph3isindexismappletitemscopeditemtypemarqu" + + "eematheaderspacermaxlength4minlength5mtextareadonlymultiplem" + + "utedonclickoncloseamlesspellcheckedoncontextmenuitemidoncuec" + + "hangeondblclickondragendondragenterondragleaveondragoverondr" + + "agstarticleondropzonemptiedondurationchangeonendedonerroronf" + + "ocusrcdocitempropenoscriptonhashchangeoninputmodeloninvalido" + + "nkeydownloadonkeypressrclangonkeyupublicontenteditableonlang" + + "uagechangeonloadeddatalistingonloadedmetadatabindexonloadsta" + + "rtonmessageonmousedownonmousemoveonmouseoutputonmouseoveronm" + + "ouseuponmousewheelonofflineononlineonpagehidesclassectionbef" + + "oreunloaddresshapeonpageshowidth6onpausemaponplayingonpopsta" + + "teonprogresstrikeytypeonratechangeonresetonresizestrongonscr" + + "ollonseekedonseekingonselectedonshowraponsortableonstalledon" + + "storageonsubmitemrefacenteronsuspendontoggleonunloadonvolume" + + "changeonwaitingoptimumanifestepromptoptionbeforeprintstylesu" + + "mmarysupsvgsystemplate" diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/charset/charset.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/charset/charset.go new file mode 100644 index 0000000..13bed15 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/charset/charset.go @@ -0,0 +1,257 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package charset provides common text encodings for HTML documents. +// +// The mapping from encoding labels to encodings is defined at +// https://encoding.spec.whatwg.org/. +package charset // import "golang.org/x/net/html/charset" + +import ( + "bytes" + "fmt" + "io" + "mime" + "strings" + "unicode/utf8" + + "golang.org/x/net/html" + "golang.org/x/text/encoding" + "golang.org/x/text/encoding/charmap" + "golang.org/x/text/encoding/htmlindex" + "golang.org/x/text/transform" +) + +// Lookup returns the encoding with the specified label, and its canonical +// name. It returns nil and the empty string if label is not one of the +// standard encodings for HTML. Matching is case-insensitive and ignores +// leading and trailing whitespace. Encoders will use HTML escape sequences for +// runes that are not supported by the character set. +func Lookup(label string) (e encoding.Encoding, name string) { + e, err := htmlindex.Get(label) + if err != nil { + return nil, "" + } + name, _ = htmlindex.Name(e) + return &htmlEncoding{e}, name +} + +type htmlEncoding struct{ encoding.Encoding } + +func (h *htmlEncoding) NewEncoder() *encoding.Encoder { + // HTML requires a non-terminating legacy encoder. We use HTML escapes to + // substitute unsupported code points. + return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder()) +} + +// DetermineEncoding determines the encoding of an HTML document by examining +// up to the first 1024 bytes of content and the declared Content-Type. +// +// See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding +func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) { + if len(content) > 1024 { + content = content[:1024] + } + + for _, b := range boms { + if bytes.HasPrefix(content, b.bom) { + e, name = Lookup(b.enc) + return e, name, true + } + } + + if _, params, err := mime.ParseMediaType(contentType); err == nil { + if cs, ok := params["charset"]; ok { + if e, name = Lookup(cs); e != nil { + return e, name, true + } + } + } + + if len(content) > 0 { + e, name = prescan(content) + if e != nil { + return e, name, false + } + } + + // Try to detect UTF-8. + // First eliminate any partial rune at the end. + for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- { + b := content[i] + if b < 0x80 { + break + } + if utf8.RuneStart(b) { + content = content[:i] + break + } + } + hasHighBit := false + for _, c := range content { + if c >= 0x80 { + hasHighBit = true + break + } + } + if hasHighBit && utf8.Valid(content) { + return encoding.Nop, "utf-8", false + } + + // TODO: change default depending on user's locale? + return charmap.Windows1252, "windows-1252", false +} + +// NewReader returns an io.Reader that converts the content of r to UTF-8. +// It calls DetermineEncoding to find out what r's encoding is. +func NewReader(r io.Reader, contentType string) (io.Reader, error) { + preview := make([]byte, 1024) + n, err := io.ReadFull(r, preview) + switch { + case err == io.ErrUnexpectedEOF: + preview = preview[:n] + r = bytes.NewReader(preview) + case err != nil: + return nil, err + default: + r = io.MultiReader(bytes.NewReader(preview), r) + } + + if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop { + r = transform.NewReader(r, e.NewDecoder()) + } + return r, nil +} + +// NewReaderLabel returns a reader that converts from the specified charset to +// UTF-8. It uses Lookup to find the encoding that corresponds to label, and +// returns an error if Lookup returns nil. It is suitable for use as +// encoding/xml.Decoder's CharsetReader function. +func NewReaderLabel(label string, input io.Reader) (io.Reader, error) { + e, _ := Lookup(label) + if e == nil { + return nil, fmt.Errorf("unsupported charset: %q", label) + } + return transform.NewReader(input, e.NewDecoder()), nil +} + +func prescan(content []byte) (e encoding.Encoding, name string) { + z := html.NewTokenizer(bytes.NewReader(content)) + for { + switch z.Next() { + case html.ErrorToken: + return nil, "" + + case html.StartTagToken, html.SelfClosingTagToken: + tagName, hasAttr := z.TagName() + if !bytes.Equal(tagName, []byte("meta")) { + continue + } + attrList := make(map[string]bool) + gotPragma := false + + const ( + dontKnow = iota + doNeedPragma + doNotNeedPragma + ) + needPragma := dontKnow + + name = "" + e = nil + for hasAttr { + var key, val []byte + key, val, hasAttr = z.TagAttr() + ks := string(key) + if attrList[ks] { + continue + } + attrList[ks] = true + for i, c := range val { + if 'A' <= c && c <= 'Z' { + val[i] = c + 0x20 + } + } + + switch ks { + case "http-equiv": + if bytes.Equal(val, []byte("content-type")) { + gotPragma = true + } + + case "content": + if e == nil { + name = fromMetaElement(string(val)) + if name != "" { + e, name = Lookup(name) + if e != nil { + needPragma = doNeedPragma + } + } + } + + case "charset": + e, name = Lookup(string(val)) + needPragma = doNotNeedPragma + } + } + + if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma { + continue + } + + if strings.HasPrefix(name, "utf-16") { + name = "utf-8" + e = encoding.Nop + } + + if e != nil { + return e, name + } + } + } +} + +func fromMetaElement(s string) string { + for s != "" { + csLoc := strings.Index(s, "charset") + if csLoc == -1 { + return "" + } + s = s[csLoc+len("charset"):] + s = strings.TrimLeft(s, " \t\n\f\r") + if !strings.HasPrefix(s, "=") { + continue + } + s = s[1:] + s = strings.TrimLeft(s, " \t\n\f\r") + if s == "" { + return "" + } + if q := s[0]; q == '"' || q == '\'' { + s = s[1:] + closeQuote := strings.IndexRune(s, rune(q)) + if closeQuote == -1 { + return "" + } + return s[:closeQuote] + } + + end := strings.IndexAny(s, "; \t\n\f\r") + if end == -1 { + end = len(s) + } + return s[:end] + } + return "" +} + +var boms = []struct { + bom []byte + enc string +}{ + {[]byte{0xfe, 0xff}, "utf-16be"}, + {[]byte{0xff, 0xfe}, "utf-16le"}, + {[]byte{0xef, 0xbb, 0xbf}, "utf-8"}, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/const.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/const.go new file mode 100644 index 0000000..52f651f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/const.go @@ -0,0 +1,102 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +// Section 12.2.3.2 of the HTML5 specification says "The following elements +// have varying levels of special parsing rules". +// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements +var isSpecialElementMap = map[string]bool{ + "address": true, + "applet": true, + "area": true, + "article": true, + "aside": true, + "base": true, + "basefont": true, + "bgsound": true, + "blockquote": true, + "body": true, + "br": true, + "button": true, + "caption": true, + "center": true, + "col": true, + "colgroup": true, + "dd": true, + "details": true, + "dir": true, + "div": true, + "dl": true, + "dt": true, + "embed": true, + "fieldset": true, + "figcaption": true, + "figure": true, + "footer": true, + "form": true, + "frame": true, + "frameset": true, + "h1": true, + "h2": true, + "h3": true, + "h4": true, + "h5": true, + "h6": true, + "head": true, + "header": true, + "hgroup": true, + "hr": true, + "html": true, + "iframe": true, + "img": true, + "input": true, + "isindex": true, + "li": true, + "link": true, + "listing": true, + "marquee": true, + "menu": true, + "meta": true, + "nav": true, + "noembed": true, + "noframes": true, + "noscript": true, + "object": true, + "ol": true, + "p": true, + "param": true, + "plaintext": true, + "pre": true, + "script": true, + "section": true, + "select": true, + "source": true, + "style": true, + "summary": true, + "table": true, + "tbody": true, + "td": true, + "template": true, + "textarea": true, + "tfoot": true, + "th": true, + "thead": true, + "title": true, + "tr": true, + "track": true, + "ul": true, + "wbr": true, + "xmp": true, +} + +func isSpecialElement(element *Node) bool { + switch element.Namespace { + case "", "html": + return isSpecialElementMap[element.Data] + case "svg": + return element.Data == "foreignObject" + } + return false +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doc.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doc.go new file mode 100644 index 0000000..94f4968 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doc.go @@ -0,0 +1,106 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package html implements an HTML5-compliant tokenizer and parser. + +Tokenization is done by creating a Tokenizer for an io.Reader r. It is the +caller's responsibility to ensure that r provides UTF-8 encoded HTML. + + z := html.NewTokenizer(r) + +Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), +which parses the next token and returns its type, or an error: + + for { + tt := z.Next() + if tt == html.ErrorToken { + // ... + return ... + } + // Process the current token. + } + +There are two APIs for retrieving the current token. The high-level API is to +call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs +allow optionally calling Raw after Next but before Token, Text, TagName, or +TagAttr. In EBNF notation, the valid call sequence per token is: + + Next {Raw} [ Token | Text | TagName {TagAttr} ] + +Token returns an independent data structure that completely describes a token. +Entities (such as "<") are unescaped, tag names and attribute keys are +lower-cased, and attributes are collected into a []Attribute. For example: + + for { + if z.Next() == html.ErrorToken { + // Returning io.EOF indicates success. + return z.Err() + } + emitToken(z.Token()) + } + +The low-level API performs fewer allocations and copies, but the contents of +the []byte values returned by Text, TagName and TagAttr may change on the next +call to Next. For example, to extract an HTML page's anchor text: + + depth := 0 + for { + tt := z.Next() + switch tt { + case ErrorToken: + return z.Err() + case TextToken: + if depth > 0 { + // emitBytes should copy the []byte it receives, + // if it doesn't process it immediately. + emitBytes(z.Text()) + } + case StartTagToken, EndTagToken: + tn, _ := z.TagName() + if len(tn) == 1 && tn[0] == 'a' { + if tt == StartTagToken { + depth++ + } else { + depth-- + } + } + } + } + +Parsing is done by calling Parse with an io.Reader, which returns the root of +the parse tree (the document element) as a *Node. It is the caller's +responsibility to ensure that the Reader provides UTF-8 encoded HTML. For +example, to process each anchor node in depth-first order: + + doc, err := html.Parse(r) + if err != nil { + // ... + } + var f func(*html.Node) + f = func(n *html.Node) { + if n.Type == html.ElementNode && n.Data == "a" { + // Do something with n... + } + for c := n.FirstChild; c != nil; c = c.NextSibling { + f(c) + } + } + f(doc) + +The relevant specifications include: +https://html.spec.whatwg.org/multipage/syntax.html and +https://html.spec.whatwg.org/multipage/syntax.html#tokenization +*/ +package html // import "golang.org/x/net/html" + +// The tokenization algorithm implemented by this package is not a line-by-line +// transliteration of the relatively verbose state-machine in the WHATWG +// specification. A more direct approach is used instead, where the program +// counter implies the state, such as whether it is tokenizing a tag or a text +// node. Specification compliance is verified by checking expected and actual +// outputs over a test suite rather than aiming for algorithmic fidelity. + +// TODO(nigeltao): Does a DOM API belong in this package or a separate one? +// TODO(nigeltao): How does parsing interact with a JavaScript engine? diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doctype.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doctype.go new file mode 100644 index 0000000..c484e5a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/doctype.go @@ -0,0 +1,156 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "strings" +) + +// parseDoctype parses the data from a DoctypeToken into a name, +// public identifier, and system identifier. It returns a Node whose Type +// is DoctypeNode, whose Data is the name, and which has attributes +// named "system" and "public" for the two identifiers if they were present. +// quirks is whether the document should be parsed in "quirks mode". +func parseDoctype(s string) (n *Node, quirks bool) { + n = &Node{Type: DoctypeNode} + + // Find the name. + space := strings.IndexAny(s, whitespace) + if space == -1 { + space = len(s) + } + n.Data = s[:space] + // The comparison to "html" is case-sensitive. + if n.Data != "html" { + quirks = true + } + n.Data = strings.ToLower(n.Data) + s = strings.TrimLeft(s[space:], whitespace) + + if len(s) < 6 { + // It can't start with "PUBLIC" or "SYSTEM". + // Ignore the rest of the string. + return n, quirks || s != "" + } + + key := strings.ToLower(s[:6]) + s = s[6:] + for key == "public" || key == "system" { + s = strings.TrimLeft(s, whitespace) + if s == "" { + break + } + quote := s[0] + if quote != '"' && quote != '\'' { + break + } + s = s[1:] + q := strings.IndexRune(s, rune(quote)) + var id string + if q == -1 { + id = s + s = "" + } else { + id = s[:q] + s = s[q+1:] + } + n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) + if key == "public" { + key = "system" + } else { + key = "" + } + } + + if key != "" || s != "" { + quirks = true + } else if len(n.Attr) > 0 { + if n.Attr[0].Key == "public" { + public := strings.ToLower(n.Attr[0].Val) + switch public { + case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": + quirks = true + default: + for _, q := range quirkyIDs { + if strings.HasPrefix(public, q) { + quirks = true + break + } + } + } + // The following two public IDs only cause quirks mode if there is no system ID. + if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || + strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { + quirks = true + } + } + if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && + strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { + quirks = true + } + } + + return n, quirks +} + +// quirkyIDs is a list of public doctype identifiers that cause a document +// to be interpreted in quirks mode. The identifiers should be in lower case. +var quirkyIDs = []string{ + "+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/entity.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/entity.go new file mode 100644 index 0000000..a50c04c --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/entity.go @@ -0,0 +1,2253 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +// All entities that do not end with ';' are 6 or fewer bytes long. +const longestEntityWithoutSemicolon = 6 + +// entity is a map from HTML entity names to their values. The semicolon matters: +// https://html.spec.whatwg.org/multipage/syntax.html#named-character-references +// lists both "amp" and "amp;" as two separate entries. +// +// Note that the HTML5 list is larger than the HTML4 list at +// http://www.w3.org/TR/html4/sgml/entities.html +var entity = map[string]rune{ + "AElig;": '\U000000C6', + "AMP;": '\U00000026', + "Aacute;": '\U000000C1', + "Abreve;": '\U00000102', + "Acirc;": '\U000000C2', + "Acy;": '\U00000410', + "Afr;": '\U0001D504', + "Agrave;": '\U000000C0', + "Alpha;": '\U00000391', + "Amacr;": '\U00000100', + "And;": '\U00002A53', + "Aogon;": '\U00000104', + "Aopf;": '\U0001D538', + "ApplyFunction;": '\U00002061', + "Aring;": '\U000000C5', + "Ascr;": '\U0001D49C', + "Assign;": '\U00002254', + "Atilde;": '\U000000C3', + "Auml;": '\U000000C4', + "Backslash;": '\U00002216', + "Barv;": '\U00002AE7', + "Barwed;": '\U00002306', + "Bcy;": '\U00000411', + "Because;": '\U00002235', + "Bernoullis;": '\U0000212C', + "Beta;": '\U00000392', + "Bfr;": '\U0001D505', + "Bopf;": '\U0001D539', + "Breve;": '\U000002D8', + "Bscr;": '\U0000212C', + "Bumpeq;": '\U0000224E', + "CHcy;": '\U00000427', + "COPY;": '\U000000A9', + "Cacute;": '\U00000106', + "Cap;": '\U000022D2', + "CapitalDifferentialD;": '\U00002145', + "Cayleys;": '\U0000212D', + "Ccaron;": '\U0000010C', + "Ccedil;": '\U000000C7', + "Ccirc;": '\U00000108', + "Cconint;": '\U00002230', + "Cdot;": '\U0000010A', + "Cedilla;": '\U000000B8', + "CenterDot;": '\U000000B7', + "Cfr;": '\U0000212D', + "Chi;": '\U000003A7', + "CircleDot;": '\U00002299', + "CircleMinus;": '\U00002296', + "CirclePlus;": '\U00002295', + "CircleTimes;": '\U00002297', + "ClockwiseContourIntegral;": '\U00002232', + "CloseCurlyDoubleQuote;": '\U0000201D', + "CloseCurlyQuote;": '\U00002019', + "Colon;": '\U00002237', + "Colone;": '\U00002A74', + "Congruent;": '\U00002261', + "Conint;": '\U0000222F', + "ContourIntegral;": '\U0000222E', + "Copf;": '\U00002102', + "Coproduct;": '\U00002210', + "CounterClockwiseContourIntegral;": '\U00002233', + "Cross;": '\U00002A2F', + "Cscr;": '\U0001D49E', + "Cup;": '\U000022D3', + "CupCap;": '\U0000224D', + "DD;": '\U00002145', + "DDotrahd;": '\U00002911', + "DJcy;": '\U00000402', + "DScy;": '\U00000405', + "DZcy;": '\U0000040F', + "Dagger;": '\U00002021', + "Darr;": '\U000021A1', + "Dashv;": '\U00002AE4', + "Dcaron;": '\U0000010E', + "Dcy;": '\U00000414', + "Del;": '\U00002207', + "Delta;": '\U00000394', + "Dfr;": '\U0001D507', + "DiacriticalAcute;": '\U000000B4', + "DiacriticalDot;": '\U000002D9', + "DiacriticalDoubleAcute;": '\U000002DD', + "DiacriticalGrave;": '\U00000060', + "DiacriticalTilde;": '\U000002DC', + "Diamond;": '\U000022C4', + "DifferentialD;": '\U00002146', + "Dopf;": '\U0001D53B', + "Dot;": '\U000000A8', + "DotDot;": '\U000020DC', + "DotEqual;": '\U00002250', + "DoubleContourIntegral;": '\U0000222F', + "DoubleDot;": '\U000000A8', + "DoubleDownArrow;": '\U000021D3', + "DoubleLeftArrow;": '\U000021D0', + "DoubleLeftRightArrow;": '\U000021D4', + "DoubleLeftTee;": '\U00002AE4', + "DoubleLongLeftArrow;": '\U000027F8', + "DoubleLongLeftRightArrow;": '\U000027FA', + "DoubleLongRightArrow;": '\U000027F9', + "DoubleRightArrow;": '\U000021D2', + "DoubleRightTee;": '\U000022A8', + "DoubleUpArrow;": '\U000021D1', + "DoubleUpDownArrow;": '\U000021D5', + "DoubleVerticalBar;": '\U00002225', + "DownArrow;": '\U00002193', + "DownArrowBar;": '\U00002913', + "DownArrowUpArrow;": '\U000021F5', + "DownBreve;": '\U00000311', + "DownLeftRightVector;": '\U00002950', + "DownLeftTeeVector;": '\U0000295E', + "DownLeftVector;": '\U000021BD', + "DownLeftVectorBar;": '\U00002956', + "DownRightTeeVector;": '\U0000295F', + "DownRightVector;": '\U000021C1', + "DownRightVectorBar;": '\U00002957', + "DownTee;": '\U000022A4', + "DownTeeArrow;": '\U000021A7', + "Downarrow;": '\U000021D3', + "Dscr;": '\U0001D49F', + "Dstrok;": '\U00000110', + "ENG;": '\U0000014A', + "ETH;": '\U000000D0', + "Eacute;": '\U000000C9', + "Ecaron;": '\U0000011A', + "Ecirc;": '\U000000CA', + "Ecy;": '\U0000042D', + "Edot;": '\U00000116', + "Efr;": '\U0001D508', + "Egrave;": '\U000000C8', + "Element;": '\U00002208', + "Emacr;": '\U00000112', + "EmptySmallSquare;": '\U000025FB', + "EmptyVerySmallSquare;": '\U000025AB', + "Eogon;": '\U00000118', + "Eopf;": '\U0001D53C', + "Epsilon;": '\U00000395', + "Equal;": '\U00002A75', + "EqualTilde;": '\U00002242', + "Equilibrium;": '\U000021CC', + "Escr;": '\U00002130', + "Esim;": '\U00002A73', + "Eta;": '\U00000397', + "Euml;": '\U000000CB', + "Exists;": '\U00002203', + "ExponentialE;": '\U00002147', + "Fcy;": '\U00000424', + "Ffr;": '\U0001D509', + "FilledSmallSquare;": '\U000025FC', + "FilledVerySmallSquare;": '\U000025AA', + "Fopf;": '\U0001D53D', + "ForAll;": '\U00002200', + "Fouriertrf;": '\U00002131', + "Fscr;": '\U00002131', + "GJcy;": '\U00000403', + "GT;": '\U0000003E', + "Gamma;": '\U00000393', + "Gammad;": '\U000003DC', + "Gbreve;": '\U0000011E', + "Gcedil;": '\U00000122', + "Gcirc;": '\U0000011C', + "Gcy;": '\U00000413', + "Gdot;": '\U00000120', + "Gfr;": '\U0001D50A', + "Gg;": '\U000022D9', + "Gopf;": '\U0001D53E', + "GreaterEqual;": '\U00002265', + "GreaterEqualLess;": '\U000022DB', + "GreaterFullEqual;": '\U00002267', + "GreaterGreater;": '\U00002AA2', + "GreaterLess;": '\U00002277', + "GreaterSlantEqual;": '\U00002A7E', + "GreaterTilde;": '\U00002273', + "Gscr;": '\U0001D4A2', + "Gt;": '\U0000226B', + "HARDcy;": '\U0000042A', + "Hacek;": '\U000002C7', + "Hat;": '\U0000005E', + "Hcirc;": '\U00000124', + "Hfr;": '\U0000210C', + "HilbertSpace;": '\U0000210B', + "Hopf;": '\U0000210D', + "HorizontalLine;": '\U00002500', + "Hscr;": '\U0000210B', + "Hstrok;": '\U00000126', + "HumpDownHump;": '\U0000224E', + "HumpEqual;": '\U0000224F', + "IEcy;": '\U00000415', + "IJlig;": '\U00000132', + "IOcy;": '\U00000401', + "Iacute;": '\U000000CD', + "Icirc;": '\U000000CE', + "Icy;": '\U00000418', + "Idot;": '\U00000130', + "Ifr;": '\U00002111', + "Igrave;": '\U000000CC', + "Im;": '\U00002111', + "Imacr;": '\U0000012A', + "ImaginaryI;": '\U00002148', + "Implies;": '\U000021D2', + "Int;": '\U0000222C', + "Integral;": '\U0000222B', + "Intersection;": '\U000022C2', + "InvisibleComma;": '\U00002063', + "InvisibleTimes;": '\U00002062', + "Iogon;": '\U0000012E', + "Iopf;": '\U0001D540', + "Iota;": '\U00000399', + "Iscr;": '\U00002110', + "Itilde;": '\U00000128', + "Iukcy;": '\U00000406', + "Iuml;": '\U000000CF', + "Jcirc;": '\U00000134', + "Jcy;": '\U00000419', + "Jfr;": '\U0001D50D', + "Jopf;": '\U0001D541', + "Jscr;": '\U0001D4A5', + "Jsercy;": '\U00000408', + "Jukcy;": '\U00000404', + "KHcy;": '\U00000425', + "KJcy;": '\U0000040C', + "Kappa;": '\U0000039A', + "Kcedil;": '\U00000136', + "Kcy;": '\U0000041A', + "Kfr;": '\U0001D50E', + "Kopf;": '\U0001D542', + "Kscr;": '\U0001D4A6', + "LJcy;": '\U00000409', + "LT;": '\U0000003C', + "Lacute;": '\U00000139', + "Lambda;": '\U0000039B', + "Lang;": '\U000027EA', + "Laplacetrf;": '\U00002112', + "Larr;": '\U0000219E', + "Lcaron;": '\U0000013D', + "Lcedil;": '\U0000013B', + "Lcy;": '\U0000041B', + "LeftAngleBracket;": '\U000027E8', + "LeftArrow;": '\U00002190', + "LeftArrowBar;": '\U000021E4', + "LeftArrowRightArrow;": '\U000021C6', + "LeftCeiling;": '\U00002308', + "LeftDoubleBracket;": '\U000027E6', + "LeftDownTeeVector;": '\U00002961', + "LeftDownVector;": '\U000021C3', + "LeftDownVectorBar;": '\U00002959', + "LeftFloor;": '\U0000230A', + "LeftRightArrow;": '\U00002194', + "LeftRightVector;": '\U0000294E', + "LeftTee;": '\U000022A3', + "LeftTeeArrow;": '\U000021A4', + "LeftTeeVector;": '\U0000295A', + "LeftTriangle;": '\U000022B2', + "LeftTriangleBar;": '\U000029CF', + "LeftTriangleEqual;": '\U000022B4', + "LeftUpDownVector;": '\U00002951', + "LeftUpTeeVector;": '\U00002960', + "LeftUpVector;": '\U000021BF', + "LeftUpVectorBar;": '\U00002958', + "LeftVector;": '\U000021BC', + "LeftVectorBar;": '\U00002952', + "Leftarrow;": '\U000021D0', + "Leftrightarrow;": '\U000021D4', + "LessEqualGreater;": '\U000022DA', + "LessFullEqual;": '\U00002266', + "LessGreater;": '\U00002276', + "LessLess;": '\U00002AA1', + "LessSlantEqual;": '\U00002A7D', + "LessTilde;": '\U00002272', + "Lfr;": '\U0001D50F', + "Ll;": '\U000022D8', + "Lleftarrow;": '\U000021DA', + "Lmidot;": '\U0000013F', + "LongLeftArrow;": '\U000027F5', + "LongLeftRightArrow;": '\U000027F7', + "LongRightArrow;": '\U000027F6', + "Longleftarrow;": '\U000027F8', + "Longleftrightarrow;": '\U000027FA', + "Longrightarrow;": '\U000027F9', + "Lopf;": '\U0001D543', + "LowerLeftArrow;": '\U00002199', + "LowerRightArrow;": '\U00002198', + "Lscr;": '\U00002112', + "Lsh;": '\U000021B0', + "Lstrok;": '\U00000141', + "Lt;": '\U0000226A', + "Map;": '\U00002905', + "Mcy;": '\U0000041C', + "MediumSpace;": '\U0000205F', + "Mellintrf;": '\U00002133', + "Mfr;": '\U0001D510', + "MinusPlus;": '\U00002213', + "Mopf;": '\U0001D544', + "Mscr;": '\U00002133', + "Mu;": '\U0000039C', + "NJcy;": '\U0000040A', + "Nacute;": '\U00000143', + "Ncaron;": '\U00000147', + "Ncedil;": '\U00000145', + "Ncy;": '\U0000041D', + "NegativeMediumSpace;": '\U0000200B', + "NegativeThickSpace;": '\U0000200B', + "NegativeThinSpace;": '\U0000200B', + "NegativeVeryThinSpace;": '\U0000200B', + "NestedGreaterGreater;": '\U0000226B', + "NestedLessLess;": '\U0000226A', + "NewLine;": '\U0000000A', + "Nfr;": '\U0001D511', + "NoBreak;": '\U00002060', + "NonBreakingSpace;": '\U000000A0', + "Nopf;": '\U00002115', + "Not;": '\U00002AEC', + "NotCongruent;": '\U00002262', + "NotCupCap;": '\U0000226D', + "NotDoubleVerticalBar;": '\U00002226', + "NotElement;": '\U00002209', + "NotEqual;": '\U00002260', + "NotExists;": '\U00002204', + "NotGreater;": '\U0000226F', + "NotGreaterEqual;": '\U00002271', + "NotGreaterLess;": '\U00002279', + "NotGreaterTilde;": '\U00002275', + "NotLeftTriangle;": '\U000022EA', + "NotLeftTriangleEqual;": '\U000022EC', + "NotLess;": '\U0000226E', + "NotLessEqual;": '\U00002270', + "NotLessGreater;": '\U00002278', + "NotLessTilde;": '\U00002274', + "NotPrecedes;": '\U00002280', + "NotPrecedesSlantEqual;": '\U000022E0', + "NotReverseElement;": '\U0000220C', + "NotRightTriangle;": '\U000022EB', + "NotRightTriangleEqual;": '\U000022ED', + "NotSquareSubsetEqual;": '\U000022E2', + "NotSquareSupersetEqual;": '\U000022E3', + "NotSubsetEqual;": '\U00002288', + "NotSucceeds;": '\U00002281', + "NotSucceedsSlantEqual;": '\U000022E1', + "NotSupersetEqual;": '\U00002289', + "NotTilde;": '\U00002241', + "NotTildeEqual;": '\U00002244', + "NotTildeFullEqual;": '\U00002247', + "NotTildeTilde;": '\U00002249', + "NotVerticalBar;": '\U00002224', + "Nscr;": '\U0001D4A9', + "Ntilde;": '\U000000D1', + "Nu;": '\U0000039D', + "OElig;": '\U00000152', + "Oacute;": '\U000000D3', + "Ocirc;": '\U000000D4', + "Ocy;": '\U0000041E', + "Odblac;": '\U00000150', + "Ofr;": '\U0001D512', + "Ograve;": '\U000000D2', + "Omacr;": '\U0000014C', + "Omega;": '\U000003A9', + "Omicron;": '\U0000039F', + "Oopf;": '\U0001D546', + "OpenCurlyDoubleQuote;": '\U0000201C', + "OpenCurlyQuote;": '\U00002018', + "Or;": '\U00002A54', + "Oscr;": '\U0001D4AA', + "Oslash;": '\U000000D8', + "Otilde;": '\U000000D5', + "Otimes;": '\U00002A37', + "Ouml;": '\U000000D6', + "OverBar;": '\U0000203E', + "OverBrace;": '\U000023DE', + "OverBracket;": '\U000023B4', + "OverParenthesis;": '\U000023DC', + "PartialD;": '\U00002202', + "Pcy;": '\U0000041F', + "Pfr;": '\U0001D513', + "Phi;": '\U000003A6', + "Pi;": '\U000003A0', + "PlusMinus;": '\U000000B1', + "Poincareplane;": '\U0000210C', + "Popf;": '\U00002119', + "Pr;": '\U00002ABB', + "Precedes;": '\U0000227A', + "PrecedesEqual;": '\U00002AAF', + "PrecedesSlantEqual;": '\U0000227C', + "PrecedesTilde;": '\U0000227E', + "Prime;": '\U00002033', + "Product;": '\U0000220F', + "Proportion;": '\U00002237', + "Proportional;": '\U0000221D', + "Pscr;": '\U0001D4AB', + "Psi;": '\U000003A8', + "QUOT;": '\U00000022', + "Qfr;": '\U0001D514', + "Qopf;": '\U0000211A', + "Qscr;": '\U0001D4AC', + "RBarr;": '\U00002910', + "REG;": '\U000000AE', + "Racute;": '\U00000154', + "Rang;": '\U000027EB', + "Rarr;": '\U000021A0', + "Rarrtl;": '\U00002916', + "Rcaron;": '\U00000158', + "Rcedil;": '\U00000156', + "Rcy;": '\U00000420', + "Re;": '\U0000211C', + "ReverseElement;": '\U0000220B', + "ReverseEquilibrium;": '\U000021CB', + "ReverseUpEquilibrium;": '\U0000296F', + "Rfr;": '\U0000211C', + "Rho;": '\U000003A1', + "RightAngleBracket;": '\U000027E9', + "RightArrow;": '\U00002192', + "RightArrowBar;": '\U000021E5', + "RightArrowLeftArrow;": '\U000021C4', + "RightCeiling;": '\U00002309', + "RightDoubleBracket;": '\U000027E7', + "RightDownTeeVector;": '\U0000295D', + "RightDownVector;": '\U000021C2', + "RightDownVectorBar;": '\U00002955', + "RightFloor;": '\U0000230B', + "RightTee;": '\U000022A2', + "RightTeeArrow;": '\U000021A6', + "RightTeeVector;": '\U0000295B', + "RightTriangle;": '\U000022B3', + "RightTriangleBar;": '\U000029D0', + "RightTriangleEqual;": '\U000022B5', + "RightUpDownVector;": '\U0000294F', + "RightUpTeeVector;": '\U0000295C', + "RightUpVector;": '\U000021BE', + "RightUpVectorBar;": '\U00002954', + "RightVector;": '\U000021C0', + "RightVectorBar;": '\U00002953', + "Rightarrow;": '\U000021D2', + "Ropf;": '\U0000211D', + "RoundImplies;": '\U00002970', + "Rrightarrow;": '\U000021DB', + "Rscr;": '\U0000211B', + "Rsh;": '\U000021B1', + "RuleDelayed;": '\U000029F4', + "SHCHcy;": '\U00000429', + "SHcy;": '\U00000428', + "SOFTcy;": '\U0000042C', + "Sacute;": '\U0000015A', + "Sc;": '\U00002ABC', + "Scaron;": '\U00000160', + "Scedil;": '\U0000015E', + "Scirc;": '\U0000015C', + "Scy;": '\U00000421', + "Sfr;": '\U0001D516', + "ShortDownArrow;": '\U00002193', + "ShortLeftArrow;": '\U00002190', + "ShortRightArrow;": '\U00002192', + "ShortUpArrow;": '\U00002191', + "Sigma;": '\U000003A3', + "SmallCircle;": '\U00002218', + "Sopf;": '\U0001D54A', + "Sqrt;": '\U0000221A', + "Square;": '\U000025A1', + "SquareIntersection;": '\U00002293', + "SquareSubset;": '\U0000228F', + "SquareSubsetEqual;": '\U00002291', + "SquareSuperset;": '\U00002290', + "SquareSupersetEqual;": '\U00002292', + "SquareUnion;": '\U00002294', + "Sscr;": '\U0001D4AE', + "Star;": '\U000022C6', + "Sub;": '\U000022D0', + "Subset;": '\U000022D0', + "SubsetEqual;": '\U00002286', + "Succeeds;": '\U0000227B', + "SucceedsEqual;": '\U00002AB0', + "SucceedsSlantEqual;": '\U0000227D', + "SucceedsTilde;": '\U0000227F', + "SuchThat;": '\U0000220B', + "Sum;": '\U00002211', + "Sup;": '\U000022D1', + "Superset;": '\U00002283', + "SupersetEqual;": '\U00002287', + "Supset;": '\U000022D1', + "THORN;": '\U000000DE', + "TRADE;": '\U00002122', + "TSHcy;": '\U0000040B', + "TScy;": '\U00000426', + "Tab;": '\U00000009', + "Tau;": '\U000003A4', + "Tcaron;": '\U00000164', + "Tcedil;": '\U00000162', + "Tcy;": '\U00000422', + "Tfr;": '\U0001D517', + "Therefore;": '\U00002234', + "Theta;": '\U00000398', + "ThinSpace;": '\U00002009', + "Tilde;": '\U0000223C', + "TildeEqual;": '\U00002243', + "TildeFullEqual;": '\U00002245', + "TildeTilde;": '\U00002248', + "Topf;": '\U0001D54B', + "TripleDot;": '\U000020DB', + "Tscr;": '\U0001D4AF', + "Tstrok;": '\U00000166', + "Uacute;": '\U000000DA', + "Uarr;": '\U0000219F', + "Uarrocir;": '\U00002949', + "Ubrcy;": '\U0000040E', + "Ubreve;": '\U0000016C', + "Ucirc;": '\U000000DB', + "Ucy;": '\U00000423', + "Udblac;": '\U00000170', + "Ufr;": '\U0001D518', + "Ugrave;": '\U000000D9', + "Umacr;": '\U0000016A', + "UnderBar;": '\U0000005F', + "UnderBrace;": '\U000023DF', + "UnderBracket;": '\U000023B5', + "UnderParenthesis;": '\U000023DD', + "Union;": '\U000022C3', + "UnionPlus;": '\U0000228E', + "Uogon;": '\U00000172', + "Uopf;": '\U0001D54C', + "UpArrow;": '\U00002191', + "UpArrowBar;": '\U00002912', + "UpArrowDownArrow;": '\U000021C5', + "UpDownArrow;": '\U00002195', + "UpEquilibrium;": '\U0000296E', + "UpTee;": '\U000022A5', + "UpTeeArrow;": '\U000021A5', + "Uparrow;": '\U000021D1', + "Updownarrow;": '\U000021D5', + "UpperLeftArrow;": '\U00002196', + "UpperRightArrow;": '\U00002197', + "Upsi;": '\U000003D2', + "Upsilon;": '\U000003A5', + "Uring;": '\U0000016E', + "Uscr;": '\U0001D4B0', + "Utilde;": '\U00000168', + "Uuml;": '\U000000DC', + "VDash;": '\U000022AB', + "Vbar;": '\U00002AEB', + "Vcy;": '\U00000412', + "Vdash;": '\U000022A9', + "Vdashl;": '\U00002AE6', + "Vee;": '\U000022C1', + "Verbar;": '\U00002016', + "Vert;": '\U00002016', + "VerticalBar;": '\U00002223', + "VerticalLine;": '\U0000007C', + "VerticalSeparator;": '\U00002758', + "VerticalTilde;": '\U00002240', + "VeryThinSpace;": '\U0000200A', + "Vfr;": '\U0001D519', + "Vopf;": '\U0001D54D', + "Vscr;": '\U0001D4B1', + "Vvdash;": '\U000022AA', + "Wcirc;": '\U00000174', + "Wedge;": '\U000022C0', + "Wfr;": '\U0001D51A', + "Wopf;": '\U0001D54E', + "Wscr;": '\U0001D4B2', + "Xfr;": '\U0001D51B', + "Xi;": '\U0000039E', + "Xopf;": '\U0001D54F', + "Xscr;": '\U0001D4B3', + "YAcy;": '\U0000042F', + "YIcy;": '\U00000407', + "YUcy;": '\U0000042E', + "Yacute;": '\U000000DD', + "Ycirc;": '\U00000176', + "Ycy;": '\U0000042B', + "Yfr;": '\U0001D51C', + "Yopf;": '\U0001D550', + "Yscr;": '\U0001D4B4', + "Yuml;": '\U00000178', + "ZHcy;": '\U00000416', + "Zacute;": '\U00000179', + "Zcaron;": '\U0000017D', + "Zcy;": '\U00000417', + "Zdot;": '\U0000017B', + "ZeroWidthSpace;": '\U0000200B', + "Zeta;": '\U00000396', + "Zfr;": '\U00002128', + "Zopf;": '\U00002124', + "Zscr;": '\U0001D4B5', + "aacute;": '\U000000E1', + "abreve;": '\U00000103', + "ac;": '\U0000223E', + "acd;": '\U0000223F', + "acirc;": '\U000000E2', + "acute;": '\U000000B4', + "acy;": '\U00000430', + "aelig;": '\U000000E6', + "af;": '\U00002061', + "afr;": '\U0001D51E', + "agrave;": '\U000000E0', + "alefsym;": '\U00002135', + "aleph;": '\U00002135', + "alpha;": '\U000003B1', + "amacr;": '\U00000101', + "amalg;": '\U00002A3F', + "amp;": '\U00000026', + "and;": '\U00002227', + "andand;": '\U00002A55', + "andd;": '\U00002A5C', + "andslope;": '\U00002A58', + "andv;": '\U00002A5A', + "ang;": '\U00002220', + "ange;": '\U000029A4', + "angle;": '\U00002220', + "angmsd;": '\U00002221', + "angmsdaa;": '\U000029A8', + "angmsdab;": '\U000029A9', + "angmsdac;": '\U000029AA', + "angmsdad;": '\U000029AB', + "angmsdae;": '\U000029AC', + "angmsdaf;": '\U000029AD', + "angmsdag;": '\U000029AE', + "angmsdah;": '\U000029AF', + "angrt;": '\U0000221F', + "angrtvb;": '\U000022BE', + "angrtvbd;": '\U0000299D', + "angsph;": '\U00002222', + "angst;": '\U000000C5', + "angzarr;": '\U0000237C', + "aogon;": '\U00000105', + "aopf;": '\U0001D552', + "ap;": '\U00002248', + "apE;": '\U00002A70', + "apacir;": '\U00002A6F', + "ape;": '\U0000224A', + "apid;": '\U0000224B', + "apos;": '\U00000027', + "approx;": '\U00002248', + "approxeq;": '\U0000224A', + "aring;": '\U000000E5', + "ascr;": '\U0001D4B6', + "ast;": '\U0000002A', + "asymp;": '\U00002248', + "asympeq;": '\U0000224D', + "atilde;": '\U000000E3', + "auml;": '\U000000E4', + "awconint;": '\U00002233', + "awint;": '\U00002A11', + "bNot;": '\U00002AED', + "backcong;": '\U0000224C', + "backepsilon;": '\U000003F6', + "backprime;": '\U00002035', + "backsim;": '\U0000223D', + "backsimeq;": '\U000022CD', + "barvee;": '\U000022BD', + "barwed;": '\U00002305', + "barwedge;": '\U00002305', + "bbrk;": '\U000023B5', + "bbrktbrk;": '\U000023B6', + "bcong;": '\U0000224C', + "bcy;": '\U00000431', + "bdquo;": '\U0000201E', + "becaus;": '\U00002235', + "because;": '\U00002235', + "bemptyv;": '\U000029B0', + "bepsi;": '\U000003F6', + "bernou;": '\U0000212C', + "beta;": '\U000003B2', + "beth;": '\U00002136', + "between;": '\U0000226C', + "bfr;": '\U0001D51F', + "bigcap;": '\U000022C2', + "bigcirc;": '\U000025EF', + "bigcup;": '\U000022C3', + "bigodot;": '\U00002A00', + "bigoplus;": '\U00002A01', + "bigotimes;": '\U00002A02', + "bigsqcup;": '\U00002A06', + "bigstar;": '\U00002605', + "bigtriangledown;": '\U000025BD', + "bigtriangleup;": '\U000025B3', + "biguplus;": '\U00002A04', + "bigvee;": '\U000022C1', + "bigwedge;": '\U000022C0', + "bkarow;": '\U0000290D', + "blacklozenge;": '\U000029EB', + "blacksquare;": '\U000025AA', + "blacktriangle;": '\U000025B4', + "blacktriangledown;": '\U000025BE', + "blacktriangleleft;": '\U000025C2', + "blacktriangleright;": '\U000025B8', + "blank;": '\U00002423', + "blk12;": '\U00002592', + "blk14;": '\U00002591', + "blk34;": '\U00002593', + "block;": '\U00002588', + "bnot;": '\U00002310', + "bopf;": '\U0001D553', + "bot;": '\U000022A5', + "bottom;": '\U000022A5', + "bowtie;": '\U000022C8', + "boxDL;": '\U00002557', + "boxDR;": '\U00002554', + "boxDl;": '\U00002556', + "boxDr;": '\U00002553', + "boxH;": '\U00002550', + "boxHD;": '\U00002566', + "boxHU;": '\U00002569', + "boxHd;": '\U00002564', + "boxHu;": '\U00002567', + "boxUL;": '\U0000255D', + "boxUR;": '\U0000255A', + "boxUl;": '\U0000255C', + "boxUr;": '\U00002559', + "boxV;": '\U00002551', + "boxVH;": '\U0000256C', + "boxVL;": '\U00002563', + "boxVR;": '\U00002560', + "boxVh;": '\U0000256B', + "boxVl;": '\U00002562', + "boxVr;": '\U0000255F', + "boxbox;": '\U000029C9', + "boxdL;": '\U00002555', + "boxdR;": '\U00002552', + "boxdl;": '\U00002510', + "boxdr;": '\U0000250C', + "boxh;": '\U00002500', + "boxhD;": '\U00002565', + "boxhU;": '\U00002568', + "boxhd;": '\U0000252C', + "boxhu;": '\U00002534', + "boxminus;": '\U0000229F', + "boxplus;": '\U0000229E', + "boxtimes;": '\U000022A0', + "boxuL;": '\U0000255B', + "boxuR;": '\U00002558', + "boxul;": '\U00002518', + "boxur;": '\U00002514', + "boxv;": '\U00002502', + "boxvH;": '\U0000256A', + "boxvL;": '\U00002561', + "boxvR;": '\U0000255E', + "boxvh;": '\U0000253C', + "boxvl;": '\U00002524', + "boxvr;": '\U0000251C', + "bprime;": '\U00002035', + "breve;": '\U000002D8', + "brvbar;": '\U000000A6', + "bscr;": '\U0001D4B7', + "bsemi;": '\U0000204F', + "bsim;": '\U0000223D', + "bsime;": '\U000022CD', + "bsol;": '\U0000005C', + "bsolb;": '\U000029C5', + "bsolhsub;": '\U000027C8', + "bull;": '\U00002022', + "bullet;": '\U00002022', + "bump;": '\U0000224E', + "bumpE;": '\U00002AAE', + "bumpe;": '\U0000224F', + "bumpeq;": '\U0000224F', + "cacute;": '\U00000107', + "cap;": '\U00002229', + "capand;": '\U00002A44', + "capbrcup;": '\U00002A49', + "capcap;": '\U00002A4B', + "capcup;": '\U00002A47', + "capdot;": '\U00002A40', + "caret;": '\U00002041', + "caron;": '\U000002C7', + "ccaps;": '\U00002A4D', + "ccaron;": '\U0000010D', + "ccedil;": '\U000000E7', + "ccirc;": '\U00000109', + "ccups;": '\U00002A4C', + "ccupssm;": '\U00002A50', + "cdot;": '\U0000010B', + "cedil;": '\U000000B8', + "cemptyv;": '\U000029B2', + "cent;": '\U000000A2', + "centerdot;": '\U000000B7', + "cfr;": '\U0001D520', + "chcy;": '\U00000447', + "check;": '\U00002713', + "checkmark;": '\U00002713', + "chi;": '\U000003C7', + "cir;": '\U000025CB', + "cirE;": '\U000029C3', + "circ;": '\U000002C6', + "circeq;": '\U00002257', + "circlearrowleft;": '\U000021BA', + "circlearrowright;": '\U000021BB', + "circledR;": '\U000000AE', + "circledS;": '\U000024C8', + "circledast;": '\U0000229B', + "circledcirc;": '\U0000229A', + "circleddash;": '\U0000229D', + "cire;": '\U00002257', + "cirfnint;": '\U00002A10', + "cirmid;": '\U00002AEF', + "cirscir;": '\U000029C2', + "clubs;": '\U00002663', + "clubsuit;": '\U00002663', + "colon;": '\U0000003A', + "colone;": '\U00002254', + "coloneq;": '\U00002254', + "comma;": '\U0000002C', + "commat;": '\U00000040', + "comp;": '\U00002201', + "compfn;": '\U00002218', + "complement;": '\U00002201', + "complexes;": '\U00002102', + "cong;": '\U00002245', + "congdot;": '\U00002A6D', + "conint;": '\U0000222E', + "copf;": '\U0001D554', + "coprod;": '\U00002210', + "copy;": '\U000000A9', + "copysr;": '\U00002117', + "crarr;": '\U000021B5', + "cross;": '\U00002717', + "cscr;": '\U0001D4B8', + "csub;": '\U00002ACF', + "csube;": '\U00002AD1', + "csup;": '\U00002AD0', + "csupe;": '\U00002AD2', + "ctdot;": '\U000022EF', + "cudarrl;": '\U00002938', + "cudarrr;": '\U00002935', + "cuepr;": '\U000022DE', + "cuesc;": '\U000022DF', + "cularr;": '\U000021B6', + "cularrp;": '\U0000293D', + "cup;": '\U0000222A', + "cupbrcap;": '\U00002A48', + "cupcap;": '\U00002A46', + "cupcup;": '\U00002A4A', + "cupdot;": '\U0000228D', + "cupor;": '\U00002A45', + "curarr;": '\U000021B7', + "curarrm;": '\U0000293C', + "curlyeqprec;": '\U000022DE', + "curlyeqsucc;": '\U000022DF', + "curlyvee;": '\U000022CE', + "curlywedge;": '\U000022CF', + "curren;": '\U000000A4', + "curvearrowleft;": '\U000021B6', + "curvearrowright;": '\U000021B7', + "cuvee;": '\U000022CE', + "cuwed;": '\U000022CF', + "cwconint;": '\U00002232', + "cwint;": '\U00002231', + "cylcty;": '\U0000232D', + "dArr;": '\U000021D3', + "dHar;": '\U00002965', + "dagger;": '\U00002020', + "daleth;": '\U00002138', + "darr;": '\U00002193', + "dash;": '\U00002010', + "dashv;": '\U000022A3', + "dbkarow;": '\U0000290F', + "dblac;": '\U000002DD', + "dcaron;": '\U0000010F', + "dcy;": '\U00000434', + "dd;": '\U00002146', + "ddagger;": '\U00002021', + "ddarr;": '\U000021CA', + "ddotseq;": '\U00002A77', + "deg;": '\U000000B0', + "delta;": '\U000003B4', + "demptyv;": '\U000029B1', + "dfisht;": '\U0000297F', + "dfr;": '\U0001D521', + "dharl;": '\U000021C3', + "dharr;": '\U000021C2', + "diam;": '\U000022C4', + "diamond;": '\U000022C4', + "diamondsuit;": '\U00002666', + "diams;": '\U00002666', + "die;": '\U000000A8', + "digamma;": '\U000003DD', + "disin;": '\U000022F2', + "div;": '\U000000F7', + "divide;": '\U000000F7', + "divideontimes;": '\U000022C7', + "divonx;": '\U000022C7', + "djcy;": '\U00000452', + "dlcorn;": '\U0000231E', + "dlcrop;": '\U0000230D', + "dollar;": '\U00000024', + "dopf;": '\U0001D555', + "dot;": '\U000002D9', + "doteq;": '\U00002250', + "doteqdot;": '\U00002251', + "dotminus;": '\U00002238', + "dotplus;": '\U00002214', + "dotsquare;": '\U000022A1', + "doublebarwedge;": '\U00002306', + "downarrow;": '\U00002193', + "downdownarrows;": '\U000021CA', + "downharpoonleft;": '\U000021C3', + "downharpoonright;": '\U000021C2', + "drbkarow;": '\U00002910', + "drcorn;": '\U0000231F', + "drcrop;": '\U0000230C', + "dscr;": '\U0001D4B9', + "dscy;": '\U00000455', + "dsol;": '\U000029F6', + "dstrok;": '\U00000111', + "dtdot;": '\U000022F1', + "dtri;": '\U000025BF', + "dtrif;": '\U000025BE', + "duarr;": '\U000021F5', + "duhar;": '\U0000296F', + "dwangle;": '\U000029A6', + "dzcy;": '\U0000045F', + "dzigrarr;": '\U000027FF', + "eDDot;": '\U00002A77', + "eDot;": '\U00002251', + "eacute;": '\U000000E9', + "easter;": '\U00002A6E', + "ecaron;": '\U0000011B', + "ecir;": '\U00002256', + "ecirc;": '\U000000EA', + "ecolon;": '\U00002255', + "ecy;": '\U0000044D', + "edot;": '\U00000117', + "ee;": '\U00002147', + "efDot;": '\U00002252', + "efr;": '\U0001D522', + "eg;": '\U00002A9A', + "egrave;": '\U000000E8', + "egs;": '\U00002A96', + "egsdot;": '\U00002A98', + "el;": '\U00002A99', + "elinters;": '\U000023E7', + "ell;": '\U00002113', + "els;": '\U00002A95', + "elsdot;": '\U00002A97', + "emacr;": '\U00000113', + "empty;": '\U00002205', + "emptyset;": '\U00002205', + "emptyv;": '\U00002205', + "emsp;": '\U00002003', + "emsp13;": '\U00002004', + "emsp14;": '\U00002005', + "eng;": '\U0000014B', + "ensp;": '\U00002002', + "eogon;": '\U00000119', + "eopf;": '\U0001D556', + "epar;": '\U000022D5', + "eparsl;": '\U000029E3', + "eplus;": '\U00002A71', + "epsi;": '\U000003B5', + "epsilon;": '\U000003B5', + "epsiv;": '\U000003F5', + "eqcirc;": '\U00002256', + "eqcolon;": '\U00002255', + "eqsim;": '\U00002242', + "eqslantgtr;": '\U00002A96', + "eqslantless;": '\U00002A95', + "equals;": '\U0000003D', + "equest;": '\U0000225F', + "equiv;": '\U00002261', + "equivDD;": '\U00002A78', + "eqvparsl;": '\U000029E5', + "erDot;": '\U00002253', + "erarr;": '\U00002971', + "escr;": '\U0000212F', + "esdot;": '\U00002250', + "esim;": '\U00002242', + "eta;": '\U000003B7', + "eth;": '\U000000F0', + "euml;": '\U000000EB', + "euro;": '\U000020AC', + "excl;": '\U00000021', + "exist;": '\U00002203', + "expectation;": '\U00002130', + "exponentiale;": '\U00002147', + "fallingdotseq;": '\U00002252', + "fcy;": '\U00000444', + "female;": '\U00002640', + "ffilig;": '\U0000FB03', + "fflig;": '\U0000FB00', + "ffllig;": '\U0000FB04', + "ffr;": '\U0001D523', + "filig;": '\U0000FB01', + "flat;": '\U0000266D', + "fllig;": '\U0000FB02', + "fltns;": '\U000025B1', + "fnof;": '\U00000192', + "fopf;": '\U0001D557', + "forall;": '\U00002200', + "fork;": '\U000022D4', + "forkv;": '\U00002AD9', + "fpartint;": '\U00002A0D', + "frac12;": '\U000000BD', + "frac13;": '\U00002153', + "frac14;": '\U000000BC', + "frac15;": '\U00002155', + "frac16;": '\U00002159', + "frac18;": '\U0000215B', + "frac23;": '\U00002154', + "frac25;": '\U00002156', + "frac34;": '\U000000BE', + "frac35;": '\U00002157', + "frac38;": '\U0000215C', + "frac45;": '\U00002158', + "frac56;": '\U0000215A', + "frac58;": '\U0000215D', + "frac78;": '\U0000215E', + "frasl;": '\U00002044', + "frown;": '\U00002322', + "fscr;": '\U0001D4BB', + "gE;": '\U00002267', + "gEl;": '\U00002A8C', + "gacute;": '\U000001F5', + "gamma;": '\U000003B3', + "gammad;": '\U000003DD', + "gap;": '\U00002A86', + "gbreve;": '\U0000011F', + "gcirc;": '\U0000011D', + "gcy;": '\U00000433', + "gdot;": '\U00000121', + "ge;": '\U00002265', + "gel;": '\U000022DB', + "geq;": '\U00002265', + "geqq;": '\U00002267', + "geqslant;": '\U00002A7E', + "ges;": '\U00002A7E', + "gescc;": '\U00002AA9', + "gesdot;": '\U00002A80', + "gesdoto;": '\U00002A82', + "gesdotol;": '\U00002A84', + "gesles;": '\U00002A94', + "gfr;": '\U0001D524', + "gg;": '\U0000226B', + "ggg;": '\U000022D9', + "gimel;": '\U00002137', + "gjcy;": '\U00000453', + "gl;": '\U00002277', + "glE;": '\U00002A92', + "gla;": '\U00002AA5', + "glj;": '\U00002AA4', + "gnE;": '\U00002269', + "gnap;": '\U00002A8A', + "gnapprox;": '\U00002A8A', + "gne;": '\U00002A88', + "gneq;": '\U00002A88', + "gneqq;": '\U00002269', + "gnsim;": '\U000022E7', + "gopf;": '\U0001D558', + "grave;": '\U00000060', + "gscr;": '\U0000210A', + "gsim;": '\U00002273', + "gsime;": '\U00002A8E', + "gsiml;": '\U00002A90', + "gt;": '\U0000003E', + "gtcc;": '\U00002AA7', + "gtcir;": '\U00002A7A', + "gtdot;": '\U000022D7', + "gtlPar;": '\U00002995', + "gtquest;": '\U00002A7C', + "gtrapprox;": '\U00002A86', + "gtrarr;": '\U00002978', + "gtrdot;": '\U000022D7', + "gtreqless;": '\U000022DB', + "gtreqqless;": '\U00002A8C', + "gtrless;": '\U00002277', + "gtrsim;": '\U00002273', + "hArr;": '\U000021D4', + "hairsp;": '\U0000200A', + "half;": '\U000000BD', + "hamilt;": '\U0000210B', + "hardcy;": '\U0000044A', + "harr;": '\U00002194', + "harrcir;": '\U00002948', + "harrw;": '\U000021AD', + "hbar;": '\U0000210F', + "hcirc;": '\U00000125', + "hearts;": '\U00002665', + "heartsuit;": '\U00002665', + "hellip;": '\U00002026', + "hercon;": '\U000022B9', + "hfr;": '\U0001D525', + "hksearow;": '\U00002925', + "hkswarow;": '\U00002926', + "hoarr;": '\U000021FF', + "homtht;": '\U0000223B', + "hookleftarrow;": '\U000021A9', + "hookrightarrow;": '\U000021AA', + "hopf;": '\U0001D559', + "horbar;": '\U00002015', + "hscr;": '\U0001D4BD', + "hslash;": '\U0000210F', + "hstrok;": '\U00000127', + "hybull;": '\U00002043', + "hyphen;": '\U00002010', + "iacute;": '\U000000ED', + "ic;": '\U00002063', + "icirc;": '\U000000EE', + "icy;": '\U00000438', + "iecy;": '\U00000435', + "iexcl;": '\U000000A1', + "iff;": '\U000021D4', + "ifr;": '\U0001D526', + "igrave;": '\U000000EC', + "ii;": '\U00002148', + "iiiint;": '\U00002A0C', + "iiint;": '\U0000222D', + "iinfin;": '\U000029DC', + "iiota;": '\U00002129', + "ijlig;": '\U00000133', + "imacr;": '\U0000012B', + "image;": '\U00002111', + "imagline;": '\U00002110', + "imagpart;": '\U00002111', + "imath;": '\U00000131', + "imof;": '\U000022B7', + "imped;": '\U000001B5', + "in;": '\U00002208', + "incare;": '\U00002105', + "infin;": '\U0000221E', + "infintie;": '\U000029DD', + "inodot;": '\U00000131', + "int;": '\U0000222B', + "intcal;": '\U000022BA', + "integers;": '\U00002124', + "intercal;": '\U000022BA', + "intlarhk;": '\U00002A17', + "intprod;": '\U00002A3C', + "iocy;": '\U00000451', + "iogon;": '\U0000012F', + "iopf;": '\U0001D55A', + "iota;": '\U000003B9', + "iprod;": '\U00002A3C', + "iquest;": '\U000000BF', + "iscr;": '\U0001D4BE', + "isin;": '\U00002208', + "isinE;": '\U000022F9', + "isindot;": '\U000022F5', + "isins;": '\U000022F4', + "isinsv;": '\U000022F3', + "isinv;": '\U00002208', + "it;": '\U00002062', + "itilde;": '\U00000129', + "iukcy;": '\U00000456', + "iuml;": '\U000000EF', + "jcirc;": '\U00000135', + "jcy;": '\U00000439', + "jfr;": '\U0001D527', + "jmath;": '\U00000237', + "jopf;": '\U0001D55B', + "jscr;": '\U0001D4BF', + "jsercy;": '\U00000458', + "jukcy;": '\U00000454', + "kappa;": '\U000003BA', + "kappav;": '\U000003F0', + "kcedil;": '\U00000137', + "kcy;": '\U0000043A', + "kfr;": '\U0001D528', + "kgreen;": '\U00000138', + "khcy;": '\U00000445', + "kjcy;": '\U0000045C', + "kopf;": '\U0001D55C', + "kscr;": '\U0001D4C0', + "lAarr;": '\U000021DA', + "lArr;": '\U000021D0', + "lAtail;": '\U0000291B', + "lBarr;": '\U0000290E', + "lE;": '\U00002266', + "lEg;": '\U00002A8B', + "lHar;": '\U00002962', + "lacute;": '\U0000013A', + "laemptyv;": '\U000029B4', + "lagran;": '\U00002112', + "lambda;": '\U000003BB', + "lang;": '\U000027E8', + "langd;": '\U00002991', + "langle;": '\U000027E8', + "lap;": '\U00002A85', + "laquo;": '\U000000AB', + "larr;": '\U00002190', + "larrb;": '\U000021E4', + "larrbfs;": '\U0000291F', + "larrfs;": '\U0000291D', + "larrhk;": '\U000021A9', + "larrlp;": '\U000021AB', + "larrpl;": '\U00002939', + "larrsim;": '\U00002973', + "larrtl;": '\U000021A2', + "lat;": '\U00002AAB', + "latail;": '\U00002919', + "late;": '\U00002AAD', + "lbarr;": '\U0000290C', + "lbbrk;": '\U00002772', + "lbrace;": '\U0000007B', + "lbrack;": '\U0000005B', + "lbrke;": '\U0000298B', + "lbrksld;": '\U0000298F', + "lbrkslu;": '\U0000298D', + "lcaron;": '\U0000013E', + "lcedil;": '\U0000013C', + "lceil;": '\U00002308', + "lcub;": '\U0000007B', + "lcy;": '\U0000043B', + "ldca;": '\U00002936', + "ldquo;": '\U0000201C', + "ldquor;": '\U0000201E', + "ldrdhar;": '\U00002967', + "ldrushar;": '\U0000294B', + "ldsh;": '\U000021B2', + "le;": '\U00002264', + "leftarrow;": '\U00002190', + "leftarrowtail;": '\U000021A2', + "leftharpoondown;": '\U000021BD', + "leftharpoonup;": '\U000021BC', + "leftleftarrows;": '\U000021C7', + "leftrightarrow;": '\U00002194', + "leftrightarrows;": '\U000021C6', + "leftrightharpoons;": '\U000021CB', + "leftrightsquigarrow;": '\U000021AD', + "leftthreetimes;": '\U000022CB', + "leg;": '\U000022DA', + "leq;": '\U00002264', + "leqq;": '\U00002266', + "leqslant;": '\U00002A7D', + "les;": '\U00002A7D', + "lescc;": '\U00002AA8', + "lesdot;": '\U00002A7F', + "lesdoto;": '\U00002A81', + "lesdotor;": '\U00002A83', + "lesges;": '\U00002A93', + "lessapprox;": '\U00002A85', + "lessdot;": '\U000022D6', + "lesseqgtr;": '\U000022DA', + "lesseqqgtr;": '\U00002A8B', + "lessgtr;": '\U00002276', + "lesssim;": '\U00002272', + "lfisht;": '\U0000297C', + "lfloor;": '\U0000230A', + "lfr;": '\U0001D529', + "lg;": '\U00002276', + "lgE;": '\U00002A91', + "lhard;": '\U000021BD', + "lharu;": '\U000021BC', + "lharul;": '\U0000296A', + "lhblk;": '\U00002584', + "ljcy;": '\U00000459', + "ll;": '\U0000226A', + "llarr;": '\U000021C7', + "llcorner;": '\U0000231E', + "llhard;": '\U0000296B', + "lltri;": '\U000025FA', + "lmidot;": '\U00000140', + "lmoust;": '\U000023B0', + "lmoustache;": '\U000023B0', + "lnE;": '\U00002268', + "lnap;": '\U00002A89', + "lnapprox;": '\U00002A89', + "lne;": '\U00002A87', + "lneq;": '\U00002A87', + "lneqq;": '\U00002268', + "lnsim;": '\U000022E6', + "loang;": '\U000027EC', + "loarr;": '\U000021FD', + "lobrk;": '\U000027E6', + "longleftarrow;": '\U000027F5', + "longleftrightarrow;": '\U000027F7', + "longmapsto;": '\U000027FC', + "longrightarrow;": '\U000027F6', + "looparrowleft;": '\U000021AB', + "looparrowright;": '\U000021AC', + "lopar;": '\U00002985', + "lopf;": '\U0001D55D', + "loplus;": '\U00002A2D', + "lotimes;": '\U00002A34', + "lowast;": '\U00002217', + "lowbar;": '\U0000005F', + "loz;": '\U000025CA', + "lozenge;": '\U000025CA', + "lozf;": '\U000029EB', + "lpar;": '\U00000028', + "lparlt;": '\U00002993', + "lrarr;": '\U000021C6', + "lrcorner;": '\U0000231F', + "lrhar;": '\U000021CB', + "lrhard;": '\U0000296D', + "lrm;": '\U0000200E', + "lrtri;": '\U000022BF', + "lsaquo;": '\U00002039', + "lscr;": '\U0001D4C1', + "lsh;": '\U000021B0', + "lsim;": '\U00002272', + "lsime;": '\U00002A8D', + "lsimg;": '\U00002A8F', + "lsqb;": '\U0000005B', + "lsquo;": '\U00002018', + "lsquor;": '\U0000201A', + "lstrok;": '\U00000142', + "lt;": '\U0000003C', + "ltcc;": '\U00002AA6', + "ltcir;": '\U00002A79', + "ltdot;": '\U000022D6', + "lthree;": '\U000022CB', + "ltimes;": '\U000022C9', + "ltlarr;": '\U00002976', + "ltquest;": '\U00002A7B', + "ltrPar;": '\U00002996', + "ltri;": '\U000025C3', + "ltrie;": '\U000022B4', + "ltrif;": '\U000025C2', + "lurdshar;": '\U0000294A', + "luruhar;": '\U00002966', + "mDDot;": '\U0000223A', + "macr;": '\U000000AF', + "male;": '\U00002642', + "malt;": '\U00002720', + "maltese;": '\U00002720', + "map;": '\U000021A6', + "mapsto;": '\U000021A6', + "mapstodown;": '\U000021A7', + "mapstoleft;": '\U000021A4', + "mapstoup;": '\U000021A5', + "marker;": '\U000025AE', + "mcomma;": '\U00002A29', + "mcy;": '\U0000043C', + "mdash;": '\U00002014', + "measuredangle;": '\U00002221', + "mfr;": '\U0001D52A', + "mho;": '\U00002127', + "micro;": '\U000000B5', + "mid;": '\U00002223', + "midast;": '\U0000002A', + "midcir;": '\U00002AF0', + "middot;": '\U000000B7', + "minus;": '\U00002212', + "minusb;": '\U0000229F', + "minusd;": '\U00002238', + "minusdu;": '\U00002A2A', + "mlcp;": '\U00002ADB', + "mldr;": '\U00002026', + "mnplus;": '\U00002213', + "models;": '\U000022A7', + "mopf;": '\U0001D55E', + "mp;": '\U00002213', + "mscr;": '\U0001D4C2', + "mstpos;": '\U0000223E', + "mu;": '\U000003BC', + "multimap;": '\U000022B8', + "mumap;": '\U000022B8', + "nLeftarrow;": '\U000021CD', + "nLeftrightarrow;": '\U000021CE', + "nRightarrow;": '\U000021CF', + "nVDash;": '\U000022AF', + "nVdash;": '\U000022AE', + "nabla;": '\U00002207', + "nacute;": '\U00000144', + "nap;": '\U00002249', + "napos;": '\U00000149', + "napprox;": '\U00002249', + "natur;": '\U0000266E', + "natural;": '\U0000266E', + "naturals;": '\U00002115', + "nbsp;": '\U000000A0', + "ncap;": '\U00002A43', + "ncaron;": '\U00000148', + "ncedil;": '\U00000146', + "ncong;": '\U00002247', + "ncup;": '\U00002A42', + "ncy;": '\U0000043D', + "ndash;": '\U00002013', + "ne;": '\U00002260', + "neArr;": '\U000021D7', + "nearhk;": '\U00002924', + "nearr;": '\U00002197', + "nearrow;": '\U00002197', + "nequiv;": '\U00002262', + "nesear;": '\U00002928', + "nexist;": '\U00002204', + "nexists;": '\U00002204', + "nfr;": '\U0001D52B', + "nge;": '\U00002271', + "ngeq;": '\U00002271', + "ngsim;": '\U00002275', + "ngt;": '\U0000226F', + "ngtr;": '\U0000226F', + "nhArr;": '\U000021CE', + "nharr;": '\U000021AE', + "nhpar;": '\U00002AF2', + "ni;": '\U0000220B', + "nis;": '\U000022FC', + "nisd;": '\U000022FA', + "niv;": '\U0000220B', + "njcy;": '\U0000045A', + "nlArr;": '\U000021CD', + "nlarr;": '\U0000219A', + "nldr;": '\U00002025', + "nle;": '\U00002270', + "nleftarrow;": '\U0000219A', + "nleftrightarrow;": '\U000021AE', + "nleq;": '\U00002270', + "nless;": '\U0000226E', + "nlsim;": '\U00002274', + "nlt;": '\U0000226E', + "nltri;": '\U000022EA', + "nltrie;": '\U000022EC', + "nmid;": '\U00002224', + "nopf;": '\U0001D55F', + "not;": '\U000000AC', + "notin;": '\U00002209', + "notinva;": '\U00002209', + "notinvb;": '\U000022F7', + "notinvc;": '\U000022F6', + "notni;": '\U0000220C', + "notniva;": '\U0000220C', + "notnivb;": '\U000022FE', + "notnivc;": '\U000022FD', + "npar;": '\U00002226', + "nparallel;": '\U00002226', + "npolint;": '\U00002A14', + "npr;": '\U00002280', + "nprcue;": '\U000022E0', + "nprec;": '\U00002280', + "nrArr;": '\U000021CF', + "nrarr;": '\U0000219B', + "nrightarrow;": '\U0000219B', + "nrtri;": '\U000022EB', + "nrtrie;": '\U000022ED', + "nsc;": '\U00002281', + "nsccue;": '\U000022E1', + "nscr;": '\U0001D4C3', + "nshortmid;": '\U00002224', + "nshortparallel;": '\U00002226', + "nsim;": '\U00002241', + "nsime;": '\U00002244', + "nsimeq;": '\U00002244', + "nsmid;": '\U00002224', + "nspar;": '\U00002226', + "nsqsube;": '\U000022E2', + "nsqsupe;": '\U000022E3', + "nsub;": '\U00002284', + "nsube;": '\U00002288', + "nsubseteq;": '\U00002288', + "nsucc;": '\U00002281', + "nsup;": '\U00002285', + "nsupe;": '\U00002289', + "nsupseteq;": '\U00002289', + "ntgl;": '\U00002279', + "ntilde;": '\U000000F1', + "ntlg;": '\U00002278', + "ntriangleleft;": '\U000022EA', + "ntrianglelefteq;": '\U000022EC', + "ntriangleright;": '\U000022EB', + "ntrianglerighteq;": '\U000022ED', + "nu;": '\U000003BD', + "num;": '\U00000023', + "numero;": '\U00002116', + "numsp;": '\U00002007', + "nvDash;": '\U000022AD', + "nvHarr;": '\U00002904', + "nvdash;": '\U000022AC', + "nvinfin;": '\U000029DE', + "nvlArr;": '\U00002902', + "nvrArr;": '\U00002903', + "nwArr;": '\U000021D6', + "nwarhk;": '\U00002923', + "nwarr;": '\U00002196', + "nwarrow;": '\U00002196', + "nwnear;": '\U00002927', + "oS;": '\U000024C8', + "oacute;": '\U000000F3', + "oast;": '\U0000229B', + "ocir;": '\U0000229A', + "ocirc;": '\U000000F4', + "ocy;": '\U0000043E', + "odash;": '\U0000229D', + "odblac;": '\U00000151', + "odiv;": '\U00002A38', + "odot;": '\U00002299', + "odsold;": '\U000029BC', + "oelig;": '\U00000153', + "ofcir;": '\U000029BF', + "ofr;": '\U0001D52C', + "ogon;": '\U000002DB', + "ograve;": '\U000000F2', + "ogt;": '\U000029C1', + "ohbar;": '\U000029B5', + "ohm;": '\U000003A9', + "oint;": '\U0000222E', + "olarr;": '\U000021BA', + "olcir;": '\U000029BE', + "olcross;": '\U000029BB', + "oline;": '\U0000203E', + "olt;": '\U000029C0', + "omacr;": '\U0000014D', + "omega;": '\U000003C9', + "omicron;": '\U000003BF', + "omid;": '\U000029B6', + "ominus;": '\U00002296', + "oopf;": '\U0001D560', + "opar;": '\U000029B7', + "operp;": '\U000029B9', + "oplus;": '\U00002295', + "or;": '\U00002228', + "orarr;": '\U000021BB', + "ord;": '\U00002A5D', + "order;": '\U00002134', + "orderof;": '\U00002134', + "ordf;": '\U000000AA', + "ordm;": '\U000000BA', + "origof;": '\U000022B6', + "oror;": '\U00002A56', + "orslope;": '\U00002A57', + "orv;": '\U00002A5B', + "oscr;": '\U00002134', + "oslash;": '\U000000F8', + "osol;": '\U00002298', + "otilde;": '\U000000F5', + "otimes;": '\U00002297', + "otimesas;": '\U00002A36', + "ouml;": '\U000000F6', + "ovbar;": '\U0000233D', + "par;": '\U00002225', + "para;": '\U000000B6', + "parallel;": '\U00002225', + "parsim;": '\U00002AF3', + "parsl;": '\U00002AFD', + "part;": '\U00002202', + "pcy;": '\U0000043F', + "percnt;": '\U00000025', + "period;": '\U0000002E', + "permil;": '\U00002030', + "perp;": '\U000022A5', + "pertenk;": '\U00002031', + "pfr;": '\U0001D52D', + "phi;": '\U000003C6', + "phiv;": '\U000003D5', + "phmmat;": '\U00002133', + "phone;": '\U0000260E', + "pi;": '\U000003C0', + "pitchfork;": '\U000022D4', + "piv;": '\U000003D6', + "planck;": '\U0000210F', + "planckh;": '\U0000210E', + "plankv;": '\U0000210F', + "plus;": '\U0000002B', + "plusacir;": '\U00002A23', + "plusb;": '\U0000229E', + "pluscir;": '\U00002A22', + "plusdo;": '\U00002214', + "plusdu;": '\U00002A25', + "pluse;": '\U00002A72', + "plusmn;": '\U000000B1', + "plussim;": '\U00002A26', + "plustwo;": '\U00002A27', + "pm;": '\U000000B1', + "pointint;": '\U00002A15', + "popf;": '\U0001D561', + "pound;": '\U000000A3', + "pr;": '\U0000227A', + "prE;": '\U00002AB3', + "prap;": '\U00002AB7', + "prcue;": '\U0000227C', + "pre;": '\U00002AAF', + "prec;": '\U0000227A', + "precapprox;": '\U00002AB7', + "preccurlyeq;": '\U0000227C', + "preceq;": '\U00002AAF', + "precnapprox;": '\U00002AB9', + "precneqq;": '\U00002AB5', + "precnsim;": '\U000022E8', + "precsim;": '\U0000227E', + "prime;": '\U00002032', + "primes;": '\U00002119', + "prnE;": '\U00002AB5', + "prnap;": '\U00002AB9', + "prnsim;": '\U000022E8', + "prod;": '\U0000220F', + "profalar;": '\U0000232E', + "profline;": '\U00002312', + "profsurf;": '\U00002313', + "prop;": '\U0000221D', + "propto;": '\U0000221D', + "prsim;": '\U0000227E', + "prurel;": '\U000022B0', + "pscr;": '\U0001D4C5', + "psi;": '\U000003C8', + "puncsp;": '\U00002008', + "qfr;": '\U0001D52E', + "qint;": '\U00002A0C', + "qopf;": '\U0001D562', + "qprime;": '\U00002057', + "qscr;": '\U0001D4C6', + "quaternions;": '\U0000210D', + "quatint;": '\U00002A16', + "quest;": '\U0000003F', + "questeq;": '\U0000225F', + "quot;": '\U00000022', + "rAarr;": '\U000021DB', + "rArr;": '\U000021D2', + "rAtail;": '\U0000291C', + "rBarr;": '\U0000290F', + "rHar;": '\U00002964', + "racute;": '\U00000155', + "radic;": '\U0000221A', + "raemptyv;": '\U000029B3', + "rang;": '\U000027E9', + "rangd;": '\U00002992', + "range;": '\U000029A5', + "rangle;": '\U000027E9', + "raquo;": '\U000000BB', + "rarr;": '\U00002192', + "rarrap;": '\U00002975', + "rarrb;": '\U000021E5', + "rarrbfs;": '\U00002920', + "rarrc;": '\U00002933', + "rarrfs;": '\U0000291E', + "rarrhk;": '\U000021AA', + "rarrlp;": '\U000021AC', + "rarrpl;": '\U00002945', + "rarrsim;": '\U00002974', + "rarrtl;": '\U000021A3', + "rarrw;": '\U0000219D', + "ratail;": '\U0000291A', + "ratio;": '\U00002236', + "rationals;": '\U0000211A', + "rbarr;": '\U0000290D', + "rbbrk;": '\U00002773', + "rbrace;": '\U0000007D', + "rbrack;": '\U0000005D', + "rbrke;": '\U0000298C', + "rbrksld;": '\U0000298E', + "rbrkslu;": '\U00002990', + "rcaron;": '\U00000159', + "rcedil;": '\U00000157', + "rceil;": '\U00002309', + "rcub;": '\U0000007D', + "rcy;": '\U00000440', + "rdca;": '\U00002937', + "rdldhar;": '\U00002969', + "rdquo;": '\U0000201D', + "rdquor;": '\U0000201D', + "rdsh;": '\U000021B3', + "real;": '\U0000211C', + "realine;": '\U0000211B', + "realpart;": '\U0000211C', + "reals;": '\U0000211D', + "rect;": '\U000025AD', + "reg;": '\U000000AE', + "rfisht;": '\U0000297D', + "rfloor;": '\U0000230B', + "rfr;": '\U0001D52F', + "rhard;": '\U000021C1', + "rharu;": '\U000021C0', + "rharul;": '\U0000296C', + "rho;": '\U000003C1', + "rhov;": '\U000003F1', + "rightarrow;": '\U00002192', + "rightarrowtail;": '\U000021A3', + "rightharpoondown;": '\U000021C1', + "rightharpoonup;": '\U000021C0', + "rightleftarrows;": '\U000021C4', + "rightleftharpoons;": '\U000021CC', + "rightrightarrows;": '\U000021C9', + "rightsquigarrow;": '\U0000219D', + "rightthreetimes;": '\U000022CC', + "ring;": '\U000002DA', + "risingdotseq;": '\U00002253', + "rlarr;": '\U000021C4', + "rlhar;": '\U000021CC', + "rlm;": '\U0000200F', + "rmoust;": '\U000023B1', + "rmoustache;": '\U000023B1', + "rnmid;": '\U00002AEE', + "roang;": '\U000027ED', + "roarr;": '\U000021FE', + "robrk;": '\U000027E7', + "ropar;": '\U00002986', + "ropf;": '\U0001D563', + "roplus;": '\U00002A2E', + "rotimes;": '\U00002A35', + "rpar;": '\U00000029', + "rpargt;": '\U00002994', + "rppolint;": '\U00002A12', + "rrarr;": '\U000021C9', + "rsaquo;": '\U0000203A', + "rscr;": '\U0001D4C7', + "rsh;": '\U000021B1', + "rsqb;": '\U0000005D', + "rsquo;": '\U00002019', + "rsquor;": '\U00002019', + "rthree;": '\U000022CC', + "rtimes;": '\U000022CA', + "rtri;": '\U000025B9', + "rtrie;": '\U000022B5', + "rtrif;": '\U000025B8', + "rtriltri;": '\U000029CE', + "ruluhar;": '\U00002968', + "rx;": '\U0000211E', + "sacute;": '\U0000015B', + "sbquo;": '\U0000201A', + "sc;": '\U0000227B', + "scE;": '\U00002AB4', + "scap;": '\U00002AB8', + "scaron;": '\U00000161', + "sccue;": '\U0000227D', + "sce;": '\U00002AB0', + "scedil;": '\U0000015F', + "scirc;": '\U0000015D', + "scnE;": '\U00002AB6', + "scnap;": '\U00002ABA', + "scnsim;": '\U000022E9', + "scpolint;": '\U00002A13', + "scsim;": '\U0000227F', + "scy;": '\U00000441', + "sdot;": '\U000022C5', + "sdotb;": '\U000022A1', + "sdote;": '\U00002A66', + "seArr;": '\U000021D8', + "searhk;": '\U00002925', + "searr;": '\U00002198', + "searrow;": '\U00002198', + "sect;": '\U000000A7', + "semi;": '\U0000003B', + "seswar;": '\U00002929', + "setminus;": '\U00002216', + "setmn;": '\U00002216', + "sext;": '\U00002736', + "sfr;": '\U0001D530', + "sfrown;": '\U00002322', + "sharp;": '\U0000266F', + "shchcy;": '\U00000449', + "shcy;": '\U00000448', + "shortmid;": '\U00002223', + "shortparallel;": '\U00002225', + "shy;": '\U000000AD', + "sigma;": '\U000003C3', + "sigmaf;": '\U000003C2', + "sigmav;": '\U000003C2', + "sim;": '\U0000223C', + "simdot;": '\U00002A6A', + "sime;": '\U00002243', + "simeq;": '\U00002243', + "simg;": '\U00002A9E', + "simgE;": '\U00002AA0', + "siml;": '\U00002A9D', + "simlE;": '\U00002A9F', + "simne;": '\U00002246', + "simplus;": '\U00002A24', + "simrarr;": '\U00002972', + "slarr;": '\U00002190', + "smallsetminus;": '\U00002216', + "smashp;": '\U00002A33', + "smeparsl;": '\U000029E4', + "smid;": '\U00002223', + "smile;": '\U00002323', + "smt;": '\U00002AAA', + "smte;": '\U00002AAC', + "softcy;": '\U0000044C', + "sol;": '\U0000002F', + "solb;": '\U000029C4', + "solbar;": '\U0000233F', + "sopf;": '\U0001D564', + "spades;": '\U00002660', + "spadesuit;": '\U00002660', + "spar;": '\U00002225', + "sqcap;": '\U00002293', + "sqcup;": '\U00002294', + "sqsub;": '\U0000228F', + "sqsube;": '\U00002291', + "sqsubset;": '\U0000228F', + "sqsubseteq;": '\U00002291', + "sqsup;": '\U00002290', + "sqsupe;": '\U00002292', + "sqsupset;": '\U00002290', + "sqsupseteq;": '\U00002292', + "squ;": '\U000025A1', + "square;": '\U000025A1', + "squarf;": '\U000025AA', + "squf;": '\U000025AA', + "srarr;": '\U00002192', + "sscr;": '\U0001D4C8', + "ssetmn;": '\U00002216', + "ssmile;": '\U00002323', + "sstarf;": '\U000022C6', + "star;": '\U00002606', + "starf;": '\U00002605', + "straightepsilon;": '\U000003F5', + "straightphi;": '\U000003D5', + "strns;": '\U000000AF', + "sub;": '\U00002282', + "subE;": '\U00002AC5', + "subdot;": '\U00002ABD', + "sube;": '\U00002286', + "subedot;": '\U00002AC3', + "submult;": '\U00002AC1', + "subnE;": '\U00002ACB', + "subne;": '\U0000228A', + "subplus;": '\U00002ABF', + "subrarr;": '\U00002979', + "subset;": '\U00002282', + "subseteq;": '\U00002286', + "subseteqq;": '\U00002AC5', + "subsetneq;": '\U0000228A', + "subsetneqq;": '\U00002ACB', + "subsim;": '\U00002AC7', + "subsub;": '\U00002AD5', + "subsup;": '\U00002AD3', + "succ;": '\U0000227B', + "succapprox;": '\U00002AB8', + "succcurlyeq;": '\U0000227D', + "succeq;": '\U00002AB0', + "succnapprox;": '\U00002ABA', + "succneqq;": '\U00002AB6', + "succnsim;": '\U000022E9', + "succsim;": '\U0000227F', + "sum;": '\U00002211', + "sung;": '\U0000266A', + "sup;": '\U00002283', + "sup1;": '\U000000B9', + "sup2;": '\U000000B2', + "sup3;": '\U000000B3', + "supE;": '\U00002AC6', + "supdot;": '\U00002ABE', + "supdsub;": '\U00002AD8', + "supe;": '\U00002287', + "supedot;": '\U00002AC4', + "suphsol;": '\U000027C9', + "suphsub;": '\U00002AD7', + "suplarr;": '\U0000297B', + "supmult;": '\U00002AC2', + "supnE;": '\U00002ACC', + "supne;": '\U0000228B', + "supplus;": '\U00002AC0', + "supset;": '\U00002283', + "supseteq;": '\U00002287', + "supseteqq;": '\U00002AC6', + "supsetneq;": '\U0000228B', + "supsetneqq;": '\U00002ACC', + "supsim;": '\U00002AC8', + "supsub;": '\U00002AD4', + "supsup;": '\U00002AD6', + "swArr;": '\U000021D9', + "swarhk;": '\U00002926', + "swarr;": '\U00002199', + "swarrow;": '\U00002199', + "swnwar;": '\U0000292A', + "szlig;": '\U000000DF', + "target;": '\U00002316', + "tau;": '\U000003C4', + "tbrk;": '\U000023B4', + "tcaron;": '\U00000165', + "tcedil;": '\U00000163', + "tcy;": '\U00000442', + "tdot;": '\U000020DB', + "telrec;": '\U00002315', + "tfr;": '\U0001D531', + "there4;": '\U00002234', + "therefore;": '\U00002234', + "theta;": '\U000003B8', + "thetasym;": '\U000003D1', + "thetav;": '\U000003D1', + "thickapprox;": '\U00002248', + "thicksim;": '\U0000223C', + "thinsp;": '\U00002009', + "thkap;": '\U00002248', + "thksim;": '\U0000223C', + "thorn;": '\U000000FE', + "tilde;": '\U000002DC', + "times;": '\U000000D7', + "timesb;": '\U000022A0', + "timesbar;": '\U00002A31', + "timesd;": '\U00002A30', + "tint;": '\U0000222D', + "toea;": '\U00002928', + "top;": '\U000022A4', + "topbot;": '\U00002336', + "topcir;": '\U00002AF1', + "topf;": '\U0001D565', + "topfork;": '\U00002ADA', + "tosa;": '\U00002929', + "tprime;": '\U00002034', + "trade;": '\U00002122', + "triangle;": '\U000025B5', + "triangledown;": '\U000025BF', + "triangleleft;": '\U000025C3', + "trianglelefteq;": '\U000022B4', + "triangleq;": '\U0000225C', + "triangleright;": '\U000025B9', + "trianglerighteq;": '\U000022B5', + "tridot;": '\U000025EC', + "trie;": '\U0000225C', + "triminus;": '\U00002A3A', + "triplus;": '\U00002A39', + "trisb;": '\U000029CD', + "tritime;": '\U00002A3B', + "trpezium;": '\U000023E2', + "tscr;": '\U0001D4C9', + "tscy;": '\U00000446', + "tshcy;": '\U0000045B', + "tstrok;": '\U00000167', + "twixt;": '\U0000226C', + "twoheadleftarrow;": '\U0000219E', + "twoheadrightarrow;": '\U000021A0', + "uArr;": '\U000021D1', + "uHar;": '\U00002963', + "uacute;": '\U000000FA', + "uarr;": '\U00002191', + "ubrcy;": '\U0000045E', + "ubreve;": '\U0000016D', + "ucirc;": '\U000000FB', + "ucy;": '\U00000443', + "udarr;": '\U000021C5', + "udblac;": '\U00000171', + "udhar;": '\U0000296E', + "ufisht;": '\U0000297E', + "ufr;": '\U0001D532', + "ugrave;": '\U000000F9', + "uharl;": '\U000021BF', + "uharr;": '\U000021BE', + "uhblk;": '\U00002580', + "ulcorn;": '\U0000231C', + "ulcorner;": '\U0000231C', + "ulcrop;": '\U0000230F', + "ultri;": '\U000025F8', + "umacr;": '\U0000016B', + "uml;": '\U000000A8', + "uogon;": '\U00000173', + "uopf;": '\U0001D566', + "uparrow;": '\U00002191', + "updownarrow;": '\U00002195', + "upharpoonleft;": '\U000021BF', + "upharpoonright;": '\U000021BE', + "uplus;": '\U0000228E', + "upsi;": '\U000003C5', + "upsih;": '\U000003D2', + "upsilon;": '\U000003C5', + "upuparrows;": '\U000021C8', + "urcorn;": '\U0000231D', + "urcorner;": '\U0000231D', + "urcrop;": '\U0000230E', + "uring;": '\U0000016F', + "urtri;": '\U000025F9', + "uscr;": '\U0001D4CA', + "utdot;": '\U000022F0', + "utilde;": '\U00000169', + "utri;": '\U000025B5', + "utrif;": '\U000025B4', + "uuarr;": '\U000021C8', + "uuml;": '\U000000FC', + "uwangle;": '\U000029A7', + "vArr;": '\U000021D5', + "vBar;": '\U00002AE8', + "vBarv;": '\U00002AE9', + "vDash;": '\U000022A8', + "vangrt;": '\U0000299C', + "varepsilon;": '\U000003F5', + "varkappa;": '\U000003F0', + "varnothing;": '\U00002205', + "varphi;": '\U000003D5', + "varpi;": '\U000003D6', + "varpropto;": '\U0000221D', + "varr;": '\U00002195', + "varrho;": '\U000003F1', + "varsigma;": '\U000003C2', + "vartheta;": '\U000003D1', + "vartriangleleft;": '\U000022B2', + "vartriangleright;": '\U000022B3', + "vcy;": '\U00000432', + "vdash;": '\U000022A2', + "vee;": '\U00002228', + "veebar;": '\U000022BB', + "veeeq;": '\U0000225A', + "vellip;": '\U000022EE', + "verbar;": '\U0000007C', + "vert;": '\U0000007C', + "vfr;": '\U0001D533', + "vltri;": '\U000022B2', + "vopf;": '\U0001D567', + "vprop;": '\U0000221D', + "vrtri;": '\U000022B3', + "vscr;": '\U0001D4CB', + "vzigzag;": '\U0000299A', + "wcirc;": '\U00000175', + "wedbar;": '\U00002A5F', + "wedge;": '\U00002227', + "wedgeq;": '\U00002259', + "weierp;": '\U00002118', + "wfr;": '\U0001D534', + "wopf;": '\U0001D568', + "wp;": '\U00002118', + "wr;": '\U00002240', + "wreath;": '\U00002240', + "wscr;": '\U0001D4CC', + "xcap;": '\U000022C2', + "xcirc;": '\U000025EF', + "xcup;": '\U000022C3', + "xdtri;": '\U000025BD', + "xfr;": '\U0001D535', + "xhArr;": '\U000027FA', + "xharr;": '\U000027F7', + "xi;": '\U000003BE', + "xlArr;": '\U000027F8', + "xlarr;": '\U000027F5', + "xmap;": '\U000027FC', + "xnis;": '\U000022FB', + "xodot;": '\U00002A00', + "xopf;": '\U0001D569', + "xoplus;": '\U00002A01', + "xotime;": '\U00002A02', + "xrArr;": '\U000027F9', + "xrarr;": '\U000027F6', + "xscr;": '\U0001D4CD', + "xsqcup;": '\U00002A06', + "xuplus;": '\U00002A04', + "xutri;": '\U000025B3', + "xvee;": '\U000022C1', + "xwedge;": '\U000022C0', + "yacute;": '\U000000FD', + "yacy;": '\U0000044F', + "ycirc;": '\U00000177', + "ycy;": '\U0000044B', + "yen;": '\U000000A5', + "yfr;": '\U0001D536', + "yicy;": '\U00000457', + "yopf;": '\U0001D56A', + "yscr;": '\U0001D4CE', + "yucy;": '\U0000044E', + "yuml;": '\U000000FF', + "zacute;": '\U0000017A', + "zcaron;": '\U0000017E', + "zcy;": '\U00000437', + "zdot;": '\U0000017C', + "zeetrf;": '\U00002128', + "zeta;": '\U000003B6', + "zfr;": '\U0001D537', + "zhcy;": '\U00000436', + "zigrarr;": '\U000021DD', + "zopf;": '\U0001D56B', + "zscr;": '\U0001D4CF', + "zwj;": '\U0000200D', + "zwnj;": '\U0000200C', + "AElig": '\U000000C6', + "AMP": '\U00000026', + "Aacute": '\U000000C1', + "Acirc": '\U000000C2', + "Agrave": '\U000000C0', + "Aring": '\U000000C5', + "Atilde": '\U000000C3', + "Auml": '\U000000C4', + "COPY": '\U000000A9', + "Ccedil": '\U000000C7', + "ETH": '\U000000D0', + "Eacute": '\U000000C9', + "Ecirc": '\U000000CA', + "Egrave": '\U000000C8', + "Euml": '\U000000CB', + "GT": '\U0000003E', + "Iacute": '\U000000CD', + "Icirc": '\U000000CE', + "Igrave": '\U000000CC', + "Iuml": '\U000000CF', + "LT": '\U0000003C', + "Ntilde": '\U000000D1', + "Oacute": '\U000000D3', + "Ocirc": '\U000000D4', + "Ograve": '\U000000D2', + "Oslash": '\U000000D8', + "Otilde": '\U000000D5', + "Ouml": '\U000000D6', + "QUOT": '\U00000022', + "REG": '\U000000AE', + "THORN": '\U000000DE', + "Uacute": '\U000000DA', + "Ucirc": '\U000000DB', + "Ugrave": '\U000000D9', + "Uuml": '\U000000DC', + "Yacute": '\U000000DD', + "aacute": '\U000000E1', + "acirc": '\U000000E2', + "acute": '\U000000B4', + "aelig": '\U000000E6', + "agrave": '\U000000E0', + "amp": '\U00000026', + "aring": '\U000000E5', + "atilde": '\U000000E3', + "auml": '\U000000E4', + "brvbar": '\U000000A6', + "ccedil": '\U000000E7', + "cedil": '\U000000B8', + "cent": '\U000000A2', + "copy": '\U000000A9', + "curren": '\U000000A4', + "deg": '\U000000B0', + "divide": '\U000000F7', + "eacute": '\U000000E9', + "ecirc": '\U000000EA', + "egrave": '\U000000E8', + "eth": '\U000000F0', + "euml": '\U000000EB', + "frac12": '\U000000BD', + "frac14": '\U000000BC', + "frac34": '\U000000BE', + "gt": '\U0000003E', + "iacute": '\U000000ED', + "icirc": '\U000000EE', + "iexcl": '\U000000A1', + "igrave": '\U000000EC', + "iquest": '\U000000BF', + "iuml": '\U000000EF', + "laquo": '\U000000AB', + "lt": '\U0000003C', + "macr": '\U000000AF', + "micro": '\U000000B5', + "middot": '\U000000B7', + "nbsp": '\U000000A0', + "not": '\U000000AC', + "ntilde": '\U000000F1', + "oacute": '\U000000F3', + "ocirc": '\U000000F4', + "ograve": '\U000000F2', + "ordf": '\U000000AA', + "ordm": '\U000000BA', + "oslash": '\U000000F8', + "otilde": '\U000000F5', + "ouml": '\U000000F6', + "para": '\U000000B6', + "plusmn": '\U000000B1', + "pound": '\U000000A3', + "quot": '\U00000022', + "raquo": '\U000000BB', + "reg": '\U000000AE', + "sect": '\U000000A7', + "shy": '\U000000AD', + "sup1": '\U000000B9', + "sup2": '\U000000B2', + "sup3": '\U000000B3', + "szlig": '\U000000DF', + "thorn": '\U000000FE', + "times": '\U000000D7', + "uacute": '\U000000FA', + "ucirc": '\U000000FB', + "ugrave": '\U000000F9', + "uml": '\U000000A8', + "uuml": '\U000000FC', + "yacute": '\U000000FD', + "yen": '\U000000A5', + "yuml": '\U000000FF', +} + +// HTML entities that are two unicode codepoints. +var entity2 = map[string][2]rune{ + // TODO(nigeltao): Handle replacements that are wider than their names. + // "nLt;": {'\u226A', '\u20D2'}, + // "nGt;": {'\u226B', '\u20D2'}, + "NotEqualTilde;": {'\u2242', '\u0338'}, + "NotGreaterFullEqual;": {'\u2267', '\u0338'}, + "NotGreaterGreater;": {'\u226B', '\u0338'}, + "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, + "NotHumpDownHump;": {'\u224E', '\u0338'}, + "NotHumpEqual;": {'\u224F', '\u0338'}, + "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, + "NotLessLess;": {'\u226A', '\u0338'}, + "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, + "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, + "NotNestedLessLess;": {'\u2AA1', '\u0338'}, + "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, + "NotRightTriangleBar;": {'\u29D0', '\u0338'}, + "NotSquareSubset;": {'\u228F', '\u0338'}, + "NotSquareSuperset;": {'\u2290', '\u0338'}, + "NotSubset;": {'\u2282', '\u20D2'}, + "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, + "NotSucceedsTilde;": {'\u227F', '\u0338'}, + "NotSuperset;": {'\u2283', '\u20D2'}, + "ThickSpace;": {'\u205F', '\u200A'}, + "acE;": {'\u223E', '\u0333'}, + "bne;": {'\u003D', '\u20E5'}, + "bnequiv;": {'\u2261', '\u20E5'}, + "caps;": {'\u2229', '\uFE00'}, + "cups;": {'\u222A', '\uFE00'}, + "fjlig;": {'\u0066', '\u006A'}, + "gesl;": {'\u22DB', '\uFE00'}, + "gvertneqq;": {'\u2269', '\uFE00'}, + "gvnE;": {'\u2269', '\uFE00'}, + "lates;": {'\u2AAD', '\uFE00'}, + "lesg;": {'\u22DA', '\uFE00'}, + "lvertneqq;": {'\u2268', '\uFE00'}, + "lvnE;": {'\u2268', '\uFE00'}, + "nGg;": {'\u22D9', '\u0338'}, + "nGtv;": {'\u226B', '\u0338'}, + "nLl;": {'\u22D8', '\u0338'}, + "nLtv;": {'\u226A', '\u0338'}, + "nang;": {'\u2220', '\u20D2'}, + "napE;": {'\u2A70', '\u0338'}, + "napid;": {'\u224B', '\u0338'}, + "nbump;": {'\u224E', '\u0338'}, + "nbumpe;": {'\u224F', '\u0338'}, + "ncongdot;": {'\u2A6D', '\u0338'}, + "nedot;": {'\u2250', '\u0338'}, + "nesim;": {'\u2242', '\u0338'}, + "ngE;": {'\u2267', '\u0338'}, + "ngeqq;": {'\u2267', '\u0338'}, + "ngeqslant;": {'\u2A7E', '\u0338'}, + "nges;": {'\u2A7E', '\u0338'}, + "nlE;": {'\u2266', '\u0338'}, + "nleqq;": {'\u2266', '\u0338'}, + "nleqslant;": {'\u2A7D', '\u0338'}, + "nles;": {'\u2A7D', '\u0338'}, + "notinE;": {'\u22F9', '\u0338'}, + "notindot;": {'\u22F5', '\u0338'}, + "nparsl;": {'\u2AFD', '\u20E5'}, + "npart;": {'\u2202', '\u0338'}, + "npre;": {'\u2AAF', '\u0338'}, + "npreceq;": {'\u2AAF', '\u0338'}, + "nrarrc;": {'\u2933', '\u0338'}, + "nrarrw;": {'\u219D', '\u0338'}, + "nsce;": {'\u2AB0', '\u0338'}, + "nsubE;": {'\u2AC5', '\u0338'}, + "nsubset;": {'\u2282', '\u20D2'}, + "nsubseteqq;": {'\u2AC5', '\u0338'}, + "nsucceq;": {'\u2AB0', '\u0338'}, + "nsupE;": {'\u2AC6', '\u0338'}, + "nsupset;": {'\u2283', '\u20D2'}, + "nsupseteqq;": {'\u2AC6', '\u0338'}, + "nvap;": {'\u224D', '\u20D2'}, + "nvge;": {'\u2265', '\u20D2'}, + "nvgt;": {'\u003E', '\u20D2'}, + "nvle;": {'\u2264', '\u20D2'}, + "nvlt;": {'\u003C', '\u20D2'}, + "nvltrie;": {'\u22B4', '\u20D2'}, + "nvrtrie;": {'\u22B5', '\u20D2'}, + "nvsim;": {'\u223C', '\u20D2'}, + "race;": {'\u223D', '\u0331'}, + "smtes;": {'\u2AAC', '\uFE00'}, + "sqcaps;": {'\u2293', '\uFE00'}, + "sqcups;": {'\u2294', '\uFE00'}, + "varsubsetneq;": {'\u228A', '\uFE00'}, + "varsubsetneqq;": {'\u2ACB', '\uFE00'}, + "varsupsetneq;": {'\u228B', '\uFE00'}, + "varsupsetneqq;": {'\u2ACC', '\uFE00'}, + "vnsub;": {'\u2282', '\u20D2'}, + "vnsup;": {'\u2283', '\u20D2'}, + "vsubnE;": {'\u2ACB', '\uFE00'}, + "vsubne;": {'\u228A', '\uFE00'}, + "vsupnE;": {'\u2ACC', '\uFE00'}, + "vsupne;": {'\u228B', '\uFE00'}, +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/escape.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/escape.go new file mode 100644 index 0000000..d856139 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/escape.go @@ -0,0 +1,258 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bytes" + "strings" + "unicode/utf8" +) + +// These replacements permit compatibility with old numeric entities that +// assumed Windows-1252 encoding. +// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference +var replacementTable = [...]rune{ + '\u20AC', // First entry is what 0x80 should be replaced with. + '\u0081', + '\u201A', + '\u0192', + '\u201E', + '\u2026', + '\u2020', + '\u2021', + '\u02C6', + '\u2030', + '\u0160', + '\u2039', + '\u0152', + '\u008D', + '\u017D', + '\u008F', + '\u0090', + '\u2018', + '\u2019', + '\u201C', + '\u201D', + '\u2022', + '\u2013', + '\u2014', + '\u02DC', + '\u2122', + '\u0161', + '\u203A', + '\u0153', + '\u009D', + '\u017E', + '\u0178', // Last entry is 0x9F. + // 0x00->'\uFFFD' is handled programmatically. + // 0x0D->'\u000D' is a no-op. +} + +// unescapeEntity reads an entity like "<" from b[src:] and writes the +// corresponding "<" to b[dst:], returning the incremented dst and src cursors. +// Precondition: b[src] == '&' && dst <= src. +// attribute should be true if parsing an attribute value. +func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { + // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference + + // i starts at 1 because we already know that s[0] == '&'. + i, s := 1, b[src:] + + if len(s) <= 1 { + b[dst] = b[src] + return dst + 1, src + 1 + } + + if s[i] == '#' { + if len(s) <= 3 { // We need to have at least "&#.". + b[dst] = b[src] + return dst + 1, src + 1 + } + i++ + c := s[i] + hex := false + if c == 'x' || c == 'X' { + hex = true + i++ + } + + x := '\x00' + for i < len(s) { + c = s[i] + i++ + if hex { + if '0' <= c && c <= '9' { + x = 16*x + rune(c) - '0' + continue + } else if 'a' <= c && c <= 'f' { + x = 16*x + rune(c) - 'a' + 10 + continue + } else if 'A' <= c && c <= 'F' { + x = 16*x + rune(c) - 'A' + 10 + continue + } + } else if '0' <= c && c <= '9' { + x = 10*x + rune(c) - '0' + continue + } + if c != ';' { + i-- + } + break + } + + if i <= 3 { // No characters matched. + b[dst] = b[src] + return dst + 1, src + 1 + } + + if 0x80 <= x && x <= 0x9F { + // Replace characters from Windows-1252 with UTF-8 equivalents. + x = replacementTable[x-0x80] + } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { + // Replace invalid characters with the replacement character. + x = '\uFFFD' + } + + return dst + utf8.EncodeRune(b[dst:], x), src + i + } + + // Consume the maximum number of characters possible, with the + // consumed characters matching one of the named references. + + for i < len(s) { + c := s[i] + i++ + // Lower-cased characters are more common in entities, so we check for them first. + if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { + continue + } + if c != ';' { + i-- + } + break + } + + entityName := string(s[1:i]) + if entityName == "" { + // No-op. + } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { + // No-op. + } else if x := entity[entityName]; x != 0 { + return dst + utf8.EncodeRune(b[dst:], x), src + i + } else if x := entity2[entityName]; x[0] != 0 { + dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) + return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + } else if !attribute { + maxLen := len(entityName) - 1 + if maxLen > longestEntityWithoutSemicolon { + maxLen = longestEntityWithoutSemicolon + } + for j := maxLen; j > 1; j-- { + if x := entity[entityName[:j]]; x != 0 { + return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + } + } + } + + dst1, src1 = dst+i, src+i + copy(b[dst:dst1], b[src:src1]) + return dst1, src1 +} + +// unescape unescapes b's entities in-place, so that "a<b" becomes "a': + esc = ">" + case '"': + // """ is shorter than """. + esc = """ + case '\r': + esc = " " + default: + panic("unrecognized escape character") + } + s = s[i+1:] + if _, err := w.WriteString(esc); err != nil { + return err + } + i = strings.IndexAny(s, escapedChars) + } + _, err := w.WriteString(s) + return err +} + +// EscapeString escapes special characters like "<" to become "<". It +// escapes only five such characters: <, >, &, ' and ". +// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't +// always true. +func EscapeString(s string) string { + if strings.IndexAny(s, escapedChars) == -1 { + return s + } + var buf bytes.Buffer + escape(&buf, s) + return buf.String() +} + +// UnescapeString unescapes entities like "<" to become "<". It unescapes a +// larger range of entities than EscapeString escapes. For example, "á" +// unescapes to "á", as does "á" and "&xE1;". +// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't +// always true. +func UnescapeString(s string) string { + for _, c := range s { + if c == '&' { + return string(unescape([]byte(s), false)) + } + } + return s +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/foreign.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/foreign.go new file mode 100644 index 0000000..d3b3844 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/foreign.go @@ -0,0 +1,226 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "strings" +) + +func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { + for i := range aa { + if newName, ok := nameMap[aa[i].Key]; ok { + aa[i].Key = newName + } + } +} + +func adjustForeignAttributes(aa []Attribute) { + for i, a := range aa { + if a.Key == "" || a.Key[0] != 'x' { + continue + } + switch a.Key { + case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", + "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + j := strings.Index(a.Key, ":") + aa[i].Namespace = a.Key[:j] + aa[i].Key = a.Key[j+1:] + } + } +} + +func htmlIntegrationPoint(n *Node) bool { + if n.Type != ElementNode { + return false + } + switch n.Namespace { + case "math": + if n.Data == "annotation-xml" { + for _, a := range n.Attr { + if a.Key == "encoding" { + val := strings.ToLower(a.Val) + if val == "text/html" || val == "application/xhtml+xml" { + return true + } + } + } + } + case "svg": + switch n.Data { + case "desc", "foreignObject", "title": + return true + } + } + return false +} + +func mathMLTextIntegrationPoint(n *Node) bool { + if n.Namespace != "math" { + return false + } + switch n.Data { + case "mi", "mo", "mn", "ms", "mtext": + return true + } + return false +} + +// Section 12.2.5.5. +var breakout = map[string]bool{ + "b": true, + "big": true, + "blockquote": true, + "body": true, + "br": true, + "center": true, + "code": true, + "dd": true, + "div": true, + "dl": true, + "dt": true, + "em": true, + "embed": true, + "h1": true, + "h2": true, + "h3": true, + "h4": true, + "h5": true, + "h6": true, + "head": true, + "hr": true, + "i": true, + "img": true, + "li": true, + "listing": true, + "menu": true, + "meta": true, + "nobr": true, + "ol": true, + "p": true, + "pre": true, + "ruby": true, + "s": true, + "small": true, + "span": true, + "strong": true, + "strike": true, + "sub": true, + "sup": true, + "table": true, + "tt": true, + "u": true, + "ul": true, + "var": true, +} + +// Section 12.2.5.5. +var svgTagNameAdjustments = map[string]string{ + "altglyph": "altGlyph", + "altglyphdef": "altGlyphDef", + "altglyphitem": "altGlyphItem", + "animatecolor": "animateColor", + "animatemotion": "animateMotion", + "animatetransform": "animateTransform", + "clippath": "clipPath", + "feblend": "feBlend", + "fecolormatrix": "feColorMatrix", + "fecomponenttransfer": "feComponentTransfer", + "fecomposite": "feComposite", + "feconvolvematrix": "feConvolveMatrix", + "fediffuselighting": "feDiffuseLighting", + "fedisplacementmap": "feDisplacementMap", + "fedistantlight": "feDistantLight", + "feflood": "feFlood", + "fefunca": "feFuncA", + "fefuncb": "feFuncB", + "fefuncg": "feFuncG", + "fefuncr": "feFuncR", + "fegaussianblur": "feGaussianBlur", + "feimage": "feImage", + "femerge": "feMerge", + "femergenode": "feMergeNode", + "femorphology": "feMorphology", + "feoffset": "feOffset", + "fepointlight": "fePointLight", + "fespecularlighting": "feSpecularLighting", + "fespotlight": "feSpotLight", + "fetile": "feTile", + "feturbulence": "feTurbulence", + "foreignobject": "foreignObject", + "glyphref": "glyphRef", + "lineargradient": "linearGradient", + "radialgradient": "radialGradient", + "textpath": "textPath", +} + +// Section 12.2.5.1 +var mathMLAttributeAdjustments = map[string]string{ + "definitionurl": "definitionURL", +} + +var svgAttributeAdjustments = map[string]string{ + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan", +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/node.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/node.go new file mode 100644 index 0000000..26b657a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/node.go @@ -0,0 +1,193 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "golang.org/x/net/html/atom" +) + +// A NodeType is the type of a Node. +type NodeType uint32 + +const ( + ErrorNode NodeType = iota + TextNode + DocumentNode + ElementNode + CommentNode + DoctypeNode + scopeMarkerNode +) + +// Section 12.2.3.3 says "scope markers are inserted when entering applet +// elements, buttons, object elements, marquees, table cells, and table +// captions, and are used to prevent formatting from 'leaking'". +var scopeMarker = Node{Type: scopeMarkerNode} + +// A Node consists of a NodeType and some Data (tag name for element nodes, +// content for text) and are part of a tree of Nodes. Element nodes may also +// have a Namespace and contain a slice of Attributes. Data is unescaped, so +// that it looks like "a 0 { + return (*s)[i-1] + } + return nil +} + +// index returns the index of the top-most occurrence of n in the stack, or -1 +// if n is not present. +func (s *nodeStack) index(n *Node) int { + for i := len(*s) - 1; i >= 0; i-- { + if (*s)[i] == n { + return i + } + } + return -1 +} + +// insert inserts a node at the given index. +func (s *nodeStack) insert(i int, n *Node) { + (*s) = append(*s, nil) + copy((*s)[i+1:], (*s)[i:]) + (*s)[i] = n +} + +// remove removes a node from the stack. It is a no-op if n is not present. +func (s *nodeStack) remove(n *Node) { + i := s.index(n) + if i == -1 { + return + } + copy((*s)[i:], (*s)[i+1:]) + j := len(*s) - 1 + (*s)[j] = nil + *s = (*s)[:j] +} diff --git a/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/parse.go b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/parse.go new file mode 100644 index 0000000..be4b2bf --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/vendor/golang.org/x/net/html/parse.go @@ -0,0 +1,2094 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "errors" + "fmt" + "io" + "strings" + + a "golang.org/x/net/html/atom" +) + +// A parser implements the HTML5 parsing algorithm: +// https://html.spec.whatwg.org/multipage/syntax.html#tree-construction +type parser struct { + // tokenizer provides the tokens for the parser. + tokenizer *Tokenizer + // tok is the most recently read token. + tok Token + // Self-closing tags like
are treated as start tags, except that + // hasSelfClosingToken is set while they are being processed. + hasSelfClosingToken bool + // doc is the document root element. + doc *Node + // The stack of open elements (section 12.2.3.2) and active formatting + // elements (section 12.2.3.3). + oe, afe nodeStack + // Element pointers (section 12.2.3.4). + head, form *Node + // Other parsing state flags (section 12.2.3.5). + scripting, framesetOK bool + // im is the current insertion mode. + im insertionMode + // originalIM is the insertion mode to go back to after completing a text + // or inTableText insertion mode. + originalIM insertionMode + // fosterParenting is whether new elements should be inserted according to + // the foster parenting rules (section 12.2.5.3). + fosterParenting bool + // quirks is whether the parser is operating in "quirks mode." + quirks bool + // fragment is whether the parser is parsing an HTML fragment. + fragment bool + // context is the context element when parsing an HTML fragment + // (section 12.4). + context *Node +} + +func (p *parser) top() *Node { + if n := p.oe.top(); n != nil { + return n + } + return p.doc +} + +// Stop tags for use in popUntil. These come from section 12.2.3.2. +var ( + defaultScopeStopTags = map[string][]a.Atom{ + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, + "svg": {a.Desc, a.ForeignObject, a.Title}, + } +) + +type scope int + +const ( + defaultScope scope = iota + listItemScope + buttonScope + tableScope + tableRowScope + tableBodyScope + selectScope +) + +// popUntil pops the stack of open elements at the highest element whose tag +// is in matchTags, provided there is no higher element in the scope's stop +// tags (as defined in section 12.2.3.2). It returns whether or not there was +// such an element. If there was not, popUntil leaves the stack unchanged. +// +// For example, the set of stop tags for table scope is: "html", "table". If +// the stack was: +// ["html", "body", "font", "table", "b", "i", "u"] +// then popUntil(tableScope, "font") would return false, but +// popUntil(tableScope, "i") would return true and the stack would become: +// ["html", "body", "font", "table", "b"] +// +// If an element's tag is in both the stop tags and matchTags, then the stack +// will be popped and the function returns true (provided, of course, there was +// no higher element in the stack that was also in the stop tags). For example, +// popUntil(tableScope, "table") returns true and leaves: +// ["html", "body", "font"] +func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { + if i := p.indexOfElementInScope(s, matchTags...); i != -1 { + p.oe = p.oe[:i] + return true + } + return false +} + +// indexOfElementInScope returns the index in p.oe of the highest element whose +// tag is in matchTags that is in scope. If no matching element is in scope, it +// returns -1. +func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { + for i := len(p.oe) - 1; i >= 0; i-- { + tagAtom := p.oe[i].DataAtom + if p.oe[i].Namespace == "" { + for _, t := range matchTags { + if t == tagAtom { + return i + } + } + switch s { + case defaultScope: + // No-op. + case listItemScope: + if tagAtom == a.Ol || tagAtom == a.Ul { + return -1 + } + case buttonScope: + if tagAtom == a.Button { + return -1 + } + case tableScope: + if tagAtom == a.Html || tagAtom == a.Table { + return -1 + } + case selectScope: + if tagAtom != a.Optgroup && tagAtom != a.Option { + return -1 + } + default: + panic("unreachable") + } + } + switch s { + case defaultScope, listItemScope, buttonScope: + for _, t := range defaultScopeStopTags[p.oe[i].Namespace] { + if t == tagAtom { + return -1 + } + } + } + } + return -1 +} + +// elementInScope is like popUntil, except that it doesn't modify the stack of +// open elements. +func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool { + return p.indexOfElementInScope(s, matchTags...) != -1 +} + +// clearStackToContext pops elements off the stack of open elements until a +// scope-defined element is found. +func (p *parser) clearStackToContext(s scope) { + for i := len(p.oe) - 1; i >= 0; i-- { + tagAtom := p.oe[i].DataAtom + switch s { + case tableScope: + if tagAtom == a.Html || tagAtom == a.Table { + p.oe = p.oe[:i+1] + return + } + case tableRowScope: + if tagAtom == a.Html || tagAtom == a.Tr { + p.oe = p.oe[:i+1] + return + } + case tableBodyScope: + if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead { + p.oe = p.oe[:i+1] + return + } + default: + panic("unreachable") + } + } +} + +// generateImpliedEndTags pops nodes off the stack of open elements as long as +// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt. +// If exceptions are specified, nodes with that name will not be popped off. +func (p *parser) generateImpliedEndTags(exceptions ...string) { + var i int +loop: + for i = len(p.oe) - 1; i >= 0; i-- { + n := p.oe[i] + if n.Type == ElementNode { + switch n.DataAtom { + case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt: + for _, except := range exceptions { + if n.Data == except { + break loop + } + } + continue + } + } + break + } + + p.oe = p.oe[:i+1] +} + +// addChild adds a child node n to the top element, and pushes n onto the stack +// of open elements if it is an element node. +func (p *parser) addChild(n *Node) { + if p.shouldFosterParent() { + p.fosterParent(n) + } else { + p.top().AppendChild(n) + } + + if n.Type == ElementNode { + p.oe = append(p.oe, n) + } +} + +// shouldFosterParent returns whether the next node to be added should be +// foster parented. +func (p *parser) shouldFosterParent() bool { + if p.fosterParenting { + switch p.top().DataAtom { + case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: + return true + } + } + return false +} + +// fosterParent adds a child node according to the foster parenting rules. +// Section 12.2.5.3, "foster parenting". +func (p *parser) fosterParent(n *Node) { + var table, parent, prev *Node + var i int + for i = len(p.oe) - 1; i >= 0; i-- { + if p.oe[i].DataAtom == a.Table { + table = p.oe[i] + break + } + } + + if table == nil { + // The foster parent is the html element. + parent = p.oe[0] + } else { + parent = table.Parent + } + if parent == nil { + parent = p.oe[i-1] + } + + if table != nil { + prev = table.PrevSibling + } else { + prev = parent.LastChild + } + if prev != nil && prev.Type == TextNode && n.Type == TextNode { + prev.Data += n.Data + return + } + + parent.InsertBefore(n, table) +} + +// addText adds text to the preceding node if it is a text node, or else it +// calls addChild with a new text node. +func (p *parser) addText(text string) { + if text == "" { + return + } + + if p.shouldFosterParent() { + p.fosterParent(&Node{ + Type: TextNode, + Data: text, + }) + return + } + + t := p.top() + if n := t.LastChild; n != nil && n.Type == TextNode { + n.Data += text + return + } + p.addChild(&Node{ + Type: TextNode, + Data: text, + }) +} + +// addElement adds a child element based on the current token. +func (p *parser) addElement() { + p.addChild(&Node{ + Type: ElementNode, + DataAtom: p.tok.DataAtom, + Data: p.tok.Data, + Attr: p.tok.Attr, + }) +} + +// Section 12.2.3.3. +func (p *parser) addFormattingElement() { + tagAtom, attr := p.tok.DataAtom, p.tok.Attr + p.addElement() + + // Implement the Noah's Ark clause, but with three per family instead of two. + identicalElements := 0 +findIdenticalElements: + for i := len(p.afe) - 1; i >= 0; i-- { + n := p.afe[i] + if n.Type == scopeMarkerNode { + break + } + if n.Type != ElementNode { + continue + } + if n.Namespace != "" { + continue + } + if n.DataAtom != tagAtom { + continue + } + if len(n.Attr) != len(attr) { + continue + } + compareAttributes: + for _, t0 := range n.Attr { + for _, t1 := range attr { + if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { + // Found a match for this attribute, continue with the next attribute. + continue compareAttributes + } + } + // If we get here, there is no attribute that matches a. + // Therefore the element is not identical to the new one. + continue findIdenticalElements + } + + identicalElements++ + if identicalElements >= 3 { + p.afe.remove(n) + } + } + + p.afe = append(p.afe, p.top()) +} + +// Section 12.2.3.3. +func (p *parser) clearActiveFormattingElements() { + for { + n := p.afe.pop() + if len(p.afe) == 0 || n.Type == scopeMarkerNode { + return + } + } +} + +// Section 12.2.3.3. +func (p *parser) reconstructActiveFormattingElements() { + n := p.afe.top() + if n == nil { + return + } + if n.Type == scopeMarkerNode || p.oe.index(n) != -1 { + return + } + i := len(p.afe) - 1 + for n.Type != scopeMarkerNode && p.oe.index(n) == -1 { + if i == 0 { + i = -1 + break + } + i-- + n = p.afe[i] + } + for { + i++ + clone := p.afe[i].clone() + p.addChild(clone) + p.afe[i] = clone + if i == len(p.afe)-1 { + break + } + } +} + +// Section 12.2.4. +func (p *parser) acknowledgeSelfClosingTag() { + p.hasSelfClosingToken = false +} + +// An insertion mode (section 12.2.3.1) is the state transition function from +// a particular state in the HTML5 parser's state machine. It updates the +// parser's fields depending on parser.tok (where ErrorToken means EOF). +// It returns whether the token was consumed. +type insertionMode func(*parser) bool + +// setOriginalIM sets the insertion mode to return to after completing a text or +// inTableText insertion mode. +// Section 12.2.3.1, "using the rules for". +func (p *parser) setOriginalIM() { + if p.originalIM != nil { + panic("html: bad parser state: originalIM was set twice") + } + p.originalIM = p.im +} + +// Section 12.2.3.1, "reset the insertion mode". +func (p *parser) resetInsertionMode() { + for i := len(p.oe) - 1; i >= 0; i-- { + n := p.oe[i] + if i == 0 && p.context != nil { + n = p.context + } + + switch n.DataAtom { + case a.Select: + p.im = inSelectIM + case a.Td, a.Th: + p.im = inCellIM + case a.Tr: + p.im = inRowIM + case a.Tbody, a.Thead, a.Tfoot: + p.im = inTableBodyIM + case a.Caption: + p.im = inCaptionIM + case a.Colgroup: + p.im = inColumnGroupIM + case a.Table: + p.im = inTableIM + case a.Head: + p.im = inBodyIM + case a.Body: + p.im = inBodyIM + case a.Frameset: + p.im = inFramesetIM + case a.Html: + p.im = beforeHeadIM + default: + continue + } + return + } + p.im = inBodyIM +} + +const whitespace = " \t\r\n\f" + +// Section 12.2.5.4.1. +func initialIM(p *parser) bool { + switch p.tok.Type { + case TextToken: + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } + case CommentToken: + p.doc.AppendChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + n, quirks := parseDoctype(p.tok.Data) + p.doc.AppendChild(n) + p.quirks = quirks + p.im = beforeHTMLIM + return true + } + p.quirks = true + p.im = beforeHTMLIM + return false +} + +// Section 12.2.5.4.2. +func beforeHTMLIM(p *parser) bool { + switch p.tok.Type { + case DoctypeToken: + // Ignore the token. + return true + case TextToken: + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } + case StartTagToken: + if p.tok.DataAtom == a.Html { + p.addElement() + p.im = beforeHeadIM + return true + } + case EndTagToken: + switch p.tok.DataAtom { + case a.Head, a.Body, a.Html, a.Br: + p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) + return false + default: + // Ignore the token. + return true + } + case CommentToken: + p.doc.AppendChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + } + p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) + return false +} + +// Section 12.2.5.4.3. +func beforeHeadIM(p *parser) bool { + switch p.tok.Type { + case TextToken: + p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) + if len(p.tok.Data) == 0 { + // It was all whitespace, so ignore it. + return true + } + case StartTagToken: + switch p.tok.DataAtom { + case a.Head: + p.addElement() + p.head = p.top() + p.im = inHeadIM + return true + case a.Html: + return inBodyIM(p) + } + case EndTagToken: + switch p.tok.DataAtom { + case a.Head, a.Body, a.Html, a.Br: + p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) + return false + default: + // Ignore the token. + return true + } + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + // Ignore the token. + return true + } + + p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) + return false +} + +// Section 12.2.5.4.4. +func inHeadIM(p *parser) bool { + switch p.tok.Type { + case TextToken: + s := strings.TrimLeft(p.tok.Data, whitespace) + if len(s) < len(p.tok.Data) { + // Add the initial whitespace to the current node. + p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) + if s == "" { + return true + } + p.tok.Data = s + } + case StartTagToken: + switch p.tok.DataAtom { + case a.Html: + return inBodyIM(p) + case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta: + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + return true + case a.Script, a.Title, a.Noscript, a.Noframes, a.Style: + p.addElement() + p.setOriginalIM() + p.im = textIM + return true + case a.Head: + // Ignore the token. + return true + } + case EndTagToken: + switch p.tok.DataAtom { + case a.Head: + n := p.oe.pop() + if n.DataAtom != a.Head { + panic("html: bad parser state: element not found, in the in-head insertion mode") + } + p.im = afterHeadIM + return true + case a.Body, a.Html, a.Br: + p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) + return false + default: + // Ignore the token. + return true + } + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + // Ignore the token. + return true + } + + p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) + return false +} + +// Section 12.2.5.4.6. +func afterHeadIM(p *parser) bool { + switch p.tok.Type { + case TextToken: + s := strings.TrimLeft(p.tok.Data, whitespace) + if len(s) < len(p.tok.Data) { + // Add the initial whitespace to the current node. + p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) + if s == "" { + return true + } + p.tok.Data = s + } + case StartTagToken: + switch p.tok.DataAtom { + case a.Html: + return inBodyIM(p) + case a.Body: + p.addElement() + p.framesetOK = false + p.im = inBodyIM + return true + case a.Frameset: + p.addElement() + p.im = inFramesetIM + return true + case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title: + p.oe = append(p.oe, p.head) + defer p.oe.remove(p.head) + return inHeadIM(p) + case a.Head: + // Ignore the token. + return true + } + case EndTagToken: + switch p.tok.DataAtom { + case a.Body, a.Html, a.Br: + // Drop down to creating an implied tag. + default: + // Ignore the token. + return true + } + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + // Ignore the token. + return true + } + + p.parseImpliedToken(StartTagToken, a.Body, a.Body.String()) + p.framesetOK = true + return false +} + +// copyAttributes copies attributes of src not found on dst to dst. +func copyAttributes(dst *Node, src Token) { + if len(src.Attr) == 0 { + return + } + attr := map[string]string{} + for _, t := range dst.Attr { + attr[t.Key] = t.Val + } + for _, t := range src.Attr { + if _, ok := attr[t.Key]; !ok { + dst.Attr = append(dst.Attr, t) + attr[t.Key] = t.Val + } + } +} + +// Section 12.2.5.4.7. +func inBodyIM(p *parser) bool { + switch p.tok.Type { + case TextToken: + d := p.tok.Data + switch n := p.oe.top(); n.DataAtom { + case a.Pre, a.Listing: + if n.FirstChild == nil { + // Ignore a newline at the start of a
 block.
+				if d != "" && d[0] == '\r' {
+					d = d[1:]
+				}
+				if d != "" && d[0] == '\n' {
+					d = d[1:]
+				}
+			}
+		}
+		d = strings.Replace(d, "\x00", "", -1)
+		if d == "" {
+			return true
+		}
+		p.reconstructActiveFormattingElements()
+		p.addText(d)
+		if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
+			// There were non-whitespace characters inserted.
+			p.framesetOK = false
+		}
+	case StartTagToken:
+		switch p.tok.DataAtom {
+		case a.Html:
+			copyAttributes(p.oe[0], p.tok)
+		case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
+			return inHeadIM(p)
+		case a.Body:
+			if len(p.oe) >= 2 {
+				body := p.oe[1]
+				if body.Type == ElementNode && body.DataAtom == a.Body {
+					p.framesetOK = false
+					copyAttributes(body, p.tok)
+				}
+			}
+		case a.Frameset:
+			if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body {
+				// Ignore the token.
+				return true
+			}
+			body := p.oe[1]
+			if body.Parent != nil {
+				body.Parent.RemoveChild(body)
+			}
+			p.oe = p.oe[:1]
+			p.addElement()
+			p.im = inFramesetIM
+			return true
+		case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul:
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+			p.popUntil(buttonScope, a.P)
+			switch n := p.top(); n.DataAtom {
+			case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+				p.oe.pop()
+			}
+			p.addElement()
+		case a.Pre, a.Listing:
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+			// The newline, if any, will be dealt with by the TextToken case.
+			p.framesetOK = false
+		case a.Form:
+			if p.form == nil {
+				p.popUntil(buttonScope, a.P)
+				p.addElement()
+				p.form = p.top()
+			}
+		case a.Li:
+			p.framesetOK = false
+			for i := len(p.oe) - 1; i >= 0; i-- {
+				node := p.oe[i]
+				switch node.DataAtom {
+				case a.Li:
+					p.oe = p.oe[:i]
+				case a.Address, a.Div, a.P:
+					continue
+				default:
+					if !isSpecialElement(node) {
+						continue
+					}
+				}
+				break
+			}
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+		case a.Dd, a.Dt:
+			p.framesetOK = false
+			for i := len(p.oe) - 1; i >= 0; i-- {
+				node := p.oe[i]
+				switch node.DataAtom {
+				case a.Dd, a.Dt:
+					p.oe = p.oe[:i]
+				case a.Address, a.Div, a.P:
+					continue
+				default:
+					if !isSpecialElement(node) {
+						continue
+					}
+				}
+				break
+			}
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+		case a.Plaintext:
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+		case a.Button:
+			p.popUntil(defaultScope, a.Button)
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+			p.framesetOK = false
+		case a.A:
+			for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- {
+				if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A {
+					p.inBodyEndTagFormatting(a.A)
+					p.oe.remove(n)
+					p.afe.remove(n)
+					break
+				}
+			}
+			p.reconstructActiveFormattingElements()
+			p.addFormattingElement()
+		case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
+			p.reconstructActiveFormattingElements()
+			p.addFormattingElement()
+		case a.Nobr:
+			p.reconstructActiveFormattingElements()
+			if p.elementInScope(defaultScope, a.Nobr) {
+				p.inBodyEndTagFormatting(a.Nobr)
+				p.reconstructActiveFormattingElements()
+			}
+			p.addFormattingElement()
+		case a.Applet, a.Marquee, a.Object:
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+			p.afe = append(p.afe, &scopeMarker)
+			p.framesetOK = false
+		case a.Table:
+			if !p.quirks {
+				p.popUntil(buttonScope, a.P)
+			}
+			p.addElement()
+			p.framesetOK = false
+			p.im = inTableIM
+			return true
+		case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr:
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+			p.oe.pop()
+			p.acknowledgeSelfClosingTag()
+			if p.tok.DataAtom == a.Input {
+				for _, t := range p.tok.Attr {
+					if t.Key == "type" {
+						if strings.ToLower(t.Val) == "hidden" {
+							// Skip setting framesetOK = false
+							return true
+						}
+					}
+				}
+			}
+			p.framesetOK = false
+		case a.Param, a.Source, a.Track:
+			p.addElement()
+			p.oe.pop()
+			p.acknowledgeSelfClosingTag()
+		case a.Hr:
+			p.popUntil(buttonScope, a.P)
+			p.addElement()
+			p.oe.pop()
+			p.acknowledgeSelfClosingTag()
+			p.framesetOK = false
+		case a.Image:
+			p.tok.DataAtom = a.Img
+			p.tok.Data = a.Img.String()
+			return false
+		case a.Isindex:
+			if p.form != nil {
+				// Ignore the token.
+				return true
+			}
+			action := ""
+			prompt := "This is a searchable index. Enter search keywords: "
+			attr := []Attribute{{Key: "name", Val: "isindex"}}
+			for _, t := range p.tok.Attr {
+				switch t.Key {
+				case "action":
+					action = t.Val
+				case "name":
+					// Ignore the attribute.
+				case "prompt":
+					prompt = t.Val
+				default:
+					attr = append(attr, t)
+				}
+			}
+			p.acknowledgeSelfClosingTag()
+			p.popUntil(buttonScope, a.P)
+			p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
+			if action != "" {
+				p.form.Attr = []Attribute{{Key: "action", Val: action}}
+			}
+			p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
+			p.parseImpliedToken(StartTagToken, a.Label, a.Label.String())
+			p.addText(prompt)
+			p.addChild(&Node{
+				Type:     ElementNode,
+				DataAtom: a.Input,
+				Data:     a.Input.String(),
+				Attr:     attr,
+			})
+			p.oe.pop()
+			p.parseImpliedToken(EndTagToken, a.Label, a.Label.String())
+			p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String())
+			p.parseImpliedToken(EndTagToken, a.Form, a.Form.String())
+		case a.Textarea:
+			p.addElement()
+			p.setOriginalIM()
+			p.framesetOK = false
+			p.im = textIM
+		case a.Xmp:
+			p.popUntil(buttonScope, a.P)
+			p.reconstructActiveFormattingElements()
+			p.framesetOK = false
+			p.addElement()
+			p.setOriginalIM()
+			p.im = textIM
+		case a.Iframe:
+			p.framesetOK = false
+			p.addElement()
+			p.setOriginalIM()
+			p.im = textIM
+		case a.Noembed, a.Noscript:
+			p.addElement()
+			p.setOriginalIM()
+			p.im = textIM
+		case a.Select:
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+			p.framesetOK = false
+			p.im = inSelectIM
+			return true
+		case a.Optgroup, a.Option:
+			if p.top().DataAtom == a.Option {
+				p.oe.pop()
+			}
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+		case a.Rp, a.Rt:
+			if p.elementInScope(defaultScope, a.Ruby) {
+				p.generateImpliedEndTags()
+			}
+			p.addElement()
+		case a.Math, a.Svg:
+			p.reconstructActiveFormattingElements()
+			if p.tok.DataAtom == a.Math {
+				adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments)
+			} else {
+				adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments)
+			}
+			adjustForeignAttributes(p.tok.Attr)
+			p.addElement()
+			p.top().Namespace = p.tok.Data
+			if p.hasSelfClosingToken {
+				p.oe.pop()
+				p.acknowledgeSelfClosingTag()
+			}
+			return true
+		case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
+			// Ignore the token.
+		default:
+			p.reconstructActiveFormattingElements()
+			p.addElement()
+		}
+	case EndTagToken:
+		switch p.tok.DataAtom {
+		case a.Body:
+			if p.elementInScope(defaultScope, a.Body) {
+				p.im = afterBodyIM
+			}
+		case a.Html:
+			if p.elementInScope(defaultScope, a.Body) {
+				p.parseImpliedToken(EndTagToken, a.Body, a.Body.String())
+				return false
+			}
+			return true
+		case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
+			p.popUntil(defaultScope, p.tok.DataAtom)
+		case a.Form:
+			node := p.form
+			p.form = nil
+			i := p.indexOfElementInScope(defaultScope, a.Form)
+			if node == nil || i == -1 || p.oe[i] != node {
+				// Ignore the token.
+				return true
+			}
+			p.generateImpliedEndTags()
+			p.oe.remove(node)
+		case a.P:
+			if !p.elementInScope(buttonScope, a.P) {
+				p.parseImpliedToken(StartTagToken, a.P, a.P.String())
+			}
+			p.popUntil(buttonScope, a.P)
+		case a.Li:
+			p.popUntil(listItemScope, a.Li)
+		case a.Dd, a.Dt:
+			p.popUntil(defaultScope, p.tok.DataAtom)
+		case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6:
+			p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6)
+		case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U:
+			p.inBodyEndTagFormatting(p.tok.DataAtom)
+		case a.Applet, a.Marquee, a.Object:
+			if p.popUntil(defaultScope, p.tok.DataAtom) {
+				p.clearActiveFormattingElements()
+			}
+		case a.Br:
+			p.tok.Type = StartTagToken
+			return false
+		default:
+			p.inBodyEndTagOther(p.tok.DataAtom)
+		}
+	case CommentToken:
+		p.addChild(&Node{
+			Type: CommentNode,
+			Data: p.tok.Data,
+		})
+	}
+
+	return true
+}
+
+func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
+	// This is the "adoption agency" algorithm, described at
+	// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency
+
+	// TODO: this is a fairly literal line-by-line translation of that algorithm.
+	// Once the code successfully parses the comprehensive test suite, we should
+	// refactor this code to be more idiomatic.
+
+	// Steps 1-4. The outer loop.
+	for i := 0; i < 8; i++ {
+		// Step 5. Find the formatting element.
+		var formattingElement *Node
+		for j := len(p.afe) - 1; j >= 0; j-- {
+			if p.afe[j].Type == scopeMarkerNode {
+				break
+			}
+			if p.afe[j].DataAtom == tagAtom {
+				formattingElement = p.afe[j]
+				break
+			}
+		}
+		if formattingElement == nil {
+			p.inBodyEndTagOther(tagAtom)
+			return
+		}
+		feIndex := p.oe.index(formattingElement)
+		if feIndex == -1 {
+			p.afe.remove(formattingElement)
+			return
+		}
+		if !p.elementInScope(defaultScope, tagAtom) {
+			// Ignore the tag.
+			return
+		}
+
+		// Steps 9-10. Find the furthest block.
+		var furthestBlock *Node
+		for _, e := range p.oe[feIndex:] {
+			if isSpecialElement(e) {
+				furthestBlock = e
+				break
+			}
+		}
+		if furthestBlock == nil {
+			e := p.oe.pop()
+			for e != formattingElement {
+				e = p.oe.pop()
+			}
+			p.afe.remove(e)
+			return
+		}
+
+		// Steps 11-12. Find the common ancestor and bookmark node.
+		commonAncestor := p.oe[feIndex-1]
+		bookmark := p.afe.index(formattingElement)
+
+		// Step 13. The inner loop. Find the lastNode to reparent.
+		lastNode := furthestBlock
+		node := furthestBlock
+		x := p.oe.index(node)
+		// Steps 13.1-13.2
+		for j := 0; j < 3; j++ {
+			// Step 13.3.
+			x--
+			node = p.oe[x]
+			// Step 13.4 - 13.5.
+			if p.afe.index(node) == -1 {
+				p.oe.remove(node)
+				continue
+			}
+			// Step 13.6.
+			if node == formattingElement {
+				break
+			}
+			// Step 13.7.
+			clone := node.clone()
+			p.afe[p.afe.index(node)] = clone
+			p.oe[p.oe.index(node)] = clone
+			node = clone
+			// Step 13.8.
+			if lastNode == furthestBlock {
+				bookmark = p.afe.index(node) + 1
+			}
+			// Step 13.9.
+			if lastNode.Parent != nil {
+				lastNode.Parent.RemoveChild(lastNode)
+			}
+			node.AppendChild(lastNode)
+			// Step 13.10.
+			lastNode = node
+		}
+
+		// Step 14. Reparent lastNode to the common ancestor,
+		// or for misnested table nodes, to the foster parent.
+		if lastNode.Parent != nil {
+			lastNode.Parent.RemoveChild(lastNode)
+		}
+		switch commonAncestor.DataAtom {
+		case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
+			p.fosterParent(lastNode)
+		default:
+			commonAncestor.AppendChild(lastNode)
+		}
+
+		// Steps 15-17. Reparent nodes from the furthest block's children
+		// to a clone of the formatting element.
+		clone := formattingElement.clone()
+		reparentChildren(clone, furthestBlock)
+		furthestBlock.AppendChild(clone)
+
+		// Step 18. Fix up the list of active formatting elements.
+		if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark {
+			// Move the bookmark with the rest of the list.
+			bookmark--
+		}
+		p.afe.remove(formattingElement)
+		p.afe.insert(bookmark, clone)
+
+		// Step 19. Fix up the stack of open elements.
+		p.oe.remove(formattingElement)
+		p.oe.insert(p.oe.index(furthestBlock)+1, clone)
+	}
+}
+
+// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
+// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content
+// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
+func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
+	for i := len(p.oe) - 1; i >= 0; i-- {
+		if p.oe[i].DataAtom == tagAtom {
+			p.oe = p.oe[:i]
+			break
+		}
+		if isSpecialElement(p.oe[i]) {
+			break
+		}
+	}
+}
+
+// Section 12.2.5.4.8.
+func textIM(p *parser) bool {
+	switch p.tok.Type {
+	case ErrorToken:
+		p.oe.pop()
+	case TextToken:
+		d := p.tok.Data
+		if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil {
+			// Ignore a newline at the start of a